Revision d6688264
b/ncclient/__init__.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
''' |
|
16 |
NOTES |
|
17 |
===== |
|
18 |
|
|
19 |
- operations complete |
|
20 |
- make operational again |
|
21 |
- LockContext |
|
22 |
- op specfic reply objects |
|
23 |
- manager testing and augmenting |
|
24 |
parse into dicts?? |
|
25 |
|
|
26 |
''' |
|
27 |
|
|
15 | 28 |
import sys |
16 | 29 |
|
17 | 30 |
if sys.version_info < (2, 6): |
b/ncclient/capabilities.py | ||
---|---|---|
67 | 67 |
if uri.startswith('urn:ietf:params:netconf:capability:'): |
68 | 68 |
return (':' + uri.split(':')[5]) |
69 | 69 |
|
70 |
|
|
71 | 70 |
CAPABILITIES = Capabilities([ |
72 |
'urn:ietf:params:netconf:base:1.0', # TODO |
|
73 |
'urn:ietf:params:netconf:capability:writable-running:1.0', # TODO |
|
74 |
'urn:ietf:params:netconf:capability:candidate:1.0', # TODO |
|
75 |
'urn:ietf:params:netconf:capability:confirmed-commit:1.0', # TODO |
|
76 |
'urn:ietf:params:netconf:capability:rollback-on-error:1.0', # TODO |
|
77 |
'urn:ietf:params:netconf:capability:startup:1.0', # TODO |
|
78 |
'urn:ietf:params:netconf:capability:url:1.0', # TODO |
|
79 |
'urn:ietf:params:netconf:capability:validate:1.0', # TODO |
|
80 |
'urn:ietf:params:netconf:capability:xpath:1.0', # TODO |
|
81 |
'urn:ietf:params:netconf:capability:notification:1.0', # TODO |
|
82 |
'urn:ietf:params:netconf:capability:interleave:1.0' # TODO |
|
83 |
]) |
|
71 |
'urn:ietf:params:netconf:base:1.0', |
|
72 |
'urn:ietf:params:netconf:capability:writable-running:1.0', |
|
73 |
'urn:ietf:params:netconf:capability:candidate:1.0', |
|
74 |
'urn:ietf:params:netconf:capability:confirmed-commit:1.0', |
|
75 |
'urn:ietf:params:netconf:capability:rollback-on-error:1.0', |
|
76 |
'urn:ietf:params:netconf:capability:startup:1.0', |
|
77 |
'urn:ietf:params:netconf:capability:url:1.0', |
|
78 |
'urn:ietf:params:netconf:capability:validate:1.0', |
|
79 |
'urn:ietf:params:netconf:capability:xpath:1.0', |
|
80 |
'urn:ietf:params:netconf:capability:notification:1.0', |
|
81 |
'urn:ietf:params:netconf:capability:interleave:1.0' |
|
82 |
]) |
b/ncclient/content.py | ||
---|---|---|
56 | 56 |
def to_string(self, encoding='utf-8'): |
57 | 57 |
"TODO: docstring" |
58 | 58 |
xml = ET.tostring(self._root, encoding) |
59 |
# some etree versions don't always include xml decl e.g. with utf-8
|
|
59 |
# some etree versions don't include xml decl with utf-8
|
|
60 | 60 |
# this is a problem with some devices |
61 |
if not xml.startswith('<?xml'):
|
|
62 |
return ((u'<?xml version="1.0" encoding="%s"?>'
|
|
61 |
if encoding == 'utf-8':
|
|
62 |
return ((u'<?xml version="1.0" encoding="utf-8"?>'
|
|
63 | 63 |
% encoding).encode(encoding) + xml) |
64 | 64 |
else: |
65 | 65 |
return xml |
... | ... | |
72 | 72 |
@staticmethod |
73 | 73 |
def build(spec): |
74 | 74 |
"TODO: docstring" |
75 |
if 'tag' in spec: |
|
75 |
if ET.iselement(spec): |
|
76 |
return spec |
|
77 |
elif isinstance(spec, basestring): |
|
78 |
return ET.XML(spec) |
|
79 |
# assume isinstance(spec, dict) |
|
80 |
elif 'tag' in spec: |
|
76 | 81 |
ele = ET.Element(spec.get('tag'), spec.get('attributes', {})) |
77 |
ele.text = spec.get('text', '')
|
|
82 |
ele.text = str(spec.get('text', ''))
|
|
78 | 83 |
children = spec.get('children', []) |
79 |
if isinstance(children, dict): |
|
80 |
children = [children] |
|
84 |
if isinstance(children, dict): children = [children] |
|
81 | 85 |
for child in children: |
82 |
ele.append(TreeBuilder.build(child))
|
|
86 |
ET.SubElement(ele, TreeBuilder.build(child))
|
|
83 | 87 |
return ele |
84 |
elif 'xml' in spec: |
|
85 |
return ET.XML(spec['xml']) |
|
86 | 88 |
elif 'comment' in spec: |
87 | 89 |
return ET.Comment(spec.get('comment')) |
88 | 90 |
else: |
b/ncclient/glue.py | ||
---|---|---|
15 | 15 |
"TODO: docstring" |
16 | 16 |
|
17 | 17 |
from cStringIO import StringIO |
18 |
from threading import Thread |
|
19 |
from Queue import Queue |
|
20 |
from threading import Lock |
|
18 |
from threading import Thread, Lock |
|
21 | 19 |
from xml.etree import cElementTree as ET |
22 | 20 |
|
23 | 21 |
import logging |
24 | 22 |
logger = logging.getLogger('ncclient.glue') |
25 | 23 |
|
24 |
|
|
26 | 25 |
def parse_root(raw): |
27 | 26 |
'''Parse the top-level element from a string representing an XML document. |
28 | 27 |
|
... | ... | |
30 | 29 |
the qualified name of the root element and `attributes` is an |
31 | 30 |
`{attribute: value}` dictionary. |
32 | 31 |
''' |
33 |
fp = StringIO(raw)
|
|
32 |
fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
|
|
34 | 33 |
for event, element in ET.iterparse(fp, events=('start',)): |
35 | 34 |
return (element.tag, element.attrib) |
36 | 35 |
|
... | ... | |
42 | 41 |
def __init__(self): |
43 | 42 |
"TODO: docstring" |
44 | 43 |
Thread.__init__(self) |
45 |
self._q = Queue() |
|
46 | 44 |
self._listeners = set() # TODO(?) weakref |
47 | 45 |
self._lock = Lock() |
48 | 46 |
|
49 | 47 |
def _dispatch_message(self, raw): |
50 | 48 |
"TODO: docstring" |
51 |
root = parse_root(raw) |
|
49 |
try: |
|
50 |
root = parse_root(raw) |
|
51 |
except Exception as e: |
|
52 |
logger.error('error parsing dispatch message: %s' % e) |
|
53 |
return |
|
52 | 54 |
with self._lock: |
53 | 55 |
listeners = list(self._listeners) |
54 | 56 |
for l in listeners: |
... | ... | |
89 | 91 |
for listener in self._listeners: |
90 | 92 |
if isinstance(listener, cls): |
91 | 93 |
return listener |
92 |
|
|
93 |
def send(self, message): |
|
94 |
"TODO: docstring" |
|
95 |
logger.debug('queueing %s' % message) |
|
96 |
self._q.put(message) |
|
97 | 94 |
|
98 | 95 |
|
99 | 96 |
class Listener(object): |
b/ncclient/manager.py | ||
---|---|---|
44 | 44 |
self._session.connect(*args, **kwds) |
45 | 45 |
|
46 | 46 |
def __getattr__(self, name): |
47 |
name = name.replace('_', '-') |
|
47 | 48 |
if name in OPERATIONS: |
48 | 49 |
return OPERATIONS[name](self._session).request |
49 | 50 |
else: |
50 | 51 |
raise AttributeError |
52 |
|
|
53 |
def get(self, *args, **kwds): |
|
54 |
g = operations.Get(self._session) |
|
55 |
reply = g.request(*args, **kwds) |
|
56 |
if reply.errors: |
|
57 |
raise RPCError(reply.errors) |
|
58 |
else: |
|
59 |
return reply.data |
|
60 |
|
|
61 |
def get_config(self, *args, **kwds): |
|
62 |
gc = operations.GetConfig(self._session) |
|
63 |
reply = gc.request(*args, **kwds) |
|
64 |
if reply.errors: |
|
65 |
raise RPCError(reply.errors) |
|
66 |
else: |
|
67 |
return reply.data |
|
68 |
|
|
69 |
def locked(self, target='running'): |
|
70 |
return LockContext(self._session, target) |
b/ncclient/operations/edit.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
from ncclient.capabilities import URI |
|
15 | 16 |
from ncclient.rpc import RPC |
16 | 17 |
|
17 |
# TODO |
|
18 |
|
|
19 |
|
|
20 |
''' |
|
21 |
notes |
|
22 |
-> editconfig and copyconfig <running> target depends on :writable-running |
|
23 |
-> |
|
24 |
|
|
25 |
''' |
|
18 |
import util |
|
26 | 19 |
|
27 | 20 |
class EditConfig(RPC): |
28 | 21 |
|
... | ... | |
36 | 29 |
def request(self): |
37 | 30 |
pass |
38 | 31 |
|
39 |
class CopyConfig(RPC): |
|
32 |
|
|
33 |
class DeleteConfig(RPC): # x |
|
40 | 34 |
|
41 | 35 |
SPEC = { |
42 |
|
|
36 |
'tag': 'delete-config', |
|
37 |
'children': [ { 'tag': 'target', 'children': None } ] |
|
43 | 38 |
} |
44 | 39 |
|
45 |
def request(self): |
|
46 |
pass |
|
40 |
def request(self, target=None, target_url=None): |
|
41 |
spec = DeleteConfig.SPEC.copy() |
|
42 |
spec['children'][0]['children'] = util.store_or_url(target, target_url) |
|
43 |
return self._request(spec) |
|
44 |
|
|
47 | 45 |
|
48 |
class DeleteConfig(RPC):
|
|
46 |
class CopyConfig(RPC): # x
|
|
49 | 47 |
|
50 | 48 |
SPEC = { |
51 |
'tag': 'delete-config',
|
|
49 |
'tag': 'copy-config',
|
|
52 | 50 |
'children': [ |
53 |
'tag': 'target',
|
|
54 |
'children': {'tag': None }
|
|
51 |
{ 'tag': 'source', 'children': {'tag': None } },
|
|
52 |
{ 'tag': 'target', 'children': {'tag': None } }
|
|
55 | 53 |
] |
56 | 54 |
} |
57 | 55 |
|
58 |
def request(self, target=None, targeturl=None): |
|
59 |
spec = deepcopy(DeleteConfig.SPEC) |
|
60 |
|
|
56 |
def request(self, source=None, source_url=None, target=None, target_url=None): |
|
57 |
spec = CopyConfig.SPEC.copy() |
|
58 |
spec['children'][0]['children'] = util.store_or_url(source, source_url) |
|
59 |
spec['children'][1]['children'] = util.store_or_url(target, target_url) |
|
60 |
return self._request(spec) |
|
61 | 61 |
|
62 |
class Validate(RPC): |
|
62 |
|
|
63 |
class Validate(RPC): # xxxxx |
|
63 | 64 |
|
64 |
DEPENDS = ['urn:ietf:params:netconf:capability:validate:1.0'] |
|
65 |
SPEC = {} |
|
65 |
DEPENDS = [':validate'] |
|
66 | 66 |
|
67 |
def request(self): |
|
68 |
pass |
|
69 |
|
|
67 |
SPEC = { |
|
68 |
'tag': 'validate', |
|
69 |
'children': [] |
|
70 |
} |
|
71 |
|
|
72 |
def request(self, source=None, config=None): |
|
73 |
#self.either_or(source, config) |
|
74 |
# |
|
75 |
#if source is None and config is None: |
|
76 |
# raise OperationError('Insufficient parameters') |
|
77 |
#if source is not None and config is not None: |
|
78 |
# raise OperationError('Too many parameters') |
|
79 |
#spec = Validate.SPEC.copy() |
|
80 |
# |
|
81 |
util.one_of(source, capability) |
|
82 |
if source is not None: |
|
83 |
spec['children'].append({ |
|
84 |
'tag': 'source', |
|
85 |
'children': {'tag': source} |
|
86 |
}) |
|
87 |
# |
|
88 |
#else: |
|
89 |
# if isinstance(config, dict): |
|
90 |
# if config['tag'] != 'config': |
|
91 |
# child['tag'] = 'config' |
|
92 |
# child['children'] = config |
|
93 |
# else: |
|
94 |
# child = config |
|
95 |
# elif isinstance(config, Element): |
|
96 |
# pass |
|
97 |
# else: |
|
98 |
# from xml.etree import cElementTree as ET |
|
99 |
# ele = ET.XML(unicode(config)) |
|
100 |
# if __(ele.tag) != 'config': |
|
101 |
# pass |
|
102 |
# else: |
|
103 |
# pass |
|
104 |
# spec['children'].append(child) |
|
105 |
# |
|
106 |
return self._request(spec) |
|
70 | 107 |
|
71 |
class Commit(RPC): |
|
108 |
class Commit(RPC): # x
|
|
72 | 109 |
|
73 |
SPEC = {'tag': 'commit'}
|
|
110 |
DEPENDS = [':candidate']
|
|
74 | 111 |
|
75 |
def request(self): |
|
112 |
SPEC = {'tag': 'commit', 'children': [] } |
|
113 |
|
|
114 |
def _parse_hook(self): |
|
115 |
pass |
|
116 |
|
|
117 |
def request(self, confirmed=False, timeout=None): |
|
118 |
spec = SPEC.copy() |
|
119 |
if confirmed: |
|
120 |
self._assert(':confirmed-commit') |
|
121 |
children = spec['children'] |
|
122 |
children.append({'tag': 'confirmed'}) |
|
123 |
if timeout is not None: |
|
124 |
children.append({ |
|
125 |
'tag': 'confirm-timeout', |
|
126 |
'text': timeout |
|
127 |
}) |
|
76 | 128 |
return self._request(Commit.SPEC) |
77 | 129 |
|
78 | 130 |
|
79 |
class DiscardChanges(RPC): |
|
131 |
class DiscardChanges(RPC): # x |
|
132 |
|
|
133 |
DEPENDS = [':candidate'] |
|
80 | 134 |
|
81 |
DEPENDS = ['urn:ietf:params:netconf:capability:candidate:1.0'] |
|
82 | 135 |
SPEC = {'tag': 'discard-changes'} |
83 | 136 |
|
84 | 137 |
def request(self): |
b/ncclient/operations/lock.py | ||
---|---|---|
20 | 20 |
|
21 | 21 |
# TODO - a context manager around some <target> would be real neat |
22 | 22 |
|
23 |
class Lock(RPC): |
|
23 |
class Lock(RPC): # x
|
|
24 | 24 |
|
25 | 25 |
SPEC = { |
26 | 26 |
'tag': 'lock', |
... | ... | |
31 | 31 |
} |
32 | 32 |
|
33 | 33 |
def request(self, target='running'): |
34 |
if target=='candidate': |
|
35 |
self._assert(':candidate') |
|
34 | 36 |
spec = deepcopy(Lock.SPEC) |
35 | 37 |
spec['children']['children']['tag'] = target |
36 | 38 |
return self._request(spec) |
37 | 39 |
|
38 | 40 |
|
39 |
class Unlock(RPC): |
|
41 |
class Unlock(RPC): # x
|
|
40 | 42 |
|
41 | 43 |
SPEC = { |
42 | 44 |
'tag': 'unlock', |
... | ... | |
47 | 49 |
} |
48 | 50 |
|
49 | 51 |
def request(self, target='running'): |
52 |
if target=='candidate': |
|
53 |
self._assert(':candidate') |
|
50 | 54 |
spec = deepcopy(Unlock.SPEC) |
51 | 55 |
spec['children']['children']['tag'] = target |
52 | 56 |
return self._request(self.spec) |
57 |
|
|
58 |
|
|
59 |
class LockContext: |
|
60 |
|
|
61 |
def __init__(self, session, target='running'): |
|
62 |
self.session = session |
|
63 |
self.target = target |
|
64 |
|
|
65 |
def __enter__(self): |
|
66 |
Lock(self.session).request(self.target) |
|
67 |
return self |
|
68 |
|
|
69 |
def __exit__(self, t, v, tb): |
|
70 |
Unlock(self.session).request(self.target) |
|
71 |
return False |
b/ncclient/operations/retrieve.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
from copy import deepcopy |
|
16 |
|
|
17 |
from ncclient.rpc import RPC |
|
15 |
from ncclient.rpc import RPC, RPCReply |
|
18 | 16 |
|
19 | 17 |
def build_filter(spec, type, criteria): |
20 | 18 |
filter = { |
21 | 19 |
'tag': 'filter', |
22 | 20 |
'attributes': {'type': type} |
23 | 21 |
} |
24 |
if type=='subtree': |
|
25 |
if isinstance(criteria, dict): |
|
26 |
filter['children'] = [criteria] |
|
27 |
else: |
|
28 |
filter['text'] = criteria |
|
29 |
elif type=='xpath': |
|
22 |
if type == 'subtree': |
|
23 |
filter['children'] = [criteria] |
|
24 |
elif type == 'xpath': |
|
30 | 25 |
filter['attributes']['select'] = criteria |
26 |
return filter |
|
31 | 27 |
|
32 |
class Get(RPC): |
|
28 |
class Get(RPC): # xx
|
|
33 | 29 |
|
34 | 30 |
SPEC = { |
35 | 31 |
'tag': 'get', |
36 | 32 |
'children': [] |
37 | 33 |
} |
38 | 34 |
|
35 |
REPLY_CLS = GetReply |
|
36 |
|
|
39 | 37 |
def request(self, filter=None): |
40 |
spec = deepcopy(SPEC)
|
|
38 |
spec = Get.SPEC.copy()
|
|
41 | 39 |
if filter is not None: |
40 |
#if filter[0] == 'xpath': |
|
41 |
# self._assert(':xpath') |
|
42 | 42 |
spec['children'].append(build_filter(*filter)) |
43 | 43 |
return self._request(spec) |
44 | 44 |
|
45 |
class GetReply(RPCReply): |
|
46 |
|
|
47 |
def parse(self): |
|
48 |
RPCReply.parse(self) |
|
45 | 49 |
|
46 |
class GetConfig(RPC): |
|
50 |
class GetConfig(RPC): # xx
|
|
47 | 51 |
|
48 | 52 |
SPEC = { |
49 | 53 |
'tag': 'get-config', |
50 | 54 |
'children': [ { 'tag': 'source', 'children': {'tag': None } } ] |
51 | 55 |
} |
52 | 56 |
|
53 |
def request(self, source='running', filter=None): |
|
54 |
spec = deepcopy(SPEC) |
|
55 |
spec['children'][0]['children']['tag'] = source |
|
57 |
REPLY_CLS = GetConfigReply |
|
58 |
|
|
59 |
def request(self, source=None, source_url=None, filter=None): |
|
60 |
self._one_of(source, source_url) |
|
61 |
spec = GetConfig.SPEC.copy() |
|
62 |
if source is not None: |
|
63 |
spec['children'][0]['children']['tag'] = source |
|
64 |
if source_url is not None: |
|
65 |
#self._assert(':url') |
|
66 |
spec['children'][0]['children']['tag'] = 'url' |
|
67 |
spec['children'][0]['children']['text'] = source_url |
|
56 | 68 |
if filter is not None: |
69 |
#if filter[0] == 'xpath': |
|
70 |
# self._assert(':xpath') |
|
57 | 71 |
spec['children'].append(build_filter(*filter)) |
58 | 72 |
return self._request(spec) |
73 |
|
|
74 |
class GetReply(RPCReply): |
|
75 |
|
|
76 |
def parse(self): |
|
77 |
RPCReply.parse(self) |
b/ncclient/operations/session.py | ||
---|---|---|
15 | 15 |
'Session-related NETCONF operations' |
16 | 16 |
|
17 | 17 |
from ncclient.rpc import RPC |
18 |
from copy import deepcopy |
|
19 | 18 |
|
20 |
class CloseSession(RPC): |
|
19 |
class CloseSession(RPC): # x
|
|
21 | 20 |
|
22 | 21 |
'CloseSession is always synchronous' |
23 | 22 |
|
... | ... | |
32 | 31 |
return self._request(CloseSession.SPEC) |
33 | 32 |
|
34 | 33 |
|
35 |
class KillSession(RPC): |
|
34 |
class KillSession(RPC): # x
|
|
36 | 35 |
|
37 | 36 |
SPEC = { |
38 | 37 |
'tag': 'kill-session', |
39 |
'children': [ { 'tag': 'session-id', 'text': None} ]
|
|
38 |
'children': { 'tag': 'session-id', 'text': None}
|
|
40 | 39 |
} |
41 | 40 |
|
42 | 41 |
def request(self, session_id): |
43 | 42 |
if not isinstance(session_id, basestring): # just making sure... |
44 | 43 |
session_id = str(session_id) |
45 |
spec = deepcopy(SPEC)
|
|
44 |
spec = KillSession.SPEC.copy()
|
|
46 | 45 |
spec['children'][0]['text'] = session_id |
47 | 46 |
return self._request(spec) |
b/ncclient/operations/util.py | ||
---|---|---|
1 |
#!/usr/bin/env python |
|
2 |
|
|
3 |
'boilerplate' |
|
4 |
|
|
5 |
from ncclient import OperationError |
|
6 |
|
|
7 |
class MissingCapabilityError(OperationError): |
|
8 |
pass |
|
9 |
|
|
10 |
def one_of(self, *args): |
|
11 |
for i, arg in enumerate(args): |
|
12 |
if arg is not None: |
|
13 |
for argh in args[i+1:]: |
|
14 |
if argh is not None: |
|
15 |
raise OperationError('Too many parameters') |
|
16 |
else: |
|
17 |
return |
|
18 |
raise OperationError('Insufficient parameters') |
|
19 |
|
|
20 |
|
|
21 |
def assert_capability(key, capabilities): |
|
22 |
if key not in capabilities: |
|
23 |
raise MissingCapabilityError |
|
24 |
|
|
25 |
|
|
26 |
def store_or_url(store, url): |
|
27 |
one_of(store, url) |
|
28 |
node = {} |
|
29 |
if store is not None: |
|
30 |
node['tag'] = store |
|
31 |
else: |
|
32 |
node['tag'] = 'url' |
|
33 |
node['text'] = url |
|
34 |
return node |
b/ncclient/rpc/__init__.py | ||
---|---|---|
15 | 15 |
from rpc import RPC |
16 | 16 |
from reply import RPCReply |
17 | 17 |
|
18 |
class ReplyTimeoutError(Exception): |
|
19 |
pass |
|
18 |
from ncclient import RPCError |
|
19 |
|
|
20 |
class ReplyTimeoutError(RPCError): pass |
|
20 | 21 |
|
21 | 22 |
__all__ = [ |
22 | 23 |
'RPC', |
b/ncclient/rpc/reply.py | ||
---|---|---|
38 | 38 |
if __(root.tag) != 'rpc-reply': |
39 | 39 |
raise ValueError('Root element is not RPC reply') |
40 | 40 |
|
41 |
ok = False |
|
41 | 42 |
# per rfc 4741 an <ok/> tag is sent when there are no errors or warnings |
42 | 43 |
oktags = _('ok') |
43 | 44 |
for oktag in oktags: |
44 | 45 |
if root.find(oktag) is not None: |
45 | 46 |
logger.debug('parsed [%s]' % oktag) |
46 |
self._parsed = True |
|
47 |
return |
|
48 |
|
|
49 |
# create RPCError objects from <rpc-error> elements |
|
50 |
errtags = _('rpc-error') |
|
51 |
for errtag in errtags: |
|
52 |
for err in root.getiterator(errtag): # a particular <rpc-error> |
|
53 |
logger.debug('parsed [%s]' % errtag) |
|
54 |
d = {} |
|
55 |
for err_detail in err.getchildren(): # <error-type> etc.. |
|
56 |
tag = __(err_detail.tag) |
|
57 |
d[tag] = (err_detail.text.strip() if tag != 'error-info' |
|
58 |
else ET.tostring(err_detail, 'utf-8')) |
|
59 |
self._errors.append(RPCError(d)) |
|
60 |
if self._errors: |
|
61 | 47 |
break |
48 |
else: |
|
49 |
# create RPCError objects from <rpc-error> elements |
|
50 |
errtags = _('rpc-error') |
|
51 |
for errtag in errtags: |
|
52 |
for err in root.getiterator(errtag): # a particular <rpc-error> |
|
53 |
logger.debug('parsed [%s]' % errtag) |
|
54 |
d = {} |
|
55 |
for err_detail in err.getchildren(): # <error-type> etc.. |
|
56 |
tag = __(err_detail.tag) |
|
57 |
d[tag] = (err_detail.text.strip() if tag != 'error-info' |
|
58 |
else ET.tostring(err_detail, 'utf-8')) |
|
59 |
self._errors.append(RPCError(d)) |
|
60 |
if self._errors: |
|
61 |
break |
|
62 |
|
|
63 |
if self.ok: |
|
64 |
# TODO: store children in some way... |
|
65 |
pass |
|
62 | 66 |
|
63 | 67 |
self._parsed = True |
64 | 68 |
|
b/ncclient/rpc/rpc.py | ||
---|---|---|
27 | 27 |
import logging |
28 | 28 |
logger = logging.getLogger('ncclient.rpc') |
29 | 29 |
|
30 |
|
|
31 | 30 |
class RPC(object): |
32 | 31 |
|
32 |
DEPENDS = [] |
|
33 |
REPLY_CLS = RPCReply |
|
34 |
|
|
33 | 35 |
def __init__(self, session, async=False, timeout=None): |
34 | 36 |
if not session.can_pipeline: |
35 | 37 |
raise UserWarning('Asynchronous mode not supported for this device/session') |
36 | 38 |
self._session = session |
39 |
try: |
|
40 |
for cap in self.DEPENDS: |
|
41 |
self.assert_capability(cap) |
|
42 |
except AttributeError: |
|
43 |
pass |
|
37 | 44 |
self._async = async |
38 | 45 |
self._timeout = timeout |
39 | 46 |
self._id = uuid1().urn |
... | ... | |
51 | 58 |
} |
52 | 59 |
return TreeBuilder(spec).to_string(encoding) |
53 | 60 |
|
54 |
def _request(self, op, timeout=None):
|
|
61 |
def _request(self, op): |
|
55 | 62 |
req = self._build(op) |
56 | 63 |
self._session.send(req) |
57 | 64 |
if self._async: |
58 | 65 |
return self._reply_event |
59 | 66 |
else: |
60 |
self._reply_event.wait(timeout) |
|
67 |
self._reply_event.wait(self._timeout)
|
|
61 | 68 |
if self._reply_event.isSet(): |
62 | 69 |
self._reply.parse() |
63 | 70 |
return self._reply |
... | ... | |
68 | 75 |
'For subclasses' |
69 | 76 |
pass |
70 | 77 |
|
78 |
def _assert(self, capability): |
|
79 |
if capability not in self._session.server_capabilities: |
|
80 |
raise MissingCapabilityError('Server does not support [%s]' % cap) |
|
81 |
|
|
71 | 82 |
def deliver(self, raw): |
72 |
self._reply = RPCReply(raw)
|
|
83 |
self._reply = self.REPLY_CLS(raw)
|
|
73 | 84 |
self._delivery_hook() |
74 | 85 |
self._reply_event.set() |
75 | 86 |
|
b/ncclient/transport/session.py | ||
---|---|---|
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 | 15 |
from threading import Event |
16 |
from Queue import Queue |
|
16 | 17 |
|
17 | 18 |
from ncclient.capabilities import Capabilities, CAPABILITIES |
18 | 19 |
from ncclient.glue import Subject |
... | ... | |
30 | 31 |
"Subclass constructor should call this" |
31 | 32 |
Subject.__init__(self) |
32 | 33 |
self.setName('session') |
34 |
self._q = Queue() |
|
33 | 35 |
self._client_capabilities = CAPABILITIES |
34 | 36 |
self._server_capabilities = None # yet |
35 | 37 |
self._id = None # session-id |
... | ... | |
71 | 73 |
"Subclass implements" |
72 | 74 |
raise NotImplementedError |
73 | 75 |
|
76 |
def send(self, message): |
|
77 |
"TODO: docstring" |
|
78 |
logger.debug('queueing %s' % message) |
|
79 |
self._q.put(message) |
|
80 |
|
|
74 | 81 |
### Properties |
75 | 82 |
|
76 | 83 |
@property |
b/ncclient/util.py | ||
---|---|---|
14 | 14 |
|
15 | 15 |
from ncclient.glue import Listener |
16 | 16 |
|
17 |
import logging |
|
18 |
logger = logging.getLogger('PrintListener') |
|
19 |
|
|
20 | 17 |
class PrintListener(Listener): |
21 | 18 |
|
22 | 19 |
def callback(self, root, raw): |
Also available in: Unified diff