Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pyroute2
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
safeedge
pyroute2
Commits
8a4793e0
Commit
8a4793e0
authored
Apr 09, 2020
by
Peter V. Saveliev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ndb: authentication plugins API
parent
c714f4d5
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
373 additions
and
24 deletions
+373
-24
docs/ndb.rst
docs/ndb.rst
+1
-0
docs/ndb_auth.rst
docs/ndb_auth.rst
+9
-0
examples/ndb/keystone_auth.py
examples/ndb/keystone_auth.py
+111
-0
pyroute2/common.py
pyroute2/common.py
+11
-0
pyroute2/ndb/auth_manager.py
pyroute2/ndb/auth_manager.py
+103
-0
pyroute2/ndb/main.py
pyroute2/ndb/main.py
+64
-11
pyroute2/ndb/objects/__init__.py
pyroute2/ndb/objects/__init__.py
+31
-4
pyroute2/ndb/objects/interface.py
pyroute2/ndb/objects/interface.py
+23
-4
pyroute2/ndb/objects/route.py
pyroute2/ndb/objects/route.py
+20
-5
No files found.
docs/ndb.rst
View file @
8a4793e0
...
...
@@ -92,5 +92,6 @@ Reference
ndb_schema
ndb_sources
ndb_debug
ndb_auth
*work in progress*
docs/ndb_auth.rst
0 → 100644
View file @
8a4793e0
.. _ndbauth:
Authorization plugins
=====================
.. automodule:: pyroute2.ndb.auth_manager
.. literalinclude:: ../examples/ndb/keystone_auth.py
:language: python
examples/ndb/keystone_auth.py
0 → 100644
View file @
8a4793e0
'''
A simplest example of a custom AuthManager and its usage
with `AuthProxy` objects.
Here we authenticate the auth token against Keystone and
allow any NDB operations until it is expired.
One can get such token with a curl request::
$ cat request.json
{ "auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "admin",
"domain": { "name": "admin_domain" },
"password": "secret"
}
}
},
"scope": {
"project": {
"id": "f0af12d451fb4bccbb38217e7f9afe9a"
}
}
}
}
$ curl -i
\
-H "Content-Type: application/json"
\
-d "@request.json"
\
http://keystone:5000/v3/auth/tokens
`X-Subject-Token` header in the response will be the token we need. Say we
get `14080769fe05e1f8b837fb43ca0f0ba4` as `X-Subject-Token`. Then you can
run::
$ . openstack.rc # <-- your OpenStack APIv3 RC file
$ export PYTHONPATH=`pwd`
$ python examples/ndb/keystone_auth.py 14080769fe05e1f8b837fb43ca0f0ba4
Using this example you can implement services that export NDB via any RPC,
e.g. HTTP, and use Keystone integration. Same scheme may be used for any
other Auth API, be it RADIUS or like that.
An example of a simple HTTP service you can find in /cli/pyroute2-cli.
'''
import
os
import
sys
import
time
from
dateutil.parser
import
parse
as
isodate
from
keystoneauth1.identity
import
v3
from
keystoneauth1
import
session
from
keystoneclient.v3
import
client
as
ksclient
from
keystoneclient.v3.tokens
import
TokenManager
from
pyroute2
import
NDB
class
OSAuthManager
(
object
):
def
__init__
(
self
,
token
,
log
):
# create a Keystone password object
auth
=
v3
.
Password
(
auth_url
=
os
.
environ
.
get
(
'OS_AUTH_URL'
),
username
=
os
.
environ
.
get
(
'OS_USERNAME'
),
password
=
os
.
environ
.
get
(
'OS_PASSWORD'
),
user_domain_name
=
(
os
.
environ
.
get
(
'OS_USER_DOMAIN_NAME'
)),
project_id
=
os
.
environ
.
get
(
'OS_PROJECT_ID'
))
# create a session object
sess
=
session
.
Session
(
auth
=
auth
)
# create a token manager
tmanager
=
TokenManager
(
ksclient
.
Client
(
session
=
sess
))
# validate the token
keystone_response
=
tmanager
.
validate
(
token
)
# init attrs
self
.
log
=
log
self
.
expire
=
isodate
(
keystone_response
[
'expires_at'
]).
timestamp
()
def
check
(
self
,
obj
,
tag
):
#
# totally ignore obj and tag, validate only token expiration
#
# problems to be solved before you use this code in production:
# 1. access levels: read-only, read-write -- match tag
# 2. how to deal with revoked tokens
#
if
time
.
time
()
>
self
.
expire
:
self
.
log
.
error
(
'%s permission denied'
%
(
tag
,
))
raise
PermissionError
(
'keystone token has been expired'
)
self
.
log
.
info
(
'%s permission granted'
%
(
tag
,
))
return
True
with
NDB
(
log
=
'debug'
)
as
ndb
:
# create a utility log channel
log
=
ndb
.
log
.
channel
(
'main'
)
# create an AuthManager-compatible object
log
.
info
(
'request keystone auth'
)
am
=
OSAuthManager
(
sys
.
argv
[
1
],
ndb
.
log
.
channel
(
'keystone'
))
log
.
info
(
'keystone auth complete, expires %s'
%
am
.
expire
)
# create an auth proxy for this particular token
ap
=
ndb
.
auth_proxy
(
am
)
# validate access via that proxy
print
(
ap
.
interfaces
[
'lo'
])
pyroute2/common.py
View file @
8a4793e0
...
...
@@ -17,14 +17,25 @@ import threading
log
=
logging
.
getLogger
(
__name__
)
try
:
#
# Python2 section
#
basestring
=
basestring
reduce
=
reduce
file
=
file
class
PermissionError
(
Exception
):
pass
except
NameError
:
#
# Python3 section
#
basestring
=
(
str
,
bytes
)
from
functools
import
reduce
reduce
=
reduce
file
=
io
.
BytesIO
PermissionError
=
PermissionError
AF_MPLS
=
28
AF_PIPE
=
255
# Right now AF_MAX == 40
...
...
pyroute2/ndb/auth_manager.py
0 → 100644
View file @
8a4793e0
'''
AAA concept
-----------
AAA refers to Authentication, Authorization and Accounting. NDB provides
a minimalistic API to integrate Authorization routines, leaving the
rest -- Authentication and Accounting -- to the user.
Some of NDB routines and RTNL object methods are guarded with a
parametrized decorator. The decorator takes the only parameter `tag`::
@check_auth('obj:read')
def __getitem__(self, key):
...
@check_auth('obj:modify')
def __setitem__(self, key, value):
...
AuthManager
-----------
The tag is checked by `AuthManager.check(...)` routine. The routine is
the only method that must be provided by AuthManager-compatible objects,
and must be defined as::
def check(self, obj, tag):
# -> True: grant access to the tag
# -> False: reject access
# -> raise Exception(): reject access with a specific exception
...
NDB module provides an example AuthManager::
from pyroute2 import NDB
from pyroute2.ndb.auth_manager import AuthManager
ndb = NDB(log='debug')
am = AuthManager({'obj:list': False, # deny dump(), summary()
'obj:read': True, # permit reading RTNL attributes
'obj:modify': True}, # permit add_ip(), commit() etc.
ndb.log.channel('auth'))
ap = ndb.auth_proxy(am)
ap.interfaces.summary() # <-- fails with PermissionError
You can implement custom AuthManager classes, the only requirement -- they
must provide `.check(self, obj, tag)` routine, which returns `True` or
`False` or raises an exception.
Usecase: OpenStack Keystone auth
--------------------------------
Say we have a public service that provides access to NDB instance via
HTTP, and authenticates users via Keystone. Then the auth flow could be:
1. Accept a connection from a client
2. Create custom auth manager object A
3. A.__init__() validates X-Auth-Token against Keystone (Authentication)
4. A.check() checks that X-Auth-Token is not expired (Authorization)
5. The auth result is being logged (Accounting)
An example AuthManager with OpenStack APIv3 support you may find in the
`/examples/ndb/` directory.
'''
from
pyroute2.common
import
PermissionError
class
check_auth
(
object
):
def
__init__
(
self
,
tag
):
self
.
tag
=
tag
def
__call__
(
self
,
f
):
def
guard
(
obj
,
*
argv
,
**
kwarg
):
if
not
getattr
(
obj
,
'_init_complete'
,
True
):
return
f
(
obj
,
*
argv
,
**
kwarg
)
if
not
obj
.
auth_managers
:
raise
PermissionError
(
'access rejected'
)
if
all
([
x
.
check
(
obj
,
self
.
tag
)
for
x
in
obj
.
auth_managers
]):
return
f
(
obj
,
*
argv
,
**
kwarg
)
raise
PermissionError
(
'access rejected'
)
return
guard
class
AuthManager
(
object
):
def
__init__
(
self
,
auth
,
log
,
policy
=
False
):
self
.
auth
=
auth
self
.
log
=
log
self
.
policy
=
policy
self
.
exception
=
PermissionError
def
check
(
self
,
obj
,
tag
):
ret
=
self
.
policy
self
.
log
.
debug
(
'%s %s auth=%s'
%
(
id
(
obj
),
tag
,
self
.
auth
))
if
isinstance
(
self
.
auth
,
dict
):
ret
=
self
.
auth
.
get
(
tag
,
self
.
policy
)
if
not
ret
and
self
.
exception
:
raise
self
.
exception
(
'%s access rejected'
%
(
tag
,
))
return
ret
pyroute2/ndb/main.py
View file @
8a4793e0
...
...
@@ -150,6 +150,8 @@ from pyroute2.ndb.messages import (cmsg,
cmsg_failed
,
cmsg_sstart
)
from
pyroute2.ndb.source
import
Source
from
pyroute2.ndb.auth_manager
import
check_auth
from
pyroute2.ndb.auth_manager
import
AuthManager
from
pyroute2.ndb.objects.interface
import
Interface
from
pyroute2.ndb.objects.interface
import
Vlan
from
pyroute2.ndb.objects.address
import
Address
...
...
@@ -157,7 +159,7 @@ from pyroute2.ndb.objects.route import Route
from
pyroute2.ndb.objects.neighbour
import
Neighbour
from
pyroute2.ndb.objects.rule
import
Rule
from
pyroute2.ndb.objects.netns
import
NetNS
from
pyroute2.ndb.query
import
Query
#
from pyroute2.ndb.query import Query
from
pyroute2.ndb.report
import
(
RecordSet
,
Record
)
try
:
...
...
@@ -214,14 +216,20 @@ class View(dict):
ndb
,
table
,
chain
=
None
,
default_target
=
'localhost'
):
default_target
=
'localhost'
,
auth_managers
=
None
):
self
.
ndb
=
ndb
self
.
log
=
ndb
.
log
.
channel
(
'view.%s'
%
table
)
self
.
table
=
table
self
.
event
=
table
# FIXME
self
.
chain
=
chain
self
.
cache
=
{}
if
auth_managers
is
None
:
auth_managers
=
[]
if
chain
:
auth_managers
+=
chain
.
auth_managers
self
.
default_target
=
default_target
self
.
auth_managers
=
auth_managers
self
.
constraints
=
{}
self
.
classes
=
OrderedDict
()
self
.
classes
[
'interfaces'
]
=
Interface
...
...
@@ -362,6 +370,7 @@ class View(dict):
gc
.
collect
()
return
ret
@
check_auth
(
'obj:read'
)
def
__getitem__
(
self
,
key
,
table
=
None
):
if
self
.
chain
:
...
...
@@ -372,7 +381,11 @@ class View(dict):
if
isinstance
(
key
,
Record
):
key
=
key
.
_as_dict
()
key
=
iclass
.
adjust_spec
(
key
,
context
)
ret
=
iclass
(
self
,
key
,
load
=
False
,
master
=
self
.
chain
)
ret
=
iclass
(
self
,
key
,
load
=
False
,
master
=
self
.
chain
,
auth_managers
=
self
.
auth_managers
)
# rtnl_object.key() returns a dcitionary that can not
# be used as a cache key. Create here a tuple from it.
...
...
@@ -426,14 +439,17 @@ class View(dict):
def
__iter__
(
self
):
return
self
.
keys
()
@
check_auth
(
'obj:list'
)
def
keys
(
self
):
for
record
in
self
.
dump
():
yield
record
@
check_auth
(
'obj:list'
)
def
values
(
self
):
for
key
in
self
.
keys
():
yield
self
[
key
]
@
check_auth
(
'obj:list'
)
def
items
(
self
):
for
key
in
self
.
keys
():
yield
(
key
,
self
[
key
])
...
...
@@ -458,11 +474,13 @@ class View(dict):
yield
Record
(
fnames
,
record
)
@
cli
.
show_result
@
check_auth
(
'obj:list'
)
def
dump
(
self
):
iclass
=
self
.
classes
[
self
.
table
]
return
RecordSet
(
self
.
_native
(
iclass
.
dump
(
self
)))
@
cli
.
show_result
@
check_auth
(
'obj:list'
)
def
summary
(
self
):
iclass
=
self
.
classes
[
self
.
table
]
return
RecordSet
(
self
.
_native
(
iclass
.
summary
(
self
)))
...
...
@@ -549,6 +567,9 @@ class Log(object):
if
target
in
(
'on'
,
'stderr'
):
handler
=
logging
.
StreamHandler
()
elif
target
==
'debug'
:
handler
=
logging
.
StreamHandler
()
level
=
logging
.
DEBUG
elif
isinstance
(
target
,
basestring
):
url
=
urlparse
(
target
)
if
not
url
.
scheme
and
url
.
path
:
...
...
@@ -645,6 +666,26 @@ def Events(*argv):
yield
item
class
AuthProxy
(
object
):
def
__init__
(
self
,
ndb
,
auth_managers
):
self
.
_ndb
=
ndb
self
.
_auth_managers
=
auth_managers
for
spec
in
((
'interfaces'
,
'localhost'
),
(
'addresses'
,
'localhost'
),
(
'routes'
,
'localhost'
),
(
'neighbours'
,
'localhost'
),
(
'rules'
,
'localhost'
),
(
'netns'
,
'nsmanager'
),
(
'vlans'
,
'localhost'
)):
view
=
View
(
self
.
_ndb
,
spec
[
0
],
default_target
=
spec
[
1
],
auth_managers
=
self
.
_auth_managers
)
setattr
(
self
,
spec
[
0
],
view
)
class
NDB
(
object
):
def
__init__
(
self
,
...
...
@@ -709,14 +750,23 @@ class NDB(object):
for
event
in
tuple
(
self
.
_dbm_autoload
):
event
.
wait
()
self
.
_dbm_autoload
=
None
self
.
interfaces
=
View
(
self
,
'interfaces'
)
self
.
addresses
=
View
(
self
,
'addresses'
)
self
.
routes
=
View
(
self
,
'routes'
)
self
.
neighbours
=
View
(
self
,
'neighbours'
)
self
.
rules
=
View
(
self
,
'rules'
)
self
.
netns
=
View
(
self
,
'netns'
,
default_target
=
'nsmanager'
)
self
.
vlans
=
View
(
self
,
'vlans'
)
self
.
query
=
Query
(
self
.
schema
)
am
=
AuthManager
({
'obj:list'
:
True
,
'obj:read'
:
True
,
'obj:modify'
:
True
},
self
.
log
.
channel
(
'auth'
))
for
spec
in
((
'interfaces'
,
'localhost'
),
(
'addresses'
,
'localhost'
),
(
'routes'
,
'localhost'
),
(
'neighbours'
,
'localhost'
),
(
'rules'
,
'localhost'
),
(
'netns'
,
'nsmanager'
),
(
'vlans'
,
'localhost'
)):
view
=
View
(
self
,
spec
[
0
],
default_target
=
spec
[
1
],
auth_managers
=
[
am
])
setattr
(
self
,
spec
[
0
],
view
)
# self.query = Query(self.schema)
def
_get_view
(
self
,
name
,
chain
=
None
):
return
View
(
self
,
name
,
chain
)
...
...
@@ -727,6 +777,9 @@ class NDB(object):
def
__exit__
(
self
,
exc_type
,
exc_value
,
traceback
):
self
.
close
()
def
auth_proxy
(
self
,
auth_manager
):
return
AuthProxy
(
self
,
[
auth_manager
,
])
def
register_handler
(
self
,
event
,
handler
):
if
event
not
in
self
.
_event_map
:
self
.
_event_map
[
event
]
=
[]
...
...
pyroute2/ndb/objects/__init__.py
View file @
8a4793e0
...
...
@@ -95,6 +95,8 @@ from functools import partial
from
pyroute2
import
cli
from
pyroute2.ndb.events
import
State
from
pyroute2.ndb.report
import
Record
from
pyroute2.ndb.auth_manager
import
check_auth
from
pyroute2.ndb.auth_manager
import
AuthManager
from
pyroute2.netlink.exceptions
import
NetlinkError
from
pyroute2.ndb.events
import
InvalidateHandlerException
...
...
@@ -120,6 +122,7 @@ class RTNL_Object(dict):
_key
=
None
_replace
=
None
_replace_on_key_change
=
False
_init_complete
=
False
# 8<------------------------------------------------------------
#
...
...
@@ -219,7 +222,8 @@ class RTNL_Object(dict):
iclass
,
ctxid
=
None
,
load
=
True
,
master
=
None
):
master
=
None
,
auth_managers
=
None
):
self
.
view
=
view
self
.
ndb
=
view
.
ndb
self
.
sources
=
view
.
ndb
.
sources
...
...
@@ -233,6 +237,9 @@ class RTNL_Object(dict):
self
.
atime
=
time
.
time
()
self
.
log
=
self
.
ndb
.
log
.
channel
(
'rtnl_object'
)
self
.
log
.
debug
(
'init'
)
if
auth_managers
is
None
:
auth_managers
=
[
AuthManager
(
None
,
self
.
ndb
.
log
.
channel
(
'auth'
))]
self
.
auth_managers
=
auth_managers
self
.
state
=
State
()
self
.
state
.
set
(
'invalid'
)
self
.
snapshot_deps
=
[]
...
...
@@ -243,6 +250,8 @@ class RTNL_Object(dict):
self
.
knorm
=
self
.
schema
.
compiled
[
self
.
table
][
'norm_idx'
]
self
.
spec
=
self
.
schema
.
compiled
[
self
.
table
][
'all_names'
]
self
.
names
=
self
.
schema
.
compiled
[
self
.
table
][
'norm_names'
]
if
self
.
event_map
is
None
:
self
.
event_map
=
{}
self
.
_apply_script
=
[]
if
isinstance
(
key
,
dict
):
self
.
chain
=
key
.
pop
(
'ndb_chain'
,
None
)
...
...
@@ -268,6 +277,7 @@ class RTNL_Object(dict):
self
.
load_sql
()
else
:
self
.
load_sql
(
table
=
self
.
table
)
self
.
_init_complete
=
True
def
mark_tflags
(
self
,
mark
):
pass
...
...
@@ -309,11 +319,18 @@ class RTNL_Object(dict):
def
__hash__
(
self
):
return
id
(
self
)
@
check_auth
(
'obj:read'
)
def
__getitem__
(
self
,
key
):
return
dict
.
__getitem__
(
self
,
key
)
@
check_auth
(
'obj:modify'
)
def
__setitem__
(
self
,
key
,
value
):
if
self
.
state
==
'system'
and
key
in
self
.
knorm
:
if
self
.
_replace_on_key_change
:
self
.
log
.
debug
(
'prepare replace for key %s'
%
(
self
.
key
))
self
.
_replace
=
type
(
self
)(
self
.
view
,
self
.
key
)
self
.
_replace
=
type
(
self
)(
self
.
view
,
self
.
key
,
auth_managers
=
self
.
auth_managers
)
self
.
state
.
set
(
'replace'
)
else
:
raise
ValueError
(
'attempt to change a key field (%s)'
%
key
)
...
...
@@ -338,6 +355,7 @@ class RTNL_Object(dict):
return
self
.
view
[
spec
]
@
cli
.
show_result
@
check_auth
(
'obj:read'
)
def
show
(
self
,
**
kwarg
):
'''
Return the object in a specified format. The format may be
...
...
@@ -405,6 +423,7 @@ class RTNL_Object(dict):
.
register_handler
(
event
,
partial
(
wr_handler
,
wr
,
fname
)))
@
check_auth
(
'obj:modify'
)
def
snapshot
(
self
,
ctxid
=
None
):
'''
Create and return a snapshot of the object. The method creates
...
...
@@ -419,7 +438,10 @@ class RTNL_Object(dict):
key
=
self
.
key
else
:
key
=
self
.
_replace
.
key
snp
=
type
(
self
)(
self
.
view
,
key
,
ctxid
=
ctxid
)
snp
=
type
(
self
)(
self
.
view
,
key
,
ctxid
=
ctxid
,
auth_managers
=
self
.
auth_managers
)
self
.
ndb
.
schema
.
save_deps
(
ctxid
,
weakref
.
ref
(
snp
),
self
.
iclass
)
snp
.
changed
=
set
(
self
.
changed
)
return
snp
...
...
@@ -472,6 +494,7 @@ class RTNL_Object(dict):
self
.
log
.
debug
(
'got %s'
%
key
)
return
key
@
check_auth
(
'obj:modify'
)
def
rollback
(
self
,
snapshot
=
None
):
'''
Try to rollback the object state using the snapshot provided as
...
...
@@ -480,7 +503,9 @@ class RTNL_Object(dict):
if
self
.
_replace
is
not
None
:
self
.
log
.
debug
(
'rollback replace: %s :: %s'
%
(
self
.
key
,
self
.
_replace
.
key
))
new_replace
=
type
(
self
)(
self
.
view
,
self
.
key
)
new_replace
=
type
(
self
)(
self
.
view
,
self
.
key
,
auth_managers
=
self
.
auth_managers
)
new_replace
.
state
.
set
(
'remove'
)
self
.
state
.
set
(
'replace'
)
self
.
update
(
self
.
_replace
)
...
...
@@ -502,6 +527,7 @@ class RTNL_Object(dict):
not
self
.
changed
and
\
not
self
.
_apply_script
@
check_auth
(
'obj:modify'
)
def
commit
(
self
):
'''
Try to commit the pending changes. If the commit fails,
...
...
@@ -616,6 +642,7 @@ class RTNL_Object(dict):
def
hook_apply
(
self
,
method
,
**
spec
):
pass
@
check_auth
(
'obj:modify'
)
def
apply
(
self
,
rollback
=
False
):
'''
Create a snapshot and apply pending changes. Do not revert
...
...
pyroute2/ndb/objects/interface.py
View file @
8a4793e0
...
...
@@ -98,6 +98,8 @@ from pyroute2.common import basestring
from
pyroute2.netlink.rtnl.ifinfmsg
import
ifinfmsg
from
pyroute2.netlink.rtnl.p2pmsg
import
p2pmsg
from
pyroute2.netlink.exceptions
import
NetlinkError
from
pyroute2.ndb.auth_manager
import
AuthManager
from
pyroute2.ndb.auth_manager
import
check_auth
def
load_ifinfmsg
(
schema
,
target
,
event
):
...
...
@@ -293,7 +295,10 @@ class Vlan(RTNL_Object):
def
__init__
(
self
,
*
argv
,
**
kwarg
):
kwarg
[
'iclass'
]
=
ifinfmsg
.
af_spec_bridge
.
vlan_info
self
.
event_map
=
{
ifinfmsg
:
"load_rtnlmsg"
}
if
'auth_managers'
not
in
kwarg
or
kwarg
[
'auth_managers'
]
is
None
:
kwarg
[
'auth_managers'
]
=
[]
log
=
argv
[
0
].
ndb
.
log
.
channel
(
'vlan auth'
)
kwarg
[
'auth_managers'
].
append
(
AuthManager
({
'obj:modify'
:
False
},
log
))
super
(
Vlan
,
self
).
__init__
(
*
argv
,
**
kwarg
)
def
make_req
(
self
,
prime
):
...
...
@@ -405,6 +410,7 @@ class Interface(RTNL_Object):
ret
.
update
(
context
)
return
ret
@
check_auth
(
'obj:modify'
)
def
add_vlan
(
self
,
spec
):
def
do_add_vlan
(
self
,
spec
):
try
:
...
...
@@ -415,6 +421,7 @@ class Interface(RTNL_Object):
self
.
_apply_script
.
append
((
do_add_vlan
,
(
self
,
spec
),
{}))
return
self
@
check_auth
(
'obj:modify'
)
def
del_vlan
(
self
,
spec
):
def
do_del_vlan
(
self
,
spec
):
try
:
...
...
@@ -426,6 +433,7 @@ class Interface(RTNL_Object):
self
.
_apply_script
.
append
((
do_del_vlan
,
(
self
,
spec
),
{}))
return
self
@
check_auth
(
'obj:modify'
)
def
add_ip
(
self
,
spec
):
def
do_add_ip
(
self
,
spec
):
try
:
...
...
@@ -436,6 +444,7 @@ class Interface(RTNL_Object):
self
.
_apply_script
.
append
((
do_add_ip
,
(
self
,
spec
),
{}))
return
self
@
check_auth
(
'obj:modify'
)
def
del_ip
(
self
,
spec
):
def
do_del_ip
(
self
,
spec
):
try
:
...
...
@@ -447,6 +456,7 @@ class Interface(RTNL_Object):
self
.
_apply_script
.
append
((
do_del_ip
,
(
self
,
spec
),
{}))
return
self
@
check_auth
(
'obj:modify'
)
def
add_port
(
self
,
spec
):
def
do_add_port
(
self
,
spec
):
try
:
...
...
@@ -461,6 +471,7 @@ class Interface(RTNL_Object):
self
.
_apply_script
.
append
((
do_add_port
,
(
self
,
spec
),
{}))
return
self
@
check_auth
(
'obj:modify'
)
def
del_port
(
self
,
spec
):
def
do_del_port
(
self
,
spec
):
try
:
...
...
@@ -476,6 +487,7 @@ class Interface(RTNL_Object):
self
.
_apply_script
.
append
((
do_del_port
,
(
self
,
spec
),
{}))
return
self
<