Statistics
| Branch: | Tag: | Revision:

root / ncclient / operations / rpc.py @ 1d540e60

History | View | Annotate | Download (4.1 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
            self._reply_event.wait()
50
            self._reply.parse()
51
            return self._reply
52
    
53
    def deliver(self, raw):
54
        self._reply = RPCReply(raw)
55
        self._reply_event.set()
56
    
57
    @property
58
    def has_reply(self):
59
        return self._reply_event.isSet()
60
    
61
    @property
62
    def reply(self):
63
        return self._reply
64
    
65
    @property
66
    def id(self):
67
        return self._id
68
    
69
    @property
70
    def session(self):
71
        return self._session
72
    
73
    @property
74
    def reply_event(self):
75
        return self._reply_event
76
    
77
    @staticmethod
78
    def build_from_spec(msgid, opspec, encoding='utf-8'):
79
        "TODO: docstring"
80
        spec = {
81
            'tag': _('rpc'),
82
            'attributes': {'message-id': msgid},
83
            'children': opspec
84
            }
85
        return TreeBuilder(spec).to_string(encoding)
86
    
87
    @staticmethod
88
    def build_from_string(msgid, opstr, encoding='utf-8'):
89
        "TODO: docstring"
90
        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
91
        doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
92
               (msgid, BASE_NS, opstr)).encode(encoding)
93
        return '%s%s' % (decl, doc)
94

    
95

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

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