Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport/2.9/62587 module_utils/network/cloudengine:fix get_nc_next. #62752

Merged
merged 3 commits into from Oct 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/60569-plugins-netconf-ce.yml
@@ -0,0 +1,2 @@
bugfixes:
- plugins-netconf-ce - to get attribute 'set-id' from rpc-reply.
@@ -0,0 +1,3 @@
bugfixes:
- "Cloudengine module_utils - the ``set-id`` (RPC-REPLY XML attribute) may change over the time althougth ``set-id`` is the identity of the next RPC packet."
- "Cloudengine netconf plugin - add a dispatch RPC function,just return original RPC-REPLY, the function is used by ``Cloudengine module_utils``."
32 changes: 30 additions & 2 deletions lib/ansible/module_utils/network/cloudengine/ce.py
Expand Up @@ -35,19 +35,24 @@

from ansible.module_utils.basic import env_fallback
from ansible.module_utils.network.common.utils import to_list, ComplexList
from ansible.module_utils.connection import exec_command
from ansible.module_utils.connection import exec_command, ConnectionError
from ansible.module_utils.six import iteritems
from ansible.module_utils._text import to_native
from ansible.module_utils.network.common.netconf import NetconfConnection


try:
from ncclient.xml_ import to_xml
from ncclient.xml_ import to_xml, new_ele_ns
HAS_NCCLIENT = True
except ImportError:
HAS_NCCLIENT = False


try:
from lxml import etree
except ImportError:
from xml.etree import ElementTree as etree

_DEVICE_CLI_CONNECTION = None
_DEVICE_NC_CONNECTION = None

Expand Down Expand Up @@ -343,6 +348,29 @@ def set_nc_config(module, xml_str):
return to_string(to_xml(out))


def get_nc_next(module, xml_str):
""" get_nc_next for exchange capability """

conn = get_nc_connection(module)
result = None
if xml_str is not None:
response = conn.get(xml_str, if_rpc_reply=True)
result = response.find('./*')
set_id = response.get('set-id')
while True and set_id is not None:
try:
fetch_node = new_ele_ns('get-next', 'http://www.huawei.com/netconf/capability/base/1.0', {'set-id': set_id})
next_xml = conn.dispatch_rpc(etree.tostring(fetch_node))
if next_xml is not None:
result.extend(next_xml.find('./*'))
set_id = next_xml.get('set-id')
except ConnectionError:
break
if result is not None:
return etree.tostring(result)
return result


def get_nc_config(module, xml_str):
""" get_config """

Expand Down
24 changes: 24 additions & 0 deletions lib/ansible/plugins/netconf/ce.py
Expand Up @@ -36,6 +36,11 @@
except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError
HAS_NCCLIENT = False

try:
from lxml.etree import fromstring
except ImportError:
from xml.etree.ElementTree import fromstring


class Netconf(NetconfBase):

Expand Down Expand Up @@ -167,6 +172,9 @@ def reboot(self):
@ensure_connected
def get(self, *args, **kwargs):
try:
if_rpc_reply = kwargs.pop('if_rpc_reply', False)
if if_rpc_reply:
return self.m.get(*args, **kwargs).xml
return self.m.get(*args, **kwargs).data_xml
except RPCError as exc:
raise Exception(to_xml(exc.xml))
Expand Down Expand Up @@ -210,3 +218,19 @@ def validate(self, *args, **kwargs):
@ensure_connected
def discard_changes(self, *args, **kwargs):
return self.m.discard_changes(*args, **kwargs).data_xml

@ensure_ncclient
@ensure_connected
def dispatch_rpc(self, rpc_command=None, source=None, filter=None):
"""
Execute rpc on the remote device eg. dispatch('get-next')
:param rpc_command: specifies rpc command to be dispatched either in plain text or in xml element format (depending on command)
:param source: name of the configuration datastore being queried
:param filter: specifies the portion of the configuration to retrieve (by default entire configuration is retrieved)
:return: Returns xml string containing the rpc-reply response received from remote host
"""
if rpc_command is None:
raise ValueError('rpc_command value must be provided')
resp = self.m.dispatch(fromstring(rpc_command), source=source, filter=filter)
# just return rpc-reply xml
return resp.xml