Skip to content

Commit

Permalink
Merge 07be80b into 10ebf71
Browse files Browse the repository at this point in the history
  • Loading branch information
einarnn committed Sep 8, 2019
2 parents 10ebf71 + 07be80b commit ad8894f
Show file tree
Hide file tree
Showing 9 changed files with 513 additions and 17 deletions.
9 changes: 6 additions & 3 deletions ncclient/devices/iosxe.py
Expand Up @@ -11,16 +11,17 @@
generic information needed for interaction with a Netconf server.
"""


from .default import DefaultDeviceHandler

from ncclient.operations.third_party.iosxe.rpc import SaveConfig
from ncclient.operations.third_party.iosxe.rpc import EstablishSubscription
from ncclient.operations.third_party.iosxe.rpc import DeleteSubscription


def iosxe_unknown_host_cb(host, fingerprint):
#This will ignore the unknown host check when connecting to CSR devices
return True


class IosxeDeviceHandler(DefaultDeviceHandler):
"""
Cisco IOS-XE handler for device specific information.
Expand All @@ -32,6 +33,8 @@ def __init__(self, device_params):
def add_additional_operations(self):
dict = {}
dict["save_config"] = SaveConfig
dict["establish_subscription"] = EstablishSubscription
dict["delete_subscription"] = DeleteSubscription
return dict

def add_additional_ssh_connect_params(self, kwargs):
Expand Down
3 changes: 3 additions & 0 deletions ncclient/operations/errors.py
Expand Up @@ -22,3 +22,6 @@ class TimeoutExpiredError(NCClientError):

class MissingCapabilityError(NCClientError):
pass

class AlreadyHasEventListener(NCClientError):
pass
30 changes: 25 additions & 5 deletions ncclient/operations/rpc.py
Expand Up @@ -162,6 +162,10 @@ def _parsing_hook(self, root):
"No-op by default. Gets passed the *root* element for the reply."
pass

def _post_process(self, original_rpc):
'''No-op by default. Gets passed the original RPC we got a reply to.'''
pass

@property
def xml(self):
"*rpc-reply* element as returned."
Expand Down Expand Up @@ -313,17 +317,32 @@ def _wrap(self, subele):
ele.append(subele)
return to_xml(ele)

def _request(self, op):
def _request(self, op, raw_xml=None):
"""Implementations of :meth:`request` call this method to send the request and process the reply.
In synchronous mode, blocks until the reply is received and returns :class:`RPCReply`. Depending on the :attr:`raise_mode` a `rpc-error` element in the reply may lead to an :exc:`RPCError` exception.
In synchronous mode, blocks until the reply is received and
returns :class:`RPCReply`. Depending on the :attr:`raise_mode`
a `rpc-error` element in the reply may lead to an
:exc:`RPCError` exception.
In asynchronous mode, returns immediately, returning
`self`. The :attr:`event` attribute will be set when the reply
has been received (see :attr:`reply`) or an error occured (see
:attr:`error`).
In asynchronous mode, returns immediately, returning `self`. The :attr:`event` attribute will be set when the reply has been received (see :attr:`reply`) or an error occured (see :attr:`error`).
*op* is the operation to be requested as an
:class:`~xml.etree.ElementTree.Element`
*raw_xml* is the ability to send a raw XML string for the
RPC. Used if the default marshaling cannot handle the message
requirements.
*op* is the operation to be requested as an :class:`~xml.etree.ElementTree.Element`
"""
self.logger.info('Requesting %r', self.__class__.__name__)
req = self._wrap(op)
if op is not None:
req = self._wrap(op)
else:
req = raw_xml
self._session.send(req)
if self._async:
self.logger.debug('Async request, returning %r', self)
Expand All @@ -336,6 +355,7 @@ def _request(self, op):
# Error that prevented reply delivery
raise self._error
self._reply.parse()
self._reply._post_process(self)
if self._reply.error is not None and not self._device_handler.is_rpc_error_exempt(self._reply.error.message):
# <rpc-error>'s [ RPCError ]

Expand Down
20 changes: 17 additions & 3 deletions ncclient/operations/subscribe.py
Expand Up @@ -12,12 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.


from ncclient.operations.rpc import RPC

from ncclient.xml_ import *

from ncclient.operations import util
from ncclient.operations.errors import AlreadyHasEventListener
from ncclient.transport.session import NotificationHandler


class CreateSubscription(RPC):
"`create-subscription` RPC. Depends on the `:notification` capability."
Expand Down Expand Up @@ -47,6 +47,8 @@ def request(self, filter=None, stream_name=None, start_time=None, stop_time=None
terminated. The format is an RFC 3339/ISO 8601 date and time.
"""
if self.session.has_event_listener:
raise AlreadyHasEventListener()
node = new_ele_ns("create-subscription", NETCONF_NOTIFICATION_NS)
if filter is not None:
node.append(util.build_filter(filter))
Expand All @@ -61,4 +63,16 @@ def request(self, filter=None, stream_name=None, start_time=None, stop_time=None
raise ValueError("You must provide start_time if you provide stop_time")
sub_ele(node, "stopTime").text = stop_time

# Install the listener if necessary, error if there is a
# conflicting listener already installed, such as a Yang Push
# Listener. Note that any new event listener type will have to
# do something siimilar if they consume the same events.
if not hasattr(self.session, 'notification_listener'):
if self.session.has_event_listener:
raise AlreadyHasEventListener()
else:
self.session.notification_listener = NotificationHandler(self.session._notification_q)
self.session.add_listener(self.session.notification_listener)
self.session.has_event_listener = True

return self._request(node)

0 comments on commit ad8894f

Please sign in to comment.