Statistics
| Branch: | Tag: | Revision:

root / ncclient / operations / rpc.py @ 7b02d55b

History | View | Annotate | Download (13 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 7b02d55b Shikhar Bhushan
    # one instance per session -- maybe there is a better way??
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 0304f041 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 0304f041 Shikhar Bhushan
        for key in attrs: # in the <rpc-reply> attributes
204 0304f041 Shikhar Bhushan
            if xml_.unqualify(key) == 'message-id': # if we found msgid attr
205 0304f041 Shikhar Bhushan
                id = attrs[key] # get the msgid
206 a14c36f9 Shikhar Bhushan
                try:
207 a14c36f9 Shikhar Bhushan
                    with self._lock:
208 0304f041 Shikhar Bhushan
                        rpc = self._id2rpc.get(id) # the corresponding rpc
209 0304f041 Shikhar Bhushan
                        logger.debug('delivering to %r' % rpc)
210 0304f041 Shikhar Bhushan
                        rpc.deliver_reply(raw)
211 a14c36f9 Shikhar Bhushan
                except KeyError:
212 0304f041 Shikhar Bhushan
                    raise OperationError('Unknown message-id: %s', id)
213 0304f041 Shikhar Bhushan
                # no catching other exceptions, fail loudly if must
214 0304f041 Shikhar Bhushan
                else:
215 0304f041 Shikhar Bhushan
                    # if no error delivering, can del the reference to the RPC
216 0304f041 Shikhar Bhushan
                    del self._id2rpc[id]
217 0304f041 Shikhar Bhushan
                    break
218 a14c36f9 Shikhar Bhushan
        else:
219 0304f041 Shikhar Bhushan
            raise OperationError('Could not find "message-id" attribute in <rpc-reply>')
220 0304f041 Shikhar Bhushan
    
221 a14c36f9 Shikhar Bhushan
    def errback(self, err):
222 6a2dfeb4 Shikhar Bhushan
        try:
223 6a2dfeb4 Shikhar Bhushan
            for rpc in self._id2rpc.values():
224 6a2dfeb4 Shikhar Bhushan
                rpc.deliver_error(err)
225 6a2dfeb4 Shikhar Bhushan
        finally:
226 6a2dfeb4 Shikhar Bhushan
            self._id2rpc.clear()
227 4de03d63 Shikhar Bhushan
228 4de03d63 Shikhar Bhushan
229 4de03d63 Shikhar Bhushan
class RPC(object):
230 4f650d54 Shikhar Bhushan
231 216bb34c Shikhar Bhushan
    """Base class for all operations.
232 216bb34c Shikhar Bhushan

233 216bb34c Shikhar Bhushan
    Directly corresponds to *<rpc>* requests. Handles making the request, and
234 216bb34c Shikhar Bhushan
    taking delivery of the reply.
235 216bb34c Shikhar Bhushan
    """
236 a7cb58ce Shikhar Bhushan
237 bbd4ce54 Shikhar Bhushan
    #: Subclasses can specify their dependencies on capabilities. List of URI's
238 a7cb58ce Shikhar Bhushan
    # or abbreviated names, e.g. ':writable-running'. These are verified at the
239 a7cb58ce Shikhar Bhushan
    # time of object creation. If the capability is not available, a
240 a7cb58ce Shikhar Bhushan
    # :exc:`MissingCapabilityError` is raised.
241 4de03d63 Shikhar Bhushan
    DEPENDS = []
242 a7cb58ce Shikhar Bhushan
243 bbd4ce54 Shikhar Bhushan
    #: Subclasses can specify a different reply class, but it must be a
244 a7cb58ce Shikhar Bhushan
    # subclass of :class:`RPCReply`.
245 4de03d63 Shikhar Bhushan
    REPLY_CLS = RPCReply
246 4f650d54 Shikhar Bhushan
247 6c70b245 Shikhar Bhushan
    def __init__(self, session, async=False, timeout=None, raise_mode='none'):
248 4de03d63 Shikhar Bhushan
        self._session = session
249 4de03d63 Shikhar Bhushan
        try:
250 4de03d63 Shikhar Bhushan
            for cap in self.DEPENDS:
251 4de03d63 Shikhar Bhushan
                self._assert(cap)
252 4de03d63 Shikhar Bhushan
        except AttributeError:
253 4f650d54 Shikhar Bhushan
            pass
254 4de03d63 Shikhar Bhushan
        self._async = async
255 4de03d63 Shikhar Bhushan
        self._timeout = timeout
256 6c70b245 Shikhar Bhushan
        self._raise_mode = raise_mode
257 4de03d63 Shikhar Bhushan
        # keeps things simple instead of having a class attr that has to be locked
258 4de03d63 Shikhar Bhushan
        self._id = uuid1().urn
259 4de03d63 Shikhar Bhushan
        self._listener = RPCReplyListener(session)
260 4de03d63 Shikhar Bhushan
        self._listener.register(self._id, self)
261 4de03d63 Shikhar Bhushan
        self._reply = None
262 4f650d54 Shikhar Bhushan
        self._error = None
263 a7cb58ce Shikhar Bhushan
        self._event = Event()
264 4f650d54 Shikhar Bhushan
265 e52e8478 Shikhar Bhushan
    def _build(self, opspec):
266 a7cb58ce Shikhar Bhushan
        # internal
267 4de03d63 Shikhar Bhushan
        spec = {
268 188649fa Shikhar Bhushan
            'tag': 'rpc',
269 188649fa Shikhar Bhushan
            'attrib': {
270 dd225c7a Shikhar Bhushan
                'xmlns': xml_.BASE_NS_1_0,
271 188649fa Shikhar Bhushan
                'message-id': self._id
272 188649fa Shikhar Bhushan
                },
273 a7cb58ce Shikhar Bhushan
            'subtree': [ opspec ]
274 4de03d63 Shikhar Bhushan
            }
275 57b5f922 Shikhar Bhushan
        return xml_.dtree2xml(spec)
276 4f650d54 Shikhar Bhushan
277 4de03d63 Shikhar Bhushan
    def _request(self, op):
278 a7cb58ce Shikhar Bhushan
        """Subclasses call this method to make the RPC request.
279 0304f041 Shikhar Bhushan
        
280 0304f041 Shikhar Bhushan
        In synchronous mode, waits until the reply is received and returns
281 a7cb58ce Shikhar Bhushan
        :class:`RPCReply`.
282 0304f041 Shikhar Bhushan
        
283 0304f041 Shikhar Bhushan
        In asynchronous mode, returns immediately, returning a reference to this
284 0304f041 Shikhar Bhushan
        object. The :attr:`event` attribute will be set when the reply has been
285 0304f041 Shikhar Bhushan
        received (see :attr:`reply`) or an error occured (see :attr:`error`).
286 0304f041 Shikhar Bhushan
        
287 a7cb58ce Shikhar Bhushan
        :arg opspec: :ref:`dtree` for the operation
288 a7cb58ce Shikhar Bhushan
        :type opspec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
289 0304f041 Shikhar Bhushan
        :rtype: :class:`RPCReply` (sync) or :class:`RPC` (async)
290 a7cb58ce Shikhar Bhushan
        """
291 0b7d3b31 Shikhar Bhushan
        logger.debug('request %r with opsepc=%r' % (self, op))
292 4de03d63 Shikhar Bhushan
        req = self._build(op)
293 66cd54c8 Shikhar Bhushan
        self._session.send(req)
294 66cd54c8 Shikhar Bhushan
        if self._async:
295 0304f041 Shikhar Bhushan
            logger.debug('async, returning')
296 0304f041 Shikhar Bhushan
            return self
297 4de03d63 Shikhar Bhushan
        else:
298 66cd54c8 Shikhar Bhushan
            logger.debug('sync, will wait for timeout=%r' % self._timeout)
299 66cd54c8 Shikhar Bhushan
            self._event.wait(self._timeout)
300 66cd54c8 Shikhar Bhushan
            if self._event.isSet():
301 66cd54c8 Shikhar Bhushan
                if self._error:
302 6c70b245 Shikhar Bhushan
                    # Error that prevented reply delivery
303 0cdb8b3c Shikhar Bhushan
                    raise self._error
304 66cd54c8 Shikhar Bhushan
                self._reply.parse()
305 6c70b245 Shikhar Bhushan
                if self._reply.error is not None:
306 6c70b245 Shikhar Bhushan
                    # <rpc-error>'s [ RPCError ]
307 6c70b245 Shikhar Bhushan
                    if self._raise_mode == "all":
308 7b02d55b Shikhar Bhushan
                        raise self._reply.error
309 6c70b245 Shikhar Bhushan
                    elif (self._raise_mode == "errors" and
310 6c70b245 Shikhar Bhushan
                          self._reply.error.type == "error"):
311 6c70b245 Shikhar Bhushan
                        raise self._reply.error
312 66cd54c8 Shikhar Bhushan
                return self._reply
313 4de03d63 Shikhar Bhushan
            else:
314 216bb34c Shikhar Bhushan
                raise TimeoutExpiredError
315 4f650d54 Shikhar Bhushan
316 a7cb58ce Shikhar Bhushan
    def request(self, *args, **kwds):
317 216bb34c Shikhar Bhushan
        """Subclasses implement this method. Here, the operation is constructed
318 216bb34c Shikhar Bhushan
        in :ref:`dtree`, and the result of :meth:`_request` returned."""
319 dd225c7a Shikhar Bhushan
        return self._request(self.SPEC)
320 6c70b245 Shikhar Bhushan
    
321 4de03d63 Shikhar Bhushan
    def _assert(self, capability):
322 a7cb58ce Shikhar Bhushan
        """Subclasses can use this method to verify that a capability is available
323 a7cb58ce Shikhar Bhushan
        with the NETCONF server, before making a request that requires it. A
324 216bb34c Shikhar Bhushan
        :exc:`MissingCapabilityError` will be raised if the capability is not
325 a7cb58ce Shikhar Bhushan
        available."""
326 4de03d63 Shikhar Bhushan
        if capability not in self._session.server_capabilities:
327 bbd4ce54 Shikhar Bhushan
            raise MissingCapabilityError('Server does not support [%s]' %
328 bbd4ce54 Shikhar Bhushan
                                         capability)
329 0304f041 Shikhar Bhushan
    
330 a7cb58ce Shikhar Bhushan
    def deliver_reply(self, raw):
331 a7cb58ce Shikhar Bhushan
        # internal use
332 4de03d63 Shikhar Bhushan
        self._reply = self.REPLY_CLS(raw)
333 0304f041 Shikhar Bhushan
        #self._delivery_hook() -- usecase?!
334 a7cb58ce Shikhar Bhushan
        self._event.set()
335 4f650d54 Shikhar Bhushan
336 a7cb58ce Shikhar Bhushan
    def deliver_error(self, err):
337 a7cb58ce Shikhar Bhushan
        # internal use
338 0cdb8b3c Shikhar Bhushan
        self._error = err
339 0304f041 Shikhar Bhushan
        #self._delivery_hook() -- usecase?!
340 a7cb58ce Shikhar Bhushan
        self._event.set()
341 4f650d54 Shikhar Bhushan
342 4de03d63 Shikhar Bhushan
    @property
343 4de03d63 Shikhar Bhushan
    def reply(self):
344 a7cb58ce Shikhar Bhushan
        ":class:`RPCReply` element if reply has been received or :const:`None`"
345 4de03d63 Shikhar Bhushan
        return self._reply
346 4f650d54 Shikhar Bhushan
347 4de03d63 Shikhar Bhushan
    @property
348 a7cb58ce Shikhar Bhushan
    def error(self):
349 a7cb58ce Shikhar Bhushan
        """:exc:`Exception` type if an error occured or :const:`None`.
350 0304f041 Shikhar Bhushan
        
351 a7cb58ce Shikhar Bhushan
        .. note::
352 a7cb58ce Shikhar Bhushan
            This represents an error which prevented a reply from being
353 a7cb58ce Shikhar Bhushan
            received. An *<rpc-error>* does not fall in that category -- see
354 a7cb58ce Shikhar Bhushan
            :class:`RPCReply` for that.
355 a7cb58ce Shikhar Bhushan
        """
356 a7cb58ce Shikhar Bhushan
        return self._error
357 a7cb58ce Shikhar Bhushan
358 a7cb58ce Shikhar Bhushan
    @property
359 4de03d63 Shikhar Bhushan
    def id(self):
360 a7cb58ce Shikhar Bhushan
        "The *message-id* for this RPC"
361 4de03d63 Shikhar Bhushan
        return self._id
362 4f650d54 Shikhar Bhushan
363 4de03d63 Shikhar Bhushan
    @property
364 4de03d63 Shikhar Bhushan
    def session(self):
365 a7cb58ce Shikhar Bhushan
        """The :class:`~ncclient.transport.Session` object associated with this
366 a7cb58ce Shikhar Bhushan
        RPC"""
367 4de03d63 Shikhar Bhushan
        return self._session
368 4f650d54 Shikhar Bhushan
369 4de03d63 Shikhar Bhushan
    @property
370 a7cb58ce Shikhar Bhushan
    def event(self):
371 a7cb58ce Shikhar Bhushan
        """:class:`~threading.Event` that is set when reply has been received or
372 a7cb58ce Shikhar Bhushan
        error occured."""
373 a7cb58ce Shikhar Bhushan
        return self._event
374 a7cb58ce Shikhar Bhushan
375 a7cb58ce Shikhar Bhushan
    def set_async(self, async=True):
376 a7cb58ce Shikhar Bhushan
        """Set asynchronous mode for this RPC."""
377 a7cb58ce Shikhar Bhushan
        self._async = async
378 a7cb58ce Shikhar Bhushan
        if async and not session.can_pipeline:
379 a7cb58ce Shikhar Bhushan
            raise UserWarning('Asynchronous mode not supported for this device/session')
380 a7cb58ce Shikhar Bhushan
381 6c70b245 Shikhar Bhushan
    def set_raise_mode(self, mode):
382 e0e01d37 Shikhar Bhushan
        assert(choice in ('all', 'errors', 'none'))
383 6c70b245 Shikhar Bhushan
        self._raise_mode = mode
384 e0e01d37 Shikhar Bhushan
385 a7cb58ce Shikhar Bhushan
    def set_timeout(self, timeout):
386 a7cb58ce Shikhar Bhushan
        """Set the timeout for synchronous waiting defining how long the RPC
387 a7cb58ce Shikhar Bhushan
        request will block on a reply before raising an error."""
388 a7cb58ce Shikhar Bhushan
        self._timeout = timeout
389 4f650d54 Shikhar Bhushan
390 a7cb58ce Shikhar Bhushan
    #: Whether this RPC is asynchronous
391 4de03d63 Shikhar Bhushan
    async = property(fget=lambda self: self._async, fset=set_async)
392 4f650d54 Shikhar Bhushan
393 a7cb58ce Shikhar Bhushan
    #: Timeout for synchronous waiting
394 4de03d63 Shikhar Bhushan
    timeout = property(fget=lambda self: self._timeout, fset=set_timeout)