Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (13.2 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 9667bcb2 Shikhar Bhushan
class RPCError(OperationError):
28 4f650d54 Shikhar Bhushan
29 19e7c7f6 Shikhar Bhushan
    "Represents an `rpc-error`. It is a type of :exc:`OperationError` and can be raised as such."
30 11bab021 Shikhar Bhushan
    
31 11bab021 Shikhar Bhushan
    tag_to_attr = {
32 11bab021 Shikhar Bhushan
        qualify("error-type"): "_type",
33 11bab021 Shikhar Bhushan
        qualify("error-tag"): "_tag",
34 11bab021 Shikhar Bhushan
        qualify("error-severity"): "_severity",
35 11bab021 Shikhar Bhushan
        qualify("error-info"): "_info",
36 11bab021 Shikhar Bhushan
        qualify("error-path"): "_path",
37 11bab021 Shikhar Bhushan
        qualify("error-message"): "_message"
38 11bab021 Shikhar Bhushan
    }
39 11bab021 Shikhar Bhushan
    
40 4bc8021f Shikhar Bhushan
    def __init__(self, raw):
41 4bc8021f Shikhar Bhushan
        self._raw = raw
42 7c4ff459 Shikhar Bhushan
        for attr in RPCError.tag_to_attr.values():
43 11bab021 Shikhar Bhushan
            setattr(self, attr, None)
44 4bc8021f Shikhar Bhushan
        for subele in raw:
45 7c4ff459 Shikhar Bhushan
            attr = RPCError.tag_to_attr.get(subele.tag, None)
46 11bab021 Shikhar Bhushan
            if attr is not None:
47 0c55eea9 Shikhar Bhushan
                setattr(self, attr, subele.text if attr != "_info" else to_xml(subele) )
48 a14c36f9 Shikhar Bhushan
        if self.message is not None:
49 d771dffc Shikhar Bhushan
            OperationError.__init__(self, self.message)
50 a14c36f9 Shikhar Bhushan
        else:
51 c1bdbd86 Shikhar Bhushan
            OperationError.__init__(self, self.to_dict())
52 22566666 Shikhar Bhushan
    
53 22566666 Shikhar Bhushan
    def to_dict(self):
54 0c55eea9 Shikhar Bhushan
        return dict([ (attr[1:], getattr(self, attr)) for attr in RPCError.tag_to_attr.values() ])
55 22566666 Shikhar Bhushan
    
56 4bc8021f Shikhar Bhushan
    @property
57 4bc8021f Shikhar Bhushan
    def xml(self):
58 19e7c7f6 Shikhar Bhushan
        "The `rpc-error` element as returned in XML."
59 4bc8021f Shikhar Bhushan
        return self._raw
60 4bc8021f Shikhar Bhushan
    
61 a14c36f9 Shikhar Bhushan
    @property
62 a14c36f9 Shikhar Bhushan
    def type(self):
63 19e7c7f6 Shikhar Bhushan
        "The contents of the `error-type` element."
64 9667bcb2 Shikhar Bhushan
        return self._type
65 bd7957fb Shikhar Bhushan
    
66 a14c36f9 Shikhar Bhushan
    @property
67 a14c36f9 Shikhar Bhushan
    def tag(self):
68 19e7c7f6 Shikhar Bhushan
        "The contents of the `error-tag` element."
69 9667bcb2 Shikhar Bhushan
        return self._tag
70 bd7957fb Shikhar Bhushan
    
71 bd7957fb Shikhar Bhushan
    @property
72 bd7957fb Shikhar Bhushan
    def severity(self):
73 19e7c7f6 Shikhar Bhushan
        "The contents of the `error-severity` element."
74 bd7957fb Shikhar Bhushan
        return self._severity
75 bd7957fb Shikhar Bhushan
    
76 a14c36f9 Shikhar Bhushan
    @property
77 a14c36f9 Shikhar Bhushan
    def path(self):
78 19e7c7f6 Shikhar Bhushan
        "The contents of the `error-path` element if present or `None`."
79 9667bcb2 Shikhar Bhushan
        return self._path
80 11bab021 Shikhar Bhushan
    
81 a14c36f9 Shikhar Bhushan
    @property
82 a14c36f9 Shikhar Bhushan
    def message(self):
83 19e7c7f6 Shikhar Bhushan
        "The contents of the `error-message` element if present or `None`."
84 9667bcb2 Shikhar Bhushan
        return self._message
85 11bab021 Shikhar Bhushan
    
86 a14c36f9 Shikhar Bhushan
    @property
87 a14c36f9 Shikhar Bhushan
    def info(self):
88 19e7c7f6 Shikhar Bhushan
        "XML string or `None`; representing the `error-info` element."
89 9667bcb2 Shikhar Bhushan
        return self._info
90 a14c36f9 Shikhar Bhushan
91 a14c36f9 Shikhar Bhushan
92 4bc8021f Shikhar Bhushan
class RPCReply:
93 4bc8021f Shikhar Bhushan
94 4bc8021f Shikhar Bhushan
    """Represents an *rpc-reply*. Only concerns itself with whether the operation was successful.
95 4bc8021f Shikhar Bhushan

96 4bc8021f Shikhar Bhushan
    .. note::
97 4bc8021f Shikhar Bhushan
        If the reply has not yet been parsed there is an implicit, one-time parsing overhead to
98 c15671aa Shikhar Bhushan
        accessing some of the attributes defined by this class.
99 4bc8021f Shikhar Bhushan
    """
100 4bc8021f Shikhar Bhushan
    
101 4bc8021f Shikhar Bhushan
    ERROR_CLS = RPCError
102 4bc8021f Shikhar Bhushan
    "Subclasses can specify a different error class, but it should be a subclass of `RPCError`."
103 4bc8021f Shikhar Bhushan
    
104 4bc8021f Shikhar Bhushan
    def __init__(self, raw):
105 4bc8021f Shikhar Bhushan
        self._raw = raw
106 4bc8021f Shikhar Bhushan
        self._parsed = False
107 4bc8021f Shikhar Bhushan
        self._root = None
108 4bc8021f Shikhar Bhushan
        self._errors = []
109 4bc8021f Shikhar Bhushan
110 4bc8021f Shikhar Bhushan
    def __repr__(self):
111 4bc8021f Shikhar Bhushan
        return self._raw
112 4bc8021f Shikhar Bhushan
    
113 4bc8021f Shikhar Bhushan
    def parse(self):
114 4bc8021f Shikhar Bhushan
        "Parses the *rpc-reply*."
115 4bc8021f Shikhar Bhushan
        if self._parsed: return
116 4bc8021f Shikhar Bhushan
        root = self._root = to_ele(self._raw) # The <rpc-reply> element
117 4bc8021f Shikhar Bhushan
        # Per RFC 4741 an <ok/> tag is sent when there are no errors or warnings
118 4bc8021f Shikhar Bhushan
        ok = root.find(qualify("ok"))
119 4bc8021f Shikhar Bhushan
        if ok is None:
120 4bc8021f Shikhar Bhushan
            # Create RPCError objects from <rpc-error> elements
121 4bc8021f Shikhar Bhushan
            error = root.find(qualify("rpc-error"))
122 4bc8021f Shikhar Bhushan
            if error is not None:
123 4bc8021f Shikhar Bhushan
                for err in root.getiterator(error.tag):
124 4bc8021f Shikhar Bhushan
                    # Process a particular <rpc-error>
125 b2d60e49 Shikhar Bhushan
                    self._errors.append(self.ERROR_CLS(err))
126 b2d60e49 Shikhar Bhushan
        self._parsing_hook(root)
127 4bc8021f Shikhar Bhushan
        self._parsed = True
128 b2d60e49 Shikhar Bhushan
129 b2d60e49 Shikhar Bhushan
    def _parsing_hook(self, root):
130 c15671aa Shikhar Bhushan
        "No-op by default. Gets passed the *root* element for the reply."
131 b2d60e49 Shikhar Bhushan
        pass
132 4bc8021f Shikhar Bhushan
    
133 4bc8021f Shikhar Bhushan
    @property
134 4bc8021f Shikhar Bhushan
    def xml(self):
135 4bc8021f Shikhar Bhushan
        "*rpc-reply* element as returned."
136 4bc8021f Shikhar Bhushan
        return self._raw
137 4bc8021f Shikhar Bhushan
    
138 4bc8021f Shikhar Bhushan
    @property
139 4bc8021f Shikhar Bhushan
    def ok(self):
140 4bc8021f Shikhar Bhushan
        "Boolean value indicating if there were no errors."
141 4bc8021f Shikhar Bhushan
        return not self.errors # empty list => false
142 4bc8021f Shikhar Bhushan
    
143 4bc8021f Shikhar Bhushan
    @property
144 4bc8021f Shikhar Bhushan
    def error(self):
145 19e7c7f6 Shikhar Bhushan
        "Returns the first :class:`RPCError` and `None` if there were no errors."
146 4bc8021f Shikhar Bhushan
        self.parse()
147 4bc8021f Shikhar Bhushan
        if self._errors:
148 4bc8021f Shikhar Bhushan
            return self._errors[0]
149 4bc8021f Shikhar Bhushan
        else:
150 4bc8021f Shikhar Bhushan
            return None
151 4bc8021f Shikhar Bhushan
    
152 4bc8021f Shikhar Bhushan
    @property
153 4bc8021f Shikhar Bhushan
    def errors(self):
154 19e7c7f6 Shikhar Bhushan
        "List of `RPCError` objects. Will be empty if there were no *rpc-error* elements in reply."
155 4bc8021f Shikhar Bhushan
        self.parse()
156 4bc8021f Shikhar Bhushan
        return self._errors
157 4bc8021f Shikhar Bhushan
158 4bc8021f Shikhar Bhushan
159 11bab021 Shikhar Bhushan
class RPCReplyListener(SessionListener): # internal use
160 11bab021 Shikhar Bhushan
    
161 11bab021 Shikhar Bhushan
    creation_lock = Lock()
162 11bab021 Shikhar Bhushan
    
163 7b02d55b Shikhar Bhushan
    # one instance per session -- maybe there is a better way??
164 a14c36f9 Shikhar Bhushan
    def __new__(cls, session):
165 7c4ff459 Shikhar Bhushan
        with RPCReplyListener.creation_lock:
166 11bab021 Shikhar Bhushan
            instance = session.get_listener_instance(cls)
167 11bab021 Shikhar Bhushan
            if instance is None:
168 11bab021 Shikhar Bhushan
                instance = object.__new__(cls)
169 11bab021 Shikhar Bhushan
                instance._lock = Lock()
170 11bab021 Shikhar Bhushan
                instance._id2rpc = {}
171 11bab021 Shikhar Bhushan
                #instance._pipelined = session.can_pipeline
172 11bab021 Shikhar Bhushan
                session.add_listener(instance)
173 11bab021 Shikhar Bhushan
            return instance
174 4f650d54 Shikhar Bhushan
175 a14c36f9 Shikhar Bhushan
    def register(self, id, rpc):
176 a14c36f9 Shikhar Bhushan
        with self._lock:
177 a14c36f9 Shikhar Bhushan
            self._id2rpc[id] = rpc
178 4f650d54 Shikhar Bhushan
179 a14c36f9 Shikhar Bhushan
    def callback(self, root, raw):
180 a14c36f9 Shikhar Bhushan
        tag, attrs = root
181 9667bcb2 Shikhar Bhushan
        if tag != qualify("rpc-reply"):
182 a14c36f9 Shikhar Bhushan
            return
183 0304f041 Shikhar Bhushan
        for key in attrs: # in the <rpc-reply> attributes
184 9667bcb2 Shikhar Bhushan
            if key == "message-id": # if we found msgid attr
185 0304f041 Shikhar Bhushan
                id = attrs[key] # get the msgid
186 9667bcb2 Shikhar Bhushan
                with self._lock:
187 11bab021 Shikhar Bhushan
                    try:
188 9667bcb2 Shikhar Bhushan
                        rpc = self._id2rpc[id] # the corresponding rpc
189 9667bcb2 Shikhar Bhushan
                        logger.debug("Delivering to %r" % rpc)
190 0304f041 Shikhar Bhushan
                        rpc.deliver_reply(raw)
191 9667bcb2 Shikhar Bhushan
                    except KeyError:
192 11bab021 Shikhar Bhushan
                        raise OperationError("Unknown 'message-id': %s", id)
193 9667bcb2 Shikhar Bhushan
                    # no catching other exceptions, fail loudly if must
194 9667bcb2 Shikhar Bhushan
                    else:
195 9667bcb2 Shikhar Bhushan
                        # if no error delivering, can del the reference to the RPC
196 9667bcb2 Shikhar Bhushan
                        del self._id2rpc[id]
197 9667bcb2 Shikhar Bhushan
                        break
198 a14c36f9 Shikhar Bhushan
        else:
199 9667bcb2 Shikhar Bhushan
            raise OperationError("Could not find 'message-id' attribute in <rpc-reply>")
200 0304f041 Shikhar Bhushan
    
201 a14c36f9 Shikhar Bhushan
    def errback(self, err):
202 6a2dfeb4 Shikhar Bhushan
        try:
203 6a2dfeb4 Shikhar Bhushan
            for rpc in self._id2rpc.values():
204 6a2dfeb4 Shikhar Bhushan
                rpc.deliver_error(err)
205 6a2dfeb4 Shikhar Bhushan
        finally:
206 6a2dfeb4 Shikhar Bhushan
            self._id2rpc.clear()
207 4de03d63 Shikhar Bhushan
208 4de03d63 Shikhar Bhushan
209 19e7c7f6 Shikhar Bhushan
class RaiseMode(object):
210 19e7c7f6 Shikhar Bhushan
211 19e7c7f6 Shikhar Bhushan
    NONE = 0
212 19e7c7f6 Shikhar Bhushan
    "Don't attempt to raise any type of `rpc-error` as :exc:`RPCError`."
213 19e7c7f6 Shikhar Bhushan
214 19e7c7f6 Shikhar Bhushan
    ERRORS = 1
215 19e7c7f6 Shikhar Bhushan
    "Raise only when the `error-type` indicates it is an honest-to-god error."
216 19e7c7f6 Shikhar Bhushan
217 19e7c7f6 Shikhar Bhushan
    ALL = 2
218 19e7c7f6 Shikhar Bhushan
    "Don't look at the `error-type`, always raise."
219 19e7c7f6 Shikhar Bhushan
220 19e7c7f6 Shikhar Bhushan
221 4de03d63 Shikhar Bhushan
class RPC(object):
222 4bc8021f Shikhar Bhushan
    
223 c15671aa Shikhar Bhushan
    """Base class for all operations, directly corresponding to *rpc* requests. Handles making the request, and taking delivery of the reply."""
224 19e7c7f6 Shikhar Bhushan
225 4de03d63 Shikhar Bhushan
    DEPENDS = []
226 c15671aa Shikhar Bhushan
    """Subclasses can specify their dependencies on capabilities as a list of URI's or abbreviated names, e.g. ':writable-running'. These are verified at the time of instantiation. If the capability is not available, :exc:`MissingCapabilityError` is raised."""
227 4bc8021f Shikhar Bhushan
    
228 4de03d63 Shikhar Bhushan
    REPLY_CLS = RPCReply
229 c15671aa Shikhar Bhushan
    "By default :class:`RPCReply`. Subclasses can specify a :class:`RPCReply` subclass."
230 4bc8021f Shikhar Bhushan
    
231 19e7c7f6 Shikhar Bhushan
    def __init__(self, session, async=False, timeout=30, raise_mode=RaiseMode.NONE):
232 c15671aa Shikhar Bhushan
        """
233 c15671aa Shikhar Bhushan
        *session* is the :class:`~ncclient.transport.Session` instance
234 c15671aa Shikhar Bhushan

235 c15671aa Shikhar Bhushan
        *async* specifies whether the request is to be made asynchronously, see :attr:`is_async`
236 c15671aa Shikhar Bhushan

237 c15671aa Shikhar Bhushan
        *timeout* is the timeout for a synchronous request, see :attr:`timeout`
238 c15671aa Shikhar Bhushan

239 c15671aa Shikhar Bhushan
        *raise_mode* specifies the exception raising mode, see :attr:`raise_mode`
240 c15671aa Shikhar Bhushan
        """
241 4de03d63 Shikhar Bhushan
        self._session = session
242 4de03d63 Shikhar Bhushan
        try:
243 4de03d63 Shikhar Bhushan
            for cap in self.DEPENDS:
244 4de03d63 Shikhar Bhushan
                self._assert(cap)
245 4de03d63 Shikhar Bhushan
        except AttributeError:
246 4f650d54 Shikhar Bhushan
            pass
247 4de03d63 Shikhar Bhushan
        self._async = async
248 4de03d63 Shikhar Bhushan
        self._timeout = timeout
249 6c70b245 Shikhar Bhushan
        self._raise_mode = raise_mode
250 4bc8021f Shikhar Bhushan
        self._id = uuid1().urn # Keeps things simple instead of having a class attr with running ID that has to be locked
251 4de03d63 Shikhar Bhushan
        self._listener = RPCReplyListener(session)
252 4de03d63 Shikhar Bhushan
        self._listener.register(self._id, self)
253 4de03d63 Shikhar Bhushan
        self._reply = None
254 4f650d54 Shikhar Bhushan
        self._error = None
255 a7cb58ce Shikhar Bhushan
        self._event = Event()
256 4bc8021f Shikhar Bhushan
    
257 4bc8021f Shikhar Bhushan
    def _wrap(self, subele):
258 4bc8021f Shikhar Bhushan
        # internal use
259 0c89e420 Leonidas Poulopoulos
        ele = new_ele("rpc", {"message-id": self._id})
260 9667bcb2 Shikhar Bhushan
        ele.append(subele)
261 9667bcb2 Shikhar Bhushan
        return to_xml(ele)
262 4f650d54 Shikhar Bhushan
263 4de03d63 Shikhar Bhushan
    def _request(self, op):
264 19e7c7f6 Shikhar Bhushan
        """Implementations of :meth:`request` call this method to send the request and process the reply.
265 0304f041 Shikhar Bhushan
        
266 19e7c7f6 Shikhar Bhushan
        In synchronous mode, blocks until the reply is received and returns :class:`RPCReply`. Depending on the :attr:`raise_mode` a `rpc-error` element in the reply may lead to an :exc:`RPCError` exception.
267 0304f041 Shikhar Bhushan
        
268 c15671aa Shikhar Bhushan
        In asynchronous mode, returns immediately, returning `self`. The :attr:`event` attribute will be set when the reply has been received (see :attr:`reply`) or an error occured (see :attr:`error`).
269 4bc8021f Shikhar Bhushan
        
270 c15671aa Shikhar Bhushan
        *op* is the operation to be requested as an :class:`~xml.etree.ElementTree.Element`
271 a7cb58ce Shikhar Bhushan
        """
272 9667bcb2 Shikhar Bhushan
        logger.info('Requesting %r' % self.__class__.__name__)
273 4bc8021f Shikhar Bhushan
        req = self._wrap(op)
274 66cd54c8 Shikhar Bhushan
        self._session.send(req)
275 66cd54c8 Shikhar Bhushan
        if self._async:
276 9667bcb2 Shikhar Bhushan
            logger.debug('Async request, returning %r', self)
277 0304f041 Shikhar Bhushan
            return self
278 4de03d63 Shikhar Bhushan
        else:
279 b2d60e49 Shikhar Bhushan
            logger.debug('Sync request, will wait for timeout=%r' % self._timeout)
280 66cd54c8 Shikhar Bhushan
            self._event.wait(self._timeout)
281 66cd54c8 Shikhar Bhushan
            if self._event.isSet():
282 66cd54c8 Shikhar Bhushan
                if self._error:
283 6c70b245 Shikhar Bhushan
                    # Error that prevented reply delivery
284 0cdb8b3c Shikhar Bhushan
                    raise self._error
285 66cd54c8 Shikhar Bhushan
                self._reply.parse()
286 6c70b245 Shikhar Bhushan
                if self._reply.error is not None:
287 6c70b245 Shikhar Bhushan
                    # <rpc-error>'s [ RPCError ]
288 19e7c7f6 Shikhar Bhushan
                    if self._raise_mode == RaiseMode.ALL:
289 7b02d55b Shikhar Bhushan
                        raise self._reply.error
290 19e7c7f6 Shikhar Bhushan
                    elif (self._raise_mode == RaiseMode.ERRORS and self._reply.error.type == "error"):
291 6c70b245 Shikhar Bhushan
                        raise self._reply.error
292 66cd54c8 Shikhar Bhushan
                return self._reply
293 4de03d63 Shikhar Bhushan
            else:
294 216bb34c Shikhar Bhushan
                raise TimeoutExpiredError
295 4f650d54 Shikhar Bhushan
296 b2d60e49 Shikhar Bhushan
    def request(self):
297 4bc8021f Shikhar Bhushan
        """Subclasses must implement this method. Typically only the request needs to be built as an
298 c15671aa Shikhar Bhushan
        :class:`~xml.etree.ElementTree.Element` and everything else can be handed off to
299 4bc8021f Shikhar Bhushan
        :meth:`_request`."""
300 4bc8021f Shikhar Bhushan
        pass
301 6c70b245 Shikhar Bhushan
    
302 4de03d63 Shikhar Bhushan
    def _assert(self, capability):
303 4bc8021f Shikhar Bhushan
        """Subclasses can use this method to verify that a capability is available with the NETCONF
304 4bc8021f Shikhar Bhushan
        server, before making a request that requires it. A :exc:`MissingCapabilityError` will be
305 4bc8021f Shikhar Bhushan
        raised if the capability is not available."""
306 4de03d63 Shikhar Bhushan
        if capability not in self._session.server_capabilities:
307 b2d60e49 Shikhar Bhushan
            raise MissingCapabilityError('Server does not support [%s]' % capability)
308 0304f041 Shikhar Bhushan
    
309 a7cb58ce Shikhar Bhushan
    def deliver_reply(self, raw):
310 a7cb58ce Shikhar Bhushan
        # internal use
311 4de03d63 Shikhar Bhushan
        self._reply = self.REPLY_CLS(raw)
312 a7cb58ce Shikhar Bhushan
        self._event.set()
313 4f650d54 Shikhar Bhushan
314 a7cb58ce Shikhar Bhushan
    def deliver_error(self, err):
315 a7cb58ce Shikhar Bhushan
        # internal use
316 0cdb8b3c Shikhar Bhushan
        self._error = err
317 a7cb58ce Shikhar Bhushan
        self._event.set()
318 4bc8021f Shikhar Bhushan
    
319 4de03d63 Shikhar Bhushan
    @property
320 4de03d63 Shikhar Bhushan
    def reply(self):
321 19e7c7f6 Shikhar Bhushan
        ":class:`RPCReply` element if reply has been received or `None`"
322 4de03d63 Shikhar Bhushan
        return self._reply
323 4bc8021f Shikhar Bhushan
    
324 4de03d63 Shikhar Bhushan
    @property
325 a7cb58ce Shikhar Bhushan
    def error(self):
326 19e7c7f6 Shikhar Bhushan
        """:exc:`Exception` type if an error occured or `None`.
327 0304f041 Shikhar Bhushan
        
328 a7cb58ce Shikhar Bhushan
        .. note::
329 4bc8021f Shikhar Bhushan
            This represents an error which prevented a reply from being received. An *rpc-error*
330 4bc8021f Shikhar Bhushan
            does not fall in that category -- see `RPCReply` for that.
331 a7cb58ce Shikhar Bhushan
        """
332 a7cb58ce Shikhar Bhushan
        return self._error
333 4bc8021f Shikhar Bhushan
    
334 a7cb58ce Shikhar Bhushan
    @property
335 4de03d63 Shikhar Bhushan
    def id(self):
336 4bc8021f Shikhar Bhushan
        "The *message-id* for this RPC."
337 4de03d63 Shikhar Bhushan
        return self._id
338 4bc8021f Shikhar Bhushan
    
339 4de03d63 Shikhar Bhushan
    @property
340 4de03d63 Shikhar Bhushan
    def session(self):
341 4bc8021f Shikhar Bhushan
        "The `~ncclient.transport.Session` object associated with this RPC."
342 4de03d63 Shikhar Bhushan
        return self._session
343 4f650d54 Shikhar Bhushan
344 4de03d63 Shikhar Bhushan
    @property
345 a7cb58ce Shikhar Bhushan
    def event(self):
346 c15671aa Shikhar Bhushan
        """:class:`~threading.Event` that is set when reply has been received or when an error preventing
347 4bc8021f Shikhar Bhushan
        delivery of the reply occurs.
348 4bc8021f Shikhar Bhushan
        """
349 a7cb58ce Shikhar Bhushan
        return self._event
350 a7cb58ce Shikhar Bhushan
351 19e7c7f6 Shikhar Bhushan
    def __set_async(self, async=True):
352 a7cb58ce Shikhar Bhushan
        self._async = async
353 a7cb58ce Shikhar Bhushan
        if async and not session.can_pipeline:
354 a7cb58ce Shikhar Bhushan
            raise UserWarning('Asynchronous mode not supported for this device/session')
355 a7cb58ce Shikhar Bhushan
356 19e7c7f6 Shikhar Bhushan
    def __set_raise_mode(self, mode):
357 9667bcb2 Shikhar Bhushan
        assert(choice in ("all", "errors", "none"))
358 6c70b245 Shikhar Bhushan
        self._raise_mode = mode
359 e0e01d37 Shikhar Bhushan
360 19e7c7f6 Shikhar Bhushan
    def __set_timeout(self, timeout):
361 a7cb58ce Shikhar Bhushan
        self._timeout = timeout
362 4f650d54 Shikhar Bhushan
363 19e7c7f6 Shikhar Bhushan
    raise_mode = property(fget=lambda self: self._raise_mode, fset=__set_raise_mode)
364 19e7c7f6 Shikhar Bhushan
    """Depending on this exception raising mode, an `rpc-error` in the reply may be raised as an :exc:`RPCError` exception. Valid values are the constants defined in :class:`RaiseMode`. """
365 4bc8021f Shikhar Bhushan
    
366 19e7c7f6 Shikhar Bhushan
    is_async = property(fget=lambda self: self._async, fset=__set_async)
367 c15671aa Shikhar Bhushan
    """Specifies whether this RPC will be / was requested asynchronously. By default RPC's are synchronous."""
368 4bc8021f Shikhar Bhushan
    
369 19e7c7f6 Shikhar Bhushan
    timeout = property(fget=lambda self: self._timeout, fset=__set_timeout)
370 19e7c7f6 Shikhar Bhushan
    """Timeout in seconds for synchronous waiting defining how long the RPC request will block on a reply before raising :exc:`TimeoutExpiredError`.
371 4bc8021f Shikhar Bhushan
    
372 4bc8021f Shikhar Bhushan
    Irrelevant for asynchronous usage.
373 4bc8021f Shikhar Bhushan
    """