Skip to content

Commit

Permalink
Merge branch 'master' of github.com:ncclient/ncclient
Browse files Browse the repository at this point in the history
  • Loading branch information
einarnn committed Dec 21, 2019
2 parents 82386a9 + fb449da commit 8d4cc71
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 20 deletions.
1 change: 1 addition & 0 deletions ncclient/devices/huaweiyang.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def get_capabilities(self):
# Just need to replace a single value in the default capabilities
c = []
c.append('urn:ietf:params:netconf:base:1.0')
c.append('urn:ietf:params:netconf:base:1.1')

return c

Expand Down
10 changes: 9 additions & 1 deletion ncclient/operations/edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

from ncclient.operations import util

from .errors import OperationError

import logging

logger = logging.getLogger("ncclient.operations.edit")
Expand Down Expand Up @@ -133,7 +135,7 @@ class Commit(RPC):

DEPENDS = [':candidate']

def request(self, confirmed=False, timeout=None, persist=None):
def request(self, confirmed=False, timeout=None, persist=None, persist_id=None):
"""Commit the candidate configuration as the device's new current configuration. Depends on the `:candidate` capability.
A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no followup commit within the *timeout* interval. If no timeout is specified the confirm timeout defaults to 600 seconds (10 minutes). A confirming commit may have the *confirmed* parameter but this is not required. Depends on the `:confirmed-commit` capability.
Expand All @@ -143,15 +145,21 @@ def request(self, confirmed=False, timeout=None, persist=None):
*timeout* specifies the confirm timeout in seconds
*persist* make the confirmed commit survive a session termination, and set a token on the ongoing confirmed commit
*persist_id* value must be equal to the value given in the <persist> parameter to the original <commit> operation.
"""
node = new_ele("commit")
if (confirmed or persist) and persist_id:
raise OperationError("Invalid operation as confirmed or persist cannot be present with persist-id")
if confirmed:
self._assert(":confirmed-commit")
sub_ele(node, "confirmed")
if timeout is not None:
sub_ele(node, "confirm-timeout").text = timeout
if persist is not None:
sub_ele(node, "persist").text = persist
if persist_id:
sub_ele(node, "persist-id").text = persist_id

return self._request(node)

Expand Down
6 changes: 3 additions & 3 deletions ncclient/operations/subscribe.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ def request(self, filter=None, stream_name=None, start_time=None, stop_time=None
if filter is not None:
node.append(util.build_filter(filter))
if stream_name is not None:
sub_ele(node, "stream").text = stream_name
sub_ele_ns(node, "stream", NETCONF_NOTIFICATION_NS).text = stream_name

if start_time is not None:
sub_ele(node, "startTime").text = start_time
sub_ele_ns(node, "startTime", NETCONF_NOTIFICATION_NS).text = start_time

if stop_time is not None:
if start_time is None:
raise ValueError("You must provide start_time if you provide stop_time")
sub_ele(node, "stopTime").text = stop_time
sub_ele_ns(node, "stopTime", NETCONF_NOTIFICATION_NS).text = stop_time

return self._request(node)
32 changes: 19 additions & 13 deletions ncclient/transport/third_party/junos/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
import logging
logger = logging.getLogger("ncclient.transport.third_party.junos.parser")

RPC_REPLY_END_TAG = "</rpc-reply>"
RPC_REPLY_END_TAG_LEN = len(RPC_REPLY_END_TAG)

RPC_REPLY_START_TAG = "<rpc-reply"
class RPCTags:
RPC_REPLY_END_TAG = "</rpc-reply>"
RPC_REPLY_END_TAG_LEN = len(RPC_REPLY_END_TAG)
RPC_REPLY_START_TAG = "<rpc-reply"
NAMESPACES = {"nc": "urn:ietf:params:xml:ns:netconf:base:1.0"}


class JunosXMLParser(DefaultXMLParser):
Expand All @@ -45,6 +47,7 @@ def __init__(self, session):
self._session = session
self.sax_parser = make_parser()
self.sax_parser.setContentHandler(SAXParser(session))
self.rpc_tags = RPCTags

def parse(self, data):
try:
Expand Down Expand Up @@ -79,9 +82,9 @@ def _delimiter_check(self, data):
# we need to renew parser, as old parser is gone.
self.sax_parser = make_parser()
self.sax_parser.setContentHandler(SAXParser(self._session))
elif RPC_REPLY_END_TAG in data:
elif self.rpc_tags.RPC_REPLY_END_TAG in data:
logger.warning("Check for rpc reply end tag within data received: %s" % data)
msg, delim, remaining = data.partition(RPC_REPLY_END_TAG)
msg, delim, remaining = data.partition(self.rpc_tags.RPC_REPLY_END_TAG)
self._session._buffer.seek(0, os.SEEK_END)
self._session._buffer.write(remaining.encode())
else:
Expand All @@ -91,9 +94,9 @@ def _delimiter_check(self, data):
# if then, wait for next iteration of data and do a recursive call to
# _delimiter_check for MSG_DELIM check
buf = self._session._buffer
buf.seek(buf.tell() - RPC_REPLY_END_TAG_LEN - MSG_DELIM_LEN)
buf.seek(buf.tell() - self.rpc_tags.RPC_REPLY_END_TAG_LEN - MSG_DELIM_LEN)
rpc_response_last_msg = buf.read().decode('UTF-8').replace('\n', '')
if RPC_REPLY_END_TAG in rpc_response_last_msg:
if self.rpc_tags.RPC_REPLY_END_TAG in rpc_response_last_msg:
# rpc_response_last_msg and data can be overlapping
match_obj = difflib.SequenceMatcher(None, rpc_response_last_msg,
data).get_matching_blocks()
Expand All @@ -111,8 +114,9 @@ def _delimiter_check(self, data):
# as first if condition will add full delimiter, so clean
# it off
clean_up = len(rpc_response_last_msg) - (
rpc_response_last_msg.find(RPC_REPLY_END_TAG) +
RPC_REPLY_END_TAG_LEN)
rpc_response_last_msg.find(
self.rpc_tags.RPC_REPLY_END_TAG) +
self.rpc_tags.RPC_REPLY_END_TAG_LEN)
self._session._buffer.truncate(buf.tell() - clean_up)
self._delimiter_check(data.encode())
else:
Expand Down Expand Up @@ -191,7 +195,10 @@ def __init__(self, session):
self._lock = Lock()

def startElement(self, tag, attributes):
if tag == 'rpc-reply':
if tag in ['rpc-reply', 'nc:rpc-reply']:
if tag == 'nc:rpc-reply':
RPCTags.RPC_REPLY_END_TAG = "</nc:rpc-reply>"
RPCTags.RPC_REPLY_START_TAG = "<nc:rpc-reply"
# in case last rpc called used sax parsing and error'd out
# without resetting use_filer in endElement rpc-reply check
with self._lock:
Expand All @@ -215,7 +222,7 @@ def startElement(self, tag, attributes):
if self._cur == self._root and self._cur.tag == tag:
node = self._root
else:
node = self._cur.find(tag)
node = self._cur.find(tag, namespaces=RPCTags.NAMESPACES)

if self._validate_reply_and_sax_tag:
if tag != self._root.tag:
Expand All @@ -231,7 +238,7 @@ def startElement(self, tag, attributes):
self._write_buffer(tag, format_str='<{}{}>', **attributes)
self._cur = node
self._currenttag = tag
elif tag == 'rpc-reply':
elif tag in ['rpc-reply', 'nc:rpc-reply']:
self._write_buffer(tag, format_str='<{}{}>', **attributes)
self._defaulttags.append(tag)
self._validate_reply_and_sax_tag = True
Expand All @@ -245,7 +252,6 @@ def endElement(self, tag):

if tag in self._defaulttags:
self._write_buffer(tag, format_str='</{}>\n')

elif self._cur.tag == tag:
self._write_buffer(tag, format_str='</{}>\n')
self._cur = self._cur.getparent()
Expand Down
6 changes: 3 additions & 3 deletions test/unit/operations/test_subscribe.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_subscribe_stream(self, mock_request):
raise_mode=RaiseMode.ALL)
obj.request(filter=None, stream_name="nameofstream")
node = new_ele_ns("create-subscription", NETCONF_NOTIFICATION_NS)
sub_ele(node, "stream").text = "nameofstream"
sub_ele_ns(node, "stream", NETCONF_NOTIFICATION_NS).text = "nameofstream"
xml = ElementTree.tostring(node)
call = mock_request.call_args_list[0][0][0]
call = ElementTree.tostring(call)
Expand All @@ -60,8 +60,8 @@ def test_subscribe_times(self, mock_request):
raise_mode=RaiseMode.ALL)
obj.request(filter=None, start_time=start_time, stop_time=stop_time)
node = new_ele_ns("create-subscription", NETCONF_NOTIFICATION_NS)
sub_ele(node, "startTime").text = start_time
sub_ele(node, "stopTime").text = stop_time
sub_ele_ns(node, "startTime", NETCONF_NOTIFICATION_NS).text = start_time
sub_ele_ns(node, "stopTime", NETCONF_NOTIFICATION_NS).text = stop_time
xml = ElementTree.tostring(node)
call = mock_request.call_args_list[0][0][0]
call = ElementTree.tostring(call)
Expand Down

0 comments on commit 8d4cc71

Please sign in to comment.