root / ncclient / operations / rpc.py @ 188649fa
History | View | Annotate | Download (12.8 kB)
1 | a14c36f9 | Shikhar Bhushan | # Copyright 2009 Shikhar Bhushan
|
---|---|---|---|
2 | a14c36f9 | Shikhar Bhushan | #
|
3 | a14c36f9 | Shikhar Bhushan | # Licensed under the Apache License, Version 2.0 (the "License");
|
4 | a14c36f9 | Shikhar Bhushan | # you may not use this file except in compliance with the License.
|
5 | a14c36f9 | Shikhar Bhushan | # You may obtain a copy of the License at
|
6 | a14c36f9 | Shikhar Bhushan | #
|
7 | a14c36f9 | Shikhar Bhushan | # http://www.apache.org/licenses/LICENSE-2.0
|
8 | a14c36f9 | Shikhar Bhushan | #
|
9 | a14c36f9 | Shikhar Bhushan | # Unless required by applicable law or agreed to in writing, software
|
10 | a14c36f9 | Shikhar Bhushan | # distributed under the License is distributed on an "AS IS" BASIS,
|
11 | a14c36f9 | Shikhar Bhushan | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 | a14c36f9 | Shikhar Bhushan | # See the License for the specific language governing permissions and
|
13 | a14c36f9 | Shikhar Bhushan | # limitations under the License.
|
14 | a14c36f9 | Shikhar Bhushan | |
15 | a14c36f9 | Shikhar Bhushan | from threading import Event, Lock |
16 | a14c36f9 | Shikhar Bhushan | from uuid import uuid1 |
17 | a14c36f9 | Shikhar Bhushan | from weakref import WeakValueDictionary |
18 | a14c36f9 | Shikhar Bhushan | |
19 | 65c6a607 | Shikhar Bhushan | from ncclient import content |
20 | 0cdb8b3c | Shikhar Bhushan | from ncclient.transport import SessionListener |
21 | a14c36f9 | Shikhar Bhushan | |
22 | 216bb34c | Shikhar Bhushan | from errors import OperationError, TimeoutExpiredError, MissingCapabilityError |
23 | a14c36f9 | Shikhar Bhushan | |
24 | a14c36f9 | Shikhar Bhushan | import logging |
25 | 4f650d54 | Shikhar Bhushan | logger = logging.getLogger('ncclient.operations.rpc')
|
26 | a14c36f9 | Shikhar Bhushan | |
27 | a14c36f9 | Shikhar Bhushan | |
28 | a14c36f9 | Shikhar Bhushan | class RPCReply: |
29 | 4f650d54 | Shikhar Bhushan | |
30 | a7cb58ce | Shikhar Bhushan | """Represents an *<rpc-reply>*. Only concerns itself with whether the
|
31 | 216bb34c | Shikhar Bhushan | operation was successful.
|
32 | 216bb34c | Shikhar Bhushan |
|
33 | 216bb34c | Shikhar Bhushan | .. note::
|
34 | 216bb34c | Shikhar Bhushan | If the reply has not yet been parsed there is an implicit, one-time
|
35 | 216bb34c | Shikhar Bhushan | parsing overhead to accessing the attributes defined by this class and
|
36 | 216bb34c | Shikhar Bhushan | any subclasses.
|
37 | 216bb34c | Shikhar Bhushan | """
|
38 | a7cb58ce | Shikhar Bhushan | |
39 | a14c36f9 | Shikhar Bhushan | def __init__(self, raw): |
40 | a14c36f9 | Shikhar Bhushan | self._raw = raw
|
41 | a14c36f9 | Shikhar Bhushan | self._parsed = False |
42 | a14c36f9 | Shikhar Bhushan | self._root = None |
43 | a14c36f9 | Shikhar Bhushan | self._errors = []
|
44 | 4f650d54 | Shikhar Bhushan | |
45 | a14c36f9 | Shikhar Bhushan | def __repr__(self): |
46 | a14c36f9 | Shikhar Bhushan | return self._raw |
47 | 4f650d54 | Shikhar Bhushan | |
48 | a7cb58ce | Shikhar Bhushan | def _parsing_hook(self, root): |
49 | a7cb58ce | Shikhar Bhushan | """Subclass can implement.
|
50 | a7cb58ce | Shikhar Bhushan |
|
51 | a7cb58ce | Shikhar Bhushan | :type root: :class:`~xml.etree.ElementTree.Element`
|
52 | a7cb58ce | Shikhar Bhushan | """
|
53 | a7cb58ce | Shikhar Bhushan | pass
|
54 | 4f650d54 | Shikhar Bhushan | |
55 | a14c36f9 | Shikhar Bhushan | def parse(self): |
56 | a7cb58ce | Shikhar Bhushan | """Parse the *<rpc-reply>*"""
|
57 | 65c6a607 | Shikhar Bhushan | if self._parsed: |
58 | 65c6a607 | Shikhar Bhushan | return
|
59 | 65c6a607 | Shikhar Bhushan | root = self._root = content.xml2ele(self._raw) # <rpc-reply> element |
60 | a14c36f9 | Shikhar Bhushan | # per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
|
61 | cb96607b | Shikhar Bhushan | ok = content.find(root, 'ok', nslist=[content.BASE_NS, content.CISCO_BS])
|
62 | a14c36f9 | Shikhar Bhushan | if ok is not None: |
63 | a14c36f9 | Shikhar Bhushan | logger.debug('parsed [%s]' % ok.tag)
|
64 | a14c36f9 | Shikhar Bhushan | else: # create RPCError objects from <rpc-error> elements |
65 | cb96607b | Shikhar Bhushan | error = content.find(root, 'rpc-error', nslist=[content.BASE_NS, content.CISCO_BS])
|
66 | a14c36f9 | Shikhar Bhushan | if error is not None: |
67 | a14c36f9 | Shikhar Bhushan | logger.debug('parsed [%s]' % error.tag)
|
68 | a14c36f9 | Shikhar Bhushan | for err in root.getiterator(error.tag): |
69 | a14c36f9 | Shikhar Bhushan | # process a particular <rpc-error>
|
70 | a14c36f9 | Shikhar Bhushan | d = {} |
71 | a14c36f9 | Shikhar Bhushan | for err_detail in err.getchildren(): # <error-type> etc.. |
72 | a14c36f9 | Shikhar Bhushan | tag = content.unqualify(err_detail.tag) |
73 | d771dffc | Shikhar Bhushan | if tag != 'error-info': |
74 | d771dffc | Shikhar Bhushan | d[tag] = err_detail.text.strip() |
75 | d771dffc | Shikhar Bhushan | else:
|
76 | d771dffc | Shikhar Bhushan | d[tag] = content.ele2xml(err_detail) |
77 | a14c36f9 | Shikhar Bhushan | self._errors.append(RPCError(d))
|
78 | a14c36f9 | Shikhar Bhushan | self._parsing_hook(root)
|
79 | a14c36f9 | Shikhar Bhushan | self._parsed = True |
80 | 4f650d54 | Shikhar Bhushan | |
81 | a14c36f9 | Shikhar Bhushan | @property
|
82 | a14c36f9 | Shikhar Bhushan | def xml(self): |
83 | a7cb58ce | Shikhar Bhushan | "*<rpc-reply>* as returned"
|
84 | a14c36f9 | Shikhar Bhushan | return self._raw |
85 | 4f650d54 | Shikhar Bhushan | |
86 | a14c36f9 | Shikhar Bhushan | @property
|
87 | a14c36f9 | Shikhar Bhushan | def ok(self): |
88 | a7cb58ce | Shikhar Bhushan | "Boolean value indicating if there were no errors."
|
89 | a14c36f9 | Shikhar Bhushan | if not self._parsed: |
90 | a14c36f9 | Shikhar Bhushan | self.parse()
|
91 | a14c36f9 | Shikhar Bhushan | return not self._errors # empty list => false |
92 | 4f650d54 | Shikhar Bhushan | |
93 | a14c36f9 | Shikhar Bhushan | @property
|
94 | a14c36f9 | Shikhar Bhushan | def error(self): |
95 | 216bb34c | Shikhar Bhushan | """Short for :attr:`errors` [0]; :const:`None` if there were no errors.
|
96 | 216bb34c | Shikhar Bhushan | """
|
97 | a14c36f9 | Shikhar Bhushan | if not self._parsed: |
98 | a14c36f9 | Shikhar Bhushan | self.parse()
|
99 | a14c36f9 | Shikhar Bhushan | if self._errors: |
100 | a14c36f9 | Shikhar Bhushan | return self._errors[0] |
101 | a14c36f9 | Shikhar Bhushan | else:
|
102 | a14c36f9 | Shikhar Bhushan | return None |
103 | 4f650d54 | Shikhar Bhushan | |
104 | a14c36f9 | Shikhar Bhushan | @property
|
105 | a14c36f9 | Shikhar Bhushan | def errors(self): |
106 | 216bb34c | Shikhar Bhushan | """`list` of :class:`RPCError` objects. Will be empty if there were no
|
107 | 216bb34c | Shikhar Bhushan | *<rpc-error>* elements in reply.
|
108 | 216bb34c | Shikhar Bhushan | """
|
109 | a14c36f9 | Shikhar Bhushan | if not self._parsed: |
110 | a14c36f9 | Shikhar Bhushan | self.parse()
|
111 | a14c36f9 | Shikhar Bhushan | return self._errors |
112 | a14c36f9 | Shikhar Bhushan | |
113 | a14c36f9 | Shikhar Bhushan | |
114 | d771dffc | Shikhar Bhushan | class RPCError(OperationError): # raise it if you like |
115 | 4f650d54 | Shikhar Bhushan | |
116 | a7cb58ce | Shikhar Bhushan | """Represents an *<rpc-error>*. It is an instance of :exc:`OperationError`
|
117 | a7cb58ce | Shikhar Bhushan | so it can be raised like any other exception."""
|
118 | a7cb58ce | Shikhar Bhushan | |
119 | a14c36f9 | Shikhar Bhushan | def __init__(self, err_dict): |
120 | a14c36f9 | Shikhar Bhushan | self._dict = err_dict
|
121 | a14c36f9 | Shikhar Bhushan | if self.message is not None: |
122 | d771dffc | Shikhar Bhushan | OperationError.__init__(self, self.message) |
123 | a14c36f9 | Shikhar Bhushan | else:
|
124 | d771dffc | Shikhar Bhushan | OperationError.__init__(self)
|
125 | 4f650d54 | Shikhar Bhushan | |
126 | a14c36f9 | Shikhar Bhushan | @property
|
127 | a14c36f9 | Shikhar Bhushan | def type(self): |
128 | a7cb58ce | Shikhar Bhushan | "`string` represeting *error-type* element"
|
129 | a14c36f9 | Shikhar Bhushan | return self.get('error-type', None) |
130 | 4f650d54 | Shikhar Bhushan | |
131 | a14c36f9 | Shikhar Bhushan | @property
|
132 | a14c36f9 | Shikhar Bhushan | def severity(self): |
133 | a7cb58ce | Shikhar Bhushan | "`string` represeting *error-severity* element"
|
134 | a14c36f9 | Shikhar Bhushan | return self.get('error-severity', None) |
135 | 4f650d54 | Shikhar Bhushan | |
136 | a14c36f9 | Shikhar Bhushan | @property
|
137 | a14c36f9 | Shikhar Bhushan | def tag(self): |
138 | a7cb58ce | Shikhar Bhushan | "`string` represeting *error-tag* element"
|
139 | a14c36f9 | Shikhar Bhushan | return self.get('error-tag', None) |
140 | 4f650d54 | Shikhar Bhushan | |
141 | a14c36f9 | Shikhar Bhushan | @property
|
142 | a14c36f9 | Shikhar Bhushan | def path(self): |
143 | a7cb58ce | Shikhar Bhushan | "`string` or :const:`None`; represeting *error-path* element"
|
144 | a14c36f9 | Shikhar Bhushan | return self.get('error-path', None) |
145 | 4f650d54 | Shikhar Bhushan | |
146 | a14c36f9 | Shikhar Bhushan | @property
|
147 | a14c36f9 | Shikhar Bhushan | def message(self): |
148 | a7cb58ce | Shikhar Bhushan | "`string` or :const:`None`; represeting *error-message* element"
|
149 | a14c36f9 | Shikhar Bhushan | return self.get('error-message', None) |
150 | 4f650d54 | Shikhar Bhushan | |
151 | a14c36f9 | Shikhar Bhushan | @property
|
152 | a14c36f9 | Shikhar Bhushan | def info(self): |
153 | a7cb58ce | Shikhar Bhushan | "`string` or :const:`None`, represeting *error-info* element"
|
154 | a14c36f9 | Shikhar Bhushan | return self.get('error-info', None) |
155 | a14c36f9 | Shikhar Bhushan | |
156 | a14c36f9 | Shikhar Bhushan | ## dictionary interface
|
157 | 4f650d54 | Shikhar Bhushan | |
158 | a14c36f9 | Shikhar Bhushan | __getitem__ = lambda self, key: self._dict.__getitem__(key) |
159 | 4f650d54 | Shikhar Bhushan | |
160 | a14c36f9 | Shikhar Bhushan | __iter__ = lambda self: self._dict.__iter__() |
161 | 4f650d54 | Shikhar Bhushan | |
162 | a14c36f9 | Shikhar Bhushan | __contains__ = lambda self, key: self._dict.__contains__(key) |
163 | 4f650d54 | Shikhar Bhushan | |
164 | a14c36f9 | Shikhar Bhushan | keys = lambda self: self._dict.keys() |
165 | 4f650d54 | Shikhar Bhushan | |
166 | a14c36f9 | Shikhar Bhushan | get = lambda self, key, default: self._dict.get(key, default) |
167 | 4f650d54 | Shikhar Bhushan | |
168 | a14c36f9 | Shikhar Bhushan | iteritems = lambda self: self._dict.iteritems() |
169 | 4f650d54 | Shikhar Bhushan | |
170 | a14c36f9 | Shikhar Bhushan | iterkeys = lambda self: self._dict.iterkeys() |
171 | 4f650d54 | Shikhar Bhushan | |
172 | a14c36f9 | Shikhar Bhushan | itervalues = lambda self: self._dict.itervalues() |
173 | 4f650d54 | Shikhar Bhushan | |
174 | a14c36f9 | Shikhar Bhushan | values = lambda self: self._dict.values() |
175 | 4f650d54 | Shikhar Bhushan | |
176 | a14c36f9 | Shikhar Bhushan | items = lambda self: self._dict.items() |
177 | 4f650d54 | Shikhar Bhushan | |
178 | a14c36f9 | Shikhar Bhushan | __repr__ = lambda self: repr(self._dict) |
179 | a14c36f9 | Shikhar Bhushan | |
180 | a14c36f9 | Shikhar Bhushan | |
181 | 0cdb8b3c | Shikhar Bhushan | class RPCReplyListener(SessionListener): |
182 | 4f650d54 | Shikhar Bhushan | |
183 | a7cb58ce | Shikhar Bhushan | # internal use
|
184 | a7cb58ce | Shikhar Bhushan | |
185 | a14c36f9 | Shikhar Bhushan | # one instance per session
|
186 | a14c36f9 | Shikhar Bhushan | def __new__(cls, session): |
187 | a14c36f9 | Shikhar Bhushan | instance = session.get_listener_instance(cls) |
188 | a14c36f9 | Shikhar Bhushan | if instance is None: |
189 | a14c36f9 | Shikhar Bhushan | instance = object.__new__(cls)
|
190 | a14c36f9 | Shikhar Bhushan | instance._lock = Lock() |
191 | a14c36f9 | Shikhar Bhushan | instance._id2rpc = WeakValueDictionary() |
192 | a14c36f9 | Shikhar Bhushan | instance._pipelined = session.can_pipeline |
193 | a14c36f9 | Shikhar Bhushan | session.add_listener(instance) |
194 | a14c36f9 | Shikhar Bhushan | return instance
|
195 | 4f650d54 | Shikhar Bhushan | |
196 | a14c36f9 | Shikhar Bhushan | def register(self, id, rpc): |
197 | a14c36f9 | Shikhar Bhushan | with self._lock: |
198 | a14c36f9 | Shikhar Bhushan | self._id2rpc[id] = rpc |
199 | 4f650d54 | Shikhar Bhushan | |
200 | a14c36f9 | Shikhar Bhushan | def callback(self, root, raw): |
201 | a14c36f9 | Shikhar Bhushan | tag, attrs = root |
202 | a14c36f9 | Shikhar Bhushan | if content.unqualify(tag) != 'rpc-reply': |
203 | a14c36f9 | Shikhar Bhushan | return
|
204 | a14c36f9 | Shikhar Bhushan | rpc = None
|
205 | a14c36f9 | Shikhar Bhushan | for key in attrs: |
206 | a14c36f9 | Shikhar Bhushan | if content.unqualify(key) == 'message-id': |
207 | a14c36f9 | Shikhar Bhushan | id = attrs[key] |
208 | a14c36f9 | Shikhar Bhushan | try:
|
209 | a14c36f9 | Shikhar Bhushan | with self._lock: |
210 | a14c36f9 | Shikhar Bhushan | rpc = self._id2rpc.pop(id) |
211 | a14c36f9 | Shikhar Bhushan | except KeyError: |
212 | a14c36f9 | Shikhar Bhushan | logger.warning('no object registered for message-id: [%s]' % id) |
213 | a14c36f9 | Shikhar Bhushan | except Exception as e: |
214 | a14c36f9 | Shikhar Bhushan | logger.debug('error - %r' % e)
|
215 | a14c36f9 | Shikhar Bhushan | break
|
216 | a14c36f9 | Shikhar Bhushan | else:
|
217 | a14c36f9 | Shikhar Bhushan | if not self._pipelined: |
218 | a14c36f9 | Shikhar Bhushan | with self._lock: |
219 | a14c36f9 | Shikhar Bhushan | assert(len(self._id2rpc) == 1) |
220 | a14c36f9 | Shikhar Bhushan | rpc = self._id2rpc.values()[0] |
221 | a14c36f9 | Shikhar Bhushan | self._id2rpc.clear()
|
222 | a14c36f9 | Shikhar Bhushan | else:
|
223 | a14c36f9 | Shikhar Bhushan | logger.warning('<rpc-reply> without message-id received: %s' % raw)
|
224 | a14c36f9 | Shikhar Bhushan | logger.debug('delivering to %r' % rpc)
|
225 | a7cb58ce | Shikhar Bhushan | rpc.deliver_reply(raw) |
226 | 4f650d54 | Shikhar Bhushan | |
227 | a14c36f9 | Shikhar Bhushan | def errback(self, err): |
228 | 0cdb8b3c | Shikhar Bhushan | for rpc in self._id2rpc.values(): |
229 | a7cb58ce | Shikhar Bhushan | rpc.deliver_error(err) |
230 | 4de03d63 | Shikhar Bhushan | |
231 | 4de03d63 | Shikhar Bhushan | |
232 | 4de03d63 | Shikhar Bhushan | class RPC(object): |
233 | 4f650d54 | Shikhar Bhushan | |
234 | 216bb34c | Shikhar Bhushan | """Base class for all operations.
|
235 | 216bb34c | Shikhar Bhushan |
|
236 | 216bb34c | Shikhar Bhushan | Directly corresponds to *<rpc>* requests. Handles making the request, and
|
237 | 216bb34c | Shikhar Bhushan | taking delivery of the reply.
|
238 | 216bb34c | Shikhar Bhushan | """
|
239 | a7cb58ce | Shikhar Bhushan | |
240 | a7cb58ce | Shikhar Bhushan | # : Subclasses can specify their dependencies on capabilities. List of URI's
|
241 | a7cb58ce | Shikhar Bhushan | # or abbreviated names, e.g. ':writable-running'. These are verified at the
|
242 | a7cb58ce | Shikhar Bhushan | # time of object creation. If the capability is not available, a
|
243 | a7cb58ce | Shikhar Bhushan | # :exc:`MissingCapabilityError` is raised.
|
244 | 4de03d63 | Shikhar Bhushan | DEPENDS = [] |
245 | a7cb58ce | Shikhar Bhushan | |
246 | a7cb58ce | Shikhar Bhushan | # : Subclasses can specify a different reply class, but it must be a
|
247 | a7cb58ce | Shikhar Bhushan | # subclass of :class:`RPCReply`.
|
248 | 4de03d63 | Shikhar Bhushan | REPLY_CLS = RPCReply |
249 | 4f650d54 | Shikhar Bhushan | |
250 | 4de03d63 | Shikhar Bhushan | def __init__(self, session, async=False, timeout=None): |
251 | 4de03d63 | Shikhar Bhushan | self._session = session
|
252 | 4de03d63 | Shikhar Bhushan | try:
|
253 | 4de03d63 | Shikhar Bhushan | for cap in self.DEPENDS: |
254 | 4de03d63 | Shikhar Bhushan | self._assert(cap)
|
255 | 4de03d63 | Shikhar Bhushan | except AttributeError: |
256 | 4f650d54 | Shikhar Bhushan | pass
|
257 | 4de03d63 | Shikhar Bhushan | self._async = async
|
258 | 4de03d63 | Shikhar Bhushan | self._timeout = timeout
|
259 | 4de03d63 | Shikhar Bhushan | # keeps things simple instead of having a class attr that has to be locked
|
260 | 4de03d63 | Shikhar Bhushan | self._id = uuid1().urn
|
261 | 4de03d63 | Shikhar Bhushan | # RPCReplyListener itself makes sure there isn't more than one instance -- i.e. multiton
|
262 | 4de03d63 | Shikhar Bhushan | self._listener = RPCReplyListener(session)
|
263 | 4de03d63 | Shikhar Bhushan | self._listener.register(self._id, self) |
264 | 4de03d63 | Shikhar Bhushan | self._reply = None |
265 | 4f650d54 | Shikhar Bhushan | self._error = None |
266 | a7cb58ce | Shikhar Bhushan | self._event = Event()
|
267 | 4f650d54 | Shikhar Bhushan | |
268 | e52e8478 | Shikhar Bhushan | def _build(self, opspec): |
269 | a7cb58ce | Shikhar Bhushan | # internal
|
270 | 4de03d63 | Shikhar Bhushan | spec = { |
271 | 188649fa | Shikhar Bhushan | 'tag': 'rpc', |
272 | 188649fa | Shikhar Bhushan | 'attrib': {
|
273 | 188649fa | Shikhar Bhushan | 'xmlns': content.BASE_NS,
|
274 | 188649fa | Shikhar Bhushan | 'message-id': self._id |
275 | 188649fa | Shikhar Bhushan | }, |
276 | a7cb58ce | Shikhar Bhushan | 'subtree': [ opspec ]
|
277 | 4de03d63 | Shikhar Bhushan | } |
278 | e52e8478 | Shikhar Bhushan | return content.dtree2xml(spec)
|
279 | 4f650d54 | Shikhar Bhushan | |
280 | 4de03d63 | Shikhar Bhushan | def _request(self, op): |
281 | a7cb58ce | Shikhar Bhushan | """Subclasses call this method to make the RPC request.
|
282 | a7cb58ce | Shikhar Bhushan |
|
283 | a7cb58ce | Shikhar Bhushan | In asynchronous mode, returns an :class:`~threading.Event` which is set
|
284 | a7cb58ce | Shikhar Bhushan | when the reply has been received or an error occured. It is prudent,
|
285 | a7cb58ce | Shikhar Bhushan | therefore, to check the :attr:`error` attribute before accesing
|
286 | a7cb58ce | Shikhar Bhushan | :attr:`reply`.
|
287 | a7cb58ce | Shikhar Bhushan |
|
288 | a7cb58ce | Shikhar Bhushan | Otherwise, waits until the reply is received and returns
|
289 | a7cb58ce | Shikhar Bhushan | :class:`RPCReply`.
|
290 | a7cb58ce | Shikhar Bhushan |
|
291 | a7cb58ce | Shikhar Bhushan | :arg opspec: :ref:`dtree` for the operation
|
292 | a7cb58ce | Shikhar Bhushan | :type opspec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
|
293 | a7cb58ce | Shikhar Bhushan | :rtype: :class:`~threading.Event` or :class:`RPCReply`
|
294 | a7cb58ce | Shikhar Bhushan | """
|
295 | 0b7d3b31 | Shikhar Bhushan | logger.debug('request %r with opsepc=%r' % (self, op)) |
296 | 4de03d63 | Shikhar Bhushan | req = self._build(op)
|
297 | 66cd54c8 | Shikhar Bhushan | self._session.send(req)
|
298 | 66cd54c8 | Shikhar Bhushan | if self._async: |
299 | 0b7d3b31 | Shikhar Bhushan | logger.debug('async, returning event')
|
300 | 66cd54c8 | Shikhar Bhushan | return self._event |
301 | 4de03d63 | Shikhar Bhushan | else:
|
302 | 66cd54c8 | Shikhar Bhushan | logger.debug('sync, will wait for timeout=%r' % self._timeout) |
303 | 66cd54c8 | Shikhar Bhushan | self._event.wait(self._timeout) |
304 | 66cd54c8 | Shikhar Bhushan | if self._event.isSet(): |
305 | 66cd54c8 | Shikhar Bhushan | if self._error: |
306 | 0cdb8b3c | Shikhar Bhushan | raise self._error |
307 | 66cd54c8 | Shikhar Bhushan | self._reply.parse()
|
308 | 66cd54c8 | Shikhar Bhushan | return self._reply |
309 | 4de03d63 | Shikhar Bhushan | else:
|
310 | 216bb34c | Shikhar Bhushan | raise TimeoutExpiredError
|
311 | 4f650d54 | Shikhar Bhushan | |
312 | a7cb58ce | Shikhar Bhushan | def request(self, *args, **kwds): |
313 | 216bb34c | Shikhar Bhushan | """Subclasses implement this method. Here, the operation is constructed
|
314 | 216bb34c | Shikhar Bhushan | in :ref:`dtree`, and the result of :meth:`_request` returned."""
|
315 | 216bb34c | Shikhar Bhushan | raise NotImplementedError |
316 | 4f650d54 | Shikhar Bhushan | |
317 | 4de03d63 | Shikhar Bhushan | def _delivery_hook(self): |
318 | a7cb58ce | Shikhar Bhushan | """Subclasses can implement this method. Will be called after
|
319 | a7cb58ce | Shikhar Bhushan | initialising the :attr:`reply` or :attr:`error` attribute and before
|
320 | a7cb58ce | Shikhar Bhushan | setting the :attr:`event`"""
|
321 | 4de03d63 | Shikhar Bhushan | pass
|
322 | 4f650d54 | Shikhar Bhushan | |
323 | 4de03d63 | Shikhar Bhushan | def _assert(self, capability): |
324 | a7cb58ce | Shikhar Bhushan | """Subclasses can use this method to verify that a capability is available
|
325 | a7cb58ce | Shikhar Bhushan | with the NETCONF server, before making a request that requires it. A
|
326 | 216bb34c | Shikhar Bhushan | :exc:`MissingCapabilityError` will be raised if the capability is not
|
327 | a7cb58ce | Shikhar Bhushan | available."""
|
328 | 4de03d63 | Shikhar Bhushan | if capability not in self._session.server_capabilities: |
329 | 4de03d63 | Shikhar Bhushan | raise MissingCapabilityError('Server does not support [%s]' % cap) |
330 | 4f650d54 | Shikhar Bhushan | |
331 | a7cb58ce | Shikhar Bhushan | def deliver_reply(self, raw): |
332 | a7cb58ce | Shikhar Bhushan | # internal use
|
333 | 4de03d63 | Shikhar Bhushan | self._reply = self.REPLY_CLS(raw) |
334 | 4de03d63 | Shikhar Bhushan | self._delivery_hook()
|
335 | a7cb58ce | Shikhar Bhushan | self._event.set()
|
336 | 4f650d54 | Shikhar Bhushan | |
337 | a7cb58ce | Shikhar Bhushan | def deliver_error(self, err): |
338 | a7cb58ce | Shikhar Bhushan | # internal use
|
339 | 0cdb8b3c | Shikhar Bhushan | self._error = err
|
340 | a7cb58ce | Shikhar Bhushan | self._delivery_hook()
|
341 | a7cb58ce | Shikhar Bhushan | self._event.set()
|
342 | 4f650d54 | Shikhar Bhushan | |
343 | 4de03d63 | Shikhar Bhushan | @property
|
344 | 4de03d63 | Shikhar Bhushan | def reply(self): |
345 | a7cb58ce | Shikhar Bhushan | ":class:`RPCReply` element if reply has been received or :const:`None`"
|
346 | 4de03d63 | Shikhar Bhushan | return self._reply |
347 | 4f650d54 | Shikhar Bhushan | |
348 | 4de03d63 | Shikhar Bhushan | @property
|
349 | a7cb58ce | Shikhar Bhushan | def error(self): |
350 | a7cb58ce | Shikhar Bhushan | """:exc:`Exception` type if an error occured or :const:`None`.
|
351 | a7cb58ce | Shikhar Bhushan |
|
352 | a7cb58ce | Shikhar Bhushan | This attribute should be checked if the request was made asynchronously,
|
353 | a7cb58ce | Shikhar Bhushan | so that it can be determined if :attr:`event` being set is because of a
|
354 | a7cb58ce | Shikhar Bhushan | reply or error.
|
355 | a7cb58ce | Shikhar Bhushan |
|
356 | a7cb58ce | Shikhar Bhushan | .. note::
|
357 | a7cb58ce | Shikhar Bhushan | This represents an error which prevented a reply from being
|
358 | a7cb58ce | Shikhar Bhushan | received. An *<rpc-error>* does not fall in that category -- see
|
359 | a7cb58ce | Shikhar Bhushan | :class:`RPCReply` for that.
|
360 | a7cb58ce | Shikhar Bhushan | """
|
361 | a7cb58ce | Shikhar Bhushan | return self._error |
362 | a7cb58ce | Shikhar Bhushan | |
363 | a7cb58ce | Shikhar Bhushan | @property
|
364 | 4de03d63 | Shikhar Bhushan | def id(self): |
365 | a7cb58ce | Shikhar Bhushan | "The *message-id* for this RPC"
|
366 | 4de03d63 | Shikhar Bhushan | return self._id |
367 | 4f650d54 | Shikhar Bhushan | |
368 | 4de03d63 | Shikhar Bhushan | @property
|
369 | 4de03d63 | Shikhar Bhushan | def session(self): |
370 | a7cb58ce | Shikhar Bhushan | """The :class:`~ncclient.transport.Session` object associated with this
|
371 | a7cb58ce | Shikhar Bhushan | RPC"""
|
372 | 4de03d63 | Shikhar Bhushan | return self._session |
373 | 4f650d54 | Shikhar Bhushan | |
374 | 4de03d63 | Shikhar Bhushan | @property
|
375 | a7cb58ce | Shikhar Bhushan | def event(self): |
376 | a7cb58ce | Shikhar Bhushan | """:class:`~threading.Event` that is set when reply has been received or
|
377 | a7cb58ce | Shikhar Bhushan | error occured."""
|
378 | a7cb58ce | Shikhar Bhushan | return self._event |
379 | a7cb58ce | Shikhar Bhushan | |
380 | a7cb58ce | Shikhar Bhushan | def set_async(self, async=True): |
381 | a7cb58ce | Shikhar Bhushan | """Set asynchronous mode for this RPC."""
|
382 | a7cb58ce | Shikhar Bhushan | self._async = async
|
383 | a7cb58ce | Shikhar Bhushan | if async and not session.can_pipeline: |
384 | a7cb58ce | Shikhar Bhushan | raise UserWarning('Asynchronous mode not supported for this device/session') |
385 | a7cb58ce | Shikhar Bhushan | |
386 | a7cb58ce | Shikhar Bhushan | def set_timeout(self, timeout): |
387 | a7cb58ce | Shikhar Bhushan | """Set the timeout for synchronous waiting defining how long the RPC
|
388 | a7cb58ce | Shikhar Bhushan | request will block on a reply before raising an error."""
|
389 | a7cb58ce | Shikhar Bhushan | self._timeout = timeout
|
390 | 4f650d54 | Shikhar Bhushan | |
391 | a7cb58ce | Shikhar Bhushan | #: Whether this RPC is asynchronous
|
392 | 4de03d63 | Shikhar Bhushan | async = property(fget=lambda self: self._async, fset=set_async) |
393 | 4f650d54 | Shikhar Bhushan | |
394 | a7cb58ce | Shikhar Bhushan | #: Timeout for synchronous waiting
|
395 | 4de03d63 | Shikhar Bhushan | timeout = property(fget=lambda self: self._timeout, fset=set_timeout) |