Revision 11d9e642

b/ncclient/operations/__init__.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
'NETCONF Remote Procedure Calls (RPC) and protocol operations'
15
'NETCONF protocol operations'
16 16

  
17 17
import logging
18 18
logger = logging.getLogger('ncclient.operations')
19 19

  
20
#from retrieve import Get, GetConfig
21
#from edit import EditConfig, DeleteConfig
20
from retrieve import Get, GetConfig
21
from edit import EditConfig, DeleteConfig
22 22
from session import CloseSession, KillSession
23 23
from lock import Lock, Unlock
24
#from notification import CreateSubscription
24
from subscribe import CreateSubscription
25 25

  
26 26
__all__ = [
27
    #'Get',
28
    #'GetConfig',
29
    #'EditConfig',
30
    #'DeleteConfig',
27
    'Get',
28
    'GetConfig',
29
    'EditConfig',
30
    'DeleteConfig',
31 31
    'Lock',
32 32
    'Unlock',
33 33
    'CloseSession',
34 34
    'KillSession',
35
#    'CreateSubscription',
35
    'CreateSubscription',
36
    # hmm
36 37
]
b/ncclient/operations/edit.py
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 rpc import RPC
16

  
17
class EditConfig(RPC):
18
    pass
19

  
20
class DeleteConfig(RPC):
21
    pass
22

  
b/ncclient/operations/listeners.py
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 Lock
16
from weakref import WeakValueDictionary
17

  
18
from . import logger
19

  
20

  
21
class RPCReplyListener(Listener):
22
    
23
    '''This is the glue between received data and the object it should be
24
    forwarded to.
25
    '''
26
    
27
    def __init__(self):
28
        # this dictionary takes care of <rpc-reply> elements received
29
        # { 'message-id': obj } dict
30
        self._id2rpc = WeakValueDictionary()
31
        # this is a more generic dict takes care of other top-level elements
32
        # that may be received, e.g. <notification>'s
33
        # {'tag': obj} dict
34
        self._tag2obj = WeakValueDictionary() 
35
        # if we receive a SessionCloseError it might not be one we want to act on
36
        self._expecting_close = False
37
        self._errback = None # error event callback
38
        self._lock = Lock()
39
    
40
    def __str__(self):
41
        return 'SessionListener'
42
    
43
    def register(self, msgid, rpc):
44
        with self._lock:
45
            self._id2rpc[msgid] = rpc
46
    
47
    def recognize(self, tag, obj):
48
        with self._lock:
49
            self._tag2obj[tag] = obj
50
    
51
    def expect_close(self):
52
        self._expecting_close = True
53
    
54
    @property
55
    def _recognized_elements(self):
56
        elems = q_rpcreply
57
        with self._lock:
58
            elems.extend(self._tag2obj.keys())
59
        return elems
60
    
61
    def set_errback(self, errback):
62
        self._errback = errback
63
    
64
    def received(self, raw):
65
        res = RootParser.parse(raw, self._recognized_elements)
66
        if res is not None:
67
            tag, attrs = res # unpack
68
        else:
69
            return
70
        logger.debug('SessionListener.reply: parsed (%r, %r)' % res)
71
        try:
72
            obj = None
73
            if tag in q_rpcreply:
74
                for key in attrs:
75
                    if __(key) == 'message-id':
76
                        id = attrs[key]
77
                        break
78
                else:
79
                    logger.warning('<rpc-reply> without message-id received: %s'
80
                                   % raw)
81
                obj = self._id2rpc.get(id, None)
82
            else:
83
                obj = self._tag2obj.get(tag, None)
84
            if obj is not None:
85
                obj.deliver(raw)
86
        except Exception as e:
87
            logger.warning('SessionListener.reply: %r' % e)
88
    
89
    def error(self, err):
90
        from ncclient.transport.errors import SessionCloseError
91
        act = True
92
        if isinstance(err, SessionCloseError):
93
            logger.debug('session closed, expecting_close=%s' %
94
                         self._expecting_close)
95
            if self._expecting_close:
96
                act = False
97
        if act:
98
            logger.error('SessionListener.error: %r' % err)
99
            if self._errback is not None:
100
                errback(err)
101

  
102

  
103
class NotificationListener(Listener):
104
    
105
    pass
b/ncclient/operations/lock.py
19 19

  
20 20
class Lock(RPC):
21 21
    
22
    def __init__(self, session):
23
        RPC.__init__(self, session)
22
    def __init__(self, *args, **kwds):
23
        RPC.__init__(self, *args, **kwds)
24 24
        self.spec = {
25 25
            'tag': 'lock',
26 26
            'children': { 'tag': 'target', 'children': {'tag': None} }
27 27
            }
28 28
    
29
    def request(self, target='running', reply_event=None):
29
    def request(self, target='running'):
30 30
        self.spec['children']['children']['tag'] = target
31
        self._do_request(self.spec, reply_event)
31
        self._request(self.spec)
32 32

  
33 33

  
34 34
class Unlock(RPC):
35 35
    
36
    def __init__(self, session):
37
        RPC.__init__(self, session)
36
    def __init__(self, *args, **kwds):
37
        RPC.__init__(self, *args, **kwds)
38 38
        self.spec = {
39 39
            'tag': 'unlock',
40 40
            'children': { 'tag': 'target', 'children': {'tag': None} }
41 41
            }
42 42
    
43
    def request(self, target='running', reply_event=None):
43
    def request(self, target='running'):
44 44
        self.spec['children']['children']['tag'] = target
45
        self._do_request(self.spec, reply_event)
45
        self._request(self.spec)
b/ncclient/operations/retrieve.py
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 rpc import RPC
16

  
17
class Get(RPC):
18
    pass
19

  
20
class GetConfig(RPC):
21
    pass
22

  
b/ncclient/operations/rpc.py
14 14

  
15 15
from threading import Event, Lock
16 16
from uuid import uuid1
17
from weakref import WeakKeyDictionary
18 17

  
19 18
from . import logger
20
from listener import SessionListener
21
from ncclient.content.builders import RPCBuilder
22
from ncclient.content.parsers import RPCReplyParser
23

  
24
_listeners = WeakKeyDictionary()
25
_lock = Lock()
26

  
27
def get_listener(session):
28
    with self._lock:
29
        return _listeners.setdefault(session, ReplyListener())
30 19

  
31 20
class RPC:
32 21
        
33
    def __init__(self, session):
22
    def __init__(self, session, async=False):
34 23
        self._session = session
35
        self._id = None
36
        self._reply = None # RPCReply
37
        self._reply_event = None
24
        self._id = uuid1().urn
25
        self._reply = RPCReply()
26
        self._reply_event = Event()
38 27
    
39
    @property
28
    def _build(self, op, encoding='utf-8'):
29
        if isinstance(op, basestring):
30
            return RPCBuilder.build_from_string(self._id, op, encoding)
31
        else:
32
            return RPCBuilder.build_from_spec(self._id, op, encoding)
40 33
    
41 34
    def _request(self, op):
42
        self._id = uuid1().urn
43 35
        self._reply = RPCReply()
44 36
        # get the listener instance for this session
45 37
        # <rpc-reply> with message id will reach response_cb
......
78 70
    @property
79 71
    def session(self):
80 72
        return self._session
81

  
82
class RPCReply:
83
    
84
    def __init__(self, event):
85
        self._delivery_event = event
86
        self._raw = None
87
        self._errs = None
88
    
89
    def __str__(self):
90
        return self._raw
91
    
92
    def parse(self):
93
        if not self._parsed:
94
            errs = RPCReplyParser.parse(self._raw)
95
            for raw, err_dict in errs:
96
                self._errs.append(RPCError(raw, err_dict))
97
            self._parsed = True
98
    
99
    def deliver(self, raw):
100
        self._raw = raw
101
        self._delivery_event.set()
102
    
103
    def received(self, timeout=None):
104
        self._delivery_event.wait(timeout)
105
        return True
106
    
107
    @property
108
    def raw(self):
109
        return self._raw
110
    
111
    @property
112
    def parsed(self):
113
        return self._parsed
114
    
115
    @property
116
    def ok(self):
117
        return True if self._parsed and not self._errs else False
118
    
119
    @property
120
    def errors(self):
121
        return self._errs
122

  
123
class RPCError(Exception): # raise it if you like
124
    
125
    def __init__(self, raw, err_dict):
126
        self._raw = raw
127
        self._dict = err_dict
128

  
129
    def __str__(self):
130
        # TODO
131
        return self._raw
132
    
133
    def __dict__(self):
134
        return self._dict
135
    
136
    @property
137
    def raw(self):
138
        return self._raw
139 73
    
140
    @property
141
    def type(self):
142
        return self._dict.get('type', None)
143
    
144
    @property
145
    def severity(self):
146
        return self._dict.get('severity', None)
147
    
148
    @property
149
    def tag(self):
150
        return self._dict.get('tag', None)
151
    
152
    @property
153
    def path(self):
154
        return self._dict.get('path', None)
155
    
156
    @property
157
    def message(self):
158
        return self._dict.get('message', None)
159
    
160
    @property
161
    def info(self):
162
        return self._dict.get('info', None)
163

  
164
class Notification:
165
    
166
    pass
167

  
168

  
169

  
170
from builder import TreeBuilder
171
from common import BASE_NS
172
from common import qualify as _
173

  
174
################################################################################
175

  
176
_ = qualify
177

  
178
def build(msgid, op, encoding='utf-8'):
179
    "TODO: docstring"
180
    if isinstance(op, basestring):
181
        return RPCBuilder.build_from_string(msgid, op, encoding)
182
    else:
183
        return RPCBuilder.build_from_spec(msgid, op, encoding)
184

  
185
def build_from_spec(msgid, opspec, encoding='utf-8'):
186
    "TODO: docstring"
187
    spec = {
188
        'tag': _('rpc', BASE_NS),
189
        'attributes': {'message-id': msgid},
190
        'children': opspec
191
        }
192
    return TreeBuilder(spec).to_string(encoding)
193

  
194
def build_from_string(msgid, opstr, encoding='utf-8'):
195
    "TODO: docstring"
196
    decl = '<?xml version="1.0" encoding="%s"?>' % encoding
197
    doc = (u'''<rpc message-id="%s" xmlns="%s">%s</rpc>''' %
198
           (msgid, BASE_NS, opstr)).encode(encoding)
199
    return (decl + doc)
200

  
201
################################################################################
202

  
203
# parsing stuff TODO
204

  
205

  
74
    @staticmethod
75
    def build_from_spec(msgid, opspec, encoding='utf-8'):
76
        "TODO: docstring"
77
        spec = {
78
            'tag': _('rpc', BASE_NS),
79
            'attributes': {'message-id': msgid},
80
            'children': opspec
81
            }
82
        return TreeBuilder(spec).to_string(encoding)
83
    
84
    @staticmethod
85
    def build_from_string(msgid, opstr, encoding='utf-8'):
86
        "TODO: docstring"
87
        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
88
        doc = (u'''<rpc message-id="%s" xmlns="%s">%s</rpc>''' %
89
               (msgid, BASE_NS, opstr)).encode(encoding)
90
        return (decl + doc)
b/ncclient/operations/session.py
19 19

  
20 20
class CloseSession(RPC):
21 21
    
22
    def __init__(self, session):
23
        RPC.__init__(self, session)
22
    def __init__(self, *args, **kwds):
23
        RPC.__init__(self, *args, **kwds)
24 24
        self.spec = { 'tag': 'close-session' }
25 25
    
26 26
    def deliver(self, reply):
......
28 28
        # can't be too huge a reply, should be ok to parse in callback
29 29
        self._reply.parse()
30 30
        if self._reply.ok:
31
            self._listener.expect_close()
31
            self._session.expect_close()
32 32
        self._session.close()
33 33
    
34 34
    def request(self, reply_event=None):
35
        self._do_request(self.spec, reply_event)
35
        self._request(self.spec, reply_event)
36 36

  
37 37

  
38 38
class KillSession(RPC):
39 39
    
40
    def __init__(self, session):
41
        RPC.__init__(self, session)
40
    def __init__(self, *args, **kwds):
41
        RPC.__init__(self, *args, **kwds)
42 42
        self.spec = {
43 43
            'tag': 'kill-session',
44 44
            'children': [ { 'tag': 'session-id', 'text': None} ]
45 45
            }
46 46
    
47
    def request(self, session_id, reply_event=None):
47
    def request(self, session_id):
48 48
        if not isinstance(session_id, basestring): # just make sure...
49 49
            session_id = str(session_id)
50 50
        self.spec['children'][0]['text'] = session_id
51
        self._do_request(self.spec, reply_event)
51
        self._request(self.spec)

Also available in: Unified diff