diff options
Diffstat (limited to 'lib/ansible/module_utils/network/iosxr')
43 files changed, 0 insertions, 8163 deletions
diff --git a/lib/ansible/module_utils/network/iosxr/argspec/acl_interfaces/acl_interfaces.py b/lib/ansible/module_utils/network/iosxr/argspec/acl_interfaces/acl_interfaces.py deleted file mode 100644 index ca3035f742..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/acl_interfaces/acl_interfaces.py +++ /dev/null @@ -1,88 +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 iosxr_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 iosxr_acl_interfaces module - """ - def __init__(self, **kwargs): - pass - - argument_spec = { - 'running_config': { - 'type': 'str' - }, - 'config': { - 'elements': 'dict', - 'options': { - 'access_groups': { - 'elements': 'dict', - 'options': { - 'acls': { - 'elements': 'dict', - 'options': { - 'direction': { - 'choices': ['in', 'out'], - 'type': 'str', - 'required': True - }, - 'name': { - 'type': 'str', - 'required': True - } - }, - 'type': 'list' - }, - 'afi': { - 'choices': ['ipv4', 'ipv6'], - 'type': 'str', - 'required': True - } - }, - 'type': 'list' - }, - 'name': { - 'type': 'str', - 'required': True - } - }, - 'type': 'list' - }, - 'state': { - 'choices': [ - 'merged', 'replaced', 'overridden', 'deleted', 'gathered', - 'parsed', 'rendered' - ], - 'default': - 'merged', - 'type': - 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/iosxr/argspec/acls/acls.py b/lib/ansible/module_utils/network/iosxr/argspec/acls/acls.py deleted file mode 100644 index 2a2fa6b261..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/acls/acls.py +++ /dev/null @@ -1,644 +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 iosxr_acls module -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class AclsArgs(object): # pylint: disable=R0903 - """The arg spec for the iosxr_acls module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'running_config': { - 'type': 'str' - }, - 'config': { - 'elements': 'dict', - 'options': { - 'acls': { - 'elements': 'dict', - 'options': { - 'name': { - 'type': 'str' - }, - 'aces': { - 'elements': 'dict', - 'mutually_exclusive': [['grant', 'remark', 'line']], - 'options': { - 'destination': { - 'mutually_exclusive': [['address', 'any', 'host', 'prefix'], ['wildcard_bits', 'any', 'host', 'prefix']], - 'options': { - 'host': { - 'type': 'str' - }, - 'address': { - 'type': 'str' - }, - 'any': { - 'type': 'bool' - }, - 'prefix': { - 'type': 'str' - }, - 'port_protocol': { - 'mutually_exclusive': [['eq', 'gt', 'lt', 'neq', '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' - }, - 'wildcard_bits': { - 'type': 'str' - } - }, - 'required_together': [['address', 'wildcard_bits']], - 'type': 'dict' - }, - 'dscp': { - 'mutually_exclusive': [['eq', 'gt', 'lt', 'neq', 'range']], - 'type': 'dict', - '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' - } - }, - }, - 'fragments': { - 'type': 'bool' - }, - 'capture': { - 'type': 'bool' - }, - 'destopts': { - 'type': 'bool' - }, - 'authen': { - 'type': 'bool' - }, - 'routing': { - 'type': 'bool' - }, - 'hop_by_hop': { - 'type': 'bool' - }, - 'grant': { - 'type': 'str', - 'choices': ['permit', 'deny'], - }, - 'icmp_off': { - 'type': 'bool' - }, - 'log': { - 'type': 'bool' - }, - 'log_input': { - 'type': 'bool' - }, - 'line': { - 'type': 'str', - 'aliases': ['ace'] - }, - 'packet_length': { - 'mutually_exclusive': [['eq', 'lt', 'neq', 'range'], ['eq', 'gt', 'neq', 'range']], - 'options': { - 'eq': { - 'type': 'int' - }, - 'gt': { - 'type': 'int' - }, - 'lt': { - 'type': 'int' - }, - 'neq': { - 'type': 'int' - }, - 'range': { - 'options': { - 'end': { - 'type': 'int' - }, - 'start': { - 'type': 'int' - } - }, - 'type': 'dict' - } - }, - 'type': - 'dict' - }, - 'precedence': { - 'type': 'str' - }, - 'protocol': { - 'type': 'str' - }, - 'protocol_options': { - 'mutually_exclusive': [['icmp', 'tcp', 'igmp', 'icmpv6']], - 'options': { - 'icmpv6': { - 'type': 'dict', - 'options': { - 'address_unreachable': { - 'type': 'bool' - }, - 'administratively_prohibited': - { - 'type': 'bool' - }, - 'beyond_scope_of_source_address': - { - 'type': 'bool' - }, - 'destination_unreachable': { - 'type': 'bool' - }, - 'echo': { - 'type': 'bool' - }, - 'echo_reply': { - 'type': 'bool' - }, - 'erroneous_header_field': { - 'type': 'bool' - }, - 'group_membership_query': { - 'type': 'bool' - }, - 'group_membership_report': { - 'type': 'bool' - }, - 'group_membership_termination': - { - 'type': 'bool' - }, - 'host_unreachable': { - 'type': 'bool' - }, - 'nd_na': { - 'type': 'bool' - }, - 'nd_ns': { - 'type': 'bool' - }, - 'neighbor_redirect': { - 'type': 'bool' - }, - 'no_route_to_destination': { - 'type': 'bool' - }, - 'node_information_request_is_refused': - { - 'type': 'bool' - }, - 'node_information_successful_reply': - { - 'type': 'bool' - }, - 'packet_too_big': { - 'type': 'bool' - }, - 'parameter_problem': { - 'type': 'bool' - }, - 'port_unreachable': { - 'type': 'bool' - }, - 'query_subject_is_IPv4address': - { - 'type': 'bool' - }, - 'query_subject_is_IPv6address': - { - 'type': 'bool' - }, - 'query_subject_is_domainname': { - 'type': 'bool' - }, - 'reassembly_timeout': { - 'type': 'bool' - }, - 'redirect': { - 'type': 'bool' - }, - 'router_advertisement': { - 'type': 'bool' - }, - 'router_renumbering': { - 'type': 'bool' - }, - 'router_solicitation': { - 'type': 'bool' - }, - 'rr_command': { - 'type': 'bool' - }, - 'rr_result': { - 'type': 'bool' - }, - 'rr_seqnum_reset': { - 'type': 'bool' - }, - 'time_exceeded': { - 'type': 'bool' - }, - 'ttl_exceeded': { - 'type': 'bool' - }, - 'unknown_query_type': { - 'type': 'bool' - }, - 'unreachable': { - 'type': 'bool' - }, - 'unrecognized_next_header': { - 'type': 'bool' - }, - 'unrecognized_option': { - 'type': 'bool' - }, - 'whoareyou_reply': { - 'type': 'bool' - }, - 'whoareyou_request': { - 'type': 'bool' - } - } - }, - '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' - }, - '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': { - 'options': { - 'dvmrp': { - 'type': 'bool' - }, - 'host_query': { - 'type': 'bool' - }, - 'host_report': { - 'type': 'bool' - }, - 'mtrace': { - 'type': 'bool' - }, - 'mtrace_response': { - 'type': 'bool' - }, - 'pim': { - 'type': 'bool' - }, - 'trace': { - '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', 'any', 'host', 'prefix']], - 'options': { - 'host': { - 'type': 'str' - }, - 'address': { - 'type': 'str' - }, - 'any': { - 'type': 'bool' - }, - 'prefix': { - 'type': 'str' - }, - 'port_protocol': { - 'mutually_exclusive': [['eq', 'gt', 'lt', 'neq', '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' - }, - 'wildcard_bits': { - 'type': 'str' - } - }, - 'required_together': [['address', 'wildcard_bits']], - 'type': 'dict' - }, - 'ttl': { - 'mutually_exclusive': [['eq', 'gt', 'lt', 'neq', 'range']], - 'options': { - 'eq': { - 'type': 'int' - }, - 'gt': { - 'type': 'int' - }, - 'lt': { - 'type': 'int' - }, - 'neq': { - 'type': 'int' - }, - 'range': { - 'options': { - 'end': { - 'type': 'int' - }, - 'start': { - 'type': 'int' - } - }, - 'type': 'dict' - } - }, - 'type': 'dict' - } - }, - 'type': 'list' - }, - }, - 'type': 'list' - }, - 'afi': { - 'choices': ['ipv4', 'ipv6'], - 'required': True, - 'type': 'str' - } - }, - 'type': 'list' - }, - 'state': { - 'choices': [ - 'merged', 'replaced', 'overridden', 'deleted', 'gathered', - 'rendered', 'parsed' - ], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/iosxr/argspec/facts/facts.py b/lib/ansible/module_utils/network/iosxr/argspec/facts/facts.py deleted file mode 100644 index dabc6a5322..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/facts/facts.py +++ /dev/null @@ -1,24 +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 iosxr facts module. -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class FactsArgs(object): # pylint: disable=R0903 - """ The arg spec for the iosxr 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/iosxr/argspec/interfaces/interfaces.py b/lib/ansible/module_utils/network/iosxr/argspec/interfaces/interfaces.py deleted file mode 100644 index 3e67c7bd16..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/interfaces/interfaces.py +++ /dev/null @@ -1,47 +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 ios_interfaces module -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class InterfacesArgs(object): - - def __init__(self, **kwargs): - pass - - argument_spec = {'config': {'elements': 'dict', - 'options': {'name': {'type': 'str', 'required': True}, - 'description': {'type': 'str'}, - 'enabled': {'default': True, 'type': 'bool'}, - 'speed': {'type': 'int'}, - 'mtu': {'type': 'int'}, - 'duplex': {'type': 'str', 'choices': ['full', 'half']}}, - 'type': 'list'}, - 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str'}} diff --git a/lib/ansible/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py deleted file mode 100644 index dbf4b0756b..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,57 +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 iosxr_l2_interfaces module -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class L2_InterfacesArgs(object): - - def __init__(self, **kwargs): - pass - - argument_spec = {'config': {'elements': 'dict', - 'options': {'name': {'type': 'str', 'required': True}, - 'native_vlan': {'type': 'int'}, - 'l2transport': {'type': 'bool'}, - 'l2protocol': {'element': 'dict', - 'type': 'list', - 'options': {'cdp': {'type': 'str', - 'choices': ['drop', 'forward', 'tunnel']}, - 'pvst': {'type': 'str', - 'choices': ['drop', 'forward', 'tunnel']}, - 'stp': {'type': 'str', - 'choices': ['drop', 'forward', 'tunnel']}, - 'vtp': {'type': 'str', - 'choices': ['drop', 'forward', 'tunnel']}, - }}, - 'q_vlan': {'type': 'list'}, - 'propagate': {'type': 'bool'}}, - 'type': 'list'}, - 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str'}} diff --git a/lib/ansible/module_utils/network/iosxr/argspec/l3_interfaces/l3_interfaces.py b/lib/ansible/module_utils/network/iosxr/argspec/l3_interfaces/l3_interfaces.py deleted file mode 100644 index 0de627ad91..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/l3_interfaces/l3_interfaces.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 ios_l3_interfaces module -""" - - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class L3_InterfacesArgs(object): - - def __init__(self, **kwargs): - pass - - argument_spec = {'config': {'elements': 'dict', - 'options': {'name': {'type': 'str', 'required': True}, - 'ipv4': {'element': 'dict', - 'type': 'list', - 'options': {'address': {'type': 'str'}, - 'secondary': {'type': 'bool'}}}, - 'ipv6': {'element': 'dict', - 'type': 'list', - 'options': {'address': {'type': 'str'}}} - }, - 'type': 'list'}, - 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], - 'default': 'merged', - 'type': 'str'}} diff --git a/lib/ansible/module_utils/network/iosxr/argspec/lacp/lacp.py b/lib/ansible/module_utils/network/iosxr/argspec/lacp/lacp.py deleted file mode 100644 index 57d8090d7c..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/lacp/lacp.py +++ /dev/null @@ -1,67 +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 iosxr_lacp module -""" - - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class LacpArgs(object): # pylint: disable=R0903 - """The arg spec for the iosxr_lacp module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'options': { - 'system': { - 'options': { - 'mac': { - 'type': 'dict', - 'options': { - 'address': { - 'type': 'str' - } - } - }, - 'priority': { - 'type': 'int' - } - }, - 'type': 'dict' - } - }, - 'type': 'dict' - }, - 'state': { - 'choices': ['merged', 'replaced', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/iosxr/argspec/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/iosxr/argspec/lacp_interfaces/lacp_interfaces.py deleted file mode 100644 index f8a0070a5d..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/lacp_interfaces/lacp_interfaces.py +++ /dev/null @@ -1,79 +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 iosxr_lacp_interfaces module -""" - - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Lacp_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the iosxr_lacp_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'churn_logging': { - 'choices': ['actor', 'partner', 'both'], - 'type': 'str' - }, - 'collector_max_delay': { - 'type': 'int' - }, - 'name': { - 'type': 'str' - }, - 'period': { - 'type': 'int' - }, - 'switchover_suppress_flaps': { - 'type': 'int' - }, - 'system': { - 'options': { - 'mac': { - 'type': 'str' - }, - 'priority': { - '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/iosxr/argspec/lag_interfaces/lag_interfaces.py b/lib/ansible/module_utils/network/iosxr/argspec/lag_interfaces/lag_interfaces.py deleted file mode 100644 index d5799ee764..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/lag_interfaces/lag_interfaces.py +++ /dev/null @@ -1,87 +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 iosxr_lag_interfaces module -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Lag_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the iosxr_lag_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'links': { - 'options': { - 'max_active': { - 'type': 'int' - }, - 'min_active': { - 'type': 'int' - } - }, - 'type': 'dict' - }, - 'load_balancing_hash': { - 'choices': ['dst-ip', 'src-ip'], - 'type': 'str' - }, - 'members': { - 'elements': 'dict', - 'options': { - 'member': { - 'type': 'str' - }, - 'mode': { - 'choices': ['on', 'active', 'passive', 'inherit'], - 'type': 'str' - } - }, - 'type': 'list' - }, - 'mode': { - 'choices': ['on', 'active', 'passive'], - 'type': 'str' - }, - '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/iosxr/argspec/lldp_global/lldp_global.py b/lib/ansible/module_utils/network/iosxr/argspec/lldp_global/lldp_global.py deleted file mode 100644 index 920c5a655e..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/lldp_global/lldp_global.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) - -############################################# -# 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 iosxr_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 iosxr_lldp module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'options': { - 'holdtime': { - 'type': 'int' - }, - 'reinit': { - 'type': 'int' - }, - 'subinterfaces': { - 'type': 'bool' - }, - 'timer': { - 'type': 'int' - }, - 'tlv_select': { - 'options': { - 'management_address': { - 'type': 'bool' - }, - 'port_description': { - 'type': 'bool' - }, - 'system_capabilities': { - 'type': 'bool' - }, - 'system_description': { - 'type': 'bool' - }, - 'system_name': { - 'type': 'bool' - } - }, - 'type': 'dict' - } - }, - 'type': 'dict' - }, - 'state': { - 'choices': ['merged', 'replaced', 'deleted'], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/iosxr/argspec/lldp_interfaces/lldp_interfaces.py b/lib/ansible/module_utils/network/iosxr/argspec/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index 1f5632efa0..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,70 +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 iosxr_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 iosxr_lldp_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'destination': { - 'type': 'dict', - 'options': { - 'mac_address': { - 'type': 'str', - 'choices': ['ieee-nearest-bridge', 'ieee-nearest-non-tmpr-bridge'], - } - } - }, - 'name': { - 'type': 'str' - }, - 'receive': { - 'type': 'bool' - }, - 'transmit': { - '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/iosxr/argspec/static_routes/static_routes.py b/lib/ansible/module_utils/network/iosxr/argspec/static_routes/static_routes.py deleted file mode 100644 index 030515b877..0000000000 --- a/lib/ansible/module_utils/network/iosxr/argspec/static_routes/static_routes.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) - -############################################# -# 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 iosxr_static_routes module -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -class Static_routesArgs(object): # pylint: disable=R0903 - """The arg spec for the iosxr_static_routes module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - 'config': { - 'elements': 'dict', - 'options': { - 'vrf': { - 'type': 'str' - }, - 'address_families': { - 'elements': 'dict', - 'options': { - 'afi': { - 'choices': ['ipv4', 'ipv6'], - 'required': True, - 'type': 'str' - }, - 'safi': { - 'choices': ['unicast', 'multicast'], - 'required': True, - 'type': 'str' - }, - 'routes': { - 'elements': 'dict', - 'options': { - 'dest': { - 'required': True, - 'type': 'str' - }, - 'next_hops': { - 'elements': 'dict', - 'options': { - 'admin_distance': { - 'type': 'int' - }, - 'description': { - 'type': 'str' - }, - 'dest_vrf': { - 'type': 'str' - }, - 'forward_router_address': { - 'type': 'str' - }, - 'interface': { - 'type': 'str' - }, - 'metric': { - 'type': 'int' - }, - 'tag': { - 'type': 'int' - }, - 'track': { - 'type': 'str' - }, - 'tunnel_id': { - 'type': 'int' - }, - 'vrflabel': { - 'type': 'int' - } - }, - 'type': 'list' - } - }, - 'type': 'list' - }, - }, - 'type': 'list' - }, - }, - 'type': 'list' - }, - 'running_config': { - 'type': 'str' - }, - 'state': { - 'choices': [ - 'merged', 'replaced', 'overridden', 'deleted', 'gathered', 'rendered', 'parsed' - ], - 'default': 'merged', - 'type': 'str' - } - } # pylint: disable=C0301 diff --git a/lib/ansible/module_utils/network/iosxr/config/acl_interfaces/acl_interfaces.py b/lib/ansible/module_utils/network/iosxr/config/acl_interfaces/acl_interfaces.py deleted file mode 100644 index 02b1ea738b..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/acl_interfaces/acl_interfaces.py +++ /dev/null @@ -1,317 +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 iosxr_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.iosxr.facts.facts import Facts -from ansible.module_utils.network.iosxr.utils.utils import normalize_interface, diff_list_of_dicts, pad_commands -from ansible.module_utils.network.common.utils \ - import ( - to_list, - search_obj_in_list, - remove_empties - ) - - -class Acl_interfaces(ConfigBase): - """ - The iosxr_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 execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - commands = list() - - if self.state in self.ACTION_STATES: - existing_acl_interfaces_facts = self.get_acl_interfaces_facts() - else: - existing_acl_interfaces_facts = [] - - if self.state in self.ACTION_STATES or self.state == "rendered": - commands.extend(self.set_config(existing_acl_interfaces_facts)) - - if commands and self.state in self.ACTION_STATES: - if not self._module.check_mode: - self._connection.edit_config(commands) - result["changed"] = True - - if self.state in self.ACTION_STATES: - result["commands"] = commands - - if self.state in self.ACTION_STATES or self.state == "gathered": - changed_acl_interfaces_facts = self.get_acl_interfaces_facts() - - elif self.state == "rendered": - result["rendered"] = commands - - elif self.state == "parsed": - running_config = self._module.params["running_config"] - if not running_config: - self._module.fail_json(msg="value of running_config parameter must not be empty for state parsed") - result["parsed"] = self.get_acl_interfaces_facts( - data=running_config) - - if self.state in self.ACTION_STATES: - result["before"] = existing_acl_interfaces_facts - if result["changed"]: - result["after"] = changed_acl_interfaces_facts - - elif self.state == "gathered": - result["gathered"] = 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 - """ - want = self._module.params['config'] - if want: - for item in want: - item['name'] = normalize_interface(item['name']) - if 'members' in want and want['members']: - for item in want['members']: - item.update({ - 'member': - normalize_interface(item['member']), - 'mode': - item['mode'] - }) - 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 in ('overridden', 'merged', 'replaced', 'rendered') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - - elif state == 'deleted': - if not want: - for intf in have: - commands.extend(self._state_deleted({}, intf)) - else: - for item in want: - obj_in_have = search_obj_in_list(item['name'], have) or {} - commands.extend( - self._state_deleted(remove_empties(item), obj_in_have)) - - else: - # Instead of passing entire want and have - # list of dictionaries to the respective - # _state_* methods we are passing the want - # and have dictionaries per interface - for item in want: - name = item['name'] - obj_in_have = search_obj_in_list(name, have) or {} - - if state == 'merged' or state == 'rendered': - commands.extend(self._state_merged(item, obj_in_have)) - - elif state == 'replaced': - commands.extend(self._state_replaced(item, obj_in_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 = [] - - want = remove_empties(want) - - delete_commands = [] - for have_afi in have.get('access_groups', []): - want_afi = search_obj_in_list(have_afi['afi'], - want.get('access_groups', []), - key='afi') or {} - afi = have_afi.get('afi') - - for acl in have_afi.get('acls', []): - if acl not in want_afi.get('acls', []): - delete_commands.extend( - self._compute_commands(afi, [acl], remove=True)) - - if delete_commands: - pad_commands(delete_commands, want['name']) - commands.extend(delete_commands) - - merged_commands = self._state_merged(want, have) - if merged_commands and delete_commands: - del merged_commands[0] - - 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 have_intf in have: - want_intf = search_obj_in_list(have_intf['name'], want) or {} - if not want_intf: - commands.extend(self._state_deleted(want_intf, have_intf)) - - for want_intf in want: - have_intf = search_obj_in_list(want_intf['name'], have) or {} - commands.extend(self._state_replaced(want_intf, have_intf)) - - 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 - """ - commands = [] - - want = remove_empties(want) - - for want_afi in want.get('access_groups', []): - have_afi = search_obj_in_list(want_afi['afi'], - have.get('access_groups', []), - key='afi') or {} - delta = diff_list_of_dicts(want_afi['acls'], - have_afi.get('acls', []), - key='direction') - commands.extend(self._compute_commands(want_afi['afi'], delta)) - - if commands: - pad_commands(commands, want['name']) - - return commands - - 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 = [] - - # This handles deletion for both empty/no config - # and just interface name provided. - if 'access_groups' not in want: - for x in have.get('access_groups', []): - afi = x.get('afi') - for have_acl in x.get('acls', []): - commands.extend( - self._compute_commands(afi, [have_acl], remove=True)) - - else: - for want_afi in want['access_groups']: - have_afi = search_obj_in_list(want_afi['afi'], - have.get('access_groups', []), - key='afi') or {} - afi = have_afi.get('afi') - - # If only the AFI has be specified, we - # delete all the access-groups for that AFI - if 'acls' not in want_afi: - for have_acl in have_afi.get('acls', []): - commands.extend( - self._compute_commands(afi, [have_acl], - remove=True)) - - # If one or more acl has been explicitly specified, we - # delete that and leave the rest untouched - else: - for acl in want_afi['acls']: - if acl in have_afi.get('acls', []): - commands.extend( - self._compute_commands(afi, [acl], - remove=True)) - - if commands: - pad_commands(commands, have['name']) - - return commands - - def _compute_commands(self, afi, delta, remove=False): - updates = [] - map_dir = {'in': 'ingress', 'out': 'egress'} - - for x in delta: - cmd = "{0} access-group {1} {2}".format(afi, x['name'], - map_dir[x['direction']]) - if remove: - cmd = "no " + cmd - updates.append(cmd) - - return updates diff --git a/lib/ansible/module_utils/network/iosxr/config/acls/acls.py b/lib/ansible/module_utils/network/iosxr/config/acls/acls.py deleted file mode 100644 index 43dd015ece..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/acls/acls.py +++ /dev/null @@ -1,440 +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 iosxr_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 - - -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.iosxr.utils.utils \ - import ( - flatten_dict, - prefix_to_address_wildcard, - is_ipv4_address - ) -from ansible.module_utils.network.common.utils \ - import ( - to_list, - search_obj_in_list, - dict_diff, - remove_empties, - ) -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.iosxr.facts.facts import Facts - - -class Acls(ConfigBase): - """ - The iosxr_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 execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - commands = list() - - if self.state in self.ACTION_STATES: - existing_acls_facts = self.get_acls_facts() - else: - existing_acls_facts = [] - - if self.state in self.ACTION_STATES or self.state == "rendered": - commands.extend(self.set_config(existing_acls_facts)) - - if commands and self.state in self.ACTION_STATES: - if not self._module.check_mode: - self._connection.edit_config(commands) - result["changed"] = True - - if self.state in self.ACTION_STATES: - result["commands"] = commands - - if self.state in self.ACTION_STATES or self.state == "gathered": - changed_acls_facts = self.get_acls_facts() - - elif self.state == "rendered": - result["rendered"] = commands - - elif self.state == "parsed": - running_config = self._module.params["running_config"] - if not running_config: - self._module.fail_json( - msg="value of running_config parameter must not be empty for state parsed" - ) - result["parsed"] = self.get_acls_facts(data=running_config) - - if self.state in self.ACTION_STATES: - result["before"] = existing_acls_facts - if result["changed"]: - result["after"] = changed_acls_facts - - elif self.state == "gathered": - result["gathered"] = 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 - """ - want = self._module.params['config'] - have = existing_acls_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 in ('overridden', 'merged', 'replaced', - 'rendered') and not want: - self._module.fail_json( - msg='value of config parameter must not be empty for state {0}' - .format(state)) - - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - - else: - # Instead of passing entire want and have - # list of dictionaries to the respective - # _state_* methods we are passing the want - # and have dictionaries per AFI - for item in want: - afi = item['afi'] - obj_in_have = search_obj_in_list(afi, have, key='afi') - - if state == 'merged' or self.state == 'rendered': - commands.extend( - self._state_merged(remove_empties(item), obj_in_have)) - - elif state == 'replaced': - commands.extend( - self._state_replaced(remove_empties(item), - obj_in_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 = [] - - for want_acl in want['acls']: - have_acl = search_obj_in_list(want_acl['name'], have['acls']) or {} - acl_updates = [] - - for have_ace in have_acl.get('aces', []): - want_ace = search_obj_in_list(have_ace['sequence'], want_acl['aces'], key='sequence') or {} - if not want_ace: - acl_updates.append('no {0}'.format(have_ace['sequence'])) - - for want_ace in want_acl.get('aces', []): - have_ace = search_obj_in_list(want_ace.get('sequence'), have_acl.get('aces', []), key='sequence') or {} - set_cmd = self._set_commands(want_ace, have_ace) - if set_cmd: - acl_updates.append(set_cmd) - - if acl_updates: - acl_updates.insert(0, '{0} access-list {1}'.format(want['afi'], want_acl['name'])) - commands.extend(acl_updates) - - 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 = [] - - # Remove extraneous AFI that are present in config but not - # specified in `want` - for have_afi in have: - want_afi = search_obj_in_list(have_afi['afi'], want, key='afi') or {} - if not want_afi: - for acl in have_afi.get('acls', []): - commands.append('no {0} access-list {1}'.format(have_afi['afi'], acl['name'])) - - # First we remove the extraneous ACLs from the AFIs that - # are present both in `want` and in `have` and then - # we call `_state_replaced` to update the ACEs within those ACLs - for want_afi in want: - want_afi = remove_empties(want_afi) - have_afi = search_obj_in_list(want_afi['afi'], have, key='afi') or {} - if have_afi: - for have_acl in have_afi.get('acls', []): - want_acl = search_obj_in_list(have_acl['name'], want_afi.get('acls', [])) or {} - if not want_acl: - commands.append('no {0} access-list {1}'.format(have_afi['afi'], have_acl['name'])) - - commands.extend(self._state_replaced(want_afi, have_afi)) - - 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 - """ - commands = [] - if not have: - have = {} - - for want_acl in want['acls']: - have_acl = search_obj_in_list(want_acl['name'], have.get('acls', {})) or {} - - acl_updates = [] - for want_ace in want_acl['aces']: - have_ace = search_obj_in_list(want_ace.get('sequence'), have_acl.get('aces', []), key='sequence') or {} - set_cmd = self._set_commands(want_ace, have_ace) - if set_cmd: - acl_updates.append(set_cmd) - - if acl_updates: - acl_updates.insert(0, '{0} access-list {1}'.format(want['afi'], want_acl['name'])) - commands.extend(acl_updates) - - return commands - - 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 want: - want = [{'afi': 'ipv4'}, {'afi': 'ipv6'}] - - for item in want: - item = remove_empties(item) - have_item = search_obj_in_list(item['afi'], have, key='afi') or {} - if 'acls' not in item: - if have_item: - for acl in have_item['acls']: - commands.append('no {0} access-list {1}'.format(have_item['afi'], acl['name'])) - else: - for want_acl in item['acls']: - have_acl = search_obj_in_list(want_acl['name'], have_item.get('acls', [])) or {} - if have_acl: - if 'aces' not in want_acl: - commands.append('no {0} access-list {1}'.format(have_item['afi'], have_acl['name'])) - else: - acl_updates = [] - for want_ace in want_acl['aces']: - have_ace = search_obj_in_list(want_ace.get('sequence'), have_acl.get('aces', []), key='sequence') or {} - if have_ace: - acl_updates.append('no {0}'.format(have_ace['sequence'])) - - if acl_updates: - acl_updates.insert(0, '{0} access-list {1}'.format(have_item['afi'], have_acl['name'])) - commands.extend(acl_updates) - - return commands - - def _compute_commands(self, want_ace): - """This command creates an ACE line from an ACE dictionary - - :rtype: A string - :returns: An ACE generated from a structured ACE dictionary - """ - def __compute_src_dest(dir_dict): - cmd = "" - if 'any' in dir_dict: - cmd += 'any ' - elif 'host' in dir_dict: - cmd += 'host {0} '.format(dir_dict['host']) - elif 'prefix' in dir_dict: - cmd += '{0} '.format(dir_dict['prefix']) - else: - cmd += '{0} {1} '.format(dir_dict['address'], - dir_dict['wildcard_bits']) - - if 'port_protocol' in dir_dict: - protocol_range = dir_dict['port_protocol'].get('range') - if protocol_range: - cmd += 'range {0} {1} '.format(protocol_range['start'], - protocol_range['end']) - else: - for key, value in iteritems(dir_dict['port_protocol']): - cmd += '{0} {1} '.format(key, value) - - return cmd - - def __compute_protocol_options(protocol_dict): - cmd = "" - for value in protocol_options.values(): - for subkey, subvalue in iteritems(value): - if subvalue: - cmd += '{0} '.format(subkey.replace('_', '-')) - return cmd - - def __compute_match_options(want_ace): - cmd = "" - - if 'precedence' in want_ace: - cmd += 'precedence {0} '.format(want_ace['precedence']) - - for x in ['dscp', 'packet_length', 'ttl']: - if x in want_ace: - opt_range = want_ace[x].get('range') - if opt_range: - cmd += '{0} range {1} {2} '.format( - x.replace('_', '-'), opt_range['start'], - opt_range['end']) - else: - for key, value in iteritems(want_ace[x]): - cmd += '{0} {1} {2} '.format( - x.replace('_', '-'), key, value) - - for x in ('authen', 'capture', 'fragments', 'routing', 'log', - 'log_input', 'icmp_off', 'destopts', 'hop_by_hop'): - if x in want_ace: - cmd += '{0} '.format(x.replace('_', '-')) - - return cmd - - cmd = "" - if 'sequence' in want_ace: - cmd += '{0} '.format(want_ace['sequence']) - - if 'remark' in want_ace: - cmd += 'remark {0}'.format(want_ace['remark']) - - elif 'line' in want_ace: - cmd += want_ace['line'] - - else: - cmd += '{0} '.format(want_ace['grant']) - if 'protocol' in want_ace: - cmd += '{0} '.format(want_ace['protocol']) - - cmd += __compute_src_dest(want_ace['source']) - cmd += __compute_src_dest(want_ace['destination']) - - protocol_options = want_ace.get('protocol_options', {}) - if protocol_options: - cmd += __compute_protocol_options(protocol_options) - - cmd += __compute_match_options(want_ace) - - return cmd.strip() - - def _set_commands(self, want_ace, have_ace): - """A helped method that checks if there is - a delta between the `have_ace` and `want_ace`. - If there is a delta then it calls `_compute_commands` - to create the ACE line. - - :rtype: A string - :returns: An ACE generated from a structured ACE dictionary - via a call to `_compute_commands` - """ - - if 'line' in want_ace: - if want_ace['line'] != have_ace.get('line'): - return self._compute_commands(want_ace) - - else: - if ('prefix' in want_ace.get('source', {})) or ('prefix' in want_ace.get('destination', {})): - self._prepare_for_diff(want_ace) - - protocol_opt_delta = {} - delta = dict_diff(have_ace, want_ace) - - # `dict_diff` doesn't work properly for `protocol_options` diff, - # so we need to handle it separately - if want_ace.get('protocol_options', {}): - protocol_opt_delta = set(flatten_dict(have_ace.get('protocol_options', {}))) ^ \ - set(flatten_dict(want_ace.get('protocol_options', {}))) - - if delta or protocol_opt_delta: - want_ace = self._dict_merge(have_ace, want_ace) - return self._compute_commands(want_ace) - - def _prepare_for_diff(self, ace): - """This method prepares the want ace dict - for diff calculation against the have ace dict. - - :param ace: The want ace to prepare for diff calculation - """ - # Convert prefixes to "address wildcard bits" format for IPv4 addresses - # Not valid for IPv6 addresses because those can only be specified as prefixes - # and are always rendered in running-config as prefixes too - for x in ['source', 'destination']: - prefix = ace.get(x, {}).get('prefix') - if prefix and is_ipv4_address(prefix): - del ace[x]['prefix'] - ace[x]['address'], ace[x]['wildcard_bits'] = prefix_to_address_wildcard(prefix) - - def _dict_merge(self, have_ace, want_ace): - for x in want_ace: - have_ace[x] = want_ace[x] - return have_ace diff --git a/lib/ansible/module_utils/network/iosxr/config/interfaces/interfaces.py b/lib/ansible/module_utils/network/iosxr/config/interfaces/interfaces.py deleted file mode 100644 index 076ae017b7..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/interfaces/interfaces.py +++ /dev/null @@ -1,265 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The iosxr_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 -from ansible.module_utils.network.iosxr.facts.facts import Facts -from ansible.module_utils.network.iosxr.utils.utils import get_interface_type, dict_to_set -from ansible.module_utils.network.iosxr.utils.utils import remove_command_from_config_list, add_command_to_config_list -from ansible.module_utils.network.iosxr.utils.utils import filter_dict_having_none_value, remove_duplicate_interface - - -class Interfaces(ConfigBase): - """ - The iosxr_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'interfaces', - ] - - params = ('description', 'mtu', 'speed', 'duplex') - - def __init__(self, module): - super(Interfaces, self).__init__(module) - - def get_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) - interfaces_facts = facts['ansible_network_resources'].get('interfaces') - if not interfaces_facts: - return [] - return 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_interfaces_facts = self.get_interfaces_facts() - commands.extend(self.set_config(existing_interfaces_facts)) - if commands: - if not self._module.check_mode: - self._connection.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): - """ 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_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 in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'overridden': - commands = self._state_overridden(want, have) - elif state == 'deleted': - commands = self._state_deleted(want, 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 = [] - - for interface in want: - for each in have: - if each['name'] == interface['name']: - break - elif interface['name'] in each['name']: - break - else: - continue - have_dict = filter_dict_having_none_value(interface, each) - want = dict() - commands.extend(self._clear_config(want, have_dict)) - commands.extend(self._set_config(interface, each)) - # Remove the duplicate interface call - commands = remove_duplicate_interface(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 each in have: - for interface in want: - if each['name'] == interface['name']: - break - elif interface['name'] in each['name']: - break - else: - # We didn't find a matching desired state, which means we can - # pretend we received an empty desired state. - interface = dict(name=each['name']) - commands.extend(self._clear_config(interface, each)) - continue - have_dict = filter_dict_having_none_value(interface, each) - want = dict() - commands.extend(self._clear_config(want, have_dict)) - commands.extend(self._set_config(interface, each)) - # Remove the duplicate interface call - commands = remove_duplicate_interface(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 - """ - commands = [] - - for interface in want: - for each in have: - if each['name'] == interface['name']: - break - elif interface['name'] in each['name']: - break - else: - continue - commands.extend(self._set_config(interface, each)) - - return commands - - 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 interface in want: - for each in have: - if each['name'] == interface['name']: - break - elif interface['name'] in each['name']: - break - else: - continue - interface = dict(name=interface['name']) - commands.extend(self._clear_config(interface, each)) - else: - for each in have: - want = dict() - commands.extend(self._clear_config(want, each)) - - return commands - - def _set_config(self, want, have): - # Set the interface config based on the want and have config - commands = [] - interface = 'interface ' + want['name'] - - # Get the diff b/w want and have - want_dict = dict_to_set(want) - have_dict = dict_to_set(have) - diff = want_dict - have_dict - - if diff: - diff = dict(diff) - for item in self.params: - if diff.get(item): - cmd = item + ' ' + str(want.get(item)) - add_command_to_config_list(interface, cmd, commands) - if diff.get('enabled'): - add_command_to_config_list(interface, 'no shutdown', commands) - elif diff.get('enabled') is False: - add_command_to_config_list(interface, 'shutdown', commands) - - return commands - - def _clear_config(self, want, have): - # Delete the interface config based on the want and have config - commands = [] - - if want.get('name'): - interface_type = get_interface_type(want['name']) - interface = 'interface ' + want['name'] - else: - interface_type = get_interface_type(have['name']) - interface = 'interface ' + have['name'] - - if have.get('description') and want.get('description') != have.get('description'): - remove_command_from_config_list(interface, 'description', commands) - if not have.get('enabled') and want.get('enabled') != have.get('enabled'): - # if enable is False set enable as True which is the default behavior - remove_command_from_config_list(interface, 'shutdown', commands) - - if interface_type.lower() == 'gigabitethernet': - if have.get('speed') and have.get('speed') != 'auto' and want.get('speed') != have.get('speed'): - remove_command_from_config_list(interface, 'speed', commands) - if have.get('duplex') and have.get('duplex') != 'auto' and want.get('duplex') != have.get('duplex'): - remove_command_from_config_list(interface, 'duplex', commands) - if have.get('mtu') and want.get('mtu') != have.get('mtu'): - remove_command_from_config_list(interface, 'mtu', commands) - - return commands diff --git a/lib/ansible/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py deleted file mode 100644 index a842026609..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,305 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The iosxr_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 to_list -from ansible.module_utils.network.iosxr.facts.facts import Facts -from ansible.module_utils.network.iosxr.utils.utils import normalize_interface, dict_to_set -from ansible.module_utils.network.iosxr.utils.utils import remove_command_from_config_list, add_command_to_config_list -from ansible.module_utils.network.iosxr.utils.utils import filter_dict_having_none_value, remove_duplicate_interface - - -class L2_Interfaces(ConfigBase): - """ - The iosxr_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'l2_interfaces', - ] - - 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 - """ - want = self._module.params['config'] - have = existing_l2_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 in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'overridden': - commands = self._state_overridden(want, have, self._module) - elif state == 'deleted': - commands = self._state_deleted(want, have) - elif state == 'merged': - commands = self._state_merged(want, have, self._module) - elif state == 'replaced': - commands = self._state_replaced(want, have, self._module) - - return commands - - def _state_replaced(self, want, have, module): - """ The command generator when state is replaced - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - - for interface in want: - interface['name'] = normalize_interface(interface['name']) - for each in have: - if each['name'] == interface['name']: - break - else: - commands.extend(self._set_config(interface, {}, module)) - continue - have_dict = filter_dict_having_none_value(interface, each) - commands.extend(self._clear_config(dict(), have_dict)) - commands.extend(self._set_config(interface, each, module)) - # Remove the duplicate interface call - commands = remove_duplicate_interface(commands) - - return commands - - def _state_overridden(self, want, have, module): - """ The command generator when state is overridden - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - not_in_have = set() - in_have = set() - for each in have: - for interface in want: - interface['name'] = normalize_interface(interface['name']) - if each['name'] == interface['name']: - in_have.add(interface['name']) - break - elif interface['name'] != each['name']: - not_in_have.add(interface['name']) - else: - # We didn't find a matching desired state, which means we can - # pretend we received an empty desired state. - interface = dict(name=each['name']) - commands.extend(self._clear_config(interface, each)) - continue - have_dict = filter_dict_having_none_value(interface, each) - commands.extend(self._clear_config(dict(), have_dict)) - commands.extend(self._set_config(interface, each, module)) - # Add the want interface that's not already configured in have interface - for each in (not_in_have - in_have): - for every in want: - interface = 'interface {0}'.format(every['name']) - if each and interface not in commands: - commands.extend(self._set_config(every, {}, module)) - - # Remove the duplicate interface call - commands = remove_duplicate_interface(commands) - - return commands - - def _state_merged(self, want, have, module): - """ The command generator when state is merged - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - commands = [] - - for interface in want: - interface['name'] = normalize_interface(interface['name']) - for each in have: - if each['name'] == interface['name']: - break - elif interface['name'] in each['name']: - break - else: - commands.extend(self._set_config(interface, {}, module)) - continue - commands.extend(self._set_config(interface, each, module)) - - return commands - - 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 interface in want: - interface['name'] = normalize_interface(interface['name']) - for each in have: - if each['name'] == interface['name']: - break - elif interface['name'] in each['name']: - break - else: - continue - interface = dict(name=interface['name']) - commands.extend(self._clear_config(interface, each)) - else: - for each in have: - want = dict() - commands.extend(self._clear_config(want, each)) - - return commands - - def _set_config(self, want, have, module): - # Set the interface config based on the want and have config - commands = [] - interface = 'interface ' + want['name'] - l2_protocol_bool = False - - # Get the diff b/w want and have - want_dict = dict_to_set(want) - have_dict = dict_to_set(have) - diff = want_dict - have_dict - - if diff: - # For merging with already configured l2protocol - if have.get('l2protocol') and len(have.get('l2protocol')) > 1: - l2_protocol_diff = [] - for each in want.get('l2protocol'): - for every in have.get('l2protocol'): - if every == each: - break - if each not in have.get('l2protocol'): - l2_protocol_diff.append(each) - l2_protocol_bool = True - l2protocol = tuple(l2_protocol_diff) - else: - l2protocol = {} - - diff = dict(diff) - wants_native = diff.get('native_vlan') - l2transport = diff.get('l2transport') - q_vlan = diff.get('q_vlan') - propagate = diff.get('propagate') - if l2_protocol_bool is False: - l2protocol = diff.get('l2protocol') - - if wants_native: - cmd = 'dot1q native vlan {0}'.format(wants_native) - add_command_to_config_list(interface, cmd, commands) - - if l2transport or l2protocol: - for each in l2protocol: - each = dict(each) - if isinstance(each, dict): - cmd = 'l2transport l2protocol {0} {1}'.format(list(each.keys())[0], list(each.values())[0]) - add_command_to_config_list(interface, cmd, commands) - if propagate and not have.get('propagate'): - cmd = 'l2transport propagate remote-status' - add_command_to_config_list(interface, cmd, commands) - elif want.get('l2transport') is False and (want.get('l2protocol') or want.get('propagate')): - module.fail_json(msg='L2transport L2protocol or Propagate can only be configured when ' - 'L2transport set to True!') - - if q_vlan and '.' in interface: - q_vlans = (" ".join(map(str, want.get('q_vlan')))) - if q_vlans != have.get('q_vlan'): - cmd = 'dot1q vlan {0}'.format(q_vlans) - add_command_to_config_list(interface, cmd, commands) - - return commands - - def _clear_config(self, want, have): - # Delete the interface config based on the want and have config - commands = [] - - if want.get('name'): - interface = 'interface ' + want['name'] - else: - interface = 'interface ' + have['name'] - if have.get('native_vlan'): - remove_command_from_config_list(interface, 'dot1q native vlan', commands) - - if have.get('q_vlan'): - remove_command_from_config_list(interface, 'encapsulation dot1q', commands) - - if have.get('l2protocol') and (want.get('l2protocol') is None or want.get('propagate') is None): - if 'no l2transport' not in commands: - remove_command_from_config_list(interface, 'l2transport', commands) - elif have.get('l2transport') and have.get('l2transport') != want.get('l2transport'): - if 'no l2transport' not in commands: - remove_command_from_config_list(interface, 'l2transport', commands) - - return commands diff --git a/lib/ansible/module_utils/network/iosxr/config/l3_interfaces/l3_interfaces.py b/lib/ansible/module_utils/network/iosxr/config/l3_interfaces/l3_interfaces.py deleted file mode 100644 index 555933ad85..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/l3_interfaces/l3_interfaces.py +++ /dev/null @@ -1,323 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The iosxr_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 - - -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.iosxr.facts.facts import Facts -from ansible.module_utils.network.iosxr.utils.utils import normalize_interface, dict_to_set -from ansible.module_utils.network.iosxr.utils.utils import remove_command_from_config_list, add_command_to_config_list -from ansible.module_utils.network.iosxr.utils.utils import filter_dict_having_none_value, remove_duplicate_interface -from ansible.module_utils.network.iosxr.utils.utils import validate_n_expand_ipv4, validate_ipv6 - - -class L3_Interfaces(ConfigBase): - """ - The iosxr_l3_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'l3_interfaces', - ] - - 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 [] - - return l3_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_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._connection.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 - """ - want = self._module.params['config'] - have = existing_l3_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 in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'overridden': - commands = self._state_overridden(want, have, self._module) - elif state == 'deleted': - commands = self._state_deleted(want, have) - elif state == 'merged': - commands = self._state_merged(want, have, self._module) - elif state == 'replaced': - commands = self._state_replaced(want, have, self._module) - - return commands - - def _state_replaced(self, want, have, module): - """ The command generator when state is replaced - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - - for interface in want: - interface['name'] = normalize_interface(interface['name']) - for each in have: - if each['name'] == interface['name']: - break - else: - commands.extend(self._set_config(interface, dict(), module)) - continue - have_dict = filter_dict_having_none_value(interface, each) - commands.extend(self._clear_config(dict(), have_dict)) - commands.extend(self._set_config(interface, each, module)) - # Remove the duplicate interface call - commands = remove_duplicate_interface(commands) - - return commands - - def _state_overridden(self, want, have, module): - """ The command generator when state is overridden - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - not_in_have = set() - in_have = set() - - for each in have: - for interface in want: - interface['name'] = normalize_interface(interface['name']) - if each['name'] == interface['name']: - in_have.add(interface['name']) - break - elif interface['name'] != each['name']: - not_in_have.add(interface['name']) - else: - # We didn't find a matching desired state, which means we can - # pretend we received an empty desired state. - interface = dict(name=each['name']) - kwargs = {'want': interface, 'have': each} - commands.extend(self._clear_config(**kwargs)) - continue - have_dict = filter_dict_having_none_value(interface, each) - commands.extend(self._clear_config(dict(), have_dict)) - commands.extend(self._set_config(interface, each, module)) - # Add the want interface that's not already configured in have interface - for each in (not_in_have - in_have): - for every in want: - interface = 'interface {0}'.format(every['name']) - if each and interface not in commands: - commands.extend(self._set_config(every, {}, module)) - # Remove the duplicate interface call - commands = remove_duplicate_interface(commands) - - return commands - - def _state_merged(self, want, have, module): - """ The command generator when state is merged - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - commands = [] - - for interface in want: - interface['name'] = normalize_interface(interface['name']) - for each in have: - if each['name'] == interface['name']: - break - else: - commands.extend(self._set_config(interface, dict(), module)) - continue - commands.extend(self._set_config(interface, each, module)) - - return commands - - 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 interface in want: - interface['name'] = normalize_interface(interface['name']) - for each in have: - if each['name'] == interface['name']: - break - elif interface['name'] in each['name']: - break - else: - continue - interface = dict(name=interface['name']) - commands.extend(self._clear_config(interface, each)) - else: - for each in have: - want = dict() - commands.extend(self._clear_config(want, each)) - - return commands - - def verify_diff_again(self, want, have): - """ - Verify the IPV4 difference again as sometimes due to - change in order of set, set difference may result into change, - when there's actually no difference between want and have - :param want: want_dict IPV4 - :param have: have_dict IPV4 - :return: diff - """ - diff = False - for each in want: - each_want = dict(each) - for every in have: - every_have = dict(every) - if each_want.get('address') != every_have.get('address') and \ - each_want.get('secondary') != every_have.get('secondary') and \ - len(each_want.keys()) == len(every_have.keys()): - diff = True - break - elif each_want.get('address') != every_have.get('address') and len(each_want.keys()) == len( - every_have.keys()): - diff = True - break - if diff: - break - - return diff - - def _set_config(self, want, have, module): - # Set the interface config based on the want and have config - commands = [] - interface = 'interface ' + want['name'] - - # To handle L3 IPV4 configuration - if want.get("ipv4"): - for each in want.get("ipv4"): - if each.get('address') != 'dhcp': - ip_addr_want = validate_n_expand_ipv4(module, each) - each['address'] = ip_addr_want - - # Get the diff b/w want and have - want_dict = dict_to_set(want) - have_dict = dict_to_set(have) - - # To handle L3 IPV4 configuration - want_ipv4 = dict(want_dict).get('ipv4') - have_ipv4 = dict(have_dict).get('ipv4') - if want_ipv4: - if have_ipv4: - diff_ipv4 = set(want_ipv4) - set(dict(have_dict).get('ipv4')) - if diff_ipv4: - diff_ipv4 = diff_ipv4 if self.verify_diff_again(want_ipv4, have_ipv4) else () - else: - diff_ipv4 = set(want_ipv4) - for each in diff_ipv4: - ipv4_dict = dict(each) - if ipv4_dict.get('address') != 'dhcp': - cmd = "ipv4 address {0}".format(ipv4_dict['address']) - if ipv4_dict.get("secondary"): - cmd += " secondary" - add_command_to_config_list(interface, cmd, commands) - - # To handle L3 IPV6 configuration - want_ipv6 = dict(want_dict).get('ipv6') - have_ipv6 = dict(have_dict).get('ipv6') - if want_ipv6: - if have_ipv6: - diff_ipv6 = set(want_ipv6) - set(have_ipv6) - else: - diff_ipv6 = set(want_ipv6) - for each in diff_ipv6: - ipv6_dict = dict(each) - validate_ipv6(ipv6_dict.get('address'), module) - cmd = "ipv6 address {0}".format(ipv6_dict.get('address')) - add_command_to_config_list(interface, cmd, commands) - - return commands - - def _clear_config(self, want, have): - # Delete the interface config based on the want and have config - count = 0 - commands = [] - if want.get('name'): - interface = 'interface ' + want['name'] - else: - interface = 'interface ' + have['name'] - - if have.get('ipv4') and want.get('ipv4'): - for each in have.get('ipv4'): - if each.get('secondary') and not (want.get('ipv4')[count].get('secondary')): - cmd = 'ipv4 address {0} secondary'.format(each.get('address')) - remove_command_from_config_list(interface, cmd, commands) - count += 1 - if have.get('ipv4') and not (want.get('ipv4')): - remove_command_from_config_list(interface, 'ipv4 address', commands) - if have.get('ipv6') and not (want.get('ipv6')): - remove_command_from_config_list(interface, 'ipv6 address', commands) - - return commands diff --git a/lib/ansible/module_utils/network/iosxr/config/lacp/lacp.py b/lib/ansible/module_utils/network/iosxr/config/lacp/lacp.py deleted file mode 100644 index 51acdb90f3..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/lacp/lacp.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) -""" -The iosxr_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 to_list -from ansible.module_utils.network.iosxr.facts.facts import Facts -from ansible.module_utils.network.common.utils import dict_diff -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.utils import remove_empties -from ansible.module_utils.network.iosxr. \ - utils.utils import flatten_dict - - -class Lacp(ConfigBase): - """ - The iosxr_lacp class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lacp', - ] - - 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 = self._module.params.get('config') - if not want: - want = {} - 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'] - if state in ('merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'deleted': - commands = self._state_deleted(want, have) - elif state == 'merged': - commands = self._state_merged(want, have) - elif state == 'replaced': - commands = self._state_replaced(want, have) - - 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 = [] - - commands.extend( - Lacp._state_deleted(want, have) - ) - - commands.extend( - Lacp._state_merged(want, have) - ) - - return commands - - @staticmethod - def _state_merged(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 = [] - - updates = dict_diff(have, want) - if updates: - for key, value in iteritems(flatten_dict(remove_empties(updates['system']))): - commands.append('lacp system {0} {1}'.format(key.replace('address', 'mac'), value)) - - return 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 = [] - - for x in [k for k in have.get('system', {}) if k not in remove_empties(want.get('system', {}))]: - commands.append('no lacp system {0}'.format(x)) - - return commands diff --git a/lib/ansible/module_utils/network/iosxr/config/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/iosxr/config/lacp_interfaces/lacp_interfaces.py deleted file mode 100644 index 34e137269b..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/lacp_interfaces/lacp_interfaces.py +++ /dev/null @@ -1,264 +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 iosxr_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 -from ansible.module_utils.network.iosxr.facts.facts import Facts -from ansible.module_utils.network.common.utils import dict_diff, remove_empties -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.utils import search_obj_in_list -from ansible.module_utils.network.iosxr.utils.utils import dict_delete, pad_commands, flatten_dict - - -class Lacp_interfaces(ConfigBase): - """ - The iosxr_lacp_interfaces class - """ - - gather_subset = [ - '!all', - '!min', - ] - - gather_network_resources = [ - 'lacp_interfaces', - ] - - 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 - """ - want = self._module.params['config'] - 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 - """ - commands = [] - state = self._module.params['state'] - - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'overridden': - commands.extend( - Lacp_interfaces._state_overridden( - want, have - ) - ) - - elif state == 'deleted': - if not want: - for intf in have: - commands.extend( - Lacp_interfaces._state_deleted( - {'name': intf['name']}, - intf - ) - ) - else: - for item in want: - obj_in_have = search_obj_in_list(item['name'], have) - commands.extend( - Lacp_interfaces._state_deleted( - item, obj_in_have - ) - ) - - else: - for item in want: - name = item['name'] - obj_in_have = search_obj_in_list(name, have) - - if state == 'merged': - commands.extend( - Lacp_interfaces._state_merged( - item, obj_in_have - ) - ) - - elif state == 'replaced': - commands.extend( - Lacp_interfaces._state_replaced( - item, obj_in_have - ) - ) - - 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 = [] - replaced_commands = [] - merged_commands = [] - - if have: - replaced_commands = Lacp_interfaces._state_deleted(want, have) - - merged_commands = Lacp_interfaces._state_merged(want, have) - - if merged_commands and replaced_commands: - del merged_commands[0] - - commands.extend(replaced_commands) - commands.extend(merged_commands) - - return commands - - @staticmethod - def _state_overridden(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 intf in have: - intf_in_want = search_obj_in_list(intf['name'], want) - if not intf_in_want: - commands.extend(Lacp_interfaces._state_deleted({'name': intf['name']}, intf)) - - for intf in want: - intf_in_have = search_obj_in_list(intf['name'], have) - commands.extend(Lacp_interfaces._state_replaced(intf, intf_in_have)) - - return commands - - @staticmethod - def _state_merged(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 = [] - - if not have: - have = {'name': want['name']} - - for key, value in iteritems(flatten_dict(remove_empties(dict_diff(have, want)))): - commands.append(Lacp_interfaces._compute_commands(key, value)) - - if commands: - pad_commands(commands, want['name']) - - return 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 = [] - - for key, value in iteritems(flatten_dict(dict_delete(have, remove_empties(want)))): - commands.append(Lacp_interfaces._compute_commands(key, value, remove=True)) - - if commands: - pad_commands(commands, have['name']) - - return commands - - @staticmethod - def _compute_commands(key, value, remove=False): - if key == "churn_logging": - cmd = "lacp churn logging {0}".format(value) - - elif key == "collector_max_delay": - cmd = "lacp collector-max-delay {0}".format(value) - - elif key == "period": - cmd = "lacp period {0}".format(value) - - elif key == "switchover_suppress_flaps": - cmd = "lacp switchover suppress-flaps {0}".format(value) - - elif key == 'mac': - cmd = "lacp system mac {0}".format(value) - - elif key == 'priority': - cmd = "lacp system priority {0}".format(value) - - if remove: - cmd = "no " + cmd - - return cmd diff --git a/lib/ansible/module_utils/network/iosxr/config/lag_interfaces/lag_interfaces.py b/lib/ansible/module_utils/network/iosxr/config/lag_interfaces/lag_interfaces.py deleted file mode 100644 index 432fcd52a0..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/lag_interfaces/lag_interfaces.py +++ /dev/null @@ -1,386 +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 iosxr_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 copy import deepcopy -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.cfg.base import ConfigBase -from ansible.module_utils.network.iosxr.facts.facts import Facts -from ansible.module_utils.network.common.utils \ - import ( - to_list, - dict_diff, - remove_empties, - search_obj_in_list, - param_list_to_dict - ) -from ansible.module_utils.network.iosxr.utils.utils \ - import ( - diff_list_of_dicts, - pad_commands, - flatten_dict, - dict_delete, - normalize_interface - ) - - -class Lag_interfaces(ConfigBase): - """ - The iosxr_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} - warnings = list() - commands = 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: - self._connection.edit_config(commands) - 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['config'] - if want: - for item in want: - item['name'] = normalize_interface(item['name']) - if 'members' in want and want['members']: - for item in want['members']: - item.update({ - 'member': normalize_interface(item['member']), - 'mode': item['mode'] - }) - 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 = [] - - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'overridden': - commands.extend(self._state_overridden(want, have)) - - elif state == 'deleted': - commands.extend(self._state_deleted(want, have)) - - else: - # Instead of passing entire want and have - # list of dictionaries to the respective - # _state_* methods we are passing the want - # and have dictionaries per interface - for item in want: - name = item['name'] - obj_in_have = search_obj_in_list(name, have) - - if state == 'merged': - commands.extend(self._state_merged(item, obj_in_have)) - - elif state == 'replaced': - commands.extend(self._state_replaced(item, obj_in_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 = [] - if have: - commands.extend(self._render_bundle_del_commands(want, have)) - commands.extend(self._render_bundle_updates(want, have)) - - if commands or have == {}: - pad_commands(commands, want['name']) - - if have: - commands.extend(self._render_interface_del_commands(want, have)) - commands.extend(self._render_interface_updates(want, have)) - - 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 have_intf in have: - intf_in_want = search_obj_in_list(have_intf['name'], want) - if not intf_in_want: - commands.extend(self._purge_attribs(have_intf)) - - for intf in want: - intf_in_have = search_obj_in_list(intf['name'], have) - commands.extend(self._state_replaced(intf, intf_in_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 - """ - commands = [] - commands.extend(self._render_bundle_updates(want, have)) - - if commands or have == {}: - pad_commands(commands, want['name']) - - commands.extend(self._render_interface_updates(want, have)) - - return commands - - 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 want: - for item in have: - commands.extend(self._purge_attribs(intf=item)) - else: - for item in want: - name = item['name'] - obj_in_have = search_obj_in_list(name, have) - if not obj_in_have: - self._module.fail_json( - msg=('interface {0} does not exist'.format(name))) - commands.extend(self._purge_attribs(intf=obj_in_have)) - - return commands - - def _render_bundle_updates(self, want, have): - """ The command generator for updates to bundles - :rtype: A list - :returns: the commands necessary to update bundles - """ - commands = [] - if not have: - have = {'name': want['name']} - - want_copy = deepcopy(want) - have_copy = deepcopy(have) - - want_copy.pop('members', []) - have_copy.pop('members', []) - - bundle_updates = dict_diff(have_copy, want_copy) - - if bundle_updates: - for key, value in iteritems( - flatten_dict(remove_empties(bundle_updates))): - commands.append(self._compute_commands(key=key, value=value)) - - return commands - - def _render_interface_updates(self, want, have): - """ The command generator for updates to member - interfaces - :rtype: A list - :returns: the commands necessary to update member - interfaces - """ - commands = [] - - if not have: - have = {'name': want['name']} - - member_diff = diff_list_of_dicts(want['members'], - have.get('members', [])) - - for diff in member_diff: - diff_cmd = [] - bundle_cmd = 'bundle id {0}'.format( - want['name'].split('Bundle-Ether')[1]) - if diff.get('mode'): - bundle_cmd += ' mode {0}'.format(diff.get('mode')) - diff_cmd.append(bundle_cmd) - pad_commands(diff_cmd, diff['member']) - commands.extend(diff_cmd) - - return commands - - def _render_bundle_del_commands(self, want, have): - """ The command generator for delete commands - w.r.t bundles - :rtype: A list - :returns: the commands necessary to update member - interfaces - """ - commands = [] - if not want: - want = {'name': have['name']} - - want_copy = deepcopy(want) - have_copy = deepcopy(have) - want_copy.pop('members', []) - have_copy.pop('members', []) - - to_delete = dict_delete(have_copy, remove_empties(want_copy)) - if to_delete: - for key, value in iteritems(flatten_dict( - remove_empties(to_delete))): - commands.append( - self._compute_commands(key=key, value=value, remove=True)) - - return commands - - def _render_interface_del_commands(self, want, have): - """ The command generator for delete commands - w.r.t member interfaces - :rtype: A list - :returns: the commands necessary to update member - interfaces - """ - commands = [] - if not want: - want = {} - have_members = have.get('members') - - if have_members: - have_members = param_list_to_dict(deepcopy(have_members), unique_key='member') - want_members = param_list_to_dict(deepcopy(want).get('members', []), unique_key='member') - - for key in have_members: - if key not in want_members: - member_cmd = ['no bundle id'] - pad_commands(member_cmd, key) - commands.extend(member_cmd) - - return commands - - def _purge_attribs(self, intf): - """ The command generator for purging attributes - :rtype: A list - :returns: the commands necessary to purge attributes - """ - commands = [] - have_copy = deepcopy(intf) - members = have_copy.pop('members', []) - - to_delete = dict_delete(have_copy, remove_empties({'name': have_copy['name']})) - if to_delete: - for key, value in iteritems(flatten_dict(remove_empties(to_delete))): - commands.append(self._compute_commands(key=key, value=value, remove=True)) - - if commands: - pad_commands(commands, intf['name']) - - if members: - members = param_list_to_dict(deepcopy(members), unique_key='member') - for key in members: - member_cmd = ['no bundle id'] - pad_commands(member_cmd, key) - commands.extend(member_cmd) - - return commands - - def _compute_commands(self, key, value, remove=False): - """ The method generates LAG commands based on the - key, value passed. When remove is set to True, - the command is negated. - :rtype: str - :returns: a command based on the `key`, `value` pair - passed and the value of `remove` - """ - if key == "mode": - cmd = "lacp mode {0}".format(value) - - elif key == "load_balancing_hash": - cmd = "bundle load-balancing hash {0}".format(value) - - elif key == "max_active": - cmd = "bundle maximum-active links {0}".format(value) - - elif key == "min_active": - cmd = "bundle minimum-active links {0}".format(value) - - if remove: - cmd = "no {0}".format(cmd) - - return cmd diff --git a/lib/ansible/module_utils/network/iosxr/config/lldp_global/lldp_global.py b/lib/ansible/module_utils/network/iosxr/config/lldp_global/lldp_global.py deleted file mode 100644 index 812daad5ed..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/lldp_global/lldp_global.py +++ /dev/null @@ -1,188 +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 iosxr_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 to_list, dict_diff, remove_empties -from ansible.module_utils.network.iosxr.facts.facts import Facts -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.iosxr. \ - utils.utils import flatten_dict, dict_delete - - -class Lldp_global(ConfigBase): - """ - The iosxr_lldp 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_facts = facts['ansible_network_resources'].get('lldp_global') - if not lldp_facts: - return {} - return lldp_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - commands = 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'] = existing_lldp_global_facts - if result['changed']: - result['after'] = 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'] - if not want and self._module.params['state'] == 'deleted': - want = {} - have = existing_lldp_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'] - if state in ('merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'deleted': - commands = self._state_deleted(want, 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 = [] - - commands.extend( - self._state_deleted(want, have) - ) - - commands.extend( - self._state_merged(want, 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 - """ - commands = [] - updates = dict_diff(have, want) - if updates: - for key, value in iteritems(flatten_dict(remove_empties(updates))): - commands.append(self._compute_commands(key, value)) - - return commands - - 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 = [] - for key, value in iteritems(flatten_dict(dict_delete(have, remove_empties(want)))): - cmd = self._compute_commands(key, value, remove=True) - if cmd: - commands.append(cmd) - - return commands - - def _compute_commands(self, key, value=None, remove=False): - if key in ['holdtime', 'reinit', 'timer']: - cmd = 'lldp {0} {1}'.format(key, value) - if remove: - return 'no {0}'.format(cmd) - else: - return cmd - - elif key == 'subinterfaces': - cmd = 'lldp subinterfaces enable' - if (value and not remove): - return cmd - elif (not value and not remove) or (value and remove): - return 'no {0}'.format(cmd) - - else: - cmd = 'lldp tlv-select {0} disable'.format(key.replace('_', '-')) - if (not value and not remove): - return cmd - elif (value and not remove) or (not value and remove): - return 'no {0}'.format(cmd) diff --git a/lib/ansible/module_utils/network/iosxr/config/lldp_interfaces/lldp_interfaces.py b/lib/ansible/module_utils/network/iosxr/config/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index 2938b3c026..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,247 +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 iosxr_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, search_obj_in_list, dict_diff, remove_empties -from ansible.module_utils.network.iosxr.facts.facts import Facts -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.iosxr.utils.utils import dict_delete, pad_commands, flatten_dict - - -class Lldp_interfaces(ConfigBase): - """ - The iosxr_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): - """ 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_interfaces_facts = facts['ansible_network_resources'].get('lldp_interfaces') - if not lldp_interfaces_facts: - return [] - return lldp_interfaces_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {'changed': False} - warnings = list() - commands = list() - - existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - commands.extend(self.set_config(existing_lldp_interfaces_facts)) - if commands: - if not self._module.check_mode: - self._connection.edit_config(commands) - result['changed'] = True - result['commands'] = commands - - changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - - result['before'] = existing_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 - """ - want = self._module.params['config'] - 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 - """ - state = self._module.params['state'] - commands = [] - if state in ('overridden', 'merged', 'replaced') and not want: - self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state)) - - if state == 'overridden': - commands.extend( - self._state_overridden( - want, have - ) - ) - - elif state == 'deleted': - if not want: - for intf in have: - commands.extend( - self._state_deleted( - {'name': intf['name']}, - intf - ) - ) - else: - for item in want: - obj_in_have = search_obj_in_list(item['name'], have) - commands.extend( - self._state_deleted( - item, obj_in_have - ) - ) - - else: - for item in want: - name = item['name'] - obj_in_have = search_obj_in_list(name, have) - - if state == 'merged': - commands.extend( - self._state_merged( - item, obj_in_have - ) - ) - - elif state == 'replaced': - commands.extend( - self._state_replaced( - item, obj_in_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 = [] - replaced_commands = [] - merged_commands = [] - - if have: - replaced_commands = self._state_deleted(want, have) - - merged_commands = self._state_merged(want, have) - - if merged_commands and replaced_commands: - del merged_commands[0] - - 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 intf in have: - intf_in_want = search_obj_in_list(intf['name'], want) - if not intf_in_want: - commands.extend(self._state_deleted({'name': intf['name']}, intf)) - - for intf in want: - intf_in_have = search_obj_in_list(intf['name'], have) - commands.extend(self._state_replaced(intf, intf_in_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 - """ - commands = [] - if not have: - have = {'name': want['name']} - - for key, value in iteritems(flatten_dict(remove_empties(dict_diff(have, want)))): - commands.append(self._compute_commands(key, value)) - - if commands: - pad_commands(commands, want['name']) - - return commands - - 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 = [] - - for key, value in iteritems(flatten_dict(dict_delete(have, remove_empties(want)))): - commands.append(self._compute_commands(key, value, remove=True)) - - if commands: - pad_commands(commands, have['name']) - - return commands - - def _compute_commands(self, key, value=None, remove=False): - if key == 'mac_address': - cmd = 'lldp destination mac-address {0}'.format(value) - if remove: - return 'no {0}'.format(cmd) - else: - return cmd - - else: - cmd = 'lldp {0} disable'.format(key) - if (not value and not remove): - return cmd - elif (value and not remove) or (not value and remove): - return 'no {0}'.format(cmd) diff --git a/lib/ansible/module_utils/network/iosxr/config/static_routes/static_routes.py b/lib/ansible/module_utils/network/iosxr/config/static_routes/static_routes.py deleted file mode 100644 index 1c843b9166..0000000000 --- a/lib/ansible/module_utils/network/iosxr/config/static_routes/static_routes.py +++ /dev/null @@ -1,560 +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 iosxr_static_routes 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.iosxr.facts.facts import Facts -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.utils import ( - search_obj_in_list, - remove_empties, - dict_diff, - dict_merge, -) - - -class Static_routes(ConfigBase): - """ - The iosxr_static_routes class - """ - - gather_subset = [ - "!all", - "!min", - ] - - gather_network_resources = [ - "static_routes", - ] - - def __init__(self, module): - super(Static_routes, self).__init__(module) - - def get_static_routes_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 - ) - static_routes_facts = facts["ansible_network_resources"].get("static_routes") - if not static_routes_facts: - return [] - return static_routes_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {"changed": False} - warnings = list() - commands = list() - - if self.state in self.ACTION_STATES: - existing_static_routes_facts = self.get_static_routes_facts() - else: - existing_static_routes_facts = [] - - if self.state in self.ACTION_STATES or self.state == "rendered": - commands.extend(self.set_config(existing_static_routes_facts)) - - if commands and self.state in self.ACTION_STATES: - if not self._module.check_mode: - self._connection.edit_config(commands) - result["changed"] = True - - if self.state in self.ACTION_STATES: - result["commands"] = commands - - if self.state in self.ACTION_STATES or self.state == "gathered": - changed_static_routes_facts = self.get_static_routes_facts() - - elif self.state == "rendered": - result["rendered"] = commands - - elif self.state == "parsed": - running_config = self._module.params["running_config"] - if not running_config: - self._module.fail_json( - msg="value of running_config parameter must not be empty for state parsed" - ) - result["parsed"] = self.get_static_routes_facts(data=running_config) - - if self.state in self.ACTION_STATES: - result["before"] = existing_static_routes_facts - if result["changed"]: - result["after"] = changed_static_routes_facts - - elif self.state == "gathered": - result["gathered"] = changed_static_routes_facts - - result["warnings"] = warnings - return result - - def set_config(self, existing_static_routes_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_static_routes_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 in ("overridden", "merged", "replaced", "rendered") and not want: - self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - state - ) - ) - - if state == "overridden": - commands.extend(self._state_overridden(want, have)) - - elif state == "deleted": - if not want: - if len(have) >= 1: - return "no router static" - - else: - for w_item in want: - obj_in_have = self._find_vrf(w_item, have) - if obj_in_have: - commands.extend( - self._state_deleted(remove_empties(w_item), obj_in_have) - ) - - else: - for w_item in want: - obj_in_have = self._find_vrf(w_item, have) - if state == "merged" or self.state == "rendered": - commands.extend( - self._state_merged(remove_empties(w_item), obj_in_have) - ) - - elif state == "replaced": - commands.extend( - self._state_replaced(remove_empties(w_item), obj_in_have) - ) - - if commands: - commands.insert(0, "router static") - - 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 = [] - - for want_afi in want.get("address_families", []): - have_afi = ( - self.find_af_context(want_afi, have.get("address_families", [])) or {} - ) - update_commands = [] - for want_route in want_afi.get("routes", []): - have_route = ( - search_obj_in_list( - want_route["dest"], have_afi.get("routes", []), key="dest" - ) - or {} - ) - - rotated_have_next_hops = self.rotate_next_hops( - have_route.get("next_hops", {}) - ) - rotated_want_next_hops = self.rotate_next_hops( - want_route.get("next_hops", {}) - ) - - for key in rotated_have_next_hops.keys(): - if key not in rotated_want_next_hops: - cmd = "no {0}".format(want_route["dest"]) - for item in key: - if "." in item or ":" in item or "/" in item: - cmd += " {0}".format(item) - else: - cmd += " vrf {0}".format(item) - update_commands.append(cmd) - - for key, value in iteritems(rotated_want_next_hops): - if key in rotated_have_next_hops: - existing = True - have_exit_point_attribs = rotated_have_next_hops[key] - - else: - existing = False - have_exit_point_attribs = {} - - updates = dict_diff(have_exit_point_attribs, value) - - if updates or not existing: - update_commands.append( - self._compute_commands( - dest=want_route["dest"], next_hop=key, updates=updates - ) - ) - - if update_commands: - update_commands.insert( - 0, - "address-family {0} {1}".format(want_afi["afi"], want_afi["safi"]), - ) - commands.extend(update_commands) - - if "vrf" in want and update_commands: - commands.insert(0, "vrf {0}".format(want["vrf"])) - - 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 = [] - - # Iterate through all the entries, i.e., VRFs and Global entry in have - # and fully remove the ones that are not present in want and then call - # replaced - - for h_item in have: - w_item = self._find_vrf(h_item, want) - - # Delete all the top-level keys (VRFs/Global Route Entry) that are - # not specified in want. - if not w_item: - if "vrf" in h_item: - commands.append("no vrf {0}".format(h_item["vrf"])) - else: - for have_afi in h_item.get("address_families", []): - commands.append( - "no address-family {0} {1}".format( - have_afi["afi"], have_afi["safi"] - ) - ) - - # For VRFs/Global Entry present in want, we also need to delete extraneous routes - # from them. We cannot reuse `_state_replaced` for this purpose since its scope is - # limited to replacing a single `dest`. - else: - del_cmds = [] - for have_afi in h_item.get("address_families", []): - want_afi = ( - self.find_af_context( - have_afi, w_item.get("address_families", []) - ) - or {} - ) - update_commands = [] - for h_route in have_afi.get("routes", []): - w_route = ( - search_obj_in_list( - h_route["dest"], want_afi.get("routes", []), key="dest" - ) - or {} - ) - if not w_route: - update_commands.append("no {0}".format(h_route["dest"])) - - if update_commands: - update_commands.insert( - 0, - "address-family {0} {1}".format( - want_afi["afi"], want_afi["safi"] - ), - ) - del_cmds.extend(update_commands) - - if "vrf" in want and update_commands: - del_cmds.insert(0, "vrf {0}".format(want["vrf"])) - - commands.extend(del_cmds) - - # We finally call `_state_replaced` to replace exiting `dest` entries - # or add new ones as specified in want. - for w_item in want: - h_item = self._find_vrf(w_item, have) - commands.extend(self._state_replaced(remove_empties(w_item), h_item)) - - 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 - """ - commands = [] - - for want_afi in want.get("address_families", []): - have_afi = ( - self.find_af_context(want_afi, have.get("address_families", [])) or {} - ) - - update_commands = [] - for want_route in want_afi.get("routes", []): - have_route = ( - search_obj_in_list( - want_route["dest"], have_afi.get("routes", []), key="dest" - ) - or {} - ) - - # convert the next_hops list of dictionaries to dictionary of - # dictionaries with (`dest_vrf`, `forward_router_address`, `interface`) tuple - # being the key for each dictionary. - # a combination of these 3 attributes uniquely identifies a route entry. - # in case `dest_vrf` is not specified, `forward_router_address` and `interface` - # become the unique identifier - rotated_have_next_hops = self.rotate_next_hops( - have_route.get("next_hops", {}) - ) - rotated_want_next_hops = self.rotate_next_hops( - want_route.get("next_hops", {}) - ) - - # for every dict in the want next_hops dictionaries, if the key - # is present in `rotated_have_next_hops`, we set `existing` to True, - # which means the the given want exit point exists and we run dict_diff - # on `value` which is basically all the other attributes of the exit point - # if the key is not present, it means that this is a new exit point - for key, value in iteritems(rotated_want_next_hops): - if key in rotated_have_next_hops: - existing = True - have_exit_point_attribs = rotated_have_next_hops[key] - - else: - existing = False - have_exit_point_attribs = {} - - updates = dict_diff(have_exit_point_attribs, value) - if updates or not existing: - update_commands.append( - self._compute_commands( - dest=want_route["dest"], - next_hop=key, - # dict_merge() is necessary to make sure that we - # don't end up overridding the entry and also to - # allow incremental updates - updates=dict_merge( - rotated_have_next_hops.get(key, {}), updates - ), - ) - ) - - if update_commands: - update_commands.insert( - 0, - "address-family {0} {1}".format(want_afi["afi"], want_afi["safi"]), - ) - commands.extend(update_commands) - - if "vrf" in want and update_commands: - commands.insert(0, "vrf {0}".format(want["vrf"])) - - return commands - - 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 "address_families" not in want: - return ["no vrf {0}".format(want["vrf"])] - - else: - for want_afi in want.get("address_families", []): - update_commands = [] - have_afi = ( - self.find_af_context(want_afi, have.get("address_families", [])) - or {} - ) - if have_afi: - if "routes" not in want_afi: - commands.append( - "no address-family {0} {1}".format( - have_afi["afi"], have_afi["safi"] - ) - ) - else: - for want_route in want_afi.get("routes", []): - have_route = ( - search_obj_in_list( - want_route["dest"], - have_afi.get("routes", []), - key="dest", - ) - or {} - ) - if have_route: - if "next_hops" not in want_route: - update_commands.append( - "no {0}".format(want_route["dest"]) - ) - else: - rotated_have_next_hops = self.rotate_next_hops( - have_route.get("next_hops", {}) - ) - rotated_want_next_hops = self.rotate_next_hops( - want_route.get("next_hops", {}) - ) - - for key in rotated_want_next_hops.keys(): - if key in rotated_have_next_hops: - cmd = "no {0}".format(want_route["dest"]) - for item in key: - if ( - "." in item - or ":" in item - or "/" in item - ): - cmd += " {0}".format(item) - else: - cmd += " vrf {0}".format(item) - update_commands.append(cmd) - - if update_commands: - update_commands.insert( - 0, - "address-family {0} {1}".format( - want_afi["afi"], want_afi["safi"] - ), - ) - commands.extend(update_commands) - - if "vrf" in want and commands: - commands.insert(0, "vrf {0}".format(want["vrf"])) - - return commands - - def _find_vrf(self, item, entries): - """ This method iterates through the items - in `entries` and returns the object that - matches `item`. - - :rtype: A dict - :returns: the obj in `entries` that matches `item` - """ - obj = {} - afi = item.get("vrf") - - if afi: - obj = search_obj_in_list(afi, entries, key="vrf") or {} - else: - for x in entries: - if "vrf" not in remove_empties(x): - obj = x - break - return obj - - def find_af_context(self, want_af_context, have_address_families): - """ This method iterates through the have AFs - and returns the one that matches the want AF - - :rtype: A dict - :returns: the corresponding AF in have AFs - that matches the want AF - """ - for have_af in have_address_families: - if ( - have_af["afi"] == want_af_context["afi"] - and have_af["safi"] == want_af_context["safi"] - ): - return have_af - - def rotate_next_hops(self, next_hops): - """ This method iterates through the list of - next hops for a given destination network - and converts it to a dictionary of dictionaries. - Each dictionary has a primary key indicated by the - tuple of `dest_vrf`, `forward_router_address` and - `interface` and the value of this key is a dictionary - that contains all the other attributes of the next hop. - - :rtype: A dict - :returns: A next_hops list in a dictionary of dictionaries format - """ - next_hops_dict = {} - - for entry in next_hops: - entry = entry.copy() - key_list = [] - - for x in ["dest_vrf", "forward_router_address", "interface"]: - if entry.get(x): - key_list.append(entry.pop(x)) - - key = tuple(key_list) - next_hops_dict[key] = entry - - return next_hops_dict - - def _compute_commands(self, dest, next_hop, updates=None): - """ This method computes a static route entry command - from the specified `dest`, `next_hop` and `updates` - - :rtype: A str - :returns: A platform specific static routes command - """ - if not updates: - updates = {} - - command = dest - - for x in next_hop: - if "." in x or ":" in x or "/" in x: - command += " {0}".format(x) - else: - command += " vrf {0}".format(x) - - for key in sorted(updates): - if key == "admin_distance": - command += " {0}".format(updates[key]) - else: - command += " {0} {1}".format(key, updates[key]) - - return command diff --git a/lib/ansible/module_utils/network/iosxr/facts/acl_interfaces/acl_interfaces.py b/lib/ansible/module_utils/network/iosxr/facts/acl_interfaces/acl_interfaces.py deleted file mode 100644 index 93aa174b16..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/acl_interfaces/acl_interfaces.py +++ /dev/null @@ -1,104 +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 iosxr 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.six import iteritems -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.iosxr.argspec.acl_interfaces.acl_interfaces import Acl_interfacesArgs - - -class Acl_interfacesFacts(object): - """ The iosxr 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 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 = connection.get_config(flags='interface') - - interfaces = data.split('interface ') - - objs = [] - for interface in interfaces: - obj = self.render_config(self.generated_spec, interface) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('acl_interfaces', None) - facts = {} - facts['acl_interfaces'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['acl_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) - config['access_groups'] = [] - map_dir = {'ingress': 'in', 'egress': 'out'} - - match = re.search(r'(?:preconfigure)*(?:\s*)(\S+)', conf, re.M) - if match: - config['name'] = match.group(1) - acls = {'ipv4': [], 'ipv6': []} - for item in conf.split('\n'): - item = item.strip() - if item.startswith('ipv4 access-group'): - acls['ipv4'].append(item) - elif item.startswith('ipv6 access-group'): - acls['ipv6'].append(item) - - for key, value in iteritems(acls): - if value: - entry = {'afi': key, 'acls': []} - for item in value: - entry['acls'].append({'name': item.split()[2], 'direction': map_dir[item.split()[3]]}) - config['access_groups'].append(entry) - - config['access_groups'] = sorted(config['access_groups'], key=lambda i: i['afi']) - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/iosxr/facts/acls/acls.py b/lib/ansible/module_utils/network/iosxr/facts/acls/acls.py deleted file mode 100644 index 685bb67a94..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/acls/acls.py +++ /dev/null @@ -1,380 +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 iosxr 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 - -from copy import deepcopy - -from collections import deque -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.iosxr.argspec.acls.acls import AclsArgs -from ansible.module_utils.network.iosxr.utils.utils import isipaddress - -PROTOCOL_OPTIONS = { - 'tcp': ( - 'ack', - 'fin', - 'psh', - 'rst', - 'syn', - 'urg', - 'established', - ), - 'igmp': ('dvmrp', 'host_query', 'host_report', 'mtrace', 'mtrace_response', - 'pim', 'trace', 'v2_leave', 'v2_report', 'v3_report'), - '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'), - 'icmpv6': - ('address_unreachable', 'administratively_prohibited', - 'beyond_scope_of_source_address', 'destination_unreachable', 'echo', - 'echo_reply', 'erroneous_header_field', 'group_membership_query', - 'group_membership_report', 'group_membership_termination', - 'host_unreachable', 'nd_na', 'nd_ns', 'neighbor_redirect', - 'no_route_to_destination', 'node_information_request_is_refused', - 'node_information_successful_reply', 'packet_too_big', - 'parameter_problem', 'port_unreachable', 'query_subject_is_IPv4address', - 'query_subject_is_IPv6address', 'query_subject_is_domainname', - 'reassembly_timeout', 'redirect', 'router_advertisement', - 'router_renumbering', 'router_solicitation', 'rr_command', 'rr_result', - 'rr_seqnum_reset', 'time_exceeded', 'ttl_exceeded', 'unknown_query_type', - 'unreachable', 'unrecognized_next_header', 'unrecognized_option', - 'whoareyou_reply', 'whoareyou_request') -} - - -class AclsFacts(object): - """ The iosxr 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 access-lists afi-all') - - 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) - - objs = [] - - acl_lines = data.splitlines() - - # We iterate through the data and create a list of ACLs - # where each ACL is a dictionary in the following format: - # {'afi': 'ipv4', 'name': 'acl_1', 'aces': ['10 permit 172.16.0.0 0.0.255.255', '20 deny 192.168.34.0 0.0.0.255']} - if acl_lines: - acl, acls = {}, [] - for line in acl_lines: - if line.startswith('ip'): - if acl: - acls.append(acl) - acl = {'aces': []} - acl['afi'], acl['name'] = line.split()[0], line.split()[2] - else: - acl['aces'].append(line.strip()) - acls.append(acl) - - # Here we group the ACLs based on AFI - # { - # 'ipv6': [{'aces': ['10 permit ipv6 2000::/12 any'], 'name': 'acl_2'}], - # 'ipv4': [{'aces': ['10 permit 172.16.0.0 0.0.255.255', '20 deny 192.168.34.0 0.0.0.255'], 'name': 'acl_1'}, - # {'aces': ['20 deny 10.0.0.0/8 log'], 'name': 'acl_3'}] - # } - - grouped_acls = {'ipv4': [], 'ipv6': []} - for acl in acls: - acl_copy = deepcopy(acl) - del acl_copy['afi'] - grouped_acls[acl['afi']].append(acl_copy) - - # Now that we have the ACLs in a fairly structured format, - # we pass it on to render_config to convert it to model spec - for key, value in iteritems(grouped_acls): - obj = self.render_config(self.generated_spec, value) - if obj: - obj['afi'] = key - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('acls', None) - facts = {} - - facts['acls'] = [] - params = utils.validate_config(self.argument_spec, {'config': objs}) - for cfg in params['config']: - facts['acls'].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) - config['acls'] = [] - - for item in conf: - acl = {'name': item['name']} - aces = item.get('aces', []) - if aces: - acl['aces'] = [] - for ace in aces: - acl['aces'].append(self._render_ace(ace)) - config['acls'].append(acl) - - return utils.remove_empties(config) - - def _render_ace(self, ace): - """ - Parses an Access Control Entry (ACE) and converts it - into model spec - - :param ace: An ACE in device specific format - :rtype: dictionary - :returns: The ACE in structured format - """ - - def __parse_src_dest(rendered_ace, ace_queue, direction): - """ - Parses the ACE queue and populates address, wildcard_bits, - host or any keys in the source/destination dictionary of - ace dictionary, i.e., `rendered_ace`. - - :param rendered_ace: The dictionary containing the ACE in structured format - :param ace_queue: The ACE queue - :param direction: Specifies whether to populate `source` or `destination` - dictionary - """ - element = ace_queue.popleft() - if element == 'host': - rendered_ace[direction] = {'host': ace_queue.popleft()} - - elif element == 'any': - rendered_ace[direction] = {'any': True} - - elif '/' in element: - rendered_ace[direction] = { - 'prefix': element - } - - elif isipaddress(element): - rendered_ace[direction] = { - 'address': element, - 'wildcard_bits': ace_queue.popleft() - } - - def __parse_port_protocol(rendered_ace, ace_queue, direction): - """ - Parses the ACE queue and populates `port_protocol` dictionary in the - ACE dictionary, i.e., `rendered_ace`. - - :param rendered_ace: The dictionary containing the ACE in structured format - :param ace_queue: The ACE queue - :param direction: Specifies whether to populate `source` or `destination` - dictionary - """ - if len(ace_queue) > 0 and ace_queue[0] in ('eq', 'gt', 'lt', 'neq', - 'range'): - element = ace_queue.popleft() - port_protocol = {} - - if element == 'range': - port_protocol['range'] = { - 'start': ace_queue.popleft(), - 'end': ace_queue.popleft() - } - else: - port_protocol[element] = ace_queue.popleft() - - rendered_ace[direction]['port_protocol'] = port_protocol - - def __parse_protocol_options(rendered_ace, ace_queue, protocol): - """ - Parses the ACE queue and populates protocol specific options - of the required dictionary and updates the ACE dictionary, i.e., - `rendered_ace`. - - :param rendered_ace: The dictionary containing the ACE in structured format - :param ace_queue: The ACE queue - :param protocol: Specifies the protocol that will be populated under - `protocol_options` dictionary - """ - if len(ace_queue) > 0: - protocol_options = {protocol: {}} - - for match_bit in PROTOCOL_OPTIONS.get(protocol, ()): - if match_bit.replace('_', '-') in ace_queue: - protocol_options[protocol][match_bit] = True - ace_queue.remove(match_bit.replace('_', '-')) - - rendered_ace['protocol_options'] = protocol_options - - def __parse_match_options(rendered_ace, ace_queue): - """ - Parses the ACE queue and populates remaining options in the ACE dictionary, - i.e., `rendered_ace` - - :param rendered_ace: The dictionary containing the ACE in structured format - :param ace_queue: The ACE queue - """ - if len(ace_queue) > 0: - # We deepcopy the actual queue and iterate through the - # copied queue. However, we pop off the elements from - # the actual queue. Then, in every pass we update the copied - # queue with the current state of the original queue. - # This is done because a queue cannot be mutated during iteration. - copy_ace_queue = deepcopy(ace_queue) - - for element in copy_ace_queue: - if element == 'precedence': - ace_queue.popleft() - rendered_ace['precedence'] = ace_queue.popleft() - - elif element == 'dscp': - ace_queue.popleft() - dscp = {} - operation = ace_queue.popleft() - - if operation in ('eq', 'gt', 'neq', 'lt', 'range'): - if operation == 'range': - dscp['range'] = { - 'start': ace_queue.popleft(), - 'end': ace_queue.popleft() - } - else: - dscp[operation] = ace_queue.popleft() - else: - # `dscp` can be followed by either the dscp value itself or - # the same thing can be represented using "dscp eq <dscp_value>". - # In both cases, it would show up as {'dscp': {'eq': "dscp_value"}}. - dscp['eq'] = operation - - rendered_ace['dscp'] = dscp - - elif element in ('packet-length', 'ttl'): - ace_queue.popleft() - element_dict = {} - operation = ace_queue.popleft() - - if operation == 'range': - element_dict['range'] = { - 'start': ace_queue.popleft(), - 'end': ace_queue.popleft() - } - else: - element_dict[operation] = ace_queue.popleft() - - rendered_ace[element.replace('-', '_')] = element_dict - - elif element in ('log', 'log-input', 'fragments', - 'icmp-off', 'capture', 'destopts', - 'authen', 'routing', 'hop-by-hop'): - rendered_ace[element.replace('-', '_')] = True - ace_queue.remove(element) - - copy_ace_queue = deepcopy(ace_queue) - - rendered_ace = {} - split_ace = ace.split() - - # Create a queue with each word in the ace - # We parse each element and pop it off the queue - ace_queue = deque(split_ace) - - # An ACE will always have a sequence number, even if - # it is not explicitly provided while configuring - sequence = int(ace_queue.popleft()) - rendered_ace['sequence'] = sequence - operation = ace_queue.popleft() - - if operation == 'remark': - rendered_ace['remark'] = ' '.join(split_ace[2:]) - - else: - rendered_ace['grant'] = operation - - # If the entry is a non-remark entry, the third element - # will always be the protocol specified. By default, it's - # the AFI. - rendered_ace['protocol'] = ace_queue.popleft() - - # Populate source dictionary - __parse_src_dest(rendered_ace, ace_queue, direction='source') - # Populate port_protocol key in source dictionary - __parse_port_protocol(rendered_ace, ace_queue, direction='source') - # Populate destination dictionary - __parse_src_dest(rendered_ace, ace_queue, direction='destination') - # Populate port_protocol key in destination dictionary - __parse_port_protocol(rendered_ace, - ace_queue, - direction='destination') - # Populate protocol_options dictionary - __parse_protocol_options(rendered_ace, - ace_queue, - protocol=rendered_ace['protocol']) - # Populate remaining match options' dictionaries - __parse_match_options(rendered_ace, ace_queue) - - # At this stage the queue should be empty - # If the queue is not empty, it means that - # we haven't been able to parse the entire ACE - # In this case, we add the whole unprocessed ACE - # to a key called `line` and send it back - if len(ace_queue) > 0: - rendered_ace = { - 'sequence': sequence, - 'line': ' '.join(split_ace[1:]) - } - - return utils.remove_empties(rendered_ace) diff --git a/lib/ansible/module_utils/network/iosxr/facts/facts.py b/lib/ansible/module_utils/network/iosxr/facts/facts.py deleted file mode 100644 index 109a813a59..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/facts.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) -""" -The facts class for iosxr -this file validates each subset of facts and selectively -calls the appropriate facts gathering function -""" - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -from ansible.module_utils.network.common.facts.facts import FactsBase -from ansible.module_utils.network.iosxr.facts.legacy.base import Default, Hardware, Interfaces, Config -from ansible.module_utils.network.iosxr.facts.lacp.lacp import LacpFacts -from ansible.module_utils.network.iosxr.facts.lacp_interfaces.lacp_interfaces import Lacp_interfacesFacts -from ansible.module_utils.network.iosxr.facts.lldp_global.lldp_global import Lldp_globalFacts -from ansible.module_utils.network.iosxr.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts -from ansible.module_utils.network.iosxr.facts.interfaces.interfaces import InterfacesFacts -from ansible.module_utils.network.iosxr.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts -from ansible.module_utils.network.iosxr.facts.l2_interfaces.l2_interfaces import L2_InterfacesFacts -from ansible.module_utils.network.iosxr.facts.l3_interfaces.l3_interfaces import L3_InterfacesFacts -from ansible.module_utils.network.iosxr.facts.acl_interfaces.acl_interfaces import Acl_interfacesFacts -from ansible.module_utils.network.iosxr.facts.acls.acls import AclsFacts -from ansible.module_utils.network.iosxr.facts.static_routes.static_routes import Static_routesFacts - - -FACT_LEGACY_SUBSETS = dict( - default=Default, - hardware=Hardware, - interfaces=Interfaces, - config=Config, -) -FACT_RESOURCE_SUBSETS = dict( - lacp=LacpFacts, - lacp_interfaces=Lacp_interfacesFacts, - lldp_global=Lldp_globalFacts, - lldp_interfaces=Lldp_interfacesFacts, - interfaces=InterfacesFacts, - l2_interfaces=L2_InterfacesFacts, - lag_interfaces=Lag_interfacesFacts, - l3_interfaces=L3_InterfacesFacts, - acl_interfaces=Acl_interfacesFacts, - acls=AclsFacts, - static_routes=Static_routesFacts -) - - -class Facts(FactsBase): - """ The fact class for iosxr - """ - - 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 iosxr - - :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/iosxr/facts/interfaces/interfaces.py b/lib/ansible/module_utils/network/iosxr/facts/interfaces/interfaces.py deleted file mode 100644 index 8ec4258d0b..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/interfaces/interfaces.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The iosxr 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 - - -from copy import deepcopy -import re -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.iosxr.utils.utils import get_interface_type -from ansible.module_utils.network.iosxr.argspec.interfaces.interfaces import InterfacesArgs - - -class InterfacesFacts(object): - """ The iosxr 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 module: the module instance - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - if not data: - data = connection.get('show running-config interface') - - # operate on a collection of resource x - config = data.split('interface ') - for conf in config: - if conf: - obj = self.render_config(self.generated_spec, conf) - if obj: - objs.append(obj) - - facts = {} - if objs: - facts['interfaces'] = [] - 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) - 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 match.group(1).lower() == "preconfigure": - match = re.search(r'^(\S+) (.*)', conf) - if match: - intf = match.group(2) - - if get_interface_type(intf) == 'unknown': - return {} - # populate the facts from the configuration - config['name'] = intf - config['description'] = utils.parse_conf_arg(conf, 'description') - if utils.parse_conf_arg(conf, 'speed'): - config['speed'] = int(utils.parse_conf_arg(conf, 'speed')) - if utils.parse_conf_arg(conf, 'mtu'): - config['mtu'] = int(utils.parse_conf_arg(conf, 'mtu')) - config['duplex'] = utils.parse_conf_arg(conf, 'duplex') - enabled = utils.parse_conf_cmd_arg(conf, 'shutdown', False) - config['enabled'] = enabled if enabled is not None else True - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py deleted file mode 100644 index dd77172da4..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py +++ /dev/null @@ -1,125 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The iosxr 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 - - -from copy import deepcopy -import re -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.iosxr.utils.utils import get_interface_type -from ansible.module_utils.network.iosxr.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs - - -class L2_InterfacesFacts(object): - """ The iosxr 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 module: the module instance - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - objs = [] - if not data: - data = connection.get('show running-config interface') - - # operate on a collection of resource x - config = data.split('interface ') - for conf in config: - if conf: - obj = self.render_config(self.generated_spec, conf) - if obj: - objs.append(obj) - 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 match.group(1).lower() == "preconfigure": - match = re.search(r'^(\S+) (.*)', conf) - if match: - intf = match.group(2) - - if get_interface_type(intf) == 'unknown': - return {} - - if intf.lower().startswith('gi'): - config['name'] = intf - - # populate the facts from the configuration - native_vlan = re.search(r"dot1q native vlan (\d+)", conf) - if native_vlan: - config["native_vlan"] = int(native_vlan.group(1)) - - dot1q = utils.parse_conf_arg(conf, 'encapsulation dot1q') - config['q_vlan'] = [] - if dot1q: - config['q_vlan'].append(int(dot1q.split(' ')[0])) - if len(dot1q.split(' ')) > 1: - config['q_vlan'].append(int(dot1q.split(' ')[2])) - - if utils.parse_conf_cmd_arg(conf, 'l2transport', True): - config['l2transport'] = True - if utils.parse_conf_arg(conf, 'propagate'): - config['propagate'] = True - config['l2protocol'] = [] - - cdp = utils.parse_conf_arg(conf, 'l2protocol cdp') - pvst = utils.parse_conf_arg(conf, 'l2protocol pvst') - stp = utils.parse_conf_arg(conf, 'l2protocol stp') - vtp = utils.parse_conf_arg(conf, 'l2protocol vtp') - if cdp: - config['l2protocol'].append({'cdp': cdp}) - if pvst: - config['l2protocol'].append({'pvst': pvst}) - if stp: - config['l2protocol'].append({'stp': stp}) - if vtp: - config['l2protocol'].append({'vtp': vtp}) - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/iosxr/facts/l3_interfaces/l3_interfaces.py b/lib/ansible/module_utils/network/iosxr/facts/l3_interfaces/l3_interfaces.py deleted file mode 100644 index 5e610701bd..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/l3_interfaces/l3_interfaces.py +++ /dev/null @@ -1,117 +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 iosxr_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 - - -from copy import deepcopy -import re -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.iosxr.utils.utils import get_interface_type -from ansible.module_utils.network.iosxr.argspec.l3_interfaces.l3_interfaces import L3_InterfacesArgs - - -class L3_InterfacesFacts(object): - """ The iosxr_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 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 interface') - # operate on a collection of resource x - config = data.split('interface ') - for conf in config: - if conf: - obj = self.render_config(self.generated_spec, conf) - if obj: - objs.append(obj) - 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 match.group(1).lower() == "preconfigure": - match = re.search(r'^(\S+) (.*)', conf) - if match: - intf = match.group(2) - - if get_interface_type(intf) == 'unknown': - return {} - - # populate the facts from the configuration - config['name'] = intf - - # Get the configured IPV4 details - ipv4 = [] - ipv4_all = re.findall(r"ipv4 address (\S+.*)", conf) - for each in ipv4_all: - each_ipv4 = dict() - if 'secondary' in each: - each_ipv4['address'] = each.split(' secondary')[0] - each_ipv4['secondary'] = True - else: - each_ipv4['address'] = each - ipv4.append(each_ipv4) - config['ipv4'] = ipv4 - - # Get the configured IPV6 details - ipv6 = [] - ipv6_all = re.findall(r"ipv6 address (\S+)", conf) - for each in ipv6_all: - each_ipv6 = dict() - each_ipv6['address'] = each - ipv6.append(each_ipv6) - config['ipv6'] = ipv6 - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/iosxr/facts/lacp/lacp.py b/lib/ansible/module_utils/network/iosxr/facts/lacp/lacp.py deleted file mode 100644 index 54226dca8b..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/lacp/lacp.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 iosxr 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 - - -from copy import deepcopy -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.iosxr.argspec.lacp.lacp import LacpArgs - - -class LacpFacts(object): - """ The iosxr 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_config(flags='lacp') - - obj = {} - if data: - lacp_obj = self.render_config(self.generated_spec, data) - if lacp_obj: - obj = lacp_obj - - ansible_facts['ansible_network_resources'].pop('lacp', None) - facts = {} - - params = utils.validate_config(self.argument_spec, {'config': obj}) - 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) - - system_priority = utils.parse_conf_arg(conf, 'priority') - config['system']['priority'] = int(system_priority) if system_priority else system_priority - config['system']['mac']['address'] = utils.parse_conf_arg(conf, 'mac') - - return config diff --git a/lib/ansible/module_utils/network/iosxr/facts/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/iosxr/facts/lacp_interfaces/lacp_interfaces.py deleted file mode 100644 index d7744c6ad2..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/lacp_interfaces/lacp_interfaces.py +++ /dev/null @@ -1,104 +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 iosxr 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.iosxr.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs -from ansible.module_utils.six import iteritems - - -class Lacp_interfacesFacts(object): - """ The iosxr 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 - """ - - if not data: - data = connection.get_config(flags='interface') - interfaces = data.split('interface ') - - objs = [] - for interface in interfaces: - obj = self.render_config(self.generated_spec, interface) - if obj: - 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'(GigabitEthernet|Bundle-Ether|TenGigE|FortyGigE|HundredGigE)(\S+)', conf, re.M) - if match: - config['name'] = match.group(1) + match.group(2) - - temp = { - 'churn_logging': 'lacp churn logging', - 'switchover_suppress_flaps': 'lacp switchover suppress-flaps', - 'collector_max_delay': 'lacp collector-max-delay', - 'period': 'lacp period' - } - - for key, value in iteritems(temp): - config[key] = utils.parse_conf_arg( - conf, value) - - for key in config['system'].keys(): - config['system'][key] = utils.parse_conf_arg( - conf, 'lacp system {0}'.format(key)) - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/iosxr/facts/lag_interfaces/lag_interfaces.py b/lib/ansible/module_utils/network/iosxr/facts/lag_interfaces/lag_interfaces.py deleted file mode 100644 index 212232cfea..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/lag_interfaces/lag_interfaces.py +++ /dev/null @@ -1,128 +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 iosxr 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.iosxr.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs - - -class Lag_interfacesFacts(object): - """ The iosxr 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 ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - - if not data: - data = connection.get_config(flags='interface') - interfaces = data.split('interface ') - - objs = [] - - for interface in interfaces: - if interface.startswith("Bundle-Ether"): - obj = self.render_config(self.generated_spec, interface, interfaces) - if obj: - objs.append(obj) - - ansible_facts['ansible_network_resources'].pop('lag_interfaces', None) - facts = {} - - 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 render_config(self, spec, conf, data): - """ - 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'(Bundle-Ether)(\d+)', conf, re.M) - if match: - config['name'] = match.group(1) + match.group(2) - config['load_balancing_hash'] = utils.parse_conf_arg( - conf, 'bundle load-balancing hash') - config['mode'] = utils.parse_conf_arg(conf, 'lacp mode') - config['links']['max_active'] = utils.parse_conf_arg( - conf, 'bundle maximum-active links') - config['links']['min_active'] = utils.parse_conf_arg( - conf, 'bundle minimum-active links') - config['members'] = self.parse_members(match.group(2), data) - - return utils.remove_empties(config) - - def parse_members(self, bundle_id, interfaces): - """ - Renders a list of member interfaces for every bundle - present in running-config. - - :param bundle_id: The Bundle-Ether ID fetched from running-config - :param interfaces: Data of all interfaces present in running-config - :rtype: list - :returns: A list of member interfaces - """ - def _parse_interface(name): - if name.startswith('preconfigure'): - return name.split()[1] - else: - return name.split()[0] - - members = [] - for interface in interfaces: - if not interface.startswith('Bu'): - match = re.search(r'bundle id (\d+) mode (\S+)', interface, re.M) - if match: - if bundle_id == match.group(1): - members.append( - { - 'member': _parse_interface(interface), - 'mode': match.group(2) - } - ) - - return members diff --git a/lib/ansible/module_utils/network/iosxr/facts/legacy/base.py b/lib/ansible/module_utils/network/iosxr/facts/legacy/base.py deleted file mode 100644 index cbcee0bb50..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/legacy/base.py +++ /dev/null @@ -1,259 +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 iosxr legacy 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 platform -import re - -from ansible.module_utils.network.iosxr.iosxr import run_commands, get_capabilities -from ansible.module_utils.six import iteritems -from ansible.module_utils.six.moves import zip - - -class FactsBase(object): - - COMMANDS = frozenset() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.warnings = list() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, list(self.COMMANDS), check_rc=False) - - -class Default(FactsBase): - - def populate(self): - self.facts.update(self.platform_facts()) - - def platform_facts(self): - platform_facts = {} - - resp = get_capabilities(self.module) - 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 - - -class Hardware(FactsBase): - - COMMANDS = [ - 'dir /all', - 'show memory summary' - ] - - def populate(self): - super(Hardware, self).populate() - data = self.responses[0] - self.facts['filesystems'] = self.parse_filesystems(data) - - data = self.responses[1] - match = re.search(r'Physical Memory: (\d+)M total \((\d+)', data) - if match: - self.facts['memtotal_mb'] = match.group(1) - self.facts['memfree_mb'] = match.group(2) - - def parse_filesystems(self, data): - return re.findall(r'^Directory of (\S+)', data, re.M) - - -class Config(FactsBase): - - COMMANDS = [ - 'show running-config' - ] - - def populate(self): - super(Config, self).populate() - self.facts['config'] = self.responses[0] - - -class Interfaces(FactsBase): - - COMMANDS = [ - 'show interfaces', - 'show ipv6 interface', - 'show lldp', - 'show lldp neighbors detail' - ] - - def populate(self): - super(Interfaces, self).populate() - self.facts['all_ipv4_addresses'] = list() - self.facts['all_ipv6_addresses'] = list() - - interfaces = self.parse_interfaces(self.responses[0]) - self.facts['interfaces'] = self.populate_interfaces(interfaces) - - data = self.responses[1] - if len(data) > 0: - data = self.parse_interfaces(data) - self.populate_ipv6_interfaces(data) - - if 'LLDP is not enabled' not in self.responses[2]: - neighbors = self.responses[3] - self.facts['neighbors'] = self.parse_neighbors(neighbors) - - def populate_interfaces(self, interfaces): - facts = dict() - for key, value in iteritems(interfaces): - intf = dict() - intf['description'] = self.parse_description(value) - intf['macaddress'] = self.parse_macaddress(value) - - ipv4 = self.parse_ipv4(value) - intf['ipv4'] = self.parse_ipv4(value) - if ipv4: - self.add_ip_address(ipv4['address'], 'ipv4') - - intf['mtu'] = self.parse_mtu(value) - intf['bandwidth'] = self.parse_bandwidth(value) - intf['duplex'] = self.parse_duplex(value) - intf['lineprotocol'] = self.parse_lineprotocol(value) - intf['operstatus'] = self.parse_operstatus(value) - intf['type'] = self.parse_type(value) - - facts[key] = intf - return facts - - def populate_ipv6_interfaces(self, data): - for key, value in iteritems(data): - if key in ['No', 'RPF'] or key.startswith('IP'): - continue - self.facts['interfaces'][key]['ipv6'] = list() - addresses = re.findall(r'\s+(.+), subnet', value, re.M) - subnets = re.findall(r', subnet is (.+)$', value, re.M) - for addr, subnet in zip(addresses, subnets): - ipv6 = dict(address=addr.strip(), subnet=subnet.strip()) - self.add_ip_address(addr.strip(), 'ipv6') - self.facts['interfaces'][key]['ipv6'].append(ipv6) - - def add_ip_address(self, address, family): - if family == 'ipv4': - self.facts['all_ipv4_addresses'].append(address) - else: - self.facts['all_ipv6_addresses'].append(address) - - def parse_neighbors(self, neighbors): - facts = dict() - nbors = neighbors.split('------------------------------------------------') - for entry in nbors[1:]: - if entry == '': - continue - intf = self.parse_lldp_intf(entry) - if intf not in facts: - facts[intf] = list() - fact = dict() - fact['host'] = self.parse_lldp_host(entry) - fact['remote_description'] = self.parse_lldp_remote_desc(entry) - fact['port'] = self.parse_lldp_port(entry) - facts[intf].append(fact) - return facts - - def parse_interfaces(self, data): - parsed = dict() - key = '' - for line in data.split('\n'): - if len(line) == 0: - continue - elif line[0] == ' ': - parsed[key] += '\n%s' % line - else: - match = re.match(r'^(\S+)', line) - if match: - key = match.group(1) - parsed[key] = line - return parsed - - def parse_description(self, data): - match = re.search(r'Description: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_macaddress(self, data): - match = re.search(r'address is (\S+)', data) - if match: - return match.group(1) - - def parse_ipv4(self, data): - match = re.search(r'Internet address is (\S+)/(\d+)', data) - if match: - addr = match.group(1) - masklen = int(match.group(2)) - return dict(address=addr, masklen=masklen) - - def parse_mtu(self, data): - match = re.search(r'MTU (\d+)', data) - if match: - return int(match.group(1)) - - def parse_bandwidth(self, data): - match = re.search(r'BW (\d+)', data) - if match: - return int(match.group(1)) - - def parse_duplex(self, data): - match = re.search(r'(\w+)(?: D|-d)uplex', data, re.M) - if match: - return match.group(1) - - def parse_type(self, data): - match = re.search(r'Hardware is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lineprotocol(self, data): - match = re.search(r'line protocol is (.+)\s+?$', data, re.M) - if match: - return match.group(1) - - def parse_operstatus(self, data): - match = re.search(r'^(?:.+) is (.+),', data, re.M) - if match: - return match.group(1) - - def parse_lldp_intf(self, data): - match = re.search(r'^Local Interface: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_lldp_remote_desc(self, data): - match = re.search(r'Port Description: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_lldp_host(self, data): - match = re.search(r'System Name: (.+)$', data, re.M) - if match: - return match.group(1) - - def parse_lldp_port(self, data): - match = re.search(r'Port id: (.+)$', data, re.M) - if match: - return match.group(1) diff --git a/lib/ansible/module_utils/network/iosxr/facts/lldp_global/lldp_global.py b/lib/ansible/module_utils/network/iosxr/facts/lldp_global/lldp_global.py deleted file mode 100644 index bbaa1f6ee4..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/lldp_global/lldp_global.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) -""" -The iosxr lldp 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 - - -from copy import deepcopy - -from ansible.module_utils.network.common import utils -from ansible.module_utils.network.iosxr.argspec.lldp_global.lldp_global import Lldp_globalArgs - - -class Lldp_globalFacts(object): - """ The iosxr lldp 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 - :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_config(flags='lldp') - - obj = {} - if data: - lldp_obj = self.render_config(self.generated_spec, data) - if lldp_obj: - obj = lldp_obj - - ansible_facts['ansible_network_resources'].pop('lldp_global', None) - facts = {} - - params = utils.validate_config(self.argument_spec, {'config': obj}) - facts['lldp_global'] = 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) - - for key in spec.keys(): - if key == 'subinterfaces': - config[key] = True if 'subinterfaces enable' in conf else None - - elif key == 'tlv_select': - for item in ['system_name', 'port_description', 'management_address', 'system_description', 'system_capabilities']: - config[key][item] = False if ('{0} disable'.format(item.replace('_', '-'))) in conf else None - - else: - value = utils.parse_conf_arg(conf, key) - config[key] = int(value) if value else value - - return config diff --git a/lib/ansible/module_utils/network/iosxr/facts/lldp_interfaces/lldp_interfaces.py b/lib/ansible/module_utils/network/iosxr/facts/lldp_interfaces/lldp_interfaces.py deleted file mode 100644 index 292493175b..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/lldp_interfaces/lldp_interfaces.py +++ /dev/null @@ -1,96 +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 iosxr 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.iosxr.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs - - -class Lldp_interfacesFacts(object): - """ The iosxr 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 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 = connection.get_config(flags='interface') - interfaces = data.split('interface ') - - objs = [] - for interface in interfaces: - obj = self.render_config(self.generated_spec, interface) - if obj: - 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'(GigabitEthernet|Bundle-Ether|TenGigE|FortyGigE|HundredGigE)(\S+)', conf, re.M) - if match: - config['name'] = match.group(1) + match.group(2) - - for key in ['receive', 'transmit']: - config[key] = False if ('{0} disable'.format(key)) in conf else None - - for x in ['ieee-nearest-bridge', 'ieee-nearest-non-tmpr-bridge']: - if x in conf: - config['destination']['mac_address'] = x - - return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/iosxr/facts/static_routes/static_routes.py b/lib/ansible/module_utils/network/iosxr/facts/static_routes/static_routes.py deleted file mode 100644 index 3485cc7813..0000000000 --- a/lib/ansible/module_utils/network/iosxr/facts/static_routes/static_routes.py +++ /dev/null @@ -1,176 +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 iosxr static_routes 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.iosxr.argspec.static_routes.static_routes import Static_routesArgs - - -class Static_routesFacts(object): - """ The iosxr static_routes fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = Static_routesArgs.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_config(flags="router static") - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for static_routes - :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 = [] - - if "No such configuration" not in data: - for entry in re.compile(r"(\s) vrf").split(data): - obj = self.render_config(self.generated_spec, entry) - if obj: - objs.append(obj) - - ansible_facts["ansible_network_resources"].pop("static_routes", None) - facts = {} - - facts["static_routes"] = [] - params = utils.validate_config(self.argument_spec, {"config": objs}) - for cfg in params["config"]: - facts["static_routes"].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) - entry_list = conf.split(" address-family") - config["address_families"] = [] - - if "router static" not in entry_list[0]: - config["vrf"] = entry_list[0].replace("!", "").strip() - - for item in entry_list[1:]: - routes = [] - address_family = {"routes": []} - address_family["afi"], address_family["safi"] = self.parse_af(item) - - destinations = re.findall(r"((?:\S+)/(?:\d+)) (?:.*)", item, re.M) - for dest in set(destinations): - route = {"next_hops": []} - route["dest"] = dest - - regex = r"%s .+$" % dest - cfg = re.findall(regex, item, re.M) - - for route_entry in cfg: - exit_point = {} - exit_point["forward_router_address"] = self.parse_faddr(route_entry) - exit_point["interface"] = self.parse_intf(route_entry) - exit_point["admin_distance"] = self.parse_admin_distance(route_entry) - - for x in [ - "tag", - "tunnel-id", - "metric", - "description", - "track", - "vrflabel", - "dest_vrf", - ]: - exit_point[x.replace("-", "_")] = self.parse_attrib( - route_entry, x.replace("dest_vrf", "vrf") - ) - - route["next_hops"].append(exit_point) - - routes.append(route) - address_family["routes"] = sorted(routes, key=lambda i: i["dest"]) - config["address_families"].append(address_family) - - return utils.remove_empties(config) - - def parse_af(self, item): - match = re.search(r"(?:\s*)(\w+)(?:\s*)(\w+)", item, re.M) - if match: - return match.group(1), match.group(2) - - def parse_faddr(self, item): - for x in item.split(" "): - if (":" in x or "." in x) and "/" not in x: - return x - - def parse_intf(self, item): - match = re.search(r" ((\w+)((?:\d)/(?:\d)/(?:\d)/(?:\d+)))", item) - if match: - return match.group(1) - - def parse_attrib(self, item, attrib): - match = re.search(r" %s (\S+)" % attrib, item) - if match: - val = match.group(1).strip("'") - if attrib in ["tunnel-id", "vrflabel", "tag", "metric"]: - val = int(val) - return val - - def parse_admin_distance(self, item): - split_item = item.split(" ") - for item in [ - "vrf", - "metric", - "tunnel-id", - "vrflabel", - "track", - "tag", - "description", - ]: - try: - del split_item[split_item.index(item) + 1] - del split_item[split_item.index(item)] - except ValueError: - continue - try: - return [ - i for i in split_item if "." not in i and ":" not in i and ord(i[0]) > 48 and ord(i[0]) < 57 - ][0] - except IndexError: - return None diff --git a/lib/ansible/module_utils/network/iosxr/iosxr.py b/lib/ansible/module_utils/network/iosxr/iosxr.py deleted file mode 100644 index 361716a07c..0000000000 --- a/lib/ansible/module_utils/network/iosxr/iosxr.py +++ /dev/null @@ -1,565 +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) 2015 Peter Sprygada, <psprygada@ansible.com> -# 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 json -import re -from difflib import Differ - -from ansible.module_utils._text import to_text, to_bytes -from ansible.module_utils.basic import env_fallback -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.connection import Connection, ConnectionError -from ansible.module_utils.network.common.netconf import NetconfConnection - -try: - from ncclient.xml_ import to_xml - HAS_NCCLIENT = True -except ImportError: - HAS_NCCLIENT = False - -try: - from lxml import etree - HAS_XML = True -except ImportError: - HAS_XML = False - -_EDIT_OPS = frozenset(['merge', 'create', 'replace', 'delete']) - -BASE_1_0 = "{urn:ietf:params:xml:ns:netconf:base:1.0}" - -NS_DICT = { - 'BASE_NSMAP': {"xc": "urn:ietf:params:xml:ns:netconf:base:1.0"}, - 'BANNERS_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-infra-cfg"}, - 'INTERFACES_NSMAP': {None: "http://openconfig.net/yang/interfaces"}, - 'INSTALL_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-installmgr-admin-oper"}, - 'HOST-NAMES_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-shellutil-cfg"}, - 'M:TYPE_NSMAP': {"idx": "urn:ietf:params:xml:ns:yang:iana-if-type"}, - 'ETHERNET_NSMAP': {None: "http://openconfig.net/yang/interfaces/ethernet"}, - 'CETHERNET_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-drivers-media-eth-cfg"}, - 'INTERFACE-CONFIGURATIONS_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"}, - 'INFRA-STATISTICS_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-statsd-oper"}, - 'INTERFACE-PROPERTIES_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-oper"}, - 'IP-DOMAIN_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ip-domain-cfg"}, - 'SYSLOG_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-syslog-cfg"}, - 'AAA_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-lib-cfg"}, - 'AAA_LOCALD_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-locald-cfg"}, -} - -iosxr_provider_spec = { - 'host': dict(), - 'port': dict(type='int'), - 'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])), - 'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True), - 'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'), - 'timeout': dict(type='int'), - 'transport': dict(type='str', default='cli', choices=['cli', 'netconf']), -} - -iosxr_argument_spec = { - 'provider': dict(type='dict', options=iosxr_provider_spec, removed_in_version=2.14) -} - -command_spec = { - 'command': dict(), - 'prompt': dict(default=None), - 'answer': dict(default=None) -} - -CONFIG_MISPLACED_CHILDREN = [ - re.compile(r'^end-\s*(.+)$') -] - -# Objects defined in Route-policy Language guide of IOS_XR. -# Reconfiguring these objects replace existing configurations. -# Hence these objects should be played direcly from candidate -# configurations -CONFIG_BLOCKS_FORCED_IN_DIFF = [ - { - 'start': re.compile(r'route-policy'), - 'end': re.compile(r'end-policy') - }, - { - 'start': re.compile(r'prefix-set'), - 'end': re.compile(r'end-set') - }, - { - 'start': re.compile(r'as-path-set'), - 'end': re.compile(r'end-set') - }, - { - 'start': re.compile(r'community-set'), - 'end': re.compile(r'end-set') - }, - { - 'start': re.compile(r'rd-set'), - 'end': re.compile(r'end-set') - }, - { - 'start': re.compile(r'extcommunity-set'), - 'end': re.compile(r'end-set') - } -] - - -def get_provider_argspec(): - return iosxr_provider_spec - - -def get_connection(module): - if hasattr(module, 'connection'): - return module.connection - - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'cliconf': - module.connection = Connection(module._socket_path) - elif network_api == 'netconf': - module.connection = NetconfConnection(module._socket_path) - else: - module.fail_json(msg='Invalid connection type {0!s}'.format(network_api)) - - return module.connection - - -def get_capabilities(module): - if hasattr(module, 'capabilities'): - return module.capabilities - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - module.capabilities = json.loads(capabilities) - - return module.capabilities - - -def build_xml_subtree(container_ele, xmap, param=None, opcode=None): - sub_root = container_ele - meta_subtree = list() - - for key, meta in xmap.items(): - candidates = meta.get('xpath', "").split("/") - if container_ele.tag == candidates[-2]: - parent = container_ele - elif sub_root.tag == candidates[-2]: - parent = sub_root - else: - parent = sub_root.find(".//" + meta.get('xpath', "").split(sub_root.tag + '/', 1)[1].rsplit('/', 1)[0]) - - if ((opcode in ('delete', 'merge') and meta.get('operation', 'unknown') == 'edit') or - meta.get('operation', None) is None): - - if meta.get('tag', False) is True: - if parent.tag == container_ele.tag: - if meta.get('ns', False) is True: - child = etree.Element(candidates[-1], nsmap=NS_DICT[key.upper() + "_NSMAP"]) - else: - child = etree.Element(candidates[-1]) - meta_subtree.append(child) - sub_root = child - else: - if meta.get('ns', False) is True: - child = etree.SubElement(parent, candidates[-1], nsmap=NS_DICT[key.upper() + "_NSMAP"]) - else: - child = etree.SubElement(parent, candidates[-1]) - - if meta.get('attrib', None) is not None and opcode in ('delete', 'merge'): - child.set(BASE_1_0 + meta.get('attrib'), opcode) - - continue - - text = None - param_key = key.split(":") - if param_key[0] == 'a': - if param is not None and param.get(param_key[1], None) is not None: - text = param.get(param_key[1]) - elif param_key[0] == 'm': - if meta.get('value', None) is not None: - text = meta.get('value') - - if text: - if meta.get('ns', False) is True: - child = etree.SubElement(parent, candidates[-1], nsmap=NS_DICT[key.upper() + "_NSMAP"]) - else: - child = etree.SubElement(parent, candidates[-1]) - child.text = text - - if meta.get('attrib', None) is not None and opcode in ('delete', 'merge'): - child.set(BASE_1_0 + meta.get('attrib'), opcode) - - if len(meta_subtree) > 1: - for item in meta_subtree: - container_ele.append(item) - - if sub_root == container_ele: - return None - else: - return sub_root - - -def build_xml(container, xmap=None, params=None, opcode=None): - """ - Builds netconf xml rpc document from meta-data - - Args: - container: the YANG container within the namespace - xmap: meta-data map to build xml tree - params: Input params that feed xml tree values - opcode: operation to be performed (merge, delete etc.) - - Example: - Module inputs: - banner_params = [{'banner':'motd', 'text':'Ansible banner example', 'state':'present'}] - - Meta-data definition: - bannermap = collections.OrderedDict() - bannermap.update([ - ('banner', {'xpath' : 'banners/banner', 'tag' : True, 'attrib' : "operation"}), - ('a:banner', {'xpath' : 'banner/banner-name'}), - ('a:text', {'xpath' : 'banner/banner-text', 'operation' : 'edit'}) - ]) - - Fields: - key: exact match to the key in arg_spec for a parameter - (prefixes --> a: value fetched from arg_spec, m: value fetched from meta-data) - xpath: xpath of the element (based on YANG model) - tag: True if no text on the element - attrib: attribute to be embedded in the element (e.g. xc:operation="merge") - operation: if edit --> includes the element in edit_config() query else ignores for get() queries - value: if key is prefixed with "m:", value is required in meta-data - - Output: - <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> - <banners xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-infra-infra-cfg"> - <banner xc:operation="merge"> - <banner-name>motd</banner-name> - <banner-text>Ansible banner example</banner-text> - </banner> - </banners> - </config> - :returns: xml rpc document as a string - """ - if opcode == 'filter': - root = etree.Element("filter", type="subtree") - elif opcode in ('delete', 'merge'): - root = etree.Element("config", nsmap=NS_DICT['BASE_NSMAP']) - - container_ele = etree.SubElement(root, container, nsmap=NS_DICT[container.upper() + "_NSMAP"]) - - if xmap is not None: - if params is None: - build_xml_subtree(container_ele, xmap, opcode=opcode) - else: - subtree_list = list() - for param in to_list(params): - subtree_ele = build_xml_subtree(container_ele, xmap, param=param, opcode=opcode) - if subtree_ele is not None: - subtree_list.append(subtree_ele) - - for item in subtree_list: - container_ele.append(item) - - return etree.tostring(root, encoding='unicode') - - -def etree_find(root, node): - try: - root = etree.fromstring(to_bytes(root)) - except (ValueError, etree.XMLSyntaxError): - pass - - return root.find('.//%s' % node.strip()) - - -def etree_findall(root, node): - try: - root = etree.fromstring(to_bytes(root)) - except (ValueError, etree.XMLSyntaxError): - pass - - return root.findall('.//%s' % node.strip()) - - -def is_cliconf(module): - capabilities = get_capabilities(module) - return (capabilities.get('network_api') == 'cliconf') - - -def is_netconf(module): - capabilities = get_capabilities(module) - network_api = capabilities.get('network_api') - if network_api == 'netconf': - if not HAS_NCCLIENT: - module.fail_json(msg='ncclient is not installed') - if not HAS_XML: - module.fail_json(msg='lxml is not installed') - return True - - return False - - -def get_config_diff(module, running=None, candidate=None): - conn = get_connection(module) - - if is_cliconf(module): - try: - response = conn.get('show commit changes diff') - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return response - elif is_netconf(module): - if running and candidate: - # ignore rpc-reply root node and diff from data element onwards - running_data_ele = etree.fromstring(to_bytes(running.strip())).getchildren()[0] - candidate_data_ele = etree.fromstring(to_bytes(candidate.strip())).getchildren()[0] - - running_data = to_text(etree.tostring(running_data_ele)).strip() - candidate_data = to_text(etree.tostring(candidate_data_ele)).strip() - if running_data != candidate_data: - d = Differ() - diff = list(d.compare(running_data.splitlines(), candidate_data.splitlines())) - return '\n'.join(diff).strip() - - return None - - -def discard_config(module): - conn = get_connection(module) - try: - if is_netconf(module): - conn.discard_changes(remove_ns=True) - else: - conn.discard_changes() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - -def commit_config(module, comment=None, confirmed=False, confirm_timeout=None, - persist=False, check=False, label=None): - conn = get_connection(module) - reply = None - try: - if is_netconf(module): - if check: - reply = conn.validate(remove_ns=True) - else: - reply = conn.commit(confirmed=confirmed, timeout=confirm_timeout, persist=persist, remove_ns=True) - elif is_cliconf(module): - if check: - module.fail_json(msg="Validate configuration is not supported with network_cli connection type") - else: - reply = conn.commit(comment=comment, label=label) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return reply - - -def get_oper(module, filter=None): - conn = get_connection(module) - - if filter is not None: - try: - if is_netconf(module): - response = conn.get(filter=filter, remove_ns=True) - else: - response = conn.get(filter) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - else: - return None - - return to_bytes(etree.tostring(response), errors='surrogate_then_replace').strip() - - -def get_config(module, config_filter=None, source='running'): - conn = get_connection(module) - - # Note: Does not cache config in favour of latest config on every get operation. - try: - if is_netconf(module): - out = to_xml(conn.get_config(source=source, filter=config_filter, remove_ns=True)) - elif is_cliconf(module): - out = conn.get_config(source=source, flags=config_filter) - cfg = out.strip() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - return cfg - - -def check_existing_commit_labels(conn, label): - out = conn.get(command='show configuration history detail | include %s' % label) - label_exist = re.search(label, out, re.M) - if label_exist: - return True - else: - return False - - -def load_config(module, command_filter, commit=False, replace=False, - comment=None, admin=False, exclusive=False, running=None, nc_get_filter=None, - label=None): - - conn = get_connection(module) - - diff = None - if is_netconf(module): - # FIXME: check for platform behaviour and restore this - # conn.lock(target = 'candidate') - # conn.discard_changes() - - try: - for filter in to_list(command_filter): - conn.edit_config(config=filter, remove_ns=True) - - candidate = get_config(module, source='candidate', config_filter=nc_get_filter) - diff = get_config_diff(module, running, candidate) - - if commit and diff: - commit_config(module) - else: - discard_config(module) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - finally: - # conn.unlock(target = 'candidate') - pass - - elif is_cliconf(module): - try: - if label: - old_label = check_existing_commit_labels(conn, label) - if old_label: - module.fail_json( - msg='commit label {%s} is already used for' - ' an earlier commit, please choose a different label' - ' and rerun task' % label - ) - - response = conn.edit_config(candidate=command_filter, commit=commit, admin=admin, - exclusive=exclusive, replace=replace, comment=comment, label=label) - if module._diff: - diff = response.get('diff') - - # Overwrite the default diff by the IOS XR commit diff. - # See plugins/cliconf/iosxr.py for this key set: show_commit_config_diff - diff = response.get('show_commit_config_diff') - - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - return diff - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - try: - return connection.run_commands(commands=commands, check_rc=check_rc) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc)) - - -def copy_file(module, src, dst, proto='scp'): - conn = get_connection(module) - try: - conn.copy_file(source=src, destination=dst, proto=proto) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - -def get_file(module, src, dst, proto='scp'): - conn = get_connection(module) - try: - conn.get_file(source=src, destination=dst, proto=proto) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) - - -# A list of commands like {end-set, end-policy, ...} are part of configuration -# block like { prefix-set, as-path-set , ... } but they are not indented properly -# to be included with their parent. sanitize_config will add indentation to -# end-* commands so they are included with their parents -def sanitize_config(config, force_diff_prefix=None): - conf_lines = config.split('\n') - for regex in CONFIG_MISPLACED_CHILDREN: - for index, line in enumerate(conf_lines): - m = regex.search(line) - if m and m.group(0): - if force_diff_prefix: - conf_lines[index] = ' ' + m.group(0) + force_diff_prefix - else: - conf_lines[index] = ' ' + m.group(0) - conf = ('\n').join(conf_lines) - return conf - - -def mask_config_blocks_from_diff(config, candidate, force_diff_prefix): - conf_lines = config.split('\n') - candidate_lines = candidate.split('\n') - - for regex in CONFIG_BLOCKS_FORCED_IN_DIFF: - block_index_start_end = [] - for index, line in enumerate(candidate_lines): - startre = regex['start'].search(line) - if startre and startre.group(0): - start_index = index - else: - endre = regex['end'].search(line) - if endre and endre.group(0): - end_index = index - new_block = True - for prev_start, prev_end in block_index_start_end: - if start_index == prev_start: - # This might be end-set of another regex - # otherwise we would be having new start - new_block = False - break - if new_block: - block_index_start_end.append((start_index, end_index)) - - for start, end in block_index_start_end: - diff = False - if candidate_lines[start] in conf_lines: - run_conf_start_index = conf_lines.index(candidate_lines[start]) - else: - diff = False - continue - for i in range(start, end + 1): - if conf_lines[run_conf_start_index] == candidate_lines[i]: - run_conf_start_index = run_conf_start_index + 1 - else: - diff = True - break - if diff: - run_conf_start_index = conf_lines.index(candidate_lines[start]) - for i in range(start, end + 1): - conf_lines[run_conf_start_index] = conf_lines[run_conf_start_index] + force_diff_prefix - run_conf_start_index = run_conf_start_index + 1 - - conf = ('\n').join(conf_lines) - return conf diff --git a/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/address_family.py b/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/address_family.py deleted file mode 100644 index 2005615d97..0000000000 --- a/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/address_family.py +++ /dev/null @@ -1,114 +0,0 @@ -# -# (c) 2019, Ansible by Red Hat, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -import re - -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.network.iosxr.providers.providers import CliProvider - - -class AddressFamily(CliProvider): - - def render(self, config=None): - commands = list() - safe_list = list() - - router_context = 'router bgp %s' % self.get_value('config.bgp_as') - context_config = None - - for item in self.get_value('config.address_family'): - context = 'address-family %s %s' % (item['afi'], item['safi']) - context_commands = list() - - if config: - context_path = [router_context, context] - context_config = self.get_config_context(config, context_path, indent=1) - - for key, value in iteritems(item): - if value is not None: - meth = getattr(self, '_render_%s' % key, None) - if meth: - resp = meth(item, context_config) - if resp: - context_commands.extend(to_list(resp)) - - if context_commands: - commands.append(context) - commands.extend(context_commands) - commands.append('exit') - - safe_list.append(context) - - if config: - resp = self._negate_config(config, safe_list) - commands.extend(resp) - - return commands - - def _negate_config(self, config, safe_list=None): - commands = list() - matches = re.findall(r'(address-family .+)$', config, re.M) - for item in set(matches).difference(safe_list): - commands.append('no %s' % item) - return commands - - def _render_networks(self, item, config=None): - commands = list() - safe_list = list() - - for entry in item['networks']: - network = entry['prefix'] - if entry['masklen']: - network = '%s/%s' % (entry['prefix'], entry['masklen']) - safe_list.append(network) - - cmd = 'network %s' % network - - if entry['route_map']: - cmd += ' route-policy %s' % entry['route_map'] - - if not config or cmd not in config: - commands.append(cmd) - - if config and self.params['operation'] == 'replace': - matches = re.findall(r'network (\S+)', config, re.M) - for entry in set(matches).difference(safe_list): - commands.append('no network %s' % entry) - - return commands - - def _render_redistribute(self, item, config=None): - commands = list() - safe_list = list() - - for entry in item['redistribute']: - option = entry['protocol'] - - cmd = 'redistribute %s' % entry['protocol'] - - if entry['id'] and entry['protocol'] in ('ospf', 'eigrp', 'isis', 'ospfv3'): - cmd += ' %s' % entry['id'] - option += ' %s' % entry['id'] - - if entry['metric']: - cmd += ' metric %s' % entry['metric'] - - if entry['route_map']: - cmd += ' route-policy %s' % entry['route_map'] - - if not config or cmd not in config: - commands.append(cmd) - - safe_list.append(option) - - if self.params['operation'] == 'replace': - if config: - matches = re.findall(r'redistribute (\S+)(?:\s*)(\d*)', config, re.M) - for i in range(0, len(matches)): - matches[i] = ' '.join(matches[i]).strip() - for entry in set(matches).difference(safe_list): - commands.append('no redistribute %s' % entry) - - return commands diff --git a/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/neighbors.py b/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/neighbors.py deleted file mode 100644 index 137691e3e1..0000000000 --- a/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/neighbors.py +++ /dev/null @@ -1,125 +0,0 @@ -# -# (c) 2019, Ansible by Red Hat, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -import re -import socket - -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.network.iosxr.providers.providers import CliProvider - - -class Neighbors(CliProvider): - - def render(self, config=None): - commands = list() - safe_list = list() - - router_context = 'router bgp %s' % self.get_value('config.bgp_as') - context_config = None - - for item in self.get_value('config.neighbors'): - context_commands = list() - - neighbor = item['neighbor'] - - try: - socket.inet_aton(neighbor) - context = 'neighbor %s' % neighbor - except socket.error: - context = 'neighbor-group %s' % neighbor - - if config: - context_path = [router_context, context] - context_config = self.get_config_context(config, context_path, indent=1) - - for key, value in iteritems(item): - if value is not None: - meth = getattr(self, '_render_%s' % key, None) - if meth: - resp = meth(item, context_config) - if resp: - context_commands.extend(to_list(resp)) - - if context_commands: - commands.append(context) - commands.extend(context_commands) - commands.append('exit') - - safe_list.append(context) - - if config and safe_list: - commands.extend(self._negate_config(config, safe_list)) - - return commands - - def _negate_config(self, config, safe_list=None): - commands = list() - matches = re.findall(r'(neighbor \S+)', config, re.M) - for item in set(matches).difference(safe_list): - commands.append('no %s' % item) - return commands - - def _render_remote_as(self, item, config=None): - cmd = 'remote-as %s' % item['remote_as'] - if not config or cmd not in config: - return cmd - - def _render_description(self, item, config=None): - cmd = 'description %s' % item['description'] - if not config or cmd not in config: - return cmd - - def _render_enabled(self, item, config=None): - cmd = 'shutdown' - if item['enabled'] is True: - cmd = 'no %s' % cmd - if not config or cmd not in config: - return cmd - - def _render_update_source(self, item, config=None): - cmd = 'update-source %s' % item['update_source'].replace(' ', '') - if not config or cmd not in config: - return cmd - - def _render_password(self, item, config=None): - cmd = 'password %s' % item['password'] - if not config or cmd not in config: - return cmd - - def _render_ebgp_multihop(self, item, config=None): - cmd = 'ebgp-multihop %s' % item['ebgp_multihop'] - if not config or cmd not in config: - return cmd - - def _render_tcp_mss(self, item, config=None): - cmd = 'tcp mss %s' % item['tcp_mss'] - if not config or cmd not in config: - return cmd - - def _render_advertisement_interval(self, item, config=None): - cmd = 'advertisement-interval %s' % item['advertisement_interval'] - if not config or cmd not in config: - return cmd - - def _render_neighbor_group(self, item, config=None): - cmd = 'use neighbor-group %s' % item['neighbor_group'] - if not config or cmd not in config: - return cmd - - def _render_timers(self, item, config): - """generate bgp timer related configuration - """ - keepalive = item['timers']['keepalive'] - holdtime = item['timers']['holdtime'] - min_neighbor_holdtime = item['timers']['min_neighbor_holdtime'] - - if keepalive and holdtime: - cmd = 'timers %s %s' % (keepalive, holdtime) - if min_neighbor_holdtime: - cmd += ' %s' % min_neighbor_holdtime - if not config or cmd not in config: - return cmd - else: - raise ValueError("required both options for timers: keepalive and holdtime") diff --git a/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/process.py b/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/process.py deleted file mode 100644 index 470d586a64..0000000000 --- a/lib/ansible/module_utils/network/iosxr/providers/cli/config/bgp/process.py +++ /dev/null @@ -1,97 +0,0 @@ -# -# (c) 2019, Ansible by Red Hat, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -import re - -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.network.iosxr.providers.providers import register_provider -from ansible.module_utils.network.iosxr.providers.providers import CliProvider -from ansible.module_utils.network.iosxr.providers.cli.config.bgp.neighbors import Neighbors -from ansible.module_utils.network.iosxr.providers.cli.config.bgp.address_family import AddressFamily - -REDISTRIBUTE_PROTOCOLS = frozenset(['ospf', 'ospfv3', 'eigrp', 'isis', 'static', - 'connected', 'lisp', 'mobile', 'rip', - 'subscriber']) - - -@register_provider('iosxr', 'iosxr_bgp') -class Provider(CliProvider): - - def render(self, config=None): - commands = list() - - existing_as = None - if config: - match = re.search(r'router bgp (\d+)', config, re.M) - if match: - existing_as = match.group(1) - - operation = self.params['operation'] - - context = None - - if self.params['config']: - context = 'router bgp %s' % self.get_value('config.bgp_as') - - if operation == 'delete': - if existing_as: - commands.append('no router bgp %s' % existing_as) - elif context: - commands.append('no %s' % context) - - else: - if operation == 'replace': - if existing_as and int(existing_as) != self.get_value('config.bgp_as'): - # The negate command has to be committed before new BGP AS is used. - self.connection.edit_config('no router bgp %s' % existing_as) - config = None - - elif operation == 'override': - if existing_as: - # The negate command has to be committed before new BGP AS is used. - self.connection.edit_config('no router bgp %s' % existing_as) - config = None - - context_commands = list() - - for key, value in iteritems(self.get_value('config')): - if value is not None: - meth = getattr(self, '_render_%s' % key, None) - if meth: - resp = meth(config) - if resp: - context_commands.extend(to_list(resp)) - - if context and context_commands: - commands.append(context) - commands.extend(context_commands) - commands.append('exit') - - return commands - - def _render_router_id(self, config=None): - cmd = 'bgp router-id %s' % self.get_value('config.router_id') - if not config or cmd not in config: - return cmd - - def _render_log_neighbor_changes(self, config=None): - cmd = 'bgp log neighbor changes' - log_neighbor_changes = self.get_value('config.log_neighbor_changes') - if log_neighbor_changes is True: - if not config or cmd not in config: - return '%s detail' % cmd - elif log_neighbor_changes is False: - if config and cmd in config: - return '%s disable' % cmd - - def _render_neighbors(self, config): - """ generate bgp neighbor configuration - """ - return Neighbors(self.params).render(config) - - def _render_address_family(self, config): - """ generate address-family configuration - """ - return AddressFamily(self.params).render(config) diff --git a/lib/ansible/module_utils/network/iosxr/providers/module.py b/lib/ansible/module_utils/network/iosxr/providers/module.py deleted file mode 100644 index 2c4d97a337..0000000000 --- a/lib/ansible/module_utils/network/iosxr/providers/module.py +++ /dev/null @@ -1,62 +0,0 @@ -# -# (c) 2019, Ansible by Red Hat, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import Connection -from ansible.module_utils.network.iosxr.providers import providers -from ansible.module_utils._text import to_text - - -class NetworkModule(AnsibleModule): - - fail_on_missing_provider = True - - def __init__(self, connection=None, *args, **kwargs): - super(NetworkModule, self).__init__(*args, **kwargs) - - if connection is None: - connection = Connection(self._socket_path) - - self.connection = connection - - @property - def provider(self): - if not hasattr(self, '_provider'): - capabilities = self.from_json(self.connection.get_capabilities()) - - network_os = capabilities['device_info']['network_os'] - network_api = capabilities['network_api'] - - if network_api == 'cliconf': - connection_type = 'network_cli' - - cls = providers.get(network_os, self._name.split('.')[-1], connection_type) - - if not cls: - msg = 'unable to find suitable provider for network os %s' % network_os - if self.fail_on_missing_provider: - self.fail_json(msg=msg) - else: - self.warn(msg) - - obj = cls(self.params, self.connection, self.check_mode) - - setattr(self, '_provider', obj) - - return getattr(self, '_provider') - - def get_facts(self, subset=None): - try: - self.provider.get_facts(subset) - except Exception as exc: - self.fail_json(msg=to_text(exc)) - - def edit_config(self, config_filter=None): - current_config = self.connection.get_config(flags=config_filter) - try: - commands = self.provider.edit_config(current_config) - changed = bool(commands) - return {'commands': commands, 'changed': changed} - except Exception as exc: - self.fail_json(msg=to_text(exc)) diff --git a/lib/ansible/module_utils/network/iosxr/providers/providers.py b/lib/ansible/module_utils/network/iosxr/providers/providers.py deleted file mode 100644 index a466b033d9..0000000000 --- a/lib/ansible/module_utils/network/iosxr/providers/providers.py +++ /dev/null @@ -1,120 +0,0 @@ -# -# (c) 2019, Ansible by Red Hat, inc -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# -import json - -from threading import RLock - -from ansible.module_utils.six import itervalues -from ansible.module_utils.network.common.utils import to_list -from ansible.module_utils.network.common.config import NetworkConfig - - -_registered_providers = {} -_provider_lock = RLock() - - -def register_provider(network_os, module_name): - def wrapper(cls): - _provider_lock.acquire() - try: - if network_os not in _registered_providers: - _registered_providers[network_os] = {} - for ct in cls.supported_connections: - if ct not in _registered_providers[network_os]: - _registered_providers[network_os][ct] = {} - for item in to_list(module_name): - for entry in itervalues(_registered_providers[network_os]): - entry[item] = cls - finally: - _provider_lock.release() - return cls - return wrapper - - -def get(network_os, module_name, connection_type): - network_os_providers = _registered_providers.get(network_os) - if network_os_providers is None: - raise ValueError('unable to find a suitable provider for this module') - if connection_type not in network_os_providers: - raise ValueError('provider does not support this connection type') - elif module_name not in network_os_providers[connection_type]: - raise ValueError('could not find a suitable provider for this module') - return network_os_providers[connection_type][module_name] - - -class ProviderBase(object): - - supported_connections = () - - def __init__(self, params, connection=None, check_mode=False): - self.params = params - self.connection = connection - self.check_mode = check_mode - - @property - def capabilities(self): - if not hasattr(self, '_capabilities'): - resp = self.from_json(self.connection.get_capabilities()) - setattr(self, '_capabilities', resp) - return getattr(self, '_capabilities') - - def get_value(self, path): - params = self.params.copy() - for key in path.split('.'): - params = params[key] - return params - - def get_facts(self, subset=None): - raise NotImplementedError(self.__class__.__name__) - - def edit_config(self): - raise NotImplementedError(self.__class__.__name__) - - -class CliProvider(ProviderBase): - - supported_connections = ('network_cli',) - - @property - def capabilities(self): - if not hasattr(self, '_capabilities'): - resp = self.from_json(self.connection.get_capabilities()) - setattr(self, '_capabilities', resp) - return getattr(self, '_capabilities') - - def get_config_context(self, config, path, indent=1): - if config is not None: - netcfg = NetworkConfig(indent=indent, contents=config) - try: - config = netcfg.get_block_config(to_list(path)) - except ValueError: - config = None - return config - - def render(self, config=None): - raise NotImplementedError(self.__class__.__name__) - - def cli(self, command): - try: - if not hasattr(self, '_command_output'): - setattr(self, '_command_output', {}) - return self._command_output[command] - except KeyError: - out = self.connection.get(command) - try: - out = json.loads(out) - except ValueError: - pass - self._command_output[command] = out - return out - - def get_facts(self, subset=None): - return self.populate() - - def edit_config(self, config=None): - commands = self.render(config) - if commands and self.check_mode is False: - self.connection.edit_config(commands) - return commands diff --git a/lib/ansible/module_utils/network/iosxr/utils/utils.py b/lib/ansible/module_utils/network/iosxr/utils/utils.py deleted file mode 100644 index 73589601ab..0000000000 --- a/lib/ansible/module_utils/network/iosxr/utils/utils.py +++ /dev/null @@ -1,355 +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) - -# utils - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -from ansible.module_utils._text import to_text -from ansible.module_utils.compat import ipaddress -from ansible.module_utils.six import iteritems -from ansible.module_utils.network.common.utils import dict_diff, is_masklen, to_netmask, search_obj_in_list - - -def remove_command_from_config_list(interface, cmd, commands): - # To delete the passed config - if interface not in commands: - commands.insert(0, interface) - commands.append('no %s' % cmd) - return commands - - -def add_command_to_config_list(interface, cmd, commands): - # To set the passed config - if interface not in commands: - commands.insert(0, interface) - commands.append(cmd) - - -def dict_to_set(sample_dict): - # Generate a set with passed dictionary for comparison - test_dict = {} - if isinstance(sample_dict, dict): - for k, v in iteritems(sample_dict): - if v is not None: - if isinstance(v, list): - if isinstance(v[0], dict): - li = [] - for each in v: - for key, value in iteritems(each): - if isinstance(value, list): - each[key] = tuple(value) - li.append(tuple(iteritems(each))) - v = tuple(li) - else: - v = tuple(v) - elif isinstance(v, dict): - li = [] - for key, value in iteritems(v): - if isinstance(value, list): - v[key] = tuple(value) - li.extend(tuple(iteritems(v))) - v = tuple(li) - test_dict.update({k: v}) - return_set = set(tuple(iteritems(test_dict))) - else: - return_set = set(sample_dict) - return return_set - - -def filter_dict_having_none_value(want, have): - # Generate dict with have dict value which is None in want dict - test_dict = dict() - test_key_dict = dict() - name = want.get('name') - if name: - test_dict['name'] = name - diff_ip = False - want_ip = '' - for k, v in iteritems(want): - if isinstance(v, dict): - for key, value in iteritems(v): - if value is None: - dict_val = have.get(k).get(key) - test_key_dict.update({key: dict_val}) - test_dict.update({k: test_key_dict}) - if isinstance(v, list) and isinstance(v[0], dict): - for key, value in iteritems(v[0]): - if value is None: - dict_val = have.get(k).get(key) - test_key_dict.update({key: dict_val}) - test_dict.update({k: test_key_dict}) - # below conditions checks are added to check if - # secondary IP is configured, if yes then delete - # the already configured IP if want and have IP - # is different else if it's same no need to delete - for each in v: - if each.get('secondary'): - want_ip = each.get('address').split('/') - have_ip = have.get('ipv4') - for each in have_ip: - if len(want_ip) > 1 and each.get('secondary'): - have_ip = each.get('address').split(' ')[0] - if have_ip != want_ip[0]: - diff_ip = True - if each.get('secondary') and diff_ip is True: - test_key_dict.update({'secondary': True}) - test_dict.update({'ipv4': test_key_dict}) - # Checks if want doesn't have secondary IP but have has secondary IP set - elif have.get('ipv4'): - if [ - True for each_have in have.get('ipv4') - if 'secondary' in each_have - ]: - test_dict.update({'ipv4': {'secondary': True}}) - if k == 'l2protocol': - if want[k] != have.get('l2protocol') and have.get('l2protocol'): - test_dict.update({k: v}) - if v is None: - val = have.get(k) - test_dict.update({k: val}) - return test_dict - - -def remove_duplicate_interface(commands): - # Remove duplicate interface from commands - set_cmd = [] - for each in commands: - if 'interface' in each: - if each not in set_cmd: - set_cmd.append(each) - else: - set_cmd.append(each) - - return set_cmd - - -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 dict_delete(base, comparable): - """ - - This function generates a dict containing key, value pairs for keys - that are present in the `base` dict but not present in the `comparable` - dict. - - :param base: dict object to base the diff on - :param comparable: dict object to compare against base - - :returns: new dict object with key, value pairs that needs to be deleted. - - """ - to_delete = dict() - - for key in base: - if isinstance(base[key], dict): - sub_diff = dict_delete(base[key], comparable.get(key, {})) - if sub_diff: - to_delete[key] = sub_diff - else: - if key not in comparable: - to_delete[key] = base[key] - - return to_delete - - -def pad_commands(commands, interface): - commands.insert(0, 'interface {0}'.format(interface)) - - -def diff_list_of_dicts(w, h, key='member'): - """ - Returns a list containing diff between - two list of dictionaries - """ - if not w: - w = [] - if not h: - h = [] - - diff = [] - for w_item in w: - h_item = search_obj_in_list(w_item[key], h, key=key) or {} - d = dict_diff(h_item, w_item) - if d: - if key not in d.keys(): - d[key] = w_item[key] - diff.append(d) - - return diff - - -def validate_ipv4(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is <ipv4 address>/<mask>, got invalid format {0}'.format(value)) - - if not is_masklen(address[1]): - module.fail_json( - msg='invalid value for mask: {0}, mask should be in range 0-32' - .format(address[1])) - - -def validate_ipv6(value, module): - if value: - address = value.split('/') - if len(address) != 2: - module.fail_json(msg='address format is <ipv6 address>/<mask>, got invalid format {0}'.format(value)) - else: - if not 0 <= int(address[1]) <= 128: - module.fail_json(msg='invalid value for mask: {0}, mask should be in range 0-128'.format(address[1])) - - -def validate_n_expand_ipv4(module, want): - # Check if input IPV4 is valid IP and expand IPV4 with its subnet mask - ip_addr_want = want.get('address') - if len(ip_addr_want.split(' ')) > 1: - return ip_addr_want - validate_ipv4(ip_addr_want, module) - ip = ip_addr_want.split('/') - if len(ip) == 2: - ip_addr_want = '{0} {1}'.format(ip[0], to_netmask(ip[1])) - - return ip_addr_want - - -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('gi'): - if_type = 'GigabitEthernet' - elif name.lower().startswith('fa'): - if_type = 'FastEthernet' - elif name.lower().startswith('fo'): - if_type = 'FortyGigE' - elif name.lower().startswith('te'): - if_type = 'TenGigE' - elif name.lower().startswith('twe'): - if_type = 'TwentyFiveGigE' - elif name.lower().startswith('hu'): - if_type = 'HundredGigE' - elif name.lower().startswith('vl'): - if_type = 'Vlan' - elif name.lower().startswith('lo'): - if_type = 'Loopback' - elif name.lower().startswith('be'): - if_type = 'Bundle-Ether' - elif name.lower().startswith('bp'): - if_type = 'Bundle-POS' - 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('GI'): - return 'GigabitEthernet' - elif interface.upper().startswith('FA'): - return 'FastEthernet' - elif interface.upper().startswith('FO'): - return 'FortyGigE' - elif interface.upper().startswith('ET'): - return 'Ethernet' - elif interface.upper().startswith('LO'): - return 'Loopback' - elif interface.upper().startswith('BE'): - return 'Bundle-Ether' - elif interface.upper().startswith('NV'): - return 'nve' - elif interface.upper().startswith('TWE'): - return 'TwentyFiveGigE' - elif interface.upper().startswith('HU'): - return 'HundredGigE' - elif interface.upper().startswith('PRE'): - return 'preconfigure' - else: - return 'unknown' - - -def isipaddress(data): - """ - Checks if the passed string is - a valid IPv4 or IPv6 address - """ - isipaddress = True - - try: - ipaddress.ip_address(data) - except ValueError: - isipaddress = False - - return isipaddress - - -def is_ipv4_address(data): - """ - Checks if the passed string is - a valid IPv4 address - """ - if '/' in data: - data = data.split('/')[0] - - if not isipaddress(to_text(data)): - raise ValueError('{0} is not a valid IP address'.format(data)) - - return (ipaddress.ip_address(to_text(data)).version == 4) - - -def prefix_to_address_wildcard(prefix): - """ Converts a IPv4 prefix into address and - wildcard mask - - :returns: IPv4 address and wildcard mask - """ - wildcard = [] - - subnet = to_text(ipaddress.IPv4Network(to_text(prefix)).netmask) - - for x in subnet.split('.'): - component = 255 - int(x) - wildcard.append(str(component)) - - wildcard = '.'.join(wildcard) - - return prefix.split('/')[0], wildcard |