1 # Copyright 2009 Shikhar Bhushan
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 from threading import Event, Lock
16 from uuid import uuid1
18 from ncclient.content import TreeBuilder, BASE_NS
19 from ncclient.glue import Listener
22 from reply import RPCReply
27 def __init__(self, session, async=False):
28 self._session = session
29 self._id = uuid1().urn
30 self._listener = RPCReplyListener(session)
31 self._listener.register(self._id, self)
33 self._reply_event = Event()
35 def _build(self, op, encoding='utf-8'):
36 if isinstance(op, dict):
37 return self.build_from_spec(self._id, op, encoding)
39 return self.build_from_string(self._id, op, encoding)
41 def _request(self, op):
43 self._session.send(req)
45 self._reply_event.wait()
49 def deliver(self, raw):
50 self._reply = RPCReply(raw)
51 self._reply_event.set()
55 return self._reply_event.isSet()
70 def reply_event(self):
71 return self._reply_event
74 def build_from_spec(msgid, opspec, encoding='utf-8'):
77 'tag': _('rpc', BASE_NS),
78 'attributes': {'message-id': msgid},
81 return TreeBuilder(spec).to_string(encoding)
84 def build_from_string(msgid, opstr, encoding='utf-8'):
86 decl = '<?xml version="1.0" encoding="%s"?>' % encoding
87 doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
88 (msgid, BASE_NS, opstr)).encode(encoding)
89 return '%s%s' % (decl, doc)
92 class RPCReplyListener(Listener):
94 # TODO - determine if need locking
96 # one instance per subject
97 def __new__(cls, subject):
98 instance = subject.get_listener_instance(cls)
100 instance = object.__new__(cls)
101 instance._id2rpc = WeakValueDictionary()
102 instance._errback = None
103 subject.add_listener(instance)
107 return 'RPCReplyListener'
109 def set_errback(self, errback):
110 self._errback = errback
112 def register(self, msgid, rpc):
113 self._id2rpc[msgid] = rpc
115 def callback(self, root, raw):
117 if __(tag) != 'rpc-reply':
120 if __(key) == 'message-id':
123 rpc = self._id2rpc[id]
126 logger.warning('RPCReplyListener.callback: no RPC '
127 + 'registered for message-id: [%s]' % id)
130 logger.warning('<rpc-reply> without message-id received: %s' % raw)
132 def errback(self, err):
133 logger.error('RPCReplyListener.errback: %r' % err)
134 if self._errback is not None: