Statistics
| Branch: | Tag: | Revision:

root / ncclient / rpc / rpc.py @ cc8de468

History | View | Annotate | Download (5.1 kB)

1 589b23e4 Shikhar Bhushan
# Copyright 2009 Shikhar Bhushan
2 589b23e4 Shikhar Bhushan
#
3 589b23e4 Shikhar Bhushan
# Licensed under the Apache License, Version 2.0 (the "License");
4 589b23e4 Shikhar Bhushan
# you may not use this file except in compliance with the License.
5 589b23e4 Shikhar Bhushan
# You may obtain a copy of the License at
6 589b23e4 Shikhar Bhushan
#
7 589b23e4 Shikhar Bhushan
#    http://www.apache.org/licenses/LICENSE-2.0
8 589b23e4 Shikhar Bhushan
#
9 589b23e4 Shikhar Bhushan
# Unless required by applicable law or agreed to in writing, software
10 589b23e4 Shikhar Bhushan
# distributed under the License is distributed on an "AS IS" BASIS,
11 589b23e4 Shikhar Bhushan
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 589b23e4 Shikhar Bhushan
# See the License for the specific language governing permissions and
13 589b23e4 Shikhar Bhushan
# limitations under the License.
14 589b23e4 Shikhar Bhushan
15 38a9b062 Shikhar Bhushan
from threading import Event, Lock
16 38a9b062 Shikhar Bhushan
from uuid import uuid1
17 1d540e60 Shikhar Bhushan
from weakref import WeakValueDictionary
18 38a9b062 Shikhar Bhushan
19 1d540e60 Shikhar Bhushan
from ncclient.content import TreeBuilder
20 1d540e60 Shikhar Bhushan
from ncclient.content import qualify as _
21 1d540e60 Shikhar Bhushan
from ncclient.content import unqualify as __
22 3e022b7b Shikhar Bhushan
from ncclient.glue import Listener
23 3e022b7b Shikhar Bhushan
24 3e022b7b Shikhar Bhushan
from . import logger
25 3e022b7b Shikhar Bhushan
from reply import RPCReply
26 3e022b7b Shikhar Bhushan
27 1fca349b Shikhar Bhushan
28 541247ba Shikhar Bhushan
# Cisco does not include message-id attribute in <rpc-reply> in case of an error.
29 541247ba Shikhar Bhushan
# This is messed up however we have to deal with it.
30 541247ba Shikhar Bhushan
# So essentially, there can be only one operation at a time if we are talking to
31 541247ba Shikhar Bhushan
# a Cisco device.
32 e91a5349 Shikhar Bhushan
33 1fca349b Shikhar Bhushan
def cisco_check(session):
34 1fca349b Shikhar Bhushan
    try:
35 1fca349b Shikhar Bhushan
        return session.is_remote_cisco
36 1fca349b Shikhar Bhushan
    except AttributeError:
37 1fca349b Shikhar Bhushan
        return False
38 1fca349b Shikhar Bhushan
39 5858a82c Shikhar Bhushan
class RPC(object):
40 5858a82c Shikhar Bhushan
    
41 11d9e642 Shikhar Bhushan
    def __init__(self, session, async=False):
42 1fca349b Shikhar Bhushan
        if cisco_check(session) and async:
43 541247ba Shikhar Bhushan
            raise UserWarning('Asynchronous mode not supported for Cisco devices')
44 38a9b062 Shikhar Bhushan
        self._session = session
45 1d540e60 Shikhar Bhushan
        self._async = async
46 11d9e642 Shikhar Bhushan
        self._id = uuid1().urn
47 5858a82c Shikhar Bhushan
        self._listener = RPCReplyListener(session)
48 5858a82c Shikhar Bhushan
        self._listener.register(self._id, self)
49 3e022b7b Shikhar Bhushan
        self._reply = None
50 11d9e642 Shikhar Bhushan
        self._reply_event = Event()
51 efed7d4c Shikhar Bhushan
    
52 11d9e642 Shikhar Bhushan
    def _build(self, op, encoding='utf-8'):
53 5858a82c Shikhar Bhushan
        if isinstance(op, dict):
54 5858a82c Shikhar Bhushan
            return self.build_from_spec(self._id, op, encoding)
55 11d9e642 Shikhar Bhushan
        else:
56 3e022b7b Shikhar Bhushan
            return self.build_from_string(self._id, op, encoding)
57 efed7d4c Shikhar Bhushan
    
58 e91a5349 Shikhar Bhushan
    def _request(self, op):
59 5858a82c Shikhar Bhushan
        req = self._build(op)
60 efed7d4c Shikhar Bhushan
        self._session.send(req)
61 1d540e60 Shikhar Bhushan
        if self._async:
62 41e2ed46 Shikhar Bhushan
            return self._reply_event
63 41e2ed46 Shikhar Bhushan
        else:
64 ee4bb099 Shikhar Bhushan
            self._reply_event.wait()
65 efed7d4c Shikhar Bhushan
            self._reply.parse()
66 3e022b7b Shikhar Bhushan
            return self._reply
67 ee4bb099 Shikhar Bhushan
    
68 41e2ed46 Shikhar Bhushan
    def _delivery_hook(self):
69 41e2ed46 Shikhar Bhushan
        'For subclasses'
70 41e2ed46 Shikhar Bhushan
        pass
71 41e2ed46 Shikhar Bhushan
    
72 41e2ed46 Shikhar Bhushan
    def deliver(self, raw):
73 6a10f112 Shikhar Bhushan
        self._reply = RPCReply(raw)
74 41e2ed46 Shikhar Bhushan
        self._delivery_hook()
75 6a10f112 Shikhar Bhushan
        self._reply_event.set()
76 41e2ed46 Shikhar Bhushan
    
77 589b23e4 Shikhar Bhushan
    @property
78 35ad9d81 Shikhar Bhushan
    def has_reply(self):
79 3e022b7b Shikhar Bhushan
        return self._reply_event.isSet()
80 589b23e4 Shikhar Bhushan
    
81 589b23e4 Shikhar Bhushan
    @property
82 ee4bb099 Shikhar Bhushan
    def reply(self):
83 ee4bb099 Shikhar Bhushan
        return self._reply
84 ee4bb099 Shikhar Bhushan
    
85 ee4bb099 Shikhar Bhushan
    @property
86 41e2ed46 Shikhar Bhushan
    def is_async(self):
87 41e2ed46 Shikhar Bhushan
        return self._async
88 41e2ed46 Shikhar Bhushan
    
89 41e2ed46 Shikhar Bhushan
    @property
90 38a9b062 Shikhar Bhushan
    def id(self):
91 ee4bb099 Shikhar Bhushan
        return self._id
92 ee4bb099 Shikhar Bhushan
    
93 ee4bb099 Shikhar Bhushan
    @property
94 ee4bb099 Shikhar Bhushan
    def session(self):
95 ee4bb099 Shikhar Bhushan
        return self._session
96 efed7d4c Shikhar Bhushan
    
97 3e022b7b Shikhar Bhushan
    @property
98 3e022b7b Shikhar Bhushan
    def reply_event(self):
99 3e022b7b Shikhar Bhushan
        return self._reply_event
100 3e022b7b Shikhar Bhushan
    
101 11d9e642 Shikhar Bhushan
    @staticmethod
102 11d9e642 Shikhar Bhushan
    def build_from_spec(msgid, opspec, encoding='utf-8'):
103 11d9e642 Shikhar Bhushan
        "TODO: docstring"
104 11d9e642 Shikhar Bhushan
        spec = {
105 1d540e60 Shikhar Bhushan
            'tag': _('rpc'),
106 11d9e642 Shikhar Bhushan
            'attributes': {'message-id': msgid},
107 11d9e642 Shikhar Bhushan
            'children': opspec
108 11d9e642 Shikhar Bhushan
            }
109 11d9e642 Shikhar Bhushan
        return TreeBuilder(spec).to_string(encoding)
110 11d9e642 Shikhar Bhushan
    
111 11d9e642 Shikhar Bhushan
    @staticmethod
112 11d9e642 Shikhar Bhushan
    def build_from_string(msgid, opstr, encoding='utf-8'):
113 11d9e642 Shikhar Bhushan
        "TODO: docstring"
114 11d9e642 Shikhar Bhushan
        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
115 5858a82c Shikhar Bhushan
        doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
116 11d9e642 Shikhar Bhushan
               (msgid, BASE_NS, opstr)).encode(encoding)
117 5858a82c Shikhar Bhushan
        return '%s%s' % (decl, doc)
118 3e022b7b Shikhar Bhushan
119 3e022b7b Shikhar Bhushan
120 3e022b7b Shikhar Bhushan
class RPCReplyListener(Listener):
121 3e022b7b Shikhar Bhushan
    
122 3e022b7b Shikhar Bhushan
    # TODO - determine if need locking
123 3e022b7b Shikhar Bhushan
    
124 541247ba Shikhar Bhushan
    # one instance per session
125 541247ba Shikhar Bhushan
    def __new__(cls, session):
126 541247ba Shikhar Bhushan
        instance = session.get_listener_instance(cls)
127 3e022b7b Shikhar Bhushan
        if instance is None:
128 3e022b7b Shikhar Bhushan
            instance = object.__new__(cls)
129 3e022b7b Shikhar Bhushan
            instance._id2rpc = WeakValueDictionary()
130 1fca349b Shikhar Bhushan
            instance._cisco = cisco_check(session)
131 3e022b7b Shikhar Bhushan
            instance._errback = None
132 541247ba Shikhar Bhushan
            session.add_listener(instance)
133 3e022b7b Shikhar Bhushan
        return instance
134 3e022b7b Shikhar Bhushan
    
135 3e022b7b Shikhar Bhushan
    def __str__(self):
136 3e022b7b Shikhar Bhushan
        return 'RPCReplyListener'
137 3e022b7b Shikhar Bhushan
    
138 3e022b7b Shikhar Bhushan
    def set_errback(self, errback):
139 3e022b7b Shikhar Bhushan
        self._errback = errback
140 3e022b7b Shikhar Bhushan
141 41e2ed46 Shikhar Bhushan
    def register(self, id, rpc):
142 41e2ed46 Shikhar Bhushan
        self._id2rpc[id] = rpc
143 3e022b7b Shikhar Bhushan
    
144 3e022b7b Shikhar Bhushan
    def callback(self, root, raw):
145 3e022b7b Shikhar Bhushan
        tag, attrs = root
146 3e022b7b Shikhar Bhushan
        if __(tag) != 'rpc-reply':
147 3e022b7b Shikhar Bhushan
            return
148 541247ba Shikhar Bhushan
        rpc = None
149 3e022b7b Shikhar Bhushan
        for key in attrs:
150 3e022b7b Shikhar Bhushan
            if __(key) == 'message-id':
151 3e022b7b Shikhar Bhushan
                id = attrs[key]
152 3e022b7b Shikhar Bhushan
                try:
153 541247ba Shikhar Bhushan
                    rpc = self._id2rpc.pop(id)
154 41e2ed46 Shikhar Bhushan
                except KeyError:
155 541247ba Shikhar Bhushan
                    logger.warning('[RPCReplyListener.callback] no object '
156 3e022b7b Shikhar Bhushan
                                   + 'registered for message-id: [%s]' % id)
157 41e2ed46 Shikhar Bhushan
                except Exception as e:
158 41e2ed46 Shikhar Bhushan
                    logger.debug('[RPCReplyListener.callback] error - %r' % e)
159 3e022b7b Shikhar Bhushan
                break
160 3e022b7b Shikhar Bhushan
        else:
161 541247ba Shikhar Bhushan
            if self._cisco:
162 541247ba Shikhar Bhushan
                assert(len(self._id2rpc) == 1)
163 541247ba Shikhar Bhushan
                rpc = self._id2rpc.values()[0]
164 541247ba Shikhar Bhushan
                self._id2rpc.clear()
165 541247ba Shikhar Bhushan
            else:
166 541247ba Shikhar Bhushan
                logger.warning('<rpc-reply> without message-id received: %s' % raw)
167 541247ba Shikhar Bhushan
        logger.debug('[RPCReplyListener.callback] delivering to %r' % rpc)
168 541247ba Shikhar Bhushan
        rpc.deliver(raw)
169 3e022b7b Shikhar Bhushan
    
170 3e022b7b Shikhar Bhushan
    def errback(self, err):
171 3e022b7b Shikhar Bhushan
        if self._errback is not None:
172 1d540e60 Shikhar Bhushan
            self._errback(err)