Revision 8b4b9936
/dev/null | ||
---|---|---|
1 |
# Copyright 2009 Shikhar Bhushan |
|
2 |
# |
|
3 |
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 |
# you may not use this file except in compliance with the License. |
|
5 |
# You may obtain a copy of the License at |
|
6 |
# |
|
7 |
# http://www.apache.org/licenses/LICENSE-2.0 |
|
8 |
# |
|
9 |
# Unless required by applicable law or agreed to in writing, software |
|
10 |
# distributed under the License is distributed on an "AS IS" BASIS, |
|
11 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 |
# See the License for the specific language governing permissions and |
|
13 |
# limitations under the License. |
|
14 |
|
|
15 |
class Capabilities: |
|
16 |
|
|
17 |
def __init__(self, capabilities=None): |
|
18 |
self._dict = {} |
|
19 |
if isinstance(capabilities, dict): |
|
20 |
self._dict = capabilities |
|
21 |
elif isinstance(capabilities, list): |
|
22 |
for uri in capabilities: |
|
23 |
self._dict[uri] = Capabilities.guess_shorthand(uri) |
|
24 |
|
|
25 |
def __contains__(self, key): |
|
26 |
return ( key in self._dict ) or ( key in self._dict.values() ) |
|
27 |
|
|
28 |
def __iter__(self): |
|
29 |
return self._dict.keys().__iter__() |
|
30 |
|
|
31 |
def __repr__(self): |
|
32 |
return repr(self._dict.keys()) |
|
33 |
|
|
34 |
def add(self, uri, shorthand=None): |
|
35 |
if shorthand is None: |
|
36 |
shorthand = Capabilities.guess_shorthand(uri) |
|
37 |
self._dict[uri] = shorthand |
|
38 |
|
|
39 |
set = add |
|
40 |
|
|
41 |
def remove(self, key): |
|
42 |
if key in self._dict: |
|
43 |
del self._dict[key] |
|
44 |
else: |
|
45 |
for uri in self._dict: |
|
46 |
if self._dict[uri] == key: |
|
47 |
del self._dict[uri] |
|
48 |
break |
|
49 |
|
|
50 |
@staticmethod |
|
51 |
def guess_shorthand(uri): |
|
52 |
if uri.startswith('urn:ietf:params:netconf:capability:'): |
|
53 |
return (':' + uri.split(':')[5]) |
|
54 |
|
|
55 |
|
|
56 |
CAPABILITIES = Capabilities([ |
|
57 |
'urn:ietf:params:netconf:base:1.0', |
|
58 |
'urn:ietf:params:netconf:capability:writable-running:1.0', |
|
59 |
'urn:ietf:params:netconf:capability:candidate:1.0', |
|
60 |
'urn:ietf:params:netconf:capability:confirmed-commit:1.0', |
|
61 |
'urn:ietf:params:netconf:capability:rollback-on-error:1.0', |
|
62 |
'urn:ietf:params:netconf:capability:startup:1.0', |
|
63 |
'urn:ietf:params:netconf:capability:url:1.0', |
|
64 |
'urn:ietf:params:netconf:capability:validate:1.0', |
|
65 |
'urn:ietf:params:netconf:capability:xpath:1.0', |
|
66 |
'urn:ietf:params:netconf:capability:notification:1.0', |
|
67 |
'urn:ietf:params:netconf:capability:interleave:1.0' |
|
68 |
]) |
|
69 |
|
|
70 |
if __name__ == "__main__": |
|
71 |
assert(':validate' in CAPABILITIES) # test __contains__ |
b/ncclient/content/__init__.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
'This module serves as an XML abstraction layer' |
|
15 |
'This module serves as an XML abstraction layer' |
|
16 |
|
|
17 |
import logging |
|
18 |
logger = logging.getLogger('ncclient.content') |
b/ncclient/content/builders.py | ||
---|---|---|
23 | 23 |
''' |
24 | 24 |
|
25 | 25 |
def __init__(self, spec): |
26 |
self._root = Builder.build(spec) |
|
26 |
self._root = TreeBuilder.build(spec)
|
|
27 | 27 |
|
28 | 28 |
def to_string(self, encoding='utf-8'): |
29 | 29 |
return ET.tostring(self._root, encoding) |
... | ... | |
39 | 39 |
ele = ET.Element(spec.get('tag'), spec.get('attributes', {})) |
40 | 40 |
ele.text = spec.get('text', '') |
41 | 41 |
for child in spec.get('children', []): |
42 |
ele.append(Builder.build(child)) |
|
42 |
ele.append(TreeBuilder.build(child))
|
|
43 | 43 |
return ele |
44 | 44 |
elif spec.has_key('comment'): |
45 | 45 |
return ET.Comment(spec.get('comment')) |
... | ... | |
53 | 53 |
def build(capabilities, encoding='utf-8'): |
54 | 54 |
children = [{'tag': 'capability', 'text': uri } for uri in capabilities] |
55 | 55 |
spec = { |
56 |
'tag': _('hello', Hello.NS),
|
|
56 |
'tag': _('hello', BASE_NS),
|
|
57 | 57 |
'children': [{ |
58 | 58 |
'tag': 'capabilities', |
59 | 59 |
'children': children |
... | ... | |
65 | 65 |
class RPCBuilder: |
66 | 66 |
|
67 | 67 |
@staticmethod |
68 |
def build(msgid, op, encoding='utf-8'): |
|
69 |
if isinstance(opspec, basestring): |
|
70 |
return build_from_string(msgid, op, encoding) |
|
71 |
else: |
|
72 |
return build_from_spec(msgid, op, encoding) |
|
73 |
|
|
74 |
@staticmethod |
|
68 | 75 |
def build_from_spec(msgid, opspec, encoding='utf-8'): |
69 | 76 |
if isinstance(opspec, dict): |
70 | 77 |
opspec = [opspec] |
71 | 78 |
return TreeBuilder({ |
72 |
'tag': _('rpc', RPC.NS),
|
|
79 |
'tag': _('rpc', BASE_NS),
|
|
73 | 80 |
'attributes': {'message-id': msgid}, |
74 | 81 |
'children': opspec |
75 | 82 |
}).to_string(encoding) |
b/ncclient/content/common.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0' |
|
16 |
|
|
15 | 17 |
def qualify(tag, namespace=None): |
16 | 18 |
'Returns qualified name of form `{namespace}tag`' |
17 | 19 |
if namespace is None: |
18 | 20 |
return tag |
19 | 21 |
else: |
20 | 22 |
return '{%s}%s' % (namespace, tag) |
21 |
|
|
22 |
BASE_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0' |
b/ncclient/content/parsers.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
from xml.etree import cElementTree as ElementTree
|
|
15 |
from xml.etree import cElementTree as ET
|
|
16 | 16 |
|
17 | 17 |
from common import BASE_NS |
18 | 18 |
from common import qualify as _ |
b/ncclient/operations/__init__.py | ||
---|---|---|
14 | 14 |
|
15 | 15 |
'NETCONF Remote Procedure Calls (RPC) and protocol operations' |
16 | 16 |
|
17 |
from ncclient import content |
|
18 |
from ncclient.capabilities import CAPABILITIES |
|
19 |
from rpc import RPC, RPCReply, RPCError |
|
17 |
import logging |
|
18 |
logger = logging.getLogger('ncclient.operations') |
|
20 | 19 |
|
21 |
from retrieve import Get, GetConfig |
|
22 |
from edit import EditConfig, DeleteConfig |
|
23 |
from session import CloseSession, KillSession |
|
24 |
from lock import Lock, Unlock |
|
25 |
from notification import CreateSubscription |
|
26 |
|
|
27 |
__all__ = [ |
|
28 |
'Get', |
|
29 |
'GetConfig', |
|
30 |
'EditConfig', |
|
31 |
'DeleteConfig', |
|
32 |
'Lock', |
|
33 |
'Unlock', |
|
34 |
'CloseSession', |
|
35 |
'KillSession', |
|
36 |
'CreateSubscription', |
|
37 |
] |
|
20 |
#from ncclient.session import CAPABILITIES |
|
21 |
# |
|
22 |
#from retrieve import Get, GetConfig |
|
23 |
#from edit import EditConfig, DeleteConfig |
|
24 |
#from session import CloseSession, KillSession |
|
25 |
#from lock import Lock, Unlock |
|
26 |
#from notification import CreateSubscription |
|
27 |
# |
|
28 |
#__all__ = [ |
|
29 |
# 'Get', |
|
30 |
# 'GetConfig', |
|
31 |
# 'EditConfig', |
|
32 |
# 'DeleteConfig', |
|
33 |
# 'Lock', |
|
34 |
# 'Unlock', |
|
35 |
# 'CloseSession', |
|
36 |
# 'KillSession', |
|
37 |
# 'CreateSubscription', |
|
38 |
# ] |
b/ncclient/operations/listener.py | ||
---|---|---|
1 |
#!/usr/bin/env python |
|
1 |
# Copyright 2009 Shikhar Bhushan |
|
2 |
# |
|
3 |
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 |
# you may not use this file except in compliance with the License. |
|
5 |
# You may obtain a copy of the License at |
|
6 |
# |
|
7 |
# http://www.apache.org/licenses/LICENSE-2.0 |
|
8 |
# |
|
9 |
# Unless required by applicable law or agreed to in writing, software |
|
10 |
# distributed under the License is distributed on an "AS IS" BASIS, |
|
11 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 |
# See the License for the specific language governing permissions and |
|
13 |
# limitations under the License. |
|
14 |
|
|
15 |
from weakref import WeakValueDictionary |
|
16 |
|
|
17 |
from ncclient.content.parsers import RootParser |
|
2 | 18 |
|
3 | 19 |
_listeners = WeakValueDictionary() |
4 | 20 |
|
b/ncclient/operations/rpc.py | ||
---|---|---|
17 | 17 |
from threading import Event, Lock |
18 | 18 |
from uuid import uuid1 |
19 | 19 |
|
20 |
_listeners = WeakValueDictionary() |
|
21 |
|
|
22 |
def get_listener(session): |
|
23 |
try: |
|
24 |
return _listeners[session] |
|
25 |
except KeyError: |
|
26 |
_listeners[session] = MessageListener() |
|
27 |
return _listeners[session] |
|
20 |
from listener import get_listener |
|
21 |
from ncclient.content.builders import RPCBuilder |
|
28 | 22 |
|
29 | 23 |
class RPC: |
30 | 24 |
|
... | ... | |
36 | 30 |
self._reply_event = Event() |
37 | 31 |
self.listener.register(self._id, self) |
38 | 32 |
session.add_listener(self.listener) |
39 |
|
|
33 |
|
|
40 | 34 |
def _response_cb(self, reply): |
41 | 35 |
self._reply = reply |
42 | 36 |
self._event.set() |
43 | 37 |
|
44 |
def _do_request(self, operation): |
|
45 |
'operation is xml string' |
|
46 |
self._session.send(content.RPC.make(self._id, operation)) |
|
38 |
def _do_request(self, op): |
|
39 |
self._session.send(RPCBuilder.build(self._id, op)) |
|
47 | 40 |
if not self._async: |
48 | 41 |
self._reply_event.wait() |
49 | 42 |
return self._reply |
... | ... | |
85 | 78 |
pass |
86 | 79 |
|
87 | 80 |
|
88 |
class MessageListener: |
|
89 |
|
|
90 |
def __init__(self): |
|
91 |
# {message-id: RPC} |
|
92 |
self._rpc = WeakValueDictionary() |
|
93 |
# if the session gets closed by remote endpoint, |
|
94 |
# need to know if it is an error event or was requested through |
|
95 |
# a NETCONF operation i.e. CloseSession |
|
96 |
self._expecting_close = False |
|
97 |
# other recognized names and behavior on receiving them |
|
98 |
self._recognized = [] |
|
99 |
|
|
100 |
def __str__(self): |
|
101 |
return 'MessageListener' |
|
102 |
|
|
103 |
def expect_close(self): |
|
104 |
self._expecting_close = True |
|
105 |
|
|
106 |
def register(self, id, op): |
|
107 |
self._id2rpc[id] = op |
|
108 |
|
|
109 |
### Events |
|
110 |
|
|
111 |
def reply(self, raw): |
|
112 |
pass |
|
113 |
|
|
114 |
def error(self, err): |
|
115 |
from ncclient.session.session import SessionCloseError |
|
116 |
if err is SessionCloseError: |
|
117 |
logger.debug('session closed by remote endpoint, expecting_close=%s' % |
|
118 |
self._expecting_close) |
|
119 |
if not self._expecting_close: |
|
120 |
raise err |
|
121 |
|
b/ncclient/session/__init__.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
from session import SessionError, SessionCloseError |
|
15 |
import logging |
|
16 |
logger = logging.getLogger('ncclient.session') |
|
17 |
|
|
18 |
from session import DebugListener, SessionError, SessionCloseError |
|
16 | 19 |
from ssh import SSHSession |
20 |
from capabilities import CAPABILITIES, Capabilities |
|
17 | 21 |
|
18 | 22 |
__all__ = [ |
23 |
'DebugListener' |
|
24 |
'Session' |
|
19 | 25 |
'SSHSession', |
20 | 26 |
'SessionError', |
21 | 27 |
'SessionCloseError', |
28 |
'Capabilities', |
|
29 |
'CAPABILITIES' |
|
22 | 30 |
] |
b/ncclient/session/capabilities.py | ||
---|---|---|
1 |
# Copyright 2009 Shikhar Bhushan |
|
2 |
# |
|
3 |
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 |
# you may not use this file except in compliance with the License. |
|
5 |
# You may obtain a copy of the License at |
|
6 |
# |
|
7 |
# http://www.apache.org/licenses/LICENSE-2.0 |
|
8 |
# |
|
9 |
# Unless required by applicable law or agreed to in writing, software |
|
10 |
# distributed under the License is distributed on an "AS IS" BASIS, |
|
11 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 |
# See the License for the specific language governing permissions and |
|
13 |
# limitations under the License. |
|
14 |
|
|
15 |
class Capabilities: |
|
16 |
|
|
17 |
def __init__(self, capabilities=None): |
|
18 |
self._dict = {} |
|
19 |
if isinstance(capabilities, dict): |
|
20 |
self._dict = capabilities |
|
21 |
elif isinstance(capabilities, list): |
|
22 |
for uri in capabilities: |
|
23 |
self._dict[uri] = Capabilities.guess_shorthand(uri) |
|
24 |
|
|
25 |
def __contains__(self, key): |
|
26 |
return ( key in self._dict ) or ( key in self._dict.values() ) |
|
27 |
|
|
28 |
def __iter__(self): |
|
29 |
return self._dict.keys().__iter__() |
|
30 |
|
|
31 |
def __repr__(self): |
|
32 |
return repr(self._dict.keys()) |
|
33 |
|
|
34 |
def add(self, uri, shorthand=None): |
|
35 |
if shorthand is None: |
|
36 |
shorthand = Capabilities.guess_shorthand(uri) |
|
37 |
self._dict[uri] = shorthand |
|
38 |
|
|
39 |
set = add |
|
40 |
|
|
41 |
def remove(self, key): |
|
42 |
if key in self._dict: |
|
43 |
del self._dict[key] |
|
44 |
else: |
|
45 |
for uri in self._dict: |
|
46 |
if self._dict[uri] == key: |
|
47 |
del self._dict[uri] |
|
48 |
break |
|
49 |
|
|
50 |
@staticmethod |
|
51 |
def guess_shorthand(uri): |
|
52 |
if uri.startswith('urn:ietf:params:netconf:capability:'): |
|
53 |
return (':' + uri.split(':')[5]) |
|
54 |
|
|
55 |
|
|
56 |
CAPABILITIES = Capabilities([ |
|
57 |
'urn:ietf:params:netconf:base:1.0', |
|
58 |
'urn:ietf:params:netconf:capability:writable-running:1.0', |
|
59 |
'urn:ietf:params:netconf:capability:candidate:1.0', |
|
60 |
'urn:ietf:params:netconf:capability:confirmed-commit:1.0', |
|
61 |
'urn:ietf:params:netconf:capability:rollback-on-error:1.0', |
|
62 |
'urn:ietf:params:netconf:capability:startup:1.0', |
|
63 |
'urn:ietf:params:netconf:capability:url:1.0', |
|
64 |
'urn:ietf:params:netconf:capability:validate:1.0', |
|
65 |
'urn:ietf:params:netconf:capability:xpath:1.0', |
|
66 |
'urn:ietf:params:netconf:capability:notification:1.0', |
|
67 |
'urn:ietf:params:netconf:capability:interleave:1.0' |
|
68 |
]) |
|
69 |
|
|
70 |
if __name__ == "__main__": |
|
71 |
assert(':validate' in CAPABILITIES) # test __contains__ |
b/ncclient/session/session.py | ||
---|---|---|
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 | 15 |
import logging |
16 |
from threading import Thread, Event |
|
16 |
from threading import Thread, Lock, Event
|
|
17 | 17 |
from Queue import Queue |
18 | 18 |
|
19 | 19 |
from capabilities import Capabilities, CAPABILITIES |
20 | 20 |
|
21 |
logger = logging.getLogger('ncclient.session.session')
|
|
21 |
logger = logging.getLogger('ncclient.session') |
|
22 | 22 |
|
23 | 23 |
class SessionError(Exception): |
24 | 24 |
|
... | ... | |
26 | 26 |
|
27 | 27 |
class SessionCloseError(SessionError): |
28 | 28 |
|
29 |
def __str__(self): |
|
30 |
return 'RECEIVED: %s | UNSENT: %s' % (self._in_buf, self._out_buf) |
|
31 |
|
|
32 | 29 |
def __init__(self, in_buf, out_buf=None): |
33 | 30 |
SessionError.__init__(self) |
34 | 31 |
self._in_buf, self._out_buf = in_buf, out_buf |
35 |
|
|
32 |
|
|
33 |
def __str__(self): |
|
34 |
msg = 'Session closed by remote endpoint.' |
|
35 |
if self._in_buf: |
|
36 |
msg += '\nIN_BUFFER: %s' % self._in_buf |
|
37 |
if self._out_buf: |
|
38 |
msg += '\nOUT_BUFFER: %s' % self._out_buf |
|
39 |
return msg |
|
40 |
|
|
36 | 41 |
class Session(Thread): |
37 | 42 |
|
38 | 43 |
def __init__(self): |
... | ... | |
42 | 47 |
self._id = None # session-id |
43 | 48 |
self._q = Queue() |
44 | 49 |
self._connected = False # to be set/cleared by subclass implementation |
45 |
self._listeners = set(listeners)
|
|
50 |
self._listeners = set([])
|
|
46 | 51 |
self._lock = Lock() |
47 | 52 |
|
48 | 53 |
def _post_connect(self): |
... | ... | |
67 | 72 |
proceed.wait() |
68 | 73 |
# received hello message or an error happened |
69 | 74 |
self.remove_listener(listener) |
70 |
if self._error:
|
|
75 |
if error: |
|
71 | 76 |
self._close() |
72 | 77 |
raise self._error |
73 | 78 |
|
... | ... | |
135 | 140 |
class HelloListener: |
136 | 141 |
|
137 | 142 |
def __init__(self, init_cb, error_cb): |
138 |
self._init_cb, self._error_cb = reply_cb, error_cb
|
|
143 |
self._init_cb, self._error_cb = init_cb, error_cb
|
|
139 | 144 |
|
140 | 145 |
def __str__(self): |
141 | 146 |
return 'HelloListener' |
... | ... | |
153 | 158 |
|
154 | 159 |
def error(self, err): |
155 | 160 |
self._error_cb(err) |
161 |
|
|
162 |
|
|
163 |
class DebugListener: |
|
164 |
|
|
165 |
def __str__(self): |
|
166 |
return 'DebugListener' |
|
167 |
|
|
168 |
def reply(self, raw): |
|
169 |
logger.debug('DebugListener:reply:%s' % raw) |
|
170 |
|
|
171 |
def error(self, err): |
|
172 |
logger.debug('DebugListener:error:%s' % err) |
b/ncclient/session/ssh.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
import logging |
|
16 | 15 |
from cStringIO import StringIO |
17 | 16 |
from os import SEEK_CUR |
18 | 17 |
import socket |
19 | 18 |
|
20 | 19 |
import paramiko |
21 | 20 |
|
21 |
from . import logger |
|
22 | 22 |
from session import Session, SessionError, SessionCloseError |
23 | 23 |
|
24 |
logger = logging.getLogger('ncclient.ssh') |
|
25 |
|
|
26 | 24 |
BUF_SIZE = 4096 |
27 | 25 |
MSG_DELIM = ']]>]]>' |
28 | 26 |
|
Also available in: Unified diff