return self._raw
def parse(self):
+ if self._parsed: return
root = ET.fromstring(self._raw) # <rpc-reply> element
# per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
oktags = _('ok')
for oktag in oktags:
if root.find(oktag) is not None:
- logger.debug('found %s' % oktag)
+ logger.debug('parsed [%s]' % oktag)
self._parsed = True
return
errtags = _('rpc-error')
for errtag in errtags:
for err in root.getiterator(errtag): # a particular <rpc-error>
+ logger.debug('parsed [%s]' % errtag)
d = {}
for err_detail in err.getchildren(): # <error-type> etc..
- d[__(err_detail)] = err_detail.text
+ d[__(err_detail.tag)] = err_detail.text
self._errors.append(RPCError(d))
if self._errors:
break
items = lambda self: self._dict.items()
- __repr__ = lambda self: repr(self._dict)
\ No newline at end of file
+ __repr__ = lambda self: repr(self._dict)
from . import logger
from reply import RPCReply
+# Cisco does not include message-id attribute in <rpc-reply> in case of an error.
+# This is messed up however we have to deal with it.
+# So essentially, there can be only one operation at a time if we are talking to
+# a Cisco device.
class RPC(object):
def __init__(self, session, async=False):
+ if session.is_remote_cisco and async:
+ raise UserWarning('Asynchronous mode not supported for Cisco devices')
self._session = session
self._async = async
self._id = uuid1().urn
# TODO - determine if need locking
- # one instance per subject
- def __new__(cls, subject):
- instance = subject.get_listener_instance(cls)
+ # one instance per session
+ def __new__(cls, session):
+ instance = session.get_listener_instance(cls)
if instance is None:
instance = object.__new__(cls)
instance._id2rpc = WeakValueDictionary()
+ instance._cisco = session.is_remote_cisco
instance._errback = None
- subject.add_listener(instance)
+ session.add_listener(instance)
return instance
def __str__(self):
tag, attrs = root
if __(tag) != 'rpc-reply':
return
+ rpc = None
for key in attrs:
if __(key) == 'message-id':
id = attrs[key]
try:
- rpc = self._id2rpc[id]
- rpc.deliver(raw)
+ rpc = self._id2rpc.pop(id)
except KeyError:
- logger.warning('[RPCReplyListener.callback] no RPC '
+ logger.warning('[RPCReplyListener.callback] no object '
+ 'registered for message-id: [%s]' % id)
- logger.debug('[RPCReplyListener.callback] registered: %r '
- % dict(self._id2rpc))
except Exception as e:
logger.debug('[RPCReplyListener.callback] error - %r' % e)
break
else:
- logger.warning('<rpc-reply> without message-id received: %s' % raw)
+ if self._cisco:
+ assert(len(self._id2rpc) == 1)
+ rpc = self._id2rpc.values()[0]
+ self._id2rpc.clear()
+ else:
+ logger.warning('<rpc-reply> without message-id received: %s' % raw)
+ logger.debug('[RPCReplyListener.callback] delivering to %r' % rpc)
+ rpc.deliver(raw)
def errback(self, err):
if self._errback is not None:
logger.debug(e)
if saved_exception is not None:
+ # need pep-3134 to do this right
raise SSHAuthenticationError(repr(saved_exception))
raise SSHAuthenticationError('No authentication methods available')
documentation for details.
'''
return self._transport
+
+ @property
+ def is_remote_cisco(self):
+ return 'Cisco' in self._transport.remote_version
class PrintListener(Listener):
def callback(self, root, raw):
- tag, attrs = root
- print '\n$ RECEIVED MESSAGE with root=[tag=%r, attrs=%r]:\n%r\n' % (tag, attrs, raw)
+ print('\n# RECEIVED MESSAGE with root=[tag=%r, attrs=%r] #\n%r\n' %
+ (root[0], root[1], raw))
def errback(self, err):
- print '\n$ RECEIVED ERROR:\n%r\n' % err
+ print('\n# RECEIVED ERROR #\n%r\n' % err)