Statistics
| Branch: | Tag: | Revision:

root / ncclient / operations / rpc.py @ 41e2ed46

History | View | Annotate | Download (4.6 kB)

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 TreeBuilder
20
from ncclient.content import qualify as _
21
from ncclient.content import unqualify as __
22
from ncclient.glue import Listener
23

    
24
from . import logger
25
from reply import RPCReply
26

    
27

    
28
class RPC(object):
29
    
30
    def __init__(self, session, async=False):
31
        self._session = session
32
        self._async = async
33
        self._id = uuid1().urn
34
        self._listener = RPCReplyListener(session)
35
        self._listener.register(self._id, self)
36
        self._reply = None
37
        self._reply_event = Event()
38
    
39
    def _build(self, op, encoding='utf-8'):
40
        if isinstance(op, dict):
41
            return self.build_from_spec(self._id, op, encoding)
42
        else:
43
            return self.build_from_string(self._id, op, encoding)
44
    
45
    def _request(self, op):
46
        req = self._build(op)
47
        self._session.send(req)
48
        if self._async:
49
            return self._reply_event
50
        else:
51
            self._reply_event.wait()
52
            self._reply.parse()
53
            return self._reply
54
    
55
    def _set_reply(self, raw):
56
        self._reply = RPCReply(raw)
57
    
58
    def _set_reply_event(self):
59
        self._reply_event.set()
60
    
61
    def _delivery_hook(self):
62
        'For subclasses'
63
        pass
64
    
65
    def deliver(self, raw):
66
        self._set_reply(raw)
67
        self._delivery_hook()
68
        self._set_reply_event()
69
    
70
    @property
71
    def has_reply(self):
72
        return self._reply_event.isSet()
73
    
74
    @property
75
    def reply(self):
76
        return self._reply
77
    
78
    @property
79
    def is_async(self):
80
        return self._async
81
    
82
    @property
83
    def id(self):
84
        return self._id
85
    
86
    @property
87
    def session(self):
88
        return self._session
89
    
90
    @property
91
    def reply_event(self):
92
        return self._reply_event
93
    
94
    @staticmethod
95
    def build_from_spec(msgid, opspec, encoding='utf-8'):
96
        "TODO: docstring"
97
        spec = {
98
            'tag': _('rpc'),
99
            'attributes': {'message-id': msgid},
100
            'children': opspec
101
            }
102
        return TreeBuilder(spec).to_string(encoding)
103
    
104
    @staticmethod
105
    def build_from_string(msgid, opstr, encoding='utf-8'):
106
        "TODO: docstring"
107
        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
108
        doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
109
               (msgid, BASE_NS, opstr)).encode(encoding)
110
        return '%s%s' % (decl, doc)
111

    
112

    
113
class RPCReplyListener(Listener):
114
    
115
    # TODO - determine if need locking
116
    
117
    # one instance per subject    
118
    def __new__(cls, subject):
119
        instance = subject.get_listener_instance(cls)
120
        if instance is None:
121
            instance = object.__new__(cls)
122
            instance._id2rpc = WeakValueDictionary()
123
            instance._errback = None
124
            subject.add_listener(instance)
125
        return instance
126
    
127
    def __str__(self):
128
        return 'RPCReplyListener'
129
    
130
    def set_errback(self, errback):
131
        self._errback = errback
132

    
133
    def register(self, id, rpc):
134
        self._id2rpc[id] = rpc
135
    
136
    def callback(self, root, raw):
137
        tag, attrs = root
138
        if __(tag) != 'rpc-reply':
139
            return
140
        for key in attrs:
141
            if __(key) == 'message-id':
142
                id = attrs[key]
143
                try:
144
                    rpc = self._id2rpc[id]
145
                    rpc.deliver(raw)
146
                except KeyError:
147
                    logger.warning('[RPCReplyListener.callback] no RPC '
148
                                   + 'registered for message-id: [%s]' % id)
149
                    logger.debug('[RPCReplyListener.callback] registered: %r '
150
                                 % dict(self._id2rpc))
151
                except Exception as e:
152
                    logger.debug('[RPCReplyListener.callback] error - %r' % e)
153
                break
154
        else:
155
            logger.warning('<rpc-reply> without message-id received: %s' % raw)
156
    
157
    def errback(self, err):
158
        if self._errback is not None:
159
            self._errback(err)