diff options
Diffstat (limited to 'lib/ansible/module_utils/network')
49 files changed, 0 insertions, 10667 deletions
diff --git a/lib/ansible/module_utils/network/nxos/argspec/acl_interfaces/acl_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/acl_interfaces/acl_interfaces.py deleted file mode 100644 index baa1a70a28..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/acl_interfaces/acl_interfaces.py +++ /dev/null @@ -1,91 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_acl_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Acl_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_acl_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'access_groups': { - 'elements': 'dict', - 'options': { - 'acls': { - 'elements': 'dict', - 'options': { - 'direction': { - 'choices': ['in', 'out'], - 'required': True, - 'type': 'str' - }, - 'name': { - 'required': True, - 'type': 'str' - }, - 'port': { - 'type': 'bool' - } - }, - 'type': 'list' - }, - 'afi': { - 'choices': ['ipv4', 'ipv6'], - 'required': True, - 'type': 'str' - } - }, - 'type': 'list' - }, - 'name': { - 'required': True, - 'type': 'str' - } - }, - 'type': 'list' - }, - 'running_config': { - 'type': 'str' - }, - 'state': { - 'choices': [ - 'deleted', 'gathered', 'merged', 'overridden', 'rendered', - 'replaced', 'parsed' - ], - 'default': - 'merged', - 'type': - 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/acls/acls.py b/lib/ansible/module_utils/network/nxos/argspec/acls/acls.py deleted file mode 100644 index 01e13f1893..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/acls/acls.py +++ /dev/null @@ -1,425 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_acls module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class AclsArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_acls module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'acls': { - 'elements': 'dict', - 'options': { - 'aces': { - 'elements': 'dict', - 'mutually_exclusive': [['grant', 'remark']], - 'options': { - 'destination': { - 'mutually_exclusive': - [['address', 'any', 'host', 'prefix'], - [ - 'wildcard_bits', 'any', 'host', - 'prefix' - ]], - 'options': { - 'address': { - 'type': 'str' - }, - 'any': { - 'type': 'bool' - }, - 'host': { - 'type': 'str' - }, - 'port_protocol': { - 'mutually_exclusive': [[ - 'eq', 'lt', 'neq', 'gt', - 'range' - ]], - 'options': { - 'eq': { - 'type': 'str' - }, - 'gt': { - 'type': 'str' - }, - 'lt': { - 'type': 'str' - }, - 'neq': { - 'type': 'str' - }, - 'range': { - 'options': { - 'end': { - 'type': 'str' - }, - 'start': { - 'type': 'str' - } - }, - 'required_together': - [['start', 'end']], - 'type': 'dict' - } - }, - 'type': 'dict' - }, - 'prefix': { - 'type': 'str' - }, - 'wildcard_bits': { - 'type': 'str' - } - }, - 'required_together': - [['address', 'wildcard_bits']], - 'type': 'dict' - }, - 'dscp': { - 'type': 'str' - }, - 'fragments': { - 'type': 'bool' - }, - 'grant': { - 'choices': ['permit', 'deny'], - 'type': 'str' - }, - 'log': { - 'type': 'bool' - }, - 'precedence': { - 'type': 'str' - }, - 'protocol': { - 'type': 'str' - }, - 'protocol_options': { - 'mutually_exclusive': - [['icmp', 'igmp', 'tcp']], - 'options': { - 'icmp': { - 'options': { - 'administratively_prohibited': - { - 'type': 'bool' - }, - 'alternate_address': { - 'type': 'bool' - }, - 'conversion_error': { - 'type': 'bool' - }, - 'dod_host_prohibited': { - 'type': 'bool' - }, - 'dod_net_prohibited': { - 'type': 'bool' - }, - 'echo': { - 'type': 'bool' - }, - 'echo_reply': { - 'type': 'bool' - }, - 'general_parameter_problem': { - 'type': 'bool' - }, - 'host_isolated': { - 'type': 'bool' - }, - 'host_precedence_unreachable': - { - 'type': 'bool' - }, - 'host_redirect': { - 'type': 'bool' - }, - 'host_tos_redirect': { - 'type': 'bool' - }, - 'host_tos_unreachable': { - 'type': 'bool' - }, - 'host_unknown': { - 'type': 'bool' - }, - 'host_unreachable': { - 'type': 'bool' - }, - 'information_reply': { - 'type': 'bool' - }, - 'information_request': { - 'type': 'bool' - }, - 'mask_reply': { - 'type': 'bool' - }, - 'mask_request': { - 'type': 'bool' - }, - 'message_code': { - 'type': 'int' - }, - 'message_type': { - 'type': 'int' - }, - 'mobile_redirect': { - 'type': 'bool' - }, - 'net_redirect': { - 'type': 'bool' - }, - 'net_tos_redirect': { - 'type': 'bool' - }, - 'net_tos_unreachable': { - 'type': 'bool' - }, - 'net_unreachable': { - 'type': 'bool' - }, - 'network_unknown': { - 'type': 'bool' - }, - 'no_room_for_option': { - 'type': 'bool' - }, - 'option_missing': { - 'type': 'bool' - }, - 'packet_too_big': { - 'type': 'bool' - }, - 'parameter_problem': { - 'type': 'bool' - }, - 'port_unreachable': { - 'type': 'bool' - }, - 'precedence_unreachable': { - 'type': 'bool' - }, - 'protocol_unreachable': { - 'type': 'bool' - }, - 'reassembly_timeout': { - 'type': 'bool' - }, - 'redirect': { - 'type': 'bool' - }, - 'router_advertisement': { - 'type': 'bool' - }, - 'router_solicitation': { - 'type': 'bool' - }, - 'source_quench': { - 'type': 'bool' - }, - 'source_route_failed': { - 'type': 'bool' - }, - 'time_exceeded': { - 'type': 'bool' - }, - 'timestamp_reply': { - 'type': 'bool' - }, - 'timestamp_request': { - 'type': 'bool' - }, - 'traceroute': { - 'type': 'bool' - }, - 'ttl_exceeded': { - 'type': 'bool' - }, - 'unreachable': { - 'type': 'bool' - } - }, - 'type': 'dict' - }, - 'igmp': { - 'mutually_exclusive': [[ - 'dvmrp', 'host_query', - 'host_report' - ]], - 'options': { - 'dvmrp': { - 'type': 'bool' - }, - 'host_query': { - 'type': 'bool' - }, - 'host_report': { - 'type': 'bool' - } - }, - 'type': - 'dict' - }, - 'tcp': { - 'options': { - 'ack': { - 'type': 'bool' - }, - 'established': { - 'type': 'bool' - }, - 'fin': { - 'type': 'bool' - }, - 'psh': { - 'type': 'bool' - }, - 'rst': { - 'type': 'bool' - }, - 'syn': { - 'type': 'bool' - }, - 'urg': { - 'type': 'bool' - } - }, - 'type': 'dict' - } - }, - 'type': 'dict' - }, - 'remark': { - 'type': 'str' - }, - 'sequence': { - 'type': 'int' - }, - 'source': { - 'mutually_exclusive': - [['address', 'any', 'host', 'prefix'], - [ - 'wildcard_bits', 'host', 'any', - 'prefix' - ]], - 'options': { - 'address': { - 'type': 'str' - }, - 'any': { - 'type': 'bool' - }, - 'host': { - 'type': 'str' - }, - 'port_protocol': { - 'mutually_exclusive': - [['eq', 'lt', 'neq', 'range'], - ['eq', 'gt', 'neq', 'range']], - 'options': { - 'eq': { - 'type': 'str' - }, - 'gt': { - 'type': 'str' - }, - 'lt': { - 'type': 'str' - }, - 'neq': { - 'type': 'str' - }, - 'range': { - 'options': { - 'end': { - 'type': 'str' - }, - 'start': { - 'type': 'str' - } - }, - 'type': 'dict' - } - }, - 'type': - 'dict' - }, - 'prefix': { - 'type': 'str' - }, - 'wildcard_bits': { - 'type': 'str' - } - }, - 'required_together': - [['address', 'wildcard_bits']], - 'type': - 'dict' - } - }, - 'type': 'list' - }, - 'name': { - 'required': True, - 'type': 'str' - } - }, - 'type': 'list' - }, - 'afi': { - 'choices': ['ipv4', 'ipv6'], - 'required': True, - 'type': 'str' - } - }, - 'type': 'list' - }, - 'running_config': { - 'type': 'str' - }, - 'state': { - 'choices': [ - 'deleted', 'gathered', 'merged', 'overridden', 'rendered', - 'replaced', 'parsed' - ], - 'default': - 'merged', - 'type': - 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/bfd_interfaces/bfd_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/bfd_interfaces/bfd_interfaces.py deleted file mode 100644 index 3c41030fe9..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/bfd_interfaces/bfd_interfaces.py +++ /dev/null @@ -1,56 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -""" -The arg spec for the nxos_bfd_interfaces module -""" - - -class Bfd_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_bfd_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'name': {'type': 'str'}, - 'bfd': { - 'choices': ['enable', 'disable'], 'type': 'str'}, - 'echo': { - 'choices': ['enable', 'disable'], 'type': 'str'}, - }, - 'type': 'list' - }, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/facts/facts.py b/lib/ansible/module_utils/network/nxos/argspec/facts/facts.py deleted file mode 100644 index 889ae5f34b..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/facts/facts.py +++ /dev/null @@ -1,21 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The arg spec for the nxos facts module. -""" - - -class FactsArgs(object): - """ The arg spec for the nxos facts module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'gather_subset': dict(default=['!config'], type='list'), - 'gather_network_resources': dict(type='list'), - } diff --git a/lib/ansible/module_utils/network/nxos/argspec/hsrp_interfaces/hsrp_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/hsrp_interfaces/hsrp_interfaces.py deleted file mode 100644 index 5183ce7ac8..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/hsrp_interfaces/hsrp_interfaces.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -""" -The arg spec for the nxos_hsrp_interfaces module -""" - - -class Hsrp_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_hsrp_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'type': 'list', - 'elements': 'dict', - 'options': { - 'name': {'type': 'str'}, - 'bfd': { - 'choices': ['enable', 'disable'], 'type': 'str'}, - }, - }, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/interfaces/interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/interfaces/interfaces.py deleted file mode 100644 index 1d9b7d0ead..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/interfaces/interfaces.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class InterfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'description': { - 'type': 'str' - }, - 'duplex': { - 'choices': ['full', 'half', 'auto'], - 'type': 'str' - }, - 'enabled': { - 'type': 'bool' - }, - 'fabric_forwarding_anycast_gateway': { - 'type': 'bool' - }, - 'ip_forward': { - 'type': 'bool' - }, - 'mode': { - 'choices': ['layer2', 'layer3'], - 'type': 'str' - }, - 'mtu': { - 'type': 'str' - }, - 'name': { - 'required': True, - 'type': 'str' - }, - 'speed': { - 'type': 'str' - } - }, - 'type': 'list' - }, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/l2_interfaces/l2_interfaces.py deleted file mode 100644 index db723be790..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,77 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_l2_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class L2_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_l2_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'access': { - 'options': { - 'vlan': { - 'type': 'int' - } - }, - 'type': 'dict' - }, - 'mode': { - 'type': 'str', - 'choices': ['access', 'trunk'] - }, - 'name': { - 'required': True, - 'type': 'str' - }, - 'trunk': { - 'options': { - 'allowed_vlans': { - 'type': 'str' - }, - 'native_vlan': { - 'type': 'int' - } - }, - 'type': 'dict' - } - }, - 'type': 'list' - }, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/l3_interfaces/l3_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/l3_interfaces/l3_interfaces.py deleted file mode 100644 index d6f2098bd6..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/l3_interfaces/l3_interfaces.py +++ /dev/null @@ -1,90 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_l3_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class L3_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_l3_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'dot1q': { - 'type': 'int' - }, - 'ipv4': { - 'elements': 'dict', - 'options': { - 'address': { - 'type': 'str' - }, - 'secondary': { - 'type': 'bool' - }, - 'tag': { - 'type': 'int' - } - }, - 'type': 'list' - }, - 'ipv6': { - 'elements': 'dict', - 'options': { - 'address': { - 'type': 'str' - }, - 'tag': { - 'type': 'int' - } - }, - 'type': 'list' - }, - 'name': { - 'required': True, - 'type': 'str' - }, - 'redirects': { - 'type': 'bool', - }, - 'unreachables': { - 'type': 'bool', - }, - }, - 'type': 'list' - }, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/lacp/lacp.py b/lib/ansible/module_utils/network/nxos/argspec/lacp/lacp.py deleted file mode 100644 index b63c10f471..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/lacp/lacp.py +++ /dev/null @@ -1,69 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_lacp module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class LacpArgs(object): - """The arg spec for the nxos_lacp module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'options': { - 'system': { - 'options': { - 'mac': { - 'options': { - 'address': { - 'type': 'str' - }, - 'role': { - 'choices': ['primary', 'secondary'], - 'type': 'str' - } - }, - 'type': 'dict' - }, - 'priority': { - 'type': 'int' - } - }, - 'type': 'dict' - } - }, - 'type': 'dict' - }, - 'state': { - 'choices': ['merged', 'replaced', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } diff --git a/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py deleted file mode 100644 index 5f72f21a1b..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py +++ /dev/null @@ -1,90 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_lacp_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Lacp_interfacesArgs(object): - """The arg spec for the nxos_lacp_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'convergence': { - 'options': { - 'graceful': { - 'type': 'bool' - }, - 'vpc': { - 'type': 'bool' - } - }, - 'type': 'dict' - }, - 'links': { - 'options': { - 'max': { - 'type': 'int' - }, - 'min': { - 'type': 'int' - } - }, - 'type': 'dict' - }, - 'mode': { - 'choices': ['delay'], - 'type': 'str' - }, - 'name': { - 'required': True, - 'type': 'str' - }, - 'port_priority': { - 'type': 'int' - }, - 'rate': { - 'choices': ['fast', 'normal'], - 'type': 'str' - }, - 'suspend_individual': { - 'type': 'bool' - } - }, - 'type': 'list' - }, - 'state': { - 'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } diff --git a/lib/ansible/module_utils/network/nxos/argspec/lag_interfaces/lag_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/lag_interfaces/lag_interfaces.py deleted file mode 100644 index dd200c177d..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/lag_interfaces/lag_interfaces.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the nxos_lag_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Lag_interfacesArgs(object): - """The arg spec for the nxos_lag_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = {'config': {'elements': 'dict', - 'options': {'members': {'elements': 'dict', - 'options': {'member': {'type': 'str'}, - 'mode': {'type': 'str', - 'choices': ['active', 'on', 'passive']}, - 'force': {'type': 'bool'}}, - 'type': 'list'}, - 'name': {'required': True, 'type': 'str'}}, - 'type': 'list'}, - 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str'}} diff --git a/lib/ansible/module_utils/network/nxos/argspec/lldp_global/lldp_global.py b/lib/ansible/module_utils/network/nxos/argspec/lldp_global/lldp_global.py deleted file mode 100644 index d3289fbf39..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/lldp_global/lldp_global.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_lldp_global module -""" -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - - -class Lldp_globalArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_lldp_global module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'options': { - 'holdtime': { - 'type': 'int' - }, - 'port_id': { - 'choices': [0, 1], - 'type': 'int' - }, - 'reinit': { - 'type': 'int' - }, - 'timer': { - 'type': 'int' - }, - 'tlv_select': { - 'options': { - 'dcbxp': { - 'type': 'bool' - }, - 'management_address': { - 'options': { - 'v4': { - 'type': 'bool' - }, - 'v6': { - 'type': 'bool' - } - }, - 'type': 'dict' - }, - 'port': { - 'options': { - 'description': { - 'type': 'bool' - }, - 'vlan': { - 'type': 'bool' - } - }, - 'type': 'dict' - }, - 'power_management': { - 'type': 'bool' - }, - 'system': { - 'options': { - 'capabilities': { - 'type': 'bool' - }, - 'description': { - 'type': 'bool' - }, - 'name': { - 'type': 'bool' - } - }, - 'type': 'dict' - } - }, - 'type': 'dict' - } - }, - 'type': 'dict' - }, - 'state': { - 'choices': ['merged', 'replaced', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } diff --git a/lib/ansible/module_utils/network/nxos/argspec/lldp_interfaces/lldp_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index ee223eafe5..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the nxos_lldp_interfaces module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Lldp_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_lldp_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'name': { - 'required': True, - 'type': 'str' - }, - 'receive': { - 'type': 'bool' - }, - 'tlv_set': { - 'options': { - 'management_address': { - 'type': 'str' - }, - 'vlan': { - 'type': 'int' - } - }, - 'type': 'dict' - }, - 'transmit': { - 'type': 'bool' - } - }, - 'type': 'list' - }, - 'running_config': { - 'type': 'str' - }, - 'state': { - 'choices': ['deleted', 'gathered', 'merged', 'overridden', 'rendered', - 'replaced', 'parsed'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/telemetry/telemetry.py b/lib/ansible/module_utils/network/nxos/argspec/telemetry/telemetry.py deleted file mode 100644 index fd9753e245..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/telemetry/telemetry.py +++ /dev/null @@ -1,90 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the nxos_telemetry module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class TelemetryArgs(object): # pylint: disable=R0903 - """The arg spec for the nxos_telemetry module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'options': { - 'certificate': { - 'options': { - 'hostname': {'type': 'str'}, - 'key': {'type': 'str'}}, - 'type': 'dict'}, - 'compression': {'choices': ['gzip'], 'type': 'str'}, - 'source_interface': {'type': 'str'}, - 'vrf': {'type': 'str'}, - 'destination_groups': { - 'options': { - 'destination': { - 'options': { - 'encoding': {'choices': ['GPB', 'JSON'], - 'type': 'str'}, - 'ip': {'type': 'str'}, - 'port': {'type': 'int'}, - 'protocol': {'choices': ['HTTP', 'TCP', 'UDP', 'gRPC'], - 'type': 'str'}}, - 'type': 'dict'}, - 'id': {'type': 'int'}}, - 'type': 'list'}, - 'sensor_groups': { - 'options': { - 'data_source': {'choices': ['NX-API', 'DME', 'YANG'], - 'type': 'str'}, - 'id': {'type': 'int'}, - 'path': { - 'options': { - 'depth': {'type': 'str'}, - 'filter_condition': {'type': 'str'}, - 'name': {'type': 'str'}, - 'query_condition': {'type': 'str'}}, - 'type': 'dict'}}, - 'type': 'list'}, - 'subscriptions': { - 'options': { - 'destination_group': {'type': 'int'}, - 'id': {'type': 'int'}, - 'sensor_group': { - 'options': { - 'id': {'type': 'int'}, - 'sample_interval': {'type': 'int'}}, - 'type': 'dict'}}, - 'type': 'list'}}, - 'type': 'dict'}, - 'state': { - 'choices': ['merged', 'replaced', 'deleted'], - 'default': 'merged', - 'type': 'str'}} # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/nxos/argspec/vlans/vlans.py b/lib/ansible/module_utils/network/nxos/argspec/vlans/vlans.py deleted file mode 100644 index e323c5dd4d..0000000000 --- a/lib/ansible/module_utils/network/nxos/argspec/vlans/vlans.py +++ /dev/null @@ -1,51 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the nxos_vlans module -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class VlansArgs(object): - """The arg spec for the nxos_vlans module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = {'config': {'elements': 'dict', - 'options': {'enabled': {'type': 'bool'}, - 'mapped_vni': {'type': 'int'}, - 'mode': {'choices': ['ce', 'fabricpath'], - 'type': 'str'}, - 'name': {'type': 'str'}, - 'vlan_id': {'required': True, 'type': 'int'}, - 'state': {'choices': ['active', 'suspend'], - 'type': 'str'}}, - 'type': 'list'}, - 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str'}} diff --git a/lib/ansible/module_utils/network/nxos/cmdref/telemetry/telemetry.py b/lib/ansible/module_utils/network/nxos/cmdref/telemetry/telemetry.py deleted file mode 100644 index 39f3604518..0000000000 --- a/lib/ansible/module_utils/network/nxos/cmdref/telemetry/telemetry.py +++ /dev/null @@ -1,145 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -# Telemetry Command Reference File - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -TMS_GLOBAL = ''' -# The cmd_ref is a yaml formatted list of module commands. -# A leading underscore denotes a non-command variable; e.g. _template. -# TMS does not have convenient global json data so this cmd_ref uses raw cli configs. ---- -_template: # _template holds common settings for all commands - # Enable feature telemetry if disabled - feature: telemetry - # Common get syntax for TMS commands - get_command: show run telemetry all - # Parent configuration for TMS commands - context: - - telemetry -certificate: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - kind: dict - getval: certificate (?P<key>\\S+) (?P<hostname>\\S+)$ - setval: certificate {key} {hostname} - default: - key: ~ - hostname: ~ -compression: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - kind: str - getval: use-compression (\\S+)$ - setval: 'use-compression {0}' - default: ~ - context: &dpcontext - - telemetry - - destination-profile -source_interface: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - kind: str - getval: source-interface (\\S+)$ - setval: 'source-interface {0}' - default: ~ - context: *dpcontext -vrf: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - kind: str - getval: use-vrf (\\S+)$ - setval: 'use-vrf {0}' - default: ~ - context: *dpcontext -''' - -TMS_DESTGROUP = ''' -# The cmd_ref is a yaml formatted list of module commands. -# A leading underscore denotes a non-command variable; e.g. _template. -# TBD: Use Structured Where Possible ---- -_template: # _template holds common settings for all commands - # Enable feature telemetry if disabled - feature: telemetry - # Common get syntax for TMS commands - get_command: show run telemetry all - # Parent configuration for TMS commands - context: - - telemetry -destination: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - multiple: true - kind: dict - getval: ip address (?P<ip>\\S+) port (?P<port>\\S+) protocol (?P<protocol>\\S+) encoding (?P<encoding>\\S+) - setval: ip address {ip} port {port} protocol {protocol} encoding {encoding} - default: - ip: ~ - port: ~ - protocol: ~ - encoding: ~ -''' - -TMS_SENSORGROUP = ''' -# The cmd_ref is a yaml formatted list of module commands. -# A leading underscore denotes a non-command variable; e.g. _template. -# TBD: Use Structured Where Possible ---- -_template: # _template holds common settings for all commands - # Enable feature telemetry if disabled - feature: telemetry - # Common get syntax for TMS commands - get_command: show run telemetry all - # Parent configuration for TMS commands - context: - - telemetry -data_source: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - kind: str - getval: data-source (\\S+)$ - setval: 'data-source {0}' - default: ~ -path: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - multiple: true - kind: dict - getval: path (?P<name>(\\S+|".*"))( depth (?P<depth>\\S+))?( query-condition (?P<query_condition>\\S+))?( filter-condition (?P<filter_condition>\\S+))?$ - setval: path {name} depth {depth} query-condition {query_condition} filter-condition {filter_condition} - default: - name: ~ - depth: ~ - query_condition: ~ - filter_condition: ~ -''' - -TMS_SUBSCRIPTION = ''' -# The cmd_ref is a yaml formatted list of module commands. -# A leading underscore denotes a non-command variable; e.g. _template. -# TBD: Use Structured Where Possible ---- -_template: # _template holds common settings for all commands - # Enable feature telemetry if disabled - feature: telemetry - # Common get syntax for TMS commands - get_command: show run telemetry all - # Parent configuration for TMS commands - context: - - telemetry -destination_group: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - multiple: true - kind: int - getval: dst-grp (\\S+)$ - setval: 'dst-grp {0}' - default: ~ -sensor_group: - _exclude: ['N3K', 'N5K', 'N6k', 'N7k'] - multiple: true - kind: dict - getval: snsr-grp (?P<id>\\S+) sample-interval (?P<sample_interval>\\S+)$ - setval: snsr-grp {id} sample-interval {sample_interval} - default: - id: ~ - sample_interval: ~ -''' diff --git a/lib/ansible/module_utils/network/nxos/config/acl_interfaces/acl_interfaces.py b/lib/ansible/module_utils/network/nxos/config/acl_interfaces/acl_interfaces.py deleted file mode 100644 index e5b56f82b1..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/acl_interfaces/acl_interfaces.py +++ /dev/null @@ -1,303 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_acl_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import to_list, remove_empties, dict_diff -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import flatten_dict, search_obj_in_list, get_interface_type, normalize_interface - - -class Acl_interfaces(ConfigBase): - """ - The nxos_acl_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'acl_interfaces', - ] - - def __init__(self, module): - super(Acl_interfaces, self).__init__(module) - - def get_acl_interfaces_facts(self, data=None): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources, data=data) - acl_interfaces_facts = facts['ansible_network_resources'].get( - 'acl_interfaces') - if not acl_interfaces_facts: - return [] - return acl_interfaces_facts - - def edit_config(self, commands): - """Wrapper method for `_connection.edit_config()` - This exists solely to allow the unit test framework to mock device connection calls. - """ - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - commands = list() - state = self._module.params['state'] - action_states = ['merged', 'replaced', 'deleted', 'overridden'] - - if state == 'gathered': - result['gathered'] = self.get_acl_interfaces_facts() - elif state == 'rendered': - result['rendered'] = self.set_config({}) - # no need to fetch facts for rendered - elif state == 'parsed': - result['parsed'] = self.set_config({}) - # no need to fetch facts for parsed - else: - existing_acl_interfaces_facts = self.get_acl_interfaces_facts() - commands.extend(self.set_config(existing_acl_interfaces_facts)) - if commands and state in action_states: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['before'] = existing_acl_interfaces_facts - result['commands'] = commands - - changed_acl_interfaces_facts = self.get_acl_interfaces_facts() - if result['changed']: - result['after'] = changed_acl_interfaces_facts - result['warnings'] = warnings - return result - - def set_config(self, existing_acl_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params['config'] - want = [] - if config: - for w in config: - if get_interface_type(w['name']) == 'loopback': - self._module.fail_json( - msg='This module works with ethernet, management or port-channe') - w.update({'name': normalize_interface(w['name'])}) - want.append(remove_empties(w)) - have = existing_acl_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - commands = [] - if state == 'overridden': - commands = (self._state_overridden(want, have)) - elif state == 'deleted': - commands = (self._state_deleted(want, have)) - elif state == 'rendered': - commands = self._state_rendered(want) - elif state == 'parsed': - want = self._module.params['running_config'] - commands = self._state_parsed(want) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(w, have)) - elif state == 'replaced': - commands.extend(self._state_replaced(w, have)) - return commands - - def _state_parsed(self, want): - return self.get_acl_interfaces_facts(want) - - def _state_rendered(self, want): - commands = [] - for w in want: - commands.extend(self.set_commands(w, {})) - return commands - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - new_commands = [] - del_dict = {'name': want['name'], 'access_groups': []} - obj_in_have = search_obj_in_list(want['name'], have, 'name') - if obj_in_have != want: - commands = [] - if obj_in_have and 'access_groups' in obj_in_have.keys(): - for ag in obj_in_have['access_groups']: - want_afi = [] - if want.get('access_groups'): - want_afi = search_obj_in_list( - ag['afi'], want['access_groups'], 'afi') - if not want_afi: - # whatever in have is not in want - del_dict['access_groups'].append(ag) - else: - del_acl = [] - for acl in ag['acls']: - if want_afi.get('acls'): - if acl not in want_afi['acls']: - del_acl.append(acl) - else: - del_acl.append(acl) - afi = want_afi['afi'] - del_dict['access_groups'].append( - {'afi': afi, 'acls': del_acl}) - - commands.extend(self._state_deleted([del_dict], have)) - commands.extend(self._state_merged(want, have)) - new_commands.append(commands[0]) - commands = [commands[i] - for i in range(1, len(commands)) if commands[i] != commands[0]] - new_commands.extend(commands) - return new_commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - want_intf = [w['name'] for w in want] - for h in have: - if h['name'] not in want_intf: - commands.extend(self._state_deleted([h], have)) - for w in want: - commands.extend(self._state_replaced(w, have)) - return commands - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(want, have) - - def set_commands(self, want, have, deleted=False): - commands = [] - have_name = search_obj_in_list(want['name'], have, 'name') - if have_name and have_name.get('access_groups'): - if want.get('access_groups'): - for w_afi in want['access_groups']: - ip = 'ipv6' - if w_afi['afi'] == 'ipv4': - ip = 'ip' - have_afi = search_obj_in_list( - w_afi['afi'], have_name['access_groups'], 'afi') - if have_afi: - new_acls = [] - if deleted: - if w_afi.get('acls') and have_afi.get('acls'): - new_acls = [ - acl for acl in w_afi.get('acls') if acl in have_afi.get('acls')] - elif 'acls' not in w_afi.keys(): - new_acls = have_afi.get('acls') - else: - if w_afi.get('acls'): - new_acls = [ - acl for acl in w_afi['acls'] if acl not in have_afi['acls']] - commands.extend(self.process_acl( - new_acls, ip, deleted)) - else: - if not deleted: - if w_afi.get('acls'): - commands.extend( - self.process_acl(w_afi['acls'], ip)) - else: - # only name is given to delete - if deleted and 'access_groups' in have_name.keys(): - commands.extend(self.process_access_group(have_name, True)) - else: - if not deleted: # and 'access_groups' in have_name.keys(): - commands.extend(self.process_access_group(want)) - - if len(commands) > 0: - commands.insert(0, 'interface ' + want['name']) - return commands - - def process_access_group(self, item, deleted=False): - commands = [] - for ag in item['access_groups']: - ip = 'ipv6' - if ag['afi'] == 'ipv4': - ip = 'ip' - if ag.get('acls'): - commands.extend(self.process_acl( - ag['acls'], ip, deleted)) - return commands - - def process_acl(self, acls, ip, deleted=False): - commands = [] - no = '' - if deleted: - no = 'no ' - for acl in acls: - port = '' - if acl.get('port'): - port = ' port' - ag = ' access-group ' - if ip == 'ipv6': - ag = ' traffic-filter ' - commands.append(no + ip + port + ag + - acl['name'] + ' ' + acl['direction']) - return commands - - def _state_deleted(self, main_want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if main_want: - for want in main_want: - commands.extend(self.set_commands(want, have, deleted=True)) - else: - for h in have: - commands.extend(self.set_commands(h, have, deleted=True)) - - return commands diff --git a/lib/ansible/module_utils/network/nxos/config/acls/acls.py b/lib/ansible/module_utils/network/nxos/config/acls/acls.py deleted file mode 100644 index 37c8b3d23a..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/acls/acls.py +++ /dev/null @@ -1,690 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_acls class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import socket -import re -from copy import deepcopy -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import to_list, remove_empties, dict_diff -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.argspec.acls.acls import AclsArgs -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.utils.utils import flatten_dict, search_obj_in_list, get_interface_type, normalize_interface - - -class Acls(ConfigBase): - """ - The nxos_acls class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'acls', - ] - - def __init__(self, module): - super(Acls, self).__init__(module) - - def get_acls_facts(self, data=None): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources, data=data) - acls_facts = facts['ansible_network_resources'].get('acls') - if not acls_facts: - return [] - return acls_facts - - def edit_config(self, commands): - """Wrapper method for `_connection.edit_config()` - This exists solely to allow the unit test framework to mock device connection calls. - """ - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - commands = list() - state = self._module.params['state'] - action_states = ['merged', 'replaced', 'deleted', 'overridden'] - - if state == 'gathered': - result['gathered'] = self.get_acls_facts() - elif state == 'rendered': - result['rendered'] = self.set_config({}) - elif state == 'parsed': - result['parsed'] = self.set_config({}) - else: - existing_acls_facts = self.get_acls_facts() - commands.extend(self.set_config(existing_acls_facts)) - if commands and state in action_states: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['before'] = existing_acls_facts - result['commands'] = commands - - changed_acls_facts = self.get_acls_facts() - if result['changed']: - result['after'] = changed_acls_facts - result['warnings'] = warnings - return result - - def set_config(self, existing_acls_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params['config'] - want = [] - if config: - for w in config: - want.append(remove_empties(w)) - have = existing_acls_facts - if want: - want = self.convert_values(want) - resp = self.set_state(want, have) - return to_list(resp) - - def convert_values(self, want): - ''' - This method is used to map and convert the user given values with what will actually be present in the device configuation - ''' - port_protocol = { - 515: 'lpd', - 517: 'talk', - 7: 'echo', - 9: 'discard', - 12: 'exec', - 13: 'login', - 14: 'cmd', - 109: 'pop2', - 19: 'chargen', - 20: 'ftp-data', - 21: 'ftp', - 23: 'telnet', - 25: 'smtp', - 540: 'uucp', - 543: 'klogin', - 544: 'kshell', - 37: 'time', - 43: 'whois', - 49: 'tacacs', - 179: 'bgp', - 53: 'domain', - 194: 'irc', - 70: 'gopher', - 79: 'finger', - 80: 'www', - 101: 'hostname', - 3949: 'drip', - 110: 'pop3', - 111: 'sunrpc', - 496: 'pim-auto-rp', - 113: 'ident', - 119: 'nntp' - } - protocol = { - 1: 'icmp', - 2: 'igmp', - 4: 'ip', - 6: 'tcp', - 103: 'pim', - 108: 'pcp', - 47: 'gre', - 17: 'udp', - 50: 'esp', - 51: 'ahp', - 88: 'eigrp', - 89: 'ospf', - 94: 'nos' - } - precedence = { - 0: 'routine', - 1: 'priority', - 2: 'immediate', - 3: 'flash', - 4: 'flash-override', - 5: 'critical', - 6: 'internet', - 7: 'network' - } - dscp = { - 10: 'AF11', - 12: 'AF12', - 14: 'AF13', - 18: 'AF21', - 20: 'AF22', - 22: 'AF23', - 26: 'AF31', - 28: 'AF32', - 30: 'AF33', - 34: 'AF41', - 36: 'AF42', - 38: 'AF43', - 8: 'CS1', - 16: 'CS2', - 24: 'CS3', - 32: 'CS4', - 40: 'CS5', - 48: 'CS6', - 56: 'CS7', - 0: 'Default', - 46: 'EF' - } - # port_pro_num = list(protocol.keys()) - for afi in want: - if 'acls' in afi.keys(): - for acl in afi['acls']: - if 'aces' in acl.keys(): - for ace in acl['aces']: - if 'dscp' in ace.keys(): - if ace['dscp'].isdigit(): - ace['dscp'] = dscp[int(ace['dscp'])] - ace['dscp'] = ace['dscp'].lower() - if 'precedence' in ace.keys(): - if ace['precedence'].isdigit(): - ace['precedence'] = precedence[int( - ace['precedence'])] - if 'protocol' in ace.keys( - ) and ace['protocol'].isdigit() and int( - ace['protocol']) in protocol.keys(): - ace['protocol'] = protocol[int( - ace['protocol'])] - # convert number to name - if 'protocol' in ace.keys( - ) and ace['protocol'] in ['tcp', 'udp']: - for end in ['source', 'destination']: - if 'port_protocol' in ace[end].keys(): - key = list(ace[end] - ['port_protocol'].keys())[0] - # key could be eq,gt,lt,neq or range - if key != 'range': - val = ace[end]['port_protocol'][ - key] - if val.isdigit() and int(val) in port_protocol.keys( - ): - ace[end]['port_protocol'][ - key] = port_protocol[int( - val)] - else: - st = int(ace[end]['port_protocol'] - ['range']['start']) - - end = int(ace[end]['port_protocol'] - ['range']['end']) - - if st in port_protocol.keys(): - ace[end]['port_protocol'][ - 'range'][ - 'start'] = port_protocol[ - st] - if end in port_protocol.keys(): - ace[end]['port_protocol'][ - 'range'][ - 'end'] = port_protocol[ - end] - return want - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - commands = [] - if state == 'overridden': - commands = (self._state_overridden(want, have)) - elif state == 'deleted': - commands = (self._state_deleted(want, have)) - elif state == 'rendered': - commands = self._state_rendered(want) - elif state == 'parsed': - want = self._module.params['running_config'] - commands = self._state_parsed(want) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(w, have)) - elif state == 'replaced': - commands.extend(self._state_replaced(w, have)) - if state != 'parsed': - commands = [c.strip() for c in commands] - return commands - - def _state_parsed(self, want): - return self.get_acls_facts(want) - - def _state_rendered(self, want): - commands = [] - for w in want: - commands.extend(self.set_commands(w, {})) - return commands - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - have_afi = search_obj_in_list(want['afi'], have, 'afi') - del_dict = {'acls': []} - want_names = [] - if have_afi != want: - if have_afi: - del_dict.update({'afi': have_afi['afi'], 'acls': []}) - if want.get('acls'): - want_names = [w['name'] for w in want['acls']] - have_names = [h['name'] for h in have_afi['acls']] - want_acls = want.get('acls') - for w in want_acls: - acl_commands = [] - if w['name'] not in have_names: - # creates new ACL in replaced state - merge_dict = {'afi': want['afi'], 'acls': [w]} - commands.extend( - self._state_merged(merge_dict, have)) - else: - # acl in want exists in have - have_name = search_obj_in_list( - w['name'], have_afi['acls'], 'name') - have_aces = have_name.get('aces') if have_name.get( - 'aces') else [] - merge_aces = [] - del_aces = [] - w_aces = w.get('aces') if w.get('aces') else [] - - for ace in have_aces: - if ace not in w_aces: - del_aces.append(ace) - for ace in w_aces: - if ace not in have_aces: - merge_aces.append(ace) - merge_dict = { - 'afi': want['afi'], - 'acls': [{ - 'name': w['name'], - 'aces': merge_aces - }] - } - del_dict = { - 'afi': want['afi'], - 'acls': [{ - 'name': w['name'], - 'aces': del_aces - }] - } - if del_dict['acls']: - acl_commands.extend( - self._state_deleted([del_dict], have)) - acl_commands.extend( - self._state_merged(merge_dict, have)) - - for i in range(1, len(acl_commands)): - if acl_commands[i] == acl_commands[0]: - acl_commands[i] = '' - commands.extend(acl_commands) - else: - acls = [] - # no acls given in want, so delete all have acls - for acl in have_afi['acls']: - acls.append({'name': acl['name']}) - del_dict['acls'] = acls - if del_dict['acls']: - commands.extend(self._state_deleted([del_dict], have)) - - else: - # want_afi is not present in have - commands.extend(self._state_merged(want, have)) - - commands = list(filter(None, commands)) - return commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - want_afi = [w['afi'] for w in want] - for h in have: - if h['afi'] in want_afi: - w = search_obj_in_list(h['afi'], want, 'afi') - for h_acl in h['acls']: - w_acl = search_obj_in_list(h_acl['name'], w['acls'], - 'name') - if not w_acl: - del_dict = { - 'afi': h['afi'], - 'acls': [{ - 'name': h_acl['name'] - }] - } - commands.extend(self._state_deleted([del_dict], have)) - else: - # if afi is not in want - commands.extend(self._state_deleted([{'afi': h['afi']}], have)) - for w in want: - commands.extend(self._state_replaced(w, have)) - return commands - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(want, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: # and have != want: - for w in want: - ip = 'ipv6' if w['afi'] == 'ipv6' else 'ip' - acl_names = [] - have_afi = search_obj_in_list(w['afi'], have, 'afi') - # if want['afi] not in have, ignore - if have_afi: - if w.get('acls'): - for acl in w['acls']: - if 'aces' in acl.keys(): - have_name = search_obj_in_list( - acl['name'], have_afi['acls'], 'name') - if have_name: - ace_commands = [] - flag = 0 - for ace in acl['aces']: - if list(ace.keys()) == ['sequence']: - # only sequence number is specified to be deleted - if 'aces' in have_name.keys(): - for h_ace in have_name['aces']: - if h_ace[ - 'sequence'] == ace[ - 'sequence']: - ace_commands.append( - 'no ' + - str(ace['sequence'] - )) - flag = 1 - else: - if 'aces' in have_name.keys(): - for h_ace in have_name['aces']: - # when want['ace'] does not have seq number - if 'sequence' not in ace.keys( - ): - del h_ace['sequence'] - if ace == h_ace: - ace_commands.append( - 'no ' + - self.process_ace( - ace)) - flag = 1 - if flag: - ace_commands.insert( - 0, - ip + ' access-list ' + acl['name']) - commands.extend(ace_commands) - else: - # only name given - for h in have_afi['acls']: - if h['name'] == acl['name']: - acl_names.append(acl['name']) - for name in acl_names: - commands.append('no ' + ip + ' access-list ' + - name) - - else: - # 'only afi is given' - if have_afi.get('acls'): - for h in have_afi['acls']: - acl_names.append(h['name']) - for name in acl_names: - commands.append('no ' + ip + ' access-list ' + - name) - else: - v6 = [] - v4 = [] - v6_local = v4_local = None - for h in have: - if h['afi'] == 'ipv6': - v6 = (acl['name'] for acl in h['acls']) - if 'match_local_traffic' in h.keys(): - v6_local = True - else: - v4 = (acl['name'] for acl in h['acls']) - if 'match_local_traffic' in h.keys(): - v4_local = True - - self.no_commands(v4, commands, v4_local, 'ip') - self.no_commands(v6, commands, v6_local, 'ipv6') - - for name in v6: - commands.append('no ipv6 access-list ' + name) - if v4_local: - commands.append('no ipv6 access-list match-local-traffic') - - return commands - - def no_commands(self, v_list, commands, match_local, ip): - for name in v_list: - commands.append('no ' + ip + ' access-list ' + name) - if match_local: - commands.append('no ' + ip + ' access-list match-local-traffic') - - def set_commands(self, want, have): - commands = [] - have_afi = search_obj_in_list(want['afi'], have, 'afi') - ip = '' - if 'v6' in want['afi']: - ip = 'ipv6 ' - else: - ip = 'ip ' - - if have_afi: - if want.get('acls'): - for w_acl in want['acls']: - have_acl = search_obj_in_list(w_acl['name'], - have_afi['acls'], 'name') - name = w_acl['name'] - flag = 0 - ace_commands = [] - if have_acl != w_acl: - if have_acl: - ace_list = [] - if w_acl.get('aces') and have_acl.get('aces'): - # case 1 --> sequence number not given in want --> new ace - # case 2 --> new sequence number in want --> new ace - # case 3 --> existing sequence number given --> update rule (only for merged state. - # For replaced and overridden, rule is deleted in the state's config) - - ace_list = [ - item for item in w_acl['aces'] - if 'sequence' not in item.keys() - ] # case 1 - - want_seq = [ - item['sequence'] for item in w_acl['aces'] - if 'sequence' in item.keys() - ] - - have_seq = [ - item['sequence'] - for item in have_acl['aces'] - ] - - new_seq = list(set(want_seq) - set(have_seq)) - common_seq = list( - set(want_seq).intersection(set(have_seq))) - - temp_list = [ - item for item in w_acl['aces'] - if 'sequence' in item.keys() - and item['sequence'] in new_seq - ] # case 2 - ace_list.extend(temp_list) - for w in w_acl['aces']: - self.argument_spec = AclsArgs.argument_spec - params = utils.validate_config( - self.argument_spec, { - 'config': [{ - 'afi': - want['afi'], - 'acls': [{ - 'name': name, - 'aces': ace_list - }] - }] - }) - if 'sequence' in w.keys( - ) and w['sequence'] in common_seq: - temp_obj = search_obj_in_list( - w['sequence'], have_acl['aces'], - 'sequence') # case 3 - if temp_obj != w: - for key, val in w.items(): - temp_obj[key] = val - ace_list.append(temp_obj) - if self._module.params[ - 'state'] == 'merged': - ace_commands.append( - 'no ' + str(w['sequence'])) - # remove existing rule to update it - elif w_acl.get('aces'): - # 'have' has ACL defined without any ACE - ace_list = [item for item in w_acl['aces']] - for w_ace in ace_list: - ace_commands.append( - self.process_ace(w_ace).strip()) - flag = 1 - - if flag: - ace_commands.insert(0, - ip + 'access-list ' + name) - - else: - commands.append(ip + 'access-list ' + name) - if 'aces' in w_acl.keys(): - for w_ace in w_acl['aces']: - commands.append( - self.process_ace(w_ace).strip()) - commands.extend(ace_commands) - else: - if want.get('acls'): - for w_acl in want['acls']: - name = w_acl['name'] - commands.append(ip + 'access-list ' + name) - if 'aces' in w_acl.keys(): - for w_ace in w_acl['aces']: - commands.append(self.process_ace(w_ace).strip()) - - return commands - - def process_ace(self, w_ace): - command = '' - ace_keys = w_ace.keys() - if 'remark' in ace_keys: - command += 'remark ' + w_ace['remark'] + ' ' - else: - command += w_ace['grant'] + ' ' - if 'protocol' in ace_keys: - command += w_ace['protocol'] + ' ' - src = self.get_address(w_ace['source'], w_ace['protocol']) - dest = self.get_address(w_ace['destination'], - w_ace['protocol']) - command += src + dest - if 'protocol_options' in ace_keys: - pro = list(w_ace['protocol_options'].keys())[0] - if pro != w_ace['protocol']: - self._module.fail_json( - msg='protocol and protocol_options mismatch') - flags = '' - for k in w_ace['protocol_options'][pro].keys(): - k = re.sub('_', '-', k) - flags += k + ' ' - command += flags - if 'dscp' in ace_keys: - command += 'dscp ' + w_ace['dscp'] + ' ' - if 'fragments' in ace_keys: - command += 'fragments ' - if 'precedence' in ace_keys: - command += 'precedence ' + w_ace['precedence'] + ' ' - if 'log' in ace_keys: - command += 'log ' - if 'sequence' in ace_keys: - command = str(w_ace['sequence']) + ' ' + command - return command - - def get_address(self, endpoint, pro=''): - ret_addr = '' - keys = list(endpoint.keys()) - if 'address' in keys: - if 'wildcard_bits' not in keys: - self._module.fail_json( - msg='wildcard bits not specified for address') - else: - ret_addr = endpoint['address'] + \ - ' ' + endpoint['wildcard_bits'] + ' ' - elif 'any' in keys: - ret_addr = 'any ' - elif 'host' in keys: - ret_addr = 'host ' + endpoint['host'] + ' ' - elif 'prefix' in keys: - ret_addr = endpoint['prefix'] + ' ' - - if pro in ['tcp', 'udp']: - if 'port_protocol' in keys: - options = self.get_options(endpoint['port_protocol']) - ret_addr += options - return ret_addr - - def get_options(self, item): - com = '' - subkey = list(item.keys()) - if 'range' in subkey: - com = 'range ' + item['range']['start'] + \ - ' ' + item['range']['end'] + ' ' - else: - com = subkey[0] + ' ' + item[subkey[0]] + ' ' - return com diff --git a/lib/ansible/module_utils/network/nxos/config/bfd_interfaces/bfd_interfaces.py b/lib/ansible/module_utils/network/nxos/config/bfd_interfaces/bfd_interfaces.py deleted file mode 100644 index 3b0d2e7af7..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/bfd_interfaces/bfd_interfaces.py +++ /dev/null @@ -1,264 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -nxos_bfd_interfaces class -This class creates a command set to bring the current device configuration -to a desired end-state. The command set is based on a comparison of the -current configuration (as dict) and the provided configuration (as dict). -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import dict_diff, to_list -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import flatten_dict, search_obj_in_list - - -class Bfd_interfaces(ConfigBase): - """ - The nxos_bfd_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - gather_network_resources = [ - 'bfd_interfaces', - ] - # exclude_params = [] - - def __init__(self, module): - super(Bfd_interfaces, self).__init__(module) - - def get_bfd_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :returns: A list of interface configs and a platform string - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - bfd_interfaces_facts = facts['ansible_network_resources'].get('bfd_interfaces', []) - platform = facts.get('ansible_net_platform', '') - return bfd_interfaces_facts, platform - - def edit_config(self, commands): - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - cmds = list() - - existing_bfd_interfaces_facts, platform = self.get_bfd_interfaces_facts() - cmds.extend(self.set_config(existing_bfd_interfaces_facts, platform)) - if cmds: - if not self._module.check_mode: - self.edit_config(cmds) - result['changed'] = True - result['commands'] = cmds - - changed_bfd_interfaces_facts, platform = self.get_bfd_interfaces_facts() - result['before'] = existing_bfd_interfaces_facts - if result['changed']: - result['after'] = changed_bfd_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_bfd_interfaces_facts, platform): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - if re.search('N[56]K', platform): - # Some platforms do not support the 'bfd' interface keyword; - # remove the 'bfd' key from each want/have interface. - orig_want = self._module.params['config'] - want = [] - for w in orig_want: - del w['bfd'] - want.append(w) - orig_have = existing_bfd_interfaces_facts - have = [] - for h in orig_have: - del h['bfd'] - have.append(h) - else: - want = self._module.params['config'] - have = existing_bfd_interfaces_facts - - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='config is required for state {0}'.format(state)) - - cmds = list() - if state == 'overridden': - cmds.extend(self._state_overridden(want, have)) - elif state == 'deleted': - cmds.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - cmds.extend(self._state_merged(flatten_dict(w), have)) - elif state == 'replaced': - cmds.extend(self._state_replaced(flatten_dict(w), have)) - return cmds - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - cmds = [] - obj_in_have = search_obj_in_list(want['name'], have, 'name') - if obj_in_have: - diff = dict_diff(want, obj_in_have) - else: - diff = want - merged_cmds = self.set_commands(want, have) - if 'name' not in diff: - diff['name'] = want['name'] - - replaced_cmds = [] - if obj_in_have: - replaced_cmds = self.del_attribs(diff) - if replaced_cmds or merged_cmds: - for cmd in set(replaced_cmds).intersection(set(merged_cmds)): - merged_cmds.remove(cmd) - cmds.extend(replaced_cmds) - cmds.extend(merged_cmds) - return cmds - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - cmds = [] - for h in have: - # Clean up bfd attrs for any interfaces not listed in the play - h = flatten_dict(h) - obj_in_want = flatten_dict(search_obj_in_list(h['name'], want, 'name')) - if obj_in_want: - # Let the 'want' loop handle all vals for this interface - continue - cmds.extend(self.del_attribs(h)) - for w in want: - # Update any want attrs if needed. The overridden state considers - # the play as the source of truth for the entire device, therefore - # set any unspecified attrs to their default state. - w = self.set_none_vals_to_defaults(flatten_dict(w)) - cmds.extend(self.set_commands(w, have)) - return cmds - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(want, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - if not (want or have): - return [] - cmds = [] - if want: - for w in want: - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - cmds.extend(self.del_attribs(obj_in_have)) - else: - for h in have: - cmds.extend(self.del_attribs(flatten_dict(h))) - return cmds - - def del_attribs(self, obj): - if not obj or len(obj.keys()) == 1: - return [] - cmds = [] - # 'bfd' and 'bfd echo' are enabled by default so the handling is - # counter-intuitive; we are enabling them to remove them. The end result - # is that they are removed from the interface config on the device. - if 'bfd' in obj and 'disable' in obj['bfd']: - cmds.append('bfd') - if 'echo' in obj and 'disable' in obj['echo']: - cmds.append('bfd echo') - if cmds: - cmds.insert(0, 'interface ' + obj['name']) - return cmds - - def set_none_vals_to_defaults(self, want): - # Set dict None values to default states - if 'bfd' in want and want['bfd'] is None: - want['bfd'] = 'enable' - if 'echo' in want and want['echo'] is None: - want['echo'] = 'enable' - return want - - def diff_of_dicts(self, want, obj_in_have): - diff = set(want.items()) - set(obj_in_have.items()) - diff = dict(diff) - if diff and want['name'] == obj_in_have['name']: - diff.update({'name': want['name']}) - return diff - - def add_commands(self, want): - if not want: - return [] - cmds = [] - if 'bfd' in want and want['bfd'] is not None: - cmd = 'bfd' if want['bfd'] == 'enable' else 'no bfd' - cmds.append(cmd) - if 'echo' in want and want['echo'] is not None: - cmd = 'bfd echo' if want['echo'] == 'enable' else 'no bfd echo' - cmds.append(cmd) - - if cmds: - cmds.insert(0, 'interface ' + want['name']) - return cmds - - def set_commands(self, want, have): - cmds = [] - obj_in_have = flatten_dict(search_obj_in_list(want['name'], have, 'name')) - if not obj_in_have: - cmds = self.add_commands(want) - else: - diff = self.diff_of_dicts(want, obj_in_have) - cmds = self.add_commands(diff) - return cmds diff --git a/lib/ansible/module_utils/network/nxos/config/hsrp_interfaces/hsrp_interfaces.py b/lib/ansible/module_utils/network/nxos/config/hsrp_interfaces/hsrp_interfaces.py deleted file mode 100644 index e3ef78e540..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/hsrp_interfaces/hsrp_interfaces.py +++ /dev/null @@ -1,248 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos hsrp_interfaces class -This class creates a command set to bring the current device configuration -to a desired end-state. The command set is based on a comparison of the -current configuration (as dict) and the provided configuration (as dict). -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import dict_diff, to_list, remove_empties -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import flatten_dict, get_interface_type, normalize_interface, search_obj_in_list, vlan_range_to_list - - -class Hsrp_interfaces(ConfigBase): - """ - The nxos_hsrp_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'hsrp_interfaces', - ] - - def __init__(self, module): - super(Hsrp_interfaces, self).__init__(module) - - def get_hsrp_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - hsrp_interfaces_facts = facts['ansible_network_resources'].get('hsrp_interfaces', []) - return hsrp_interfaces_facts - - def edit_config(self, commands): - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - cmds = list() - - existing_hsrp_interfaces_facts = self.get_hsrp_interfaces_facts() - cmds.extend(self.set_config(existing_hsrp_interfaces_facts)) - if cmds: - if not self._module.check_mode: - self.edit_config(cmds) - result['changed'] = True - result['commands'] = cmds - changed_hsrp_interfaces_facts = self.get_hsrp_interfaces_facts() - - result['before'] = existing_hsrp_interfaces_facts - if result['changed']: - result['after'] = changed_hsrp_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_hsrp_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params['config'] - want = [] - if config: - for w in config: - w.update({'name': normalize_interface(w['name'])}) - want.append(w) - have = existing_hsrp_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - # check for 'config' keyword in play - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='config is required for state {0}'.format(state)) - - cmds = list() - if state == 'overridden': - cmds.extend(self._state_overridden(want, have)) - elif state == 'deleted': - cmds.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - cmds.extend(self._state_merged(flatten_dict(w), have)) - elif state == 'replaced': - cmds.extend(self._state_replaced(flatten_dict(w), have)) - return cmds - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - cmds = [] - obj_in_have = search_obj_in_list(want['name'], have, 'name') - if obj_in_have: - diff = dict_diff(want, obj_in_have) - else: - diff = want - merged_cmds = self.set_commands(want, have) - if 'name' not in diff: - diff['name'] = want['name'] - - replaced_cmds = [] - if obj_in_have: - replaced_cmds = self.del_attribs(diff) - if replaced_cmds or merged_cmds: - for cmd in set(replaced_cmds).intersection(set(merged_cmds)): - merged_cmds.remove(cmd) - cmds.extend(replaced_cmds) - cmds.extend(merged_cmds) - return cmds - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - cmds = [] - for h in have: - # Check existing states, set to default if not in want or different than want - h = flatten_dict(h) - obj_in_want = search_obj_in_list(h['name'], want, 'name') - if obj_in_want: - # Let the 'want' loop handle all vals for this interface - continue - cmds.extend(self.del_attribs(h)) - for w in want: - # Update any want attrs if needed. The overridden state considers - # the play as the source of truth for the entire device, therefore - # set any unspecified attrs to their default state. - w = self.set_none_vals_to_defaults(flatten_dict(w)) - cmds.extend(self.set_commands(w, have)) - return cmds - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(want, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - if not (want or have): - return [] - cmds = [] - if want: - for w in want: - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - cmds.extend(self.del_attribs(obj_in_have)) - else: - for h in have: - cmds.extend(self.del_attribs(flatten_dict(h))) - return cmds - - def del_attribs(self, obj): - if not obj or len(obj.keys()) == 1: - return [] - cmds = [] - if 'bfd' in obj: - cmds.append('no hsrp bfd') - if cmds: - cmds.insert(0, 'interface ' + obj['name']) - return cmds - - def set_none_vals_to_defaults(self, want): - # Set dict None values to default states - if 'bfd' in want and want['bfd'] is None: - want['bfd'] = 'disable' - return want - - def diff_of_dicts(self, want, obj_in_have): - diff = set(want.items()) - set(obj_in_have.items()) - diff = dict(diff) - if diff and want['name'] == obj_in_have['name']: - diff.update({'name': want['name']}) - return diff - - def add_commands(self, want, obj_in_have): - if not want: - return [] - cmds = [] - if 'bfd' in want and want['bfd'] is not None: - if want['bfd'] == 'enable': - cmd = 'hsrp bfd' - cmds.append(cmd) - elif want['bfd'] == 'disable' and obj_in_have and obj_in_have.get('bfd') == 'enable': - cmd = 'no hsrp bfd' - cmds.append(cmd) - - if cmds: - cmds.insert(0, 'interface ' + want['name']) - return cmds - - def set_commands(self, want, have): - cmds = [] - obj_in_have = search_obj_in_list(want['name'], have, 'name') - if not obj_in_have: - cmds = self.add_commands(want, obj_in_have) - else: - diff = self.diff_of_dicts(want, obj_in_have) - cmds = self.add_commands(diff, obj_in_have) - return cmds diff --git a/lib/ansible/module_utils/network/nxos/config/interfaces/interfaces.py b/lib/ansible/module_utils/network/nxos/config/interfaces/interfaces.py deleted file mode 100644 index 0082b376b7..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/interfaces/interfaces.py +++ /dev/null @@ -1,379 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from copy import deepcopy -import re - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import dict_diff, to_list, remove_empties -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import normalize_interface, search_obj_in_list -from ansible.module_utils.network.nxos.utils.utils import remove_rsvd_interfaces -from ansible.module_utils.network.nxos.nxos import default_intf_enabled - - -class Interfaces(ConfigBase): - """ - The nxos_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'interfaces', - ] - - exclude_params = [ - 'description', - 'mtu', - 'speed', - 'duplex', - ] - - def __init__(self, module): - super(Interfaces, self).__init__(module) - - def get_interfaces_facts(self, get_default_interfaces=False): - """ Get the 'facts' (the current configuration) - - :get_default_interfaces: boolean - when True include a list of existing-but-default interface names in the facts dict. - - The defaults list is primarily used to detect non-existent virtual interfaces. - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - interfaces_facts = facts['ansible_network_resources'].get('interfaces') - interfaces_facts = remove_rsvd_interfaces(interfaces_facts) - if get_default_interfaces: - default_interfaces = facts['ansible_network_resources'].get('default_interfaces', []) - interfaces_facts.append(default_interfaces) - - self.intf_defs = facts.get('intf_defs', {}) - return interfaces_facts - - def edit_config(self, commands): - """Wrapper method for `_connection.edit_config()` - This method exists solely to allow the unit test framework to mock device connection calls. - """ - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_interfaces_facts = self.get_interfaces_facts(get_default_interfaces=True) - default_intf_list = existing_interfaces_facts.pop() - commands.extend(self.set_config(existing_interfaces_facts, default_intf_list)) - if commands: - if not self._module.check_mode: - self.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_interfaces_facts = self.get_interfaces_facts() - - result['before'] = existing_interfaces_facts - if result['changed']: - result['after'] = changed_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_interfaces_facts, default_intf_list): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params.get('config') - want = [] - if config: - for w in config: - w.update({'name': normalize_interface(w['name'])}) - want.append(remove_empties(w)) - have = deepcopy(existing_interfaces_facts) - for i in want: - # 'have' does not include objects from the default_interfaces list. - # Add any 'want' names from default_interfaces to the 'have' list. - if i['name'] in default_intf_list: - have.append({'name': i['name']}) - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='config is required for state {0}'.format(state)) - - commands = list() - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(w, have)) - elif state == 'replaced': - commands.extend(self._state_replaced(w, have)) - return commands - - def _state_replaced(self, w, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - name = w['name'] - obj_in_have = search_obj_in_list(name, have, 'name') - if obj_in_have: - # If 'w' does not specify mode then intf may need to change to its - # default mode, however default mode may depend on sysdef. - if not w.get('mode') and re.search('Ethernet|port-channel', name): - sysdefs = self.intf_defs['sysdefs'] - sysdef_mode = sysdefs['mode'] - if obj_in_have.get('mode') != sysdef_mode: - w['mode'] = sysdef_mode - diff = dict_diff(w, obj_in_have) - else: - diff = w - - merged_commands = self.set_commands(w, have) - if merged_commands: - # merged_commands: - # - These commands are changes specified by the playbook. - # - merged_commands apply to both existing and new objects - # replaced_commands: - # - These are the unspecified commands, used to reset any params - # that are not already set to default states - # - replaced_commands should only be used on 'have' objects - # (interfaces that already exist) - if obj_in_have: - if 'name' not in diff: - diff['name'] = name - wkeys = w.keys() - dkeys = diff.keys() - for k in wkeys: - if k in self.exclude_params and k in dkeys: - del diff[k] - replaced_commands = self.del_attribs(diff) - cmds = set(replaced_commands).intersection(set(merged_commands)) - for cmd in cmds: - merged_commands.remove(cmd) - commands.extend(replaced_commands) - - commands.extend(merged_commands) - return commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - # overridden is the same as replaced behavior except for the scope. - cmds = [] - existing_interfaces = [] - for h in have: - existing_interfaces.append(h['name']) - obj_in_want = search_obj_in_list(h['name'], want, 'name') - if obj_in_want: - if h != obj_in_want: - replaced_cmds = self._state_replaced(obj_in_want, [h]) - if replaced_cmds: - cmds.extend(replaced_cmds) - else: - cmds.extend(self.del_attribs(h)) - - for w in want: - if w['name'] not in existing_interfaces: - # This is an object that was excluded from the 'have' list - # because all of its params are currently set to default states - # -OR- it's a new object that does not exist on the device yet. - cmds.extend(self.add_commands(w)) - return cmds - - def _state_merged(self, w, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(w, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - for w in want: - obj_in_have = search_obj_in_list(w['name'], have, 'name') - commands.extend(self.del_attribs(obj_in_have)) - else: - if not have: - return commands - for h in have: - commands.extend(self.del_attribs(h)) - return commands - - def default_enabled(self, want=None, have=None, action=''): - # 'enabled' default state depends on the interface type and L2 state. - # Note that the current default could change when changing L2/L3 modes. - if want is None: - want = {} - if have is None: - have = {} - name = have.get('name') - if name is None: - return None - - sysdefs = self.intf_defs['sysdefs'] - sysdef_mode = sysdefs['mode'] - - # Get the default enabled state for this interface. This was collected - # during Facts gathering. - intf_def_enabled = self.intf_defs.get(name) - - have_mode = have.get('mode', sysdef_mode) - if action == 'delete' and not want: - want_mode = sysdef_mode - else: - want_mode = want.get('mode', have_mode) - if (want_mode and have_mode) is None or (want_mode != have_mode) or intf_def_enabled is None: - # L2-L3 is changing or this is a new virtual intf. Get new default. - intf_def_enabled = default_intf_enabled(name=name, sysdefs=sysdefs, mode=want_mode) - - return intf_def_enabled - - def del_attribs(self, obj): - commands = [] - if not obj or len(obj.keys()) == 1: - return commands - # mode/switchport changes should occur before other changes - sysdef_mode = self.intf_defs['sysdefs']['mode'] - if 'mode' in obj and obj['mode'] != sysdef_mode: - no_cmd = 'no ' if sysdef_mode == 'layer3' else '' - commands.append(no_cmd + 'switchport') - if 'description' in obj: - commands.append('no description') - if 'speed' in obj: - commands.append('no speed') - if 'duplex' in obj: - commands.append('no duplex') - if 'enabled' in obj: - sysdef_enabled = self.default_enabled(have=obj, action='delete') - if obj['enabled'] is False and sysdef_enabled is True: - commands.append('no shutdown') - elif obj['enabled'] is True and sysdef_enabled is False: - commands.append('shutdown') - if 'mtu' in obj: - commands.append('no mtu') - if 'ip_forward' in obj and obj['ip_forward'] is True: - commands.append('no ip forward') - if 'fabric_forwarding_anycast_gateway' in obj and obj['fabric_forwarding_anycast_gateway'] is True: - commands.append('no fabric forwarding mode anycast-gateway') - if commands: - commands.insert(0, 'interface ' + obj['name']) - - return commands - - def diff_of_dicts(self, w, obj): - diff = set(w.items()) - set(obj.items()) - diff = dict(diff) - if diff and w['name'] == obj['name']: - diff.update({'name': w['name']}) - return diff - - def add_commands(self, d, obj_in_have=None): - commands = [] - if not d: - return commands - if obj_in_have is None: - obj_in_have = {} - # mode/switchport changes should occur before other changes - if 'mode' in d: - sysdef_mode = self.intf_defs['sysdefs']['mode'] - have_mode = obj_in_have.get('mode', sysdef_mode) - want_mode = d['mode'] - if have_mode == 'layer2': - if want_mode == 'layer3': - commands.append('no switchport') - elif want_mode == 'layer2': - commands.append('switchport') - if 'description' in d: - commands.append('description ' + d['description']) - if 'speed' in d: - commands.append('speed ' + str(d['speed'])) - if 'duplex' in d: - commands.append('duplex ' + d['duplex']) - if 'enabled' in d: - have_enabled = obj_in_have.get('enabled', self.default_enabled(d, obj_in_have)) - if d['enabled'] is False and have_enabled is True: - commands.append('shutdown') - elif d['enabled'] is True and have_enabled is False: - commands.append('no shutdown') - if 'mtu' in d: - commands.append('mtu ' + str(d['mtu'])) - if 'ip_forward' in d: - if d['ip_forward'] is True: - commands.append('ip forward') - else: - commands.append('no ip forward') - if 'fabric_forwarding_anycast_gateway' in d: - if d['fabric_forwarding_anycast_gateway'] is True: - commands.append('fabric forwarding mode anycast-gateway') - else: - commands.append('no fabric forwarding mode anycast-gateway') - if commands or not obj_in_have: - commands.insert(0, 'interface' + ' ' + d['name']) - return commands - - def set_commands(self, w, have): - commands = [] - obj_in_have = search_obj_in_list(w['name'], have, 'name') - if not obj_in_have: - commands = self.add_commands(w) - else: - diff = self.diff_of_dicts(w, obj_in_have) - commands = self.add_commands(diff, obj_in_have) - return commands diff --git a/lib/ansible/module_utils/network/nxos/config/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/nxos/config/l2_interfaces/l2_interfaces.py deleted file mode 100644 index dba9afc981..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,301 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_l2_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import dict_diff, to_list, remove_empties -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import flatten_dict, normalize_interface, search_obj_in_list, vlan_range_to_list - - -class L2_interfaces(ConfigBase): - """ - The nxos_l2_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'l2_interfaces', - ] - - exclude_params = [ - 'vlan', - 'allowed_vlans', - 'native_vlans', - ] - - def __init__(self, module): - super(L2_interfaces, self).__init__(module) - - def get_l2_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - l2_interfaces_facts = facts['ansible_network_resources'].get('l2_interfaces') - if not l2_interfaces_facts: - return [] - return l2_interfaces_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_l2_interfaces_facts = self.get_l2_interfaces_facts() - commands.extend(self.set_config(existing_l2_interfaces_facts)) - if commands: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_l2_interfaces_facts = self.get_l2_interfaces_facts() - - result['before'] = existing_l2_interfaces_facts - if result['changed']: - result['after'] = changed_l2_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_l2_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params.get('config') - want = [] - if config: - for w in config: - w.update({'name': normalize_interface(w['name'])}) - self.expand_trunk_allowed_vlans(w) - want.append(remove_empties(w)) - have = existing_l2_interfaces_facts - for h in have: - self.expand_trunk_allowed_vlans(h) - resp = self.set_state(want, have) - return to_list(resp) - - def expand_trunk_allowed_vlans(self, d): - if not d: - return None - if 'trunk' in d and d['trunk']: - if 'allowed_vlans' in d['trunk']: - allowed_vlans = vlan_range_to_list(d['trunk']['allowed_vlans']) - vlans_list = [str(l) for l in sorted(allowed_vlans)] - d['trunk']['allowed_vlans'] = ",".join(vlans_list) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='config is required for state {0}'.format(state)) - - commands = list() - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(flatten_dict(w), have)) - elif state == 'replaced': - commands.extend(self._state_replaced(flatten_dict(w), have)) - return commands - - def _state_replaced(self, w, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - if obj_in_have: - diff = dict_diff(w, obj_in_have) - else: - diff = w - merged_commands = self.set_commands(w, have, True) - if 'name' not in diff: - diff['name'] = w['name'] - - dkeys = diff.keys() - for k in w.copy(): - if k in self.exclude_params and k in dkeys: - del diff[k] - replaced_commands = self.del_attribs(diff) - - if merged_commands: - cmds = set(replaced_commands).intersection(set(merged_commands)) - for cmd in cmds: - merged_commands.remove(cmd) - commands.extend(replaced_commands) - commands.extend(merged_commands) - return commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - for h in have: - h = flatten_dict(h) - obj_in_want = flatten_dict(search_obj_in_list(h['name'], want, 'name')) - if h == obj_in_want: - continue - for w in want: - w = flatten_dict(w) - if h['name'] == w['name']: - wkeys = w.keys() - hkeys = h.keys() - for k in wkeys: - if k in self.exclude_params and k in hkeys: - del h[k] - commands.extend(self.del_attribs(h)) - for w in want: - commands.extend(self.set_commands(flatten_dict(w), have, True)) - return commands - - def _state_merged(self, w, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(w, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - for w in want: - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - commands.extend(self.del_attribs(obj_in_have)) - else: - if not have: - return commands - for h in have: - commands.extend(self.del_attribs(flatten_dict(h))) - return commands - - def del_attribs(self, obj): - commands = [] - if not obj or len(obj.keys()) == 1: - return commands - - cmd = 'no switchport ' - if 'vlan' in obj: - commands.append(cmd + 'access vlan') - if 'mode' in obj: - commands.append(cmd + 'mode') - if 'allowed_vlans' in obj: - commands.append(cmd + 'trunk allowed vlan') - if 'native_vlan' in obj: - commands.append(cmd + 'trunk native vlan') - if commands: - commands.insert(0, 'interface ' + obj['name']) - return commands - - def diff_of_dicts(self, w, obj): - diff = set(w.items()) - set(obj.items()) - diff = dict(diff) - if diff and w['name'] == obj['name']: - diff.update({'name': w['name']}) - return diff - - def add_commands(self, d, vlan_exists=False): - commands = [] - if not d: - return commands - - cmd = 'switchport ' - if 'mode' in d: - commands.append(cmd + 'mode {0}'.format(d['mode'])) - if 'vlan' in d: - commands.append(cmd + 'access vlan ' + str(d['vlan'])) - if 'allowed_vlans' in d: - if vlan_exists: - commands.append(cmd + 'trunk allowed vlan add ' + str(d['allowed_vlans'])) - else: - commands.append(cmd + 'trunk allowed vlan ' + str(d['allowed_vlans'])) - if 'native_vlan' in d: - commands.append(cmd + 'trunk native vlan ' + str(d['native_vlan'])) - if commands: - commands.insert(0, 'interface ' + d['name']) - return commands - - def set_commands(self, w, have, replace=False): - commands = [] - - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - if not obj_in_have: - commands = self.add_commands(w) - else: - diff = self.diff_of_dicts(w, obj_in_have) - if diff and not replace: - if 'mode' in diff.keys() and diff['mode']: - commands = self.add_commands(diff) - if "allowed_vlans" in diff.keys() and diff["allowed_vlans"]: - vlan_tobe_added = diff["allowed_vlans"].split(',') - vlan_list = vlan_tobe_added[:] - if obj_in_have.get("allowed_vlans"): - have_vlans = obj_in_have["allowed_vlans"].split(',') - else: - have_vlans = [] - for w_vlans in vlan_list: - if w_vlans in have_vlans: - vlan_tobe_added.pop(vlan_tobe_added.index(w_vlans)) - if vlan_tobe_added: - diff.update({"allowed_vlans": ','.join(vlan_tobe_added)}) - if have_vlans: - commands = self.add_commands(diff, True) - else: - commands = self.add_commands(diff) - return commands - commands = self.add_commands(diff) - return commands diff --git a/lib/ansible/module_utils/network/nxos/config/l3_interfaces/l3_interfaces.py b/lib/ansible/module_utils/network/nxos/config/l3_interfaces/l3_interfaces.py deleted file mode 100644 index 6febab96d7..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/l3_interfaces/l3_interfaces.py +++ /dev/null @@ -1,488 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_l3_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re - -from copy import deepcopy -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import to_list, remove_empties -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import normalize_interface, search_obj_in_list -from ansible.module_utils.network.nxos.utils.utils import remove_rsvd_interfaces, get_interface_type - - -class L3_interfaces(ConfigBase): - """ - The nxos_l3_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'l3_interfaces', - ] - - exclude_params = [ - ] - - def __init__(self, module): - super(L3_interfaces, self).__init__(module) - - def get_l3_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - l3_interfaces_facts = facts['ansible_network_resources'].get('l3_interfaces') - if not l3_interfaces_facts: - return [] - - self.platform = self.get_platform_type() - return remove_rsvd_interfaces(l3_interfaces_facts) - - def get_platform_type(self): - default, _warnings = Facts(self._module).get_facts(legacy_facts_type=['default']) - return default.get('ansible_net_platform', '') - - def edit_config(self, commands): - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_l3_interfaces_facts = self.get_l3_interfaces_facts() - commands.extend(self.set_config(existing_l3_interfaces_facts)) - if commands: - if not self._module.check_mode: - self.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_l3_interfaces_facts = self.get_l3_interfaces_facts() - - result['before'] = existing_l3_interfaces_facts - if result['changed']: - result['after'] = changed_l3_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_l3_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params.get('config') - want = [] - if config: - for w in config: - w.update({'name': normalize_interface(w['name'])}) - if get_interface_type(w['name']) == 'management': - self._module.fail_json(msg="The 'management' interface is not allowed to be managed by this module") - want.append(remove_empties(w)) - have = deepcopy(existing_l3_interfaces_facts) - self.init_check_existing(have) - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='config is required for state {0}'.format(state)) - - commands = list() - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(w, have)) - elif state == 'replaced': - commands.extend(self._state_replaced(w, have)) - return commands - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - Scope is limited to interface objects defined in the playbook. - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - cmds = [] - name = want['name'] - obj_in_have = search_obj_in_list(want['name'], have, 'name') - - have_v4 = obj_in_have.pop('ipv4', []) if obj_in_have else [] - have_v6 = obj_in_have.pop('ipv6', []) if obj_in_have else [] - - # Process lists of dicts separately - v4_cmds = self._v4_cmds(want.pop('ipv4', []), have_v4, state='replaced') - v6_cmds = self._v6_cmds(want.pop('ipv6', []), have_v6, state='replaced') - - # Process remaining attrs - if obj_in_have: - # Find 'want' changes first - diff = self.diff_of_dicts(want, obj_in_have) - rmv = {'name': name} - haves_not_in_want = set(obj_in_have.keys()) - set(want.keys()) - set(diff.keys()) - for i in haves_not_in_want: - rmv[i] = obj_in_have[i] - cmds.extend(self.generate_delete_commands(rmv)) - else: - diff = want - - cmds.extend(self.add_commands(diff, name=name)) - cmds.extend(v4_cmds) - cmds.extend(v6_cmds) - self.cmd_order_fixup(cmds, name) - return cmds - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - Scope includes all interface objects on the device. - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - # overridden behavior is the same as replaced except for scope. - cmds = [] - existing_vlans = [] - for i in have: - obj_in_want = search_obj_in_list(i['name'], want, 'name') - if obj_in_want: - if i != obj_in_want: - v4_cmds = self._v4_cmds(obj_in_want.pop('ipv4', []), i.pop('ipv4', []), state='overridden') - replaced_cmds = self._state_replaced(obj_in_want, [i]) - replaced_cmds.extend(v4_cmds) - self.cmd_order_fixup(replaced_cmds, obj_in_want['name']) - cmds.extend(replaced_cmds) - else: - deleted_cmds = self.generate_delete_commands(i) - self.cmd_order_fixup(deleted_cmds, i['name']) - cmds.extend(deleted_cmds) - - for i in want: - if [item for item in have if i['name'] == item['name']]: - continue - cmds.extend(self.add_commands(i, name=i['name'])) - - return cmds - - def _state_merged(self, w, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(w, have) - - def _v4_cmds(self, want, have, state=None): - """Helper method for processing ipv4 changes. - This is needed to handle primary/secondary address changes, which require a specific sequence when changing. - """ - # The ip address cli does not allow removing primary addresses while - # secondaries are present, but it does allow changing a primary to a - # new address as long as the address is not a current secondary. - # Be aware of scenarios where a secondary is taking over - # the role of the primary, which must be changed in sequence. - # In general, primaries/secondaries should change in this order: - # Step 1. Remove secondaries that are being changed or removed - # Step 2. Change the primary if needed - # Step 3. Merge secondaries - - # Normalize inputs (add tag key if not present) - for i in want: - i['tag'] = i.get('tag') - for i in have: - i['tag'] = i.get('tag') - - merged = True if state == 'merged' else False - replaced = True if state == 'replaced' else False - overridden = True if state == 'overridden' else False - - # Create secondary and primary wants/haves - sec_w = [i for i in want if i.get('secondary')] - sec_h = [i for i in have if i.get('secondary')] - pri_w = [i for i in want if not i.get('secondary')] - pri_h = [i for i in have if not i.get('secondary')] - pri_w = pri_w[0] if pri_w else {} - pri_h = pri_h[0] if pri_h else {} - cmds = [] - - # Remove all addrs when no primary is specified in want (pri_w) - if pri_h and not pri_w and (replaced or overridden): - cmds.append('no ip address') - return cmds - - # 1. Determine which secondaries are changing and remove them. Need a have/want - # diff instead of want/have because a have sec addr may be changing to a pri. - sec_to_rmv = [] - sec_diff = self.diff_list_of_dicts(sec_h, sec_w) - for i in sec_diff: - if overridden or [w for w in sec_w if w['address'] == i['address']]: - sec_to_rmv.append(i['address']) - - # Check if new primary is currently a secondary - if pri_w and [h for h in sec_h if h['address'] == pri_w['address']]: - if not overridden: - sec_to_rmv.append(pri_w['address']) - - # Remove the changing secondaries - cmds.extend(['no ip address %s secondary' % i for i in sec_to_rmv]) - - # 2. change primary - if pri_w: - diff = dict(set(pri_w.items()) - set(pri_h.items())) - if diff: - cmd = 'ip address %s' % diff['address'] - tag = diff.get('tag') - cmd += ' tag %s' % tag if tag else '' - cmds.append(cmd) - - # 3. process remaining secondaries last - sec_w_to_chg = self.diff_list_of_dicts(sec_w, sec_h) - for i in sec_w_to_chg: - cmd = 'ip address %s secondary' % i['address'] - cmd += ' tag %s' % i['tag'] if i['tag'] else '' - cmds.append(cmd) - - return cmds - - def _v6_cmds(self, want, have, state=''): - """Helper method for processing ipv6 changes. - This is needed to avoid unnecessary churn on the device when removing or changing multiple addresses. - """ - # Normalize inputs (add tag key if not present) - for i in want: - i['tag'] = i.get('tag') - for i in have: - i['tag'] = i.get('tag') - - cmds = [] - # items to remove (items in 'have' only) - if state == 'replaced': - for i in self.diff_list_of_dicts(have, want): - want_addr = [w for w in want if w['address'] == i['address']] - if not want_addr: - cmds.append('no ipv6 address %s' % i['address']) - elif i['tag'] and not want_addr[0]['tag']: - # Must remove entire cli when removing tag - cmds.append('no ipv6 address %s' % i['address']) - - # items to merge/add - for i in self.diff_list_of_dicts(want, have): - addr = i['address'] - tag = i['tag'] - if not tag and state == 'merged': - # When want is IP-no-tag and have is IP+tag it will show up in diff, - # but for merged nothing has changed, so ignore it for idempotence. - have_addr = [h for h in have if h['address'] == addr] - if have_addr and have_addr[0].get('tag'): - continue - cmd = 'ipv6 address %s' % i['address'] - cmd += ' tag %s' % tag if tag else '' - cmds.append(cmd) - - return cmds - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - for w in want: - obj_in_have = search_obj_in_list(w['name'], have, 'name') - commands.extend(self.del_all_attribs(obj_in_have)) - else: - if not have: - return commands - for h in have: - commands.extend(self.del_all_attribs(h)) - return commands - - def del_all_attribs(self, obj): - commands = [] - if not obj or len(obj.keys()) == 1: - return commands - commands = self.generate_delete_commands(obj) - self.cmd_order_fixup(commands, obj['name']) - return commands - - def generate_delete_commands(self, obj): - """Generate CLI commands to remove non-default settings. - obj: dict of attrs to remove - """ - commands = [] - name = obj.get('name') - if 'dot1q' in obj: - commands.append('no encapsulation dot1q') - if 'redirects' in obj: - if not self.check_existing(name, 'has_secondary') or re.match('N[3567]', self.platform): - # device auto-enables redirects when secondaries are removed; - # auto-enable may fail on legacy platforms so always do explicit enable - commands.append('ip redirects') - if 'unreachables' in obj: - commands.append('no ip unreachables') - if 'ipv4' in obj: - commands.append('no ip address') - if 'ipv6' in obj: - commands.append('no ipv6 address') - return commands - - def init_check_existing(self, have): - """Creates a class var dict for easier access to existing states - """ - self.existing_facts = dict() - have_copy = deepcopy(have) - for intf in have_copy: - name = intf['name'] - self.existing_facts[name] = intf - # Check for presence of secondaries; used for ip redirects logic - if [i for i in intf.get('ipv4', []) if i.get('secondary')]: - self.existing_facts[name]['has_secondary'] = True - - def check_existing(self, name, query): - """Helper method to lookup existing states on an interface. - This is needed for attribute changes that have additional dependencies; - e.g. 'ip redirects' may auto-enable when all secondary ip addrs are removed. - """ - if name: - have = self.existing_facts.get(name, {}) - if 'has_secondary' in query: - return have.get('has_secondary', False) - if 'redirects' in query: - return have.get('redirects', True) - if 'unreachables' in query: - return have.get('unreachables', False) - return None - - def diff_of_dicts(self, w, obj): - diff = set(w.items()) - set(obj.items()) - diff = dict(diff) - if diff and w['name'] == obj['name']: - diff.update({'name': w['name']}) - return diff - - def diff_list_of_dicts(self, w, h): - diff = [] - set_w = set(tuple(sorted(d.items())) for d in w) if w else set() - set_h = set(tuple(sorted(d.items())) for d in h) if h else set() - difference = set_w.difference(set_h) - for element in difference: - diff.append(dict((x, y) for x, y in element)) - return diff - - def add_commands(self, diff, name=''): - commands = [] - if not diff: - return commands - if 'dot1q' in diff: - commands.append('encapsulation dot1q ' + str(diff['dot1q'])) - if 'redirects' in diff: - # Note: device will auto-disable redirects when secondaries are present - if diff['redirects'] != self.check_existing(name, 'redirects'): - no_cmd = 'no ' if diff['redirects'] is False else '' - commands.append(no_cmd + 'ip redirects') - self.cmd_order_fixup(commands, name) - if 'unreachables' in diff: - if diff['unreachables'] != self.check_existing(name, 'unreachables'): - no_cmd = 'no ' if diff['unreachables'] is False else '' - commands.append(no_cmd + 'ip unreachables') - if 'ipv4' in diff: - commands.extend(self.generate_afi_commands(diff['ipv4'])) - if 'ipv6' in diff: - commands.extend(self.generate_afi_commands(diff['ipv6'])) - self.cmd_order_fixup(commands, name) - - return commands - - def generate_afi_commands(self, diff): - cmds = [] - for i in diff: - cmd = 'ipv6 address ' if re.search('::', i['address']) else 'ip address ' - cmd += i['address'] - if i.get('secondary'): - cmd += ' secondary' - if i.get('tag'): - cmd += ' tag ' + str(i['tag']) - cmds.append(cmd) - return cmds - - def set_commands(self, w, have): - commands = [] - name = w['name'] - obj_in_have = search_obj_in_list(name, have, 'name') - if not obj_in_have: - commands = self.add_commands(w, name=name) - else: - # lists of dicts must be processed separately from non-list attrs - v4_cmds = self._v4_cmds(w.pop('ipv4', []), obj_in_have.pop('ipv4', []), state='merged') - v6_cmds = self._v6_cmds(w.pop('ipv6', []), obj_in_have.pop('ipv6', []), state='merged') - - # diff remaining attrs - diff = self.diff_of_dicts(w, obj_in_have) - commands = self.add_commands(diff, name=name) - commands.extend(v4_cmds) - commands.extend(v6_cmds) - - self.cmd_order_fixup(commands, name) - return commands - - def cmd_order_fixup(self, cmds, name): - """Inserts 'interface <name>' config at the beginning of populated command list; reorders dependent commands that must process after others. - """ - if cmds: - if name and not [item for item in cmds if item.startswith('interface')]: - cmds.insert(0, 'interface ' + name) - - redirects = [item for item in cmds if re.match('(no )*ip redirects', item)] - if redirects: - # redirects should occur after ipv4 commands, just move to end of list - redirects = redirects.pop() - cmds.remove(redirects) - cmds.append(redirects) diff --git a/lib/ansible/module_utils/network/nxos/config/lacp/lacp.py b/lib/ansible/module_utils/network/nxos/config/lacp/lacp.py deleted file mode 100644 index ebed250184..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/lacp/lacp.py +++ /dev/null @@ -1,203 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_lacp class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import dict_diff, to_list, remove_empties -from ansible.module_utils.network.nxos.facts.facts import Facts - - -class Lacp(ConfigBase): - """ - The nxos_lacp class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lacp', - ] - - exclude_params = [ - 'priority', - 'mac', - ] - - def __init__(self, module): - super(Lacp, self).__init__(module) - - def get_lacp_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - lacp_facts = facts['ansible_network_resources'].get('lacp') - if not lacp_facts: - return [] - return lacp_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_lacp_facts = self.get_lacp_facts() - commands.extend(self.set_config(existing_lacp_facts)) - if commands: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_lacp_facts = self.get_lacp_facts() - - result['before'] = existing_lacp_facts - if result['changed']: - result['after'] = changed_lacp_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_lacp_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - want = remove_empties(self._module.params['config']) - have = existing_lacp_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - commands = list() - - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - elif state == 'merged': - commands.extend(self._state_merged(want, have)) - elif state == 'replaced': - commands.extend(self._state_replaced(want, have)) - return commands - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - diff = dict_diff(want, have) - wkeys = want.keys() - dkeys = diff.keys() - for k in wkeys: - if k in self.exclude_params and k in dkeys: - del diff[k] - deleted_commands = self.del_all(diff) - merged_commands = self._state_merged(want, have) - - commands.extend(deleted_commands) - if merged_commands: - commands.extend(merged_commands) - - return commands - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(want, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if not have: - return commands - commands.extend(self.del_all(have)) - return commands - - def get_diff(self, comparable, base): - diff = {} - if not base: - diff = comparable - else: - diff = dict_diff(base, comparable) - return diff - - def del_all(self, diff): - commands = [] - base = 'no lacp system-' - diff = diff.get('system') - if diff: - if 'priority' in diff: - commands.append(base + 'priority') - if 'mac' in diff: - commands.append(base + 'mac') - return commands - - def add_commands(self, diff): - commands = [] - base = 'lacp system-' - diff = diff.get('system') - if diff and 'priority' in diff: - cmd = base + 'priority' + ' ' + str(diff['priority']) - commands.append(cmd) - if diff and 'mac' in diff: - cmd = '' - if 'address' in diff['mac']: - cmd += base + 'mac' + ' ' + diff['mac']['address'] - if 'role' in diff['mac']: - cmd += ' ' + 'role' + ' ' + diff['mac']['role'] - if cmd: - commands.append(cmd) - - return commands - - def set_commands(self, want, have): - if not want: - return [] - diff = self.get_diff(want, have) - return self.add_commands(diff) diff --git a/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py deleted file mode 100644 index 891d2466c8..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py +++ /dev/null @@ -1,284 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_lacp_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import to_list, dict_diff, remove_empties -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import flatten_dict, search_obj_in_list, get_interface_type, normalize_interface - - -class Lacp_interfaces(ConfigBase): - """ - The nxos_lacp_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lacp_interfaces', - ] - - exclude_params = [ - 'port_priority', - 'rate', - 'min', - 'max', - ] - - def __init__(self, module): - super(Lacp_interfaces, self).__init__(module) - - def get_lacp_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - lacp_interfaces_facts = facts['ansible_network_resources'].get('lacp_interfaces') - if not lacp_interfaces_facts: - return [] - return lacp_interfaces_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_lacp_interfaces_facts = self.get_lacp_interfaces_facts() - commands.extend(self.set_config(existing_lacp_interfaces_facts)) - if commands: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_lacp_interfaces_facts = self.get_lacp_interfaces_facts() - - result['before'] = existing_lacp_interfaces_facts - if result['changed']: - result['after'] = changed_lacp_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_lacp_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params.get('config') - want = [] - if config: - for w in config: - if get_interface_type(w['name']) not in ('portchannel', 'ethernet'): - self._module.fail_json(msg='This module works with either portchannel or ethernet') - w.update({'name': normalize_interface(w['name'])}) - want.append(remove_empties(w)) - have = existing_lacp_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='config is required for state {0}'.format(state)) - commands = list() - - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(flatten_dict(w), have)) - elif state == 'replaced': - commands.extend(self._state_replaced(flatten_dict(w), have)) - return commands - - def _state_replaced(self, w, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - diff = dict_diff(w, obj_in_have) - merged_commands = self.set_commands(w, have) - if 'name' not in diff: - diff['name'] = w['name'] - wkeys = w.keys() - dkeys = diff.keys() - for k in wkeys: - if k in self.exclude_params and k in dkeys: - del diff[k] - replaced_commands = self.del_attribs(diff) - - if merged_commands: - cmds = set(replaced_commands).intersection(set(merged_commands)) - for cmd in cmds: - merged_commands.remove(cmd) - commands.extend(replaced_commands) - commands.extend(merged_commands) - return commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - for h in have: - h = flatten_dict(h) - obj_in_want = flatten_dict(search_obj_in_list(h['name'], want, 'name')) - if h == obj_in_want: - continue - for w in want: - w = flatten_dict(w) - if h['name'] == w['name']: - wkeys = w.keys() - hkeys = h.keys() - for k in wkeys: - if k in self.exclude_params and k in hkeys: - del h[k] - commands.extend(self.del_attribs(h)) - for w in want: - commands.extend(self.set_commands(flatten_dict(w), have)) - return commands - - def _state_merged(self, w, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(w, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - for w in want: - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - commands.extend(self.del_attribs(obj_in_have)) - else: - if not have: - return commands - for h in have: - commands.extend(self.del_attribs(flatten_dict(h))) - return commands - - def del_attribs(self, obj): - commands = [] - if not obj or len(obj.keys()) == 1: - return commands - commands.append('interface ' + obj['name']) - if 'graceful' in obj: - commands.append('lacp graceful-convergence') - if 'vpc' in obj: - commands.append('no lacp vpn-convergence') - if 'suspend_individual' in obj: - commands.append('lacp suspend_individual') - if 'mode' in obj: - commands.append('no lacp mode ' + obj['mode']) - if 'max' in obj: - commands.append('no lacp max-bundle') - if 'min' in obj: - commands.append('no lacp min-links') - if 'port_priority' in obj: - commands.append('no lacp port-priority') - if 'rate' in obj: - commands.append('no lacp rate') - return commands - - def diff_of_dicts(self, w, obj): - diff = set(w.items()) - set(obj.items()) - diff = dict(diff) - if diff and w['name'] == obj['name']: - diff.update({'name': w['name']}) - return diff - - def add_commands(self, d): - commands = [] - if not d: - return commands - commands.append('interface' + ' ' + d['name']) - - if 'port_priority' in d: - commands.append('lacp port-priority ' + str(d['port_priority'])) - if 'rate' in d: - commands.append('lacp rate ' + str(d['rate'])) - if 'min' in d: - commands.append('lacp min-links ' + str(d['min'])) - if 'max' in d: - commands.append('lacp max-bundle ' + str(d['max'])) - if 'mode' in d: - commands.append('lacp mode ' + d['mode']) - if 'suspend_individual' in d: - if d['suspend_individual'] is True: - commands.append('lacp suspend-individual') - else: - commands.append('no lacp suspend-individual') - if 'graceful' in d: - if d['graceful'] is True: - commands.append('lacp graceful-convergence') - else: - commands.append('no lacp graceful-convergence') - if 'vpc' in d: - if d['vpc'] is True: - commands.append('lacp vpc-convergence') - else: - commands.append('no lacp vpc-convergence') - return commands - - def set_commands(self, w, have): - commands = [] - obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name')) - if not obj_in_have: - commands = self.add_commands(w) - else: - diff = self.diff_of_dicts(w, obj_in_have) - commands = self.add_commands(diff) - return commands diff --git a/lib/ansible/module_utils/network/nxos/config/lag_interfaces/lag_interfaces.py b/lib/ansible/module_utils/network/nxos/config/lag_interfaces/lag_interfaces.py deleted file mode 100644 index ed000f5467..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/lag_interfaces/lag_interfaces.py +++ /dev/null @@ -1,273 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The junos_lag_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import to_list, remove_empties, dict_diff, search_obj_in_list -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import normalize_interface - - -class Lag_interfaces(ConfigBase): - """ - The nxos_lag_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lag_interfaces', - ] - - def __init__(self, module): - super(Lag_interfaces, self).__init__(module) - - def get_lag_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - lag_interfaces_facts = facts['ansible_network_resources'].get('lag_interfaces') - if not lag_interfaces_facts: - return [] - return lag_interfaces_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_lag_interfaces_facts = self.get_lag_interfaces_facts() - commands.extend(self.set_config(existing_lag_interfaces_facts)) - if commands: - if not self._module.check_mode: - resp = self._connection.edit_config(commands) - if 'response' in resp: - for item in resp['response']: - if item: - err_str = item - if err_str.lower().startswith('cannot add'): - self._module.fail_json(msg=err_str) - result['changed'] = True - result['commands'] = commands - - changed_lag_interfaces_facts = self.get_lag_interfaces_facts() - - result['before'] = existing_lag_interfaces_facts - if result['changed']: - result['after'] = changed_lag_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_lag_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - want = self._module.params.get('config') - if want: - for w in want: - w.update(remove_empties(w)) - if 'members' in w and w['members']: - for item in w['members']: - item.update({'member': normalize_interface(item['member'])}) - have = existing_lag_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - commands = list() - - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(w, have)) - if state == 'replaced': - commands.extend(self._state_replaced(w, have)) - return commands - - def _state_replaced(self, w, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - merged_commands = self.set_commands(w, have) - replaced_commands = self.del_intf_commands(w, have) - if merged_commands: - commands.extend(replaced_commands) - commands.extend(merged_commands) - return commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - for h in have: - obj_in_want = search_obj_in_list(h['name'], want, 'name') - if obj_in_want: - diff = self.diff_list_of_dicts(h['members'], obj_in_want['members']) - if not diff: - continue - commands.extend(self.del_all_commands(h)) - for w in want: - commands.extend(self.set_commands(w, have)) - return commands - - def _state_merged(self, w, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(w, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - for w in want: - obj_in_have = search_obj_in_list(w['name'], have, 'name') - commands.extend(self.del_all_commands(obj_in_have)) - else: - if not have: - return commands - for h in have: - commands.extend(self.del_all_commands(h)) - return commands - - def diff_list_of_dicts(self, want, have): - if not want: - want = [] - - if not have: - have = [] - - diff = [] - for w_item in want: - h_item = search_obj_in_list(w_item['member'], have, key='member') or {} - delta = dict_diff(h_item, w_item) - if delta: - if h_item: - if 'mode' in delta.keys() and delta['mode'] == 'on' and 'mode' not in h_item.keys(): - # mode = on will not be displayed in running-config - continue - if 'member' not in delta.keys(): - delta['member'] = w_item['member'] - diff.append(delta) - - return diff - - def intersect_list_of_dicts(self, w, h): - intersect = [] - wmem = [] - hmem = [] - for d in w: - wmem.append({'member': d['member']}) - for d in h: - hmem.append({'member': d['member']}) - set_w = set(tuple(sorted(d.items())) for d in wmem) - set_h = set(tuple(sorted(d.items())) for d in hmem) - intersection = set_w.intersection(set_h) - for element in intersection: - intersect.append(dict((x, y) for x, y in element)) - return intersect - - def add_commands(self, diff, name): - commands = [] - name = name.strip('port-channel') - for d in diff: - commands.append('interface' + ' ' + d['member']) - cmd = '' - group_cmd = 'channel-group {0}'.format(name) - if 'force' in d: - cmd = group_cmd + ' force ' + d['force'] - if 'mode' in d: - if cmd: - cmd = cmd + ' mode ' + d['mode'] - else: - cmd = group_cmd + ' mode ' + d['mode'] - if not cmd: - cmd = group_cmd - commands.append(cmd) - return commands - - def set_commands(self, w, have): - commands = [] - obj_in_have = search_obj_in_list(w['name'], have, 'name') - if not obj_in_have: - commands = self.add_commands(w['members'], w['name']) - else: - diff = self.diff_list_of_dicts(w['members'], obj_in_have['members']) - commands = self.add_commands(diff, w['name']) - return commands - - def del_all_commands(self, obj_in_have): - commands = [] - if not obj_in_have: - return commands - for m in obj_in_have['members']: - commands.append('interface' + ' ' + m['member']) - commands.append('no channel-group') - return commands - - def del_intf_commands(self, w, have): - commands = [] - obj_in_have = search_obj_in_list(w['name'], have, 'name') - if obj_in_have: - lst_to_del = self.intersect_list_of_dicts(w['members'], obj_in_have['members']) - if lst_to_del: - for item in lst_to_del: - commands.append('interface' + ' ' + item['member']) - commands.append('no channel-group') - return commands diff --git a/lib/ansible/module_utils/network/nxos/config/lldp_global/lldp_global.py b/lib/ansible/module_utils/network/nxos/config/lldp_global/lldp_global.py deleted file mode 100644 index 75f0d753ff..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/lldp_global/lldp_global.py +++ /dev/null @@ -1,240 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_lldp_global class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import remove_empties, dict_diff -from ansible.module_utils.network.nxos.facts.facts import Facts - - -class Lldp_global(ConfigBase): - """ - The nxos_lldp_global class - """ - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lldp_global', - ] - - def __init__(self, module): - super(Lldp_global, self).__init__(module) - - def get_lldp_global_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources) - lldp_global_facts = facts['ansible_network_resources'].get( - 'lldp_global') - if not lldp_global_facts: - return {} - return lldp_global_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_lldp_global_facts = self.get_lldp_global_facts() - commands.extend(self.set_config(existing_lldp_global_facts)) - if commands: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_lldp_global_facts = self.get_lldp_global_facts() - - result['before'] = dict(existing_lldp_global_facts) - if result['changed']: - result['after'] = dict(changed_lldp_global_facts) - result['warnings'] = warnings - return result - - def set_config(self, existing_lldp_global_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - want = self._module.params['config'] - have = existing_lldp_global_facts - resp = self.set_state(remove_empties(want), have) - return resp - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state == 'deleted': - commands = self._state_deleted(have) - elif state == 'merged': - commands = self._state_merged(want, have) - elif state == 'replaced': - commands = self._state_replaced(want, have) - return commands - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - merge_dict = dict_diff(have, want) - # merge_dict will contain new and unique values in want - delete_dict = self.find_delete_params(have, want) - self._module.params['state'] = 'deleted' - commands.extend(self._state_deleted(delete_dict)) # delete - self._module.params['state'] = 'merged' - commands.extend(self.set_commands(merge_dict)) # merge - self._module.params['state'] = 'replaced' - return commands - - def delete_nested_dict(self, have, want): - """ - Returns tlv_select nested dict that needs to be defaulted - """ - outer_dict = {} - for key, val in have.items(): - inner_dict = {} - if not isinstance(val, dict): - if key not in want.keys(): - inner_dict.update({key: val}) - return inner_dict - else: - if key in want.keys(): - outer_dict.update( - {key: self.delete_nested_dict(val, want[key])}) - else: - outer_dict.update({key: val}) - return outer_dict - - def find_delete_params(self, have, want): - """ - Returns parameters that are present in have and not in want, that need to be defaulted - """ - delete_dict = {} - for key, val in have.items(): - if key not in want.keys(): - delete_dict.update({key: val}) - else: - if key == 'tlv_select': - delete_dict.update({key: self.delete_nested_dict( - have['tlv_select'], want['tlv_select'])}) - return delete_dict - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - commands = [] - diff = dict_diff(have, want) - commands.extend(self.set_commands(diff)) - return commands - - def _state_deleted(self, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if have: - for key, val in have.items(): - if 'tlv_select' in key: - commands.extend(self.process_nested_dict(val)) - else: - if key == 'port_id': - key = 'portid-subtype' - commands.append('no lldp ' + key + ' ' + str(val)) - - return commands - - def set_commands(self, diff): - commands = [] - for key, val in diff.items(): - commands.extend(self.add_commands(key, val)) - return commands - - def add_commands(self, key, val): - command = [] - if 'port_id' in key: - command.append('lldp portid-subtype ' + str(val)) - elif 'tlv_select' in key: - command.extend(self.process_nested_dict(val)) - else: - if val: - command.append('lldp ' + key + ' ' + str(val)) - return command - - def process_nested_dict(self, val): - nested_commands = [] - for k, v in val.items(): - if isinstance(v, dict): - for k1, v1 in v.items(): - com1 = 'lldp tlv-select ' - com2 = '' - if 'system' in k: - com2 = 'system-' + k1 - elif 'management_address' in k: - com2 = 'management-address ' + k1 - elif 'port' in k: - com2 = 'port-' + k1 - - com1 += com2 - com1 = self.negate_command(com1, v1) - nested_commands.append(com1) - else: - com1 = 'lldp tlv-select ' - if 'power_management' in k: - com1 += 'power-management' - else: - com1 += k - - com1 = self.negate_command(com1, v) - nested_commands.append(com1) - return nested_commands - - def negate_command(self, command, val): - # for merged, replaced vals need to be checked to add 'no' - if self._module.params['state'] == 'merged': - if not val: - command = 'no ' + command - return command diff --git a/lib/ansible/module_utils/network/nxos/config/lldp_interfaces/lldp_interfaces.py b/lib/ansible/module_utils/network/nxos/config/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index 37e29ae5c6..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,304 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_lldp_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import to_list, remove_empties, dict_diff -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import flatten_dict, search_obj_in_list, get_interface_type, normalize_interface - - -class Lldp_interfaces(ConfigBase): - """ - The nxos_lldp_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lldp_interfaces', - ] - - def __init__(self, module): - super(Lldp_interfaces, self).__init__(module) - - def get_lldp_interfaces_facts(self, data=None): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources, data=data) - lldp_interfaces_facts = facts['ansible_network_resources'].get( - 'lldp_interfaces') - if not lldp_interfaces_facts: - return [] - return lldp_interfaces_facts - - def edit_config(self, commands): - """Wrapper method for `_connection.edit_config()` - This exists solely to allow the unit test framework to mock device connection calls. - """ - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - state = self._module.params['state'] - action_states = ['merged', 'replaced', 'deleted', 'overridden'] - - if state == 'gathered': - result['gathered'] = self.get_lldp_interfaces_facts() - elif state == 'rendered': - result['rendered'] = self.set_config({}) - elif state == 'parsed': - result['parsed'] = self.set_config({}) - else: - existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - commands.extend(self.set_config(existing_lldp_interfaces_facts)) - if commands and state in action_states: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['before'] = existing_lldp_interfaces_facts - result['commands'] = commands - result['commands'] = commands - - changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - - if result['changed']: - result['after'] = changed_lldp_interfaces_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_lldp_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params['config'] - want = [] - if config: - for w in config: - if get_interface_type(w['name']) not in ('management', - 'ethernet'): - self._module.fail_json( - msg='This module works with either management or ethernet') - w.update({'name': normalize_interface(w['name'])}) - want.append(remove_empties(w)) - have = existing_lldp_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - state = self._module.params['state'] - if state == 'overridden': - commands = self._state_overridden(want, have) - elif state == 'deleted': - commands = self._state_deleted(want, have) - elif state == 'rendered': - commands = self._state_rendered(want) - elif state == 'parsed': - want = self._module.params['running_config'] - commands = self._state_parsed(want) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(flatten_dict(w), have)) - elif state == 'replaced': - commands.extend(self._state_replaced( - flatten_dict(w), have)) - return commands - - def _state_parsed(self, want): - return self.get_lldp_interfaces_facts(want) - - def _state_rendered(self, want): - commands = [] - for w in want: - commands.extend(self.set_commands(w, {})) - return commands - - def _state_gathered(self, have): - """ The command generator when state is gathered - - :rtype: A list - :returns: the commands necessary to reproduce the current configuration - """ - commands = [] - want = {} - commands.append(self.set_commands(want, have)) - return commands - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - del_commands = [] - delete_dict = {} - obj_in_have = flatten_dict( - search_obj_in_list(want['name'], have, 'name')) - for k1 in obj_in_have.keys(): - if k1 not in want.keys(): - delete_dict.update({k1: obj_in_have[k1]}) - - if delete_dict: - delete_dict.update({'name': want['name']}) - del_commands = self.del_commands(delete_dict) - merged_commands = self.set_commands(want, have) - - if merged_commands: - cmds = set(del_commands).intersection(set(merged_commands)) - for cmd in cmds: - merged_commands.remove(cmd) - - commands.extend(del_commands) - commands.extend(merged_commands) - return commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - want_intfs = [w['name'] for w in want] - for h in have: - h = flatten_dict(h) - delete_dict = {} - if h['name'] in want_intfs: - for w in want: - if w['name'] == h['name']: - delete_keys = list(set(h) - set(flatten_dict(w))) - for k in delete_keys: - delete_dict.update({k: h[k]}) - delete_dict.update({'name': h['name']}) - break - else: - delete_dict.update(h) - commands.extend(self.del_commands(delete_dict)) - for w in want: - commands.extend(self.set_commands(flatten_dict(w), have)) - return commands - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - return self.set_commands(want, have) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - for w in want: - obj_in_have = flatten_dict( - search_obj_in_list(w['name'], have, 'name')) - commands.extend(self.del_commands(obj_in_have)) - else: - if not have: - return commands - for h in have: - commands.extend(self.del_commands(flatten_dict(h))) - return commands - - def set_commands(self, want, have): - commands = [] - obj_in_have = flatten_dict( - search_obj_in_list(want['name'], have, 'name')) - if not obj_in_have: - commands = self.add_commands(flatten_dict(want)) - else: - diff = dict_diff(obj_in_have, want) - if diff: - diff.update({'name': want['name']}) - commands = self.add_commands(diff) - return commands - - def add_commands(self, d): - commands = [] - if not d: - return commands - commands.append('interface ' + d['name']) - if 'transmit' in d: - if (d['transmit']): - commands.append('lldp transmit') - else: - commands.append('no lldp transmit') - if 'receive' in d: - if (d['receive']): - commands.append('lldp receive') - else: - commands.append('no lldp receive') - if 'management_address' in d: - commands.append('lldp tlv-set management-address ' + - d['management_address']) - if 'vlan' in d: - commands.append('lldp tlv-set vlan ' + str(d['vlan'])) - - return commands - - def del_commands(self, obj): - commands = [] - if not obj or len(obj.keys()) == 1: - return commands - commands.append('interface ' + obj['name']) - if 'transmit' in obj: - commands.append('lldp transmit') - if 'receive' in obj: - commands.append('lldp receive') - if 'management_address' in obj: - commands.append('no lldp tlv-set management-address ' + - obj['management_address']) - if 'vlan' in obj: - commands.append('no lldp tlv-set vlan ' + str(obj['vlan'])) - - return commands diff --git a/lib/ansible/module_utils/network/nxos/config/telemetry/telemetry.py b/lib/ansible/module_utils/network/nxos/config/telemetry/telemetry.py deleted file mode 100644 index dac91b232c..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/telemetry/telemetry.py +++ /dev/null @@ -1,503 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_telemetry class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.cmdref.telemetry.telemetry import TMS_GLOBAL, TMS_DESTGROUP, TMS_SENSORGROUP, TMS_SUBSCRIPTION -from ansible.module_utils.network.nxos.utils.telemetry.telemetry import normalize_data, remove_duplicate_context -from ansible.module_utils.network.nxos.utils.telemetry.telemetry import valiate_input, get_setval_path, massage_data -from ansible.module_utils.network.nxos.utils.telemetry.telemetry import get_module_params_subsection, remove_duplicate_commands -from ansible.module_utils.network.nxos.utils.utils import normalize_interface -from ansible.module_utils.network.nxos.nxos import NxosCmdRef - - -class Telemetry(ConfigBase): - """ - The nxos_telemetry class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'telemetry', - ] - - def __init__(self, module): - super(Telemetry, self).__init__(module) - - def get_telemetry_facts(self): - """ Get the 'facts' (the current configuration) - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - telemetry_facts = facts['ansible_network_resources'].get('telemetry') - if not telemetry_facts: - return {} - return telemetry_facts - - def edit_config(self, commands): - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - state = self._module.params['state'] - if 'overridden' in state: - self._module.fail_json(msg='State <overridden> is invalid for this module.') - # When state is 'deleted', the module_params should not contain data - # under the 'config' key - if 'deleted' in state and self._module.params.get('config'): - self._module.fail_json(msg='Remove config key from playbook when state is <deleted>') - - if self._module.params['config'] is None: - self._module.params['config'] = {} - # Normalize interface name. - int = self._module.params['config'].get('source_interface') - if int: - self._module.params['config']['source_interface'] = normalize_interface(int) - - existing_telemetry_facts = self.get_telemetry_facts() - commands.extend(self.set_config(existing_telemetry_facts)) - if commands: - if not self._module.check_mode: - self.edit_config(commands) - # TODO: edit_config is only available for network_cli. Once we - # add support for httpapi, we will need to switch to load_config - # or add support to httpapi for edit_config - # - # self._connection.load_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_telemetry_facts = self.get_telemetry_facts() - - result['before'] = existing_telemetry_facts - if result['changed']: - result['after'] = changed_telemetry_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_tms_global_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params['config'] - want = dict((k, v) for k, v in config.items() if v is not None) - have = existing_tms_global_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - - # The deleted case is very simple since we purge all telemetry config - # and does not require any processing using NxosCmdRef objects. - if state == 'deleted': - return self._state_deleted(want, have) - elif state == 'replaced': - if want == have: - return [] - return self._state_replaced(want, have) - - # Save off module params - ALL_MP = self._module.params['config'] - - cmd_ref = {} - cmd_ref['TMS_GLOBAL'] = {} - cmd_ref['TMS_DESTGROUP'] = {} - cmd_ref['TMS_SENSORGROUP'] = {} - cmd_ref['TMS_SUBSCRIPTION'] = {} - - # Build Telemetry Global NxosCmdRef Object - cmd_ref['TMS_GLOBAL']['ref'] = [] - self._module.params['config'] = get_module_params_subsection(ALL_MP, 'TMS_GLOBAL') - cmd_ref['TMS_GLOBAL']['ref'].append(NxosCmdRef(self._module, TMS_GLOBAL)) - ref = cmd_ref['TMS_GLOBAL']['ref'][0] - ref.set_context() - ref.get_existing() - ref.get_playvals() - device_cache = ref.cache_existing - - def build_cmdref_objects(td): - cmd_ref[td['type']]['ref'] = [] - saved_ids = [] - if want.get(td['name']): - for playvals in want[td['name']]: - valiate_input(playvals, td['name'], self._module) - if playvals['id'] in saved_ids: - continue - saved_ids.append(playvals['id']) - resource_key = td['cmd'].format(playvals['id']) - # Only build the NxosCmdRef object for the td['name'] module parameters. - self._module.params['config'] = get_module_params_subsection(ALL_MP, td['type'], playvals['id']) - cmd_ref[td['type']]['ref'].append(NxosCmdRef(self._module, td['obj'])) - ref = cmd_ref[td['type']]['ref'][-1] - ref.set_context([resource_key]) - if td['type'] == 'TMS_SENSORGROUP' and get_setval_path(self._module): - # Sensor group path setting can contain optional values. - # Call get_setval_path helper function to process any - # optional setval keys. - ref._ref['path']['setval'] = get_setval_path(self._module) - ref.get_existing(device_cache) - ref.get_playvals() - if td['type'] == 'TMS_DESTGROUP': - normalize_data(ref) - - # Build Telemetry Destination Group NxosCmdRef Objects - td = {'name': 'destination_groups', 'type': 'TMS_DESTGROUP', - 'obj': TMS_DESTGROUP, 'cmd': 'destination-group {0}'} - build_cmdref_objects(td) - - # Build Telemetry Sensor Group NxosCmdRef Objects - td = {'name': 'sensor_groups', 'type': 'TMS_SENSORGROUP', - 'obj': TMS_SENSORGROUP, 'cmd': 'sensor-group {0}'} - build_cmdref_objects(td) - - # Build Telemetry Subscription NxosCmdRef Objects - td = {'name': 'subscriptions', 'type': 'TMS_SUBSCRIPTION', - 'obj': TMS_SUBSCRIPTION, 'cmd': 'subscription {0}'} - build_cmdref_objects(td) - - if state == 'merged': - if want == have: - return [] - commands = self._state_merged(cmd_ref) - return commands - - @staticmethod - def _state_replaced(want, have): - """ The command generator when state is replaced - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - massaged_have = massage_data(have) - massaged_want = massage_data(want) - - ref = {} - ref['tms_global'] = NxosCmdRef([], TMS_GLOBAL, ref_only=True) - ref['tms_destgroup'] = NxosCmdRef([], TMS_DESTGROUP, ref_only=True) - ref['tms_sensorgroup'] = NxosCmdRef([], TMS_SENSORGROUP, ref_only=True) - ref['tms_subscription'] = NxosCmdRef([], TMS_SUBSCRIPTION, ref_only=True) - - # Order matters for state replaced. - # First remove all subscriptions, followed by sensor-groups and destination-groups. - # Second add all destination-groups, followed by sensor-groups and subscriptions - add = {'TMS_GLOBAL': [], 'TMS_DESTGROUP': [], 'TMS_SENSORGROUP': [], 'TMS_SUBSCRIPTION': []} - delete = {'TMS_DESTGROUP': [], 'TMS_SENSORGROUP': [], 'TMS_SUBSCRIPTION': []} - - # Process Telemetry Global Want and Have Values - # Possible states: - # - want and have are (set) (equal: no action, not equal: replace with want) - # - want (set) have (not set) (add want) - # - want (not set) have (set) (delete have) - # - want (not set) have (not set) (no action) - # global_ctx = ref['tms_global']._ref['_template']['context'] - # property_ctx = ref['tms_global']._ref['certificate'].get('context') - # setval = ref['tms_global']._ref['certificate']['setval'] - # - all_global_properties = ['certificate', 'compression', 'source_interface', 'vrf'] - dest_profile_properties = ['compression', 'source_interface', 'vrf'] - dest_profile_remote_commands = [] - for property in all_global_properties: - cmd = None - global_ctx = ref['tms_global']._ref['_template']['context'] - property_ctx = ref['tms_global']._ref[property].get('context') - setval = ref['tms_global']._ref[property]['setval'] - kind = ref['tms_global']._ref[property]['kind'] - if want.get(property) is not None: - if have.get(property) is not None: - if want.get(property) != have.get(property): - if kind == 'dict': - cmd = [setval.format(**want.get(property))] - else: - cmd = [setval.format(want.get(property))] - elif have.get(property) is None: - if kind == 'dict': - cmd = [setval.format(**want.get(property))] - else: - cmd = [setval.format(want.get(property))] - elif want.get(property) is None: - if have.get(property) is not None: - if kind == 'dict': - cmd = ['no ' + setval.format(**have.get(property))] - else: - cmd = ['no ' + setval.format(have.get(property))] - if property in dest_profile_properties: - dest_profile_remote_commands.extend(cmd) - - if cmd is not None: - ctx = global_ctx - if property_ctx is not None: - ctx.extend(property_ctx) - add['TMS_GLOBAL'].extend(ctx) - add['TMS_GLOBAL'].extend(cmd) - - add['TMS_GLOBAL'] = remove_duplicate_commands(add['TMS_GLOBAL']) - # If all destination profile commands are being removed then just - # remove the config context instead. - if len(dest_profile_remote_commands) == 3: - for item in dest_profile_remote_commands: - add['TMS_GLOBAL'].remove(item) - add['TMS_GLOBAL'].remove('destination-profile') - add['TMS_GLOBAL'].extend(['no destination-profile']) - - # Process Telemetry destination_group, sensor_group and subscription Want and Have Values - # Possible states: - # - want (not set) have (set) (delete have) - # - want and have are (set) (equal: no action, not equal: replace with want) - # - want (set) have (not set) (add want) - # - want (not set) have (not set) (no action) - tms_resources = ['TMS_DESTGROUP', 'TMS_SENSORGROUP', 'TMS_SUBSCRIPTION'] - for resource in tms_resources: - if resource == 'TMS_DESTGROUP': - name = 'destination-group' - cmd_property = 'destination' - global_ctx = ref['tms_destgroup']._ref['_template']['context'] - setval = ref['tms_destgroup']._ref['destination']['setval'] - want_resources = massaged_want.get('destination_groups') - have_resources = massaged_have.get('destination_groups') - if resource == 'TMS_SENSORGROUP': - name = 'sensor-group' - global_ctx = ref['tms_sensorgroup']._ref['_template']['context'] - setval = {} - setval['data_source'] = ref['tms_sensorgroup']._ref['data_source']['setval'] - setval['path'] = ref['tms_sensorgroup']._ref['path']['setval'] - want_resources = massaged_want.get('sensor_groups') - have_resources = massaged_have.get('sensor_groups') - if resource == 'TMS_SUBSCRIPTION': - name = 'subscription' - global_ctx = ref['tms_subscription']._ref['_template']['context'] - setval = {} - setval['destination_group'] = ref['tms_subscription']._ref['destination_group']['setval'] - setval['sensor_group'] = ref['tms_subscription']._ref['sensor_group']['setval'] - want_resources = massaged_want.get('subscriptions') - have_resources = massaged_have.get('subscriptions') - - if not want_resources and have_resources: - # want not and have not set so delete have - for key in have_resources.keys(): - remove_context = ['{0} {1} {2}'.format('no', name, key)] - delete[resource].extend(global_ctx) - if remove_context[0] not in delete[resource]: - delete[resource].extend(remove_context) - else: - # want and have are set. - # process wants: - for want_key in want_resources.keys(): - if want_key not in have_resources.keys(): - # Want resource key not in have resource key so add it - property_ctx = ['{0} {1}'.format(name, want_key)] - for item in want_resources[want_key]: - if resource == 'TMS_DESTGROUP': - cmd = [setval.format(**item[cmd_property])] - add[resource].extend(global_ctx) - if property_ctx[0] not in add[resource]: - add[resource].extend(property_ctx) - add[resource].extend(cmd) - if resource == 'TMS_SENSORGROUP': - cmd = {} - if item.get('data_source'): - cmd['data_source'] = [setval['data_source'].format(item['data_source'])] - if item.get('path'): - setval['path'] = get_setval_path(item.get('path')) - cmd['path'] = [setval['path'].format(**item['path'])] - add[resource].extend(global_ctx) - if property_ctx[0] not in add[resource]: - add[resource].extend(property_ctx) - if cmd.get('data_source'): - add[resource].extend(cmd['data_source']) - if cmd.get('path'): - add[resource].extend(cmd['path']) - if resource == 'TMS_SUBSCRIPTION': - cmd = {} - if item.get('destination_group'): - cmd['destination_group'] = [setval['destination_group'].format(item['destination_group'])] - if item.get('sensor_group'): - cmd['sensor_group'] = [setval['sensor_group'].format(**item['sensor_group'])] - add[resource].extend(global_ctx) - if property_ctx[0] not in add[resource]: - add[resource].extend(property_ctx) - if cmd.get('destination_group'): - add[resource].extend(cmd['destination_group']) - if cmd.get('sensor_group'): - add[resource].extend(cmd['sensor_group']) - - elif want_key in have_resources.keys(): - # Want resource key exists in have resource keys but we need to - # inspect the individual items under the resource key - # for differences - for item in want_resources[want_key]: - if item not in have_resources[want_key]: - if item is None: - continue - # item wanted but does not exist so add it - property_ctx = ['{0} {1}'.format(name, want_key)] - if resource == 'TMS_DESTGROUP': - cmd = [setval.format(**item[cmd_property])] - add[resource].extend(global_ctx) - if property_ctx[0] not in add[resource]: - add[resource].extend(property_ctx) - add[resource].extend(cmd) - if resource == 'TMS_SENSORGROUP': - cmd = {} - if item.get('data_source'): - cmd['data_source'] = [setval['data_source'].format(item['data_source'])] - if item.get('path'): - setval['path'] = get_setval_path(item.get('path')) - cmd['path'] = [setval['path'].format(**item['path'])] - add[resource].extend(global_ctx) - if property_ctx[0] not in add[resource]: - add[resource].extend(property_ctx) - if cmd.get('data_source'): - add[resource].extend(cmd['data_source']) - if cmd.get('path'): - add[resource].extend(cmd['path']) - if resource == 'TMS_SUBSCRIPTION': - cmd = {} - if item.get('destination_group'): - cmd['destination_group'] = [setval['destination_group'].format(item['destination_group'])] - if item.get('sensor_group'): - cmd['sensor_group'] = [setval['sensor_group'].format(**item['sensor_group'])] - add[resource].extend(global_ctx) - if property_ctx[0] not in add[resource]: - add[resource].extend(property_ctx) - if cmd.get('destination_group'): - add[resource].extend(cmd['destination_group']) - if cmd.get('sensor_group'): - add[resource].extend(cmd['sensor_group']) - - # process haves: - for have_key in have_resources.keys(): - if have_key not in want_resources.keys(): - # Want resource key is not in have resource keys so remove it - cmd = ['no ' + '{0} {1}'.format(name, have_key)] - delete[resource].extend(global_ctx) - delete[resource].extend(cmd) - elif have_key in want_resources.keys(): - # Have resource key exists in want resource keys but we need to - # inspect the individual items under the resource key - # for differences - for item in have_resources[have_key]: - if item not in want_resources[have_key]: - if item is None: - continue - # have item not wanted so remove it - property_ctx = ['{0} {1}'.format(name, have_key)] - if resource == 'TMS_DESTGROUP': - cmd = ['no ' + setval.format(**item[cmd_property])] - delete[resource].extend(global_ctx) - if property_ctx[0] not in delete[resource]: - delete[resource].extend(property_ctx) - delete[resource].extend(cmd) - if resource == 'TMS_SENSORGROUP': - cmd = {} - if item.get('data_source'): - cmd['data_source'] = ['no ' + setval['data_source'].format(item['data_source'])] - if item.get('path'): - setval['path'] = get_setval_path(item.get('path')) - cmd['path'] = ['no ' + setval['path'].format(**item['path'])] - delete[resource].extend(global_ctx) - if property_ctx[0] not in delete[resource]: - delete[resource].extend(property_ctx) - if cmd.get('data_source'): - delete[resource].extend(cmd['data_source']) - if cmd.get('path'): - delete[resource].extend(cmd['path']) - if resource == 'TMS_SUBSCRIPTION': - cmd = {} - if item.get('destination_group'): - cmd['destination_group'] = ['no ' + setval['destination_group'].format(item['destination_group'])] - if item.get('sensor_group'): - cmd['sensor_group'] = ['no ' + setval['sensor_group'].format(**item['sensor_group'])] - delete[resource].extend(global_ctx) - if property_ctx[0] not in delete[resource]: - delete[resource].extend(property_ctx) - if cmd.get('destination_group'): - delete[resource].extend(cmd['destination_group']) - if cmd.get('sensor_group'): - delete[resource].extend(cmd['sensor_group']) - - add[resource] = remove_duplicate_context(add[resource]) - delete[resource] = remove_duplicate_context(delete[resource]) - - commands.extend(delete['TMS_SUBSCRIPTION']) - commands.extend(delete['TMS_SENSORGROUP']) - commands.extend(delete['TMS_DESTGROUP']) - commands.extend(add['TMS_DESTGROUP']) - commands.extend(add['TMS_SENSORGROUP']) - commands.extend(add['TMS_SUBSCRIPTION']) - commands.extend(add['TMS_GLOBAL']) - commands = remove_duplicate_context(commands) - - return commands - - @staticmethod - def _state_merged(cmd_ref): - """ The command generator when state is merged - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - commands = cmd_ref['TMS_GLOBAL']['ref'][0].get_proposed() - - if cmd_ref['TMS_DESTGROUP'].get('ref'): - for cr in cmd_ref['TMS_DESTGROUP']['ref']: - commands.extend(cr.get_proposed()) - - if cmd_ref['TMS_SENSORGROUP'].get('ref'): - for cr in cmd_ref['TMS_SENSORGROUP']['ref']: - commands.extend(cr.get_proposed()) - - if cmd_ref['TMS_SUBSCRIPTION'].get('ref'): - for cr in cmd_ref['TMS_SUBSCRIPTION']['ref']: - commands.extend(cr.get_proposed()) - - return remove_duplicate_context(commands) - - @staticmethod - def _state_deleted(want, have): - """ The command generator when state is deleted - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want != have: - commands = ['no telemetry'] - return commands diff --git a/lib/ansible/module_utils/network/nxos/config/vlans/vlans.py b/lib/ansible/module_utils/network/nxos/config/vlans/vlans.py deleted file mode 100644 index 16a8cfa856..0000000000 --- a/lib/ansible/module_utils/network/nxos/config/vlans/vlans.py +++ /dev/null @@ -1,290 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos_vlans class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.common.utils import dict_diff, to_list, remove_empties -from ansible.module_utils.network.nxos.facts.facts import Facts -from ansible.module_utils.network.nxos.utils.utils import search_obj_in_list - - -class Vlans(ConfigBase): - """ - The nxos_vlans class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'vlans', - ] - - def __init__(self, module): - super(Vlans, self).__init__(module) - - def get_vlans_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) - vlans_facts = facts['ansible_network_resources'].get('vlans') - if not vlans_facts: - return [] - - # Remove vlan 1 from facts list - vlans_facts = [i for i in vlans_facts if (int(i['vlan_id'])) != 1] - return vlans_facts - - def edit_config(self, commands): - """Wrapper method for `_connection.edit_config()` - This exists solely to allow the unit test framework to mock device connection calls. - """ - return self._connection.edit_config(commands) - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - commands = list() - warnings = list() - - existing_vlans_facts = self.get_vlans_facts() - commands.extend(self.set_config(existing_vlans_facts)) - if commands: - if not self._module.check_mode: - self.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_vlans_facts = self.get_vlans_facts() - - result['before'] = existing_vlans_facts - if result['changed']: - result['after'] = changed_vlans_facts - - result['warnings'] = warnings - return result - - def set_config(self, existing_vlans_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - config = self._module.params.get('config') - want = [] - if config: - for w in config: - if int(w['vlan_id']) == 1: - self._module.fail_json(msg="Vlan 1 is not allowed to be managed by this module") - want.append(remove_empties(w)) - have = existing_vlans_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - state = self._module.params['state'] - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='config is required for state {0}'.format(state)) - - commands = list() - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - else: - for w in want: - if state == 'merged': - commands.extend(self._state_merged(w, have)) - elif state == 'replaced': - commands.extend(self._state_replaced(w, have)) - return commands - - def remove_default_states(self, obj): - """Removes non-empty but default states from the obj. - """ - default_states = { - 'enabled': True, - 'state': 'active', - 'mode': 'ce', - } - for k in default_states.keys(): - if obj[k] == default_states[k]: - obj.pop(k, None) - return obj - - def _state_replaced(self, want, have): - """ The command generator when state is replaced. - Scope is limited to vlan objects defined in the playbook. - :rtype: A list - :returns: The minimum command set required to migrate the current - configuration to the desired configuration. - """ - obj_in_have = search_obj_in_list(want['vlan_id'], have, 'vlan_id') - if obj_in_have: - # ignore states that are already reset, then diff what's left - obj_in_have = self.remove_default_states(obj_in_have) - diff = dict_diff(want, obj_in_have) - # Remove merge items from diff; what's left will be used to - # remove states not specified in the playbook - for k in dict(set(want.items()) - set(obj_in_have.items())).keys(): - diff.pop(k, None) - else: - diff = want - - # merged_cmds: 'want' cmds to update 'have' states that don't match - # replaced_cmds: remaining 'have' cmds that need to be reset to default - merged_cmds = self.set_commands(want, have) - replaced_cmds = [] - if obj_in_have: - # Remaining diff items are used to reset states to default - replaced_cmds = self.del_attribs(diff) - cmds = [] - if replaced_cmds or merged_cmds: - cmds += ['vlan %s' % str(want['vlan_id'])] - cmds += merged_cmds + replaced_cmds - return cmds - - def _state_overridden(self, want, have): - """ The command generator when state is overridden. - Scope includes all vlan objects on the device. - :rtype: A list - :returns: the minimum command set required to migrate the current - configuration to the desired configuration. - """ - # overridden behavior is the same as replaced except for scope. - cmds = [] - existing_vlans = [] - for h in have: - existing_vlans.append(h['vlan_id']) - obj_in_want = search_obj_in_list(h['vlan_id'], want, 'vlan_id') - if obj_in_want: - if h != obj_in_want: - replaced_cmds = self._state_replaced(obj_in_want, [h]) - if replaced_cmds: - cmds.extend(replaced_cmds) - else: - cmds.append('no vlan %s' % h['vlan_id']) - - # Add wanted vlans that don't exist on the device yet - for w in want: - if w['vlan_id'] not in existing_vlans: - new_vlan = ['vlan %s' % w['vlan_id']] - cmds.extend(new_vlan + self.add_commands(w)) - return cmds - - def _state_merged(self, w, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - cmds = self.set_commands(w, have) - if cmds: - cmds.insert(0, 'vlan %s' % str(w['vlan_id'])) - return(cmds) - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - for w in want: - obj_in_have = search_obj_in_list(w['vlan_id'], have, 'vlan_id') - if obj_in_have: - commands.append('no vlan ' + str(obj_in_have['vlan_id'])) - else: - if not have: - return commands - for h in have: - commands.append('no vlan ' + str(h['vlan_id'])) - return commands - - def del_attribs(self, obj): - """Returns a list of commands to reset states to default - """ - commands = [] - if not obj: - return commands - default_cmds = { - 'name': 'no name', - 'state': 'no state', - 'enabled': 'no shutdown', - 'mode': 'mode ce', - 'mapped_vni': 'no vn-segment', - } - for k in obj: - commands.append(default_cmds[k]) - return commands - - def diff_of_dicts(self, w, obj): - diff = set(w.items()) - set(obj.items()) - diff = dict(diff) - if diff and w['vlan_id'] == obj['vlan_id']: - diff.update({'vlan_id': w['vlan_id']}) - return diff - - def add_commands(self, d): - commands = [] - if not d: - return commands - if 'name' in d: - commands.append('name ' + d['name']) - if 'state' in d: - commands.append('state ' + d['state']) - if 'enabled' in d: - if d['enabled'] is True: - commands.append('no shutdown') - else: - commands.append('shutdown') - if 'mode' in d: - commands.append('mode ' + d['mode']) - if 'mapped_vni' in d: - commands.append('vn-segment %s' % d['mapped_vni']) - - return commands - - def set_commands(self, w, have): - commands = [] - obj_in_have = search_obj_in_list(w['vlan_id'], have, 'vlan_id') - if not obj_in_have: - commands = self.add_commands(w) - else: - diff = self.diff_of_dicts(w, obj_in_have) - commands = self.add_commands(diff) - return commands diff --git a/lib/ansible/module_utils/network/nxos/facts/acl_interfaces/acl_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/acl_interfaces/acl_interfaces.py deleted file mode 100644 index 87d522df67..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/acl_interfaces/acl_interfaces.py +++ /dev/null @@ -1,119 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos acl_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.acl_interfaces.acl_interfaces import Acl_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import normalize_interface - - -class Acl_interfacesFacts(object): - """ The nxos acl_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Acl_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def get_device_data(self, connection): - return connection.get('show running-config | section interface') - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for acl_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = self.get_device_data(connection) - data = data.split('interface') - - resources = [] - for i in range(len(data)): - intf = data[i].split('\n') - for l in range(1, len(intf)): - if not re.search('ip(v6)?( port)? (access-group|traffic-filter)', intf[l]): - intf[l] = '' - intf = list(filter(None, intf)) - resources.append(intf) - - objs = [] - for resource in resources: - if resource: - obj = self.render_config(self.generated_spec, resource) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('acl_interfaces', None) - facts = {} - if objs: - params = utils.validate_config( - self.argument_spec, {'config': objs}) - params = utils.remove_empties(params) - facts['acl_interfaces'] = params['config'] - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - name = conf[0].strip() - config['name'] = normalize_interface(name) - config['access_groups'] = [] - v4 = {'afi': 'ipv4', 'acls': []} - v6 = {'afi': 'ipv6', 'acls': []} - for c in conf[1:]: - if c: - acl4 = re.search(r'ip( port)? access-group (\w*) (\w*)', c) - acl6 = re.search(r'ipv6( port)? traffic-filter (\w*) (\w*)', c) - if acl4: - acl = {'name': acl4.group(2).strip( - ), 'direction': acl4.group(3).strip()} - if acl4.group(1): - acl.update({'port': True}) - v4['acls'].append(acl) - elif acl6: - acl = {'name': acl6.group(2), 'direction': acl6.group(3)} - if acl6.group(1): - acl.update({'port': True}) - v6['acls'].append(acl) - - if len(v4['acls']) > 0: - config['access_groups'].append(v4) - if len(v6['acls']) > 0: - config['access_groups'].append(v6) - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/acls/acls.py b/lib/ansible/module_utils/network/nxos/facts/acls/acls.py deleted file mode 100644 index e9b97b2456..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/acls/acls.py +++ /dev/null @@ -1,236 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos acls fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.acls.acls import AclsArgs - - -class AclsFacts(object): - """ The nxos acls fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = AclsArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def get_device_data(self, connection): - return connection.get( - "show running-config | section 'ip(v6)* access-list'") - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for acls - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = self.get_device_data(connection) - data = re.split('\nip', data) - v6 = [] - v4 = [] - - for i in range(len(data)): - if str(data[i]): - if 'v6' in str(data[i]).split()[0]: - v6.append(data[i]) - else: - v4.append(data[i]) - - resources = [] - resources.append(v6) - resources.append(v4) - objs = [] - for resource in resources: - if resource: - obj = self.render_config(self.generated_spec, resource) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('acls', None) - facts = {} - if objs: - params = utils.validate_config(self.argument_spec, - {'config': objs}) - params = utils.remove_empties(params) - facts['acls'] = params['config'] - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def get_endpoint(self, ace, pro): - ret_dict = {} - option = ace.split()[0] - if option == 'any': - ret_dict.update({'any': True}) - else: - # it could be a.b.c.d or a.b.c.d/x or a.b.c.d/32 - if '/' in option: # or 'host' in option: - ip = re.search(r'(.*)/(\d+)', option) - if int(ip.group(2)) < 32 or 32 < int(ip.group(2)) < 128: - ret_dict.update({'prefix': option}) - else: - ret_dict.update({'host': ip.group(1)}) - else: - ret_dict.update({'address': option}) - wb = ace.split()[1] - ret_dict.update({'wildcard_bits': wb}) - ace = re.sub('{0}'.format(wb), '', ace, 1) - ace = re.sub(option, '', ace, 1) - if pro in ['tcp', 'udp']: - keywords = ['eq', 'lt', 'gt', 'neq', 'range'] - if len(ace.split()) and ace.split()[0] in keywords: - port_protocol = {} - port_pro = re.search(r'(eq|lt|gt|neq) (\w*)', ace) - if port_pro: - port_protocol.update( - {port_pro.group(1): port_pro.group(2)}) - ace = re.sub(port_pro.group(1), '', ace, 1) - ace = re.sub(port_pro.group(2), '', ace, 1) - else: - limit = re.search(r'(range) (\w*) (\w*)', ace) - if limit: - port_protocol.update({ - 'range': { - 'start': limit.group(2), - 'end': limit.group(3) - } - }) - ace = re.sub(limit.group(2), '', ace, 1) - ace = re.sub(limit.group(3), '', ace, 1) - if port_protocol: - ret_dict.update({'port_protocol': port_protocol}) - return ace, ret_dict - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - protocol_options = { - 'tcp': ['fin', 'established', 'psh', 'rst', 'syn', 'urg', 'ack'], - 'icmp': [ - 'administratively_prohibited', 'alternate_address', - 'conversion_error', 'dod_host_prohibited', - 'dod_net_prohibited', 'echo', 'echo_reply', - 'general_parameter_problem', 'host_isolated', - 'host_precedence_unreachable', 'host_redirect', - 'host_tos_redirect', 'host_tos_unreachable', 'host_unknown', - 'host_unreachable', 'information_reply', 'information_request', - 'mask_reply', 'mask_request', 'mobile_redirect', - 'net_redirect', 'net_tos_redirect', 'net_tos_unreachable', - 'net_unreachable', 'network_unknown', 'no_room_for_option', - 'option_missing', 'packet_too_big', 'parameter_problem', - 'port_unreachable', 'precedence_unreachable', - 'protocol_unreachable', 'reassembly_timeout', 'redirect', - 'router_advertisement', 'router_solicitation', 'source_quench', - 'source_route_failed', 'time_exceeded', 'timestamp_reply', - 'timestamp_request', 'traceroute', 'ttl_exceeded', - 'unreachable' - ], - 'igmp': ['dvmrp', 'host_query', 'host_report'], - } - if conf: - if 'v6' in conf[0].split()[0]: - config['afi'] = 'ipv6' - else: - config['afi'] = 'ipv4' - config['acls'] = [] - for acl in conf: - acls = {} - if 'match-local-traffic' in acl: - config['match_local_traffic'] = True - continue - acl = acl.split('\n') - acl = [a.strip() for a in acl] - acl = list(filter(None, acl)) - acls['name'] = re.match(r'(ip)?(v6)?\s?access-list (.*)', - acl[0]).group(3) - acls['aces'] = [] - for ace in list(filter(None, acl[1:])): - if re.search(r'ip(.*)access-list.*', ace): - break - entry = {} - ace = ace.strip() - seq = re.match(r'(\d*)', ace).group(0) - entry.update({'sequence': seq}) - ace = re.sub(seq, '', ace, 1) - grant = ace.split()[0] - rem = '' - if grant != 'remark': - entry.update({'grant': grant}) - else: - rem = re.match('.*remark (.*)', ace).group(1) - entry.update({'remark': rem}) - - if not rem: - ace = re.sub(grant, '', ace, 1) - pro = ace.split()[0] - entry.update({'protocol': pro}) - ace = re.sub(pro, '', ace, 1) - ace, source = self.get_endpoint(ace, pro) - entry.update({'source': source}) - ace, dest = self.get_endpoint(ace, pro) - entry.update({'destination': dest}) - - dscp = re.search(r'dscp (\w*)', ace) - if dscp: - entry.update({'dscp': dscp.group(1)}) - - frag = re.search(r'fragments', ace) - if frag: - entry.update({'fragments': True}) - - prec = re.search(r'precedence (\w*)', ace) - if prec: - entry.update({'precedence': prec.group(1)}) - - log = re.search('log', ace) - if log: - entry.update({'log': True}) - - if pro == 'tcp' or pro == 'icmp' or pro == 'igmp': - pro_options = {} - options = {} - for option in protocol_options[pro]: - option = re.sub('_', '-', option) - if option in ace: - option = re.sub('-', '_', option) - options.update({option: True}) - if options: - pro_options.update({pro: options}) - if pro_options: - entry.update({'protocol_options': pro_options}) - acls['aces'].append(entry) - config['acls'].append(acls) - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/bfd_interfaces/bfd_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/bfd_interfaces/bfd_interfaces.py deleted file mode 100644 index ad1c63a859..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/bfd_interfaces/bfd_interfaces.py +++ /dev/null @@ -1,97 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -""" -The nxos bfd_interfaces fact class -Populate the facts tree based on the current device configuration. -""" -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.bfd_interfaces.bfd_interfaces import Bfd_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type - - -class Bfd_interfacesFacts(object): - """ The nxos_bfd_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Bfd_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for bfd_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - - if not data: - data = connection.get("show running-config | section '^interface|^feature bfd'") - - # Some of the bfd attributes - if 'feature bfd' in data.split('\n'): - resources = data.split('interface ') - resources.pop(0) - else: - resources = [] - for resource in resources: - if resource: - obj = self.render_config(self.generated_spec, resource) - if obj and len(obj.keys()) > 1: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('bfd_interfaces', None) - facts = {} - if objs: - facts['bfd_interfaces'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['bfd_interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - match = re.search(r'^(\S+)', conf) - intf = match.group(1) - if get_interface_type(intf) == 'unknown': - return {} - config['name'] = intf - # 'bfd'/'bfd echo' do not nvgen when enabled thus set to 'enable' when None. - # 'bfd' is not supported on some platforms - config['bfd'] = utils.parse_conf_cmd_arg(conf, 'bfd', 'enable', 'disable') or 'enable' - config['echo'] = utils.parse_conf_cmd_arg(conf, 'bfd echo', 'enable', 'disable') or 'enable' - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/facts.py b/lib/ansible/module_utils/network/nxos/facts/facts.py deleted file mode 100644 index 301652b385..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/facts.py +++ /dev/null @@ -1,82 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The facts class for nxos -this file validates each subset of facts and selectively -calls the appropriate facts gathering function -""" - -from ansible.module_utils.network.common.facts.facts import FactsBase -from ansible.module_utils.network.nxos.facts.legacy.base import Default, Legacy, Hardware, Config, Interfaces, Features -from ansible.module_utils.network.nxos.facts.bfd_interfaces.bfd_interfaces import Bfd_interfacesFacts -from ansible.module_utils.network.nxos.facts.hsrp_interfaces.hsrp_interfaces import Hsrp_interfacesFacts -from ansible.module_utils.network.nxos.facts.interfaces.interfaces import InterfacesFacts -from ansible.module_utils.network.nxos.facts.l2_interfaces.l2_interfaces import L2_interfacesFacts -from ansible.module_utils.network.nxos.facts.lacp.lacp import LacpFacts -from ansible.module_utils.network.nxos.facts.l3_interfaces.l3_interfaces import L3_interfacesFacts -from ansible.module_utils.network.nxos.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts -from ansible.module_utils.network.nxos.facts.telemetry.telemetry import TelemetryFacts -from ansible.module_utils.network.nxos.facts.vlans.vlans import VlansFacts -from ansible.module_utils.network.nxos.facts.lacp_interfaces.lacp_interfaces import Lacp_interfacesFacts -from ansible.module_utils.network.nxos.facts.lldp_global.lldp_global import Lldp_globalFacts -from ansible.module_utils.network.nxos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts -from ansible.module_utils.network.nxos.facts.acl_interfaces.acl_interfaces import Acl_interfacesFacts -from ansible.module_utils.network.nxos.facts.acls.acls import AclsFacts - - -FACT_LEGACY_SUBSETS = dict( - default=Default, - legacy=Legacy, - hardware=Hardware, - interfaces=Interfaces, - config=Config, - features=Features, -) -FACT_RESOURCE_SUBSETS = dict( - bfd_interfaces=Bfd_interfacesFacts, - hsrp_interfaces=Hsrp_interfacesFacts, - lag_interfaces=Lag_interfacesFacts, - lldp_global=Lldp_globalFacts, - telemetry=TelemetryFacts, - vlans=VlansFacts, - lacp=LacpFacts, - lacp_interfaces=Lacp_interfacesFacts, - interfaces=InterfacesFacts, - l3_interfaces=L3_interfacesFacts, - l2_interfaces=L2_interfacesFacts, - lldp_interfaces=Lldp_interfacesFacts, - acl_interfaces=Acl_interfacesFacts, - acls=AclsFacts, -) - - -class Facts(FactsBase): - """ The fact class for nxos - """ - - VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) - VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) - - def __init__(self, module): - super(Facts, self).__init__(module) - - def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None): - """ Collect the facts for nxos - :param legacy_facts_type: List of legacy facts types - :param resource_facts_type: List of resource fact types - :param data: previously collected conf - :rtype: dict - :return: the facts gathered - """ - if self.VALID_RESOURCE_SUBSETS: - self.get_network_resources_facts( - FACT_RESOURCE_SUBSETS, resource_facts_type, data) - - if self.VALID_LEGACY_GATHER_SUBSETS: - self.get_network_legacy_facts( - FACT_LEGACY_SUBSETS, legacy_facts_type) - - return self.ansible_facts, self._warnings diff --git a/lib/ansible/module_utils/network/nxos/facts/hsrp_interfaces/hsrp_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/hsrp_interfaces/hsrp_interfaces.py deleted file mode 100644 index b63eca4afd..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/hsrp_interfaces/hsrp_interfaces.py +++ /dev/null @@ -1,89 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -""" -The nxos hsrp_interfaces fact class -Populate the facts tree based on the current device configuration. -""" -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.hsrp_interfaces.hsrp_interfaces import Hsrp_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type - - -class Hsrp_interfacesFacts(object): - """ The nxos hsrp_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Hsrp_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for hsrp_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - - if not data: - data = connection.get('show running-config | section ^interface') - - resources = data.split('interface ') - for resource in resources: - if resource: - obj = self.render_config(self.generated_spec, resource) - if obj and len(obj.keys()) > 1: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('hsrp_interfaces', None) - facts = {} - if objs: - facts['hsrp_interfaces'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['hsrp_interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - match = re.search(r'^(\S+)', conf) - intf = match.group(1) - if get_interface_type(intf) == 'unknown': - return {} - config['name'] = intf - config['bfd'] = utils.parse_conf_cmd_arg(conf, 'hsrp bfd', 'enable', 'disable') - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/interfaces/interfaces.py b/lib/ansible/module_utils/network/nxos/facts/interfaces/interfaces.py deleted file mode 100644 index 59a49376cc..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/interfaces/interfaces.py +++ /dev/null @@ -1,151 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python -""" -The nxos interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.interfaces.interfaces import InterfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type -from ansible.module_utils.network.nxos.nxos import default_intf_enabled - - -class InterfacesFacts(object): - """ The nxos interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = InterfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for interfaces - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - if not data: - data = connection.get("show running-config all | incl 'system default switchport'") - data += connection.get('show running-config | section ^interface') - - # Collect device defaults & per-intf defaults - self.render_system_defaults(data) - intf_defs = {'sysdefs': self.sysdefs} - - config = data.split('interface ') - default_interfaces = [] - for conf in config: - conf = conf.strip() - if conf: - obj = self.render_config(self.generated_spec, conf) - if obj: - intf_defs[obj['name']] = obj.pop('enabled_def', None) - if len(obj.keys()) > 1: - objs.append(obj) - elif len(obj.keys()) == 1: - # Existing-but-default interfaces are not included in the - # objs list; however a list of default interfaces is - # necessary to prevent idempotence issues and to help - # with virtual interfaces that haven't been created yet. - default_interfaces.append(obj['name']) - - ansible_facts['ansible_network_resources'].pop('interfaces', None) - facts = {} - facts['interfaces'] = [] - if objs: - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - ansible_facts['ansible_network_resources']['default_interfaces'] = default_interfaces - ansible_facts['intf_defs'] = intf_defs - return ansible_facts - - def _device_info(self): - return self._module._capabilities.get('device_info', {}) - - def render_system_defaults(self, config): - """Collect user-defined-default states for 'system default switchport' configurations. - These configurations determine default L2/L3 modes and enabled/shutdown - states. The default values for user-defined-default configurations may - be different for legacy platforms. - Notes: - - L3 enabled default state is False on N9K,N7K but True for N3K,N6K - - Changing L2-L3 modes may change the default enabled value. - - '(no) system default switchport shutdown' only applies to L2 interfaces. - """ - platform = self._device_info().get('network_os_platform', '') - L3_enabled = True if re.search('N[356]K', platform) else False - sysdefs = { - 'mode': None, - 'L2_enabled': None, - 'L3_enabled': L3_enabled - } - pat = '(no )*system default switchport$' - m = re.search(pat, config, re.MULTILINE) - if m: - sysdefs['mode'] = 'layer3' if 'no ' in m.groups() else 'layer2' - - pat = '(no )*system default switchport shutdown$' - m = re.search(pat, config, re.MULTILINE) - if m: - sysdefs['L2_enabled'] = True if 'no ' in m.groups() else False - - self.sysdefs = sysdefs - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - match = re.search(r'^(\S+)', conf) - intf = match.group(1) - if get_interface_type(intf) == 'unknown': - return {} - config['name'] = intf - config['description'] = utils.parse_conf_arg(conf, 'description') - config['speed'] = utils.parse_conf_arg(conf, 'speed') - config['mtu'] = utils.parse_conf_arg(conf, 'mtu') - config['duplex'] = utils.parse_conf_arg(conf, 'duplex') - config['mode'] = utils.parse_conf_cmd_arg(conf, 'switchport', 'layer2', 'layer3') - - config['enabled'] = utils.parse_conf_cmd_arg(conf, 'shutdown', False, True) - - # Capture the default 'enabled' state, which may be interface-specific - config['enabled_def'] = default_intf_enabled(name=intf, sysdefs=self.sysdefs, mode=config['mode']) - - config['fabric_forwarding_anycast_gateway'] = utils.parse_conf_cmd_arg(conf, 'fabric forwarding mode anycast-gateway', True) - config['ip_forward'] = utils.parse_conf_cmd_arg(conf, 'ip forward', True) - - interfaces_cfg = utils.remove_empties(config) - return interfaces_cfg diff --git a/lib/ansible/module_utils/network/nxos/facts/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/l2_interfaces/l2_interfaces.py deleted file mode 100644 index 2063dd495e..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,94 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python -""" -The nxos l2_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.l2_interfaces.l2_interfaces import L2_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type - - -class L2_interfacesFacts(object): - """The nxos l2_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = L2_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for l2_interfaces - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - if not data: - data = connection.get('show running-config | section ^interface') - - config = data.split('interface ') - for conf in config: - conf = conf.strip() - if conf: - obj = self.render_config(self.generated_spec, conf) - if obj and len(obj.keys()) > 1: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('l2_interfaces', None) - facts = {} - if objs: - facts['l2_interfaces'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['l2_interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - match = re.search(r'^(\S+)', conf) - intf = match.group(1) - if get_interface_type(intf) == 'unknown': - return {} - - config['name'] = intf - config['mode'] = utils.parse_conf_arg(conf, 'switchport mode') - config['ip_forward'] = utils.parse_conf_arg(conf, 'ip forward') - config['access']['vlan'] = utils.parse_conf_arg(conf, 'switchport access vlan') - config['trunk']['allowed_vlans'] = utils.parse_conf_arg(conf, 'switchport trunk allowed vlan') - config['trunk']['native_vlan'] = utils.parse_conf_arg(conf, 'switchport trunk native vlan') - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/l3_interfaces/l3_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/l3_interfaces/l3_interfaces.py deleted file mode 100644 index c5be39a4dc..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/l3_interfaces/l3_interfaces.py +++ /dev/null @@ -1,126 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python -""" -The nxos l3_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type - - -class L3_interfacesFacts(object): - """ The nxos l3_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = L3_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for l3_interfaces - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - if not data: - data = connection.get('show running-config | section ^interface') - - config = data.split('interface ') - for conf in config: - conf = conf.strip() - if conf: - obj = self.render_config(self.generated_spec, conf) - if obj and len(obj.keys()) > 1: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('l3_interfaces', None) - facts = {} - if objs: - facts['l3_interfaces'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['l3_interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - match = re.search(r'^(\S+)', conf) - intf = match.group(1) - if get_interface_type(intf) == 'unknown': - return {} - config['name'] = intf - config['dot1q'] = utils.parse_conf_arg(conf, 'encapsulation dot1[qQ]') - config['redirects'] = utils.parse_conf_cmd_arg(conf, 'no ip redirects', False, True) - config['unreachables'] = utils.parse_conf_cmd_arg(conf, 'ip unreachables', True, False) - ipv4_match = re.compile(r'\n ip address (.*)') - matches = ipv4_match.findall(conf) - if matches: - if matches[0]: - config['ipv4'] = [] - for m in matches: - ipv4_conf = m.split() - addr = ipv4_conf[0] - if addr: - config_dict = {'address': addr} - if len(ipv4_conf) > 1: - d = ipv4_conf[1] - if d == 'secondary': - config_dict.update({'secondary': True}) - if len(ipv4_conf) == 4: - if ipv4_conf[2] == 'tag': - config_dict.update({'tag': int(ipv4_conf[-1])}) - elif d == 'tag': - config_dict.update({'tag': int(ipv4_conf[-1])}) - config['ipv4'].append(config_dict) - - ipv6_match = re.compile(r'\n ipv6 address (.*)') - matches = ipv6_match.findall(conf) - if matches: - if matches[0]: - config['ipv6'] = [] - for m in matches: - ipv6_conf = m.split() - addr = ipv6_conf[0] - if addr: - config_dict = {'address': addr} - if len(ipv6_conf) > 1: - d = ipv6_conf[1] - if d == 'tag': - config_dict.update({'tag': int(ipv6_conf[-1])}) - config['ipv6'].append(config_dict) - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/lacp/lacp.py b/lib/ansible/module_utils/network/nxos/facts/lacp/lacp.py deleted file mode 100644 index 90524503db..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/lacp/lacp.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos lacp fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.lacp.lacp import LacpArgs - - -class LacpFacts(object): - """ The nxos lacp fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = LacpArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lacp - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = connection.get("show running-config | include lacp") - resources = data.strip() - objs = self.render_config(self.generated_spec, resources) - ansible_facts['ansible_network_resources'].pop('lacp', None) - facts = {} - if objs: - params = utils.validate_config(self.argument_spec, {'config': objs}) - facts['lacp'] = utils.remove_empties(params['config']) - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - p_match = re.search(r'lacp system-priority (\d+)', conf, re.M) - if p_match: - config['system']['priority'] = p_match.group(1) - - a_match = re.search(r'lacp system-mac (\S+)', conf, re.M) - if a_match: - address = a_match.group(1) - config['system']['mac']['address'] = address - r_match = re.search(r'lacp system-mac {0} role (\S+)'.format(address), conf, re.M) - if r_match: - config['system']['mac']['role'] = r_match.group(1) - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py deleted file mode 100644 index 000e1de72b..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos lacp_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type - - -class Lacp_interfacesFacts(object): - """ The nxos lacp_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Lacp_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lacp_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - - if not data: - data = connection.get('show running-config | section ^interface') - - resources = data.split('interface ') - for resource in resources: - if resource and re.search(r'lacp', resource): - obj = self.render_config(self.generated_spec, resource) - if obj and len(obj.keys()) > 1: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('lacp_interfaces', None) - facts = {} - if objs: - facts['lacp_interfaces'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['lacp_interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - - match = re.search(r'^(\S+)', conf) - intf = match.group(1) - if get_interface_type(intf) == 'unknown': - return {} - config['name'] = intf - config['port_priority'] = utils.parse_conf_arg(conf, 'lacp port-priority') - config['rate'] = utils.parse_conf_arg(conf, 'lacp rate') - config['mode'] = utils.parse_conf_arg(conf, 'mode') - suspend_individual = re.search(r'no lacp suspend-individual', conf) - if suspend_individual: - config['suspend_individual'] = False - max_links = utils.parse_conf_arg(conf, 'lacp max-bundle') - if max_links: - config['links']['max'] = max_links - min_links = utils.parse_conf_arg(conf, 'lacp min-links') - if min_links: - config['links']['min'] = min_links - graceful = re.search(r'no lacp graceful-convergence', conf) - if graceful: - config['convergence']['gracefule'] = False - vpc = re.search(r'lacp vpc-convergence', conf) - if vpc: - config['convergence']['vpc'] = True - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/lag_interfaces/lag_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/lag_interfaces/lag_interfaces.py deleted file mode 100644 index 983212409d..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/lag_interfaces/lag_interfaces.py +++ /dev/null @@ -1,124 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python -""" -The nxos lag_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type, normalize_interface - - -class Lag_interfacesFacts(object): - """ The nxos lag_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Lag_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lag_interfaces - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - if not data: - data = connection.get('show running-config | include channel-group') - config = re.split('(\n |)channel-group ', data) - config = list(dict.fromkeys(config)) - for conf in config: - if conf: - obj = self.render_config(self.generated_spec, conf, connection) - if obj and len(obj.keys()) > 1: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('lag_interfaces', None) - facts = {} - if objs: - facts['lag_interfaces'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['lag_interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def get_members(self, id, connection): - """ - Returns members associated with a channel-group - - :param name: The channel group - :rtype: list - :returns: Members - """ - members = [] - data = connection.get('show port-channel summary') - match = re.search(r'{0} (.+)(|\n)'.format(id), data) - if match: - interfaces = re.search(r'Eth\d(.+)$', match.group()) - if interfaces: - for i in interfaces.group().split(): - if get_interface_type(i[:-3]) != 'unknown': - members.append(normalize_interface(i[:-3])) - - return members - - def render_config(self, spec, conf, connection): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - match = re.search(r'(\d+)( |)(force )?(mode \S+)?', conf, re.M) - if match: - matches = match.groups() - config['name'] = 'port-channel' + str(matches[0]) - config['members'] = [] - members = self.get_members(config['name'].strip('port-channel'), connection) - if members: - for m in members: - m_dict = {} - if matches[2]: - m_dict['force'] = matches[2] - if matches[3]: - m_dict['mode'] = matches[3][5:] - m_dict['member'] = m - config['members'].append(m_dict) - else: - config = {} - - lag_intf_cfg = utils.remove_empties(config) - # if lag interfaces config is not present return empty dict - if len(lag_intf_cfg) == 1: - return {} - else: - return lag_intf_cfg diff --git a/lib/ansible/module_utils/network/nxos/facts/legacy/base.py b/lib/ansible/module_utils/network/nxos/facts/legacy/base.py deleted file mode 100644 index 28be70b6b6..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/legacy/base.py +++ /dev/null @@ -1,756 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -import platform -import re - -from ansible.module_utils.network.nxos.nxos import run_commands, get_config, get_capabilities -from ansible.module_utils.network.nxos.utils.utils import get_interface_type, normalize_interface -from ansible.module_utils.six import iteritems - - -g_config = None - - -class FactsBase(object): - - def __init__(self, module): - self.module = module - self.warnings = list() - self.facts = dict() - self.capabilities = get_capabilities(self.module) - - def populate(self): - pass - - def run(self, command, output='text'): - command_string = command - command = { - 'command': command, - 'output': output - } - resp = run_commands(self.module, [command], check_rc='retry_json') - try: - return resp[0] - except IndexError: - self.warnings.append('command %s failed, facts for this command will not be populated' % command_string) - return None - - def get_config(self): - global g_config - if not g_config: - g_config = get_config(self.module) - return g_config - - def transform_dict(self, data, keymap): - transform = dict() - for key, fact in keymap: - if key in data: - transform[fact] = data[key] - return transform - - def transform_iterable(self, iterable, keymap): - for item in iterable: - yield self.transform_dict(item, keymap) - - -class Default(FactsBase): - - def populate(self): - data = None - data = self.run('show version') - - if data: - self.facts['serialnum'] = self.parse_serialnum(data) - - data = self.run('show license host-id') - if data: - self.facts['license_hostid'] = self.parse_license_hostid(data) - - self.facts.update(self.platform_facts()) - - def parse_serialnum(self, data): - match = re.search(r'Processor Board ID\s*(\S+)', data, re.M) - if match: - return match.group(1) - - def platform_facts(self): - platform_facts = {} - - resp = self.capabilities - device_info = resp['device_info'] - - platform_facts['system'] = device_info['network_os'] - - for item in ('model', 'image', 'version', 'platform', 'hostname'): - val = device_info.get('network_os_%s' % item) - if val: - platform_facts[item] = val - - platform_facts['api'] = resp['network_api'] - platform_facts['python_version'] = platform.python_version() - - return platform_facts - - def parse_license_hostid(self, data): - match = re.search(r'License hostid: VDH=(.+)$', data, re.M) - if match: - return match.group(1) - - -class Config(FactsBase): - - def populate(self): - super(Config, self).populate() - self.facts['config'] = self.get_config() - - -class Features(FactsBase): - - def populate(self): - super(Features, self).populate() - data = self.get_config() - - if data: - features = [] - for line in data.splitlines(): - if line.startswith('feature'): - features.append(line.replace('feature', '').strip()) - - self.facts['features_enabled'] = features - - -class Hardware(FactsBase): - - def populate(self): - data = self.run('dir') - if data: - self.facts['filesystems'] = self.parse_filesystems(data) - - data = None - data = self.run('show system resources', output='json') - - if data: - if isinstance(data, dict): - self.facts['memtotal_mb'] = int(data['memory_usage_total']) / 1024 - self.facts['memfree_mb'] = int(data['memory_usage_free']) / 1024 - else: - self.facts['memtotal_mb'] = self.parse_memtotal_mb(data) - self.facts['memfree_mb'] = self.parse_memfree_mb(data) - - def parse_filesystems(self, data): - return re.findall(r'^Usage for (\S+)//', data, re.M) - - def parse_memtotal_mb(self, data): - match = re.search(r'(\S+)K(\s+|)total', data, re.M) - if match: - memtotal = match.group(1) - return int(memtotal) / 1024 - - def parse_memfree_mb(self, data): - match = re.search(r'(\S+)K(\s+|)free', data, re.M) - if match: - memfree = match.group(1) - return int(memfree) / 1024 - - -class Interfaces(FactsBase): - - INTERFACE_MAP = frozenset([ - ('state', 'state'), - ('desc', 'description'), - ('eth_bw', 'bandwidth'), - ('eth_duplex', 'duplex'), - ('eth_speed', 'speed'), - ('eth_mode', 'mode'), - ('eth_hw_addr', 'macaddress'), - ('eth_mtu', 'mtu'), - ('eth_hw_desc', 'type') - ]) - - INTERFACE_SVI_MAP = frozenset([ - ('svi_line_proto', 'state'), - ('svi_bw', 'bandwidth'), - ('svi_mac', 'macaddress'), - ('svi_mtu', 'mtu'), - ('type', 'type') - ]) - - INTERFACE_IPV4_MAP = frozenset([ - ('eth_ip_addr', 'address'), - ('eth_ip_mask', 'masklen') - ]) - - INTERFACE_SVI_IPV4_MAP = frozenset([ - ('svi_ip_addr', 'address'), - ('svi_ip_mask', 'masklen') - ]) - - INTERFACE_IPV6_MAP = frozenset([ - ('addr', 'address'), - ('prefix', 'subnet') - ]) - - def ipv6_structure_op_supported(self): - data = self.capabilities - if data: - nxos_os_version = data['device_info']['network_os_version'] - unsupported_versions = ['I2', 'F1', 'A8'] - for ver in unsupported_versions: - if ver in nxos_os_version: - return False - return True - - def populate(self): - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - self.facts['neighbors'] = {} - data = None - - data = self.run('show interface', output='json') - - if data: - if isinstance(data, dict): - self.facts['interfaces'] = self.populate_structured_interfaces(data) - else: - interfaces = self.parse_interfaces(data) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - - if self.ipv6_structure_op_supported(): - data = self.run('show ipv6 interface', output='json') - else: - data = None - if data: - if isinstance(data, dict): - self.populate_structured_ipv6_interfaces(data) - else: - interfaces = self.parse_interfaces(data) - self.populate_ipv6_interfaces(interfaces) - - data = self.run('show lldp neighbors', output='json') - if data: - if isinstance(data, dict): - self.facts['neighbors'].update(self.populate_structured_neighbors_lldp(data)) - else: - self.facts['neighbors'].update(self.populate_neighbors(data)) - - data = self.run('show cdp neighbors detail', output='json') - if data: - if isinstance(data, dict): - self.facts['neighbors'].update(self.populate_structured_neighbors_cdp(data)) - else: - self.facts['neighbors'].update(self.populate_neighbors_cdp(data)) - - self.facts['neighbors'].pop(None, None) # Remove null key - - def populate_structured_interfaces(self, data): - interfaces = dict() - for item in data['TABLE_interface']['ROW_interface']: - name = item['interface'] - - intf = dict() - if 'type' in item: - intf.update(self.transform_dict(item, self.INTERFACE_SVI_MAP)) - else: - intf.update(self.transform_dict(item, self.INTERFACE_MAP)) - - if 'eth_ip_addr' in item: - intf['ipv4'] = self.transform_dict(item, self.INTERFACE_IPV4_MAP) - self.facts['all_ipv4_addresses'].append(item['eth_ip_addr']) - - if 'svi_ip_addr' in item: - intf['ipv4'] = self.transform_dict(item, self.INTERFACE_SVI_IPV4_MAP) - self.facts['all_ipv4_addresses'].append(item['svi_ip_addr']) - - interfaces[name] = intf - - return interfaces - - def populate_structured_ipv6_interfaces(self, data): - try: - data = data['TABLE_intf'] - if data: - if isinstance(data, dict): - data = [data] - for item in data: - name = item['ROW_intf']['intf-name'] - intf = self.facts['interfaces'][name] - intf['ipv6'] = self.transform_dict(item, self.INTERFACE_IPV6_MAP) - try: - addr = item['ROW_intf']['addr'] - except KeyError: - addr = item['ROW_intf']['TABLE_addr']['ROW_addr']['addr'] - self.facts['all_ipv6_addresses'].append(addr) - else: - return "" - except TypeError: - return "" - - def populate_structured_neighbors_lldp(self, data): - objects = dict() - data = data['TABLE_nbor']['ROW_nbor'] - - if isinstance(data, dict): - data = [data] - - for item in data: - local_intf = normalize_interface(item['l_port_id']) - objects[local_intf] = list() - nbor = dict() - nbor['port'] = item['port_id'] - nbor['host'] = nbor['sysname'] = item['chassis_id'] - objects[local_intf].append(nbor) - - return objects - - def populate_structured_neighbors_cdp(self, data): - objects = dict() - data = data['TABLE_cdp_neighbor_detail_info']['ROW_cdp_neighbor_detail_info'] - - if isinstance(data, dict): - data = [data] - - for item in data: - local_intf = item['intf_id'] - objects[local_intf] = list() - nbor = dict() - nbor['port'] = item['port_id'] - nbor['host'] = nbor['sysname'] = item['device_id'] - objects[local_intf].append(nbor) - - return objects - - def parse_interfaces(self, data): - parsed = dict() - key = '' - for line in data.split('\n'): - if len(line) == 0: - continue - elif line.startswith('admin') or line[0] == ' ': - parsed[key] += '\n%s' % line - else: - match = re.match(r'^(\S+)', line) - if match: - key = match.group(1) - if not key.startswith('admin') or not key.startswith('IPv6 Interface'): - parsed[key] = line - return parsed - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - if get_interface_type(key) == 'svi': - intf['state'] = self.parse_state(key, value, intf_type='svi') - intf['macaddress'] = self.parse_macaddress(value, intf_type='svi') - intf['mtu'] = self.parse_mtu(value, intf_type='svi') - intf['bandwidth'] = self.parse_bandwidth(value, intf_type='svi') - intf['type'] = self.parse_type(value, intf_type='svi') - if 'Internet Address' in value: - intf['ipv4'] = self.parse_ipv4_address(value, intf_type='svi') - facts[key] = intf - else: - intf['state'] = self.parse_state(key, value) - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - intf['mode'] = self.parse_mode(value) - intf['mtu'] = self.parse_mtu(value) - intf['bandwidth'] = self.parse_bandwidth(value) - intf['duplex'] = self.parse_duplex(value) - intf['speed'] = self.parse_speed(value) - intf['type'] = self.parse_type(value) - if 'Internet Address' in value: - intf['ipv4'] = self.parse_ipv4_address(value) - facts[key] = intf - - return facts - - def parse_state(self, key, value, intf_type='ethernet'): - match = None - if intf_type == 'svi': - match = re.search(r'line protocol is\s*(\S+)', value, re.M) - else: - match = re.search(r'%s is\s*(\S+)' % key, value, re.M) - - if match: - return match.group(1) - - def parse_macaddress(self, value, intf_type='ethernet'): - match = None - if intf_type == 'svi': - match = re.search(r'address is\s*(\S+)', value, re.M) - else: - match = re.search(r'address:\s*(\S+)', value, re.M) - - if match: - return match.group(1) - - def parse_mtu(self, value, intf_type='ethernet'): - match = re.search(r'MTU\s*(\S+)', value, re.M) - if match: - return match.group(1) - - def parse_bandwidth(self, value, intf_type='ethernet'): - match = re.search(r'BW\s*(\S+)', value, re.M) - if match: - return match.group(1) - - def parse_type(self, value, intf_type='ethernet'): - match = None - if intf_type == 'svi': - match = re.search(r'Hardware is\s*(\S+)', value, re.M) - else: - match = re.search(r'Hardware:\s*(.+),', value, re.M) - - if match: - return match.group(1) - - def parse_description(self, value, intf_type='ethernet'): - match = re.search(r'Description: (.+)$', value, re.M) - if match: - return match.group(1) - - def parse_mode(self, value, intf_type='ethernet'): - match = re.search(r'Port mode is (\S+)', value, re.M) - if match: - return match.group(1) - - def parse_duplex(self, value, intf_type='ethernet'): - match = re.search(r'(\S+)-duplex', value, re.M) - if match: - return match.group(1) - - def parse_speed(self, value, intf_type='ethernet'): - match = re.search(r'duplex, (.+)$', value, re.M) - if match: - return match.group(1) - - def parse_ipv4_address(self, value, intf_type='ethernet'): - ipv4 = {} - match = re.search(r'Internet Address is (.+)$', value, re.M) - if match: - address = match.group(1) - addr = address.split('/')[0] - ipv4['address'] = address.split('/')[0] - ipv4['masklen'] = address.split('/')[1] - self.facts['all_ipv4_addresses'].append(addr) - return ipv4 - - def populate_neighbors(self, data): - objects = dict() - # if there are no neighbors the show command returns - # ERROR: No neighbour information - if data.startswith('ERROR'): - return dict() - - regex = re.compile(r'(\S+)\s+(\S+)\s+\d+\s+\w+\s+(\S+)') - - for item in data.split('\n')[4:-1]: - match = regex.match(item) - if match: - nbor = dict() - nbor['host'] = nbor['sysname'] = match.group(1) - nbor['port'] = match.group(3) - local_intf = normalize_interface(match.group(2)) - if local_intf not in objects: - objects[local_intf] = [] - objects[local_intf].append(nbor) - - return objects - - def populate_neighbors_cdp(self, data): - facts = dict() - - for item in data.split('----------------------------------------'): - if item == '': - continue - local_intf = self.parse_lldp_intf(item) - if local_intf not in facts: - facts[local_intf] = list() - - fact = dict() - fact['port'] = self.parse_lldp_port(item) - fact['sysname'] = self.parse_lldp_sysname(item) - facts[local_intf].append(fact) - - return facts - - def parse_lldp_intf(self, data): - match = re.search(r'Interface:\s*(\S+)', data, re.M) - if match: - return match.group(1).strip(',') - - def parse_lldp_port(self, data): - match = re.search(r'Port ID \(outgoing port\):\s*(\S+)', data, re.M) - if match: - return match.group(1) - - def parse_lldp_sysname(self, data): - match = re.search(r'Device ID:(.+)$', data, re.M) - if match: - return match.group(1) - - def populate_ipv6_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['ipv6'] = self.parse_ipv6_address(value) - facts[key] = intf - - def parse_ipv6_address(self, value): - ipv6 = {} - match_addr = re.search(r'IPv6 address:\s*(\S+)', value, re.M) - if match_addr: - addr = match_addr.group(1) - ipv6['address'] = addr - self.facts['all_ipv6_addresses'].append(addr) - match_subnet = re.search(r'IPv6 subnet:\s*(\S+)', value, re.M) - if match_subnet: - ipv6['subnet'] = match_subnet.group(1) - - return ipv6 - - -class Legacy(FactsBase): - # facts from nxos_facts 2.1 - - VERSION_MAP = frozenset([ - ('host_name', '_hostname'), - ('kickstart_ver_str', '_os'), - ('chassis_id', '_platform') - ]) - - MODULE_MAP = frozenset([ - ('model', 'model'), - ('modtype', 'type'), - ('ports', 'ports'), - ('status', 'status') - ]) - - FAN_MAP = frozenset([ - ('fanname', 'name'), - ('fanmodel', 'model'), - ('fanhwver', 'hw_ver'), - ('fandir', 'direction'), - ('fanstatus', 'status') - ]) - - POWERSUP_MAP = frozenset([ - ('psmodel', 'model'), - ('psnum', 'number'), - ('ps_status', 'status'), - ('ps_status_3k', 'status'), - ('actual_out', 'actual_output'), - ('actual_in', 'actual_in'), - ('total_capa', 'total_capacity'), - ('input_type', 'input_type'), - ('watts', 'watts'), - ('amps', 'amps') - ]) - - def populate(self): - data = None - - data = self.run('show version', output='json') - if data: - if isinstance(data, dict): - self.facts.update(self.transform_dict(data, self.VERSION_MAP)) - else: - self.facts['hostname'] = self.parse_hostname(data) - self.facts['os'] = self.parse_os(data) - self.facts['platform'] = self.parse_platform(data) - - data = self.run('show interface', output='json') - if data: - if isinstance(data, dict): - self.facts['interfaces_list'] = self.parse_structured_interfaces(data) - else: - self.facts['interfaces_list'] = self.parse_interfaces(data) - - data = self.run('show vlan brief', output='json') - if data: - if isinstance(data, dict): - self.facts['vlan_list'] = self.parse_structured_vlans(data) - else: - self.facts['vlan_list'] = self.parse_vlans(data) - - data = self.run('show module', output='json') - if data: - if isinstance(data, dict): - self.facts['module'] = self.parse_structured_module(data) - else: - self.facts['module'] = self.parse_module(data) - - data = self.run('show environment fan', output='json') - if data: - if isinstance(data, dict): - self.facts['fan_info'] = self.parse_structured_fan_info(data) - else: - self.facts['fan_info'] = self.parse_fan_info(data) - - data = self.run('show environment power', output='json') - if data: - if isinstance(data, dict): - self.facts['power_supply_info'] = self.parse_structured_power_supply_info(data) - else: - self.facts['power_supply_info'] = self.parse_power_supply_info(data) - - def parse_structured_interfaces(self, data): - objects = list() - for item in data['TABLE_interface']['ROW_interface']: - objects.append(item['interface']) - return objects - - def parse_structured_vlans(self, data): - objects = list() - data = data['TABLE_vlanbriefxbrief']['ROW_vlanbriefxbrief'] - if isinstance(data, dict): - objects.append(data['vlanshowbr-vlanid-utf']) - elif isinstance(data, list): - for item in data: - objects.append(item['vlanshowbr-vlanid-utf']) - return objects - - def parse_structured_module(self, data): - data = data['TABLE_modinfo']['ROW_modinfo'] - if isinstance(data, dict): - data = [data] - objects = list(self.transform_iterable(data, self.MODULE_MAP)) - return objects - - def parse_structured_fan_info(self, data): - objects = list() - - for key in ("fandetails", "fandetails_3k"): - if data.get(key): - try: - data = data[key]['TABLE_faninfo']['ROW_faninfo'] - except KeyError: - # Some virtual images don't actually report faninfo. In this case, move on and - # just return an empty list. - pass - else: - objects = list(self.transform_iterable(data, self.FAN_MAP)) - break - - return objects - - def parse_structured_power_supply_info(self, data): - if data.get('powersup').get('TABLE_psinfo_n3k'): - fact = data['powersup']['TABLE_psinfo_n3k']['ROW_psinfo_n3k'] - else: - if isinstance(data['powersup']['TABLE_psinfo'], list): - fact = [] - for i in data['powersup']['TABLE_psinfo']: - fact.append(i['ROW_psinfo']) - else: - fact = data['powersup']['TABLE_psinfo']['ROW_psinfo'] - - objects = list(self.transform_iterable(fact, self.POWERSUP_MAP)) - return objects - - def parse_hostname(self, data): - match = re.search(r'\s+Device name:\s+(\S+)', data, re.M) - if match: - return match.group(1) - - def parse_os(self, data): - match = re.search(r'\s+system:\s+version\s*(\S+)', data, re.M) - if match: - return match.group(1) - else: - match = re.search(r'\s+kickstart:\s+version\s*(\S+)', data, re.M) - if match: - return match.group(1) - - def parse_platform(self, data): - match = re.search(r'Hardware\n\s+cisco\s+(\S+\s+\S+)', data, re.M) - if match: - return match.group(1) - - def parse_interfaces(self, data): - objects = list() - for line in data.split('\n'): - if len(line) == 0: - continue - elif line.startswith('admin') or line[0] == ' ': - continue - else: - match = re.match(r'^(\S+)', line) - if match: - intf = match.group(1) - if get_interface_type(intf) != 'unknown': - objects.append(intf) - return objects - - def parse_vlans(self, data): - objects = list() - for line in data.splitlines(): - if line == '': - continue - if line[0].isdigit(): - vlan = line.split()[0] - objects.append(vlan) - return objects - - def parse_module(self, data): - objects = list() - for line in data.splitlines(): - if line == '': - break - if line[0].isdigit(): - obj = {} - match_port = re.search(r'\d\s*(\d*)', line, re.M) - if match_port: - obj['ports'] = match_port.group(1) - - match = re.search(r'\d\s*\d*\s*(.+)$', line, re.M) - if match: - l = match.group(1).split(' ') - items = list() - for item in l: - if item == '': - continue - items.append(item.strip()) - - if items: - obj['type'] = items[0] - obj['model'] = items[1] - obj['status'] = items[2] - - objects.append(obj) - return objects - - def parse_fan_info(self, data): - objects = list() - - for l in data.splitlines(): - if '-----------------' in l or 'Status' in l: - continue - line = l.split() - if len(line) > 1: - obj = {} - obj['name'] = line[0] - obj['model'] = line[1] - obj['hw_ver'] = line[-2] - obj['status'] = line[-1] - objects.append(obj) - return objects - - def parse_power_supply_info(self, data): - objects = list() - - for l in data.splitlines(): - if l == '': - break - if l[0].isdigit(): - obj = {} - line = l.split() - obj['model'] = line[1] - obj['number'] = line[0] - obj['status'] = line[-1] - - objects.append(obj) - return objects diff --git a/lib/ansible/module_utils/network/nxos/facts/lldp_global/lldp_global.py b/lib/ansible/module_utils/network/nxos/facts/lldp_global/lldp_global.py deleted file mode 100644 index c780c2030c..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/lldp_global/lldp_global.py +++ /dev/null @@ -1,105 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos lldp_global fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.lldp_global.lldp_global import Lldp_globalArgs - - -class Lldp_globalFacts(object): - """ The nxos lldp_global fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Lldp_globalArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lldp_global - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - - if not data: - data = connection.get('show running-config | include lldp') - - objs = {} - objs = self.render_config(self.generated_spec, data) - ansible_facts['ansible_network_resources'].pop('lldp_global', None) - facts = {} - if objs: - params = utils.validate_config( - self.argument_spec, {'config': objs}) - facts['lldp_global'] = params['config'] - facts = utils.remove_empties(facts) - ansible_facts['ansible_network_resources'].update((facts)) - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - conf = re.split('\n', conf) - for command in conf: - param = re.search( - r'(.*)lldp (\w+(-?)\w+)', - command) # get the word after 'lldp' - if param: - # get the nested-dict/value for that param - key2 = re.search(r'%s(.*)' % param.group(2), command) - key2 = key2.group(1).strip() - key1 = param.group(2).replace('-', '_') - - if key1 == 'portid_subtype': - key1 = 'port_id' - config[key1] = key2 - elif key1 == 'tlv_select': - key2 = key2.split() - key2[0] = key2[0].replace('-', '_') - if len(key2) == 1: - if 'port' in key2[0] or 'system' in key2[0]: # nested dicts - key2 = key2[0].split('_') - # config[tlv_select][system][name]=False - config[key1][key2[0]][key2[1]] = False - else: - # config[tlv_select][dcbxp]=False - config[key1][key2[0]] = False - else: - # config[tlv_select][management_address][v6]=False - config[key1][key2[0]][key2[1]] = False - else: - config[key1] = key2 # config[reinit]=4 - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/lldp_interfaces/lldp_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index 63ee03e7f6..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,121 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos lldp_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs -from ansible.module_utils.network.nxos.utils.utils import get_interface_type - - -class Lldp_interfacesFacts(object): - """ The nxos lldp_interfaces fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = Lldp_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def get_device_data(self, connection): - return connection.get('show running-config | section ^interface') - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lldp_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = self.get_device_data(connection) - - objs = [] - - data = data.split('interface') - resources = [] - - for i in range(len(data)): - intf = data[i].split('\n') - for l in range(1, len(intf)): - if not re.search('lldp', intf[l]): - intf[l] = '' - intf = list(filter(None, intf)) - intf = ''.join(i for i in intf) - resources.append(intf) - - for resource in resources: - if resource: # and re.search(r'lldp', resource): - obj = self.render_config(self.generated_spec, resource) - if obj and len(obj.keys()) >= 1: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('lldp_interfaces', None) - facts = {} - if objs: - facts['lldp_interfaces'] = [] - params = utils.validate_config( - self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['lldp_interfaces'].append(utils.remove_empties(cfg)) - - ansible_facts['ansible_network_resources'].update(facts) - - return ansible_facts - - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - match = re.search(r'^ (\S+)', conf) - if match is None: - return {} - intf = match.group(1) - if get_interface_type(intf) not in ['management', 'ethernet']: - return {} - config['name'] = intf - if 'lldp receive' in conf: # for parsed state only - config['receive'] = True - if 'no lldp receive' in conf: - config['receive'] = False - - if 'lldp transmit' in conf: # for parsed state only - config['transmit'] = True - if 'no lldp transmit' in conf: - config['transmit'] = False - if 'management-address' in conf: - config['tlv_set']['management_address'] = re.search( - r'management-address (\S*)', conf).group(1) - if 'vlan' in conf: - config['tlv_set']['vlan'] = re.search( - r'vlan (\S*)', conf).group(1) - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/telemetry/telemetry.py b/lib/ansible/module_utils/network/nxos/facts/telemetry/telemetry.py deleted file mode 100644 index 825d1bc8df..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/telemetry/telemetry.py +++ /dev/null @@ -1,163 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos telemetry fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.nxos.argspec.telemetry.telemetry import TelemetryArgs -from ansible.module_utils.network.nxos.cmdref.telemetry.telemetry import TMS_GLOBAL, TMS_DESTGROUP, TMS_SENSORGROUP, TMS_SUBSCRIPTION -from ansible.module_utils.network.nxos.utils.telemetry.telemetry import get_instance_data, cr_key_lookup -from ansible.module_utils.network.nxos.utils.telemetry.telemetry import normalize_data -from ansible.module_utils.network.nxos.nxos import NxosCmdRef - - -class TelemetryFacts(object): - """ The nxos telemetry fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = TelemetryArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for telemetry - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if connection: # just for linting purposes, remove - pass - - cmd_ref = {} - cmd_ref['TMS_GLOBAL'] = {} - cmd_ref['TMS_DESTGROUP'] = {} - cmd_ref['TMS_SENSORGROUP'] = {} - cmd_ref['TMS_SUBSCRIPTION'] = {} - - # For fact gathering, module state should be 'present' when using - # NxosCmdRef to query state - if self._module.params.get('state'): - saved_module_state = self._module.params['state'] - self._module.params['state'] = 'present' - - # Get Telemetry Global Data - cmd_ref['TMS_GLOBAL']['ref'] = [] - cmd_ref['TMS_GLOBAL']['ref'].append(NxosCmdRef(self._module, TMS_GLOBAL)) - ref = cmd_ref['TMS_GLOBAL']['ref'][0] - ref.set_context() - ref.get_existing() - device_cache = ref.cache_existing - - if device_cache is None: - device_cache_lines = [] - else: - device_cache_lines = device_cache.split("\n") - - # Get Telemetry Destination Group Data - cmd_ref['TMS_DESTGROUP']['ref'] = [] - for line in device_cache_lines: - if re.search(r'destination-group', line): - resource_key = line.strip() - cmd_ref['TMS_DESTGROUP']['ref'].append(NxosCmdRef(self._module, TMS_DESTGROUP)) - ref = cmd_ref['TMS_DESTGROUP']['ref'][-1] - ref.set_context([resource_key]) - ref.get_existing(device_cache) - normalize_data(ref) - - # Get Telemetry Sensorgroup Group Data - cmd_ref['TMS_SENSORGROUP']['ref'] = [] - for line in device_cache_lines: - if re.search(r'sensor-group', line): - resource_key = line.strip() - cmd_ref['TMS_SENSORGROUP']['ref'].append(NxosCmdRef(self._module, TMS_SENSORGROUP)) - ref = cmd_ref['TMS_SENSORGROUP']['ref'][-1] - ref.set_context([resource_key]) - ref.get_existing(device_cache) - - # Get Telemetry Subscription Data - cmd_ref['TMS_SUBSCRIPTION']['ref'] = [] - for line in device_cache_lines: - if re.search(r'subscription', line): - resource_key = line.strip() - cmd_ref['TMS_SUBSCRIPTION']['ref'].append(NxosCmdRef(self._module, TMS_SUBSCRIPTION)) - ref = cmd_ref['TMS_SUBSCRIPTION']['ref'][-1] - ref.set_context([resource_key]) - ref.get_existing(device_cache) - - objs = [] - objs = self.render_config(self.generated_spec, cmd_ref) - facts = {'telemetry': {}} - if objs: - # params = utils.validate_config(self.argument_spec, {'config': objs}) - facts['telemetry'] = objs - - ansible_facts['ansible_network_resources'].update(facts) - if self._module.params.get('state'): - self._module.params['state'] = saved_module_state - return ansible_facts - - def render_config(self, spec, cmd_ref): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = deepcopy(spec) - config['destination_groups'] = [] - config['sensor_groups'] = [] - config['subscriptions'] = [] - managed_objects = ['TMS_GLOBAL', 'TMS_DESTGROUP', 'TMS_SENSORGROUP', 'TMS_SUBSCRIPTION'] - - # Walk the argspec and cmd_ref objects and build out config dict. - for key in config.keys(): - for mo in managed_objects: - for cr in cmd_ref[mo]['ref']: - cr_keys = cr_key_lookup(key, mo) - for cr_key in cr_keys: - if cr._ref.get(cr_key) and cr._ref[cr_key].get('existing'): - if isinstance(config[key], dict): - for k in config[key].keys(): - for existing_key in cr._ref[cr_key]['existing'].keys(): - config[key][k] = cr._ref[cr_key]['existing'][existing_key][k] - continue - if isinstance(config[key], list): - for existing_key in cr._ref[cr_key]['existing'].keys(): - data = get_instance_data(key, cr_key, cr, existing_key) - config[key].append(data) - continue - for existing_key in cr._ref[cr_key]['existing'].keys(): - config[key] = cr._ref[cr_key]['existing'][existing_key] - elif cr._ref.get(cr_key): - data = get_instance_data(key, cr_key, cr, None) - if isinstance(config[key], list) and data not in config[key]: - config[key].append(data) - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/facts/vlans/vlans.py b/lib/ansible/module_utils/network/nxos/facts/vlans/vlans.py deleted file mode 100644 index 260fae1a23..0000000000 --- a/lib/ansible/module_utils/network/nxos/facts/vlans/vlans.py +++ /dev/null @@ -1,172 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python -""" -The nxos vlans fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -import ast -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.common.utils import parse_conf_arg, parse_conf_cmd_arg -from ansible.module_utils.network.nxos.argspec.vlans.vlans import VlansArgs - - -class VlansFacts(object): - """ The nxos vlans fact class - """ - - def __init__(self, module, subspec='config', options='options'): - self._module = module - self.argument_spec = VlansArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def get_device_data(self, connection, show_cmd): - """Wrapper method for `connection.get()` - This exists solely to allow the unit test framework to mock device connection calls. - """ - return connection.get(show_cmd) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for vlans - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - # **TBD** - # N7K EOL/legacy image 6.2 does not support show vlan | json output. - # If support is still required for this image then: - # - Wrapp the json calls below in a try/except - # - When excepted, use a helper method to parse the run_cfg_output, - # using the run_cfg_output data to generate compatible json data that - # can be read by normalize_table_data. - if not data: - # Use structured for most of the vlan parameter states. - # This data is consistent across the supported nxos platforms. - structured = self.get_device_data(connection, 'show vlan | json') - - # Raw cli config is needed for mapped_vni, which is not included in structured. - run_cfg_output = self.get_device_data(connection, 'show running-config | section ^vlan') - - # Create a single dictionary from all data sources - data = self.normalize_table_data(structured, run_cfg_output) - - for vlan in data: - obj = self.render_config(self.generated_spec, vlan) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('vlans', None) - facts = {} - if objs: - facts['vlans'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['vlans'].append(utils.remove_empties(cfg)) - ansible_facts['ansible_network_resources'].update(facts) - return ansible_facts - - def render_config(self, spec, vlan): - """ - Render config as dictionary structure and delete keys - from spec for null values - :param spec: The facts tree, generated from the argspec - :param vlan: structured data vlan settings (dict) and raw cfg from device - :rtype: dictionary - :returns: The generated config - Sample inputs: test/units/modules/network/nxos/fixtures/nxos_vlans/show_vlan - """ - obj = deepcopy(spec) - - obj['vlan_id'] = vlan['vlan_id'] - - # name: 'VLAN000x' (default name) or custom name - name = vlan['vlanshowbr-vlanname'] - if name and re.match("VLAN%04d" % int(vlan['vlan_id']), name): - name = None - obj['name'] = name - - # mode: 'ce-vlan' or 'fabricpath-vlan' - obj['mode'] = vlan['vlanshowinfo-vlanmode'].replace('-vlan', '') - - # enabled: shutdown, noshutdown - obj['enabled'] = True if 'noshutdown' in vlan['vlanshowbr-shutstate'] else False - - # state: active, suspend - obj['state'] = vlan['vlanshowbr-vlanstate'] - - # non-structured data - obj['mapped_vni'] = parse_conf_arg(vlan['run_cfg'], 'vn-segment') - - return utils.remove_empties(obj) - - def normalize_table_data(self, structured, run_cfg_output): - """Normalize structured output and raw running-config output into - a single dict to simplify render_config usage. - This is needed because: - - The NXOS devices report most of the vlan settings within two - structured data keys: 'vlanbrief' and 'mtuinfo', but the output is - incomplete and therefore raw running-config data is also needed. - - running-config by itself is insufficient because of major differences - in the cli config syntax across platforms. - - Thus a helper method combines settings from the separate top-level keys, - and adds a 'run_cfg' key containing raw cli from the device. - """ - # device output may be string, convert to list - structured = ast.literal_eval(str(structured)) - - vlanbrief = [] - mtuinfo = [] - if 'TABLE_vlanbrief' in structured: - # SAMPLE: {"TABLE_vlanbriefid": {"ROW_vlanbriefid": { - # "vlanshowbr-vlanid": "4", "vlanshowbr-vlanid-utf": "4", - # "vlanshowbr-vlanname": "VLAN0004", "vlanshowbr-vlanstate": "active", - # "vlanshowbr-shutstate": "noshutdown"}}, - vlanbrief = structured['TABLE_vlanbrief']['ROW_vlanbrief'] - - # SAMPLE: "TABLE_mtuinfoid": {"ROW_mtuinfoid": { - # "vlanshowinfo-vlanid": "4", "vlanshowinfo-media-type": "enet", - # "vlanshowinfo-vlanmode": "ce-vlan"}} - mtuinfo = structured['TABLE_mtuinfo']['ROW_mtuinfo'] - - if type(vlanbrief) is not list: - # vlanbrief is not a list when only one vlan is found. - vlanbrief = [vlanbrief] - mtuinfo = [mtuinfo] - - # split out any per-vlan cli config - run_cfg_list = re.split(r'[\n^]vlan ', run_cfg_output) - - # Create a list of vlan dicts where each dict contains vlanbrief, - # mtuinfo, and non-structured running-config data for one vlan. - vlans = [] - for index, v in enumerate(vlanbrief): - v['vlan_id'] = v.get('vlanshowbr-vlanid-utf') - vlan = {} - vlan.update(v) - vlan.update(mtuinfo[index]) - - run_cfg = [i for i in run_cfg_list if "%s\n" % v['vlan_id'] in i] or [''] - vlan['run_cfg'] = run_cfg.pop() - vlans.append(vlan) - return vlans diff --git a/lib/ansible/module_utils/network/nxos/nxos.py b/lib/ansible/module_utils/network/nxos/nxos.py deleted file mode 100644 index 431691c47c..0000000000 --- a/lib/ansible/module_utils/network/nxos/nxos.py +++ /dev/null @@ -1,1310 +0,0 @@ -# -# This code is part of Ansible, but is an independent component. -# -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# Copyright: (c) 2017, Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -import collections -import json -import re -import sys -from copy import deepcopy - -from ansible.module_utils._text import to_text -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 Connection, ConnectionError -from ansible.module_utils.common._collections_compat import Mapping -from ansible.module_utils.network.common.config import NetworkConfig, dumps -from ansible.module_utils.network.common.config import CustomNetworkConfig -from ansible.module_utils.six import iteritems, PY2, PY3 -from ansible.module_utils.urls import fetch_url - -try: - import yaml - HAS_YAML = True -except ImportError: - HAS_YAML = False - -try: - if sys.version_info[:2] < (2, 7): - from ordereddict import OrderedDict - else: - from collections import OrderedDict - HAS_ORDEREDDICT = True -except ImportError: - HAS_ORDEREDDICT = False - -_DEVICE_CONNECTION = None - -nxos_provider_spec = { - 'host': dict(type='str'), - 'port': dict(type='int'), - - 'username': dict(type='str', fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])), - 'password': dict(type='str', no_log=True, fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD'])), - 'ssh_keyfile': dict(type='str', fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE'])), - - 'authorize': dict(type='bool', fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE'])), - 'auth_pass': dict(type='str', no_log=True, fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS'])), - - 'use_ssl': dict(type='bool'), - 'use_proxy': dict(type='bool', default=True), - 'validate_certs': dict(type='bool'), - - 'timeout': dict(type='int'), - - 'transport': dict(type='str', default='cli', choices=['cli', 'nxapi']) -} -nxos_argument_spec = { - 'provider': dict(type='dict', options=nxos_provider_spec, removed_in_version=2.14), -} - - -def get_provider_argspec(): - return nxos_provider_spec - - -def get_connection(module): - global _DEVICE_CONNECTION - if not _DEVICE_CONNECTION: - if is_local_nxapi(module): - conn = LocalNxapi(module) - else: - connection_proxy = Connection(module._socket_path) - cap = json.loads(connection_proxy.get_capabilities()) - if cap['network_api'] == 'cliconf': - conn = Cli(module) - elif cap['network_api'] == 'nxapi': - conn = HttpApi(module) - _DEVICE_CONNECTION = conn - return _DEVICE_CONNECTION - - -class Cli: - - def __init__(self, module): - self._module = module - self._device_configs = {} - self._connection = None - - def _get_connection(self): - if self._connection: - return self._connection - self._connection = Connection(self._module._socket_path) - - return self._connection - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'show running-config ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - try: - return self._device_configs[cmd] - except KeyError: - connection = self._get_connection() - try: - out = connection.get_config(flags=flags) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - cfg = to_text(out, errors='surrogate_then_replace').strip() + '\n' - self._device_configs[cmd] = cfg - return cfg - - def run_commands(self, commands, check_rc=True): - """Run list of commands on remote device and return results - """ - connection = self._get_connection() - - try: - out = connection.run_commands(commands, check_rc) - if check_rc == 'retry_json': - capabilities = self.get_capabilities() - network_api = capabilities.get('network_api') - - if network_api == 'cliconf' and out: - for index, resp in enumerate(out): - if ('Invalid command at' in resp or 'Ambiguous command at' in resp) and 'json' in resp: - if commands[index]['output'] == 'json': - commands[index]['output'] = 'text' - out = connection.run_commands(commands, check_rc) - return out - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc)) - - def load_config(self, config, return_error=False, opts=None, replace=None): - """Sends configuration commands to the remote device - """ - if opts is None: - opts = {} - - connection = self._get_connection() - responses = [] - try: - resp = connection.edit_config(config, replace=replace) - if isinstance(resp, Mapping): - resp = resp['response'] - except ConnectionError as e: - code = getattr(e, 'code', 1) - message = getattr(e, 'err', e) - err = to_text(message, errors='surrogate_then_replace') - if opts.get('ignore_timeout') and code: - responses.append(err) - return responses - elif code and 'no graceful-restart' in err: - if 'ISSU/HA will be affected if Graceful Restart is disabled' in err: - msg = [''] - responses.extend(msg) - return responses - else: - self._module.fail_json(msg=err) - elif code: - self._module.fail_json(msg=err) - - responses.extend(resp) - return responses - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - conn = self._get_connection() - try: - response = conn.get_diff(candidate=candidate, running=running, diff_match=diff_match, diff_ignore_lines=diff_ignore_lines, path=path, - diff_replace=diff_replace) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return response - - def get_capabilities(self): - """Returns platform info of the remove device - """ - if hasattr(self._module, '_capabilities'): - return self._module._capabilities - - connection = self._get_connection() - try: - capabilities = connection.get_capabilities() - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - self._module._capabilities = json.loads(capabilities) - return self._module._capabilities - - def read_module_context(self, module_key): - connection = self._get_connection() - try: - module_context = connection.read_module_context(module_key) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return module_context - - def save_module_context(self, module_key, module_context): - connection = self._get_connection() - try: - connection.save_module_context(module_key, module_context) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return None - - -class LocalNxapi: - - OUTPUT_TO_COMMAND_TYPE = { - 'text': 'cli_show_ascii', - 'json': 'cli_show', - 'bash': 'bash', - 'config': 'cli_conf' - } - - def __init__(self, module): - self._module = module - self._nxapi_auth = None - self._device_configs = {} - self._module_context = {} - - provider = self._module.params.get("provider") or {} - self._module.params['url_username'] = provider.get('username') - self._module.params['url_password'] = provider.get('password') - - host = provider.get('host') - port = provider.get('port') - - if provider.get('use_ssl'): - proto = 'https' - port = port or 443 - else: - proto = 'http' - port = port or 80 - - self._url = '%s://%s:%s/ins' % (proto, host, port) - - def _error(self, msg, **kwargs): - self._nxapi_auth = None - if 'url' not in kwargs: - kwargs['url'] = self._url - self._module.fail_json(msg=msg, **kwargs) - - def _request_builder(self, commands, output, version='1.0', chunk='0', sid=None): - """Encodes a NXAPI JSON request message - """ - try: - command_type = self.OUTPUT_TO_COMMAND_TYPE[output] - except KeyError: - msg = 'invalid format, received %s, expected one of %s' % \ - (output, ','.join(self.OUTPUT_TO_COMMAND_TYPE.keys())) - self._error(msg=msg) - - if isinstance(commands, (list, set, tuple)): - commands = ' ;'.join(commands) - - # Order should not matter but some versions of NX-OS software fail - # to process the payload properly if 'input' gets serialized before - # 'type' and the payload of 'input' contains the word 'type'. - msg = collections.OrderedDict() - msg['version'] = version - msg['type'] = command_type - msg['chunk'] = chunk - msg['sid'] = sid - msg['input'] = commands - msg['output_format'] = 'json' - - return dict(ins_api=msg) - - def send_request(self, commands, output='text', check_status=True, - return_error=False, opts=None): - # only 10 show commands can be encoded in each request - # messages sent to the remote device - if opts is None: - opts = {} - if output != 'config': - commands = collections.deque(to_list(commands)) - stack = list() - requests = list() - - while commands: - stack.append(commands.popleft()) - if len(stack) == 10: - body = self._request_builder(stack, output) - data = self._module.jsonify(body) - requests.append(data) - stack = list() - - if stack: - body = self._request_builder(stack, output) - data = self._module.jsonify(body) - requests.append(data) - - else: - body = self._request_builder(commands, 'config') - requests = [self._module.jsonify(body)] - - headers = {'Content-Type': 'application/json'} - result = list() - timeout = self._module.params['provider']['timeout'] - use_proxy = self._module.params['provider']['use_proxy'] - - for req in requests: - if self._nxapi_auth: - headers['Cookie'] = self._nxapi_auth - - response, headers = fetch_url( - self._module, self._url, data=req, headers=headers, - timeout=timeout, method='POST', use_proxy=use_proxy - ) - self._nxapi_auth = headers.get('set-cookie') - - if opts.get('ignore_timeout') and re.search(r'(-1|5\d\d)', str(headers['status'])): - result.append(headers['status']) - return result - elif headers['status'] != 200: - self._error(**headers) - - try: - response = self._module.from_json(response.read()) - except ValueError: - self._module.fail_json(msg='unable to parse response') - - if response['ins_api'].get('outputs'): - output = response['ins_api']['outputs']['output'] - for item in to_list(output): - if check_status is True and item['code'] != '200': - if return_error: - result.append(item) - else: - self._error(output=output, **item) - elif 'body' in item: - result.append(item['body']) - # else: - # error in command but since check_status is disabled - # silently drop it. - # result.append(item['msg']) - - return result - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'show running-config ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - try: - return self._device_configs[cmd] - except KeyError: - out = self.send_request(cmd) - cfg = str(out[0]).strip() - self._device_configs[cmd] = cfg - return cfg - - def run_commands(self, commands, check_rc=True): - """Run list of commands on remote device and return results - """ - output = None - queue = list() - responses = list() - - def _send(commands, output): - return self.send_request(commands, output, check_status=check_rc) - - for item in to_list(commands): - if is_json(item['command']): - item['command'] = str(item['command']).rsplit('|', 1)[0] - item['output'] = 'json' - - if all((output == 'json', item['output'] == 'text')) or all((output == 'text', item['output'] == 'json')): - responses.extend(_send(queue, output)) - queue = list() - - output = item['output'] or 'json' - queue.append(item['command']) - - if queue: - responses.extend(_send(queue, output)) - - return responses - - def load_config(self, commands, return_error=False, opts=None, replace=None): - """Sends the ordered set of commands to the device - """ - - if opts is None: - opts = {} - - responses = [] - - if replace: - device_info = self.get_device_info() - if '9K' not in device_info.get('network_os_platform', ''): - self._module.fail_json(msg='replace is supported only on Nexus 9K devices') - commands = 'config replace {0}'.format(replace) - - commands = to_list(commands) - try: - resp = self.send_request(commands, output='config', check_status=True, - return_error=return_error, opts=opts) - except ValueError as exc: - code = getattr(exc, 'code', 1) - message = getattr(exc, 'err', exc) - err = to_text(message, errors='surrogate_then_replace') - if opts.get('ignore_timeout') and code: - responses.append(code) - return responses - elif code and 'no graceful-restart' in err: - if 'ISSU/HA will be affected if Graceful Restart is disabled' in err: - msg = [''] - responses.extend(msg) - return responses - else: - self._module.fail_json(msg=err) - elif code: - self._module.fail_json(msg=err) - - if return_error: - return resp - else: - return responses.extend(resp) - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - diff = {} - - # prepare candidate configuration - candidate_obj = NetworkConfig(indent=2) - candidate_obj.load(candidate) - - if running and diff_match != 'none' and diff_replace != 'config': - # running configuration - running_obj = NetworkConfig(indent=2, contents=running, ignore_lines=diff_ignore_lines) - configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace) - - else: - configdiffobjs = candidate_obj.items - - diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else '' - return diff - - def get_device_info(self): - device_info = {} - - device_info['network_os'] = 'nxos' - reply = self.run_commands({'command': 'show version', 'output': 'json'}) - data = reply[0] - - platform_reply = self.run_commands({'command': 'show inventory', 'output': 'json'}) - platform_info = platform_reply[0] - - device_info['network_os_version'] = data.get('sys_ver_str') or data.get('kickstart_ver_str') - device_info['network_os_model'] = data['chassis_id'] - device_info['network_os_hostname'] = data['host_name'] - device_info['network_os_image'] = data.get('isan_file_name') or data.get('kick_file_name') - - if platform_info: - inventory_table = platform_info['TABLE_inv']['ROW_inv'] - for info in inventory_table: - if 'Chassis' in info['name']: - device_info['network_os_platform'] = info['productid'] - - return device_info - - def get_capabilities(self): - result = {} - result['device_info'] = self.get_device_info() - result['network_api'] = 'nxapi' - return result - - def read_module_context(self, module_key): - if self._module_context.get(module_key): - return self._module_context[module_key] - - return None - - def save_module_context(self, module_key, module_context): - self._module_context[module_key] = module_context - - return None - - -class HttpApi: - def __init__(self, module): - self._module = module - self._device_configs = {} - self._module_context = {} - self._connection_obj = None - - @property - def _connection(self): - if not self._connection_obj: - self._connection_obj = Connection(self._module._socket_path) - - return self._connection_obj - - def run_commands(self, commands, check_rc=True): - """Runs list of commands on remote device and returns results - """ - try: - out = self._connection.send_request(commands) - except ConnectionError as exc: - if check_rc is True: - raise - out = to_text(exc) - - out = to_list(out) - if not out[0]: - return out - - for index, response in enumerate(out): - if response[0] == '{': - out[index] = json.loads(response) - - return out - - def get_config(self, flags=None): - """Retrieves the current config from the device or cache - """ - flags = [] if flags is None else flags - - cmd = 'show running-config ' - cmd += ' '.join(flags) - cmd = cmd.strip() - - try: - return self._device_configs[cmd] - except KeyError: - try: - out = self._connection.send_request(cmd) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - cfg = to_text(out).strip() - self._device_configs[cmd] = cfg - return cfg - - def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - diff = {} - - # prepare candidate configuration - candidate_obj = NetworkConfig(indent=2) - candidate_obj.load(candidate) - - if running and diff_match != 'none' and diff_replace != 'config': - # running configuration - running_obj = NetworkConfig(indent=2, contents=running, ignore_lines=diff_ignore_lines) - configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace) - - else: - configdiffobjs = candidate_obj.items - - diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else '' - return diff - - def load_config(self, commands, return_error=False, opts=None, replace=None): - """Sends the ordered set of commands to the device - """ - if opts is None: - opts = {} - - responses = [] - try: - resp = self.edit_config(commands, replace=replace) - except ConnectionError as exc: - code = getattr(exc, 'code', 1) - message = getattr(exc, 'err', exc) - err = to_text(message, errors='surrogate_then_replace') - if opts.get('ignore_timeout') and code: - responses.append(code) - return responses - elif opts.get('catch_clierror') and '400' in code: - return [code, err] - elif code and 'no graceful-restart' in err: - if 'ISSU/HA will be affected if Graceful Restart is disabled' in err: - msg = [''] - responses.extend(msg) - return responses - else: - self._module.fail_json(msg=err) - elif code: - self._module.fail_json(msg=err) - - responses.extend(resp) - return responses - - def edit_config(self, candidate=None, commit=True, replace=None, comment=None): - resp = list() - - self.check_edit_config_capability(candidate, commit, replace, comment) - - if replace: - candidate = 'config replace {0}'.format(replace) - - responses = self._connection.send_request(candidate, output='config') - for response in to_list(responses): - if response != '{}': - resp.append(response) - if not resp: - resp = [''] - - return resp - - def get_capabilities(self): - """Returns platform info of the remove device - """ - try: - capabilities = self._connection.get_capabilities() - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return json.loads(capabilities) - - def check_edit_config_capability(self, candidate=None, commit=True, replace=None, comment=None): - operations = self._connection.get_device_operations() - - if not candidate and not replace: - raise ValueError("must provide a candidate or replace to load configuration") - - if commit not in (True, False): - raise ValueError("'commit' must be a bool, got %s" % commit) - - if replace and not operations.get('supports_replace'): - raise ValueError("configuration replace is not supported") - - if comment and not operations.get('supports_commit_comment', False): - raise ValueError("commit comment is not supported") - - def read_module_context(self, module_key): - try: - module_context = self._connection.read_module_context(module_key) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return module_context - - def save_module_context(self, module_key, module_context): - try: - self._connection.save_module_context(module_key, module_context) - except ConnectionError as exc: - self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return None - - -class NxosCmdRef: - """NXOS Command Reference utilities. - The NxosCmdRef class takes a yaml-formatted string of nxos module commands - and converts it into dict-formatted database of getters/setters/defaults - and associated common and platform-specific values. The utility methods - add additional data such as existing states, playbook states, and proposed cli. - The utilities also abstract away platform differences such as different - defaults and different command syntax. - - Callers must provide a yaml formatted string that defines each command and - its properties; e.g. BFD global: - --- - _template: # _template holds common settings for all commands - # Enable feature bfd if disabled - feature: bfd - # Common getter syntax for BFD commands - get_command: show run bfd all | incl '^(no )*bfd' - - interval: - kind: dict - getval: bfd interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+) - setval: bfd interval {tx} min_rx {min_rx} multiplier {multiplier} - default: - tx: 50 - min_rx: 50 - multiplier: 3 - N3K: - # Platform overrides - default: - tx: 250 - min_rx: 250 - multiplier: 3 - """ - - def __init__(self, module, cmd_ref_str, ref_only=False): - """Initialize cmd_ref from yaml data.""" - - self._module = module - self._check_imports() - self._yaml_load(cmd_ref_str) - self.cache_existing = None - self.present_states = ['present', 'merged', 'replaced'] - self.absent_states = ['absent', 'deleted'] - ref = self._ref - - # Create a list of supported commands based on ref keys - ref['commands'] = sorted([k for k in ref if not k.startswith('_')]) - ref['_proposed'] = [] - ref['_context'] = [] - ref['_resource_key'] = None - - if not ref_only: - ref['_state'] = module.params.get('state', 'present') - self.feature_enable() - self.get_platform_defaults() - self.normalize_defaults() - - def __getitem__(self, key=None): - if key is None: - return self._ref - return self._ref[key] - - def _check_imports(self): - module = self._module - msg = nxosCmdRef_import_check() - if msg: - module.fail_json(msg=msg) - - def _yaml_load(self, cmd_ref_str): - if PY2: - self._ref = yaml.load(cmd_ref_str) - elif PY3: - self._ref = yaml.load(cmd_ref_str, Loader=yaml.FullLoader) - - def feature_enable(self): - """Add 'feature <foo>' to _proposed if ref includes a 'feature' key. """ - ref = self._ref - feature = ref['_template'].get('feature') - if feature: - show_cmd = "show run | incl 'feature {0}'".format(feature) - output = self.execute_show_command(show_cmd, 'text') - if not output or 'CLI command error' in output: - msg = "** 'feature {0}' is not enabled. Module will auto-enable feature {0} ** ".format(feature) - self._module.warn(msg) - ref['_proposed'].append('feature {0}'.format(feature)) - ref['_cli_is_feature_disabled'] = ref['_proposed'] - - def get_platform_shortname(self): - """Query device for platform type, normalize to a shortname/nickname. - Returns platform shortname (e.g. 'N3K-3058P' returns 'N3K') or None. - """ - # TBD: add this method logic to get_capabilities() after those methods - # are made consistent across transports - platform_info = self.execute_show_command('show inventory', 'json') - if not platform_info or not isinstance(platform_info, dict): - return None - inventory_table = platform_info['TABLE_inv']['ROW_inv'] - for info in inventory_table: - if 'Chassis' in info['name']: - network_os_platform = info['productid'] - break - else: - return None - - # Supported Platforms: N3K,N5K,N6K,N7K,N9K,N3K-F,N9K-F - m = re.match('(?P<short>N[35679][K57])-(?P<N35>C35)*', network_os_platform) - if not m: - return None - shortname = m.group('short') - - # Normalize - if m.groupdict().get('N35'): - shortname = 'N35' - elif re.match('N77', shortname): - shortname = 'N7K' - elif re.match(r'N3K|N9K', shortname): - for info in inventory_table: - if '-R' in info['productid']: - # Fretta Platform - shortname += '-F' - break - return shortname - - def get_platform_defaults(self): - """Update ref with platform specific defaults""" - plat = self.get_platform_shortname() - if not plat: - return - - ref = self._ref - ref['_platform_shortname'] = plat - # Remove excluded commands (no platform support for command) - for k in ref['commands']: - if plat in ref[k].get('_exclude', ''): - ref['commands'].remove(k) - - # Update platform-specific settings for each item in ref - plat_spec_cmds = [k for k in ref['commands'] if plat in ref[k]] - for k in plat_spec_cmds: - for plat_key in ref[k][plat]: - ref[k][plat_key] = ref[k][plat][plat_key] - - def normalize_defaults(self): - """Update ref defaults with normalized data""" - ref = self._ref - for k in ref['commands']: - if 'default' in ref[k] and ref[k]['default']: - kind = ref[k]['kind'] - if 'int' == kind: - ref[k]['default'] = int(ref[k]['default']) - elif 'list' == kind: - ref[k]['default'] = [str(i) for i in ref[k]['default']] - elif 'dict' == kind: - for key, v in ref[k]['default'].items(): - if v: - v = str(v) - ref[k]['default'][key] = v - - def execute_show_command(self, command, format): - """Generic show command helper. - Warning: 'CLI command error' exceptions are caught, must be handled by caller. - Return device output as a newline-separated string or None. - """ - cmds = [{ - 'command': command, - 'output': format, - }] - output = None - try: - output = run_commands(self._module, cmds) - if output: - output = output[0] - except ConnectionError as exc: - if 'CLI command error' in repr(exc): - # CLI may be feature disabled - output = repr(exc) - else: - raise - return output - - def pattern_match_existing(self, output, k): - """Pattern matching helper for `get_existing`. - `k` is the command name string. Use the pattern from cmd_ref to - find a matching string in the output. - Return regex match object or None. - """ - ref = self._ref - pattern = re.compile(ref[k]['getval']) - multiple = 'multiple' in ref[k].keys() - match_lines = [re.search(pattern, line) for line in output] - if 'dict' == ref[k]['kind']: - match = [m for m in match_lines if m] - if not match: - return None - if len(match) > 1 and not multiple: - raise ValueError("get_existing: multiple matches found for property {0}".format(k)) - else: - match = [m.groups() for m in match_lines if m] - if not match: - return None - if len(match) > 1 and not multiple: - raise ValueError("get_existing: multiple matches found for property {0}".format(k)) - for item in match: - index = match.index(item) - match[index] = list(item) # tuple to list - - # Handle config strings that nvgen with the 'no' prefix. - # Example match behavior: - # When pattern is: '(no )*foo *(\S+)*$' AND - # When output is: 'no foo' -> match: ['no ', None] - # When output is: 'foo 50' -> match: [None, '50'] - if None is match[index][0]: - match[index].pop(0) - elif 'no' in match[index][0]: - match[index].pop(0) - if not match: - return None - - return match - - def set_context(self, context=None): - """Update ref with command context. - """ - if context is None: - context = [] - ref = self._ref - # Process any additional context that this propoerty might require. - # 1) Global context from NxosCmdRef _template. - # 2) Context passed in using context arg. - ref['_context'] = ref['_template'].get('context', []) - for cmd in context: - ref['_context'].append(cmd) - # Last key in context is the resource key - ref['_resource_key'] = context[-1] if context else ref['_resource_key'] - - def get_existing(self, cache_output=None): - """Update ref with existing command states from the device. - Store these states in each command's 'existing' key. - """ - ref = self._ref - if ref.get('_cli_is_feature_disabled'): - # Add context to proposed if state is present - if ref['_state'] in self.present_states: - [ref['_proposed'].append(ctx) for ctx in ref['_context']] - return - - show_cmd = ref['_template']['get_command'] - if cache_output: - output = cache_output - else: - output = self.execute_show_command(show_cmd, 'text') or [] - self.cache_existing = output - - # Add additional command context if needed. - if ref['_context']: - output = CustomNetworkConfig(indent=2, contents=output) - output = output.get_section(ref['_context']) - - if not output: - # Add context to proposed if state is present - if ref['_state'] in self.present_states: - [ref['_proposed'].append(ctx) for ctx in ref['_context']] - return - - # We need to remove the last item in context for state absent case. - if ref['_state'] in self.absent_states and ref['_context']: - if ref['_resource_key'] and ref['_resource_key'] == ref['_context'][-1]: - if ref['_context'][-1] in output: - ref['_context'][-1] = 'no ' + ref['_context'][-1] - else: - del ref['_context'][-1] - return - - # Walk each cmd in ref, use cmd pattern to discover existing cmds - output = output.split('\n') - for k in ref['commands']: - match = self.pattern_match_existing(output, k) - if not match: - continue - ref[k]['existing'] = {} - for item in match: - index = match.index(item) - kind = ref[k]['kind'] - if 'int' == kind: - ref[k]['existing'][index] = int(item[0]) - elif 'list' == kind: - ref[k]['existing'][index] = [str(i) for i in item[0]] - elif 'dict' == kind: - # The getval pattern should contain regex named group keys that - # match up with the setval named placeholder keys; e.g. - # getval: my-cmd (?P<foo>\d+) bar (?P<baz>\d+) - # setval: my-cmd {foo} bar {baz} - ref[k]['existing'][index] = {} - for key in item.groupdict().keys(): - ref[k]['existing'][index][key] = str(item.group(key)) - elif 'str' == kind: - ref[k]['existing'][index] = item[0] - else: - raise ValueError("get_existing: unknown 'kind' value specified for key '{0}'".format(k)) - - def get_playvals(self): - """Update ref with values from the playbook. - Store these values in each command's 'playval' key. - """ - ref = self._ref - module = self._module - params = {} - if module.params.get('config'): - # Resource module builder packs playvals under 'config' key - param_data = module.params.get('config') - params['global'] = param_data - for key in param_data.keys(): - if isinstance(param_data[key], list): - params[key] = param_data[key] - else: - params['global'] = module.params - for k in ref.keys(): - for level in params.keys(): - if isinstance(params[level], dict): - params[level] = [params[level]] - for item in params[level]: - if k in item and item[k] is not None: - if not ref[k].get('playval'): - ref[k]['playval'] = {} - playval = item[k] - index = params[level].index(item) - # Normalize each value - if 'int' == ref[k]['kind']: - playval = int(playval) - elif 'list' == ref[k]['kind']: - playval = [str(i) for i in playval] - elif 'dict' == ref[k]['kind']: - for key, v in playval.items(): - playval[key] = str(v) - ref[k]['playval'][index] = playval - - def build_cmd_set(self, playval, existing, k): - """Helper function to create list of commands to configure device - Return a list of commands - """ - ref = self._ref - proposed = ref['_proposed'] - cmd = None - kind = ref[k]['kind'] - if 'int' == kind: - cmd = ref[k]['setval'].format(playval) - elif 'list' == kind: - cmd = ref[k]['setval'].format(*(playval)) - elif 'dict' == kind: - # The setval pattern should contain placeholder keys that - # match up with the getval regex named group keys; e.g. - # getval: my-cmd (?P<foo>\d+) bar (?P<baz>\d+) - # setval: my-cmd {foo} bar {baz} - cmd = ref[k]['setval'].format(**playval) - elif 'str' == kind: - if 'deleted' in playval: - if existing: - cmd = 'no ' + ref[k]['setval'].format(existing) - else: - cmd = ref[k]['setval'].format(playval) - else: - raise ValueError("get_proposed: unknown 'kind' value specified for key '{0}'".format(k)) - if cmd: - if ref['_state'] in self.absent_states and not re.search(r'^no', cmd): - cmd = 'no ' + cmd - # Commands may require parent commands for proper context. - # Global _template context is replaced by parameter context - [proposed.append(ctx) for ctx in ref['_context']] - [proposed.append(ctx) for ctx in ref[k].get('context', [])] - proposed.append(cmd) - - def get_proposed(self): - """Compare playbook values against existing states and create a list - of proposed commands. - Return a list of raw cli command strings. - """ - ref = self._ref - # '_proposed' may be empty list or contain initializations; e.g. ['feature foo'] - proposed = ref['_proposed'] - - if ref['_context'] and ref['_context'][-1].startswith('no'): - [proposed.append(ctx) for ctx in ref['_context']] - return proposed - - # Create a list of commands that have playbook values - play_keys = [k for k in ref['commands'] if 'playval' in ref[k]] - - def compare(playval, existing): - if ref['_state'] in self.present_states: - if existing is None: - return False - elif playval == existing: - return True - elif isinstance(existing, dict) and playval in existing.values(): - return True - - if ref['_state'] in self.absent_states: - if isinstance(existing, dict) and all(x is None for x in existing.values()): - existing = None - if existing is None or playval not in existing.values(): - return True - return False - - # Compare against current state - for k in play_keys: - playval = ref[k]['playval'] - # Create playval copy to avoid RuntimeError - # dictionary changed size during iteration error - playval_copy = deepcopy(playval) - existing = ref[k].get('existing', ref[k]['default']) - multiple = 'multiple' in ref[k].keys() - - # Multiple Instances: - if isinstance(existing, dict) and multiple: - for ekey, evalue in existing.items(): - if isinstance(evalue, dict): - # Remove values set to string 'None' from dvalue - evalue = dict((k, v) for k, v in evalue.items() if v != 'None') - for pkey, pvalue in playval.items(): - if compare(pvalue, evalue): - if playval_copy.get(pkey): - del playval_copy[pkey] - if not playval_copy: - continue - # Single Instance: - else: - for pkey, pval in playval.items(): - if compare(pval, existing): - if playval_copy.get(pkey): - del playval_copy[pkey] - if not playval_copy: - continue - - playval = playval_copy - # Multiple Instances: - if isinstance(existing, dict): - for dkey, dvalue in existing.items(): - for pval in playval.values(): - self.build_cmd_set(pval, dvalue, k) - # Single Instance: - else: - for pval in playval.values(): - self.build_cmd_set(pval, existing, k) - - # Remove any duplicate commands before returning. - # pylint: disable=unnecessary-lambda - cmds = sorted(set(proposed), key=lambda x: proposed.index(x)) - return cmds - - -def nxosCmdRef_import_check(): - """Return import error messages or empty string""" - msg = '' - if PY2: - if not HAS_ORDEREDDICT and sys.version_info[:2] < (2, 7): - msg += "Mandatory python library 'ordereddict' is not present, try 'pip install ordereddict'\n" - if not HAS_YAML: - msg += "Mandatory python library 'yaml' is not present, try 'pip install yaml'\n" - elif PY3: - if not HAS_YAML: - msg += "Mandatory python library 'PyYAML' is not present, try 'pip install PyYAML'\n" - return msg - - -def is_json(cmd): - return to_text(cmd).endswith('| json') - - -def is_text(cmd): - return not is_json(cmd) - - -def is_local_nxapi(module): - provider = module.params.get('provider') - if provider: - return provider.get("transport") == 'nxapi' - return False - - -def to_command(module, commands): - if is_local_nxapi(module): - default_output = 'json' - else: - default_output = 'text' - - transform = ComplexList(dict( - command=dict(key=True), - output=dict(default=default_output), - prompt=dict(type='list'), - answer=dict(type='list'), - newline=dict(type='bool', default=True), - sendonly=dict(type='bool', default=False), - check_all=dict(type='bool', default=False), - ), module) - - commands = transform(to_list(commands)) - - for item in commands: - if is_json(item['command']): - item['output'] = 'json' - - return commands - - -def get_config(module, flags=None): - flags = [] if flags is None else flags - - conn = get_connection(module) - return conn.get_config(flags=flags) - - -def run_commands(module, commands, check_rc=True): - conn = get_connection(module) - return conn.run_commands(to_command(module, commands), check_rc) - - -def load_config(module, config, return_error=False, opts=None, replace=None): - conn = get_connection(module) - return conn.load_config(config, return_error, opts, replace=replace) - - -def get_capabilities(module): - conn = get_connection(module) - return conn.get_capabilities() - - -def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'): - conn = self.get_connection() - return conn.get_diff(candidate=candidate, running=running, diff_match=diff_match, diff_ignore_lines=diff_ignore_lines, path=path, diff_replace=diff_replace) - - -def normalize_interface(name): - """Return the normalized interface name - """ - if not name: - return - - def _get_number(name): - digits = '' - for char in name: - if char.isdigit() or char in '/.': - digits += char - return digits - - if name.lower().startswith('et'): - if_type = 'Ethernet' - elif name.lower().startswith('vl'): - if_type = 'Vlan' - elif name.lower().startswith('lo'): - if_type = 'loopback' - elif name.lower().startswith('po'): - if_type = 'port-channel' - elif name.lower().startswith('nv'): - if_type = 'nve' - else: - if_type = None - - number_list = name.split(' ') - if len(number_list) == 2: - number = number_list[-1].strip() - else: - number = _get_number(name) - - if if_type: - proper_interface = if_type + number - else: - proper_interface = name - - return proper_interface - - -def get_interface_type(interface): - """Gets the type of interface - """ - if interface.upper().startswith('ET'): - return 'ethernet' - elif interface.upper().startswith('VL'): - return 'svi' - elif interface.upper().startswith('LO'): - return 'loopback' - elif interface.upper().startswith('MG'): - return 'management' - elif interface.upper().startswith('MA'): - return 'management' - elif interface.upper().startswith('PO'): - return 'portchannel' - elif interface.upper().startswith('NV'): - return 'nve' - else: - return 'unknown' - - -def default_intf_enabled(name='', sysdefs=None, mode=None): - """Get device/version/interface-specific default 'enabled' state. - L3: - - Most L3 intfs default to 'shutdown'. Loopbacks default to 'no shutdown'. - - Some legacy platforms default L3 intfs to 'no shutdown'. - L2: - - User-System-Default 'system default switchport shutdown' defines the - enabled state for L2 intf's. USD defaults may be different on some platforms. - - An intf may be explicitly defined as L2 with 'switchport' or it may be - implicitly defined as L2 when USD 'system default switchport' is defined. - """ - if not name: - return None - if sysdefs is None: - sysdefs = {} - default = False - - if re.search('port-channel|loopback', name): - default = True - else: - if mode is None: - # intf 'switchport' cli is not present so use the user-system-default - mode = sysdefs.get('mode') - - if mode == 'layer3': - default = sysdefs.get('L3_enabled') - elif mode == 'layer2': - default = sysdefs.get('L2_enabled') - return default - - -def read_module_context(module): - conn = get_connection(module) - return conn.read_module_context(module._name) - - -def save_module_context(module, module_context): - conn = get_connection(module) - return conn.save_module_context(module._name, module_context) diff --git a/lib/ansible/module_utils/network/nxos/utils/telemetry/telemetry.py b/lib/ansible/module_utils/network/nxos/utils/telemetry/telemetry.py deleted file mode 100644 index 597adb5674..0000000000 --- a/lib/ansible/module_utils/network/nxos/utils/telemetry/telemetry.py +++ /dev/null @@ -1,250 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Cisco and/or its affiliates. -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The nxos telemetry utility library -""" -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -import re -from copy import deepcopy - - -def get_module_params_subsection(module_params, tms_config, resource_key=None): - """ - Helper method to get a specific module_params subsection - """ - mp = {} - if tms_config == 'TMS_GLOBAL': - relevant_keys = ['certificate', - 'compression', - 'source_interface', - 'vrf'] - for key in relevant_keys: - mp[key] = module_params[key] - - if tms_config == 'TMS_DESTGROUP': - mp['destination_groups'] = [] - for destgrp in module_params['destination_groups']: - if destgrp['id'] == resource_key: - mp['destination_groups'].append(destgrp) - - if tms_config == 'TMS_SENSORGROUP': - mp['sensor_groups'] = [] - for sensor in module_params['sensor_groups']: - if sensor['id'] == resource_key: - mp['sensor_groups'].append(sensor) - - if tms_config == 'TMS_SUBSCRIPTION': - mp['subscriptions'] = [] - for sensor in module_params['subscriptions']: - if sensor['id'] == resource_key: - mp['subscriptions'].append(sensor) - - return mp - - -def valiate_input(playvals, type, module): - """ - Helper method to validate playbook values for destination groups - """ - if type == 'destination_groups': - if not playvals.get('id'): - msg = "Invalid playbook value: {0}.".format(playvals) - msg += " Parameter <id> under <destination_groups> is required" - module.fail_json(msg=msg) - if playvals.get('destination') and not isinstance(playvals['destination'], dict): - msg = "Invalid playbook value: {0}.".format(playvals) - msg += " Parameter <destination> under <destination_groups> must be a dict" - module.fail_json(msg=msg) - if not playvals.get('destination') and len(playvals) > 1: - msg = "Invalid playbook value: {0}.".format(playvals) - msg += " Playbook entry contains unrecongnized parameters." - msg += " Make sure <destination> keys under <destination_groups> are specified as follows:" - msg += " destination: {ip: <ip>, port: <port>, protocol: <prot>, encoding: <enc>}}" - module.fail_json(msg=msg) - - if type == 'sensor_groups': - if not playvals.get('id'): - msg = "Invalid playbook value: {0}.".format(playvals) - msg += " Parameter <id> under <sensor_groups> is required" - module.fail_json(msg=msg) - if playvals.get('path') and 'name' not in playvals['path'].keys(): - msg = "Invalid playbook value: {0}.".format(playvals) - msg += " Parameter <path> under <sensor_groups> requires <name> key" - module.fail_json(msg=msg) - - -def get_instance_data(key, cr_key, cr, existing_key): - """ - Helper method to get instance data used to populate list structure in config - fact dictionary - """ - data = {} - if existing_key is None: - instance = None - else: - instance = cr._ref[cr_key]['existing'][existing_key] - - patterns = { - 'destination_groups': r"destination-group (\d+)", - 'sensor_groups': r"sensor-group (\d+)", - 'subscriptions': r"subscription (\d+)", - } - if key in patterns.keys(): - m = re.search(patterns[key], cr._ref['_resource_key']) - instance_key = m.group(1) - data = {'id': instance_key, cr_key: instance} - - # Remove None values - data = dict((k, v) for k, v in data.items() if v is not None) - return data - - -def cr_key_lookup(key, mo): - """ - Helper method to get instance key value for Managed Object (mo) - """ - cr_keys = [key] - if key == 'destination_groups' and mo == 'TMS_DESTGROUP': - cr_keys = ['destination'] - elif key == 'sensor_groups' and mo == 'TMS_SENSORGROUP': - cr_keys = ['data_source', 'path'] - elif key == 'subscriptions' and mo == 'TMS_SUBSCRIPTION': - cr_keys = ['destination_group', 'sensor_group'] - - return cr_keys - - -def normalize_data(cmd_ref): - ''' Normalize playbook values and get_exisiting data ''' - - playval = cmd_ref._ref.get('destination').get('playval') - existing = cmd_ref._ref.get('destination').get('existing') - - dest_props = ['protocol', 'encoding'] - if playval: - for prop in dest_props: - for key in playval.keys(): - playval[key][prop] = playval[key][prop].lower() - if existing: - for key in existing.keys(): - for prop in dest_props: - existing[key][prop] = existing[key][prop].lower() - - -def remove_duplicate_context(cmds): - ''' Helper method to remove duplicate telemetry context commands ''' - if not cmds: - return cmds - feature_indices = [i for i, x in enumerate(cmds) if x == "feature telemetry"] - telemetry_indices = [i for i, x in enumerate(cmds) if x == "telemetry"] - if len(feature_indices) == 1 and len(telemetry_indices) == 1: - return cmds - if len(feature_indices) == 1 and not telemetry_indices: - return cmds - if len(telemetry_indices) == 1 and not feature_indices: - return cmds - if feature_indices and feature_indices[-1] > 1: - cmds.pop(feature_indices[-1]) - return remove_duplicate_context(cmds) - if telemetry_indices and telemetry_indices[-1] > 1: - cmds.pop(telemetry_indices[-1]) - return remove_duplicate_context(cmds) - - -def get_setval_path(module_or_path_data): - ''' Build setval for path parameter based on playbook inputs - Full Command: - - path {name} depth {depth} query-condition {query_condition} filter-condition {filter_condition} - Required: - - path {name} - Optional: - - depth {depth} - - query-condition {query_condition}, - - filter-condition {filter_condition} - ''' - if isinstance(module_or_path_data, dict): - path = module_or_path_data - else: - path = module_or_path_data.params['config']['sensor_groups'][0].get('path') - if path is None: - return path - - setval = 'path {name}' - if 'depth' in path.keys(): - if path.get('depth') != 'None': - setval = setval + ' depth {depth}' - if 'query_condition' in path.keys(): - if path.get('query_condition') != 'None': - setval = setval + ' query-condition {query_condition}' - if 'filter_condition' in path.keys(): - if path.get('filter_condition') != 'None': - setval = setval + ' filter-condition {filter_condition}' - - return setval - - -def remove_duplicate_commands(commands_list): - # Remove any duplicate commands. - # pylint: disable=unnecessary-lambda - return sorted(set(commands_list), key=lambda x: commands_list.index(x)) - - -def massage_data(have_or_want): - # Massage non global into a data structure that is indexed by id and - # normalized for destination_groups, sensor_groups and subscriptions. - data = deepcopy(have_or_want) - massaged = {} - massaged['destination_groups'] = {} - massaged['sensor_groups'] = {} - massaged['subscriptions'] = {} - from pprint import pprint - for subgroup in ['destination_groups', 'sensor_groups', 'subscriptions']: - for item in data.get(subgroup, []): - id = str(item.get('id')) - if id not in massaged[subgroup].keys(): - massaged[subgroup][id] = [] - item.pop('id') - if not item: - item = None - else: - if item.get('destination'): - if item.get('destination').get('port'): - item['destination']['port'] = str(item['destination']['port']) - if item.get('destination').get('protocol'): - item['destination']['protocol'] = item['destination']['protocol'].lower() - if item.get('destination').get('encoding'): - item['destination']['encoding'] = item['destination']['encoding'].lower() - if item.get('path'): - for key in ['filter_condition', 'query_condition', 'depth']: - if item.get('path').get(key) == 'None': - del item['path'][key] - if item.get('path').get('depth') is not None: - item['path']['depth'] = str(item['path']['depth']) - if item.get('destination_group'): - item['destination_group'] = str(item['destination_group']) - if item.get('sensor_group'): - if item.get('sensor_group').get('id'): - item['sensor_group']['id'] = str(item['sensor_group']['id']) - if item.get('sensor_group').get('sample_interval'): - item['sensor_group']['sample_interval'] = str(item['sensor_group']['sample_interval']) - if item.get('destination_group') and item.get('sensor_group'): - item_copy = deepcopy(item) - del item_copy['sensor_group'] - del item['destination_group'] - massaged[subgroup][id].append(item_copy) - massaged[subgroup][id].append(item) - continue - if item.get('path') and item.get('data_source'): - item_copy = deepcopy(item) - del item_copy['data_source'] - del item['path'] - massaged[subgroup][id].append(item_copy) - massaged[subgroup][id].append(item) - continue - massaged[subgroup][id].append(item) - return massaged diff --git a/lib/ansible/module_utils/network/nxos/utils/utils.py b/lib/ansible/module_utils/network/nxos/utils/utils.py deleted file mode 100644 index 9564758cb7..0000000000 --- a/lib/ansible/module_utils/network/nxos/utils/utils.py +++ /dev/null @@ -1,138 +0,0 @@ -import socket - -from ansible.module_utils.six import iteritems - - -def search_obj_in_list(name, lst, identifier): - for o in lst: - if o[identifier] == name: - return o - return None - - -def flatten_dict(x): - result = {} - if not isinstance(x, dict): - return result - - for key, value in iteritems(x): - if isinstance(value, dict): - result.update(flatten_dict(value)) - else: - result[key] = value - - return result - - -def validate_ipv4_addr(address): - address = address.split('/')[0] - try: - socket.inet_aton(address) - except socket.error: - return False - return address.count('.') == 3 - - -def validate_ipv6_addr(address): - address = address.split('/')[0] - try: - socket.inet_pton(socket.AF_INET6, address) - except socket.error: - return False - return True - - -def normalize_interface(name): - """Return the normalized interface name - """ - if not name: - return - - def _get_number(name): - digits = '' - for char in name: - if char.isdigit() or char in '/.': - digits += char - return digits - - if name.lower().startswith('et'): - if_type = 'Ethernet' - elif name.lower().startswith('vl'): - if_type = 'Vlan' - elif name.lower().startswith('lo'): - if_type = 'loopback' - elif name.lower().startswith('po'): - if_type = 'port-channel' - elif name.lower().startswith('nv'): - if_type = 'nve' - else: - if_type = None - - number_list = name.split(' ') - if len(number_list) == 2: - number = number_list[-1].strip() - else: - number = _get_number(name) - - if if_type: - proper_interface = if_type + number - else: - proper_interface = name - - return proper_interface - - -def get_interface_type(interface): - """Gets the type of interface - """ - if interface.upper().startswith('ET'): - return 'ethernet' - elif interface.upper().startswith('VL'): - return 'svi' - elif interface.upper().startswith('LO'): - return 'loopback' - elif interface.upper().startswith('MG'): - return 'management' - elif interface.upper().startswith('MA'): - return 'management' - elif interface.upper().startswith('PO'): - return 'portchannel' - elif interface.upper().startswith('NV'): - return 'nve' - else: - return 'unknown' - - -def remove_rsvd_interfaces(interfaces): - """Exclude reserved interfaces from user management - """ - return [i for i in interfaces if get_interface_type(i['name']) != 'management'] - - -def vlan_range_to_list(vlans): - result = [] - if vlans: - for part in vlans.split(','): - if part == 'none': - break - if '-' in part: - a, b = part.split('-') - a, b = int(a), int(b) - result.extend(range(a, b + 1)) - else: - a = int(part) - result.append(a) - return numerical_sort(result) - return result - - -def numerical_sort(string_int_list): - """Sorts list of integers that are digits in numerical order. - """ - - as_int_list = [] - - for vlan in string_int_list: - as_int_list.append(int(vlan)) - as_int_list.sort() - return as_int_list |