Statistics
| Branch: | Tag: | Revision:

root / ncclient / operations / rpc.py @ 3e022b7b

History | View | Annotate | Download (3.9 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

    
18
from ncclient.content import TreeBuilder, BASE_NS
19
from ncclient.glue import Listener
20

    
21
from . import logger
22
from reply import RPCReply
23

    
24

    
25
class RPC(object):
26
    
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)
32
        self._reply = None
33
        self._reply_event = Event()
34
    
35
    def _build(self, op, encoding='utf-8'):
36
        if isinstance(op, dict):
37
            return self.build_from_spec(self._id, op, encoding)
38
        else:
39
            return self.build_from_string(self._id, op, encoding)
40
    
41
    def _request(self, op):
42
        req = self._build(op)
43
        self._session.send(req)
44
        if async:
45
            self._reply_event.wait()
46
            self._reply.parse()
47
            return self._reply
48
    
49
    def deliver(self, raw):
50
        self._reply = RPCReply(raw)
51
        self._reply_event.set()
52
    
53
    @property
54
    def has_reply(self):
55
        return self._reply_event.isSet()
56
    
57
    @property
58
    def reply(self):
59
        return self._reply
60
    
61
    @property
62
    def id(self):
63
        return self._id
64
    
65
    @property
66
    def session(self):
67
        return self._session
68
    
69
    @property
70
    def reply_event(self):
71
        return self._reply_event
72
    
73
    @staticmethod
74
    def build_from_spec(msgid, opspec, encoding='utf-8'):
75
        "TODO: docstring"
76
        spec = {
77
            'tag': _('rpc', BASE_NS),
78
            'attributes': {'message-id': msgid},
79
            'children': opspec
80
            }
81
        return TreeBuilder(spec).to_string(encoding)
82
    
83
    @staticmethod
84
    def build_from_string(msgid, opstr, encoding='utf-8'):
85
        "TODO: docstring"
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)
90

    
91

    
92
class RPCReplyListener(Listener):
93
    
94
    # TODO - determine if need locking
95
    
96
    # one instance per subject    
97
    def __new__(cls, subject):
98
        instance = subject.get_listener_instance(cls)
99
        if instance is None:
100
            instance = object.__new__(cls)
101
            instance._id2rpc = WeakValueDictionary()
102
            instance._errback = None
103
            subject.add_listener(instance)
104
        return instance
105
    
106
    def __str__(self):
107
        return 'RPCReplyListener'
108
    
109
    def set_errback(self, errback):
110
        self._errback = errback
111

    
112
    def register(self, msgid, rpc):
113
        self._id2rpc[msgid] = rpc
114
    
115
    def callback(self, root, raw):
116
        tag, attrs = root
117
        if __(tag) != 'rpc-reply':
118
            return
119
        for key in attrs:
120
            if __(key) == 'message-id':
121
                id = attrs[key]
122
                try:
123
                    rpc = self._id2rpc[id]
124
                    rpc.deliver(raw)
125
                except:
126
                    logger.warning('RPCReplyListener.callback: no RPC '
127
                                   + 'registered for message-id: [%s]' % id)
128
                break
129
        else:
130
            logger.warning('<rpc-reply> without message-id received: %s' % raw)
131
    
132
    def errback(self, err):
133
        logger.error('RPCReplyListener.errback: %r' % err)
134
        if self._errback is not None:
135
            self._errback(err)