Statistics
| Branch: | Tag: | Revision:

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)