Statistics
| Branch: | Tag: | Revision:

root / ncclient / operations / rpc.py @ bd7957fb

History | View | Annotate | Download (12.5 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 9667bcb2 Shikhar Bhushan
from ncclient.xml_ import *
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 9667bcb2 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 9667bcb2 Shikhar Bhushan
        root = self._root = to_ele(self._raw) # The <rpc-reply> element
59 9667bcb2 Shikhar Bhushan
        # Per RFC 4741 an <ok/> tag is sent when there are no errors or warnings
60 9667bcb2 Shikhar Bhushan
        ok = root.find(qualify("ok"))
61 9667bcb2 Shikhar Bhushan
        if ok is None:
62 9667bcb2 Shikhar Bhushan
            # Create RPCError objects from <rpc-error> elements
63 9667bcb2 Shikhar Bhushan
            error = root.find(qualify("rpc-error"))
64 a14c36f9 Shikhar Bhushan
            if error is not None:
65 a14c36f9 Shikhar Bhushan
                for err in root.getiterator(error.tag):
66 9667bcb2 Shikhar Bhushan
                    # Process a particular <rpc-error>
67 9667bcb2 Shikhar Bhushan
                    self._errors.append(RPCError(err))
68 a14c36f9 Shikhar Bhushan
        self._parsing_hook(root)
69 a14c36f9 Shikhar Bhushan
        self._parsed = True
70 4f650d54 Shikhar Bhushan
71 a14c36f9 Shikhar Bhushan
    @property
72 a14c36f9 Shikhar Bhushan
    def xml(self):
73 a7cb58ce Shikhar Bhushan
        "*<rpc-reply>* as returned"
74 a14c36f9 Shikhar Bhushan
        return self._raw
75 4f650d54 Shikhar Bhushan
76 a14c36f9 Shikhar Bhushan
    @property
77 a14c36f9 Shikhar Bhushan
    def ok(self):
78 a7cb58ce Shikhar Bhushan
        "Boolean value indicating if there were no errors."
79 a14c36f9 Shikhar Bhushan
        if not self._parsed:
80 a14c36f9 Shikhar Bhushan
            self.parse()
81 a14c36f9 Shikhar Bhushan
        return not self._errors # empty list => false
82 4f650d54 Shikhar Bhushan
83 a14c36f9 Shikhar Bhushan
    @property
84 a14c36f9 Shikhar Bhushan
    def error(self):
85 216bb34c Shikhar Bhushan
        """Short for :attr:`errors` [0]; :const:`None` if there were no errors.
86 216bb34c Shikhar Bhushan
        """
87 a14c36f9 Shikhar Bhushan
        if not self._parsed:
88 a14c36f9 Shikhar Bhushan
            self.parse()
89 a14c36f9 Shikhar Bhushan
        if self._errors:
90 a14c36f9 Shikhar Bhushan
            return self._errors[0]
91 a14c36f9 Shikhar Bhushan
        else:
92 a14c36f9 Shikhar Bhushan
            return None
93 4f650d54 Shikhar Bhushan
94 a14c36f9 Shikhar Bhushan
    @property
95 a14c36f9 Shikhar Bhushan
    def errors(self):
96 216bb34c Shikhar Bhushan
        """`list` of :class:`RPCError` objects. Will be empty if there were no
97 216bb34c Shikhar Bhushan
        *<rpc-error>* elements in reply.
98 216bb34c Shikhar Bhushan
        """
99 a14c36f9 Shikhar Bhushan
        if not self._parsed:
100 a14c36f9 Shikhar Bhushan
            self.parse()
101 a14c36f9 Shikhar Bhushan
        return self._errors
102 a14c36f9 Shikhar Bhushan
103 a14c36f9 Shikhar Bhushan
104 9667bcb2 Shikhar Bhushan
class RPCError(OperationError):
105 4f650d54 Shikhar Bhushan
106 9667bcb2 Shikhar Bhushan
    """Represents an *<rpc-error>*. It is a type of :exc:`OperationError`
107 2abf9522 Shikhar Bhushan
    and can be raised like any other exception."""
108 a7cb58ce Shikhar Bhushan
109 9667bcb2 Shikhar Bhushan
    def __init__(self, err):
110 9667bcb2 Shikhar Bhushan
        self._type = None
111 bd7957fb Shikhar Bhushan
        self._tag = None
112 9667bcb2 Shikhar Bhushan
        self._severity = None
113 9667bcb2 Shikhar Bhushan
        self._info = None
114 9667bcb2 Shikhar Bhushan
        self._path = None
115 9667bcb2 Shikhar Bhushan
        self._message = None
116 9667bcb2 Shikhar Bhushan
        for subele in err:
117 22566666 Shikhar Bhushan
            if subele.tag == qualify("error-type"):
118 22566666 Shikhar Bhushan
                self._type = subele.text
119 22566666 Shikhar Bhushan
            elif subele.tag == qualify("error-tag"):
120 9667bcb2 Shikhar Bhushan
                self._tag = subele.text
121 9667bcb2 Shikhar Bhushan
            elif subele.tag == qualify("error-severity"):
122 9667bcb2 Shikhar Bhushan
                self._severity = subele.text
123 9667bcb2 Shikhar Bhushan
            elif subele.tag == qualify("error-info"):
124 9667bcb2 Shikhar Bhushan
                self._info = subele.text
125 9667bcb2 Shikhar Bhushan
            elif subele.tag == qualify("error-path"):
126 9667bcb2 Shikhar Bhushan
                self._path = subele.text
127 9667bcb2 Shikhar Bhushan
            elif subele.tag == qualify("error-message"):
128 9667bcb2 Shikhar Bhushan
                self._message = subele.text
129 a14c36f9 Shikhar Bhushan
        if self.message is not None:
130 d771dffc Shikhar Bhushan
            OperationError.__init__(self, self.message)
131 a14c36f9 Shikhar Bhushan
        else:
132 c1bdbd86 Shikhar Bhushan
            OperationError.__init__(self, self.to_dict())
133 22566666 Shikhar Bhushan
    
134 22566666 Shikhar Bhushan
    def to_dict(self):
135 22566666 Shikhar Bhushan
        return {
136 22566666 Shikhar Bhushan
            'type': self.type,
137 bd7957fb Shikhar Bhushan
            'tag': self.tag,
138 bd7957fb Shikhar Bhushan
            'severity': self.severity,
139 22566666 Shikhar Bhushan
            'path': self.path,
140 22566666 Shikhar Bhushan
            'message': self.message,
141 22566666 Shikhar Bhushan
            'info': self.info
142 22566666 Shikhar Bhushan
        }
143 22566666 Shikhar Bhushan
    
144 a14c36f9 Shikhar Bhushan
    @property
145 a14c36f9 Shikhar Bhushan
    def type(self):
146 2abf9522 Shikhar Bhushan
        "`string` representing text of *error-type* element"
147 9667bcb2 Shikhar Bhushan
        return self._type
148 bd7957fb Shikhar Bhushan
    
149 a14c36f9 Shikhar Bhushan
    @property
150 a14c36f9 Shikhar Bhushan
    def tag(self):
151 2abf9522 Shikhar Bhushan
        "`string` representing text of *error-tag* element"
152 9667bcb2 Shikhar Bhushan
        return self._tag
153 bd7957fb Shikhar Bhushan
    
154 bd7957fb Shikhar Bhushan
    @property
155 bd7957fb Shikhar Bhushan
    def severity(self):
156 bd7957fb Shikhar Bhushan
        "`string` representing text of *error-severity* element"
157 bd7957fb Shikhar Bhushan
        return self._severity
158 bd7957fb Shikhar Bhushan
    
159 a14c36f9 Shikhar Bhushan
    @property
160 a14c36f9 Shikhar Bhushan
    def path(self):
161 2abf9522 Shikhar Bhushan
        "`string` or :const:`None`; representing text of *error-path* element"
162 9667bcb2 Shikhar Bhushan
        return self._path
163 4f650d54 Shikhar Bhushan
164 a14c36f9 Shikhar Bhushan
    @property
165 a14c36f9 Shikhar Bhushan
    def message(self):
166 2abf9522 Shikhar Bhushan
        "`string` or :const:`None`; representing text of *error-message* element"
167 9667bcb2 Shikhar Bhushan
        return self._message
168 4f650d54 Shikhar Bhushan
169 a14c36f9 Shikhar Bhushan
    @property
170 a14c36f9 Shikhar Bhushan
    def info(self):
171 2abf9522 Shikhar Bhushan
        "`string` (XML) or :const:`None`, representing *error-info* element"
172 9667bcb2 Shikhar Bhushan
        return self._info
173 a14c36f9 Shikhar Bhushan
174 a14c36f9 Shikhar Bhushan
175 0cdb8b3c Shikhar Bhushan
class RPCReplyListener(SessionListener):
176 4f650d54 Shikhar Bhushan
177 a7cb58ce Shikhar Bhushan
    # internal use
178 a7cb58ce Shikhar Bhushan
179 7b02d55b Shikhar Bhushan
    # one instance per session -- maybe there is a better way??
180 a14c36f9 Shikhar Bhushan
    def __new__(cls, session):
181 a14c36f9 Shikhar Bhushan
        instance = session.get_listener_instance(cls)
182 a14c36f9 Shikhar Bhushan
        if instance is None:
183 a14c36f9 Shikhar Bhushan
            instance = object.__new__(cls)
184 a14c36f9 Shikhar Bhushan
            instance._lock = Lock()
185 6a2dfeb4 Shikhar Bhushan
            instance._id2rpc = {}
186 0304f041 Shikhar Bhushan
            #instance._pipelined = session.can_pipeline
187 a14c36f9 Shikhar Bhushan
            session.add_listener(instance)
188 a14c36f9 Shikhar Bhushan
        return instance
189 4f650d54 Shikhar Bhushan
190 a14c36f9 Shikhar Bhushan
    def register(self, id, rpc):
191 a14c36f9 Shikhar Bhushan
        with self._lock:
192 a14c36f9 Shikhar Bhushan
            self._id2rpc[id] = rpc
193 4f650d54 Shikhar Bhushan
194 a14c36f9 Shikhar Bhushan
    def callback(self, root, raw):
195 a14c36f9 Shikhar Bhushan
        tag, attrs = root
196 9667bcb2 Shikhar Bhushan
        if tag != qualify("rpc-reply"):
197 a14c36f9 Shikhar Bhushan
            return
198 0304f041 Shikhar Bhushan
        for key in attrs: # in the <rpc-reply> attributes
199 9667bcb2 Shikhar Bhushan
            if key == "message-id": # if we found msgid attr
200 0304f041 Shikhar Bhushan
                id = attrs[key] # get the msgid
201 9667bcb2 Shikhar Bhushan
                with self._lock:
202 9667bcb2 Shikhar Bhushan
                    try:                    
203 9667bcb2 Shikhar Bhushan
                        rpc = self._id2rpc[id] # the corresponding rpc
204 9667bcb2 Shikhar Bhushan
                        logger.debug("Delivering to %r" % rpc)
205 0304f041 Shikhar Bhushan
                        rpc.deliver_reply(raw)
206 9667bcb2 Shikhar Bhushan
                    except KeyError:
207 9667bcb2 Shikhar Bhushan
                        raise OperationError("Unknown message-id: %s", id)
208 9667bcb2 Shikhar Bhushan
                    # no catching other exceptions, fail loudly if must
209 9667bcb2 Shikhar Bhushan
                    else:
210 9667bcb2 Shikhar Bhushan
                        # if no error delivering, can del the reference to the RPC
211 9667bcb2 Shikhar Bhushan
                        del self._id2rpc[id]
212 9667bcb2 Shikhar Bhushan
                        break
213 a14c36f9 Shikhar Bhushan
        else:
214 9667bcb2 Shikhar Bhushan
            raise OperationError("Could not find 'message-id' attribute in <rpc-reply>")
215 0304f041 Shikhar Bhushan
    
216 a14c36f9 Shikhar Bhushan
    def errback(self, err):
217 6a2dfeb4 Shikhar Bhushan
        try:
218 6a2dfeb4 Shikhar Bhushan
            for rpc in self._id2rpc.values():
219 6a2dfeb4 Shikhar Bhushan
                rpc.deliver_error(err)
220 6a2dfeb4 Shikhar Bhushan
        finally:
221 6a2dfeb4 Shikhar Bhushan
            self._id2rpc.clear()
222 4de03d63 Shikhar Bhushan
223 4de03d63 Shikhar Bhushan
224 4de03d63 Shikhar Bhushan
class RPC(object):
225 4f650d54 Shikhar Bhushan
226 216bb34c Shikhar Bhushan
    """Base class for all operations.
227 216bb34c Shikhar Bhushan

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