Revision b2d60e49
b/ncclient/capabilities.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
#_capability_map = { |
|
16 |
# "urn:liberouter:params:netconf:capability:power-control:1.0": |
|
17 |
# [":power-control", ":power-control:1.0"] |
|
18 |
#} |
|
19 |
|
|
20 | 15 |
def _abbreviate(uri): |
21 | 16 |
if uri.startswith("urn:ietf:params:netconf:"): |
22 | 17 |
splitted = uri.split(":") |
23 | 18 |
if ":capability:" in uri: |
24 |
return [ ":" + splitted[5], ":" + splitted[5] + ":" + splitted[6] ] |
|
19 |
name, version = splitted[5], splitted[6] |
|
20 |
return [ ":" + name, ":" + name + ":" + version ] |
|
25 | 21 |
elif ":base:" in uri: |
26 | 22 |
return [ ":base", ":base" + ":" + splitted[5] ] |
27 |
#elif uri in _capability_map: |
|
28 |
# return _capability_map[uri] |
|
29 | 23 |
return [] |
30 | 24 |
|
31 | 25 |
def schemes(url_uri): |
... | ... | |
62 | 56 |
return len(self._dict) |
63 | 57 |
|
64 | 58 |
def __iter__(self): |
65 |
return self._dict.keys().__iter__()
|
|
59 |
return self._dict.iterkeys()
|
|
66 | 60 |
|
67 | 61 |
def __repr__(self): |
68 | 62 |
return repr(self._dict.keys()) |
69 | 63 |
|
70 |
def __list__(self): |
|
71 |
return self._dict.keys() |
|
72 |
|
|
73 | 64 |
def add(self, uri): |
74 | 65 |
"Add a capability." |
75 | 66 |
self._dict[uri] = _abbreviate(uri) |
... | ... | |
77 | 68 |
def remove(self, uri): |
78 | 69 |
"Remove a capability." |
79 | 70 |
if key in self._dict: |
80 |
del self._dict[key] |
|
81 |
|
|
82 |
#def get_uri(self, shorthand): |
|
83 |
# "Returns the URI that is inferred for a given shorthand." |
|
84 |
# for uri, abbrs in self._dict.items(): |
|
85 |
# if shorthand in abbrs: |
|
86 |
# return uri |
|
71 |
del self._dict[key] |
b/ncclient/manager.py | ||
---|---|---|
19 | 19 |
import transport |
20 | 20 |
|
21 | 21 |
import logging |
22 |
|
|
22 | 23 |
logger = logging.getLogger('ncclient.manager') |
23 | 24 |
|
24 | 25 |
CAPABILITIES = [ |
... | ... | |
33 | 34 |
"urn:ietf:params:netconf:capability:xpath:1.0", |
34 | 35 |
"urn:liberouter:params:netconf:capability:power-control:1.0" |
35 | 36 |
"urn:ietf:params:netconf:capability:interleave:1.0" |
36 |
#'urn:ietf:params:netconf:capability:notification:1.0', # TODO |
|
37 | 37 |
] |
38 |
"""A list of URI's representing the client's capabilities. This is used during the initial |
|
39 |
capability exchange. Modify this if you need to announce some capability not already included. |
|
40 |
""" |
|
38 |
"A list of URI's representing the client's capabilities. This is used during the initial capability exchange. Modify this if you need to announce some capability not already included." |
|
41 | 39 |
|
42 | 40 |
OPERATIONS = { |
43 | 41 |
"get": operations.Get, |
... | ... | |
55 | 53 |
"poweroff_machine": operations.PoweroffMachine, |
56 | 54 |
"reboot_machine": operations.RebootMachine |
57 | 55 |
} |
58 |
"""Dictionary of method names and corresponding `~ncclient.operations.RPC` subclasses. It is used to |
|
59 |
lookup operations, e.g. "get_config" is mapped to `~ncclient.operations.GetConfig`. It is thus |
|
60 |
possible to add additional operations to the `Manager` API.""" |
|
56 |
"""Dictionary of method names and corresponding `~ncclient.operations.RPC` subclasses. It is used to lookup operations, e.g. "get_config" is mapped to `~ncclient.operations.GetConfig`. It is thus possible to add additional operations to the `Manager` API.""" |
|
61 | 57 |
|
62 | 58 |
def connect_ssh(*args, **kwds): |
63 |
"""Initializes a NETCONF session over SSH, and creates a connected `Manager` instance. *host* |
|
64 |
must be specified, all the other arguments are optional and depend on the kind of host key |
|
65 |
verification and user authentication you want to complete. |
|
59 |
"""Initializes a NETCONF session over SSH, and creates a connected `Manager` instance. *host* must be specified, all the other arguments are optional and depend on the kind of host key verification and user authentication you want to complete. |
|
66 | 60 |
|
67 |
For the purpose of host key verification, on -NIX systems a user's :file:`~/.ssh/known_hosts` |
|
68 |
file is automatically considered. The *unknown_host_cb* argument specifies a callback that will |
|
69 |
be invoked when the server's host key cannot be verified. See |
|
70 |
:func:`~ncclient.transport.ssh.default_unknown_host_cb` for function signature. |
|
61 |
For the purpose of host key verification, on -NIX systems a user's :file:`~/.ssh/known_hosts` file is automatically considered. The *unknown_host_cb* argument specifies a callback that will be invoked when the server's host key cannot be verified. See :func:`~ncclient.transport.ssh.default_unknown_host_cb` for function signature. |
|
71 | 62 |
|
72 | 63 |
First, ``publickey`` authentication is attempted. If a specific *key_filename* is specified, it |
73 | 64 |
will be loaded and authentication attempted using it. If *allow_agent* is :const:`True` and an |
... | ... | |
76 | 67 |
an encrypted key file is encountered, the *password* argument will be used as a decryption |
77 | 68 |
passphrase. |
78 | 69 |
|
79 |
If ``publickey`` authentication fails and the *password* argument has been supplied, |
|
80 |
``password`` / ``keyboard-interactive`` SSH authentication will be attempted. |
|
70 |
If ``publickey`` authentication fails and the *password* argument has been supplied, ``password`` / ``keyboard-interactive`` SSH authentication will be attempted. |
|
81 | 71 |
|
82 | 72 |
:param host: hostname or address on which to connect |
83 | 73 |
:type host: `string` |
... | ... | |
110 | 100 |
:raises: :exc:`~ncclient.transport.AuthenticationError` |
111 | 101 |
|
112 | 102 |
:rtype: `Manager` |
113 |
"""
|
|
103 |
""" |
|
114 | 104 |
session = transport.SSHSession(capabilities.Capabilities(CAPABILITIES)) |
115 | 105 |
session.load_known_hosts() |
116 | 106 |
session.connect(*args, **kwds) |
... | ... | |
119 | 109 |
connect = connect_ssh |
120 | 110 |
"Same as :func:`connect_ssh`, since SSH is the default (and currently, the only) transport." |
121 | 111 |
|
112 |
class OpExecutor(type): |
|
113 |
def __new__(cls, name, bases, attrs): |
|
114 |
def make_wrapper(op_cls): |
|
115 |
def wrapper(self, *args, **kwds): |
|
116 |
return self.execute(op_cls, *args, **kwds) |
|
117 |
wrapper.func_doc = op_cls.request.func_doc |
|
118 |
return wrapper |
|
119 |
for op_name, op_cls in OPERATIONS.iteritems(): |
|
120 |
attrs[op_name] = make_wrapper(op_cls) |
|
121 |
return super(OpExecutor, cls).__new__(cls, name, bases, attrs) |
|
122 |
|
|
122 | 123 |
class Manager(object): |
123 | 124 |
|
125 |
__metaclass__ = OpExecutor |
|
126 |
|
|
127 |
RAISE_NONE = 0 |
|
128 |
RAISE_ERRORS = 1 |
|
129 |
RAISE_ALL = 2 |
|
130 |
|
|
124 | 131 |
def __init__(self, session): |
125 | 132 |
self._session = session |
126 | 133 |
self._async_mode = False |
127 | 134 |
self._timeout = None |
128 |
self._raise_mode = 'all'
|
|
135 |
self._raise_mode = self.RAISE_ALL
|
|
129 | 136 |
|
130 | 137 |
def __enter__(self): |
131 | 138 |
return self |
132 | 139 |
|
133 |
def __exit__(self, *argss):
|
|
140 |
def __exit__(self, *args): |
|
134 | 141 |
self.close_session() |
135 | 142 |
return False |
136 | 143 |
|
137 |
def __getattr__(self, name): |
|
138 |
op = OPERATIONS.get(name, None) |
|
139 |
if op is None: |
|
140 |
raise AttributeError |
|
141 |
else: |
|
142 |
return op(self._session, |
|
143 |
async=self._async_mode, |
|
144 |
timeout=self._timeout, |
|
145 |
raise_mode=self._raise_mode).request |
|
146 |
|
|
144 |
def execute(self, cls, *args, **kwds): |
|
145 |
return cls(self._session, |
|
146 |
async=self._async_mode, |
|
147 |
timeout=self._timeout, |
|
148 |
raise_mode=self._raise_mode).request(*args, **kwds) |
|
149 |
|
|
147 | 150 |
def locked(self, target): |
148 | 151 |
return operations.LockContext(self._session, target) |
149 |
|
|
152 |
|
|
150 | 153 |
@property |
151 | 154 |
def client_capabilities(self): |
152 | 155 |
return self._session._client_capabilities |
... | ... | |
167 | 170 |
self._async_mode = mode |
168 | 171 |
|
169 | 172 |
def set_raise_mode(self, mode): |
170 |
assert(choice in ("all", "errors", "none"))
|
|
173 |
assert(choice in (self.RAISE_NONE, self.RAISE_ERRORS, self.RAISE_ALL))
|
|
171 | 174 |
self._raise_mode = mode |
172 | 175 |
|
173 | 176 |
async_mode = property(fget=lambda self: self._async_mode, fset=set_async_mode) |
b/ncclient/operations/edit.py | ||
---|---|---|
27 | 27 |
|
28 | 28 |
"*<edit-config>* RPC" |
29 | 29 |
|
30 |
def request(self, target, config, default_operation=None, test_option=None, |
|
31 |
error_option=None): |
|
30 |
def request(self, target, config, default_operation=None, test_option=None, error_option=None): |
|
32 | 31 |
node = new_ele("edit-config") |
33 | 32 |
node.append(util.datastore_or_url("target", target, self._assert)) |
34 | 33 |
if error_option is not None: |
... | ... | |
95 | 94 |
self._assert(":confirmed-commit") |
96 | 95 |
sub_ele(node, "confirmed") |
97 | 96 |
if timeout is not None: |
98 |
# TODO check if timeout is a valid integer? |
|
99 | 97 |
sub_ele(node, "confirm-timeout").text = timeout |
100 | 98 |
return self._request(node) |
101 | 99 |
|
b/ncclient/operations/flowmon.py | ||
---|---|---|
26 | 26 |
|
27 | 27 |
DEPENDS = ["urn:liberouter:params:netconf:capability:power-control:1.0"] |
28 | 28 |
|
29 |
def request(self, target):
|
|
29 |
def request(self): |
|
30 | 30 |
return self._request(new_ele(qualify("poweroff-machine", PC_URN))) |
31 | 31 |
|
32 | 32 |
class RebootMachine(RPC): |
... | ... | |
35 | 35 |
|
36 | 36 |
DEPENDS = ["urn:liberouter:params:netconf:capability:power-control:1.0"] |
37 | 37 |
|
38 |
def request(self, target):
|
|
38 |
def request(self): |
|
39 | 39 |
return self._request(new_ele(qualify("reboot-machine", PC_URN))) |
b/ncclient/operations/lock.py | ||
---|---|---|
28 | 28 |
|
29 | 29 |
def request(self, target): |
30 | 30 |
node = new_ele("lock") |
31 |
sub_ele(sub_ele(node, "target"), "running")
|
|
31 |
sub_ele(sub_ele(node, "target"), target)
|
|
32 | 32 |
return self._request(node) |
33 | 33 |
|
34 | 34 |
|
... | ... | |
38 | 38 |
|
39 | 39 |
def request(self, target): |
40 | 40 |
node = new_ele("unlock") |
41 |
sub_ele(sub_ele(node, "target"), "running")
|
|
41 |
sub_ele(sub_ele(node, "target"), target)
|
|
42 | 42 |
return self._request(node) |
43 | 43 |
|
44 | 44 |
|
b/ncclient/operations/rpc.py | ||
---|---|---|
54 | 54 |
def to_dict(self): |
55 | 55 |
return dict([ (attr[1:], getattr(self, attr)) for attr in RPCError.tag_to_attr.values() ]) |
56 | 56 |
|
57 |
"*rpc-error* element as returned." |
|
58 | 57 |
@property |
59 | 58 |
def xml(self): |
59 |
"*rpc-error* element as returned." |
|
60 | 60 |
return self._raw |
61 | 61 |
|
62 | 62 |
@property |
... | ... | |
123 | 123 |
if error is not None: |
124 | 124 |
for err in root.getiterator(error.tag): |
125 | 125 |
# Process a particular <rpc-error> |
126 |
self._errors.append(ERROR_CLS(err)) |
|
126 |
self._errors.append(self.ERROR_CLS(err)) |
|
127 |
self._parsing_hook(root) |
|
127 | 128 |
self._parsed = True |
129 |
|
|
130 |
def _parsing_hook(self, root): |
|
131 |
pass |
|
128 | 132 |
|
129 | 133 |
@property |
130 | 134 |
def xml(self): |
... | ... | |
264 | 268 |
logger.debug('Async request, returning %r', self) |
265 | 269 |
return self |
266 | 270 |
else: |
267 |
logger.debug('Sync request, will wait for timeout=%r' % |
|
268 |
self._timeout) |
|
271 |
logger.debug('Sync request, will wait for timeout=%r' % self._timeout) |
|
269 | 272 |
self._event.wait(self._timeout) |
270 | 273 |
if self._event.isSet(): |
271 | 274 |
if self._error: |
... | ... | |
283 | 286 |
else: |
284 | 287 |
raise TimeoutExpiredError |
285 | 288 |
|
286 |
def request(self, *args, **kwds):
|
|
289 |
def request(self): |
|
287 | 290 |
"""Subclasses must implement this method. Typically only the request needs to be built as an |
288 | 291 |
`~xml.etree.ElementTree.Element` and everything else can be handed off to |
289 | 292 |
:meth:`_request`.""" |
... | ... | |
294 | 297 |
server, before making a request that requires it. A :exc:`MissingCapabilityError` will be |
295 | 298 |
raised if the capability is not available.""" |
296 | 299 |
if capability not in self._session.server_capabilities: |
297 |
raise MissingCapabilityError('Server does not support [%s]' % |
|
298 |
capability) |
|
300 |
raise MissingCapabilityError('Server does not support [%s]' % capability) |
|
299 | 301 |
|
300 | 302 |
def deliver_reply(self, raw): |
301 | 303 |
# internal use |
b/ncclient/transport/errors.py | ||
---|---|---|
36 | 36 |
class SSHUnknownHostError(SSHError): |
37 | 37 |
|
38 | 38 |
def __init__(self, host, fingerprint): |
39 |
SSHError.__init__(self, 'Unknown host key [%s] for [%s]' |
|
40 |
% (fingerprint, host)) |
|
39 |
SSHError.__init__(self, 'Unknown host key [%s] for [%s]' % (fingerprint, host)) |
|
41 | 40 |
self.host = host |
42 | 41 |
self.fingerprint = fingerprint |
b/ncclient/xml_.py | ||
---|---|---|
71 | 71 |
return xml if xml.startswith('<?xml') else '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml) |
72 | 72 |
|
73 | 73 |
def to_ele(x): |
74 |
"""Convert XML to `~xml.etree.ElementTree.Element`. If passed an |
|
75 |
`~xml.etree.ElementTree.Element` simply returns that. |
|
74 |
"""Convert XML to `~xml.etree.ElementTree.Element`. If passed an `~xml.etree.ElementTree.Element` simply returns that. |
|
76 | 75 |
|
77 | 76 |
:arg x: the XML document or element |
78 | 77 |
:type x: `string` or `~xml.etree.ElementTree.Element` |
Also available in: Unified diff