git-svn-id: http://ncclient.googlecode.com/svn/trunk@104 6dbcf712-26ac-11de-a2f3...
[ncclient] / ncclient / operations / rpc / rpc.py
1 # Copyright 2009 Shikhar Bhushan
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 from threading import Event, Lock
16 from uuid import uuid1
17 from weakref import WeakValueDictionary
18
19 from ncclient.content import XMLConverter
20 from ncclient.content import qualify as _
21 from ncclient.content import unqualify as __
22 from ncclient.glue import Listener
23
24 from listener import RPCReplyListener
25 from reply import RPCReply
26
27 import logging
28 logger = logging.getLogger('ncclient.rpc')
29
30 class RPC(object):
31     
32     DEPENDS = []
33     REPLY_CLS = RPCReply
34     
35     def __init__(self, session, async=False, timeout=None):
36         if not session.can_pipeline:
37             raise UserWarning('Asynchronous mode not supported for this device/session')
38         self._session = session
39         try:
40             for cap in self.DEPENDS:
41                 self._assert(cap)
42         except AttributeError:
43             pass        
44         self._async = async
45         self._timeout = timeout
46         self._id = uuid1().urn
47         self._listener = RPCReplyListener(session)
48         self._listener.register(self._id, self)
49         self._reply = None
50         self._reply_event = Event()
51     
52     def _build(self, opspec, encoding='utf-8'):
53         "TODO: docstring"
54         spec = {
55             'tag': _('rpc'),
56             'attributes': {'message-id': self._id},
57             'subtree': opspec
58             }
59         return XMLConverter(spec).to_string(encoding)
60     
61     def _request(self, op):
62         req = self._build(op)
63         self._session.send(req)
64         if self._async:
65             return self._reply_event
66         else:
67             self._reply_event.wait(self._timeout)
68             if self._reply_event.isSet():
69                 self._reply.parse()
70                 return self._reply
71             else:
72                 raise ReplyTimeoutError
73     
74     def request(self):
75         return self._request(self.SPEC)
76     
77     def _delivery_hook(self):
78         'For subclasses'
79         pass
80     
81     def _assert(self, capability):
82         if capability not in self._session.server_capabilities:
83             raise MissingCapabilityError('Server does not support [%s]' % cap)
84     
85     def deliver(self, raw):
86         self._reply = self.REPLY_CLS(raw)
87         self._delivery_hook()
88         self._reply_event.set()
89     
90     @property
91     def has_reply(self):
92         return self._reply_event.isSet()
93     
94     @property
95     def reply(self):
96         return self._reply
97     
98     @property
99     def id(self):
100         return self._id
101     
102     @property
103     def session(self):
104         return self._session
105     
106     @property
107     def reply_event(self):
108         return self._reply_event
109     
110     def set_async(self, bool): self._async = bool
111     async = property(fget=lambda self: self._async, fset=set_async)
112     
113     def set_timeout(self, timeout): self._timeout = timeout
114     timeout = property(fget=lambda self: self._timeout, fset=set_timeout)