[stable-2.8] Move missing library abort to use rather than import (#55649)
* [stable-2.8] Move missing library abort to use rather than import for netconf (#55384)
(cherry picked from commit b442706
)
Co-authored-by: Nathaniel Case <this.is@nathanielca.se>
* Add changelog
This commit is contained in:
parent
cec67624ba
commit
a87b36219b
6 changed files with 66 additions and 52 deletions
2
changelogs/fragments/55384-netconf-import.yaml
Normal file
2
changelogs/fragments/55384-netconf-import.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
bugfixes:
|
||||
- Move netconf import errors from import to use.
|
|
@ -24,13 +24,17 @@ from functools import wraps
|
|||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.plugins import AnsiblePlugin
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
|
||||
try:
|
||||
from ncclient.operations import RPCError
|
||||
from ncclient.xml_ import to_xml, to_ele
|
||||
except ImportError:
|
||||
raise AnsibleError("ncclient is not installed")
|
||||
HAS_NCCLIENT = True
|
||||
NCCLIENT_IMP_ERR = None
|
||||
except (ImportError, AttributeError) as err: # paramiko and gssapi are incompatible and raise AttributeError not ImportError
|
||||
HAS_NCCLIENT = False
|
||||
NCCLIENT_IMP_ERR = err
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement, tostring, fromstring
|
||||
|
@ -47,6 +51,15 @@ def ensure_connected(func):
|
|||
return wrapped
|
||||
|
||||
|
||||
def ensure_ncclient(func):
|
||||
@wraps(func)
|
||||
def wrapped(self, *args, **kwargs):
|
||||
if not HAS_NCCLIENT:
|
||||
raise AnsibleError("%s: %s" % (missing_required_lib('ncclient'), to_native(NCCLIENT_IMP_ERR)))
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
class NetconfBase(AnsiblePlugin):
|
||||
"""
|
||||
A base class for implementing Netconf connections
|
||||
|
@ -107,6 +120,7 @@ class NetconfBase(AnsiblePlugin):
|
|||
def m(self):
|
||||
return self._connection._manager
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def rpc(self, name):
|
||||
"""
|
||||
|
|
|
@ -22,29 +22,31 @@ __metaclass__ = type
|
|||
import json
|
||||
import re
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text, to_bytes, to_native
|
||||
from ansible.errors import AnsibleConnectionFailure, AnsibleError
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.plugins.netconf import NetconfBase
|
||||
from ansible.plugins.netconf import ensure_connected
|
||||
from ansible.plugins.netconf import ensure_connected, ensure_ncclient
|
||||
|
||||
try:
|
||||
from ncclient import manager
|
||||
from ncclient.operations import RPCError
|
||||
from ncclient.transport.errors import SSHUnknownHostError
|
||||
from ncclient.xml_ import to_ele, to_xml, new_ele
|
||||
except ImportError:
|
||||
raise AnsibleError("ncclient is not installed")
|
||||
HAS_NCCLIENT = True
|
||||
except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError
|
||||
HAS_NCCLIENT = False
|
||||
|
||||
|
||||
class Netconf(NetconfBase):
|
||||
|
||||
@ensure_ncclient
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return to_text(ele.find(tag).text, errors='surrogate_then_replace').strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@ensure_ncclient
|
||||
def get_device_info(self):
|
||||
device_info = dict()
|
||||
device_info['network_os'] = 'ce'
|
||||
|
@ -65,6 +67,7 @@ class Netconf(NetconfBase):
|
|||
:name: Name of rpc in string format"""
|
||||
return self.rpc(name)
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def load_configuration(self, *args, **kwargs):
|
||||
"""Loads given configuration on device
|
||||
|
@ -95,8 +98,8 @@ class Netconf(NetconfBase):
|
|||
return json.dumps(result)
|
||||
|
||||
@staticmethod
|
||||
@ensure_ncclient
|
||||
def guess_network_os(obj):
|
||||
|
||||
try:
|
||||
m = manager.connect(
|
||||
host=obj._play_context.remote_addr,
|
||||
|
@ -135,6 +138,7 @@ class Netconf(NetconfBase):
|
|||
:rollback: rollback id"""
|
||||
return self.m.compare_configuration(*args, **kwargs).data_xml
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def execute_action(self, xml_str):
|
||||
"""huawei execute-action"""
|
||||
|
@ -156,11 +160,7 @@ class Netconf(NetconfBase):
|
|||
"""reboot the device"""
|
||||
return self.m.reboot().data_xml
|
||||
|
||||
@ensure_connected
|
||||
def halt(self):
|
||||
"""reboot the device"""
|
||||
return self.m.halt().data_xml
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def get(self, *args, **kwargs):
|
||||
try:
|
||||
|
@ -168,6 +168,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def get_config(self, *args, **kwargs):
|
||||
try:
|
||||
|
@ -175,6 +176,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def edit_config(self, *args, **kwargs):
|
||||
try:
|
||||
|
@ -182,6 +184,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def execute_nc_cli(self, *args, **kwargs):
|
||||
try:
|
||||
|
@ -189,6 +192,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def commit(self, *args, **kwargs):
|
||||
try:
|
||||
|
@ -203,9 +207,3 @@ class Netconf(NetconfBase):
|
|||
@ensure_connected
|
||||
def discard_changes(self, *args, **kwargs):
|
||||
return self.m.discard_changes(*args, **kwargs).data_xml
|
||||
|
||||
@ensure_connected
|
||||
def execute_rpc(self, name):
|
||||
"""RPC to be execute on remote device
|
||||
:name: Name of rpc in string format"""
|
||||
return self.rpc(name)
|
||||
|
|
|
@ -24,30 +24,24 @@ import json
|
|||
import re
|
||||
import collections
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.network.common.netconf import remove_namespaces
|
||||
from ansible.module_utils.network.iosxr.iosxr import build_xml, etree_find
|
||||
from ansible.errors import AnsibleConnectionFailure, AnsibleError
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.plugins.netconf import NetconfBase
|
||||
from ansible.plugins.netconf import ensure_connected
|
||||
from ansible.plugins.netconf import ensure_connected, ensure_ncclient
|
||||
|
||||
try:
|
||||
from ncclient import manager
|
||||
from ncclient.operations import RPCError
|
||||
from ncclient.transport.errors import SSHUnknownHostError
|
||||
from ncclient.xml_ import to_ele, to_xml, new_ele
|
||||
except ImportError:
|
||||
raise AnsibleError("ncclient is not installed")
|
||||
|
||||
try:
|
||||
from lxml import etree
|
||||
except ImportError:
|
||||
raise AnsibleError("lxml is not installed")
|
||||
from ncclient.xml_ import to_xml
|
||||
HAS_NCCLIENT = True
|
||||
except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError
|
||||
HAS_NCCLIENT = False
|
||||
|
||||
|
||||
class Netconf(NetconfBase):
|
||||
|
||||
@ensure_connected
|
||||
def get_device_info(self):
|
||||
device_info = {}
|
||||
|
@ -93,6 +87,7 @@ class Netconf(NetconfBase):
|
|||
return json.dumps(result)
|
||||
|
||||
@staticmethod
|
||||
@ensure_ncclient
|
||||
def guess_network_os(obj):
|
||||
"""
|
||||
Guess the remote network os name
|
||||
|
@ -124,6 +119,7 @@ class Netconf(NetconfBase):
|
|||
return guessed_os
|
||||
|
||||
# TODO: change .xml to .data_xml, when ncclient supports data_xml on all platforms
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def get(self, filter=None, remove_ns=False):
|
||||
if isinstance(filter, list):
|
||||
|
@ -138,6 +134,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def get_config(self, source=None, filter=None, remove_ns=False):
|
||||
if isinstance(filter, list):
|
||||
|
@ -152,6 +149,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def edit_config(self, config=None, format='xml', target='candidate', default_operation=None, test_option=None, error_option=None, remove_ns=False):
|
||||
if config is None:
|
||||
|
@ -167,6 +165,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def commit(self, confirmed=False, timeout=None, persist=None, remove_ns=False):
|
||||
try:
|
||||
|
@ -179,6 +178,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def validate(self, source="candidate", remove_ns=False):
|
||||
try:
|
||||
|
@ -191,6 +191,7 @@ class Netconf(NetconfBase):
|
|||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def discard_changes(self, remove_ns=False):
|
||||
try:
|
||||
|
|
|
@ -22,29 +22,29 @@ __metaclass__ = type
|
|||
import json
|
||||
import re
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text, to_bytes, to_native
|
||||
from ansible.errors import AnsibleConnectionFailure, AnsibleError
|
||||
from ansible.module_utils._text import to_text, to_native
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.plugins.netconf import NetconfBase
|
||||
from ansible.plugins.netconf import ensure_connected
|
||||
from ansible.plugins.netconf import ensure_connected, ensure_ncclient
|
||||
|
||||
try:
|
||||
from ncclient import manager
|
||||
from ncclient.operations import RPCError
|
||||
from ncclient.transport.errors import SSHUnknownHostError
|
||||
from ncclient.xml_ import to_ele, to_xml, new_ele, sub_ele
|
||||
except ImportError:
|
||||
raise AnsibleError("ncclient is not installed")
|
||||
HAS_NCCLIENT = True
|
||||
except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError
|
||||
HAS_NCCLIENT = False
|
||||
|
||||
|
||||
class Netconf(NetconfBase):
|
||||
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return to_text(ele.find(tag).text, errors='surrogate_then_replace').strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@ensure_ncclient
|
||||
def get_device_info(self):
|
||||
device_info = dict()
|
||||
device_info['network_os'] = 'junos'
|
||||
|
@ -68,6 +68,7 @@ class Netconf(NetconfBase):
|
|||
"""
|
||||
return self.rpc(name)
|
||||
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def load_configuration(self, format='xml', action='merge', target='candidate', config=None):
|
||||
"""
|
||||
|
@ -101,6 +102,7 @@ class Netconf(NetconfBase):
|
|||
return json.dumps(result)
|
||||
|
||||
@staticmethod
|
||||
@ensure_ncclient
|
||||
def guess_network_os(obj):
|
||||
"""
|
||||
Guess the remote network os name
|
||||
|
@ -165,6 +167,7 @@ class Netconf(NetconfBase):
|
|||
# below commit() is a workaround which build's raw `commit-configuration` xml with required tags and uses
|
||||
# ncclient generic rpc() method to execute rpc on remote host.
|
||||
# Remove below method after the issue in ncclient is fixed.
|
||||
@ensure_ncclient
|
||||
@ensure_connected
|
||||
def commit(self, confirmed=False, check=False, timeout=None, comment=None, synchronize=False, at_time=None):
|
||||
"""
|
||||
|
|
|
@ -22,24 +22,18 @@ __metaclass__ = type
|
|||
import json
|
||||
import re
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_text, to_bytes, to_native
|
||||
from ansible.errors import AnsibleConnectionFailure, AnsibleError
|
||||
from ansible.module_utils._text import to_text, to_native
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.plugins.netconf import NetconfBase
|
||||
from ansible.plugins.netconf import ensure_connected
|
||||
from ansible.plugins.netconf import ensure_ncclient
|
||||
|
||||
try:
|
||||
from ncclient import manager
|
||||
from ncclient.operations import RPCError
|
||||
from ncclient.transport.errors import SSHUnknownHostError
|
||||
from ncclient.xml_ import to_ele, to_xml, new_ele
|
||||
except ImportError:
|
||||
raise AnsibleError("ncclient is not installed")
|
||||
|
||||
try:
|
||||
from lxml import etree
|
||||
except ImportError:
|
||||
raise AnsibleError("lxml is not installed")
|
||||
from ncclient.xml_ import to_ele
|
||||
HAS_NCCLIENT = True
|
||||
except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError
|
||||
HAS_NCCLIENT = False
|
||||
|
||||
|
||||
class Netconf(NetconfBase):
|
||||
|
@ -49,6 +43,7 @@ class Netconf(NetconfBase):
|
|||
except AttributeError:
|
||||
pass
|
||||
|
||||
@ensure_ncclient
|
||||
def get_device_info(self):
|
||||
device_info = dict()
|
||||
device_info['network_os'] = 'sros'
|
||||
|
@ -75,6 +70,7 @@ class Netconf(NetconfBase):
|
|||
return json.dumps(result)
|
||||
|
||||
@staticmethod
|
||||
@ensure_ncclient
|
||||
def guess_network_os(obj):
|
||||
try:
|
||||
m = manager.connect(
|
||||
|
|
Loading…
Reference in a new issue