Revision 541247ba

b/ncclient/operations/reply.py
31 31
        return self._raw
32 32
    
33 33
    def parse(self):
34
        if self._parsed: return
34 35
        root = ET.fromstring(self._raw) # <rpc-reply> element
35 36
        
36 37
        # per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
37 38
        oktags = _('ok')
38 39
        for oktag in oktags:
39 40
            if root.find(oktag) is not None:
40
                logger.debug('found %s' % oktag)
41
                logger.debug('parsed [%s]' % oktag)
41 42
                self._parsed = True
42 43
                return
43 44
        
......
45 46
        errtags = _('rpc-error')
46 47
        for errtag in errtags:
47 48
            for err in root.getiterator(errtag): # a particular <rpc-error>
49
                logger.debug('parsed [%s]' % errtag)
48 50
                d = {}
49 51
                for err_detail in err.getchildren(): # <error-type> etc..
50
                    d[__(err_detail)] = err_detail.text
52
                    d[__(err_detail.tag)] = err_detail.text
51 53
                self._errors.append(RPCError(d))
52 54
            if self._errors:
53 55
                break
......
128 130
    
129 131
    items = lambda self: self._dict.items()
130 132
    
131
    __repr__ = lambda self: repr(self._dict)
133
    __repr__ = lambda self: repr(self._dict)
b/ncclient/operations/rpc.py
24 24
from . import logger
25 25
from reply import RPCReply
26 26

  
27
# Cisco does not include message-id attribute in <rpc-reply> in case of an error.
28
# This is messed up however we have to deal with it.
29
# So essentially, there can be only one operation at a time if we are talking to
30
# a Cisco device.
27 31

  
28 32
class RPC(object):
29 33
    
30 34
    def __init__(self, session, async=False):
35
        if session.is_remote_cisco and async:
36
            raise UserWarning('Asynchronous mode not supported for Cisco devices')
31 37
        self._session = session
32 38
        self._async = async
33 39
        self._id = uuid1().urn
......
114 120
    
115 121
    # TODO - determine if need locking
116 122
    
117
    # one instance per subject    
118
    def __new__(cls, subject):
119
        instance = subject.get_listener_instance(cls)
123
    # one instance per session
124
    def __new__(cls, session):
125
        instance = session.get_listener_instance(cls)
120 126
        if instance is None:
121 127
            instance = object.__new__(cls)
122 128
            instance._id2rpc = WeakValueDictionary()
129
            instance._cisco = session.is_remote_cisco
123 130
            instance._errback = None
124
            subject.add_listener(instance)
131
            session.add_listener(instance)
125 132
        return instance
126 133
    
127 134
    def __str__(self):
......
137 144
        tag, attrs = root
138 145
        if __(tag) != 'rpc-reply':
139 146
            return
147
        rpc = None
140 148
        for key in attrs:
141 149
            if __(key) == 'message-id':
142 150
                id = attrs[key]
143 151
                try:
144
                    rpc = self._id2rpc[id]
145
                    rpc.deliver(raw)
152
                    rpc = self._id2rpc.pop(id)
146 153
                except KeyError:
147
                    logger.warning('[RPCReplyListener.callback] no RPC '
154
                    logger.warning('[RPCReplyListener.callback] no object '
148 155
                                   + 'registered for message-id: [%s]' % id)
149
                    logger.debug('[RPCReplyListener.callback] registered: %r '
150
                                 % dict(self._id2rpc))
151 156
                except Exception as e:
152 157
                    logger.debug('[RPCReplyListener.callback] error - %r' % e)
153 158
                break
154 159
        else:
155
            logger.warning('<rpc-reply> without message-id received: %s' % raw)
160
            if self._cisco:
161
                assert(len(self._id2rpc) == 1)
162
                rpc = self._id2rpc.values()[0]
163
                self._id2rpc.clear()
164
            else:
165
                logger.warning('<rpc-reply> without message-id received: %s' % raw)
166
        logger.debug('[RPCReplyListener.callback] delivering to %r' % rpc)
167
        rpc.deliver(raw)
156 168
    
157 169
    def errback(self, err):
158 170
        if self._errback is not None:
b/ncclient/operations/session.py
27 27
    def _delivery_hook(self):
28 28
        if self.reply.ok:
29 29
            self.session.expect_close()
30
        self.session.close()
30 31
    
31 32
    def request(self):
32 33
        return self._request(self.spec)
b/ncclient/transport/ssh.py
241 241
                logger.debug(e)
242 242
        
243 243
        if saved_exception is not None:
244
            # need pep-3134 to do this right
244 245
            raise SSHAuthenticationError(repr(saved_exception))
245 246
        
246 247
        raise SSHAuthenticationError('No authentication methods available')
......
286 287
        documentation for details.
287 288
        '''
288 289
        return self._transport
290
    
291
    @property
292
    def is_remote_cisco(self):
293
        return 'Cisco' in self._transport.remote_version
b/ncclient/util.py
20 20
class PrintListener(Listener):
21 21
    
22 22
    def callback(self, root, raw):
23
        tag, attrs = root
24
        print '\n$ RECEIVED MESSAGE with root=[tag=%r, attrs=%r]:\n%r\n' % (tag, attrs, raw)
23
        print('\n# RECEIVED MESSAGE with root=[tag=%r, attrs=%r] #\n%r\n' %
24
              (root[0], root[1], raw))
25 25
    
26 26
    def errback(self, err):
27
        print '\n$ RECEIVED ERROR:\n%r\n' % err
27
        print('\n# RECEIVED ERROR #\n%r\n' % err)

Also available in: Unified diff