Revision 4de03d63

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

  
15
from cStringIO import StringIO
15 16
from xml.etree import cElementTree as ET
16 17

  
17 18
from ncclient import NCClientError
......
44 45

  
45 46
### XML with Python data structures
46 47

  
47
dtree2ele = DictTree.Element
48
dtree2xml = DictTree.XML
49
ele2dtree = Element.DictTree
50
ele2xml = Element.XML
51
xml2dtree = XML.DictTree
52
xml2ele = XML.Element
53

  
54 48
class DictTree:
55 49

  
56 50
    @staticmethod
......
78 72
            raise ContentError('Invalid tree spec')
79 73
    
80 74
    @staticmethod
81
    def XML(spec):
82
        Element.XML(DictTree.Element(spec))
75
    def XML(spec, encoding='utf-8'):
76
        Element.XML(DictTree.Element(spec), encoding)
83 77

  
84 78
class Element:
85 79
    
......
101 95
class XML:
102 96
    
103 97
    @staticmethod
104
    def DictTree(ele):
105
        return Element.DictTree(Element.XML(ele))
98
    def DictTree(xml):
99
        return Element.DictTree(XML.Element(xml))
106 100
    
107 101
    @staticmethod
108 102
    def Element(xml):
109 103
        return ET.fromstring(xml)
110 104

  
105
dtree2ele = DictTree.Element
106
dtree2xml = DictTree.XML
107
ele2dtree = Element.DictTree
108
ele2xml = Element.XML
109
xml2dtree = XML.DictTree
110
xml2ele = XML.Element
111

  
111 112
### Other utility functions
112 113

  
113 114
iselement = ET.iselement
b/ncclient/manager.py
16 16
import operations
17 17
import transport
18 18

  
19
OPERATIONS = {
20
    'get': operations.Get,
21
    'get-config': operations.GetConfig,
22
    'edit-config': operations.EditConfig,
23
    'copy-config': operations.CopyConfig,
24
    'validate': operations.Validate,
25
    'commit': operations.Commit,
26
    'discard-changes': operations.DiscardChanges,
27
    'delete-config': operations.DeleteConfig,
28
    'lock': operations.Lock,
29
    'unlock': operations.Unlock,
30
    'close_session': operations.CloseSession,
31
    'kill-session': operations.KillSession,
32
}
33 19

  
34 20
def connect_ssh(*args, **kwds):
35 21
    session = transport.SSHSession(capabilities.CAPABILITIES)
......
39 25

  
40 26
connect = connect_ssh # default session type
41 27

  
28
RAISE_ALL, RAISE_ERROR, RAISE_NONE = range(3)
29

  
42 30
class Manager:
43 31
    
44 32
    "Thin layer of abstraction for the API."
45 33
    
46
    RAISE_ALL, RAISE_ERROR, RAISE_NONE = range(3)
47
    
48
    def __init__(self, session, rpc_errors=Manager.RAISE_ALL):
34
    def __init__(self, session, rpc_error=RAISE_ALL):
49 35
        self._session = session
50 36
        self._raise = rpc_error
51 37

  
52 38
    def do(self, op, *args, **kwds):
53
        op = OPERATIONS[op](self._session)
39
        op = operations.OPERATIONS[op](self._session)
54 40
        reply = op.request(*args, **kwds)
55 41
        if not reply.ok:
56
            if self._raise == Manager.RAISE_ALL:
42
            if self._raise == RAISE_ALL:
57 43
                raise reply.error
58
            elif self._raise == Manager.RAISE_ERROR:
44
            elif self._raise == RAISE_ERROR:
59 45
                for error in reply.errors:
60 46
                    if error.severity == 'error':
61 47
                        raise error
b/ncclient/operations/rpc.py
23 23
import logging
24 24
logger = logging.getLogger('ncclient.rpc')
25 25

  
26
class RPC(object):
27
    
28
    DEPENDS = []
29
    REPLY_CLS = RPCReply
30
    
31
    def __init__(self, session, async=False, timeout=None):
32
        if not session.can_pipeline:
33
            raise UserWarning('Asynchronous mode not supported for this device/session')
34
        self._session = session
35
        try:
36
            for cap in self.DEPENDS:
37
                self._assert(cap)
38
        except AttributeError:
39
            pass        
40
        self._async = async
41
        self._timeout = timeout
42
        # keeps things simple instead of having a class attr that has to be locked
43
        self._id = uuid1().urn
44
        self._listener = RPCReplyListener(session)
45
        self._listener.register(self._id, self)
46
        self._reply = None
47
        self._reply_event = Event()
48
    
49
    def _build(self, opspec, encoding='utf-8'):
50
        "TODO: docstring"
51
        spec = {
52
            'tag': content.qualify('rpc'),
53
            'attributes': {'message-id': self._id},
54
            'subtree': opspec
55
            }
56
        return content.dtree2xml(encoding)
57
    
58
    def _request(self, op):
59
        req = self._build(op)
60
        self._session.send(req)
61
        if self._async:
62
            return self._reply_event
63
        else:
64
            self._reply_event.wait(self._timeout)
65
            if self._reply_event.isSet():
66
                self._reply.parse()
67
                return self._reply
68
            else:
69
                raise ReplyTimeoutError
70
    
71
    def request(self):
72
        return self._request(self.SPEC)
73
    
74
    def _delivery_hook(self):
75
        'For subclasses'
76
        pass
77
    
78
    def _assert(self, capability):
79
        if capability not in self._session.server_capabilities:
80
            raise MissingCapabilityError('Server does not support [%s]' % cap)
81
    
82
    def deliver(self, raw):
83
        self._reply = self.REPLY_CLS(raw)
84
        self._delivery_hook()
85
        self._reply_event.set()
86
    
87
    @property
88
    def has_reply(self):
89
        return self._reply_event.isSet()
90
    
91
    @property
92
    def reply(self):
93
        return self._reply
94
    
95
    @property
96
    def id(self):
97
        return self._id
98
    
99
    @property
100
    def session(self):
101
        return self._session
102
    
103
    @property
104
    def reply_event(self):
105
        return self._reply_event
106
    
107
    def set_async(self, bool): self._async = bool
108
    async = property(fget=lambda self: self._async, fset=set_async)
109
    
110
    def set_timeout(self, timeout): self._timeout = timeout
111
    timeout = property(fget=lambda self: self._timeout, fset=set_timeout)
112

  
113 26

  
114 27
class RPCReply:
115 28
    
......
235 148
    __repr__ = lambda self: repr(self._dict)
236 149

  
237 150

  
238
class RPCReplyListener:
151
class RPCReplyListener(object):
239 152
    
240 153
    # one instance per session
241 154
    def __new__(cls, session):
......
286 199
    def errback(self, err):
287 200
        if self._errback is not None:
288 201
            self._errback(err)
202

  
203

  
204
class RPC(object):
205
    
206
    DEPENDS = []
207
    REPLY_CLS = RPCReply
208
    
209
    def __init__(self, session, async=False, timeout=None):
210
        if not session.can_pipeline:
211
            raise UserWarning('Asynchronous mode not supported for this device/session')
212
        self._session = session
213
        try:
214
            for cap in self.DEPENDS:
215
                self._assert(cap)
216
        except AttributeError:
217
            pass        
218
        self._async = async
219
        self._timeout = timeout
220
        # keeps things simple instead of having a class attr that has to be locked
221
        self._id = uuid1().urn
222
        # RPCReplyListener itself makes sure there isn't more than one instance -- i.e. multiton
223
        self._listener = RPCReplyListener(session)
224
        self._listener.register(self._id, self)
225
        self._reply = None
226
        self._reply_event = Event()
227
    
228
    def _build(self, opspec, encoding='utf-8'):
229
        "TODO: docstring"
230
        spec = {
231
            'tag': content.qualify('rpc'),
232
            'attributes': {'message-id': self._id},
233
            'subtree': opspec
234
            }
235
        return content.dtree2xml(encoding)
236
    
237
    def _request(self, op):
238
        req = self._build(op)
239
        self._session.send(req)
240
        if self._async:
241
            return self._reply_event
242
        else:
243
            self._reply_event.wait(self._timeout)
244
            if self._reply_event.isSet():
245
                self._reply.parse()
246
                return self._reply
247
            else:
248
                raise ReplyTimeoutError
249
    
250
    def request(self):
251
        return self._request(self.SPEC)
252
    
253
    def _delivery_hook(self):
254
        'For subclasses'
255
        pass
256
    
257
    def _assert(self, capability):
258
        if capability not in self._session.server_capabilities:
259
            raise MissingCapabilityError('Server does not support [%s]' % cap)
260
    
261
    def deliver(self, raw):
262
        self._reply = self.REPLY_CLS(raw)
263
        self._delivery_hook()
264
        self._reply_event.set()
265
    
266
    @property
267
    def has_reply(self):
268
        return self._reply_event.isSet()
269
    
270
    @property
271
    def reply(self):
272
        return self._reply
273
    
274
    @property
275
    def id(self):
276
        return self._id
277
    
278
    @property
279
    def session(self):
280
        return self._session
281
    
282
    @property
283
    def reply_event(self):
284
        return self._reply_event
285
    
286
    def set_async(self, bool): self._async = bool
287
    async = property(fget=lambda self: self._async, fset=set_async)
288
    
289
    def set_timeout(self, timeout): self._timeout = timeout
290
    timeout = property(fget=lambda self: self._timeout, fset=set_timeout)
b/ncclient/operations/subscribe.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
from ncclient.rpc import RPC
15
from rpc import RPC
16 16

  
17 17
from ncclient.glue import Listener
18 18
from ncclient.content import qualify as _
b/ncclient/transport/errors.py
16 16

  
17 17
from ncclient import NCClientError
18 18

  
19
class TransportError(NClientError):
19
class TransportError(NCClientError):
20 20
    pass
21 21

  
22 22
class AuthenticationError(TransportError):
b/ncclient/transport/hello.py
45 45
                    [{'tag': 'capability', 'text': uri} for uri in capabilities]
46 46
                }]
47 47
            }
48
        return content.to_xml(spec)
48
        return content.dtree2xml(spec)
49 49
    
50 50
    @staticmethod
51 51
    def parse(raw):
......
53 53
        sid, capabilities = 0, []
54 54
        root = content.xml2ele(raw)
55 55
        for child in root.getchildren():
56
            tag = content.unqualify(child['tag'])
56
            tag = content.unqualify(child.tag)
57 57
            if tag == 'session-id':
58 58
                sid = child.text
59 59
            elif tag == 'capabilities':
b/ncclient/transport/session.py
12 12
# See the License for the specific language governing permissions and
13 13
# limitations under the License.
14 14

  
15
from threading import Event
16 15
from Queue import Queue
16
from threading import Thread, Lock, Event
17 17

  
18 18
from ncclient.capabilities import Capabilities
19
from ncclient.glue import Subject
19
from ncclient.content import parse_root
20 20

  
21 21
from hello import HelloHandler
22 22

  
23 23
import logging
24 24
logger = logging.getLogger('ncclient.transport.session')
25 25

  
26

  
26 27
class Session(Thread):
27 28
    
28 29
    "TODO: docstring"

Also available in: Unified diff