Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (5 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 541247ba Shikhar Bhushan
# Cisco does not include message-id attribute in <rpc-reply> in case of an error.
28 541247ba Shikhar Bhushan
# This is messed up however we have to deal with it.
29 541247ba Shikhar Bhushan
# So essentially, there can be only one operation at a time if we are talking to
30 541247ba Shikhar Bhushan
# a Cisco device.
31 e91a5349 Shikhar Bhushan
32 5858a82c Shikhar Bhushan
class RPC(object):
33 5858a82c Shikhar Bhushan
    
34 11d9e642 Shikhar Bhushan
    def __init__(self, session, async=False):
35 541247ba Shikhar Bhushan
        if session.is_remote_cisco and async:
36 541247ba Shikhar Bhushan
            raise UserWarning('Asynchronous mode not supported for Cisco devices')
37 38a9b062 Shikhar Bhushan
        self._session = session
38 1d540e60 Shikhar Bhushan
        self._async = async
39 11d9e642 Shikhar Bhushan
        self._id = uuid1().urn
40 5858a82c Shikhar Bhushan
        self._listener = RPCReplyListener(session)
41 5858a82c Shikhar Bhushan
        self._listener.register(self._id, self)
42 3e022b7b Shikhar Bhushan
        self._reply = None
43 11d9e642 Shikhar Bhushan
        self._reply_event = Event()
44 efed7d4c Shikhar Bhushan
    
45 11d9e642 Shikhar Bhushan
    def _build(self, op, encoding='utf-8'):
46 5858a82c Shikhar Bhushan
        if isinstance(op, dict):
47 5858a82c Shikhar Bhushan
            return self.build_from_spec(self._id, op, encoding)
48 11d9e642 Shikhar Bhushan
        else:
49 3e022b7b Shikhar Bhushan
            return self.build_from_string(self._id, op, encoding)
50 efed7d4c Shikhar Bhushan
    
51 e91a5349 Shikhar Bhushan
    def _request(self, op):
52 5858a82c Shikhar Bhushan
        req = self._build(op)
53 efed7d4c Shikhar Bhushan
        self._session.send(req)
54 1d540e60 Shikhar Bhushan
        if self._async:
55 41e2ed46 Shikhar Bhushan
            return self._reply_event
56 41e2ed46 Shikhar Bhushan
        else:
57 ee4bb099 Shikhar Bhushan
            self._reply_event.wait()
58 efed7d4c Shikhar Bhushan
            self._reply.parse()
59 3e022b7b Shikhar Bhushan
            return self._reply
60 ee4bb099 Shikhar Bhushan
    
61 41e2ed46 Shikhar Bhushan
    def _delivery_hook(self):
62 41e2ed46 Shikhar Bhushan
        'For subclasses'
63 41e2ed46 Shikhar Bhushan
        pass
64 41e2ed46 Shikhar Bhushan
    
65 41e2ed46 Shikhar Bhushan
    def deliver(self, raw):
66 6a10f112 Shikhar Bhushan
        self._reply = RPCReply(raw)
67 41e2ed46 Shikhar Bhushan
        self._delivery_hook()
68 6a10f112 Shikhar Bhushan
        self._reply_event.set()
69 41e2ed46 Shikhar Bhushan
    
70 589b23e4 Shikhar Bhushan
    @property
71 35ad9d81 Shikhar Bhushan
    def has_reply(self):
72 3e022b7b Shikhar Bhushan
        return self._reply_event.isSet()
73 589b23e4 Shikhar Bhushan
    
74 589b23e4 Shikhar Bhushan
    @property
75 ee4bb099 Shikhar Bhushan
    def reply(self):
76 ee4bb099 Shikhar Bhushan
        return self._reply
77 ee4bb099 Shikhar Bhushan
    
78 ee4bb099 Shikhar Bhushan
    @property
79 41e2ed46 Shikhar Bhushan
    def is_async(self):
80 41e2ed46 Shikhar Bhushan
        return self._async
81 41e2ed46 Shikhar Bhushan
    
82 41e2ed46 Shikhar Bhushan
    @property
83 38a9b062 Shikhar Bhushan
    def id(self):
84 ee4bb099 Shikhar Bhushan
        return self._id
85 ee4bb099 Shikhar Bhushan
    
86 ee4bb099 Shikhar Bhushan
    @property
87 ee4bb099 Shikhar Bhushan
    def session(self):
88 ee4bb099 Shikhar Bhushan
        return self._session
89 efed7d4c Shikhar Bhushan
    
90 3e022b7b Shikhar Bhushan
    @property
91 3e022b7b Shikhar Bhushan
    def reply_event(self):
92 3e022b7b Shikhar Bhushan
        return self._reply_event
93 3e022b7b Shikhar Bhushan
    
94 11d9e642 Shikhar Bhushan
    @staticmethod
95 11d9e642 Shikhar Bhushan
    def build_from_spec(msgid, opspec, encoding='utf-8'):
96 11d9e642 Shikhar Bhushan
        "TODO: docstring"
97 11d9e642 Shikhar Bhushan
        spec = {
98 1d540e60 Shikhar Bhushan
            'tag': _('rpc'),
99 11d9e642 Shikhar Bhushan
            'attributes': {'message-id': msgid},
100 11d9e642 Shikhar Bhushan
            'children': opspec
101 11d9e642 Shikhar Bhushan
            }
102 11d9e642 Shikhar Bhushan
        return TreeBuilder(spec).to_string(encoding)
103 11d9e642 Shikhar Bhushan
    
104 11d9e642 Shikhar Bhushan
    @staticmethod
105 11d9e642 Shikhar Bhushan
    def build_from_string(msgid, opstr, encoding='utf-8'):
106 11d9e642 Shikhar Bhushan
        "TODO: docstring"
107 11d9e642 Shikhar Bhushan
        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
108 5858a82c Shikhar Bhushan
        doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
109 11d9e642 Shikhar Bhushan
               (msgid, BASE_NS, opstr)).encode(encoding)
110 5858a82c Shikhar Bhushan
        return '%s%s' % (decl, doc)
111 3e022b7b Shikhar Bhushan
112 3e022b7b Shikhar Bhushan
113 3e022b7b Shikhar Bhushan
class RPCReplyListener(Listener):
114 3e022b7b Shikhar Bhushan
    
115 3e022b7b Shikhar Bhushan
    # TODO - determine if need locking
116 3e022b7b Shikhar Bhushan
    
117 541247ba Shikhar Bhushan
    # one instance per session
118 541247ba Shikhar Bhushan
    def __new__(cls, session):
119 541247ba Shikhar Bhushan
        instance = session.get_listener_instance(cls)
120 3e022b7b Shikhar Bhushan
        if instance is None:
121 3e022b7b Shikhar Bhushan
            instance = object.__new__(cls)
122 3e022b7b Shikhar Bhushan
            instance._id2rpc = WeakValueDictionary()
123 541247ba Shikhar Bhushan
            instance._cisco = session.is_remote_cisco
124 3e022b7b Shikhar Bhushan
            instance._errback = None
125 541247ba Shikhar Bhushan
            session.add_listener(instance)
126 3e022b7b Shikhar Bhushan
        return instance
127 3e022b7b Shikhar Bhushan
    
128 3e022b7b Shikhar Bhushan
    def __str__(self):
129 3e022b7b Shikhar Bhushan
        return 'RPCReplyListener'
130 3e022b7b Shikhar Bhushan
    
131 3e022b7b Shikhar Bhushan
    def set_errback(self, errback):
132 3e022b7b Shikhar Bhushan
        self._errback = errback
133 3e022b7b Shikhar Bhushan
134 41e2ed46 Shikhar Bhushan
    def register(self, id, rpc):
135 41e2ed46 Shikhar Bhushan
        self._id2rpc[id] = rpc
136 3e022b7b Shikhar Bhushan
    
137 3e022b7b Shikhar Bhushan
    def callback(self, root, raw):
138 3e022b7b Shikhar Bhushan
        tag, attrs = root
139 3e022b7b Shikhar Bhushan
        if __(tag) != 'rpc-reply':
140 3e022b7b Shikhar Bhushan
            return
141 541247ba Shikhar Bhushan
        rpc = None
142 3e022b7b Shikhar Bhushan
        for key in attrs:
143 3e022b7b Shikhar Bhushan
            if __(key) == 'message-id':
144 3e022b7b Shikhar Bhushan
                id = attrs[key]
145 3e022b7b Shikhar Bhushan
                try:
146 541247ba Shikhar Bhushan
                    rpc = self._id2rpc.pop(id)
147 41e2ed46 Shikhar Bhushan
                except KeyError:
148 541247ba Shikhar Bhushan
                    logger.warning('[RPCReplyListener.callback] no object '
149 3e022b7b Shikhar Bhushan
                                   + 'registered for message-id: [%s]' % id)
150 41e2ed46 Shikhar Bhushan
                except Exception as e:
151 41e2ed46 Shikhar Bhushan
                    logger.debug('[RPCReplyListener.callback] error - %r' % e)
152 3e022b7b Shikhar Bhushan
                break
153 3e022b7b Shikhar Bhushan
        else:
154 541247ba Shikhar Bhushan
            if self._cisco:
155 541247ba Shikhar Bhushan
                assert(len(self._id2rpc) == 1)
156 541247ba Shikhar Bhushan
                rpc = self._id2rpc.values()[0]
157 541247ba Shikhar Bhushan
                self._id2rpc.clear()
158 541247ba Shikhar Bhushan
            else:
159 541247ba Shikhar Bhushan
                logger.warning('<rpc-reply> without message-id received: %s' % raw)
160 541247ba Shikhar Bhushan
        logger.debug('[RPCReplyListener.callback] delivering to %r' % rpc)
161 541247ba Shikhar Bhushan
        rpc.deliver(raw)
162 3e022b7b Shikhar Bhushan
    
163 3e022b7b Shikhar Bhushan
    def errback(self, err):
164 3e022b7b Shikhar Bhushan
        if self._errback is not None:
165 1d540e60 Shikhar Bhushan
            self._errback(err)