Statistics
| Branch: | Tag: | Revision:

root / ncclient / operations / rpc.py @ 6c70b245

History | View | Annotate | Download (13.1 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
18 57b5f922 Shikhar Bhushan
from ncclient import xml_
19 0cdb8b3c Shikhar Bhushan
from ncclient.transport import SessionListener
20 a14c36f9 Shikhar Bhushan
21 216bb34c Shikhar Bhushan
from errors import OperationError, TimeoutExpiredError, MissingCapabilityError
22 a14c36f9 Shikhar Bhushan
23 a14c36f9 Shikhar Bhushan
import logging
24 4f650d54 Shikhar Bhushan
logger = logging.getLogger('ncclient.operations.rpc')
25 a14c36f9 Shikhar Bhushan
26 a14c36f9 Shikhar Bhushan
27 a14c36f9 Shikhar Bhushan
class RPCReply:
28 4f650d54 Shikhar Bhushan
29 a7cb58ce Shikhar Bhushan
    """Represents an *<rpc-reply>*. Only concerns itself with whether the
30 216bb34c Shikhar Bhushan
    operation was successful.
31 216bb34c Shikhar Bhushan

32 216bb34c Shikhar Bhushan
    .. note::
33 216bb34c Shikhar Bhushan
        If the reply has not yet been parsed there is an implicit, one-time
34 216bb34c Shikhar Bhushan
        parsing overhead to accessing the attributes defined by this class and
35 216bb34c Shikhar Bhushan
        any subclasses.
36 216bb34c Shikhar Bhushan
    """
37 a7cb58ce Shikhar Bhushan
38 a14c36f9 Shikhar Bhushan
    def __init__(self, raw):
39 a14c36f9 Shikhar Bhushan
        self._raw = raw
40 a14c36f9 Shikhar Bhushan
        self._parsed = False
41 a14c36f9 Shikhar Bhushan
        self._root = None
42 a14c36f9 Shikhar Bhushan
        self._errors = []
43 4f650d54 Shikhar Bhushan
44 a14c36f9 Shikhar Bhushan
    def __repr__(self):
45 a14c36f9 Shikhar Bhushan
        return self._raw
46 4f650d54 Shikhar Bhushan
47 a7cb58ce Shikhar Bhushan
    def _parsing_hook(self, root):
48 a7cb58ce Shikhar Bhushan
        """Subclass can implement.
49 a7cb58ce Shikhar Bhushan

50 a7cb58ce Shikhar Bhushan
        :type root: :class:`~xml.etree.ElementTree.Element`
51 a7cb58ce Shikhar Bhushan
        """
52 a7cb58ce Shikhar Bhushan
        pass
53 4f650d54 Shikhar Bhushan
54 a14c36f9 Shikhar Bhushan
    def parse(self):
55 a7cb58ce Shikhar Bhushan
        """Parse the *<rpc-reply>*"""
56 65c6a607 Shikhar Bhushan
        if self._parsed:
57 65c6a607 Shikhar Bhushan
            return
58 57b5f922 Shikhar Bhushan
        root = self._root = xml_.xml2ele(self._raw) # <rpc-reply> element
59 a14c36f9 Shikhar Bhushan
        # per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
60 dd225c7a Shikhar Bhushan
        ok = xml_.find(root, 'ok', nslist=xml_.NSLIST)
61 a14c36f9 Shikhar Bhushan
        if ok is not None:
62 a14c36f9 Shikhar Bhushan
            logger.debug('parsed [%s]' % ok.tag)
63 a14c36f9 Shikhar Bhushan
        else: # create RPCError objects from <rpc-error> elements
64 dd225c7a Shikhar Bhushan
            error = xml_.find(root, 'rpc-error', nslist=xml_.NSLIST)
65 a14c36f9 Shikhar Bhushan
            if error is not None:
66 a14c36f9 Shikhar Bhushan
                logger.debug('parsed [%s]' % error.tag)
67 a14c36f9 Shikhar Bhushan
                for err in root.getiterator(error.tag):
68 a14c36f9 Shikhar Bhushan
                    # process a particular <rpc-error>
69 a14c36f9 Shikhar Bhushan
                    d = {}
70 a14c36f9 Shikhar Bhushan
                    for err_detail in err.getchildren(): # <error-type> etc..
71 57b5f922 Shikhar Bhushan
                        tag = xml_.unqualify(err_detail.tag)
72 d771dffc Shikhar Bhushan
                        if tag != 'error-info':
73 d771dffc Shikhar Bhushan
                            d[tag] = err_detail.text.strip()
74 d771dffc Shikhar Bhushan
                        else:
75 57b5f922 Shikhar Bhushan
                            d[tag] = xml_.ele2xml(err_detail)
76 a14c36f9 Shikhar Bhushan
                    self._errors.append(RPCError(d))
77 a14c36f9 Shikhar Bhushan
        self._parsing_hook(root)
78 a14c36f9 Shikhar Bhushan
        self._parsed = True
79 4f650d54 Shikhar Bhushan
80 a14c36f9 Shikhar Bhushan
    @property
81 a14c36f9 Shikhar Bhushan
    def xml(self):
82 a7cb58ce Shikhar Bhushan
        "*<rpc-reply>* as returned"
83 a14c36f9 Shikhar Bhushan
        return self._raw
84 4f650d54 Shikhar Bhushan
85 a14c36f9 Shikhar Bhushan
    @property
86 a14c36f9 Shikhar Bhushan
    def ok(self):
87 a7cb58ce Shikhar Bhushan
        "Boolean value indicating if there were no errors."
88 a14c36f9 Shikhar Bhushan
        if not self._parsed:
89 a14c36f9 Shikhar Bhushan
            self.parse()
90 a14c36f9 Shikhar Bhushan
        return not self._errors # empty list => false
91 4f650d54 Shikhar Bhushan
92 a14c36f9 Shikhar Bhushan
    @property
93 a14c36f9 Shikhar Bhushan
    def error(self):
94 216bb34c Shikhar Bhushan
        """Short for :attr:`errors` [0]; :const:`None` if there were no errors.
95 216bb34c Shikhar Bhushan
        """
96 a14c36f9 Shikhar Bhushan
        if not self._parsed:
97 a14c36f9 Shikhar Bhushan
            self.parse()
98 a14c36f9 Shikhar Bhushan
        if self._errors:
99 a14c36f9 Shikhar Bhushan
            return self._errors[0]
100 a14c36f9 Shikhar Bhushan
        else:
101 a14c36f9 Shikhar Bhushan
            return None
102 4f650d54 Shikhar Bhushan
103 a14c36f9 Shikhar Bhushan
    @property
104 a14c36f9 Shikhar Bhushan
    def errors(self):
105 216bb34c Shikhar Bhushan
        """`list` of :class:`RPCError` objects. Will be empty if there were no
106 216bb34c Shikhar Bhushan
        *<rpc-error>* elements in reply.
107 216bb34c Shikhar Bhushan
        """
108 a14c36f9 Shikhar Bhushan
        if not self._parsed:
109 a14c36f9 Shikhar Bhushan
            self.parse()
110 a14c36f9 Shikhar Bhushan
        return self._errors
111 a14c36f9 Shikhar Bhushan
112 a14c36f9 Shikhar Bhushan
113 d771dffc Shikhar Bhushan
class RPCError(OperationError): # raise it if you like
114 4f650d54 Shikhar Bhushan
115 a7cb58ce Shikhar Bhushan
    """Represents an *<rpc-error>*. It is an instance of :exc:`OperationError`
116 2abf9522 Shikhar Bhushan
    and can be raised like any other exception."""
117 a7cb58ce Shikhar Bhushan
118 a14c36f9 Shikhar Bhushan
    def __init__(self, err_dict):
119 a14c36f9 Shikhar Bhushan
        self._dict = err_dict
120 a14c36f9 Shikhar Bhushan
        if self.message is not None:
121 d771dffc Shikhar Bhushan
            OperationError.__init__(self, self.message)
122 a14c36f9 Shikhar Bhushan
        else:
123 d771dffc Shikhar Bhushan
            OperationError.__init__(self)
124 4f650d54 Shikhar Bhushan
125 a14c36f9 Shikhar Bhushan
    @property
126 a14c36f9 Shikhar Bhushan
    def type(self):
127 2abf9522 Shikhar Bhushan
        "`string` representing text of *error-type* element"
128 a14c36f9 Shikhar Bhushan
        return self.get('error-type', None)
129 4f650d54 Shikhar Bhushan
130 a14c36f9 Shikhar Bhushan
    @property
131 a14c36f9 Shikhar Bhushan
    def severity(self):
132 2abf9522 Shikhar Bhushan
        "`string` representing text of *error-severity* element"
133 a14c36f9 Shikhar Bhushan
        return self.get('error-severity', None)
134 4f650d54 Shikhar Bhushan
135 a14c36f9 Shikhar Bhushan
    @property
136 a14c36f9 Shikhar Bhushan
    def tag(self):
137 2abf9522 Shikhar Bhushan
        "`string` representing text of *error-tag* element"
138 a14c36f9 Shikhar Bhushan
        return self.get('error-tag', None)
139 4f650d54 Shikhar Bhushan
140 a14c36f9 Shikhar Bhushan
    @property
141 a14c36f9 Shikhar Bhushan
    def path(self):
142 2abf9522 Shikhar Bhushan
        "`string` or :const:`None`; representing text of *error-path* element"
143 a14c36f9 Shikhar Bhushan
        return self.get('error-path', None)
144 4f650d54 Shikhar Bhushan
145 a14c36f9 Shikhar Bhushan
    @property
146 a14c36f9 Shikhar Bhushan
    def message(self):
147 2abf9522 Shikhar Bhushan
        "`string` or :const:`None`; representing text of *error-message* element"
148 a14c36f9 Shikhar Bhushan
        return self.get('error-message', None)
149 4f650d54 Shikhar Bhushan
150 a14c36f9 Shikhar Bhushan
    @property
151 a14c36f9 Shikhar Bhushan
    def info(self):
152 2abf9522 Shikhar Bhushan
        "`string` (XML) or :const:`None`, representing *error-info* element"
153 a14c36f9 Shikhar Bhushan
        return self.get('error-info', None)
154 a14c36f9 Shikhar Bhushan
155 a14c36f9 Shikhar Bhushan
    ## dictionary interface
156 4f650d54 Shikhar Bhushan
157 a14c36f9 Shikhar Bhushan
    __getitem__ = lambda self, key: self._dict.__getitem__(key)
158 4f650d54 Shikhar Bhushan
159 a14c36f9 Shikhar Bhushan
    __iter__ = lambda self: self._dict.__iter__()
160 4f650d54 Shikhar Bhushan
161 a14c36f9 Shikhar Bhushan
    __contains__ = lambda self, key: self._dict.__contains__(key)
162 4f650d54 Shikhar Bhushan
163 a14c36f9 Shikhar Bhushan
    keys = lambda self: self._dict.keys()
164 4f650d54 Shikhar Bhushan
165 a14c36f9 Shikhar Bhushan
    get = lambda self, key, default: self._dict.get(key, default)
166 4f650d54 Shikhar Bhushan
167 a14c36f9 Shikhar Bhushan
    iteritems = lambda self: self._dict.iteritems()
168 4f650d54 Shikhar Bhushan
169 a14c36f9 Shikhar Bhushan
    iterkeys = lambda self: self._dict.iterkeys()
170 4f650d54 Shikhar Bhushan
171 a14c36f9 Shikhar Bhushan
    itervalues = lambda self: self._dict.itervalues()
172 4f650d54 Shikhar Bhushan
173 a14c36f9 Shikhar Bhushan
    values = lambda self: self._dict.values()
174 4f650d54 Shikhar Bhushan
175 a14c36f9 Shikhar Bhushan
    items = lambda self: self._dict.items()
176 4f650d54 Shikhar Bhushan
177 a14c36f9 Shikhar Bhushan
    __repr__ = lambda self: repr(self._dict)
178 a14c36f9 Shikhar Bhushan
179 a14c36f9 Shikhar Bhushan
180 0cdb8b3c Shikhar Bhushan
class RPCReplyListener(SessionListener):
181 4f650d54 Shikhar Bhushan
182 a7cb58ce Shikhar Bhushan
    # internal use
183 a7cb58ce Shikhar Bhushan
184 a14c36f9 Shikhar Bhushan
    # one instance per session
185 a14c36f9 Shikhar Bhushan
    def __new__(cls, session):
186 a14c36f9 Shikhar Bhushan
        instance = session.get_listener_instance(cls)
187 a14c36f9 Shikhar Bhushan
        if instance is None:
188 a14c36f9 Shikhar Bhushan
            instance = object.__new__(cls)
189 a14c36f9 Shikhar Bhushan
            instance._lock = Lock()
190 6a2dfeb4 Shikhar Bhushan
            instance._id2rpc = {}
191 a14c36f9 Shikhar Bhushan
            instance._pipelined = session.can_pipeline
192 a14c36f9 Shikhar Bhushan
            session.add_listener(instance)
193 a14c36f9 Shikhar Bhushan
        return instance
194 4f650d54 Shikhar Bhushan
195 a14c36f9 Shikhar Bhushan
    def register(self, id, rpc):
196 a14c36f9 Shikhar Bhushan
        with self._lock:
197 a14c36f9 Shikhar Bhushan
            self._id2rpc[id] = rpc
198 4f650d54 Shikhar Bhushan
199 a14c36f9 Shikhar Bhushan
    def callback(self, root, raw):
200 a14c36f9 Shikhar Bhushan
        tag, attrs = root
201 57b5f922 Shikhar Bhushan
        if xml_.unqualify(tag) != 'rpc-reply':
202 a14c36f9 Shikhar Bhushan
            return
203 a14c36f9 Shikhar Bhushan
        rpc = None
204 a14c36f9 Shikhar Bhushan
        for key in attrs:
205 57b5f922 Shikhar Bhushan
            if xml_.unqualify(key) == 'message-id':
206 a14c36f9 Shikhar Bhushan
                id = attrs[key]
207 a14c36f9 Shikhar Bhushan
                try:
208 a14c36f9 Shikhar Bhushan
                    with self._lock:
209 a14c36f9 Shikhar Bhushan
                        rpc = self._id2rpc.pop(id)
210 a14c36f9 Shikhar Bhushan
                except KeyError:
211 a14c36f9 Shikhar Bhushan
                    logger.warning('no object registered for message-id: [%s]' % id)
212 a14c36f9 Shikhar Bhushan
                except Exception as e:
213 a14c36f9 Shikhar Bhushan
                    logger.debug('error - %r' % e)
214 a14c36f9 Shikhar Bhushan
                break
215 a14c36f9 Shikhar Bhushan
        else:
216 a14c36f9 Shikhar Bhushan
            if not self._pipelined:
217 a14c36f9 Shikhar Bhushan
                with self._lock:
218 a14c36f9 Shikhar Bhushan
                    assert(len(self._id2rpc) == 1)
219 a14c36f9 Shikhar Bhushan
                    rpc = self._id2rpc.values()[0]
220 a14c36f9 Shikhar Bhushan
                    self._id2rpc.clear()
221 a14c36f9 Shikhar Bhushan
            else:
222 a14c36f9 Shikhar Bhushan
                logger.warning('<rpc-reply> without message-id received: %s' % raw)
223 a14c36f9 Shikhar Bhushan
        logger.debug('delivering to %r' % rpc)
224 a7cb58ce Shikhar Bhushan
        rpc.deliver_reply(raw)
225 4f650d54 Shikhar Bhushan
226 a14c36f9 Shikhar Bhushan
    def errback(self, err):
227 6a2dfeb4 Shikhar Bhushan
        try:
228 6a2dfeb4 Shikhar Bhushan
            for rpc in self._id2rpc.values():
229 6a2dfeb4 Shikhar Bhushan
                rpc.deliver_error(err)
230 6a2dfeb4 Shikhar Bhushan
        finally:
231 6a2dfeb4 Shikhar Bhushan
            self._id2rpc.clear()
232 4de03d63 Shikhar Bhushan
233 4de03d63 Shikhar Bhushan
234 4de03d63 Shikhar Bhushan
class RPC(object):
235 4f650d54 Shikhar Bhushan
236 216bb34c Shikhar Bhushan
    """Base class for all operations.
237 216bb34c Shikhar Bhushan

238 216bb34c Shikhar Bhushan
    Directly corresponds to *<rpc>* requests. Handles making the request, and
239 216bb34c Shikhar Bhushan
    taking delivery of the reply.
240 216bb34c Shikhar Bhushan
    """
241 a7cb58ce Shikhar Bhushan
242 bbd4ce54 Shikhar Bhushan
    #: Subclasses can specify their dependencies on capabilities. List of URI's
243 a7cb58ce Shikhar Bhushan
    # or abbreviated names, e.g. ':writable-running'. These are verified at the
244 a7cb58ce Shikhar Bhushan
    # time of object creation. If the capability is not available, a
245 a7cb58ce Shikhar Bhushan
    # :exc:`MissingCapabilityError` is raised.
246 4de03d63 Shikhar Bhushan
    DEPENDS = []
247 a7cb58ce Shikhar Bhushan
248 bbd4ce54 Shikhar Bhushan
    #: Subclasses can specify a different reply class, but it must be a
249 a7cb58ce Shikhar Bhushan
    # subclass of :class:`RPCReply`.
250 4de03d63 Shikhar Bhushan
    REPLY_CLS = RPCReply
251 4f650d54 Shikhar Bhushan
252 6c70b245 Shikhar Bhushan
    def __init__(self, session, async=False, timeout=None, raise_mode='none'):
253 4de03d63 Shikhar Bhushan
        self._session = session
254 4de03d63 Shikhar Bhushan
        try:
255 4de03d63 Shikhar Bhushan
            for cap in self.DEPENDS:
256 4de03d63 Shikhar Bhushan
                self._assert(cap)
257 4de03d63 Shikhar Bhushan
        except AttributeError:
258 4f650d54 Shikhar Bhushan
            pass
259 4de03d63 Shikhar Bhushan
        self._async = async
260 4de03d63 Shikhar Bhushan
        self._timeout = timeout
261 6c70b245 Shikhar Bhushan
        self._raise_mode = raise_mode
262 4de03d63 Shikhar Bhushan
        # keeps things simple instead of having a class attr that has to be locked
263 4de03d63 Shikhar Bhushan
        self._id = uuid1().urn
264 4de03d63 Shikhar Bhushan
        self._listener = RPCReplyListener(session)
265 4de03d63 Shikhar Bhushan
        self._listener.register(self._id, self)
266 4de03d63 Shikhar Bhushan
        self._reply = None
267 4f650d54 Shikhar Bhushan
        self._error = None
268 a7cb58ce Shikhar Bhushan
        self._event = Event()
269 4f650d54 Shikhar Bhushan
270 e52e8478 Shikhar Bhushan
    def _build(self, opspec):
271 a7cb58ce Shikhar Bhushan
        # internal
272 4de03d63 Shikhar Bhushan
        spec = {
273 188649fa Shikhar Bhushan
            'tag': 'rpc',
274 188649fa Shikhar Bhushan
            'attrib': {
275 dd225c7a Shikhar Bhushan
                'xmlns': xml_.BASE_NS_1_0,
276 188649fa Shikhar Bhushan
                'message-id': self._id
277 188649fa Shikhar Bhushan
                },
278 a7cb58ce Shikhar Bhushan
            'subtree': [ opspec ]
279 4de03d63 Shikhar Bhushan
            }
280 57b5f922 Shikhar Bhushan
        return xml_.dtree2xml(spec)
281 4f650d54 Shikhar Bhushan
282 4de03d63 Shikhar Bhushan
    def _request(self, op):
283 a7cb58ce Shikhar Bhushan
        """Subclasses call this method to make the RPC request.
284 a7cb58ce Shikhar Bhushan

285 a7cb58ce Shikhar Bhushan
        In asynchronous mode, returns an :class:`~threading.Event` which is set
286 a7cb58ce Shikhar Bhushan
        when the reply has been received or an error occured. It is prudent,
287 a7cb58ce Shikhar Bhushan
        therefore, to check the :attr:`error` attribute before accesing
288 a7cb58ce Shikhar Bhushan
        :attr:`reply`.
289 a7cb58ce Shikhar Bhushan

290 a7cb58ce Shikhar Bhushan
        Otherwise, waits until the reply is received and returns
291 a7cb58ce Shikhar Bhushan
        :class:`RPCReply`.
292 a7cb58ce Shikhar Bhushan

293 a7cb58ce Shikhar Bhushan
        :arg opspec: :ref:`dtree` for the operation
294 a7cb58ce Shikhar Bhushan
        :type opspec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
295 a7cb58ce Shikhar Bhushan
        :rtype: :class:`~threading.Event` or :class:`RPCReply`
296 a7cb58ce Shikhar Bhushan
        """
297 0b7d3b31 Shikhar Bhushan
        logger.debug('request %r with opsepc=%r' % (self, op))
298 4de03d63 Shikhar Bhushan
        req = self._build(op)
299 66cd54c8 Shikhar Bhushan
        self._session.send(req)
300 66cd54c8 Shikhar Bhushan
        if self._async:
301 0b7d3b31 Shikhar Bhushan
            logger.debug('async, returning event')
302 66cd54c8 Shikhar Bhushan
            return self._event
303 4de03d63 Shikhar Bhushan
        else:
304 66cd54c8 Shikhar Bhushan
            logger.debug('sync, will wait for timeout=%r' % self._timeout)
305 66cd54c8 Shikhar Bhushan
            self._event.wait(self._timeout)
306 66cd54c8 Shikhar Bhushan
            if self._event.isSet():
307 66cd54c8 Shikhar Bhushan
                if self._error:
308 6c70b245 Shikhar Bhushan
                    # Error that prevented reply delivery
309 0cdb8b3c Shikhar Bhushan
                    raise self._error
310 66cd54c8 Shikhar Bhushan
                self._reply.parse()
311 6c70b245 Shikhar Bhushan
                if self._reply.error is not None:
312 6c70b245 Shikhar Bhushan
                    # <rpc-error>'s [ RPCError ]
313 6c70b245 Shikhar Bhushan
                    if self._raise_mode == "all":
314 6c70b245 Shikhar Bhushan
                        raise self._reply._error
315 6c70b245 Shikhar Bhushan
                    elif (self._raise_mode == "errors" and
316 6c70b245 Shikhar Bhushan
                          self._reply.error.type == "error"):
317 6c70b245 Shikhar Bhushan
                        raise self._reply.error
318 66cd54c8 Shikhar Bhushan
                return self._reply
319 4de03d63 Shikhar Bhushan
            else:
320 216bb34c Shikhar Bhushan
                raise TimeoutExpiredError
321 4f650d54 Shikhar Bhushan
322 a7cb58ce Shikhar Bhushan
    def request(self, *args, **kwds):
323 216bb34c Shikhar Bhushan
        """Subclasses implement this method. Here, the operation is constructed
324 216bb34c Shikhar Bhushan
        in :ref:`dtree`, and the result of :meth:`_request` returned."""
325 dd225c7a Shikhar Bhushan
        return self._request(self.SPEC)
326 6c70b245 Shikhar Bhushan
    
327 4de03d63 Shikhar Bhushan
    def _assert(self, capability):
328 a7cb58ce Shikhar Bhushan
        """Subclasses can use this method to verify that a capability is available
329 a7cb58ce Shikhar Bhushan
        with the NETCONF server, before making a request that requires it. A
330 216bb34c Shikhar Bhushan
        :exc:`MissingCapabilityError` will be raised if the capability is not
331 a7cb58ce Shikhar Bhushan
        available."""
332 4de03d63 Shikhar Bhushan
        if capability not in self._session.server_capabilities:
333 bbd4ce54 Shikhar Bhushan
            raise MissingCapabilityError('Server does not support [%s]' %
334 bbd4ce54 Shikhar Bhushan
                                         capability)
335 4f650d54 Shikhar Bhushan
336 a7cb58ce Shikhar Bhushan
    def deliver_reply(self, raw):
337 a7cb58ce Shikhar Bhushan
        # internal use
338 4de03d63 Shikhar Bhushan
        self._reply = self.REPLY_CLS(raw)
339 4de03d63 Shikhar Bhushan
        self._delivery_hook()
340 a7cb58ce Shikhar Bhushan
        self._event.set()
341 4f650d54 Shikhar Bhushan
342 a7cb58ce Shikhar Bhushan
    def deliver_error(self, err):
343 a7cb58ce Shikhar Bhushan
        # internal use
344 0cdb8b3c Shikhar Bhushan
        self._error = err
345 a7cb58ce Shikhar Bhushan
        self._delivery_hook()
346 a7cb58ce Shikhar Bhushan
        self._event.set()
347 4f650d54 Shikhar Bhushan
348 4de03d63 Shikhar Bhushan
    @property
349 4de03d63 Shikhar Bhushan
    def reply(self):
350 a7cb58ce Shikhar Bhushan
        ":class:`RPCReply` element if reply has been received or :const:`None`"
351 4de03d63 Shikhar Bhushan
        return self._reply
352 4f650d54 Shikhar Bhushan
353 4de03d63 Shikhar Bhushan
    @property
354 a7cb58ce Shikhar Bhushan
    def error(self):
355 a7cb58ce Shikhar Bhushan
        """:exc:`Exception` type if an error occured or :const:`None`.
356 a7cb58ce Shikhar Bhushan

357 a7cb58ce Shikhar Bhushan
        This attribute should be checked if the request was made asynchronously,
358 a7cb58ce Shikhar Bhushan
        so that it can be determined if :attr:`event` being set is because of a
359 a7cb58ce Shikhar Bhushan
        reply or error.
360 a7cb58ce Shikhar Bhushan

361 a7cb58ce Shikhar Bhushan
        .. note::
362 a7cb58ce Shikhar Bhushan
            This represents an error which prevented a reply from being
363 a7cb58ce Shikhar Bhushan
            received. An *<rpc-error>* does not fall in that category -- see
364 a7cb58ce Shikhar Bhushan
            :class:`RPCReply` for that.
365 a7cb58ce Shikhar Bhushan
        """
366 a7cb58ce Shikhar Bhushan
        return self._error
367 a7cb58ce Shikhar Bhushan
368 a7cb58ce Shikhar Bhushan
    @property
369 4de03d63 Shikhar Bhushan
    def id(self):
370 a7cb58ce Shikhar Bhushan
        "The *message-id* for this RPC"
371 4de03d63 Shikhar Bhushan
        return self._id
372 4f650d54 Shikhar Bhushan
373 4de03d63 Shikhar Bhushan
    @property
374 4de03d63 Shikhar Bhushan
    def session(self):
375 a7cb58ce Shikhar Bhushan
        """The :class:`~ncclient.transport.Session` object associated with this
376 a7cb58ce Shikhar Bhushan
        RPC"""
377 4de03d63 Shikhar Bhushan
        return self._session
378 4f650d54 Shikhar Bhushan
379 4de03d63 Shikhar Bhushan
    @property
380 a7cb58ce Shikhar Bhushan
    def event(self):
381 a7cb58ce Shikhar Bhushan
        """:class:`~threading.Event` that is set when reply has been received or
382 a7cb58ce Shikhar Bhushan
        error occured."""
383 a7cb58ce Shikhar Bhushan
        return self._event
384 a7cb58ce Shikhar Bhushan
385 a7cb58ce Shikhar Bhushan
    def set_async(self, async=True):
386 a7cb58ce Shikhar Bhushan
        """Set asynchronous mode for this RPC."""
387 a7cb58ce Shikhar Bhushan
        self._async = async
388 a7cb58ce Shikhar Bhushan
        if async and not session.can_pipeline:
389 a7cb58ce Shikhar Bhushan
            raise UserWarning('Asynchronous mode not supported for this device/session')
390 a7cb58ce Shikhar Bhushan
391 6c70b245 Shikhar Bhushan
    def set_raise_mode(self, mode):
392 e0e01d37 Shikhar Bhushan
        assert(choice in ('all', 'errors', 'none'))
393 6c70b245 Shikhar Bhushan
        self._raise_mode = mode
394 e0e01d37 Shikhar Bhushan
395 a7cb58ce Shikhar Bhushan
    def set_timeout(self, timeout):
396 a7cb58ce Shikhar Bhushan
        """Set the timeout for synchronous waiting defining how long the RPC
397 a7cb58ce Shikhar Bhushan
        request will block on a reply before raising an error."""
398 a7cb58ce Shikhar Bhushan
        self._timeout = timeout
399 4f650d54 Shikhar Bhushan
400 a7cb58ce Shikhar Bhushan
    #: Whether this RPC is asynchronous
401 4de03d63 Shikhar Bhushan
    async = property(fget=lambda self: self._async, fset=set_async)
402 4f650d54 Shikhar Bhushan
403 a7cb58ce Shikhar Bhushan
    #: Timeout for synchronous waiting
404 4de03d63 Shikhar Bhushan
    timeout = property(fget=lambda self: self._timeout, fset=set_timeout)