Migrated to cisco.iosxr

pull/68298/head
Ansible Core Team 4 years ago committed by Matt Martz
parent 7e04b5ba8b
commit 5aa37733c3

@ -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

@ -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

@ -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'),
}

@ -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'}}

@ -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'}}

@ -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'}}

@ -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

@ -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

@ -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'
}
}

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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)

@ -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)

@ -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

@ -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)

@ -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)

@ -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

@ -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)

@ -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)

@ -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)

@ -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

@ -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)

@ -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

@ -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)

@ -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

@ -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)

@ -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

@ -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

@ -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

@ -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")

@ -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)

@ -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))

@ -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

@ -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

@ -1,664 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_interface
version_added: "2.4"
author:
- "Ganesh Nalawade (@ganeshrn)"
- "Kedar Kekan (@kedarX)"
short_description: Manage Interface on Cisco IOS XR network devices
description:
- This module provides declarative management of Interfaces
on Cisco IOS XR network devices.
deprecated:
removed_in: '2.13'
alternative: iosxr_interfaces
why: Newer and updated modules released with more functionality in Ansible 2.9
requirements:
- ncclient >= 0.5.3 when using netconf
- lxml >= 4.1.1 when using netconf
extends_documentation_fragment: iosxr
notes:
- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
- Tested against IOS XRv 6.1.3.
- Preconfiguration of physical interfaces is not supported with C(netconf) transport.
options:
name:
description:
- Name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0)
required: true
description:
description:
- Description of Interface being configured.
enabled:
description:
- Removes the shutdown configuration, which removes the forced administrative down on the interface,
enabling it to move to an up or down state.
type: bool
default: True
active:
description:
- Whether the interface is C(active) or C(preconfigured). Preconfiguration allows you to configure modular
services cards before they are inserted into the router. When the cards are inserted, they are instantly
configured. Active cards are the ones already inserted.
choices: ['active', 'preconfigure']
default: active
version_added: 2.5
speed:
description:
- Configure the speed for an interface. Default is auto-negotiation when not configured.
choices: ['10', '100', '1000']
mtu:
description:
- Sets the MTU value for the interface. Range is between 64 and 65535'
duplex:
description:
- Configures the interface duplex mode. Default is auto-negotiation when not configured.
choices: ['full', 'half']
tx_rate:
description:
- Transmit rate in bits per second (bps).
- This is state check parameter only.
- Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
rx_rate:
description:
- Receiver rate in bits per second (bps).
- This is state check parameter only.
- Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
aggregate:
description:
- List of Interface definitions. Include multiple interface configurations together,
one each on a separate line
delay:
description:
- Time in seconds to wait before checking for the operational state on remote
device. This wait is applicable for operational state argument which are
I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate).
default: 10
state:
description:
- State of the Interface configuration, C(up) means present and
operationally up and C(down) means present and operationally C(down)
default: present
choices: ['present', 'absent', 'up', 'down']
"""
EXAMPLES = """
- name: configure interface
iosxr_interface:
name: GigabitEthernet0/0/0/2
description: test-interface
speed: 100
duplex: half
mtu: 512
- name: remove interface
iosxr_interface:
name: GigabitEthernet0/0/0/2
state: absent
- name: make interface up
iosxr_interface:
name: GigabitEthernet0/0/0/2
enabled: True
- name: make interface down
iosxr_interface:
name: GigabitEthernet0/0/0/2
enabled: False
- name: Create interface using aggregate
iosxr_interface:
aggregate:
- name: GigabitEthernet0/0/0/3
- name: GigabitEthernet0/0/0/2
speed: 100
duplex: full
mtu: 512
state: present
- name: Create interface using aggregate along with additional params in aggregate
iosxr_interface:
aggregate:
- { name: GigabitEthernet0/0/0/3, description: test-interface 3 }
- { name: GigabitEthernet0/0/0/2, description: test-interface 2 }
speed: 100
duplex: full
mtu: 512
state: present
- name: Delete interface using aggregate
iosxr_interface:
aggregate:
- name: GigabitEthernet0/0/0/3
- name: GigabitEthernet0/0/0/2
state: absent
- name: Check intent arguments
iosxr_interface:
name: GigabitEthernet0/0/0/5
state: up
delay: 20
- name: Config + intent
iosxr_interface:
name: GigabitEthernet0/0/0/5
enabled: False
state: down
delay: 20
"""
RETURN = """
commands:
description: The list of configuration mode commands sent to device with transport C(cli)
returned: always (empty list when no commands to send)
type: list
sample:
- interface GigabitEthernet0/0/0/2
- description test-interface
- duplex half
- mtu 512
xml:
description: NetConf rpc xml sent to device with transport C(netconf)
returned: always (empty list when no xml rpc to send)
type: list
version_added: 2.5
sample:
- '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
<interface-configuration xc:operation="merge">
<active>act</active>
<interface-name>GigabitEthernet0/0/0/0</interface-name>
<description>test-interface-0</description>
<mtus><mtu>
<owner>GigabitEthernet</owner>
<mtu>512</mtu>
</mtu></mtus>
<ethernet xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-drivers-media-eth-cfg">
<speed>100</speed>
<duplex>half</duplex>
</ethernet>
</interface-configuration>
</interface-configurations></config>'
"""
import re
from time import sleep
from copy import deepcopy
import collections
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config, build_xml
from ansible.module_utils.network.iosxr.iosxr import run_commands, iosxr_argument_spec, get_oper
from ansible.module_utils.network.iosxr.iosxr import is_netconf, is_cliconf, etree_findall, etree_find
from ansible.module_utils.network.common.utils import conditional, remove_default_spec
def validate_mtu(value):
if value and not 64 <= int(value) <= 65535:
return False, 'mtu must be between 64 and 65535'
return True, None
class ConfigBase(object):
def __init__(self, module):
self._module = module
self._result = {'changed': False, 'warnings': []}
self._want = list()
self._have = list()
def validate_param_values(self, param=None):
for key, value in param.items():
# validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key)
if callable(validator):
rc, msg = validator(value)
if not rc:
self._module.fail_json(msg=msg)
def map_params_to_obj(self):
aggregate = self._module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = self._module.params[key]
self.validate_param_values(item)
d = item.copy()
match = re.match(r"(^[a-z]+)([0-9/]+$)", d['name'], re.I)
if match:
d['owner'] = match.groups()[0]
if d['active'] == 'preconfigure':
d['active'] = 'pre'
else:
d['active'] = 'act'
self._want.append(d)
else:
self.validate_param_values(self._module.params)
params = {
'name': self._module.params['name'],
'description': self._module.params['description'],
'speed': self._module.params['speed'],
'mtu': self._module.params['mtu'],
'duplex': self._module.params['duplex'],
'state': self._module.params['state'],
'delay': self._module.params['delay'],
'tx_rate': self._module.params['tx_rate'],
'rx_rate': self._module.params['rx_rate'],
'enabled': self._module.params['enabled'],
'active': self._module.params['active'],
}
match = re.match(r"(^[a-z]+)([0-9/]+$)", params['name'], re.I)
if match:
params['owner'] = match.groups()[0]
if params['active'] == 'preconfigure':
params['active'] = 'pre'
else:
params['active'] = 'act'
self._want.append(params)
class CliConfiguration(ConfigBase):
def __init__(self, module):
super(CliConfiguration, self).__init__(module)
def parse_shutdown(self, intf_config):
for cfg in intf_config:
match = re.search(r'%s' % 'shutdown', cfg, re.M)
if match:
return True
return False
def parse_config_argument(self, intf_config, arg):
for cfg in intf_config:
match = re.search(r'%s (.+)$' % arg, cfg, re.M)
if match:
return match.group(1)
def search_obj_in_list(self, name):
for obj in self._have:
if obj['name'] == name:
return obj
return None
def map_config_to_obj(self):
data = get_config(self._module, config_filter='interface')
data_lines = data.splitlines()
start_indexes = [i for i, e in enumerate(data_lines) if e.startswith('interface')]
end_indexes = [i for i, e in enumerate(data_lines) if e == '!']
intf_configs = list()
for start_index, end_index in zip(start_indexes, end_indexes):
intf_configs.append([i.strip() for i in data_lines[start_index:end_index]])
if not intf_configs:
return list()
for intf_config in intf_configs:
name = intf_config[0].strip().split()[1]
active = 'act'
if name == 'preconfigure':
active = 'pre'
name = intf_config[0].strip().split()[2]
obj = {
'name': name,
'description': self.parse_config_argument(intf_config, 'description'),
'speed': self.parse_config_argument(intf_config, 'speed'),
'duplex': self.parse_config_argument(intf_config, 'duplex'),
'mtu': self.parse_config_argument(intf_config, 'mtu'),
'enabled': not bool(self.parse_shutdown(intf_config)),
'active': active,
'state': 'present'
}
self._have.append(obj)
def map_obj_to_commands(self):
commands = list()
args = ('speed', 'description', 'duplex', 'mtu')
for want_item in self._want:
name = want_item['name']
disable = not want_item['enabled']
state = want_item['state']
obj_in_have = self.search_obj_in_list(name)
interface = 'interface ' + name
if state == 'absent' and obj_in_have:
commands.append('no ' + interface)
elif state in ('present', 'up', 'down'):
if obj_in_have:
for item in args:
candidate = want_item.get(item)
running = obj_in_have.get(item)
if candidate != running:
if candidate:
cmd = interface + ' ' + item + ' ' + str(candidate)
commands.append(cmd)
if disable and obj_in_have.get('enabled', False):
commands.append(interface + ' shutdown')
elif not disable and not obj_in_have.get('enabled', False):
commands.append('no ' + interface + ' shutdown')
else:
for item in args:
value = want_item.get(item)
if value:
commands.append(interface + ' ' + item + ' ' + str(value))
if not disable:
commands.append('no ' + interface + ' shutdown')
self._result['commands'] = commands
if commands:
commit = not self._module.check_mode
diff = load_config(self._module, commands, commit=commit)
if diff:
self._result['diff'] = dict(prepared=diff)
self._result['changed'] = True
def check_declarative_intent_params(self):
failed_conditions = []
for want_item in self._want:
want_state = want_item.get('state')
want_tx_rate = want_item.get('tx_rate')
want_rx_rate = want_item.get('rx_rate')
if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate:
continue
if self._result['changed']:
sleep(want_item['delay'])
command = 'show interfaces {0!s}'.format(want_item['name'])
out = run_commands(self._module, command)[0]
if want_state in ('up', 'down'):
match = re.search(r'%s (\w+)' % 'line protocol is', out, re.M)
have_state = None
if match:
have_state = match.group(1)
if have_state.strip() == 'administratively':
match = re.search(r'%s (\w+)' % 'administratively', out, re.M)
if match:
have_state = match.group(1)
if have_state is None or not conditional(want_state, have_state.strip()):
failed_conditions.append('state ' + 'eq({0!s})'.format(want_state))
if want_tx_rate:
match = re.search(r'%s (\d+)' % 'output rate', out, re.M)
have_tx_rate = None
if match:
have_tx_rate = match.group(1)
if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int):
failed_conditions.append('tx_rate ' + want_tx_rate)
if want_rx_rate:
match = re.search(r'%s (\d+)' % 'input rate', out, re.M)
have_rx_rate = None
if match:
have_rx_rate = match.group(1)
if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int):
failed_conditions.append('rx_rate ' + want_rx_rate)
if failed_conditions:
msg = 'One or more conditional statements have not been satisfied'
self._module.fail_json(msg=msg, failed_conditions=failed_conditions)
def run(self):
self.map_params_to_obj()
self.map_config_to_obj()
self.map_obj_to_commands()
self.check_declarative_intent_params()
return self._result
class NCConfiguration(ConfigBase):
def __init__(self, module):
super(NCConfiguration, self).__init__(module)
self._intf_meta = collections.OrderedDict()
self._shut_meta = collections.OrderedDict()
self._data_rate_meta = collections.OrderedDict()
self._line_state_meta = collections.OrderedDict()
def map_obj_to_xml_rpc(self):
self._intf_meta.update([
('interface-configuration', {'xpath': 'interface-configurations/interface-configuration', 'tag': True, 'attrib': 'operation'}),
('a:active', {'xpath': 'interface-configurations/interface-configuration/active', 'operation': 'edit'}),
('a:name', {'xpath': 'interface-configurations/interface-configuration/interface-name'}),
('a:description', {'xpath': 'interface-configurations/interface-configuration/description', 'operation': 'edit'}),
('mtus', {'xpath': 'interface-configurations/interface-configuration/mtus', 'tag': True, 'operation': 'edit'}),
('mtu', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu', 'tag': True, 'operation': 'edit'}),
('a:owner', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu/owner', 'operation': 'edit'}),
('a:mtu', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu/mtu', 'operation': 'edit'}),
('CEthernet', {'xpath': 'interface-configurations/interface-configuration/ethernet', 'tag': True, 'operation': 'edit', 'ns': True}),
('a:speed', {'xpath': 'interface-configurations/interface-configuration/ethernet/speed', 'operation': 'edit'}),
('a:duplex', {'xpath': 'interface-configurations/interface-configuration/ethernet/duplex', 'operation': 'edit'}),
])
self._shut_meta.update([
('interface-configuration', {'xpath': 'interface-configurations/interface-configuration', 'tag': True}),
('a:active', {'xpath': 'interface-configurations/interface-configuration/active', 'operation': 'edit'}),
('a:name', {'xpath': 'interface-configurations/interface-configuration/interface-name'}),
('shutdown', {'xpath': 'interface-configurations/interface-configuration/shutdown', 'tag': True, 'operation': 'edit', 'attrib': 'operation'}),
])
state = self._module.params['state']
_get_filter = build_xml('interface-configurations', xmap=self._intf_meta, params=self._want, opcode="filter")
running = get_config(self._module, source='running', config_filter=_get_filter)
intfcfg_nodes = etree_findall(running, 'interface-configuration')
intf_list = set()
shut_list = set()
for item in intfcfg_nodes:
intf_name = etree_find(item, 'interface-name').text
if intf_name is not None:
intf_list.add(intf_name)
if etree_find(item, 'shutdown') is not None:
shut_list.add(intf_name)
intf_params = list()
shut_params = list()
noshut_params = list()
for index, item in enumerate(self._want):
if item['name'] in intf_list:
intf_params.append(item)
if not item['enabled']:
shut_params.append(item)
if item['name'] in shut_list and item['enabled']:
noshut_params.append(item)
opcode = None
if state == 'absent':
if intf_params:
opcode = "delete"
elif state in ('present', 'up', 'down'):
intf_params = self._want
opcode = 'merge'
self._result['xml'] = []
_edit_filter_list = list()
if opcode:
_edit_filter_list.append(build_xml('interface-configurations', xmap=self._intf_meta,
params=intf_params, opcode=opcode))
if opcode == 'merge':
if len(shut_params):
_edit_filter_list.append(build_xml('interface-configurations', xmap=self._shut_meta,
params=shut_params, opcode='merge'))
if len(noshut_params):
_edit_filter_list.append(build_xml('interface-configurations', xmap=self._shut_meta,
params=noshut_params, opcode='delete'))
diff = None
if len(_edit_filter_list):
commit = not self._module.check_mode
diff = load_config(self._module, _edit_filter_list, commit=commit, running=running,
nc_get_filter=_get_filter)
if diff:
if self._module._diff:
self._result['diff'] = dict(prepared=diff)
self._result['xml'] = _edit_filter_list
self._result['changed'] = True
def check_declarative_intent_params(self):
failed_conditions = []
self._data_rate_meta.update([
('interfaces', {'xpath': 'infra-statistics/interfaces', 'tag': True}),
('interface', {'xpath': 'infra-statistics/interfaces/interface', 'tag': True}),
('a:name', {'xpath': 'infra-statistics/interfaces/interface/interface-name'}),
('cache', {'xpath': 'infra-statistics/interfaces/interface/cache', 'tag': True}),
('data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate', 'tag': True}),
('input-data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate/input-data-rate', 'tag': True}),
('output-data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate/output-data-rate', 'tag': True}),
])
self._line_state_meta.update([
('data-nodes', {'xpath': 'interface-properties/data-nodes', 'tag': True}),
('data-node', {'xpath': 'interface-properties/data-nodes/data-node', 'tag': True}),
('system-view', {'xpath': 'interface-properties/data-nodes/data-node/system-view', 'tag': True}),
('interfaces', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces', 'tag': True}),
('interface', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface', 'tag': True}),
('a:name', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface/interface-name'}),
('line-state', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface/line-state', 'tag': True}),
])
_rate_filter = build_xml('infra-statistics', xmap=self._data_rate_meta, params=self._want, opcode="filter")
out = get_oper(self._module, filter=_rate_filter)
data_rate_list = etree_findall(out, 'interface')
data_rate_map = dict()
for item in data_rate_list:
data_rate_map.update({etree_find(item, 'interface-name').text: dict()})
data_rate_map[etree_find(item, 'interface-name').text].update({'input-data-rate': etree_find(item, 'input-data-rate').text,
'output-data-rate': etree_find(item, 'output-data-rate').text})
_line_state_filter = build_xml('interface-properties', xmap=self._line_state_meta, params=self._want, opcode="filter")
out = get_oper(self._module, filter=_line_state_filter)
line_state_list = etree_findall(out, 'interface')
line_state_map = dict()
for item in line_state_list:
line_state_map.update({etree_find(item, 'interface-name').text: etree_find(item, 'line-state').text})
for want_item in self._want:
want_state = want_item.get('state')
want_tx_rate = want_item.get('tx_rate')
want_rx_rate = want_item.get('rx_rate')
if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate:
continue
if self._result['changed']:
sleep(want_item['delay'])
if want_state in ('up', 'down'):
if want_state not in line_state_map[want_item['name']]:
failed_conditions.append('state ' + 'eq({0!s})'.format(want_state))
if want_tx_rate:
if want_tx_rate != data_rate_map[want_item['name']]['output-data-rate']:
failed_conditions.append('tx_rate ' + want_tx_rate)
if want_rx_rate:
if want_rx_rate != data_rate_map[want_item['name']]['input-data-rate']:
failed_conditions.append('rx_rate ' + want_rx_rate)
if failed_conditions:
msg = 'One or more conditional statements have not been satisfied'
self._module.fail_json(msg=msg, failed_conditions=failed_conditions)
def run(self):
self.map_params_to_obj()
self.map_obj_to_xml_rpc()
self.check_declarative_intent_params()
return self._result
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(type='str'),
description=dict(type='str'),
speed=dict(choices=['10', '100', '1000']),
mtu=dict(),
duplex=dict(choices=['full', 'half']),
enabled=dict(default=True, type='bool'),
active=dict(default='active', type='str', choices=['active', 'preconfigure']),
tx_rate=dict(),
rx_rate=dict(),
delay=dict(default=10, type='int'),
state=dict(default='present',
choices=['present', 'absent', 'up', 'down'])
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec['name'] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(iosxr_argument_spec)
required_one_of = [['name', 'aggregate']]
mutually_exclusive = [['name', 'aggregate']]
module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
config_object = None
if is_cliconf(module):
# Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
# module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
# version='2.9')
config_object = CliConfiguration(module)
elif is_netconf(module):
if module.params['active'] == 'preconfigure':
module.fail_json(msg="Physical interface pre-configuration is not supported with transport 'netconf'")
config_object = NCConfiguration(module)
result = {}
if config_object:
result = config_object.run()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,743 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_acl_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: iosxr_acl_interfaces
version_added: "2.10"
short_description: Manage Access Control Lists (ACLs) configuration for interfaces in IOS-XR.
description:
- This module manages adding and removing Access Control Lists (ACLs) from interfaces on devices running IOS-XR software.
author: Nilashish Chakraborty (@NilashishC)
options:
config:
description: A dictionary of ACL options for interfaces.
type: list
elements: dict
suboptions:
name:
description:
- Name/Identifier for the interface
type: str
required: True
access_groups:
type: list
elements: dict
description:
- Specifies ACLs attached to the interfaces.
suboptions:
afi:
description:
- Specifies the AFI for the ACL(s) to be configured on this interface.
type: str
choices: ['ipv4', 'ipv6']
required: True
acls:
type: list
description:
- Specifies the ACLs for the provided AFI.
elements: dict
suboptions:
name:
description:
- Specifies the name of the IPv4/IPv6 ACL for the interface.
type: str
required: True
direction:
description:
- Specifies the direction of packets that the ACL will be applied on.
type: str
choices: ['in', 'out']
required: True
running_config:
description:
- The module, by default, will connect to the remote device and
retrieve the current running-config to use as a base for comparing
against the contents of source. There are times when it is not
desirable to have the task get the current running-config for
every task in a playbook. The I(running_config) argument allows the
implementer to pass in the configuration to use as the base
config for comparison. This value of this option should be the
output received from device by executing command
B(show running-config interface).
type: str
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- gathered
- parsed
- rendered
default: merged
"""
EXAMPLES = """
# Using merged
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:22:32.911 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# !
- name: Merge the provided configuration with the existing running configuration
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
- afi: ipv6
acls:
- name: acl6_1
direction: in
- name: acl6_2
direction: out
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: out
state: merged
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:27:49.378 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
# Using merged to update interface ACL configuration
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:27:49.378 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
#
- name: Update acl_interfaces configuration using merged
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_2
direction: out
- name: acl_1
direction: in
state: merged
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:27:49.378 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# !
#
# Using replaced
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
- name: Replace device configurations of listed interface with provided configurations
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv6
acls:
- name: acl6_3
direction: in
state: replaced
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv6 access-group acl6_3 ingress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
#
# Using overridden
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
#
- name: Overridde all interface ACL configuration with provided configuration
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_2
direction: in
- afi: ipv6
acls:
- name: acl6_3
direction: out
state: overridden
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_2 ingress
# ipv6 access-group acl6_3 egress
# !
#
# Using 'deleted' to delete all ACL attributes of a single interface
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
#
- name: Delete all ACL attributes of GigabitEthernet0/0/0/1
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/1
state: deleted
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# !
#
# Using 'deleted' to delete a single attached ACL from an interface
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
#
- name: Delete a single ACL attached to GigabitEthernet0/0/0/0
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_2
direction: out
state: deleted
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# !
#
# Using 'deleted' to delete all ACLs of a particular AFI from an interface
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
#
- name: Delete all IPv6 ACLs attached to GigabitEthernet0/0/0/0
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv6
state: deleted
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# !
#
# Using 'deleted' to remove all ACLs attached to all the interfaces in the device
# Before state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
#
- name: Delete all ACL interfaces configuration from the device
iosxr_acl_interfaces:
state: deleted
# After state:
# -------------
#
# RP/0/RP0/CPU0:ios#sh running-config interface
# Wed Jan 15 12:34:56.689 UTC
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# !
#
# Using parsed
# parsed.cfg
# ------------
#
# interface MgmtEth0/RP0/CPU0/0
# ipv4 address dhcp
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# ipv4 access-group acl_1 ingress
# ipv4 access-group acl_2 egress
# ipv6 access-group acl6_1 ingress
# ipv6 access-group acl6_2 egress
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# ipv4 access-group acl_1 egress
# !
# - name: Convert ACL interfaces config to argspec without connecting to the appliance
# iosxr_acl_interfaces:
# running_config: "{{ lookup('file', './parsed.cfg') }}"
# state: parsed
# Task Output (redacted)
# -----------------------
# "parsed": [
# {
# "name": "MgmtEth0/RP0/CPU0/0"
# },
# {
# "access_groups": [
# {
# "acls": [
# {
# "direction": "in",
# "name": "acl_1"
# },
# {
# "direction": "out",
# "name": "acl_2"
# }
# ],
# "afi": "ipv4"
# },
# {
# "acls": [
# {
# "direction": "in",
# "name": "acl6_1"
# },
# {
# "direction": "out",
# "name": "acl6_2"
# }
# ],
# "afi": "ipv6"
# }
# ],
# "name": "GigabitEthernet0/0/0/0"
# },
# {
# "access_groups": [
# {
# "acls": [
# {
# "direction": "out",
# "name": "acl_1"
# }
# ],
# "afi": "ipv4"
# }
# ],
# "name": "GigabitEthernet0/0/0/1"
# }
# ]
# }
# Using gathered
- name: Gather ACL interfaces facts using gathered state
iosxr_acl_interfaces:
state: gathered
# Task Output (redacted)
# -----------------------
#
# "gathered": [
# {
# "name": "MgmtEth0/RP0/CPU0/0"
# },
# {
# "access_groups": [
# {
# "acls": [
# {
# "direction": "in",
# "name": "acl_1"
# },
# {
# "direction": "out",
# "name": "acl_2"
# }
# ],
# "afi": "ipv4"
# }
# "name": "GigabitEthernet0/0/0/0"
# },
# {
# "access_groups": [
# {
# "acls": [
# {
# "direction": "in",
# "name": "acl6_1"
# }
# ],
# "afi": "ipv6"
# }
# "name": "GigabitEthernet0/0/0/1"
# }
# ]
# Using rendered
- name: Render platform specific commands from task input using rendered state
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
state: rendered
# Task Output (redacted)
# -----------------------
# "rendered": [
# "interface GigabitEthernet0/0/0/0",
# "ipv4 access-group acl_1 ingress",
# "ipv4 access-group acl_2 egress"
# ]
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample:
- "interface GigabitEthernet0/0/0/1"
- "ipv4 access-group acl_1 ingress"
- "ipv4 access-group acl_2 egress"
- "ipv6 access-group acl6_1 ingress"
- "interface GigabitEthernet0/0/0/2"
- "no ipv4 access-group acl_3 ingress"
- "ipv4 access-group acl_4 egress"
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.acl_interfaces.acl_interfaces import Acl_interfacesArgs
from ansible.module_utils.network.iosxr.config.acl_interfaces.acl_interfaces import Acl_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',)),
('state', 'overridden', ('config',)),
('state', 'rendered', ('config',)),
('state', 'parsed', ('running_config',))]
module = AnsibleModule(argument_spec=Acl_interfacesArgs.argument_spec, required_if=required_if,
supports_check_mode=True)
result = Acl_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

@ -1,255 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_banner
version_added: "2.4"
author:
- Trishna Guha (@trishnaguha)
- Kedar Kekan (@kedarX)
short_description: Manage multiline banners on Cisco IOS XR devices
description:
- This module will configure both exec and motd banners on remote device
running Cisco IOS XR. It allows playbooks to add or remove
banner text from the running configuration.
requirements:
- ncclient >= 0.5.3 when using netconf
- lxml >= 4.1.1 when using netconf
extends_documentation_fragment: iosxr
notes:
- Tested against IOS XRv 6.1.3.
- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
options:
banner:
description:
- Specifies the type of banner to configure on remote device.
required: true
choices: ['login', 'motd']
text:
description:
- Banner text to be configured. Accepts multiline string,
without empty lines. Requires I(state=present).
state:
description:
- Existential state of the configuration on the device.
default: present
choices: ['present', 'absent']
"""
EXAMPLES = """
- name: configure the login banner
iosxr_banner:
banner: login
text: |
this is my login banner
that contains a multiline
string
state: present
- name: remove the motd banner
iosxr_banner:
banner: motd
state: absent
- name: Configure banner from file
iosxr_banner:
banner: motd
text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}"
state: present
"""
RETURN = """
commands:
description: The list of configuration mode commands sent to device with transport C(cli)
returned: always (empty list when no commands to send)
type: list
sample:
- banner login
- this is my login banner
- that contains a multiline
- string
xml:
description: NetConf rpc xml sent to device with transport C(netconf)
returned: always (empty list when no xml rpc to send)
type: list
version_added: 2.5
sample:
- '<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>'
"""
import re
import collections
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec
from ansible.module_utils.network.iosxr.iosxr import build_xml, is_cliconf
from ansible.module_utils.network.iosxr.iosxr import etree_find, is_netconf
class ConfigBase(object):
def __init__(self, module):
self._module = module
self._result = {'changed': False, 'warnings': []}
self._want = {}
self._have = {}
def map_params_to_obj(self):
text = self._module.params['text']
if text:
text = "{0!r}".format(str(text).strip())
self._want.update({
'banner': self._module.params['banner'],
'text': text,
'state': self._module.params['state']
})
class CliConfiguration(ConfigBase):
def __init__(self, module):
super(CliConfiguration, self).__init__(module)
def map_obj_to_commands(self):
commands = list()
state = self._module.params['state']
if state == 'absent':
if self._have.get('state') != 'absent' and ('text' in self._have.keys() and self._have['text']):
commands.append('no banner {0!s}'.format(self._module.params['banner']))
elif state == 'present':
if (self._want['text'] and
self._want['text'].encode().decode('unicode_escape').strip("'") != self._have.get('text')):
banner_cmd = 'banner {0!s} '.format(self._module.params['banner'])
banner_cmd += self._want['text'].strip()
commands.append(banner_cmd)
self._result['commands'] = commands
if commands:
commit = not self._module.check_mode
diff = load_config(self._module, commands, commit=commit)
if diff:
self._result['diff'] = dict(prepared=diff)
self._result['changed'] = True
def map_config_to_obj(self):
cli_filter = 'banner {0!s}'.format(self._module.params['banner'])
output = get_config(self._module, config_filter=cli_filter)
match = re.search(r'banner (\S+) (.*)', output, re.DOTALL)
if match:
text = match.group(2).strip("'")
else:
text = None
obj = {'banner': self._module.params['banner'], 'state': 'absent'}
if output:
obj['text'] = text
obj['state'] = 'present'
self._have.update(obj)
def run(self):
self.map_params_to_obj()
self.map_config_to_obj()
self.map_obj_to_commands()
return self._result
class NCConfiguration(ConfigBase):
def __init__(self, module):
super(NCConfiguration, self).__init__(module)
self._banners_meta = collections.OrderedDict()
self._banners_meta.update([
('banner', {'xpath': 'banners/banner', 'tag': True, 'attrib': "operation"}),
('a:banner', {'xpath': 'banner/banner-name'}),
('a:text', {'xpath': 'banner/banner-text', 'operation': 'edit'})
])
def map_obj_to_xml_rpc(self):
state = self._module.params['state']
_get_filter = build_xml('banners', xmap=self._banners_meta, params=self._module.params, opcode="filter")
running = get_config(self._module, source='running', config_filter=_get_filter)
banner_name = None
banner_text = None
if etree_find(running, 'banner-text') is not None:
banner_name = etree_find(running, 'banner-name').text
banner_text = etree_find(running, 'banner-text').text
opcode = None
if state == 'absent' and banner_name == self._module.params['banner'] and len(banner_text):
opcode = "delete"
elif state == 'present':
opcode = 'merge'
self._result['xml'] = []
if opcode:
_edit_filter = build_xml('banners', xmap=self._banners_meta, params=self._module.params, opcode=opcode)
if _edit_filter is not None:
commit = not self._module.check_mode
diff = load_config(self._module, _edit_filter, commit=commit, running=running, nc_get_filter=_get_filter)
if diff:
self._result['xml'] = _edit_filter
if self._module._diff:
self._result['diff'] = dict(prepared=diff)
self._result['changed'] = True
def run(self):
self.map_params_to_obj()
self.map_obj_to_xml_rpc()
return self._result
def main():
""" main entry point for module execution
"""
argument_spec = dict(
banner=dict(required=True, choices=['login', 'motd']),
text=dict(),
state=dict(default='present', choices=['present', 'absent'])
)
argument_spec.update(iosxr_argument_spec)
required_if = [('state', 'present', ('text',))]
module = AnsibleModule(argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True)
config_object = None
if is_cliconf(module):
# Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
# module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
# version='2.9')
config_object = CliConfiguration(module)
elif is_netconf(module):
config_object = NCConfiguration(module)
result = None
if config_object is not None:
result = config_object.run()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,296 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (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 __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_bgp
version_added: "2.8"
author: "Nilashish Chakraborty (@NilashishC)"
short_description: Configure global BGP protocol settings on Cisco IOS-XR
description:
- This module provides configuration management of global BGP parameters
on devices running Cisco IOS-XR
notes:
- Tested against Cisco IOS XR Software Version 6.1.3
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
options:
config:
description:
- Specifies the BGP related configuration.
suboptions:
bgp_as:
description:
- Specifies the BGP Autonomous System (AS) number to configure on the device.
type: int
required: true
router_id:
description:
- Configures the BGP routing process router-id value.
default: null
log_neighbor_changes:
description:
- Enable/disable logging neighbor up/down and reset reason.
type: bool
neighbors:
description:
- Specifies BGP neighbor related configurations.
suboptions:
neighbor:
description:
- Neighbor router address.
required: True
remote_as:
description:
- Remote AS of the BGP neighbor to configure.
type: int
required: True
update_source:
description:
- Source of the routing updates.
password:
description:
- Password to authenticate the BGP peer connection.
enabled:
description:
- Administratively shutdown or enable a neighbor.
type: bool
description:
description:
- Neighbor specific description.
advertisement_interval:
description:
- Specifies the minimum interval (in seconds) between sending BGP routing updates.
- The range is from 0 to 600.
type: int
tcp_mss:
description:
- Specifies the TCP initial maximum segment size to use.
- The range is from 68 to 10000.
type: int
ebgp_multihop:
description:
- Specifies the maximum hop count for EBGP neighbors not on directly connected networks.
- The range is from 0 to 255.
type: int
timers:
description:
- Specifies BGP neighbor timer related configurations.
suboptions:
keepalive:
description:
- Frequency with which the Cisco IOS-XR software sends keepalive messages to its peer.
- The range is from 0 to 65535.
type: int
required: True
holdtime:
description:
- Interval after not receiving a keepalive message that the software declares a peer dead.
- The range is from 3 to 65535.
type: int
required: True
min_neighbor_holdtime:
description:
- Interval specifying the minimum acceptable hold-time from a BGP neighbor.
- The minimum acceptable hold-time must be less than, or equal to, the interval specified in the holdtime argument.
- The range is from 3 to 65535.
type: int
address_family:
description:
- Specifies BGP address family related configurations.
suboptions:
afi:
description:
- Type of address family to configure.
choices:
- ipv4
- ipv6
required: True
safi:
description:
- Specifies the type of cast for the address family.
choices:
- flowspec
- unicast
- multicast
- labeled-unicast
default: unicast
redistribute:
description:
- Specifies the redistribute information from another routing protocol.
suboptions:
protocol:
description:
- Specifies the protocol for configuring redistribute information.
choices: ['ospf', 'ospfv3', 'eigrp', 'isis', 'static', 'connected', 'lisp', 'mobile', 'rip', 'subscriber']
required: True
id:
description:
- Identifier for the routing protocol for configuring redistribute information.
- Valid for protocols 'ospf', 'eigrp', 'isis' and 'ospfv3'.
metric:
description:
- Specifies the metric for redistributed routes.
route_map:
description:
- Specifies the route map reference.
networks:
description:
- Specify networks to announce via BGP.
- For operation replace, this option is mutually exclusive with root level networks option.
suboptions:
network:
description:
- Network ID to announce via BGP.
required: True
masklen:
description:
- Subnet mask length for the network to announce(e.g, 8, 16, 24, etc.).
route_map:
description:
- Route map to modify the attributes.
operation:
description:
- Specifies the operation to be performed on the BGP process configured on the device.
- In case of merge, the input configuration will be merged with the existing BGP configuration on the device.
- In case of replace, if there is a diff between the existing configuration and the input configuration, the
existing configuration will be replaced by the input configuration for every option that has the diff.
- In case of override, all the existing BGP configuration will be removed from the device and replaced with
the input configuration.
- In case of delete the existing BGP configuration will be removed from the device.
default: merge
choices: ['merge', 'replace', 'override', 'delete']
"""
EXAMPLES = """
- name: configure global bgp as 65000
iosxr_bgp:
bgp_as: 65000
router_id: 1.1.1.1
neighbors:
- neighbor: 182.168.10.1
remote_as: 500
description: PEER_1
- neighbor: 192.168.20.1
remote_as: 500
update_source: GigabitEthernet 0/0/0/0
address_family:
- name: ipv4
cast: unicast
networks:
- network: 192.168.2.0/23
- network: 10.0.0.0/8
redistribute:
- protocol: ospf
id: 400
metric: 110
- name: remove bgp as 65000 from config
ios_bgp:
bgp_as: 65000
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- router bgp 65000
- bgp router-id 1.1.1.1
- neighbor 182.168.10.1 remote-as 500
- neighbor 182.168.10.1 description PEER_1
- neighbor 192.168.20.1 remote-as 500
- neighbor 192.168.20.1 update-source GigabitEthernet0/0/0/0
- address-family ipv4 unicast
- redistribute ospf 400 metric 110
- network 192.168.2.0/23
- network 10.0.0.0/8
- exit
"""
from ansible.module_utils._text import to_text
from ansible.module_utils.network.iosxr.providers.module import NetworkModule
from ansible.module_utils.network.iosxr.providers.cli.config.bgp.process import REDISTRIBUTE_PROTOCOLS
def main():
""" main entry point for module execution
"""
network_spec = {
'prefix': dict(required=True),
'masklen': dict(type='int', required=True),
'route_map': dict(),
}
redistribute_spec = {
'protocol': dict(choices=REDISTRIBUTE_PROTOCOLS, required=True),
'id': dict(),
'metric': dict(type='int'),
'route_map': dict(),
}
timer_spec = {
'keepalive': dict(type='int'),
'holdtime': dict(type='int'),
'min_neighbor_holdtime': dict(type='int'),
}
neighbor_spec = {
'neighbor': dict(required=True),
'remote_as': dict(type='int', required=True),
'update_source': dict(),
'password': dict(no_log=True),
'enabled': dict(type='bool'),
'description': dict(),
'advertisement_interval': dict(type='int'),
'ebgp_multihop': dict(type='int'),
'tcp_mss': dict(type='int'),
'timers': dict(type='dict', options=timer_spec),
}
address_family_spec = {
'afi': dict(choices=['ipv4', 'ipv6'], required=True),
'safi': dict(choices=['flowspec', 'labeled-unicast', 'multicast', 'unicast'], default='unicast'),
'networks': dict(type='list', elements='dict', options=network_spec),
'redistribute': dict(type='list', elements='dict', options=redistribute_spec),
}
config_spec = {
'bgp_as': dict(type='int', required=True),
'router_id': dict(),
'log_neighbor_changes': dict(type='bool'),
'neighbors': dict(type='list', elements='dict', options=neighbor_spec),
'address_family': dict(type='list', elements='dict', options=address_family_spec),
}
argument_spec = {
'config': dict(type='dict', options=config_spec),
'operation': dict(default='merge', choices=['merge', 'replace', 'override', 'delete'])
}
module = NetworkModule(argument_spec=argument_spec,
supports_check_mode=True)
try:
result = module.edit_config(config_filter='router bgp')
except Exception as exc:
module.fail_json(msg=to_text(exc))
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,212 +0,0 @@
#!/usr/bin/python
#
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_command
version_added: "2.1"
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
short_description: Run commands on remote devices running Cisco IOS XR
description:
- Sends arbitrary commands to an IOS XR node and returns the results
read from the device. This module includes an
argument that will cause the module to wait for a specific condition
before returning or timing out if the condition is not met.
- This module does not support running commands in configuration mode.
Please use M(iosxr_config) to configure iosxr devices.
extends_documentation_fragment: iosxr
notes:
- Make sure the user has been authorized to execute commands terminal length 0, terminal width 512 and terminal exec prompt no-timestamp.
- This module works with C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
- This module does not support C(netconf) connection.
- Tested against IOS XR 6.1.3
options:
commands:
description:
- List of commands to send to the remote iosxr device over the
configured provider. The resulting output from the command
is returned. If the I(wait_for) argument is provided, the
module is not returned until the condition is satisfied or
the number of retries has expired.
required: true
wait_for:
description:
- List of conditions to evaluate against the output of the
command. The task will wait for each condition to be true
before moving forward. If the conditional is not true
within the configured number of retries, the task fails.
See examples.
aliases: ['waitfor']
version_added: "2.2"
match:
description:
- The I(match) argument is used in conjunction with the
I(wait_for) argument to specify the match policy. Valid
values are C(all) or C(any). If the value is set to C(all)
then all conditionals in the wait_for must be satisfied. If
the value is set to C(any) then only one of the values must be
satisfied.
default: all
choices: ['any', 'all']
version_added: "2.2"
retries:
description:
- Specifies the number of retries a command should by tried
before it is considered failed. The command is run on the
target device every retry and evaluated against the
I(wait_for) conditions.
default: 10
interval:
description:
- Configures the interval in seconds to wait between retries
of the command. If the command does not pass the specified
conditions, the interval indicates how long to wait before
trying the command again.
default: 1
"""
EXAMPLES = """
tasks:
- name: run show version on remote devices
iosxr_command:
commands: show version
- name: run show version and check to see if output contains iosxr
iosxr_command:
commands: show version
wait_for: result[0] contains IOS-XR
- name: run multiple commands on remote nodes
iosxr_command:
commands:
- show version
- show interfaces
- { command: example command that prompts, prompt: expected prompt, answer: yes}
- name: run multiple commands and evaluate the output
iosxr_command:
commands:
- show version
- show interfaces
wait_for:
- result[0] contains IOS-XR
- result[1] contains Loopback0
"""
RETURN = """
stdout:
description: The set of responses from the commands
returned: always apart from low level errors (such as action plugin)
type: list
sample: ['...', '...']
stdout_lines:
description: The value of stdout split into a list
returned: always apart from low level errors (such as action plugin)
type: list
sample: [['...', '...'], ['...'], ['...']]
failed_conditions:
description: The list of conditionals that have failed
returned: failed
type: list
sample: ['...', '...']
"""
import time
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.parsing import Conditional
from ansible.module_utils.network.common.utils import to_lines
from ansible.module_utils.network.iosxr.iosxr import run_commands, iosxr_argument_spec
from ansible.module_utils.network.iosxr.iosxr import command_spec
def parse_commands(module, warnings):
commands = module.params['commands']
for item in list(commands):
try:
command = item['command']
except Exception:
command = item
if module.check_mode and not command.startswith('show'):
warnings.append(
'Only show commands are supported when using check mode, not '
'executing %s' % command
)
commands.remove(item)
return commands
def main():
argument_spec = dict(
commands=dict(type='list', required=True),
wait_for=dict(type='list', aliases=['waitfor']),
match=dict(default='all', choices=['all', 'any']),
retries=dict(default=10, type='int'),
interval=dict(default=1, type='int')
)
argument_spec.update(iosxr_argument_spec)
argument_spec.update(command_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
warnings = list()
result = {'changed': False, 'warnings': warnings}
commands = parse_commands(module, warnings)
wait_for = module.params['wait_for'] or list()
try:
conditionals = [Conditional(c) for c in wait_for]
except AttributeError as exc:
module.fail_json(msg=to_text(exc))
retries = module.params['retries']
interval = module.params['interval']
match = module.params['match']
while retries > 0:
responses = run_commands(module, commands)
for item in list(conditionals):
if item(responses):
if match == 'any':
conditionals = list()
break
conditionals.remove(item)
if not conditionals:
break
time.sleep(interval)
retries -= 1
if conditionals:
failed_conditions = [item.raw for item in conditionals]
msg = 'One or more conditional statements have not been satisfied'
module.fail_json(msg=msg, failed_conditions=failed_conditions)
result.update({
'stdout': responses,
'stdout_lines': list(to_lines(responses)),
})
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,436 +0,0 @@
#!/usr/bin/python
#
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_config
version_added: "2.1"
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
short_description: Manage Cisco IOS XR configuration sections
description:
- Cisco IOS XR configurations use a simple block indent file syntax
for segmenting configuration into sections. This module provides
an implementation for working with IOS XR configuration sections in
a deterministic way.
extends_documentation_fragment: iosxr
notes:
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
- Tested against IOS XRv 6.1.3.
- This module does not support C(netconf) connection
- Abbreviated commands are NOT idempotent, see
L(Network FAQ,../network/user_guide/faq.html#why-do-the-config-modules-always-return-changed-true-with-abbreviated-commands).
- Avoid service disrupting changes (viz. Management IP) from config replace.
- Do not use C(end) in the replace config file.
options:
lines:
description:
- The ordered set of commands that should be configured in the
section. The commands must be the exact same commands as found
in the device running-config. Be sure to note the configuration
command syntax as some commands are automatically modified by the
device config parser.
aliases: ['commands']
parents:
description:
- The ordered set of parents that uniquely identify the section or hierarchy
the commands should be checked against. If the parents argument
is omitted, the commands are checked against the set of top
level or global commands.
src:
description:
- Specifies the source path to the file that contains the configuration
or configuration template to load. The path to the source file can
either be the full path on the Ansible control host or a relative
path from the playbook or role root directory. This argument is mutually
exclusive with I(lines), I(parents).
version_added: "2.2"
before:
description:
- The ordered set of commands to push on to the command stack if
a change needs to be made. This allows the playbook designer
the opportunity to perform configuration commands prior to pushing
any changes without affecting how the set of commands are matched
against the system.
after:
description:
- The ordered set of commands to append to the end of the command
stack if a change needs to be made. Just like with I(before) this
allows the playbook designer to append a set of commands to be
executed after the command set.
match:
description:
- Instructs the module on the way to perform the matching of
the set of commands against the current device config. If
match is set to I(line), commands are matched line by line. If
match is set to I(strict), command lines are matched with respect
to position. If match is set to I(exact), command lines
must be an equal match. Finally, if match is set to I(none), the
module will not attempt to compare the source configuration with
the running configuration on the remote device.
default: line
choices: ['line', 'strict', 'exact', 'none']
replace:
description:
- Instructs the module on the way to perform the configuration
on the device. If the replace argument is set to I(line) then
the modified lines are pushed to the device in configuration
mode. If the replace argument is set to I(block) then the entire
command block is pushed to the device in configuration mode if any
line is not correct.
default: line
choices: ['line', 'block', 'config']
force:
description:
- The force argument instructs the module to not consider the
current devices running-config. When set to true, this will
cause the module to push the contents of I(src) into the device
without first checking if already configured.
- Note this argument should be considered deprecated. To achieve
the equivalent, set the C(match=none) which is idempotent. This argument
will be removed in a future release.
type: bool
default: 'no'
version_added: "2.2"
config:
description:
- The module, by default, will connect to the remote device and
retrieve the current running-config to use as a base for comparing
against the contents of source. There are times when it is not
desirable to have the task get the current running-config for
every task in a playbook. The I(config) argument allows the
implementer to pass in the configuration to use as the base
config for comparison.
backup:
description:
- This argument will cause the module to create a full backup of
the current C(running-config) from the remote device before any
changes are made. If the C(backup_options) value is not given,
the backup file is written to the C(backup) folder in the playbook
root directory or role root directory, if playbook is part of an
ansible role. If the directory does not exist, it is created.
type: bool
default: 'no'
version_added: "2.2"
comment:
description:
- Allows a commit description to be specified to be included
when the configuration is committed. If the configuration is
not changed or committed, this argument is ignored.
default: 'configured by iosxr_config'
version_added: "2.2"
admin:
description:
- Enters into administration configuration mode for making config
changes to the device.
type: bool
default: 'no'
version_added: "2.4"
label:
description:
- Allows a commit label to be specified to be included when the
configuration is committed. A valid label must begin with an alphabet
and not exceed 30 characters, only alphabets, digits, hyphens and
underscores are allowed. If the configuration is not changed or
committed, this argument is ignored.
version_added: "2.7"
backup_options:
description:
- This is a dict object containing configurable options related to backup file path.
The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set
to I(no) this option will be silently ignored.
suboptions:
filename:
description:
- The filename to be used to store the backup configuration. If the filename
is not given it will be generated based on the hostname, current time and date
in format defined by <hostname>_config.<current-date>@<current-time>
dir_path:
description:
- This option provides the path ending with directory name in which the backup
configuration file will be stored. If the directory does not exist it will be first
created and the filename is either the value of C(filename) or default filename
as described in C(filename) options description. If the path value is not given
in that case a I(backup) directory will be created in the current working directory
and backup configuration will be copied in C(filename) within I(backup) directory.
type: path
type: dict
version_added: "2.8"
exclusive:
description:
- Enters into exclusive configuration mode that locks out all users from committing
configuration changes until the exclusive session ends.
type: bool
default: false
version_added: "2.9"
"""
EXAMPLES = """
- name: configure top level configuration
iosxr_config:
lines: hostname {{ inventory_hostname }}
- name: configure interface settings
iosxr_config:
lines:
- description test interface
- ip address 172.31.1.1 255.255.255.0
parents: interface GigabitEthernet0/0/0/0
- name: load a config from disk and replace the current config
iosxr_config:
src: config.cfg
replace: config
backup: yes
- name: for idempotency, use full-form commands
iosxr_config:
lines:
# - shut
- shutdown
# parents: int g0/0/0/1
parents: interface GigabitEthernet0/0/0/1
- name: configurable backup path
iosxr_config:
src: config.cfg
backup: yes
backup_options:
filename: backup.cfg
dir_path: /home/user
"""
RETURN = """
commands:
description: The set of commands that will be pushed to the remote device
returned: If there are commands to run against the host
type: list
sample: ['hostname foo', 'router ospf 1', 'router-id 1.1.1.1']
backup_path:
description: The full path to the backup file
returned: when backup is yes
type: str
sample: /playbooks/ansible/backup/iosxr01_config.2016-07-16@22:28:34
filename:
description: The name of the backup file
returned: when backup is yes and filename is not specified in backup options
type: str
sample: iosxr01_config.2016-07-16@22:28:34
shortname:
description: The full path to the backup file excluding the timestamp
returned: when backup is yes and filename is not specified in backup options
type: str
sample: /playbooks/ansible/backup/iosxr01_config
date:
description: The date extracted from the backup file name
returned: when backup is yes
type: str
sample: "2016-07-16"
time:
description: The time extracted from the backup file name
returned: when backup is yes
type: str
sample: "22:28:34"
"""
import re
from ansible.module_utils._text import to_text, to_bytes
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import ConnectionError
from ansible.module_utils.network.iosxr.iosxr import load_config, get_config, get_connection
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, copy_file
from ansible.module_utils.network.common.config import NetworkConfig, dumps
DEFAULT_COMMIT_COMMENT = 'configured by iosxr_config'
def copy_file_to_node(module):
""" Copy config file to IOS-XR node. We use SFTP because older IOS-XR versions don't handle SCP very well.
"""
src = '/tmp/ansible_config.txt'
file = open(src, 'wb')
file.write(to_bytes(module.params['src'], errors='surrogate_or_strict'))
file.close()
dst = '/harddisk:/ansible_config.txt'
copy_file(module, src, dst, 'sftp')
return True
def check_args(module, warnings):
if module.params['comment']:
if len(module.params['comment']) > 60:
module.fail_json(msg='comment argument cannot be more than 60 characters')
if module.params['label']:
label = module.params['label']
if len(label) > 30:
module.fail_json(msg='label argument cannot be more than 30 characters')
if not label[0].isalpha():
module.fail_json(msg='label argument must begin with an alphabet')
valid_chars = re.match(r'[\w-]*$', label)
if not valid_chars:
module.fail_json(
msg='label argument must only contain alphabets,' +
'digits, underscores or hyphens'
)
if module.params['force']:
warnings.append('The force argument is deprecated, please use '
'match=none instead. This argument will be '
'removed in the future')
def get_running_config(module):
contents = module.params['config']
if not contents:
contents = get_config(module)
return contents
def get_candidate(module):
candidate = ''
if module.params['src']:
candidate = module.params['src']
elif module.params['lines']:
candidate_obj = NetworkConfig(indent=1)
parents = module.params['parents'] or list()
candidate_obj.add(module.params['lines'], parents=parents)
candidate = dumps(candidate_obj, 'raw')
return candidate
def run(module, result):
match = module.params['match']
replace = module.params['replace']
replace_config = replace == 'config'
path = module.params['parents']
comment = module.params['comment']
admin = module.params['admin']
exclusive = module.params['exclusive']
check_mode = module.check_mode
label = module.params['label']
candidate_config = get_candidate(module)
running_config = get_running_config(module)
commands = None
replace_file_path = None
connection = get_connection(module)
try:
response = connection.get_diff(candidate=candidate_config, running=running_config, diff_match=match, path=path, diff_replace=replace)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
config_diff = response.get('config_diff')
if replace_config:
running_base_diff_resp = connection.get_diff(candidate=running_config, running=candidate_config, diff_match=match, path=path, diff_replace=replace)
if config_diff or running_base_diff_resp['config_diff']:
ret = copy_file_to_node(module)
if not ret:
module.fail_json(msg='Copy of config file to the node failed')
commands = ['load harddisk:/ansible_config.txt']
replace_file_path = 'harddisk:/ansible_config.txt'
if config_diff or commands:
if not replace_config:
commands = config_diff.split('\n')
if any((module.params['lines'], module.params['src'])):
if module.params['before']:
commands[:0] = module.params['before']
if module.params['after']:
commands.extend(module.params['after'])
result['commands'] = commands
commit = not check_mode
diff = load_config(
module, commands, commit=commit,
replace=replace_file_path, comment=comment, admin=admin, exclusive=exclusive,
label=label
)
if diff:
result['diff'] = dict(prepared=diff)
result['changed'] = True
def main():
"""main entry point for module execution
"""
backup_spec = dict(
filename=dict(),
dir_path=dict(type='path')
)
argument_spec = dict(
src=dict(type='path'),
lines=dict(aliases=['commands'], type='list'),
parents=dict(type='list'),
before=dict(type='list'),
after=dict(type='list'),
match=dict(default='line', choices=['line', 'strict', 'exact', 'none']),
replace=dict(default='line', choices=['line', 'block', 'config']),
# this argument is deprecated in favor of setting match: none
# it will be removed in a future version
force=dict(default=False, type='bool'),
config=dict(),
backup=dict(type='bool', default=False),
backup_options=dict(type='dict', options=backup_spec),
comment=dict(default=DEFAULT_COMMIT_COMMENT),
admin=dict(type='bool', default=False),
exclusive=dict(type='bool', default=False),
label=dict()
)
argument_spec.update(iosxr_argument_spec)
mutually_exclusive = [('lines', 'src'),
('parents', 'src')]
required_if = [('match', 'strict', ['lines']),
('match', 'exact', ['lines']),
('replace', 'block', ['lines']),
('replace', 'config', ['src'])]
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
required_if=required_if,
supports_check_mode=True)
if module.params['force'] is True:
module.params['match'] = 'none'
warnings = list()
check_args(module, warnings)
result = dict(changed=False, warnings=warnings)
if module.params['backup']:
result['__backup__'] = get_config(module)
if any((module.params['src'], module.params['lines'])):
run(module, result)
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,215 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_facts
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': [u'preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_facts
version_added: 2.2
short_description: Get facts about iosxr devices.
extends_documentation_fragment: iosxr
description:
- Collects facts from network devices running the iosxr operating
system. This module places the facts gathered in the fact tree keyed by the
respective resource name. The facts module will always collect a
base set of facts from the device and can enable or disable
collection of additional facts.
notes:
- Tested against IOS-XR 6.1.3.
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
author:
- Ricardo Carrillo Cruz (@rcarrillocruz)
- Nilashish Chakraborty (@Nilashishc)
options:
gather_subset:
description:
- When supplied, this argument will restrict the facts collected
to a given subset. Possible values for this argument include
all, hardware, config, and interfaces. Can specify a list of
values to include a larger subset. Values can also be used
with an initial C(M(!)) to specify that a specific subset should
not be collected.
required: false
default: '!config'
gather_network_resources:
description:
- When supplied, this argument will restrict the facts collected
to a given subset. Possible values for this argument include
all and the resources like interfaces, lacp etc.
Can specify a list of values to include a larger subset. Values
can also be used with an initial C(M(!)) to specify that a
specific subset should not be collected.
Valid subsets are 'all', 'lacp', 'lacp_interfaces', 'lldp_global',
'lldp_interfaces', 'interfaces', 'l2_interfaces', 'l3_interfaces',
'lag_interfaces', 'acls', 'acl_interfaces', 'static_routes.
required: false
version_added: "2.9"
"""
EXAMPLES = """
# Gather all facts
- iosxr_facts:
gather_subset: all
gather_network_resources: all
# Collect only the config and default facts
- iosxr_facts:
gather_subset:
- config
# Do not collect hardware facts
- iosxr_facts:
gather_subset:
- "!hardware"
# Collect only the lacp facts
- iosxr_facts:
gather_subset:
- "!all"
- "!min"
gather_network_resources:
- lacp
# Do not collect lacp_interfaces facts
- iosxr_facts:
gather_network_resources:
- "!lacp_interfaces"
# Collect lacp and minimal default facts
- iosxr_facts:
gather_subset: min
gather_network_resources: lacp
# Collect only the interfaces facts
- iosxr_facts:
gather_subset:
- "!all"
- "!min"
gather_network_resources:
- interfaces
- l2_interfaces
"""
RETURN = """
ansible_net_gather_subset:
description: The list of fact subsets collected from the device
returned: always
type: list
# default
ansible_net_version:
description: The operating system version running on the remote device
returned: always
type: str
ansible_net_hostname:
description: The configured hostname of the device
returned: always
type: str
ansible_net_image:
description: The image file the device is running
returned: always
type: str
ansible_net_api:
description: The name of the transport
returned: always
type: str
ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
ansible_net_model:
description: The model name returned from the device
returned: always
type: str
# hardware
ansible_net_filesystems:
description: All file system names available on the device
returned: when hardware is configured
type: list
ansible_net_memfree_mb:
description: The available free memory on the remote device in Mb
returned: when hardware is configured
type: int
ansible_net_memtotal_mb:
description: The total memory on the remote device in Mb
returned: when hardware is configured
type: int
# config
ansible_net_config:
description: The current active config from the device
returned: when config is configured
type: str
# interfaces
ansible_net_all_ipv4_addresses:
description: All IPv4 addresses configured on the device
returned: when interfaces is configured
type: list
ansible_net_all_ipv6_addresses:
description: All IPv6 addresses configured on the device
returned: when interfaces is configured
type: list
ansible_net_interfaces:
description: A hash of all interfaces running on the system
returned: when interfaces is configured
type: dict
ansible_net_neighbors:
description: The list of LLDP neighbors from the remote device
returned: when interfaces is configured
type: dict
# network resources
ansible_net_gather_network_resources:
description: The list of fact resource subsets collected from the device
returned: always
type: list
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec
from ansible.module_utils.network.iosxr.argspec.facts.facts import FactsArgs
from ansible.module_utils.network.iosxr.facts.facts import Facts
def main():
"""
Main entry point for module execution
:returns: ansible_facts
"""
argument_spec = FactsArgs.argument_spec
argument_spec.update(iosxr_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
warnings = []
if module.params["gather_subset"] == "!config":
warnings.append('default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards')
result = Facts(module).get_facts()
ansible_facts, additional_warnings = result
warnings.extend(additional_warnings)
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
if __name__ == '__main__':
main()

@ -1,365 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
module: iosxr_interfaces
version_added: 2.9
short_description: Manage interface attributes on Cisco IOS-XR network devices
description: This module manages the interface attributes on Cisco IOS-XR network devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOS-XRv Version 6.1.3 on VIRL.
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
options:
config:
description: A dictionary of interface options
type: list
elements: dict
suboptions:
name:
description:
- Full name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0)
type: str
required: True
description:
description:
- Interface description.
type: str
enabled:
default: True
description:
- Administrative state of the interface.
- Set the value to C(True) to administratively enable the interface or C(False) to disable it.
type: bool
speed:
description:
- Configure the speed for an interface. Default is auto-negotiation when not configured.
type: int
mtu:
description:
- Sets the MTU value for the interface. Applicable for Ethernet interfaces only.
- Refer to vendor documentation for valid values.
type: int
duplex:
description:
- Configures the interface duplex mode. Default is auto-negotiation when not configured.
type: str
choices: ['full', 'half']
state:
choices:
- merged
- replaced
- overridden
- deleted
default: merged
description:
- The state of the configuration after module completion
type: str
"""
EXAMPLES = """
---
# Using merged
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# description Replaced by Ansible Team
# mtu 2000
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# dot1q native vlan 1021
# !
- name: Configure Ethernet interfaces
iosxr_interfaces:
config:
- name: GigabitEthernet0/0/0/2
description: 'Configured by Ansible'
enabled: True
- name: GigabitEthernet0/0/0/3
description: 'Configured by Ansible Network'
enabled: False
duplex: full
state: merged
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# description Configured and Merged by Ansible Network
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# description Configured and Merged by Ansible Network
# mtu 2600
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex full
# shutdown
# dot1q native vlan 1021
# !
# Using replaced
# Before state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# description Configured by Ansible
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# description Test
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# dot1q native vlan 1021
# !
- name: Configure following interfaces and replace their existing config
iosxr_interfaces:
config:
- name: GigabitEthernet0/0/0/2
description: Configured by Ansible
enabled: True
mtu: 2000
- name: GigabitEthernet0/0/0/3
description: 'Configured by Ansible Network'
enabled: False
duplex: auto
state: replaced
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# description Configured by Ansible
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# description Configured and Replaced by Ansible
# mtu 2000
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# description Configured and Replaced by Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 1021
# !
# Using overridden
# Before state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# description Configured by Ansible
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# description Configured by Ansible
# mtu 2600
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex full
# shutdown
# dot1q native vlan 1021
# !
- name: Override interfaces
iosxr_interfaces:
config:
- name: GigabitEthernet0/0/0/2
description: 'Configured by Ansible'
enabled: True
duplex: auto
- name: GigabitEthernet0/0/0/3
description: 'Configured by Ansible Network'
enabled: False
speed: 1000
state: overridden
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# description Configured and Overridden by Ansible Network
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# speed 1000
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# description Configured and Overridden by Ansible Network
# mtu 2000
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex full
# shutdown
# dot1q native vlan 1021
# !
# Using deleted
# Before state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# description Configured and Overridden by Ansible Network
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# speed 1000
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# description Configured and Overridden by Ansible Network
# mtu 2000
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex full
# shutdown
# dot1q native vlan 1021
# !
- name: Delete IOSXR interfaces as in given arguments
iosxr_interfaces:
config:
- name: GigabitEthernet0/0/0/2
- name: GigabitEthernet0/0/0/3
state: deleted
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# vrf custB
# ipv4 address 178.18.169.23 255.255.255.0
# dot1q native vlan 30
# !
# interface GigabitEthernet0/0/0/3
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# dot1q native vlan 1021
# !
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: The configuration returned will always be in the same format of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: The configuration returned will always be in the same format of the parameters above.
commands:
description: The set of commands pushed to the remote device
returned: always
type: list
sample: ['interface GigabitEthernet0/0/0/2', 'description: Configured by Ansible', 'shutdown']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.interfaces.interfaces import InterfacesArgs
from ansible.module_utils.network.iosxr.config.interfaces.interfaces import Interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',)),
('state', 'overridden', ('config',))]
module = AnsibleModule(argument_spec=InterfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True)
result = Interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,429 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_l2_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
GENERATOR_VERSION = '1.0'
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_l2_interfaces
version_added: 2.9
short_description: Manage Layer-2 interface on Cisco IOS-XR devices
description: This module manages the Layer-2 interface attributes on Cisco IOS-XR devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOS-XRv Version 6.1.3 on VIRL.
- This module works with connection C(network_cli).
See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
options:
config:
description: A dictionary of Layer-2 interface options
type: list
elements: dict
suboptions:
name:
description:
- Full name of the interface/sub-interface excluding any logical unit number,
e.g. GigabitEthernet0/0/0/1 or GigabitEthernet0/0/0/1.100.
type: str
required: True
native_vlan:
description:
- Configure a native VLAN ID for the trunk
type: int
l2transport:
description:
- Switchport mode access command to configure the interface as a layer 2 access
type: bool
l2protocol:
description:
- Configures Layer 2 protocol tunneling and protocol data unit (PDU) filtering on an interface.
type: list
suboptions:
cdp:
description:
- Cisco Discovery Protocol (CDP) tunneling and data unit parameters.
choices: ['drop','forward', 'tunnel']
type: str
pvst:
description:
- Configures the per-VLAN Spanning Tree Protocol (PVST) tunneling and data unit parameters.
choices: ['drop','forward', 'tunnel']
type: str
stp:
description:
- Spanning Tree Protocol (STP) tunneling and data unit parameters.
choices: ['drop','forward', 'tunnel']
type: str
vtp:
description:
- VLAN Trunk Protocol (VTP) tunneling and data unit parameters.
choices: ['drop','forward', 'tunnel']
type: str
q_vlan:
description:
- 802.1Q VLAN configuration. Note that it can accept either 2 VLAN IDs when configuring Q-in-Q VLAN,
or it will accept 1 VLAN ID and 'any' as input list when configuring Q-in-any vlan as input. Note, that
this option is valid only with respect to Sub-Interface and is not valid when configuring for Interface.
type: list
propagate:
description:
- Propagate Layer 2 transport events. Note that it will work only when the I(l2tranport) option is set
to TRUE
type: bool
state:
choices:
- merged
- replaced
- overridden
- deleted
default: merged
description:
- The state of the configuration after module completion
type: str
"""
EXAMPLES = """
---
# Using merged
#
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# !
- name: Merge provided configuration with device configuration
iosxr_l2_interfaces:
config:
- name: GigabitEthernet0/0/0/3
native_vlan: 20
- name: GigabitEthernet0/0/0/4
native_vlan: 40
l2transport: True
l2protocol:
- stp: tunnel
- name: GigabitEthernet0/0/0/3.900
l2transport: True
q_vlan:
- 20
- 40
state: merged
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 20
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# dot1q native vlan 10
# l2transport
# l2protocol stp tunnel
# !
# !
# interface GigabitEthernet0/0/0/3.900 l2transport
# dot1q vlan 20 40
# !
# Using replaced
#
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 20
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# dot1q native vlan 10
# l2transport
# l2protocol stp tunnel
# !
# !
# interface GigabitEthernet0/0/0/3.900 l2transport
# dot1q vlan 20 40
# !
- name: Replaces device configuration of listed interfaces with provided configuration
iosxr_l2_interfaces:
config:
- name: GigabitEthernet0/0/0/4
native_vlan: 40
l2transport: True
l2protocol:
- stp: forward
- name: GigabitEthernet0/0/0/3.900
q_vlan:
- 20
- any
state: replaced
# After state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 20
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# dot1q native vlan 40
# l2transport
# l2protocol stp forward
# !
# !
# interface GigabitEthernet0/0/0/3.900 l2transport
# dot1q vlan 20 any
# !
# Using overridden
#
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 20
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# dot1q native vlan 10
# l2transport
# l2protocol stp tunnel
# !
# !
# interface GigabitEthernet0/0/0/3.900 l2transport
# dot1q vlan 20 40
# !
- name: Override device configuration of all interfaces with provided configuration
iosxr_l2_interfaces:
config:
- name: GigabitEthernet0/0/0/4
native_vlan: 40
l2transport: True
l2protocol:
- stp: forward
- name: GigabitEthernet0/0/0/3.900
q_vlan:
- 20
- any
state: overridden
# After state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# dot1q native vlan 40
# l2transport
# l2protocol stp forward
# !
# !
# interface GigabitEthernet0/0/0/3.900
# dot1q vlan 20 any
# !
# Using deleted
#
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 20
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# dot1q native vlan 10
# l2transport
# l2protocol stp tunnel
# !
# !
#
- name: "Delete L2 attributes of given interfaces (Note: This won't delete the interface itself)"
iosxr_l2_interfaces:
config:
- name: GigabitEthernet0/0/0/4
state: deleted
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 20
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# !
# Using Deleted without any config passed
# "(NOTE: This will delete all of configured resource module attributes from each configured interface)"
#
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# dot1q native vlan 20
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# dot1q native vlan 10
# l2transport
# l2protocol stp tunnel
# !
# !
- name: "Delete L2 attributes of all interfaces (Note: This won't delete the interface itself)"
iosxr_l2_interfaces:
state: deleted
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/3
# description Ansible Network
# vrf custB
# ipv4 address 10.10.0.2 255.255.255.0
# duplex half
# shutdown
# !
# interface GigabitEthernet0/0/0/4
# description Test description
# !
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: The configuration returned will always be in the same format of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: The configuration returned will always be in the same format of the parameters above.
commands:
description: The set of commands pushed to the remote device
returned: always
type: list
sample: ['interface GigabitEthernet0/0/0/2', 'command 2', 'command 3']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs
from ansible.module_utils.network.iosxr.config.l2_interfaces.l2_interfaces import L2_Interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',)),
('state', 'overridden', ('config',))]
module = AnsibleModule(argument_spec=L2_InterfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True)
result = L2_Interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,424 +0,0 @@
#!/usr/bin/python
# -*- 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)
##############################################
# 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 module file for ios_l3_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
module: iosxr_l3_interfaces
version_added: 2.9
short_description: Manage Layer-3 interface on Cisco IOS-XR devices.
description: This module provides declarative management of Layer-3 interface on Cisco IOS-XR devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOS-XRv Version 6.1.3 on VIRL.
- This module works with connection C(network_cli).
See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
options:
config:
description: A dictionary of Layer-3 interface options
type: list
elements: dict
suboptions:
name:
description:
- Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1.
type: str
required: True
ipv4:
description:
- IPv4 address to be set for the Layer-3 interface mentioned in I(name) option.
- The address format is <ipv4 address>/<mask>, the mask is number in range 0-32
eg. 192.168.0.1/24
type: list
suboptions:
address:
description:
- Configures the IPv4 address for Interface.
type: str
secondary:
description:
- Configures the IP address as a secondary address.
type: bool
ipv6:
description:
- IPv6 address to be set for the Layer-3 interface mentioned in I(name) option.
- The address format is <ipv6 address>/<mask>, the mask is number in range 0-128
eg. fd5d:12c9:2201:1::1/64
type: list
suboptions:
address:
description:
- Configures the IPv6 address for Interface.
type: str
state:
choices:
- merged
- replaced
- overridden
- deleted
default: merged
description:
- The state of the configuration after module completion
type: str
"""
EXAMPLES = """
---
# Using merged
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv4 address 192.168.0.2 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# !
# interface GigabitEthernet0/0/0/4
# ipv6 address fd5d:12c9:2201:1::1/64
# shutdown
# !
- name: Merge provided configuration with device configuration
iosxr_l3_interfaces:
config:
- name: GigabitEthernet0/0/0/2
ipv4:
- address: 192.168.0.1/24
- name: GigabitEthernet0/0/0/3
ipv4:
- address: 192.168.2.1/24
secondary: True
state: merged
# After state:
# ------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# ipv4 address 192.168.0.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv4 address 192.168.1.0 255.255.255.0
# ipv4 address 192.168.2.1 255.255.255.0 secondary
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# !
# interface GigabitEthernet0/0/0/4
# ipv6 address fd5d:12c9:2201:1::1/64
# shutdown
# !
# Using overridden
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# ipv4 address 192.168.0.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv4 address 192.168.1.0 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# !
# interface GigabitEthernet0/0/0/4
# ipv6 address fd5d:12c9:2201:1::1/64
# shutdown
# !
- name: Override device configuration of all interfaces with provided configuration
iosxr_l3_interfaces:
config:
- name: GigabitEthernet0/0/0/3
ipv4:
- address: 192.168.0.1/24
- name: GigabitEthernet0/0/0/3.700
ipv4:
- address: 192.168.0.2/24
- address: 192.168.2.1/24
secondary: True
state: overridden
# After state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv4 address 192.168.0.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# ipv4 address 192.168.0.2 255.255.255.0
# ipv4 address 192.168.2.1 255.255.255.0 secondary
# !
# interface GigabitEthernet0/0/0/4
# shutdown
# !
# Using replaced
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv4 address 192.168.0.2 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# ipv4 address 192.168.0.1 255.255.255.0
# !
# interface GigabitEthernet0/0/0/4
# ipv6 address fd5d:12c9:2201:1::1/64
# shutdown
# !
- name: Replaces device configuration of listed interfaces with provided configuration
iosxr_l3_interfaces:
config:
- name: GigabitEthernet0/0/0/3
ipv6:
- address: fd5d:12c9:2201:1::1/64
- name: GigabitEthernet0/0/0/4
ipv4:
- address: 192.168.0.2/24
state: replaced
# After state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv6 address fd5d:12c9:2201:1::1/64
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# ipv4 address 192.168.0.1 255.255.255.0
# !
# interface GigabitEthernet0/0/0/4
# ipv4 address 192.168.0.2 255.255.255.0
# shutdown
# !
# Using deleted
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# ipv4 address 192.168.2.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# ipv4 address 192.168.3.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv4 address 192.168.0.2 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# ipv4 address 192.168.0.1 255.255.255.0
# !
# interface GigabitEthernet0/0/0/4
# ipv6 address fd5d:12c9:2201:1::1/64
# shutdown
# !
- name: "Delete L3 attributes of given interfaces (Note: This won't delete the interface itself)"
iosxr_l3_interfaces:
config:
- name: GigabitEthernet0/0/0/3
- name: GigabitEthernet0/0/0/4
- name: GigabitEthernet0/0/0/3.700
state: deleted
# After state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# ipv4 address 192.168.2.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# ipv4 address 192.168.3.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# !
# interface GigabitEthernet0/0/0/4
# shutdown
# !
# Using Deleted without any config passed
# "(NOTE: This will delete all of configured resource module attributes from each configured interface)"
# Before state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# ipv4 address 192.168.2.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# ipv4 address 192.168.3.1 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# ipv4 address 192.168.0.2 255.255.255.0
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# ipv4 address 192.168.0.1 255.255.255.0
# !
# interface GigabitEthernet0/0/0/4
# ipv6 address fd5d:12c9:2201:1::1/64
# shutdown
# !
- name: "Delete L3 attributes of all interfaces (Note: This won't delete the interface itself)"
iosxr_l3_interfaces:
state: deleted
# After state:
# -------------
#
# viosxr#show running-config interface
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# shutdown
# !
# interface GigabitEthernet0/0/0/3.700
# !
# interface GigabitEthernet0/0/0/4
# shutdown
# !
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: The configuration returned will always be in the same format of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: The configuration returned will always be in the same format of the parameters above.
commands:
description: The set of commands pushed to the remote device
returned: always
type: list
sample: ['interface GigabitEthernet0/0/0/1', 'ipv4 address 192.168.0.1 255.255.255.0']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.l3_interfaces.l3_interfaces import L3_InterfacesArgs
from ansible.module_utils.network.iosxr.config.l3_interfaces.l3_interfaces import L3_Interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',)),
('state', 'overridden', ('config',))]
module = AnsibleModule(argument_spec=L3_InterfacesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True)
result = L3_Interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,300 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_lacp
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: iosxr_lacp
version_added: 2.9
short_description: Manage Global Link Aggregation Control Protocol (LACP) on IOS-XR devices.
description:
- This module manages Global Link Aggregation Control Protocol (LACP) on IOS-XR devices.
notes:
- Tested against IOS-XR 6.1.3.
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
author: Nilashish Chakraborty (@nilashishc)
options:
config:
description: The provided configurations.
type: dict
suboptions:
system:
description: This option sets the default system parameters for LACP bundles.
type: dict
suboptions:
priority:
description:
- The system priority to use in LACP negotiations.
- Lower value is higher priority.
- Refer to vendor documentation for valid values.
type: int
mac:
type: dict
description:
- The system MAC related configuration for LACP.
suboptions:
address:
description:
- The system ID to use in LACP negotiations.
type: str
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
#
# ------------
# Before state
# ------------
#
#
# RP/0/0/CPU0:iosxr01#show running-config lacp
# Tue Jul 16 17:46:08.147 UTC
# % No such configuration item(s)
#
#
- name: Merge provided configuration with device configuration
iosxr_lacp:
config:
system:
priority: 10
mac:
address: 00c1.4c00.bd15
state: merged
#
#
# -----------------------
# Module Execution Result
# -----------------------
#
# "before": {}
#
#
# "commands": [
# "lacp system priority 10",
# "lacp system mac 00c1.4c00.bd15"
# ]
#
#
# "after": {
# "system": {
# "mac": {
# "address": "00c1.4c00.bd15"
# },
# "priority": 10
# }
# }
#
# -----------
# After state
# -----------
#
#
# RP/0/0/CPU0:iosxr01#sh run lacp
# Tue Jul 16 17:51:29.365 UTC
# lacp system mac 00c1.4c00.bd15
# lacp system priority 10
#
#
# Using replaced
#
#
# -------------
# Before state
# -------------
#
#
# RP/0/0/CPU0:iosxr01#sh run lacp
# Tue Jul 16 17:53:59.904 UTC
# lacp system mac 00c1.4c00.bd15
# lacp system priority 10
#
- name: Replace device global lacp configuration with the given configuration
iosxr_lacp:
config:
system:
priority: 11
state: replaced
#
#
# -----------------------
# Module Execution Result
# -----------------------
# "before": {
# "system": {
# "mac": {
# "address": "00c1.4c00.bd15"
# },
# "priority": 10
# }
# }
#
#
# "commands": [
# "no lacp system mac",
# "lacp system priority 11"
# ]
#
#
# "after": {
# "system": {
# "priority": 11
# }
# }
#
# -----------
# After state
# -----------
#
#
# RP/0/0/CPU0:iosxr01#sh run lacp
# Tue Jul 16 18:02:40.379 UTC
# lacp system priority 11
#
#
# Using deleted
#
#
# ------------
# Before state
# ------------
#
#
# RP/0/0/CPU0:iosxr01#sh run lacp
# Tue Jul 16 18:37:09.727 UTC
# lacp system mac 00c1.4c00.bd15
# lacp system priority 11
#
#
- name: Delete global LACP configurations from the device
iosxr_lacp:
state: deleted
#
#
# -----------------------
# Module Execution Result
# -----------------------
# "before": {
# "system": {
# "mac": {
# "address": "00c1.4c00.bd15"
# },
# "priority": 11
# }
# }
#
#
# "commands": [
# "no lacp system mac",
# "no lacp system priority"
# ]
#
#
# "after": {}
#
# ------------
# After state
# ------------
#
#
# RP/0/0/CPU0:iosxr01#sh run lacp
# Tue Jul 16 18:39:44.116 UTC
# % No such configuration item(s)
#
#
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['lacp system priority 10', 'lacp system mac 00c1.4c00.bd15']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.lacp.lacp import LacpArgs
from ansible.module_utils.network.iosxr.config.lacp.lacp import Lacp
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',))]
module = AnsibleModule(argument_spec=LacpArgs.argument_spec, required_if=required_if,
supports_check_mode=True)
result = Lacp(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,539 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_lacp_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: iosxr_lacp_interfaces
version_added: 2.9
short_description: Manage Link Aggregation Control Protocol (LACP) attributes of interfaces on IOS-XR devices.
description:
- This module manages Link Aggregation Control Protocol (LACP) attributes of interfaces on IOS-XR devices.
notes:
- Tested against IOS-XR 6.1.3.
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
author: Nilashish Chakraborty (@nilashishc)
options:
config:
description: A dictionary of LACP interfaces options.
type: list
suboptions:
name:
description:
- Name/Identifier of the interface or Ether-Bundle.
type: str
churn_logging:
description:
- Specifies the parameter for logging of LACP churn events.
- Valid only for ether-bundles.
- Mode 'actor' logs actor churn events only.
- Mode 'partner' logs partner churn events only.
- Mode 'both' logs actor and partner churn events only.
type: str
choices: ['actor', 'partner', 'both']
collector_max_delay:
description:
- Specifies the collector max delay to be signaled to the LACP partner.
- Valid only for ether-bundles.
- Refer to vendor documentation for valid values.
type: int
period:
description:
- Specifies the rate at which packets are sent or received.
- For ether-bundles, this specifies the period to be used
by its member links.
- Refer to vendor documentation for valid values.
type: int
switchover_suppress_flaps:
description:
- Specifies the time for which to suppress flaps during
a LACP switchover.
- Valid only for ether-bundles.
- Refer to vendor documentation for valid values.
type: int
system:
description:
- This dict object contains configurable options related to LACP
system parameters for ether-bundles.
type: dict
suboptions:
priority:
description:
- Specifies the system priority to use in LACP negotiations for
the bundle.
- Refer to vendor documentation for valid values.
type: int
mac:
description:
- Specifies the system ID to use in LACP negotiations for
the bundle, encoded as a MAC address.
type: str
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
#
# ------------
# Before state
# ------------
#
#
#
# RP/0/0/CPU0:an-iosxr#sh running-config interface
# Sun Jul 21 18:01:35.079 UTC
# interface Bundle-Ether10
# !
# interface Bundle-Ether11
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1'
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
#
- name: Merge provided configuration with device configuration
iosxr_lacp_interfaces:
config:
- name: Bundle-Ether10
churn_logging: actor
collector_max_delay: 100
switchover_suppress_flaps: 500
- name: Bundle-Ether11
system:
mac: 00c2.4c00.bd15
- name: GigabitEthernet0/0/0/1
period: 200
state: merged
#
#
# -----------
# After state
# -----------
#
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Jul 21 18:24:52.413 UTC
# interface Bundle-Ether10
# lacp churn logging actor
# lacp switchover suppress-flaps 500
# lacp collector-max-delay 100
# !
# interface Bundle-Ether11
# lacp system mac 00c2.4c00.bd15
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# lacp period 200
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
# Using replaced
#
#
# ------------
# Before state
# ------------
#
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Jul 21 18:24:52.413 UTC
# interface Bundle-Ether10
# lacp churn logging actor
# lacp switchover suppress-flaps 500
# lacp collector-max-delay 100
# !
# interface Bundle-Ether11
# lacp system mac 00c2.4c00.bd15
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# lacp period 200
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
- name: Replace LACP configuration of listed interfaces with provided configuration
iosxr_lacp_interfaces:
config:
- name: Bundle-Ether10
churn_logging: partner
- name: GigabitEthernet0/0/0/2
period: 300
state: replaced
#
#
# -----------
# After state
# -----------
#
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Jul 21 18:50:21.929 UTC
# interface Bundle-Ether10
# lacp churn logging partner
# !
# interface Bundle-Ether11
# lacp system mac 00c2.4c00.bd15
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# lacp period 200
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# lacp period 300
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
#
# Using overridden
#
#
# ------------
# Before state
# ------------
#
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Jul 21 18:24:52.413 UTC
# interface Bundle-Ether10
# lacp churn logging actor
# lacp switchover suppress-flaps 500
# lacp collector-max-delay 100
# !
# interface Bundle-Ether11
# lacp system mac 00c2.4c00.bd15
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# lacp period 200
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# lacp period 200
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
#
- name: Override all interface LACP configuration with provided configuration
iosxr_lacp_interfaces:
config:
- name: Bundle-Ether12
churn_logging: both
collector_max_delay: 100
switchover_suppress_flaps: 500
- name: GigabitEthernet0/0/0/1
period: 300
state: overridden
#
#
# -----------
# After state
# -----------
#
#
# RP/0/0/CPU0:an-iosxr(config-if)#do sh run int
# Sun Jul 21 19:32:36.115 UTC
# interface Bundle-Ether10
# !
# interface Bundle-Ether11
# !
# interface Bundle-Ether12
# lacp churn logging both
# lacp switchover suppress-flaps 500
# lacp collector-max-delay 100
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# lacp period 300
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
# Using deleted
#
#
# ------------
# Before state
# ------------
#
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Jul 21 18:24:52.413 UTC
# interface Bundle-Ether10
# lacp churn logging actor
# lacp switchover suppress-flaps 500
# lacp collector-max-delay 100
# !
# interface Bundle-Ether11
# lacp non-revertive
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# lacp period 200
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# lacp period 300
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
- name: Deleted LACP configurations of provided interfaces (Note - This won't delete the interface itself)
iosxr_lacp_interfaces:
config:
- name: Bundle-Ether10
- name: Bundle-Ether11
- name: GigabitEthernet0/0/0/1
- name: GigabitEthernet0/0/0/2
state: deleted
#
#
# -----------
# After state
# -----------
#
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Jul 21 19:51:03.499 UTC
# interface Bundle-Ether10
# !
# interface Bundle-Ether11
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['interface Bundle-Ether10', 'lacp churn logging partner', 'lacp period 150']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs
from ansible.module_utils.network.iosxr.config.lacp_interfaces.lacp_interfaces import Lacp_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',)),
('state', 'overridden', ('config',))]
module = AnsibleModule(argument_spec=Lacp_interfacesArgs.argument_spec, required_if=required_if,
supports_check_mode=True)
result = Lacp_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,639 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_lag_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: iosxr_lag_interfaces
version_added: 2.9
short_description: Manages attributes of LAG/Ether-Bundle interfaces on IOS-XR devices.
description:
- This module manages the attributes of LAG/Ether-Bundle interfaces on IOS-XR devices.
notes:
- Tested against IOS-XR 6.1.3.
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
author: Nilashish Chakraborty (@NilashishC)
options:
config:
description: A provided Link Aggregation Group (LAG) configuration.
type: list
elements: dict
suboptions:
name:
description:
- Name/Identifier of the LAG/Ether-Bundle to configure.
type: str
required: True
members:
description:
- List of member interfaces for the LAG/Ether-Bundle.
type: list
elements: dict
suboptions:
member:
description:
- Name of the member interface.
type: str
mode:
description:
- Specifies the mode of the operation for the member interface.
- Mode 'active' runs LACP in active mode.
- Mode 'on' does not run LACP over the port.
- Mode 'passive' runs LACP in passive mode over the port.
- Mode 'inherit' runs LACP as configured in the bundle.
choices: ['on', 'active', 'passive', 'inherit']
type: str
mode:
description:
- LAG mode.
- Mode 'active' runs LACP in active mode over the port.
- Mode 'on' does not run LACP over the port.
- Mode 'passive' runs LACP in passive mode over the port.
choices: ['on', 'active', 'passive']
type: str
links:
description:
- This dict contains configurable options related to LAG/Ether-Bundle links.
type: dict
suboptions:
max_active:
description:
- Specifies the limit on the number of links that can be active in the LAG/Ether-Bundle.
- Refer to vendor documentation for valid values.
type: int
min_active:
description:
- Specifies the minimum number of active links needed to bring up the LAG/Ether-Bundle.
- Refer to vendor documentation for valid values.
type: int
load_balancing_hash:
description:
- Specifies the hash function used for traffic forwarded over the LAG/Ether-Bundle.
- Option 'dst-ip' uses the destination IP as the hash function.
- Option 'src-ip' uses the source IP as the hash function.
type: str
choices: ['dst-ip', 'src-ip']
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
#
# ------------
# Before state
# ------------
#
# RP/0/0/CPU0:iosxr01#show run int
# Sun Jul 7 19:42:59.416 UTC
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description "GigabitEthernet - 1"
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
#
- name: Merge provided configuration with device configuration
iosxr_lag_interfaces:
config:
- name: Bundle-Ether10
members:
- member: GigabitEthernet0/0/0/1
mode: inherit
- member: GigabitEthernet0/0/0/3
mode: inherit
mode: active
links:
max_active: 5
min_active: 2
load_balancing_hash: src-ip
- name: Bundle-Ether12
members:
- member: GigabitEthernet0/0/0/2
mode: passive
- member: GigabitEthernet0/0/0/4
mode: passive
load_balancing_hash: dst-ip
state: merged
#
#
# -----------
# After state
# -----------
#
# RP/0/0/CPU0:iosxr01#show run int
# Sun Jul 7 20:51:17.685 UTC
# interface Bundle-Ether10
# lacp mode active
# bundle load-balancing hash src-ip
# bundle maximum-active links 5
# bundle minimum-active links 2
# !
# interface Bundle-Ether12
# bundle load-balancing hash dst-ip
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# bundle id 12 mode passive
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# bundle id 12 mode passive
# !
#
# Using replaced
#
#
# -------------
# Before state
# -------------
#
#
# RP/0/0/CPU0:iosxr01#sho run int
# Sun Jul 7 20:58:06.527 UTC
# interface Bundle-Ether10
# lacp mode active
# bundle load-balancing hash src-ip
# bundle maximum-active links 5
# bundle minimum-active links 2
# !
# interface Bundle-Ether12
# bundle load-balancing hash dst-ip
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# bundle id 12 mode passive
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# bundle id 12 mode passive
# !
#
#
- name: Replace device configuration of listed Bundles with provided configurations
iosxr_lag_interfaces:
config:
- name: Bundle-Ether12
members:
- name: GigabitEthernet0/0/0/2
mode: passive
- name: Bundle-Ether11
members:
- name: GigabitEthernet0/0/0/4
load_balancing_hash: src-ip
state: replaced
#
#
# -----------
# After state
# -----------
#
#
# RP/0/0/CPU0:iosxr01#sh run int
# Sun Jul 7 21:22:27.397 UTC
# interface Bundle-Ether10
# lacp mode active
# bundle load-balancing hash src-ip
# bundle maximum-active links 5
# bundle minimum-active links 2
# !
# interface Bundle-Ether11
# bundle load-balancing hash src-ip
# !
# interface Bundle-Ether12
# lacp mode passive
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# bundle id 12 mode on
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# bundle id 11 mode on
# !
#
#
# Using overridden
#
#
# ------------
# Before state
# ------------
#
#
# RP/0/0/CPU0:iosxr01#sh run int
# Sun Jul 7 21:22:27.397 UTC
# interface Bundle-Ether10
# lacp mode active
# bundle load-balancing hash src-ip
# bundle maximum-active links 5
# bundle minimum-active links 2
# !
# interface Bundle-Ether11
# bundle load-balancing hash src-ip
# !
# interface Bundle-Ether12
# lacp mode passive
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# bundle id 12 mode on
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# bundle id 11 mode on
# !
#
#
- name: Overrides all device configuration with provided configuration
iosxr_lag_interfaces:
config:
- name: Bundle-Ether10
members:
- member: GigabitEthernet0/0/0/1
mode: inherit
- member: GigabitEthernet0/0/0/2
mode: inherit
mode: active
load_balancing_hash: dst-ip
state: overridden
#
#
# ------------
# After state
# ------------
#
#
# RP/0/0/CPU0:iosxr01#sh run int
# Sun Jul 7 21:43:04.802 UTC
# interface Bundle-Ether10
# lacp mode active
# bundle load-balancing hash dst-ip
# !
# interface Bundle-Ether11
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
#
# Using deleted
#
#
# ------------
# Before state
# ------------
#
# RP/0/0/CPU0:iosxr01#sh run int
# Sun Jul 7 21:22:27.397 UTC
# interface Bundle-Ether10
# lacp mode active
# bundle load-balancing hash src-ip
# bundle maximum-active links 5
# bundle minimum-active links 2
# !
# interface Bundle-Ether11
# bundle load-balancing hash src-ip
# !
# interface Bundle-Ether12
# lacp mode passive
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# bundle id 12 mode on
# !n
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# bundle id 10 mode inherit
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# bundle id 11 mode on
# !
#
#
- name: Delete attributes of given bundles and removes member interfaces from them (Note - This won't delete the bundles themselves)
iosxr_lag_interfaces:
config:
- name: Bundle-Ether10
- name: Bundle-Ether11
- name: Bundle-Ether12
state: deleted
#
#
# ------------
# After state
# ------------
#
# RP/0/0/CPU0:iosxr01#sh run int
# Sun Jul 7 21:49:50.004 UTC
# interface Bundle-Ether10
# !
# interface Bundle-Ether11
# !
# interface Bundle-Ether12
# !
# interface Loopback888
# description test for ansible
# shutdown
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/1
# description 'GigabitEthernet - 1"
# !
# interface GigabitEthernet0/0/0/2
# description "GigabitEthernet - 2"
# !
# interface GigabitEthernet0/0/0/3
# description "GigabitEthernet - 3"
# !
# interface GigabitEthernet0/0/0/4
# description "GigabitEthernet - 4"
# !
#
#
# Using deleted (without config)
#
#
# ------------
# Before state
# ------------
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Aug 18 19:49:51.908 UTC
# interface Bundle-Ether10
# lacp mode active
# bundle load-balancing hash src-ip
# bundle maximum-active links 10
# bundle minimum-active links 2
# !
# interface Bundle-Ether11
# bundle load-balancing hash dst-ip
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 192.0.2.11 255.255.255.0
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# !
# interface GigabitEthernet0/0/0/1
# bundle id 10 mode inherit
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# bundle id 10 mode passive
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# bundle id 11 mode passive
# shutdown
# !
# interface GigabitEthernet0/0/0/4
# bundle id 11 mode passive
# shutdown
# !
#
- name: Delete attributes of all bundles and removes member interfaces from them (Note - This won't delete the bundles themselves)
iosxr_lag_interfaces:
state: deleted
#
#
# ------------
# After state
# ------------
#
#
# RP/0/0/CPU0:an-iosxr#sh run int
# Sun Aug 18 19:54:22.389 UTC
# interface Bundle-Ether10
# !
# interface Bundle-Ether11
# !
# interface MgmtEth0/0/CPU0/0
# ipv4 address 10.8.38.69 255.255.255.0
# !
# interface GigabitEthernet0/0/0/0
# shutdown
# !
# interface GigabitEthernet0/0/0/1
# shutdown
# !
# interface GigabitEthernet0/0/0/2
# shutdown
# !
# interface GigabitEthernet0/0/0/3
# shutdown
# !
# interface GigabitEthernet0/0/0/4
# shutdown
# !
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['interface Bundle-Ether10', 'bundle minimum-active links 2', 'bundle load-balancing hash src-ip']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
from ansible.module_utils.network.iosxr.config.lag_interfaces.lag_interfaces import Lag_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',)),
('state', 'overridden', ('config',))]
module = AnsibleModule(argument_spec=Lag_interfacesArgs.argument_spec, required_if=required_if,
supports_check_mode=True)
result = Lag_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,376 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_lldp_global
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: iosxr_lldp_global
version_added: 2.9
short_description: Manage Global Link Layer Discovery Protocol (LLDP) settings on IOS-XR devices.
description:
- This module manages Global Link Layer Discovery Protocol (LLDP) settings on IOS-XR devices.
notes:
- Tested against IOS-XR 6.1.3.
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
author: Nilashish Chakraborty (@NilashishC)
options:
config:
description: The provided global LLDP configuration.
type: dict
suboptions:
holdtime:
description:
- Specifies the holdtime (in sec) to be sent in packets.
type: int
reinit:
description:
- Specifies the delay (in sec) for LLDP initialization on any interface.
type: int
subinterfaces:
description:
- Enable or disable LLDP over sub-interfaces.
type: bool
timer:
description:
- Specifies the rate at which LLDP packets are sent (in sec).
type: int
tlv_select:
description:
- Specifies the LLDP TLVs to enable or disable.
type: dict
suboptions:
management_address:
description:
- Enable or disable management address TLV.
type: bool
port_description:
description:
- Enable or disable port description TLV.
type: bool
system_capabilities:
description:
- Enable or disable system capabilities TLV.
type: bool
system_description:
description:
- Enable or disable system description TLV.
type: bool
system_name:
description:
- Enable or disable system name TLV.
type: bool
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
#
# -------------
# Before State
# -------------
#
#
# RP/0/0/CPU0:an-iosxr#sh run lldp
# Tue Aug 6 19:27:54.933 UTC
# % No such configuration item(s)
#
#
- name: Merge provided LLDP configuration with the existing configuration
iosxr_lldp_global:
config:
holdtime: 100
reinit: 2
timer: 3000
subinterfaces: True
tlv_select:
management_address: False
system_description: False
state: merged
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
# "before": {}
#
# "commands": [
# "lldp subinterfaces enable",
# "lldp holdtime 100",
# "lldp reinit 2",
# "lldp tlv-select system-description disable",
# "lldp tlv-select management-address disable",
# "lldp timer 3000"
# ]
#
# "after": {
# "holdtime": 100,
# "reinit": 2,
# "subinterfaces": true,
# "timer": 3000,
# "tlv_select": {
# "management_address": false,
# "system_description": false
# }
# }
#
#
# ------------
# After state
# ------------
#
#
# RP/0/0/CPU0:an-iosxr#sh run lldp
# Tue Aug 6 21:31:10.587 UTC
# lldp
# timer 3000
# reinit 2
# subinterfaces enable
# holdtime 100
# tlv-select
# management-address disable
# system-description disable
# !
# !
#
#
# Using replaced
#
#
# -------------
# Before State
# -------------
#
# RP/0/0/CPU0:an-iosxr#sh run lldp
# Tue Aug 6 21:31:10.587 UTC
# lldp
# timer 3000
# reinit 2
# subinterfaces enable
# holdtime 100
# tlv-select
# management-address disable
# system-description disable
# !
# !
#
#
- name: Replace existing LLDP device configuration with provided configuration
iosxr_lldp_global:
config:
holdtime: 100
tlv_select:
port_description: False
system_description: True
management_description: True
state: replaced
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
# "before": {
# "holdtime": 100,
# "reinit": 2,
# "subinterfaces": true,
# "timer": 3000,
# "tlv_select": {
# "management_address": false,
# "system_description": false
# }
# }
#
# "commands": [
# "no lldp reinit 2",
# "no lldp subinterfaces enable",
# "no lldp timer 3000",
# "no lldp tlv-select management-address disable",
# "no lldp tlv-select system-description disable",
# "lldp tlv-select port-description disable"
# ]
#
# "after": {
# "holdtime": 100,
# "tlv_select": {
# "port_description": false
# }
# }
#
#
# ------------
# After state
# ------------
#
# RP/0/0/CPU0:an-iosxr#sh run lldp
# Tue Aug 6 21:53:08.407 UTC
# lldp
# holdtime 100
# tlv-select
# port-description disable
# !
# !
#
#
# Using deleted
#
# ------------
# Before state
# ------------
#
#
# RP/0/0/CPU0:an-iosxr#sh run lldp
# Tue Aug 6 21:31:10.587 UTC
# lldp
# timer 3000
# reinit 2
# subinterfaces enable
# holdtime 100
# tlv-select
# management-address disable
# system-description disable
# !
# !
#
#
- name: Deleted existing LLDP configurations from the device
iosxr_lldp_global:
state: deleted
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
# "before": {
# "holdtime": 100,
# "reinit": 2,
# "subinterfaces": true,
# "timer": 3000,
# "tlv_select": {
# "management_address": false,
# "system_description": false
# }
# },
#
# "commands": [
# "no lldp holdtime 100",
# "no lldp reinit 2",
# "no lldp subinterfaces enable",
# "no lldp timer 3000",
# "no lldp tlv-select management-address disable",
# "no lldp tlv-select system-description disable"
# ]
#
# "after": {}
#
#
# -----------
# After state
# -----------
#
# RP/0/0/CPU0:an-iosxr#sh run lldp
# Tue Aug 6 21:38:31.187 UTC
# lldp
# !
#
#
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['lldp subinterfaces enable', 'lldp holdtime 100', 'no lldp tlv-select management-address disable']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.lldp_global.lldp_global import Lldp_globalArgs
from ansible.module_utils.network.iosxr.config.lldp_global.lldp_global import Lldp_global
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',))]
module = AnsibleModule(argument_spec=Lldp_globalArgs.argument_spec, required_if=required_if,
supports_check_mode=True)
result = Lldp_global(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,588 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_lldp_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: iosxr_lldp_interfaces
version_added: 2.9
short_description: Manage Link Layer Discovery Protocol (LLDP) attributes of interfaces on IOS-XR devices.
description:
- This module manages Link Layer Discovery Protocol (LLDP) attributes of interfaces on IOS-XR devices.
notes:
- Tested against IOS-XR 6.1.3.
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
author: Nilashish Chakraborty (@nilashishc)
options:
config:
description: A dictionary of LLDP interfaces options.
type: list
elements: dict
suboptions:
name:
description:
- Name/Identifier of the interface or Ether-Bundle.
type: str
destination:
description:
- Specifies LLDP destination configuration on the interface.
suboptions:
mac_address:
description:
- Specifies the LLDP destination mac address on the interface.
type: str
choices: ['ieee-nearest-bridge', 'ieee-nearest-non-tmpr-bridge']
type: dict
receive:
description:
- Enable/disable LLDP RX on an interface.
type: bool
transmit:
description:
- Enable/disable LLDP TX on an interface.
type: bool
state:
description:
- The state of the configuration after module completion.
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
#
# ------------
# Before state
# ------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 12:40:23.104 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# !
# interface preconfigure GigabitEthernet0/0/0/2
# !
#
#
- name: Merge provided configuration with running configuration
iosxr_lldp_interfaces:
config:
- name: GigabitEthernet0/0/0/1
destination:
mac_address: ieee-nearest-non-tmpr-bridge
transmit: False
- name: GigabitEthernet0/0/0/2
destination:
mac_address: ieee-nearest-bridge
receive: False
state: merged
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
#
# "before": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "name": "GigabitEthernet0/0/0/1"
# },
# {
# "name": "GigabitEthernet0/0/0/2"
# }
# ]
#
# "commands": [
# "interface GigabitEthernet0/0/0/2",
# "lldp destination mac-address ieee-nearest-non-tmpr-bridge",
# "lldp transmit disable",
# "interface GigabitEthernet0/0/0/1",
# "lldp receive disable",
# "lldp destination mac-address ieee-nearest-bridge"
# ]
#
# "after": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-bridge"
# },
# "name": "GigabitEthernet0/0/0/1",
# "receive": false
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-non-tmpr-bridge"
# },
# "name": "GigabitEthernet0/0/0/2",
# "transmit": false
# }
# ]
#
#
# ------------
# After state
# ------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 12:49:51.517 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# lldp
# receive disable
# destination mac-address
# ieee-nearest-bridge
# !
# !
# !
# interface preconfigure GigabitEthernet0/0/0/2
# lldp
# transmit disable
# destination mac-address
# ieee-nearest-non-tmpr-bridge
# !
# !
# !
#
#
# Using replaced
#
#
# -------------
# Before state
# -------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 12:49:51.517 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# lldp
# receive disable
# destination mac-address
# ieee-nearest-bridge
# !
# !
# !
# interface preconfigure GigabitEthernet0/0/0/2
# lldp
# transmit disable
# destination mac-address
# ieee-nearest-non-tmpr-bridge
# !
# !
# !
#
#
- name: Replace existing LLDP configurations of specified interfaces with provided configuration
iosxr_lldp_interfaces:
config:
- name: GigabitEthernet0/0/0/1
destination:
mac_address: ieee-nearest-non-tmpr-bridge
state: replaced
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
# "before": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-bridge"
# },
# "name": "GigabitEthernet0/0/0/1",
# "receive": false
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-non-tmpr-bridge"
# },
# "name": "GigabitEthernet0/0/0/2",
# "transmit": false
# }
# ]
#
#
# "commands": [
# "interface GigabitEthernet0/0/0/1",
# "no lldp receive disable",
# "lldp destination mac-address ieee-nearest-non-tmpr-bridge"
# ]
#
#
# "after": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-non-tmpr-bridge"
# },
# "name": "GigabitEthernet0/0/0/1"
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-non-tmpr-bridge"
# },
# "name": "GigabitEthernet0/0/0/2",
# "transmit": false
# }
# ]
#
#
# ------------
# After state
# ------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 13:02:57.062 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# lldp
# destination mac-address
# ieee-nearest-non-tmpr-bridge
# !
# !
# !
# interface preconfigure GigabitEthernet0/0/0/2
# lldp
# transmit disable
# destination mac-address
# ieee-nearest-non-tmpr-bridge
# !
# !
# !
#
#
# Using overridden
#
#
# -------------
# Before state
# -------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 13:15:40.465 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# lldp
# receive disable
# destination mac-address
# ieee-nearest-bridge
# !
# !
# !
# interface preconfigure GigabitEthernet0/0/0/2
# lldp
# transmit disable
# destination mac-address
# ieee-nearest-non-tmpr-bridge
# !
# !
# !
#
#
- name: Override the LLDP configurations of all the interfaces with provided configurations
iosxr_lldp_interfaces:
config:
- name: GigabitEthernet0/0/0/1
transmit: False
state: overridden
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
#
# "before": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-bridge"
# },
# "name": "GigabitEthernet0/0/0/1",
# "receive": false
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-non-tmpr-bridge"
# },
# "name": "GigabitEthernet0/0/0/2",
# "transmit": false
# }
# ]
#
# "commands": [
# "interface GigabitEthernet0/0/0/2",
# "no lldp destination mac-address ieee-nearest-non-tmpr-bridge",
# "no lldp transmit disable",
# "interface GigabitEthernet0/0/0/1",
# "no lldp destination mac-address ieee-nearest-bridge",
# "no lldp receive disable",
# "lldp transmit disable"
# ]
#
#
# "after": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "name": "GigabitEthernet0/0/0/1",
# "transmit": false
# },
# {
# "name": "GigabitEthernet0/0/0/2"
# }
# ]
#
#
# ------------
# After state
# ------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 13:22:25.604 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# lldp
# transmit disable
# !
# !
# interface preconfigure GigabitEthernet0/0/0/2
# !
#
#
# Using deleted
#
#
# -------------
# Before state
# -------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 13:26:21.498 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# lldp
# receive disable
# destination mac-address
# ieee-nearest-bridge
# !
# !
# !
# interface preconfigure GigabitEthernet0/0/0/2
# lldp
# transmit disable
# destination mac-address
# ieee-nearest-non-tmpr-bridge
# !
# !
# !
#
#
- name: Delete LLDP configurations of all interfaces (Note - This won't delete the interfaces themselves)
iosxr_lldp_interfaces:
state: deleted
#
#
#
# ------------------------
# Module Execution Result
# ------------------------
#
#
# "before": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-bridge"
# },
# "name": "GigabitEthernet0/0/0/1",
# "receive": false
# },
# {
# "destination": {
# "mac_address": "ieee-nearest-non-tmpr-bridge"
# },
# "name": "GigabitEthernet0/0/0/2",
# "transmit": false
# }
# ]
#
#
# "commands": [
# "interface GigabitEthernet0/0/0/1",
# "no lldp destination mac-address ieee-nearest-bridge",
# "no lldp receive disable",
# "interface GigabitEthernet0/0/0/2",
# "no lldp destination mac-address ieee-nearest-non-tmpr-bridge",
# "no lldp transmit disable"
# ]
#
#
# "after": [
# {
# "name": "TenGigE0/0/0/0"
# },
# {
# "name": "GigabitEthernet0/0/0/1"
# },
# {
# "name": "GigabitEthernet0/0/0/2"
# }
# ]
#
#
# ------------
# After state
# ------------
#
#
# RP/0/RP0/CPU0:ios#sh run int
# Mon Aug 12 13:30:14.618 UTC
# interface TenGigE0/0/0/0
# ipv4 address 192.0.2.11 255.255.255.192
# !
# interface preconfigure GigabitEthernet0/0/0/1
# !
# interface preconfigure GigabitEthernet0/0/0/2
# !
#
#
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['interface GigabitEthernet0/0/0/1', 'lldp destination mac-address ieee-nearest-non-tmpr-bridge', 'no lldp transmit disable']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs
from ansible.module_utils.network.iosxr.config.lldp_interfaces.lldp_interfaces import Lldp_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [('state', 'merged', ('config',)),
('state', 'replaced', ('config',)),
('state', 'overridden', ('config',))]
module = AnsibleModule(argument_spec=Lldp_interfacesArgs.argument_spec, required_if=required_if,
supports_check_mode=True)
result = Lldp_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,728 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_logging
version_added: "2.4"
author:
- "Trishna Guha (@trishnaguha)"
- "Kedar Kekan (@kedarX)"
short_description: Configuration management of system logging services on network devices
description:
- This module provides declarative management configuration of system logging (syslog)
on Cisco IOS XR devices.
requirements:
- ncclient >= 0.5.3 when using netconf
- lxml >= 4.1.1 when using netconf
notes:
- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
- Tested against IOS XRv 6.1.3
options:
dest:
description:
- Destination for system logging (syslog) messages.
choices: ['host', 'console', 'monitor', 'buffered', 'file']
name:
description:
- When C(dest) = I(file) name indicates file-name
- When C(dest) = I(host) name indicates the host-name or ip-address of syslog server.
vrf:
description:
- vrf name when syslog server is configured, C(dest) = C(host)
default: default
version_added: 2.5
size:
description:
- Size of buffer when C(dest) = C(buffered). The acceptable value is in the range I(307200 to 125000000 bytes). Default 307200
- Size of file when C(dest) = C(file). The acceptable value is in the range I(1 to 2097152)KB. Default 2 GB
facility:
description:
- To configure the type of syslog facility in which system logging (syslog) messages are sent to syslog servers
Optional config for C(dest) = C(host)
default: local7
hostnameprefix:
description:
- To append a hostname prefix to system logging (syslog) messages logged to syslog servers.
Optional config for C(dest) = C(host)
version_added: 2.5
level:
description:
- Specifies the severity level for the logging.
default: debugging
aliases: ['severity']
aggregate:
description: List of syslog logging configuration definitions.
state:
description:
- Existential state of the logging configuration on the node.
default: present
choices: ['present', 'absent']
extends_documentation_fragment: iosxr
"""
EXAMPLES = """
- name: configure logging for syslog server host
iosxr_logging:
dest: host
name: 10.10.10.1
level: critical
state: present
- name: add hostnameprefix configuration
iosxr_logging:
hostnameprefix: host1
state: absent
- name: add facility configuration
iosxr_logging:
facility: local1
state: present
- name: configure console logging level
iosxr_logging:
dest: console
level: debugging
state: present
- name: configure monitor logging level
iosxr_logging:
dest: monitor
level: errors
state: present
- name: configure syslog to a file
iosxr_logging:
dest: file
name: file_name
size: 2048
level: errors
state: present
- name: configure buffered logging with size
iosxr_logging:
dest: buffered
size: 5100000
- name: Configure logging using aggregate
iosxr_logging:
aggregate:
- { dest: console, level: warning }
- { dest: buffered, size: 4800000 }
- { dest: file, name: file3, size: 2048}
- { dest: host, name: host3, level: critical}
- name: Delete logging using aggregate
iosxr_logging:
aggregate:
- { dest: console, level: warning }
- { dest: buffered, size: 4800000 }
- { dest: file, name: file3, size: 2048}
- { dest: host, name: host3, level: critical}
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always (empty list when no commands to send)
type: list
sample:
- logging 10.10.10.1 vrf default severity debugging
- logging facility local7
- logging hostnameprefix host1
- logging console critical
- logging buffered 2097153
- logging buffered warnings
- logging monitor errors
- logging file log_file maxfilesize 1024 severity info
xml:
description: NetConf rpc xml sent to device with transport C(netconf)
returned: always (empty list when no xml rpc to send)
type: list
version_added: 2.5
sample:
- '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<syslog xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-infra-syslog-cfg">
<files>
<file xc:operation="delete">
<file-name>file1</file-name>
<file-log-attributes>
<max-file-size>2097152</max-file-size>
<severity>2</severity>
</file-log-attributes>
</file>
</files>
</syslog>
</config>'
"""
import re
import collections
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config, build_xml
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, etree_findall
from ansible.module_utils.network.iosxr.iosxr import is_netconf, is_cliconf, etree_find
from ansible.module_utils.network.common.utils import remove_default_spec
severity_level = {'emergency': '0',
'alert': '1',
'critical': '2',
'error': '3',
'warning': '4',
'notice': '5',
'info': '6',
'debug': '7',
'disable': '15'}
severity_transpose = {'emergencies': 'emergency',
'alerts': 'alert',
'critical': 'critical',
'errors': 'error',
'warning': 'warning',
'notifications': 'notice',
'informational': 'info',
'debugging': 'debug'}
class ConfigBase(object):
def __init__(self, module):
self._flag = None
self._module = module
self._result = {'changed': False, 'warnings': []}
self._want = list()
self._have = list()
def validate_size(self, value, type=None):
if value:
if type == 'buffer':
if value and not int(307200) <= value <= int(125000000):
self._module.fail_json(msg='buffer size must be between 307200 and 125000000')
elif type == 'file':
if value and not int(1) <= value <= int(2097152):
self._module.fail_json(msg='file size must be between 1 and 2097152')
return value
def map_params_to_obj(self, required_if=None):
aggregate = self._module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = self._module.params[key]
d = item.copy()
if d['dest'] not in ('host', 'file'):
d['name'] = None
if d['dest'] == 'buffered':
if d['size'] is not None:
d['size'] = str(self.validate_size(d['size'], 'buffer'))
else:
d['size'] = str(307200)
elif d['dest'] == 'file':
if d['size'] is not None:
d['size'] = str(self.validate_size(d['size'], 'file'))
else:
d['size'] = str(2097152)
else:
d['size'] = None
if self._flag == 'NC':
d['level'] = severity_transpose[d['level']]
self._want.append(d)
else:
params = self._module.params
if params['dest'] not in ('host', 'file'):
params['name'] = None
if params['dest'] == 'buffered':
if params['size'] is not None:
params['size'] = str(self.validate_size(params['size'], 'buffer'))
else:
params['size'] = str(307200)
elif params['dest'] == 'file':
if params['size'] is not None:
params['size'] = str(self.validate_size(params['size'], 'file'))
else:
params['size'] = str(2097152)
else:
params['size'] = None
if self._flag == 'NC':
params['level'] = severity_transpose[params['level']]
self._want.append({
'dest': params['dest'],
'name': params['name'],
'vrf': params['vrf'],
'size': params['size'],
'facility': params['facility'],
'level': params['level'],
'hostnameprefix': params['hostnameprefix'],
'state': params['state']
})
class CliConfiguration(ConfigBase):
def __init__(self, module):
super(CliConfiguration, self).__init__(module)
self._file_list = set()
self._host_list = set()
def map_obj_to_commands(self):
commands = list()
for want_item in self._want:
dest = want_item['dest']
name = want_item['name']
size = want_item['size']
facility = want_item['facility']
level = want_item['level']
vrf = want_item['vrf']
hostnameprefix = want_item['hostnameprefix']
state = want_item['state']
del want_item['state']
have_size = None
have_console_level = None
have_monitor_level = None
have_prefix = None
have_facility = None
for item in self._have:
if item['dest'] == 'buffered':
have_size = item['size']
if item['dest'] == 'console':
have_console_level = item['level']
if item['dest'] == 'monitor':
have_monitor_level = item['level']
if item['dest'] is None and item['hostnameprefix'] is not None:
have_prefix = item['hostnameprefix']
if item['dest'] is None and item['hostnameprefix'] is None and item['facility'] is not None:
have_facility = item['facility']
if state == 'absent':
if dest == 'host' and name in self._host_list:
commands.append('no logging {0} vrf {1}'.format(name, vrf))
elif dest == 'file' and name in self._file_list:
commands.append('no logging file {0}'.format(name))
elif dest == 'console' and have_console_level is not None:
commands.append('no logging {0}'.format(dest))
elif dest == 'monitor' and have_monitor_level:
commands.append('no logging {0}'.format(dest))
elif dest == 'buffered' and have_size:
commands.append('no logging {0}'.format(dest))
if dest is None and hostnameprefix is not None and have_prefix == hostnameprefix:
commands.append('no logging hostnameprefix {0}'.format(hostnameprefix))
if dest is None and facility is not None and have_facility == facility:
commands.append('no logging facility {0}'.format(facility))
if state == 'present':
if dest == 'host' and name not in self._host_list:
if level == 'errors' or level == 'informational':
level = severity_transpose[level]
commands.append('logging {0} vrf {1} severity {2}'.format(name, vrf, level))
elif dest == 'file' and name not in self._file_list:
if level == 'errors' or level == 'informational':
level = severity_transpose[level]
commands.append('logging file {0} maxfilesize {1} severity {2}'.format(name, size, level))
elif dest == 'buffered' and (have_size is None or (have_size is not None and size != have_size)):
commands.append('logging buffered {0}'.format(size))
elif dest == 'console' and (have_console_level is None or
(have_console_level is not None and have_console_level != level)):
commands.append('logging console {0}'.format(level))
elif dest == 'monitor' and (have_monitor_level is None or
(have_monitor_level is not None and have_monitor_level != level)):
commands.append('logging monitor {0}'.format(level))
if dest is None and hostnameprefix is not None and (have_prefix is None or
(have_prefix is not None and hostnameprefix != have_prefix)):
commands.append('logging hostnameprefix {0}'.format(hostnameprefix))
if dest is None and hostnameprefix is None and facility != have_facility:
commands.append('logging facility {0}'.format(facility))
self._result['commands'] = commands
if commands:
commit = not self._module.check_mode
diff = load_config(self._module, commands, commit=commit)
if diff:
self._result['diff'] = dict(prepared=diff)
self._result['changed'] = True
def parse_facility(self, line):
match = re.search(r'logging facility (\S+)', line, re.M)
facility = None
if match:
facility = match.group(1)
return facility
def parse_size(self, line, dest):
size = None
if dest == 'buffered':
match = re.search(r'logging buffered (\S+)', line, re.M)
if match:
try:
int_size = int(match.group(1))
except ValueError:
int_size = None
if int_size is not None:
if isinstance(int_size, int):
size = str(match.group(1))
return size
def parse_hostnameprefix(self, line):
prefix = None
match = re.search(r'logging hostnameprefix (\S+)', line, re.M)
if match:
prefix = match.group(1)
return prefix
def parse_name(self, line, dest):
name = None
if dest == 'file':
match = re.search(r'logging file (\S+)', line, re.M)
if match:
name = match.group(1)
elif dest == 'host':
match = re.search(r'logging (\S+)', line, re.M)
if match:
name = match.group(1)
return name
def parse_level(self, line, dest):
level_group = ('emergencies', 'alerts', 'critical', 'errors', 'warning',
'notifications', 'informational', 'debugging')
level = None
match = re.search(r'logging {0} (\S+)'.format(dest), line, re.M)
if match:
if match.group(1) in level_group:
level = match.group(1)
return level
def parse_dest(self, line, group):
dest_group = ('console', 'monitor', 'buffered', 'file')
dest = None
if group in dest_group:
dest = group
elif 'vrf' in line:
dest = 'host'
return dest
def parse_vrf(self, line, dest):
vrf = None
if dest == 'host':
match = re.search(r'logging (\S+) vrf (\S+)', line, re.M)
if match:
vrf = match.group(2)
return vrf
def map_config_to_obj(self):
data = get_config(self._module, config_filter='logging')
lines = data.split("\n")
for line in lines:
match = re.search(r'logging (\S+)', line, re.M)
if match:
dest = self.parse_dest(line, match.group(1))
name = self.parse_name(line, dest)
if dest == 'host' and name is not None:
self._host_list.add(name)
if dest == 'file' and name is not None:
self._file_list.add(name)
self._have.append({
'dest': dest,
'name': name,
'size': self.parse_size(line, dest),
'facility': self.parse_facility(line),
'level': self.parse_level(line, dest),
'vrf': self.parse_vrf(line, dest),
'hostnameprefix': self.parse_hostnameprefix(line),
})
def run(self):
self.map_params_to_obj()
self.map_config_to_obj()
self.map_obj_to_commands()
return self._result
class NCConfiguration(ConfigBase):
def __init__(self, module):
super(NCConfiguration, self).__init__(module)
self._flag = 'NC'
self._log_file_meta = collections.OrderedDict()
self._log_host_meta = collections.OrderedDict()
self._log_console_meta = collections.OrderedDict()
self._log_monitor_meta = collections.OrderedDict()
self._log_buffered_size_meta = collections.OrderedDict()
self._log_buffered_level_meta = collections.OrderedDict()
self._log_facility_meta = collections.OrderedDict()
self._log_prefix_meta = collections.OrderedDict()
def map_obj_to_xml_rpc(self):
self._log_file_meta.update([
('files', {'xpath': 'syslog/files', 'tag': True, 'operation': 'edit'}),
('file', {'xpath': 'syslog/files/file', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:name', {'xpath': 'syslog/files/file/file-name', 'operation': 'edit'}),
('file-attrib', {'xpath': 'syslog/files/file/file-log-attributes', 'tag': True, 'operation': 'edit'}),
('a:size', {'xpath': 'syslog/files/file/file-log-attributes/max-file-size', 'operation': 'edit'}),
('a:level', {'xpath': 'syslog/files/file/file-log-attributes/severity', 'operation': 'edit'}),
])
self._log_host_meta.update([
('host-server', {'xpath': 'syslog/host-server', 'tag': True, 'operation': 'edit'}),
('vrfs', {'xpath': 'syslog/host-server/vrfs', 'tag': True, 'operation': 'edit'}),
('vrf', {'xpath': 'syslog/host-server/vrfs/vrf', 'tag': True, 'operation': 'edit'}),
('a:vrf', {'xpath': 'syslog/host-server/vrfs/vrf/vrf-name', 'operation': 'edit'}),
('ipv4s', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s', 'tag': True, 'operation': 'edit'}),
('ipv4', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:name', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4/address', 'operation': 'edit'}),
('ipv4-sev', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4/ipv4-severity-port', 'tag': True, 'operation': 'edit'}),
('a:level', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4/ipv4-severity-port/severity', 'operation': 'edit'}),
])
self._log_console_meta.update([
('a:enable-console', {'xpath': 'syslog/enable-console-logging', 'operation': 'edit', 'attrib': "operation"}),
('console', {'xpath': 'syslog/console-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:console-level', {'xpath': 'syslog/console-logging/logging-level', 'operation': 'edit'}),
])
self._log_monitor_meta.update([
('monitor', {'xpath': 'syslog/monitor-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:monitor-level', {'xpath': 'syslog/monitor-logging/logging-level', 'operation': 'edit'}),
])
self._log_buffered_size_meta.update([
('buffered', {'xpath': 'syslog/buffered-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:size', {'xpath': 'syslog/buffered-logging/buffer-size', 'operation': 'edit'}),
])
self._log_buffered_level_meta.update([
('buffered', {'xpath': 'syslog/buffered-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:level', {'xpath': 'syslog/buffered-logging/logging-level', 'operation': 'edit'}),
])
self._log_facility_meta.update([
('facility', {'xpath': 'syslog/logging-facilities', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:facility', {'xpath': 'syslog/logging-facilities/facility-level', 'operation': 'edit'}),
])
self._log_prefix_meta.update([
('a:hostnameprefix', {'xpath': 'syslog/host-name-prefix', 'operation': 'edit', 'attrib': "operation"}),
])
state = self._module.params['state']
_get_filter = build_xml('syslog', opcode="filter")
running = get_config(self._module, source='running', config_filter=_get_filter)
file_ele = etree_findall(running, 'file')
file_list = list()
if len(file_ele):
for file in file_ele:
file_name = etree_find(file, 'file-name')
file_list.append(file_name.text if file_name is not None else None)
vrf_ele = etree_findall(running, 'vrf')
host_list = list()
for vrf in vrf_ele:
host_ele = etree_findall(vrf, 'ipv4')
for host in host_ele:
host_name = etree_find(host, 'address')
host_list.append(host_name.text if host_name is not None else None)
console_ele = etree_find(running, 'console-logging')
console_level = etree_find(console_ele, 'logging-level') if console_ele is not None else None
have_console = console_level.text if console_level is not None else None
monitor_ele = etree_find(running, 'monitor-logging')
monitor_level = etree_find(monitor_ele, 'logging-level') if monitor_ele is not None else None
have_monitor = monitor_level.text if monitor_level is not None else None
buffered_ele = etree_find(running, 'buffered-logging')
buffered_size = etree_find(buffered_ele, 'buffer-size') if buffered_ele is not None else None
have_buffered = buffered_size.text if buffered_size is not None else None
facility_ele = etree_find(running, 'logging-facilities')
facility_level = etree_find(facility_ele, 'facility-level') if facility_ele is not None else None
have_facility = facility_level.text if facility_level is not None else None
prefix_ele = etree_find(running, 'host-name-prefix')
have_prefix = prefix_ele.text if prefix_ele is not None else None
file_params = list()
host_params = list()
console_params = dict()
monitor_params = dict()
buffered_params = dict()
facility_params = dict()
prefix_params = dict()
opcode = None
if state == 'absent':
opcode = "delete"
for item in self._want:
if item['dest'] == 'file' and item['name'] in file_list:
item['level'] = severity_level[item['level']]
file_params.append(item)
elif item['dest'] == 'host' and item['name'] in host_list:
item['level'] = severity_level[item['level']]
host_params.append(item)
elif item['dest'] == 'console' and have_console:
console_params.update({'console-level': item['level']})
elif item['dest'] == 'monitor' and have_monitor:
monitor_params.update({'monitor-level': item['level']})
elif item['dest'] == 'buffered' and have_buffered:
buffered_params['size'] = str(item['size']) if item['size'] else None
buffered_params['level'] = item['level'] if item['level'] else None
elif item['dest'] is None and item['hostnameprefix'] is None and \
item['facility'] is not None and have_facility:
facility_params.update({'facility': item['facility']})
elif item['dest'] is None and item['hostnameprefix'] is not None and have_prefix:
prefix_params.update({'hostnameprefix': item['hostnameprefix']})
elif state == 'present':
opcode = 'merge'
for item in self._want:
if item['dest'] == 'file':
item['level'] = severity_level[item['level']]
file_params.append(item)
elif item['dest'] == 'host':
item['level'] = severity_level[item['level']]
host_params.append(item)
elif item['dest'] == 'console':
console_params.update({'console-level': item['level']})
elif item['dest'] == 'monitor':
monitor_params.update({'monitor-level': item['level']})
elif item['dest'] == 'buffered':
buffered_params['size'] = str(item['size']) if item['size'] else None
buffered_params['level'] = item['level'] if item['level'] else None
elif item['dest'] is None and item['hostnameprefix'] is None and \
item['facility'] is not None:
facility_params.update({'facility': item['facility']})
elif item['dest'] is None and item['hostnameprefix'] is not None:
prefix_params.update({'hostnameprefix': item['hostnameprefix']})
self._result['xml'] = []
_edit_filter_list = list()
if opcode:
if len(file_params):
_edit_filter_list.append(build_xml('syslog', xmap=self._log_file_meta,
params=file_params, opcode=opcode))
if len(host_params):
_edit_filter_list.append(build_xml('syslog', xmap=self._log_host_meta,
params=host_params, opcode=opcode))
if len(console_params):
_edit_filter_list.append(build_xml('syslog', xmap=self._log_console_meta,
params=console_params, opcode=opcode))
if len(monitor_params):
_edit_filter_list.append(build_xml('syslog', xmap=self._log_monitor_meta,
params=monitor_params, opcode=opcode))
if len(buffered_params):
_edit_filter_list.append(build_xml('syslog', xmap=self._log_buffered_size_meta,
params=buffered_params, opcode=opcode))
_edit_filter_list.append(build_xml('syslog', xmap=self._log_buffered_level_meta,
params=buffered_params, opcode=opcode))
if len(facility_params):
_edit_filter_list.append(build_xml('syslog', xmap=self._log_facility_meta,
params=facility_params, opcode=opcode))
if len(prefix_params):
_edit_filter_list.append(build_xml('syslog', xmap=self._log_prefix_meta,
params=prefix_params, opcode=opcode))
diff = None
if len(_edit_filter_list):
commit = not self._module.check_mode
diff = load_config(self._module, _edit_filter_list, commit=commit, running=running,
nc_get_filter=_get_filter)
if diff:
if self._module._diff:
self._result['diff'] = dict(prepared=diff)
self._result['xml'] = _edit_filter_list
self._result['changed'] = True
def run(self):
self.map_params_to_obj()
self.map_obj_to_xml_rpc()
return self._result
def main():
""" main entry point for module execution
"""
element_spec = dict(
dest=dict(type='str', choices=['host', 'console', 'monitor', 'buffered', 'file']),
name=dict(type='str'),
size=dict(type='int'),
vrf=dict(type='str', default='default'),
facility=dict(type='str', default='local7'),
hostnameprefix=dict(type='str'),
level=dict(type='str', default='informational', aliases=['severity'],
choices=['emergencies', 'alerts', 'critical', 'errors', 'warning',
'notifications', 'informational', 'debugging']),
state=dict(default='present', choices=['present', 'absent']),
)
aggregate_spec = deepcopy(element_spec)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
mutually_exclusive = [('dest', 'facility', 'hostnameprefix')]
required_if = [('dest', 'host', ['name']),
('dest', 'file', ['name']),
('dest', 'buffered', ['size']),
('dest', 'console', ['level']),
('dest', 'monitor', ['level'])]
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec,
mutually_exclusive=mutually_exclusive, required_if=required_if),
)
argument_spec.update(element_spec)
argument_spec.update(iosxr_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
required_if=required_if,
supports_check_mode=True)
config_object = None
if is_cliconf(module):
# Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
# module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
# version='2.9')
config_object = CliConfiguration(module)
elif is_netconf(module):
config_object = NCConfiguration(module)
if config_object:
result = config_object.run()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,204 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_netconf
version_added: "2.5"
author: "Kedar Kekan (@kedarX)"
short_description: Configures NetConf sub-system service on Cisco IOS-XR devices
description:
- This module provides an abstraction that enables and configures
the netconf system service running on Cisco IOS-XR Software.
This module can be used to easily enable the Netconf API. Netconf provides
a programmatic interface for working with configuration and state
resources as defined in RFC 6242.
extends_documentation_fragment: iosxr
options:
netconf_port:
description:
- This argument specifies the port the netconf service should
listen on for SSH connections. The default port as defined
in RFC 6242 is 830.
required: false
default: 830
aliases: ['listens_on']
netconf_vrf:
description:
- netconf vrf name
required: false
default: default
aliases: ['vrf']
state:
description:
- Specifies the state of the C(iosxr_netconf) resource on
the remote device. If the I(state) argument is set to
I(present) the netconf service will be configured. If the
I(state) argument is set to I(absent) the netconf service
will be removed from the configuration.
required: false
default: present
choices: ['present', 'absent']
notes:
- This module works with connection C(network_cli). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
- Tested against Cisco IOS XR Software, Version 6.1.3
"""
EXAMPLES = """
- name: enable netconf service on port 830
iosxr_netconf:
listens_on: 830
state: present
- name: disable netconf service
iosxr_netconf:
state: absent
"""
RETURN = """
commands:
description: Returns the command sent to the remote device
returned: when changed is True
type: str
sample: 'ssh server netconf port 830'
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config
from ansible.module_utils.six import iteritems
USE_PERSISTENT_CONNECTION = True
def map_obj_to_commands(updates):
want, have = updates
commands = list()
if want['state'] == 'absent':
if have['state'] == 'present':
commands.append('no netconf-yang agent ssh')
if 'netconf_port' in have:
commands.append('no ssh server netconf port %s' % have['netconf_port'])
if have['netconf_vrf']:
for vrf in have['netconf_vrf']:
commands.append('no ssh server netconf vrf %s' % vrf)
else:
if have['state'] == 'absent':
commands.append('netconf-yang agent ssh')
if want['netconf_port'] is not None and (want['netconf_port'] != have.get('netconf_port')):
commands.append(
'ssh server netconf port %s' % want['netconf_port']
)
if want['netconf_vrf'] is not None and (want['netconf_vrf'] not in have['netconf_vrf']):
commands.append(
'ssh server netconf vrf %s' % want['netconf_vrf']
)
return commands
def parse_vrf(config):
match = re.search(r'vrf (\w+)', config)
if match:
return match.group(1)
def parse_port(config):
match = re.search(r'port (\d+)', config)
if match:
return int(match.group(1))
def map_config_to_obj(module):
obj = {'state': 'absent'}
netconf_config = get_config(module, config_filter='netconf-yang agent')
ssh_config = get_config(module, config_filter='ssh server')
ssh_config = [config_line for config_line in (line.strip() for line in ssh_config.splitlines()) if config_line]
obj['netconf_vrf'] = []
for config in ssh_config:
if 'netconf port' in config:
obj.update({'netconf_port': parse_port(config)})
if 'netconf vrf' in config:
obj['netconf_vrf'].append(parse_vrf(config))
if 'ssh' in netconf_config and ('netconf_port' in obj or obj['netconf_vrf']):
obj.update({'state': 'present'})
if 'ssh' in netconf_config and 'netconf_port' not in obj:
obj.update({'netconf_port': 830})
return obj
def validate_netconf_port(value, module):
if not 1 <= value <= 65535:
module.fail_json(msg='netconf_port must be between 1 and 65535')
def map_params_to_obj(module):
obj = {
'netconf_port': module.params['netconf_port'],
'netconf_vrf': module.params['netconf_vrf'],
'state': module.params['state']
}
for key, value in iteritems(obj):
# validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key)
if callable(validator):
validator(value, module)
return obj
def main():
"""main entry point for module execution
"""
argument_spec = dict(
netconf_port=dict(type='int', default=830, aliases=['listens_on']),
netconf_vrf=dict(aliases=['vrf'], default='default'),
state=dict(default='present', choices=['present', 'absent']),
)
argument_spec.update(iosxr_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
warnings = list()
result = {'changed': False, 'warnings': warnings}
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have))
result['commands'] = commands
if commands:
commit = not module.check_mode
diff = load_config(module, commands, commit=commit)
if diff:
result['diff'] = dict(prepared=diff)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,966 +0,0 @@
#!/usr/bin/python
# -*- 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 module file for iosxr_static_routes
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.1",
"status": ["preview"],
"supported_by": "network",
}
DOCUMENTATION = """
---
module: iosxr_static_routes
version_added: "2.10"
short_description: Manage static routes on devices running Cisco IOS-XR.
description:
- This module manages static routes on devices running Cisco IOS-XR.
author: Nilashish Chakraborty (@NilashishC)
options:
running_config:
description:
- The module, by default, will connect to the remote device and
retrieve the current running-config to use as a base for comparing
against the contents of source. There are times when it is not
desirable to have the task get the current running-config for
every task in a playbook. The I(running_config) argument allows the
implementer to pass in the configuration to use as the base
config for comparison. This value of this option should be the
output received from device by executing command
B(show running-config router static).
type: str
config:
description: A dictionary of static route options.
type: list
elements: dict
suboptions:
vrf:
description:
- The VRF to which the static route(s) belong.
type: str
address_families:
description: A dictionary specifying the address family to which the static route(s) belong.
type: list
elements: dict
suboptions:
afi:
description:
- Specifies the top level address family indicator.
type: str
choices: ['ipv4', 'ipv6']
required: True
safi:
description:
- Specifies the subsequent address family indicator.
type: str
choices: ['unicast', 'multicast']
required: True
routes:
description: A dictionary that specifies the static route configurations.
elements: dict
type: list
suboptions:
dest:
description:
- An IPv4 or IPv6 address in CIDR notation that specifies the destination network for the static route.
type: str
required: True
next_hops:
description:
- Next hops to the specified destination.
type: list
elements: dict
suboptions:
forward_router_address:
description:
- The IP address of the next hop that can be used to reach the destination network.
type: str
interface:
description:
- The interface to use to reach the destination.
type: str
dest_vrf:
description:
- The destination VRF.
type: str
admin_distance:
description:
- The administrative distance for this static route.
- Refer to vendor documentation for valid values.
type: int
metric:
description:
- Specifes the metric for this static route.
- Refer to vendor documentation for valid values.
type: int
description:
description:
- Specifies the description for this static route.
type: str
vrflabel:
description:
- Specifies the VRF label for this static route.
- Refer to vendor documentation for valid values.
type: int
tag:
description:
- Specifies a numeric tag for this static route.
- Refer to vendor documentation for valid values.
type: int
track:
description:
- Specifies the object to be tracked.
- This enables object tracking for static routes.
type: str
tunnel_id:
description:
- Specifies a tunnel id for the route.
- Refer to vendor documentation for valid values.
type: int
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- gathered
- rendered
- parsed
default: merged
"""
EXAMPLES = """
# Using merged
# Before state
# -------------
# RP/0/RP0/CPU0:ios#show running-config router static
# Sat Feb 22 07:46:30.089 UTC
# % No such configuration item(s)
#
- name: Merge the provided configuration with the exisiting running configuration
iosxr_static_routes: &merged
config:
- address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.16/28
next_hops:
- forward_router_address: 192.0.2.10
interface: FastEthernet0/0/0/1
description: "LAB"
metric: 120
tag: 10
- interface: FastEthernet0/0/0/5
track: ip_sla_1
- dest: 192.0.2.32/28
next_hops:
- forward_router_address: 192.0.2.11
admin_distance: 100
- afi: ipv6
safi: unicast
routes:
- dest: 2001:db8:1000::/36
next_hops:
- interface: FastEthernet0/0/0/7
description: "DC"
- interface: FastEthernet0/0/0/8
forward_router_address: 2001:db8:2000:2::1
- vrf: DEV_SITE
address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.48/28
next_hops:
- forward_router_address: 192.0.2.12
description: "DEV"
dest_vrf: test_1
- dest: 192.0.2.80/28
next_hops:
- interface: FastEthernet0/0/0/2
forward_router_address: 192.0.2.14
dest_vrf: test_1
track: ip_sla_2
vrflabel: 124
state: merged
# After state
# -------------
# RP/0/RP0/CPU0:ios#show running-config router static
# Sat Feb 22 07:49:11.754 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
# Using merged to update existing static routes
# Before state
# -------------
# RP/0/RP0/CPU0:ios#show running-config router static
# Sat Feb 22 07:49:11.754 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Update existing static routes configuration using merged
iosxr_static_routes: &merged_update
config:
- vrf: DEV_SITE
address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.48/28
next_hops:
- forward_router_address: 192.0.2.12
vrflabel: 2301
dest_vrf: test_1
- dest: 192.0.2.80/28
next_hops:
- interface: FastEthernet0/0/0/2
forward_router_address: 192.0.2.14
dest_vrf: test_1
description: "rt_test_1"
state: merged
# After state
# -------------
# RP/0/RP0/CPU0:ios#show running-config router static
# Sat Feb 22 07:49:11.754 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV vrflabel 2301
# 192.0.2.80/28 vrf test_1 192.0.2.14 FastEthernet0/0/0/2 description rt_test_1 track ip_sla_2 vrflabel 124
# !
# !
# !
# Using replaced to replace all next hop entries for a single destination network
# Before state
# --------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Replace device configurations of static routes with provided configurations
iosxr_static_routes: &replaced
config:
- vrf: DEV_SITE
address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.48/28
next_hops:
- forward_router_address: 192.0.2.15
interface: FastEthernet0/0/0/3
description: "DEV_NEW"
dest_vrf: dev_test_2
state: replaced
# After state
# ------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 08:04:07.085 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf dev_test_2 FastEthernet0/0/0/3 192.0.2.15 description DEV_NEW
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
# Using overridden to override all static route entries on the device
# Before state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Overridde all static routes configuration with provided configuration
iosxr_static_routes: &overridden
config:
- vrf: DEV_NEW
address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.48/28
next_hops:
- forward_router_address: 192.0.2.15
interface: FastEthernet0/0/0/3
description: "DEV1"
- afi: ipv6
safi: unicast
routes:
- dest: 2001:db8:3000::/36
next_hops:
- interface: FastEthernet0/0/0/4
forward_router_address: 2001:db8:2000:2::2
description: "PROD1"
track: ip_sla_1
state: overridden
# After state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 08:07:41.516 UTC
# router static
# vrf DEV_NEW
# address-family ipv4 unicast
# 192.0.2.48/28 FastEthernet0/0/0/3 192.0.2.15 description DEV1
# !
# address-family ipv6 unicast
# 2001:db8:3000::/36 FastEthernet0/0/0/4 2001:db8:2000:2::2 description PROD1 track ip_sla_1
# !
# !
# !
# Using deleted to delete a single next hop for a destination network
# Before state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Delete a single next_hop from a destination network
iosxr_static_routes:
config:
- address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.16/28
next_hops:
- forward_router_address: 192.0.2.10
interface: FastEthernet0/0/0/1
state: deleted
# After state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
# Using deleted to delete a destination network entry
# Before state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Delete a destination network entry
iosxr_static_routes:
config:
- vrf: DEV_SITE
address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.48/28
state: deleted
# After state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
# Using deleted to delete all destination network entries under a single AFI
# Before state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Delete all destination network entries under a single AFI
iosxr_static_routes:
config:
- vrf: DEV_SITE
address_families:
- afi: ipv4
safi: unicast
state: deleted
# After state
# ------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 08:16:41.464 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# !
# !
# Using deleted to remove all static route entries from the device
# Before state
# -------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 07:59:08.669 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.48/28 GigabitEthernet0/0/0/1 192.0.3.24 vrflabel 2302
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Delete static routes configuration
iosxr_static_routes: &deleted
state: deleted
# After state
# ------------
# RP/0/RP0/CPU0:ios#sh running-config router static
# Sat Feb 22 08:50:43.038 UTC
# % No such configuration item(s)
# Using gathered to gather static route facts from the device
- name: Gather static routes facts from the device using iosxr_static_routes module
iosxr_static_routes:
state: gathered
# Task output (redacted)
# -----------------------
# "gathered": [
# {
# "address_families": [
# {
# "afi": "ipv4",
# "routes": [
# {
# "dest": "192.0.2.16/28",
# "next_hops": [
# {
# "description": "LAB",
# "forward_router_address": "192.0.2.10",
# "interface": "FastEthernet0/0/0/1",
# "metric": 120,
# "tag": 10
# },
# {
# "interface": "FastEthernet0/0/0/5",
# "track": "ip_sla_1"
# }
# ]
# },
# {
# "dest": "192.0.2.32/28",
# "next_hops": [
# {
# "admin_distance": 100,
# "forward_router_address": "192.0.2.11"
# }
# ]
# }
# ],
# "safi": "unicast"
# },
# {
# "afi": "ipv6",
# "routes": [
# {
# "dest": "2001:db8:1000::/36",
# "next_hops": [
# {
# "description": "DC",
# "interface": "FastEthernet0/0/0/7"
# },
# {
# "forward_router_address": "2001:db8:2000:2::1",
# "interface": "FastEthernet0/0/0/8"
# }
# ]
# }
# ],
# "safi": "unicast"
# }
# ]
# },
# {
# "address_families": [
# {
# "afi": "ipv4",
# "routes": [
# {
# "dest": "192.0.2.48/28",
# "next_hops": [
# {
# "description": "DEV",
# "dest_vrf": "test_1",
# "forward_router_address": "192.0.2.12"
# },
# {
# "forward_router_address": "192.0.3.24",
# "interface": "GigabitEthernet0/0/0/1",
# "vrflabel": 2302
# }
# ]
# },
# {
# "dest": "192.0.2.80/28",
# "next_hops": [
# {
# "dest_vrf": "test_1",
# "forward_router_address": "192.0.2.14",
# "interface": "FastEthernet0/0/0/2",
# "track": "ip_sla_2",
# "vrflabel": 124
# }
# ]
# }
# ],
# "safi": "unicast"
# }
# ],
# "vrf": "DEV_SITE"
# }
# ]
# Using rendered
- name: Render platform specific commands (without connecting to the device)
iosxr_static_routes:
config:
- vrf: DEV_SITE
address_families:
- afi: ipv4
safi: unicast
routes:
- dest: 192.0.2.48/28
next_hops:
- forward_router_address: 192.0.2.12
description: "DEV"
dest_vrf: test_1
- dest: 192.0.2.80/28
next_hops:
- interface: FastEthernet0/0/0/2
forward_router_address: 192.0.2.14
dest_vrf: test_1
track: ip_sla_2
vrflabel: 124
# Task Output (redacted)
# -----------------------
# "rendered": [
# "router static"s,
# "vrf DEV_SITE",
# "address-family ipv4 unicast",
# "192.0.2.48/28 vrf test_1 192.0.2.12 description DEV",
# "192.0.2.80/28 vrf test_1 192.0.2.14 FastEthernet0/0/0/2 track ip_sla_2 vrflabel 124"
# Using parsed
# parsed.cfg
# ------------
# Fri Nov 29 21:10:41.896 UTC
# router static
# address-family ipv4 unicast
# 192.0.2.16/28 FastEthernet0/0/0/1 192.0.2.10 tag 10 description LAB metric 120
# 192.0.2.16/28 FastEthernet0/0/0/5 track ip_sla_1
# 192.0.2.32/28 192.0.2.11 100
# !
# address-family ipv6 unicast
# 2001:db8:1000::/36 FastEthernet0/0/0/7 description DC
# 2001:db8:1000::/36 FastEthernet0/0/0/8 2001:db8:2000:2::1
# !
# vrf DEV_SITE
# address-family ipv4 unicast
# 192.0.2.48/28 vrf test_1 192.0.2.12 description DEV
# 192.0.2.80/28 vrf test_1 FastEthernet0/0/0/2 192.0.2.14 vrflabel 124 track ip_sla_2
# !
# !
# !
- name: Use parsed state to convert externally supplied device specific static routes commands to structured format
iosxr_static_routes:
running_config: "{{ lookup('file', '../../fixtures/parsed.cfg') }}"
state: parsed
# Task output (redacted)
# -----------------------
# "parsed": [
# {
# "address_families": [
# {
# "afi": "ipv4",
# "routes": [
# {
# "dest": "192.0.2.16/28",
# "next_hops": [
# {
# "description": "LAB",
# "forward_router_address": "192.0.2.10",
# "interface": "FastEthernet0/0/0/1",
# "metric": 120,
# "tag": 10
# },
# {
# "interface": "FastEthernet0/0/0/5",
# "track": "ip_sla_1"
# }
# ]
# },
# {
# "dest": "192.0.2.32/28",
# "next_hops": [
# {
# "admin_distance": 100,
# "forward_router_address": "192.0.2.11"
# }
# ]
# }
# ],
# "safi": "unicast"
# },
# {
# "afi": "ipv6",
# "routes": [
# {
# "dest": "2001:db8:1000::/36",
# "next_hops": [
# {
# "description": "DC",
# "interface": "FastEthernet0/0/0/7"
# },
# {
# "forward_router_address": "2001:db8:2000:2::1",
# "interface": "FastEthernet0/0/0/8"
# }
# ]
# }
# ],
# "safi": "unicast"
# }
# ]
# },
# {
# "address_families": [
# {
# "afi": "ipv4",
# "routes": [
# {
# "dest": "192.0.2.48/28",
# "next_hops": [
# {
# "description": "DEV",
# "dest_vrf": "test_1",
# "forward_router_address": "192.0.2.12"
# }
# ]
# },
# {
# "dest": "192.0.2.80/28",
# "next_hops": [
# {
# "dest_vrf": "test_1",
# "forward_router_address": "192.0.2.14",
# "interface": "FastEthernet0/0/0/2",
# "track": "ip_sla_2",
# "vrflabel": 124
# }
# ]
# }
# ],
# "safi": "unicast"
# }
# ],
# "vrf": "DEV_SITE"
# }
# ]
# }
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample:
- router static
- vrf dev_site
- address-family ipv4 unicast
- 192.0.2.48/28 192.0.2.12 FastEthernet0/0/0/1 track ip_sla_10 description dev1
- address-family ipv6 unicast
- no 2001:db8:1000::/36
- 2001:db8:3000::/36 2001:db8:2000:2::2 FastEthernet0/0/0/4 track ip_sla_11 description prod1
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.argspec.static_routes.static_routes import Static_routesArgs
from ansible.module_utils.network.iosxr.config.static_routes.static_routes import Static_routes
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "overridden", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Static_routesArgs.argument_spec,
required_if=required_if,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
result = Static_routes(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()

@ -1,594 +0,0 @@
#!/usr/bin/python
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_system
version_added: "2.3"
author:
- "Peter Sprygada (@privateip)"
- "Kedar Kekan (@kedarX)"
short_description: Manage the system attributes on Cisco IOS XR devices
description:
- This module provides declarative management of node system attributes
on Cisco IOS XR devices. It provides an option to configure host system
parameters or remove those parameters from the device active
configuration.
requirements:
- ncclient >= 0.5.3 when using netconf
- lxml >= 4.1.1 when using netconf
extends_documentation_fragment: iosxr
notes:
- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
- Tested against IOS XRv 6.1.3
- name-servers I(state=absent) operation with C(netconf) transport is a success, but with rpc-error. This is
due to XR platform issue. Recommended to use I(ignore_errors) option with the task as a workaround.
options:
hostname:
description:
- Configure the device hostname parameter. This option takes an ASCII string value.
vrf:
description:
- VRF name for domain services
version_added: 2.5
domain_name:
description:
- Configure the IP domain name
on the remote device to the provided value. Value
should be in the dotted name form and will be
appended to the C(hostname) to create a fully-qualified
domain name.
domain_search:
description:
- Provides the list of domain suffixes to
append to the hostname for the purpose of doing name resolution.
This argument accepts a list of names and will be reconciled
with the current active configuration on the running node.
lookup_source:
description:
- The C(lookup_source) argument provides one or more source
interfaces to use for performing DNS lookups. The interface
provided in C(lookup_source) must be a valid interface configured
on the device.
lookup_enabled:
description:
- Provides administrative control
for enabling or disabling DNS lookups. When this argument is
set to True, lookups are performed and when it is set to False,
lookups are not performed.
type: bool
name_servers:
description:
- The C(name_serves) argument accepts a list of DNS name servers by
way of either FQDN or IP address to use to perform name resolution
lookups. This argument accepts wither a list of DNS servers See
examples.
state:
description:
- State of the configuration
values in the device's current active configuration. When set
to I(present), the values should be configured in the device active
configuration and when set to I(absent) the values should not be
in the device active configuration
default: present
choices: ['present', 'absent']
"""
EXAMPLES = """
- name: configure hostname and domain-name (default vrf=default)
iosxr_system:
hostname: iosxr01
domain_name: test.example.com
domain_search:
- ansible.com
- redhat.com
- cisco.com
- name: remove configuration
iosxr_system:
hostname: iosxr01
domain_name: test.example.com
domain_search:
- ansible.com
- redhat.com
- cisco.com
state: absent
- name: configure hostname and domain-name with vrf
iosxr_system:
hostname: iosxr01
vrf: nondefault
domain_name: test.example.com
domain_search:
- ansible.com
- redhat.com
- cisco.com
- name: configure DNS lookup sources
iosxr_system:
lookup_source: MgmtEth0/0/CPU0/0
lookup_enabled: True
- name: configure name servers
iosxr_system:
name_servers:
- 8.8.8.8
- 8.8.4.4
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- hostname iosxr01
- ip domain-name test.example.com
xml:
description: NetConf rpc xml sent to device with transport C(netconf)
returned: always (empty list when no xml rpc to send)
type: list
version_added: 2.5
sample:
- '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<ip-domain xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ip-domain-cfg">
<vrfs>
<vrf>
<vrf-name>default</vrf-name>
<lists>
<list xc:operation="merge">
<order>0</order>
<list-name>redhat.com</list-name>
</list>
</lists>
</vrf>
</vrfs>
</ip-domain>
</config>'
"""
import re
import collections
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config, etree_findall
from ansible.module_utils.network.iosxr.iosxr import is_cliconf, is_netconf, etree_find
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, build_xml
def diff_list(want, have):
adds = set(want).difference(have)
removes = set(have).difference(want)
return (adds, removes)
class ConfigBase(object):
def __init__(self, module):
self._module = module
self._result = {'changed': False, 'warnings': []}
self._want = dict()
self._have = dict()
def map_params_to_obj(self):
self._want.update({
'hostname': self._module.params['hostname'],
'vrf': self._module.params['vrf'],
'domain_name': self._module.params['domain_name'],
'domain_search': self._module.params['domain_search'],
'lookup_source': self._module.params['lookup_source'],
'lookup_enabled': self._module.params['lookup_enabled'],
'name_servers': self._module.params['name_servers']
})
class CliConfiguration(ConfigBase):
def __init__(self, module):
super(CliConfiguration, self).__init__(module)
def map_obj_to_commands(self):
commands = list()
state = self._module.params['state']
def needs_update(x):
return self._want.get(x) and (self._want.get(x) != self._have.get(x))
if state == 'absent':
if self._have['hostname'] != 'ios':
commands.append('no hostname')
if self._have['domain_name']:
commands.append('no domain name')
if self._have['lookup_source']:
commands.append('no domain lookup source-interface {0!s}'.format(self._have['lookup_source']))
if not self._have['lookup_enabled']:
commands.append('no domain lookup disable')
for item in self._have['name_servers']:
commands.append('no domain name-server {0!s}'.format(item))
for item in self._have['domain_search']:
commands.append('no domain list {0!s}'.format(item))
elif state == 'present':
if needs_update('hostname'):
commands.append('hostname {0!s}'.format(self._want['hostname']))
if needs_update('domain_name'):
commands.append('domain name {0!s}'.format(self._want['domain_name']))
if needs_update('lookup_source'):
commands.append('domain lookup source-interface {0!s}'.format(self._want['lookup_source']))
cmd = None
if not self._want['lookup_enabled'] and self._have['lookup_enabled']:
cmd = 'domain lookup disable'
elif self._want['lookup_enabled'] and not self._have['lookup_enabled']:
cmd = 'no domain lookup disable'
if cmd is not None:
commands.append(cmd)
if self._want['name_servers'] is not None:
adds, removes = diff_list(self._want['name_servers'], self._have['name_servers'])
for item in adds:
commands.append('domain name-server {0!s}'.format(item))
for item in removes:
commands.append('no domain name-server {0!s}'.format(item))
if self._want['domain_search'] is not None:
adds, removes = diff_list(self._want['domain_search'], self._have['domain_search'])
for item in adds:
commands.append('domain list {0!s}'.format(item))
for item in removes:
commands.append('no domain list {0!s}'.format(item))
self._result['commands'] = []
if commands:
commit = not self._module.check_mode
diff = load_config(self._module, commands, commit=commit)
if diff:
self._result['diff'] = dict(prepared=diff)
self._result['commands'] = commands
self._result['changed'] = True
def parse_hostname(self, config):
match = re.search(r'^hostname (\S+)', config, re.M)
if match:
return match.group(1)
def parse_domain_name(self, config):
match = re.search(r'^domain name (\S+)', config, re.M)
if match:
return match.group(1)
def parse_lookup_source(self, config):
match = re.search(r'^domain lookup source-interface (\S+)', config, re.M)
if match:
return match.group(1)
def map_config_to_obj(self):
config = get_config(self._module)
self._have.update({
'hostname': self.parse_hostname(config),
'domain_name': self.parse_domain_name(config),
'domain_search': re.findall(r'^domain list (\S+)', config, re.M),
'lookup_source': self.parse_lookup_source(config),
'lookup_enabled': 'domain lookup disable' not in config,
'name_servers': re.findall(r'^domain name-server (\S+)', config, re.M)
})
def run(self):
self.map_params_to_obj()
self.map_config_to_obj()
self.map_obj_to_commands()
return self._result
class NCConfiguration(ConfigBase):
def __init__(self, module):
super(NCConfiguration, self).__init__(module)
self._system_meta = collections.OrderedDict()
self._system_domain_meta = collections.OrderedDict()
self._system_server_meta = collections.OrderedDict()
self._hostname_meta = collections.OrderedDict()
self._lookup_source_meta = collections.OrderedDict()
self._lookup_meta = collections.OrderedDict()
def map_obj_to_xml_rpc(self):
self._system_meta.update([
('vrfs', {'xpath': 'ip-domain/vrfs', 'tag': True, 'operation': 'edit'}),
('vrf', {'xpath': 'ip-domain/vrfs/vrf', 'tag': True, 'operation': 'edit'}),
('a:vrf', {'xpath': 'ip-domain/vrfs/vrf/vrf-name', 'operation': 'edit'}),
('a:domain_name', {'xpath': 'ip-domain/vrfs/vrf/name', 'operation': 'edit', 'attrib': "operation"}),
])
self._system_domain_meta.update([
('vrfs', {'xpath': 'ip-domain/vrfs', 'tag': True, 'operation': 'edit'}),
('vrf', {'xpath': 'ip-domain/vrfs/vrf', 'tag': True, 'operation': 'edit'}),
('a:vrf', {'xpath': 'ip-domain/vrfs/vrf/vrf-name', 'operation': 'edit'}),
('lists', {'xpath': 'ip-domain/vrfs/vrf/lists', 'tag': True, 'operation': 'edit'}),
('list', {'xpath': 'ip-domain/vrfs/vrf/lists/list', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:order', {'xpath': 'ip-domain/vrfs/vrf/lists/list/order', 'operation': 'edit'}),
('a:domain_search', {'xpath': 'ip-domain/vrfs/vrf/lists/list/list-name', 'operation': 'edit'}),
])
self._system_server_meta.update([
('vrfs', {'xpath': 'ip-domain/vrfs', 'tag': True, 'operation': 'edit'}),
('vrf', {'xpath': 'ip-domain/vrfs/vrf', 'tag': True, 'operation': 'edit'}),
('a:vrf', {'xpath': 'ip-domain/vrfs/vrf/vrf-name', 'operation': 'edit'}),
('servers', {'xpath': 'ip-domain/vrfs/vrf/servers', 'tag': True, 'operation': 'edit'}),
('server', {'xpath': 'ip-domain/vrfs/vrf/servers/server', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
('a:order', {'xpath': 'ip-domain/vrfs/vrf/servers/server/order', 'operation': 'edit'}),
('a:name_servers', {'xpath': 'ip-domain/vrfs/vrf/servers/server/server-address', 'operation': 'edit'}),
])
self._hostname_meta.update([
('a:hostname', {'xpath': 'host-names/host-name', 'operation': 'edit', 'attrib': "operation"}),
])
self._lookup_source_meta.update([
('vrfs', {'xpath': 'ip-domain/vrfs', 'tag': True, 'operation': 'edit'}),
('vrf', {'xpath': 'ip-domain/vrfs/vrf', 'tag': True, 'operation': 'edit'}),
('a:vrf', {'xpath': 'ip-domain/vrfs/vrf/vrf-name', 'operation': 'edit'}),
('a:lookup_source', {'xpath': 'ip-domain/vrfs/vrf/source-interface', 'operation': 'edit', 'attrib': "operation"}),
])
self._lookup_meta.update([
('vrfs', {'xpath': 'ip-domain/vrfs', 'tag': True, 'operation': 'edit'}),
('vrf', {'xpath': 'ip-domain/vrfs/vrf', 'tag': True, 'operation': 'edit'}),
('a:vrf', {'xpath': 'ip-domain/vrfs/vrf/vrf-name', 'operation': 'edit'}),
('lookup', {'xpath': 'ip-domain/vrfs/vrf/lookup', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
])
state = self._module.params['state']
_get_filter = build_xml('ip-domain', opcode="filter")
running = get_config(self._module, source='running', config_filter=_get_filter)
_get_filter = build_xml('host-names', opcode="filter")
hostname_runn = get_config(self._module, source='running', config_filter=_get_filter)
hostname_ele = etree_find(hostname_runn, 'host-name')
hostname = hostname_ele.text if hostname_ele is not None else None
vrf_ele = etree_findall(running, 'vrf')
vrf_map = {}
for vrf in vrf_ele:
name_server_list = list()
domain_list = list()
vrf_name_ele = etree_find(vrf, 'vrf-name')
vrf_name = vrf_name_ele.text if vrf_name_ele is not None else None
domain_name_ele = etree_find(vrf, 'name')
domain_name = domain_name_ele.text if domain_name_ele is not None else None
domain_ele = etree_findall(vrf, 'list-name')
for domain in domain_ele:
domain_list.append(domain.text)
server_ele = etree_findall(vrf, 'server-address')
for server in server_ele:
name_server_list.append(server.text)
lookup_source_ele = etree_find(vrf, 'source-interface')
lookup_source = lookup_source_ele.text if lookup_source_ele is not None else None
lookup_enabled = False if etree_find(vrf, 'lookup') is not None else True
vrf_map[vrf_name] = {'domain_name': domain_name,
'domain_search': domain_list,
'name_servers': name_server_list,
'lookup_source': lookup_source,
'lookup_enabled': lookup_enabled}
opcode = None
hostname_param = {}
lookup_param = {}
system_param = {}
sys_server_params = list()
sys_domain_params = list()
add_domain_params = list()
del_domain_params = list()
add_server_params = list()
del_server_params = list()
lookup_source_params = {}
try:
sys_node = vrf_map[self._want['vrf']]
except KeyError:
sys_node = {'domain_name': None,
'domain_search': [],
'name_servers': [],
'lookup_source': None,
'lookup_enabled': True}
if state == 'absent':
opcode = "delete"
def needs_update(x):
return self._want[x] is not None and self._want[x] == sys_node[x]
if needs_update('domain_name'):
system_param = {'vrf': self._want['vrf'], 'domain_name': self._want['domain_name']}
if needs_update('hostname'):
hostname_param = {'hostname': hostname}
if not self._want['lookup_enabled'] and not sys_node['lookup_enabled']:
lookup_param['vrf'] = self._want['vrf']
if needs_update('lookup_source'):
lookup_source_params['vrf'] = self._want['vrf']
lookup_source_params['lookup_source'] = self._want['lookup_source']
if self._want['domain_search']:
domain_param = {}
domain_param['domain_name'] = self._want['domain_name']
domain_param['vrf'] = self._want['vrf']
domain_param['order'] = '0'
for domain in self._want['domain_search']:
if domain in sys_node['domain_search']:
domain_param['domain_search'] = domain
sys_domain_params.append(domain_param.copy())
if self._want['name_servers']:
server_param = {}
server_param['vrf'] = self._want['vrf']
server_param['order'] = '0'
for server in self._want['name_servers']:
if server in sys_node['name_servers']:
server_param['name_servers'] = server
sys_server_params.append(server_param.copy())
elif state == 'present':
opcode = "merge"
def needs_update(x):
return self._want[x] is not None and (sys_node[x] is None or
(sys_node[x] is not None and self._want[x] != sys_node[x]))
if needs_update('domain_name'):
system_param = {'vrf': self._want['vrf'], 'domain_name': self._want['domain_name']}
if self._want['hostname'] is not None and self._want['hostname'] != hostname:
hostname_param = {'hostname': self._want['hostname']}
if not self._want['lookup_enabled'] and sys_node['lookup_enabled']:
lookup_param['vrf'] = self._want['vrf']
if needs_update('lookup_source'):
lookup_source_params['vrf'] = self._want['vrf']
lookup_source_params['lookup_source'] = self._want['lookup_source']
if self._want['domain_search']:
domain_adds, domain_removes = diff_list(self._want['domain_search'], sys_node['domain_search'])
domain_param = {}
domain_param['domain_name'] = self._want['domain_name']
domain_param['vrf'] = self._want['vrf']
domain_param['order'] = '0'
for domain in domain_adds:
if domain not in sys_node['domain_search']:
domain_param['domain_search'] = domain
add_domain_params.append(domain_param.copy())
for domain in domain_removes:
if domain in sys_node['domain_search']:
domain_param['domain_search'] = domain
del_domain_params.append(domain_param.copy())
if self._want['name_servers']:
server_adds, server_removes = diff_list(self._want['name_servers'], sys_node['name_servers'])
server_param = {}
server_param['vrf'] = self._want['vrf']
server_param['order'] = '0'
for domain in server_adds:
if domain not in sys_node['name_servers']:
server_param['name_servers'] = domain
add_server_params.append(server_param.copy())
for domain in server_removes:
if domain in sys_node['name_servers']:
server_param['name_servers'] = domain
del_server_params.append(server_param.copy())
self._result['xml'] = []
_edit_filter_list = list()
if opcode:
if hostname_param:
_edit_filter_list.append(build_xml('host-names', xmap=self._hostname_meta,
params=hostname_param, opcode=opcode))
if system_param:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._system_meta,
params=system_param, opcode=opcode))
if lookup_source_params:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._lookup_source_meta,
params=lookup_source_params, opcode=opcode))
if lookup_param:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._lookup_meta,
params=lookup_param, opcode=opcode))
if opcode == 'delete':
if sys_domain_params:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._system_domain_meta,
params=sys_domain_params, opcode=opcode))
if sys_server_params:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._system_server_meta,
params=sys_server_params, opcode=opcode))
if self._want['vrf'] != 'default':
self._result['warnings'] = ["name-servers delete operation with non-default vrf is a success, "
"but with rpc-error. Recommended to use 'ignore_errors' option with the task as a workaround"]
elif opcode == 'merge':
if add_domain_params:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._system_domain_meta,
params=add_domain_params, opcode=opcode))
if del_domain_params:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._system_domain_meta,
params=del_domain_params, opcode="delete"))
if add_server_params:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._system_server_meta,
params=add_server_params, opcode=opcode))
if del_server_params:
_edit_filter_list.append(build_xml('ip-domain', xmap=self._system_server_meta,
params=del_server_params, opcode="delete"))
diff = None
if _edit_filter_list:
commit = not self._module.check_mode
diff = load_config(self._module, _edit_filter_list, commit=commit, running=running,
nc_get_filter=_get_filter)
if diff:
if self._module._diff:
self._result['diff'] = dict(prepared=diff)
self._result['xml'] = _edit_filter_list
self._result['changed'] = True
def run(self):
self.map_params_to_obj()
self.map_obj_to_xml_rpc()
return self._result
def main():
""" Main entry point for Ansible module execution
"""
argument_spec = dict(
hostname=dict(),
vrf=dict(type='str', default='default'),
domain_name=dict(),
domain_search=dict(type='list'),
name_servers=dict(type='list'),
lookup_source=dict(),
lookup_enabled=dict(type='bool', default=True),
state=dict(choices=['present', 'absent'], default='present')
)
argument_spec.update(iosxr_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
config_object = None
if is_cliconf(module):
# Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
# module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
# version='2.9')
config_object = CliConfiguration(module)
elif is_netconf(module):
config_object = NCConfiguration(module)
result = None
if config_object:
result = config_object.run()
module.exit_json(**result)
if __name__ == "__main__":
main()

@ -1,721 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: iosxr_user
version_added: "2.4"
author:
- "Trishna Guha (@trishnaguha)"
- "Sebastiaan van Doesselaar (@sebasdoes)"
- "Kedar Kekan (@kedarX)"
short_description: Manage the aggregate of local users on Cisco IOS XR device
description:
- This module provides declarative management of the local usernames
configured on network devices. It allows playbooks to manage
either individual usernames or the aggregate of usernames in the
current running config. It also supports purging usernames from the
configuration that are not explicitly defined.
extends_documentation_fragment: iosxr
notes:
- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
- Tested against IOS XRv 6.1.3
options:
aggregate:
description:
- The set of username objects to be configured on the remote
Cisco IOS XR device. The list entries can either be the username
or a hash of username and properties. This argument is mutually
exclusive with the C(name) argument.
aliases: ['users', 'collection']
name:
description:
- The username to be configured on the Cisco IOS XR device.
This argument accepts a string value and is mutually exclusive
with the C(aggregate) argument.
Please note that this option is not same as C(provider username).
configured_password:
description:
- The password to be configured on the Cisco IOS XR device. The password
needs to be provided in clear text. Password is encrypted on the device
when used with I(cli) and by Ansible when used with I(netconf)
using the same MD5 hash technique with salt size of 3.
Please note that this option is not same as C(provider password).
update_password:
description:
- Since passwords are encrypted in the device running config, this
argument will instruct the module when to change the password. When
set to C(always), the password will always be updated in the device
and when set to C(on_create) the password will be updated only if
the username is created.
default: always
choices: ['on_create', 'always']
group:
description:
- Configures the group for the username in the
device running configuration. The argument accepts a string value
defining the group name. This argument does not check if the group
has been configured on the device.
aliases: ['role']
groups:
version_added: "2.5"
description:
- Configures the groups for the username in the device running
configuration. The argument accepts a list of group names.
This argument does not check if the group has been configured
on the device. It is similar to the aggregate command for
usernames, but lets you configure multiple groups for the user(s).
purge:
description:
- Instructs the module to consider the
resource definition absolute. It will remove any previously
configured usernames on the device with the exception of the
`admin` user and the current defined set of users.
type: bool
default: false
admin:
description:
- Enters into administration configuration mode for making config
changes to the device.
- Applicable only when using network_cli transport
type: bool
default: false
version_added: "2.8"
state:
description:
- Configures the state of the username definition
as it relates to the device operational configuration. When set
to I(present), the username(s) should be configured in the device active
configuration and when set to I(absent) the username(s) should not be
in the device active configuration
default: present
choices: ['present', 'absent']
public_key:
version_added: "2.5"
description:
- Configures the contents of the public keyfile to upload to the IOS-XR node.
This enables users to login using the accompanying private key. IOS-XR
only accepts base64 decoded files, so this will be decoded and uploaded
to the node. Do note that this requires an OpenSSL public key file,
PuTTy generated files will not work! Mutually exclusive with
public_key_contents. If used with multiple users in aggregates, then the
same key file is used for all users.
public_key_contents:
version_added: "2.5"
description:
- Configures the contents of the public keyfile to upload to the IOS-XR node.
This enables users to login using the accompanying private key. IOS-XR
only accepts base64 decoded files, so this will be decoded and uploaded
to the node. Do note that this requires an OpenSSL public key file,
PuTTy generated files will not work! Mutually exclusive with
public_key.If used with multiple users in aggregates, then the
same key file is used for all users.
requirements:
- ncclient >= 0.5.3 when using netconf
- lxml >= 4.1.1 when using netconf
- base64 when using I(public_key_contents) or I(public_key)
- paramiko when using I(public_key_contents) or I(public_key)
"""
EXAMPLES = """
- name: create a new user
iosxr_user:
name: ansible
configured_password: mypassword
state: present
- name: create a new user in admin configuration mode
iosxr_user:
name: ansible
configured_password: mypassword
admin: True
state: present
- name: remove all users except admin
iosxr_user:
purge: True
- name: set multiple users to group sys-admin
iosxr_user:
aggregate:
- name: netop
- name: netend
group: sysadmin
state: present
- name: set multiple users to multiple groups
iosxr_user:
aggregate:
- name: netop
- name: netend
groups:
- sysadmin
- root-system
state: present
- name: Change Password for User netop
iosxr_user:
name: netop
configured_password: "{{ new_password }}"
update_password: always
state: present
- name: Add private key authentication for user netop
iosxr_user:
name: netop
state: present
public_key_contents: "{{ lookup('file', '/home/netop/.ssh/id_rsa.pub' }}"
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- username ansible secret password group sysadmin
- username admin secret admin
xml:
description: NetConf rpc xml sent to device with transport C(netconf)
returned: always (empty list when no xml rpc to send)
type: list
version_added: 2.5
sample:
- '<config xmlns:xc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">
<aaa xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-lib-cfg\">
<usernames xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XR-aaa-locald-cfg\">
<username xc:operation=\"merge\">
<name>test7</name>
<usergroup-under-usernames>
<usergroup-under-username>
<name>sysadmin</name>
</usergroup-under-username>
</usergroup-under-usernames>
<secret>$1$ZsXC$zZ50wqhDC543ZWQkkAHLW0</secret>
</username>
</usernames>
</aaa>
</config>'
"""
import os
from functools import partial
from copy import deepcopy
import collections
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.compat.paramiko import paramiko
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config, is_netconf, is_cliconf
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, build_xml, etree_findall
try:
from base64 import b64decode
HAS_B64 = True
except ImportError:
HAS_B64 = False
class PublicKeyManager(object):
def __init__(self, module, result):
self._module = module
self._result = result
def convert_key_to_base64(self):
""" IOS-XR only accepts base64 decoded files, this converts the public key to a temp file.
"""
if self._module.params['aggregate']:
name = 'aggregate'
else:
name = self._module.params['name']
if self._module.params['public_key_contents']:
key = self._module.params['public_key_contents']
elif self._module.params['public_key']:
readfile = open(self._module.params['public_key'], 'r')
key = readfile.read()
splitfile = key.split()[1]
base64key = b64decode(splitfile)
base64file = open('/tmp/publickey_%s.b64' % (name), 'wb')
base64file.write(base64key)
base64file.close()
return '/tmp/publickey_%s.b64' % (name)
def copy_key_to_node(self, base64keyfile):
""" Copy key to IOS-XR node. We use SFTP because older IOS-XR versions don't handle SCP very well.
"""
provider = self._module.params.get("provider") or {}
node = provider.get('host')
if node is None:
return False
user = provider.get('username')
if user is None:
return False
password = provider.get('password')
ssh_keyfile = provider.get('ssh_keyfile')
if self._module.params['aggregate']:
name = 'aggregate'
else:
name = self._module.params['name']
src = base64keyfile
dst = '/harddisk:/publickey_%s.b64' % (name)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if not ssh_keyfile:
ssh.connect(node, username=user, password=password)
else:
ssh.connect(node, username=user, allow_agent=True)
sftp = ssh.open_sftp()
sftp.put(src, dst)
sftp.close()
ssh.close()
def addremovekey(self, command):
""" Add or remove key based on command
"""
provider = self._module.params.get("provider") or {}
node = provider.get('host')
if node is None:
return False
user = provider.get('username')
if user is None:
return False
password = provider.get('password')
ssh_keyfile = provider.get('ssh_keyfile')
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if not ssh_keyfile:
ssh.connect(node, username=user, password=password)
else:
ssh.connect(node, username=user, allow_agent=True)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command('%s \r' % (command))
readmsg = ssh_stdout.read(100) # We need to read a bit to actually apply for some reason
if ('already' in readmsg) or ('removed' in readmsg) or ('really' in readmsg):
ssh_stdin.write('yes\r')
ssh_stdout.read(1) # We need to read a bit to actually apply for some reason
ssh.close()
return readmsg
def run(self):
if self._module.params['state'] == 'present':
if not self._module.check_mode:
key = self.convert_key_to_base64()
copykeys = self.copy_key_to_node(key)
if copykeys is False:
self._result['warnings'].append('Please set up your provider before running this playbook')
if self._module.params['aggregate']:
for user in self._module.params['aggregate']:
cmdtodo = "admin crypto key import authentication rsa username %s harddisk:/publickey_aggregate.b64" % (user)
addremove = self.addremovekey(cmdtodo)
if addremove is False:
self._result['warnings'].append('Please set up your provider before running this playbook')
else:
cmdtodo = "admin crypto key import authentication rsa username %s harddisk:/publickey_%s.b64" % \
(self._module.params['name'], self._module.params['name'])
addremove = self.addremovekey(cmdtodo)
if addremove is False:
self._result['warnings'].append('Please set up your provider before running this playbook')
elif self._module.params['state'] == 'absent':
if not self._module.check_mode:
if self._module.params['aggregate']:
for user in self._module.params['aggregate']:
cmdtodo = "admin crypto key zeroize authentication rsa username %s" % (user)
addremove = self.addremovekey(cmdtodo)
if addremove is False:
self._result['warnings'].append('Please set up your provider before running this playbook')
else:
cmdtodo = "admin crypto key zeroize authentication rsa username %s" % (self._module.params['name'])
addremove = self.addremovekey(cmdtodo)
if addremove is False:
self._result['warnings'].append('Please set up your provider before running this playbook')
elif self._module.params['purge'] is True:
if not self._module.check_mode:
cmdtodo = "admin crypto key zeroize authentication rsa all"
addremove = self.addremovekey(cmdtodo)
if addremove is False:
self._result['warnings'].append('Please set up your provider before running this playbook')
return self._result
def search_obj_in_list(name, lst):
for o in lst:
if o['name'] == name:
return o
return None
class ConfigBase(object):
def __init__(self, module, result, flag=None):
self._module = module
self._result = result
self._want = list()
self._have = list()
def get_param_value(self, key, item):
# if key doesn't exist in the item, get it from module.params
if not item.get(key):
value = self._module.params[key]
# if key does exist, do a type check on it to validate it
else:
value_type = self._module.argument_spec[key].get('type', 'str')
type_checker = self._module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type]
type_checker(item[key])
value = item[key]
# validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key)
if all((value, validator)):
validator(value, self._module)
return value
def map_params_to_obj(self):
users = self._module.params['aggregate']
aggregate = list()
if not users:
if not self._module.params['name'] and self._module.params['purge']:
pass
elif not self._module.params['name']:
self._module.fail_json(msg='username is required')
else:
aggregate = [{'name': self._module.params['name']}]
else:
for item in users:
if not isinstance(item, dict):
aggregate.append({'name': item})
elif 'name' not in item:
self._module.fail_json(msg='name is required')
else:
aggregate.append(item)
for item in aggregate:
get_value = partial(self.get_param_value, item=item)
item['configured_password'] = get_value('configured_password')
item['group'] = get_value('group')
item['groups'] = get_value('groups')
item['state'] = get_value('state')
self._want.append(item)
class CliConfiguration(ConfigBase):
def __init__(self, module, result):
super(CliConfiguration, self).__init__(module, result)
def map_config_to_obj(self):
data = get_config(self._module, config_filter='username')
users = data.strip().rstrip('!').split('!')
for user in users:
user_config = user.strip().splitlines()
name = user_config[0].strip().split()[1]
group = None
if len(user_config) > 1:
group_or_secret = user_config[1].strip().split()
if group_or_secret[0] == 'group':
group = group_or_secret[1]
obj = {
'name': name,
'state': 'present',
'configured_password': None,
'group': group
}
self._have.append(obj)
def map_obj_to_commands(self):
commands = list()
for w in self._want:
name = w['name']
state = w['state']
obj_in_have = search_obj_in_list(name, self._have)
if state == 'absent' and obj_in_have:
commands.append('no username ' + name)
elif state == 'present' and not obj_in_have:
user_cmd = 'username ' + name
commands.append(user_cmd)
if w['configured_password']:
commands.append(user_cmd + ' secret ' + w['configured_password'])
if w['group']:
commands.append(user_cmd + ' group ' + w['group'])
elif w['groups']:
for group in w['groups']:
commands.append(user_cmd + ' group ' + group)
elif state == 'present' and obj_in_have:
user_cmd = 'username ' + name
if self._module.params['update_password'] == 'always' and w['configured_password']:
commands.append(user_cmd + ' secret ' + w['configured_password'])
if w['group'] and w['group'] != obj_in_have['group']:
commands.append(user_cmd + ' group ' + w['group'])
elif w['groups']:
for group in w['groups']:
commands.append(user_cmd + ' group ' + group)
if self._module.params['purge']:
want_users = [x['name'] for x in self._want]
have_users = [x['name'] for x in self._have]
for item in set(have_users).difference(set(want_users)):
if item != 'admin':
commands.append('no username %s' % item)
if 'no username admin' in commands:
self._module.fail_json(msg='cannot delete the `admin` account')
self._result['commands'] = []
if commands:
commit = not self._module.check_mode
admin = self._module.params['admin']
diff = load_config(self._module, commands, commit=commit, admin=admin)
if diff:
self._result['diff'] = dict(prepared=diff)
self._result['commands'] = commands
self._result['changed'] = True
def run(self):
self.map_params_to_obj()
self.map_config_to_obj()
self.map_obj_to_commands()
return self._result
class NCConfiguration(ConfigBase):
def __init__(self, module, result):
super(NCConfiguration, self).__init__(module, result)
self._locald_meta = collections.OrderedDict()
self._locald_group_meta = collections.OrderedDict()
def generate_md5_hash(self, arg):
'''
Generate MD5 hash with randomly generated salt size of 3.
:param arg:
:return passwd:
'''
cmd = "openssl passwd -salt `openssl rand -base64 3` -1 "
return os.popen(cmd + arg).readlines()[0].strip()
def map_obj_to_xml_rpc(self):
self._locald_meta.update([
('aaa_locald', {'xpath': 'aaa/usernames', 'tag': True, 'ns': True}),
('username', {'xpath': 'aaa/usernames/username', 'tag': True, 'attrib': "operation"}),
('a:name', {'xpath': 'aaa/usernames/username/name'}),
('a:configured_password', {'xpath': 'aaa/usernames/username/secret', 'operation': 'edit'}),
])
self._locald_group_meta.update([
('aaa_locald', {'xpath': 'aaa/usernames', 'tag': True, 'ns': True}),
('username', {'xpath': 'aaa/usernames/username', 'tag': True, 'attrib': "operation"}),
('a:name', {'xpath': 'aaa/usernames/username/name'}),
('usergroups', {'xpath': 'aaa/usernames/username/usergroup-under-usernames', 'tag': True, 'operation': 'edit'}),
('usergroup', {'xpath': 'aaa/usernames/username/usergroup-under-usernames/usergroup-under-username', 'tag': True, 'operation': 'edit'}),
('a:group', {'xpath': 'aaa/usernames/username/usergroup-under-usernames/usergroup-under-username/name', 'operation': 'edit'}),
])
state = self._module.params['state']
_get_filter = build_xml('aaa', opcode="filter")
running = get_config(self._module, source='running', config_filter=_get_filter)
elements = etree_findall(running, 'username')
users = list()
for element in elements:
name_list = etree_findall(element, 'name')
users.append(name_list[0].text)
list_size = len(name_list)
if list_size == 1:
self._have.append({'name': name_list[0].text, 'group': None, 'groups': None})
elif list_size == 2:
self._have.append({'name': name_list[0].text, 'group': name_list[1].text, 'groups': None})
elif list_size > 2:
name_iter = iter(name_list)
next(name_iter)
tmp_list = list()
for name in name_iter:
tmp_list.append(name.text)
self._have.append({'name': name_list[0].text, 'group': None, 'groups': tmp_list})
locald_params = list()
locald_group_params = list()
opcode = None
if state == 'absent':
opcode = "delete"
for want_item in self._want:
if want_item['name'] in users:
want_item['configured_password'] = None
locald_params.append(want_item)
elif state == 'present':
opcode = "merge"
for want_item in self._want:
if want_item['name'] not in users:
want_item['configured_password'] = self.generate_md5_hash(want_item['configured_password'])
locald_params.append(want_item)
if want_item['group'] is not None:
locald_group_params.append(want_item)
if want_item['groups'] is not None:
for group in want_item['groups']:
want_item['group'] = group
locald_group_params.append(want_item.copy())
else:
if self._module.params['update_password'] == 'always' and want_item['configured_password'] is not None:
want_item['configured_password'] = self.generate_md5_hash(want_item['configured_password'])
locald_params.append(want_item)
else:
want_item['configured_password'] = None
obj_in_have = search_obj_in_list(want_item['name'], self._have)
if want_item['group'] is not None and want_item['group'] != obj_in_have['group']:
locald_group_params.append(want_item)
elif want_item['groups'] is not None:
for group in want_item['groups']:
want_item['group'] = group
locald_group_params.append(want_item.copy())
purge_params = list()
if self._module.params['purge']:
want_users = [x['name'] for x in self._want]
have_users = [x['name'] for x in self._have]
for item in set(have_users).difference(set(want_users)):
if item != 'admin':
purge_params.append({'name': item})
self._result['xml'] = []
_edit_filter_list = list()
if opcode is not None:
if locald_params:
_edit_filter_list.append(build_xml('aaa', xmap=self._locald_meta,
params=locald_params, opcode=opcode))
if locald_group_params:
_edit_filter_list.append(build_xml('aaa', xmap=self._locald_group_meta,
params=locald_group_params, opcode=opcode))
if purge_params:
_edit_filter_list.append(build_xml('aaa', xmap=self._locald_meta,
params=purge_params, opcode="delete"))
diff = None
if _edit_filter_list:
commit = not self._module.check_mode
diff = load_config(self._module, _edit_filter_list, commit=commit, running=running,
nc_get_filter=_get_filter)
if diff:
if self._module._diff:
self._result['diff'] = dict(prepared=diff)
self._result['xml'] = _edit_filter_list
self._result['changed'] = True
def run(self):
self.map_params_to_obj()
self.map_obj_to_xml_rpc()
return self._result
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
configured_password=dict(no_log=True),
update_password=dict(default='always', choices=['on_create', 'always']),
admin=dict(type='bool', default=False),
public_key=dict(),
public_key_contents=dict(),
group=dict(aliases=['role']),
groups=dict(type='list', elements='dict'),
state=dict(default='present', choices=['present', 'absent'])
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec['name'] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
mutually_exclusive = [('name', 'aggregate'), ('public_key', 'public_key_contents'), ('group', 'groups')]
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec, aliases=['users', 'collection'],
mutually_exclusive=mutually_exclusive),
purge=dict(type='bool', default=False)
)
argument_spec.update(element_spec)
argument_spec.update(iosxr_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
if (module.params['public_key_contents'] or module.params['public_key']):
if not HAS_B64:
module.fail_json(
msg='library base64 is required but does not appear to be '
'installed. It can be installed using `pip install base64`'
)
if paramiko is None:
module.fail_json(
msg='library paramiko is required but does not appear to be '
'installed. It can be installed using `pip install paramiko`'
)
result = {'changed': False, 'warnings': []}
config_object = None
if is_cliconf(module):
# Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
# module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
# version='2.9')
config_object = CliConfiguration(module, result)
elif is_netconf(module):
config_object = NCConfiguration(module, result)
if config_object:
result = config_object.run()
if module.params['public_key_contents'] or module.params['public_key']:
pubkey_object = PublicKeyManager(module, result)
result = pubkey_object.run()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,106 +0,0 @@
#
# (c) 2016 Red Hat Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import sys
import copy
from ansible.module_utils.network.iosxr.iosxr import iosxr_provider_spec
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
from ansible.module_utils.network.common.utils import load_provider
from ansible.utils.display import Display
display = Display()
class ActionModule(ActionNetworkModule):
def run(self, tmp=None, task_vars=None):
del tmp # tmp no longer has any effect
module_name = self._task.action.split('.')[-1]
self._config_module = True if module_name == 'iosxr_config' else False
force_cli = module_name in ('iosxr_netconf', 'iosxr_config', 'iosxr_command', 'iosxr_facts')
persistent_connection = self._play_context.connection.split('.')[-1]
warnings = []
if self._play_context.connection == 'local':
provider = load_provider(iosxr_provider_spec, self._task.args)
pc = copy.deepcopy(self._play_context)
pc.network_os = 'cisco.iosxr.iosxr'
if force_cli or provider['transport'] == 'cli':
pc.connection = 'ansible.netcommon.network_cli'
pc.port = int(provider['port'] or self._play_context.port or 22)
elif provider['transport'] == 'netconf':
pc.connection = 'ansible.netcommon.netconf'
pc.port = int(provider['port'] or self._play_context.port or 830)
else:
return {'failed': True, 'msg': 'Transport type %s is not valid for this module' % provider['transport']}
pc.remote_addr = provider['host'] or self._play_context.remote_addr
pc.port = int(provider['port'] or self._play_context.port or 22)
pc.remote_user = provider['username'] or self._play_context.connection_user
pc.password = provider['password'] or self._play_context.password
connection = self._shared_loader_obj.connection_loader.get('ansible.netcommon.persistent', pc, sys.stdin,
task_uuid=self._task._uuid)
# TODO: Remove below code after ansible minimal is cut out
if connection is None:
pc.network_os = 'iosxr'
if pc.connection.split('.')[-1] == 'netconf':
pc.connection = 'netconf'
else:
pc.connection = 'network_cli'
connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid)
display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr)
command_timeout = int(provider['timeout']) if provider['timeout'] else connection.get_option('persistent_command_timeout')
connection.set_options(direct={'persistent_command_timeout': command_timeout})
socket_path = connection.run()
display.vvvv('socket_path: %s' % socket_path, pc.remote_addr)
if not socket_path:
return {'failed': True,
'msg': 'unable to open shell. Please see: ' +
'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell'}
task_vars['ansible_socket'] = socket_path
warnings.append(['connection local support for this module is deprecated and will be removed in version 2.14, use connection %s' % pc.connection])
elif persistent_connection in ('netconf', 'network_cli'):
if force_cli and persistent_connection != 'network_cli':
return {'failed': True, 'msg': 'Connection type %s is not valid for module %s' %
(self._play_context.connection, module_name)}
provider = self._task.args.get('provider', {})
if any(provider.values()):
display.warning('provider is unnecessary when using {0} and will be ignored'.format(self._play_context.connection))
del self._task.args['provider']
else:
return {'failed': True, 'msg': 'Connection type %s is not valid for this module' % self._play_context.connection}
result = super(ActionModule, self).run(task_vars=task_vars)
if warnings:
if 'warnings' in result:
result['warnings'].extend(warnings)
else:
result['warnings'] = warnings
return result

@ -1,276 +0,0 @@
#
# (c) 2017 Red Hat Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = """
---
author: Ansible Networking Team
cliconf: iosxr
short_description: Use iosxr cliconf to run command on Cisco IOS XR platform
description:
- This iosxr plugin provides low level abstraction apis for
sending and receiving CLI commands from Cisco IOS XR network devices.
version_added: "2.4"
"""
import re
import json
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_text
from ansible.module_utils.common._collections_compat import Mapping
from ansible.module_utils.connection import ConnectionError
from ansible.module_utils.network.common.config import NetworkConfig, dumps
from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.network.iosxr.iosxr import sanitize_config, mask_config_blocks_from_diff
from ansible.plugins.cliconf import CliconfBase
class Cliconf(CliconfBase):
def get_device_info(self):
device_info = {}
device_info['network_os'] = 'iosxr'
reply = self.get('show version | utility head -n 20')
data = to_text(reply, errors='surrogate_or_strict').strip()
match = re.search(r'Version (\S+)$', data, re.M)
if match:
device_info['network_os_version'] = match.group(1)
match = re.search(r'image file is "(.+)"', data)
if match:
device_info['network_os_image'] = match.group(1)
model_search_strs = [r'^[Cc]isco (.+) \(revision', r'^[Cc]isco (\S+ \S+).+bytes of .*memory']
for item in model_search_strs:
match = re.search(item, data, re.M)
if match:
device_info['network_os_model'] = match.group(1)
break
match = re.search(r'^(.+) uptime', data, re.M)
if match:
device_info['network_os_hostname'] = match.group(1)
return device_info
def configure(self, admin=False, exclusive=False):
prompt = to_text(self._connection.get_prompt(), errors='surrogate_or_strict').strip()
if not prompt.endswith(')#'):
if admin and 'admin-' not in prompt:
self.send_command('admin')
if exclusive:
self.send_command('configure exclusive')
return
self.send_command('configure terminal')
def abort(self, admin=False):
prompt = to_text(self._connection.get_prompt(), errors='surrogate_or_strict').strip()
if prompt.endswith(')#'):
self.send_command('abort')
if admin and 'admin-' in prompt:
self.send_command('exit')
def get_config(self, source='running', format='text', flags=None):
if source not in ['running']:
raise ValueError("fetching configuration from %s is not supported" % source)
lookup = {'running': 'running-config'}
cmd = 'show {0} '.format(lookup[source])
cmd += ' '.join(to_list(flags))
cmd = cmd.strip()
return self.send_command(cmd)
def edit_config(self, candidate=None, commit=True, admin=False, exclusive=False, replace=None, comment=None, label=None):
operations = self.get_device_operations()
self.check_edit_config_capability(operations, candidate, commit, replace, comment)
resp = {}
results = []
requests = []
self.configure(admin=admin, exclusive=exclusive)
if replace:
candidate = 'load {0}'.format(replace)
for line in to_list(candidate):
if not isinstance(line, Mapping):
line = {'command': line}
cmd = line['command']
results.append(self.send_command(**line))
requests.append(cmd)
# Before any commit happend, we can get a real configuration
# diff from the device and make it available by the iosxr_config module.
# This information can be usefull either in check mode or normal mode.
resp['show_commit_config_diff'] = self.get('show commit changes diff')
if commit:
self.commit(comment=comment, label=label, replace=replace)
else:
self.discard_changes()
self.abort(admin=admin)
resp['request'] = requests
resp['response'] = results
return resp
def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'):
diff = {}
device_operations = self.get_device_operations()
option_values = self.get_option_values()
if candidate is None and device_operations['supports_generate_diff']:
raise ValueError("candidate configuration is required to generate diff")
if diff_match not in option_values['diff_match']:
raise ValueError("'match' value %s in invalid, valid values are %s" % (diff_match, ', '.join(option_values['diff_match'])))
if diff_replace not in option_values['diff_replace']:
raise ValueError("'replace' value %s in invalid, valid values are %s" % (diff_replace, ', '.join(option_values['diff_replace'])))
# prepare candidate configuration
sanitized_candidate = sanitize_config(candidate)
candidate_obj = NetworkConfig(indent=1)
candidate_obj.load(sanitized_candidate)
if running and diff_match != 'none':
# running configuration
running = mask_config_blocks_from_diff(running, candidate, "ansible")
running = sanitize_config(running)
running_obj = NetworkConfig(indent=1, contents=running, ignore_lines=diff_ignore_lines)
configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace)
else:
configdiffobjs = candidate_obj.items
diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else ''
return diff
def get(self, command=None, prompt=None, answer=None, sendonly=False, newline=True, output=None, check_all=False):
if output:
raise ValueError("'output' value %s is not supported for get" % output)
return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all)
def commit(self, comment=None, label=None, replace=None):
cmd_obj = {}
if replace:
cmd_obj['command'] = 'commit replace'
cmd_obj['prompt'] = 'This commit will replace or remove the entire running configuration'
cmd_obj['answer'] = 'yes'
else:
if comment and label:
cmd_obj['command'] = 'commit label {0} comment {1}'.format(label, comment)
elif comment:
cmd_obj['command'] = 'commit comment {0}'.format(comment)
elif label:
cmd_obj['command'] = 'commit label {0}'.format(label)
else:
cmd_obj['command'] = 'commit show-error'
# In some cases even a normal commit, i.e., !replace,
# throws a prompt and we need to handle it before
# proceeding further
cmd_obj['prompt'] = '(C|c)onfirm'
cmd_obj['answer'] = 'y'
self.send_command(**cmd_obj)
def run_commands(self, commands=None, check_rc=True):
if commands is None:
raise ValueError("'commands' value is required")
responses = list()
for cmd in to_list(commands):
if not isinstance(cmd, Mapping):
cmd = {'command': cmd}
output = cmd.pop('output', None)
if output:
raise ValueError("'output' value %s is not supported for run_commands" % output)
try:
out = self.send_command(**cmd)
except AnsibleConnectionFailure as e:
if check_rc:
raise
out = getattr(e, 'err', e)
if out is not None:
try:
out = to_text(out, errors='surrogate_or_strict').strip()
except UnicodeError:
raise ConnectionError(message=u'Failed to decode output from %s: %s' % (cmd, to_text(out)))
try:
out = json.loads(out)
except ValueError:
pass
responses.append(out)
return responses
def discard_changes(self):
self.send_command('abort')
def get_device_operations(self):
return {
'supports_diff_replace': True,
'supports_commit': True,
'supports_rollback': False,
'supports_defaults': False,
'supports_onbox_diff': False,
'supports_commit_comment': True,
'supports_multiline_delimiter': False,
'supports_diff_match': True,
'supports_diff_ignore_lines': True,
'supports_generate_diff': True,
'supports_replace': True,
'supports_admin': True,
'supports_commit_label': True
}
def get_option_values(self):
return {
'format': ['text'],
'diff_match': ['line', 'strict', 'exact', 'none'],
'diff_replace': ['line', 'block', 'config'],
'output': []
}
def get_capabilities(self):
result = super(Cliconf, self).get_capabilities()
result['rpc'] += ['commit', 'discard_changes', 'get_diff', 'configure', 'exit']
result['device_operations'] = self.get_device_operations()
result.update(self.get_option_values())
return json.dumps(result)
def set_cli_prompt_context(self):
"""
Make sure we are in the operational cli mode
:return: None
"""
if self._connection.connected:
self._update_cli_prompt_context(config_context=')#', exit_command='abort')

@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = r'''
options:
provider:
description:
- B(Deprecated)
- "Starting with Ansible 2.5 we recommend using C(connection: network_cli)."
- For more information please see the L(Network Guide, ../network/getting_started/network_differences.html#multiple-communication-protocols).
- HORIZONTALLINE
- A dict object containing connection details.
type: dict
suboptions:
host:
description:
- Specifies the DNS host name or address for connecting to the remote
device over the specified transport. The value of host is used as
the destination address for the transport.
type: str
required: true
port:
description:
- Specifies the port to use when building the connection to the remote device.
type: int
default: 22
username:
description:
- Configures the username to use to authenticate the connection to
the remote device. This value is used to authenticate
the SSH session. If the value is not specified in the task, the
value of environment variable C(ANSIBLE_NET_USERNAME) will be used instead.
type: str
password:
description:
- Specifies the password to use to authenticate the connection to
the remote device. This value is used to authenticate
the SSH session. If the value is not specified in the task, the
value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead.
type: str
timeout:
description:
- Specifies the timeout in seconds for communicating with the network device
for either connecting or sending commands. If the timeout is
exceeded before the operation is completed, the module will error.
type: int
default: 10
ssh_keyfile:
description:
- Specifies the SSH key to use to authenticate the connection to
the remote device. This value is the path to the
key used to authenticate the SSH session. If the value is not specified
in the task, the value of environment variable C(ANSIBLE_NET_SSH_KEYFILE)
will be used instead.
type: path
notes:
- For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide <network_guide>`
- For more information on using Ansible to manage Cisco devices see the `Cisco integration page <https://www.ansible.com/integrations/networks/cisco>`_.
'''

@ -1,214 +0,0 @@
#
# (c) 2017 Red Hat Inc.
# (c) 2017 Kedar Kekan (kkekan@redhat.com)
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = """
---
author: Ansible Networking Team
netconf: iosxr
short_description: Use iosxr netconf plugin to run netconf commands on Cisco IOSXR platform
description:
- This iosxr plugin provides low level abstraction apis for
sending and receiving netconf commands from Cisco iosxr network devices.
version_added: "2.9"
options:
ncclient_device_handler:
type: str
default: iosxr
description:
- Specifies the ncclient device handler name for Cisco iosxr network os. To
identify the ncclient device handler name refer ncclient library documentation.
"""
import json
import re
import collections
from ansible.module_utils._text import to_native
from ansible.module_utils.network.common.netconf import remove_namespaces
from ansible.module_utils.network.iosxr.iosxr import build_xml, etree_find
from ansible.errors import AnsibleConnectionFailure
from ansible.plugins.netconf import NetconfBase, ensure_ncclient
try:
from ncclient import manager
from ncclient.operations import RPCError
from ncclient.transport.errors import SSHUnknownHostError
from ncclient.xml_ import to_xml
HAS_NCCLIENT = True
except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError
HAS_NCCLIENT = False
class Netconf(NetconfBase):
def get_device_info(self):
device_info = {}
device_info['network_os'] = 'iosxr'
install_meta = collections.OrderedDict()
install_meta.update([
('boot-variables', {'xpath': 'install/boot-variables', 'tag': True}),
('boot-variable', {'xpath': 'install/boot-variables/boot-variable', 'tag': True, 'lead': True}),
('software', {'xpath': 'install/software', 'tag': True}),
('alias-devices', {'xpath': 'install/software/alias-devices', 'tag': True}),
('alias-device', {'xpath': 'install/software/alias-devices/alias-device', 'tag': True}),
('m:device-name', {'xpath': 'install/software/alias-devices/alias-device/device-name', 'value': 'disk0:'}),
])
install_filter = build_xml('install', install_meta, opcode='filter')
try:
reply = self.get(install_filter)
resp = remove_namespaces(re.sub(r'<\?xml version="1.0" encoding="UTF-8"\?>', '', reply))
ele_boot_variable = etree_find(resp, 'boot-variable/boot-variable')
if ele_boot_variable is not None:
device_info['network_os_image'] = re.split('[:|,]', ele_boot_variable.text)[1]
ele_package_name = etree_find(reply, 'package-name')
if ele_package_name is not None:
device_info['network_os_package'] = ele_package_name.text
device_info['network_os_version'] = re.split('-', ele_package_name.text)[-1]
hostname_filter = build_xml('host-names', opcode='filter')
reply = self.get(hostname_filter)
resp = remove_namespaces(re.sub(r'<\?xml version="1.0" encoding="UTF-8"\?>', '', reply))
hostname_ele = etree_find(resp.strip(), 'host-name')
device_info['network_os_hostname'] = hostname_ele.text if hostname_ele is not None else None
except Exception as exc:
self._connection.queue_message('vvvv', 'Fail to retrieve device info %s' % exc)
return device_info
def get_capabilities(self):
result = dict()
result['rpc'] = self.get_base_rpc()
result['network_api'] = 'netconf'
result['device_info'] = self.get_device_info()
result['server_capabilities'] = [c for c in self.m.server_capabilities]
result['client_capabilities'] = [c for c in self.m.client_capabilities]
result['session_id'] = self.m.session_id
result['device_operations'] = self.get_device_operations(result['server_capabilities'])
return json.dumps(result)
@staticmethod
@ensure_ncclient
def guess_network_os(obj):
"""
Guess the remote network os name
:param obj: Netconf connection class object
:return: Network OS name
"""
try:
m = manager.connect(
host=obj._play_context.remote_addr,
port=obj._play_context.port or 830,
username=obj._play_context.remote_user,
password=obj._play_context.password,
key_filename=obj.key_filename,
hostkey_verify=obj.get_option('host_key_checking'),
look_for_keys=obj.get_option('look_for_keys'),
allow_agent=obj._play_context.allow_agent,
timeout=obj.get_option('persistent_connect_timeout'),
# We need to pass in the path to the ssh_config file when guessing
# the network_os so that a jumphost is correctly used if defined
ssh_config=obj._ssh_config
)
except SSHUnknownHostError as exc:
raise AnsibleConnectionFailure(to_native(exc))
guessed_os = None
for c in m.server_capabilities:
if re.search('IOS-XR', c):
guessed_os = 'iosxr'
break
m.close_session()
return guessed_os
# TODO: change .xml to .data_xml, when ncclient supports data_xml on all platforms
def get(self, filter=None, remove_ns=False):
if isinstance(filter, list):
filter = tuple(filter)
try:
resp = self.m.get(filter=filter)
if remove_ns:
response = remove_namespaces(resp)
else:
response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml
return response
except RPCError as exc:
raise Exception(to_xml(exc.xml))
def get_config(self, source=None, filter=None, remove_ns=False):
if isinstance(filter, list):
filter = tuple(filter)
try:
resp = self.m.get_config(source=source, filter=filter)
if remove_ns:
response = remove_namespaces(resp)
else:
response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml
return response
except RPCError as exc:
raise Exception(to_xml(exc.xml))
def edit_config(self, config=None, format='xml', target='candidate', default_operation=None, test_option=None, error_option=None, remove_ns=False):
if config is None:
raise ValueError('config value must be provided')
try:
resp = self.m.edit_config(config, format=format, target=target, default_operation=default_operation, test_option=test_option,
error_option=error_option)
if remove_ns:
response = remove_namespaces(resp)
else:
response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml
return response
except RPCError as exc:
raise Exception(to_xml(exc.xml))
def commit(self, confirmed=False, timeout=None, persist=None, remove_ns=False):
try:
resp = self.m.commit(confirmed=confirmed, timeout=timeout, persist=persist)
if remove_ns:
response = remove_namespaces(resp)
else:
response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml
return response
except RPCError as exc:
raise Exception(to_xml(exc.xml))
def validate(self, source="candidate", remove_ns=False):
try:
resp = self.m.validate(source=source)
if remove_ns:
response = remove_namespaces(resp)
else:
response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml
return response
except RPCError as exc:
raise Exception(to_xml(exc.xml))
def discard_changes(self, remove_ns=False):
try:
resp = self.m.discard_changes()
if remove_ns:
response = remove_namespaces(resp)
else:
response = resp.data_xml if hasattr(resp, 'data_xml') else resp.xml
return response
except RPCError as exc:
raise Exception(to_xml(exc.xml))

@ -1,52 +0,0 @@
#
# (c) 2016 Red Hat Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import re
from ansible.plugins.terminal import TerminalBase
from ansible.errors import AnsibleConnectionFailure
class TerminalModule(TerminalBase):
terminal_stdout_re = [
re.compile(br"[\r\n]*[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
re.compile(br']]>]]>[\r\n]?')
]
terminal_stderr_re = [
re.compile(br"% ?Error"),
re.compile(br"% ?Bad secret"),
re.compile(br"% ?This command is not authorized"),
re.compile(br"invalid input", re.I),
re.compile(br"(?:incomplete|ambiguous) command", re.I),
re.compile(br"(?<!\()connection timed out(?!\))", re.I),
re.compile(br"[^\r\n]+ not found", re.I),
re.compile(br"'[^']' +returned error code: ?\d+"),
re.compile(br"Failed to commit", re.I)
]
def on_open_shell(self):
try:
for cmd in (b'terminal length 0', b'terminal width 512', b'terminal exec prompt no-timestamp'):
self._exec_cli_command(cmd)
except AnsibleConnectionFailure:
raise AnsibleConnectionFailure('unable to set terminal parameters')

@ -1,3 +0,0 @@
---
testcase: "[^_].*"
test_items: []

@ -1,20 +0,0 @@
---
- name: Collect all cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yaml"
use_regex: true
register: test_cases
delegate_to: localhost
- name: Set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
delegate_to: localhost
- name: Run test case (connection=network_cli)
include: "{{ test_case_to_run }}"
vars:
ansible_connection: network_cli
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

@ -1,2 +0,0 @@
---
- { include: cli.yaml, tags: ['cli'] }

@ -1,31 +0,0 @@
---
- name: Populate the device with ACLs
iosxr_config:
lines: |
ipv4 access-list acl_1
10 permit ipv4 any any
ipv4 access-list acl_2
10 permit ipv4 any any
ipv4 access-list acl_3
10 permit ipv4 any any
ipv6 access-list acl6_1
10 permit ipv6 any any
ipv6 access-list acl6_2
10 permit ipv6 any any
ipv6 access-list acl6_3
10 permit ipv6 any any
- name: Setup ACL interfaces configuration for GigabitEthernet0/0/0/0
iosxr_config:
lines: |
ipv4 access-group acl_1 ingress
ipv4 access-group acl_2 egress
ipv6 access-group acl6_1 ingress
ipv6 access-group acl6_2 egress
parents: interface GigabitEthernet0/0/0/0
- name: Setup ACL interfaces configuration for GigabitEthernet0/0/0/1
iosxr_config:
lines: ipv4 access-group acl_1 egress
parents: interface GigabitEthernet0/0/0/1

@ -1,34 +0,0 @@
---
- name: Remove/Default Resources
cli_config:
config: "{{ lines }}"
vars:
lines: |
default interface GigabitEthernet0/0/0/0
default interface GigabitEthernet0/0/0/1
no ipv4 access-list acl_1
no ipv4 access-list acl_2
no ipv6 access-list acl6_1
no ipv6 access-list acl6_2
no ipv6 access-list acl6_3
- name: Initialize interfaces
iosxr_config:
lines: shutdown
parents: "{{ item }}"
loop:
- interface GigabitEthernet0/0/0/0
- interface GigabitEthernet0/0/0/1
# To make sure our assertions are not affected by
# spill overs from previous tests
- name: Remove unwanted interfaces from config
iosxr_config:
lines:
- "no interface GigabitEthernet{{ item }}"
loop:
- 0/0/0/2
- 0/0/0/3
- 0/0/0/4
- 0/0/0/5
ignore_errors: yes

@ -1,85 +0,0 @@
---
- debug:
msg: "Start iosxr_acl_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Delete ACL attributes of GigabitEthernet0/0/0/1
iosxr_acl_interfaces: &deleted
config:
- name: GigabitEthernet0/0/0/1
state: deleted
register: result
- assert:
that:
- "'interface GigabitEthernet0/0/0/1' in result.commands"
- "'no ipv4 access-group acl_1 egress' in result.commands"
- "result.commands|length == 2"
- name: Delete ACL attributes of GigabitEthernet0/0/0/1 (IDEMPOTENT)
iosxr_acl_interfaces: *deleted
register: result
- assert:
that:
- "result.changed == False"
- "result.commands|length == 0"
- name: Delete a single ACL attached to GigabitEthernet0/0/0/0
iosxr_acl_interfaces: &deleted_1
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_2
direction: out
state: deleted
register: result
- assert:
that:
- "'interface GigabitEthernet0/0/0/0' in result.commands"
- "'no ipv4 access-group acl_2 egress' in result.commands"
- "result.commands|length == 2"
- name: Delete a single ACL attached to GigabitEthernet0/0/0/0 (IDEMPOTENT)
iosxr_acl_interfaces: *deleted_1
register: result
- assert:
that:
- "result.changed == False"
- "result.commands|length == 0"
- name: Delete all IPv6 ACLs attached to GigabitEthernet0/0/0/0
iosxr_acl_interfaces: &deleted_2
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv6
state: deleted
register: result
- assert:
that:
- "'interface GigabitEthernet0/0/0/0' in result.commands"
- "'no ipv6 access-group acl6_1 ingress' in result.commands"
- "'no ipv6 access-group acl6_2 egress' in result.commands"
- "result.commands|length == 3"
- name: Delete all IPv6 ACLs attached to GigabitEthernet0/0/0/0 (IDEMPOTENT)
iosxr_acl_interfaces: *deleted_2
register: result
- assert:
that:
- "result.changed == False"
- "result.commands|length == 0"
always:
- include_tasks: _remove_config.yaml

@ -1,46 +0,0 @@
---
- debug:
msg: "Start iosxr_acl_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Delete all ACL interfaces configuration from the device
iosxr_acl_interfaces: &deleted_3
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that the correct set of commands were generated
assert:
that:
- "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that the after dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete ACL attributes of all interfaces (IDEMPOTENT)
iosxr_acl_interfaces: *deleted_3
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,58 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces empty_config integration tests on connection={{ ansible_connection }}"
- name: Merged with empty config should give appropriate error message
iosxr_acl_interfaces:
config:
state: merged
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state merged'
- name: Replaced with empty config should give appropriate error message
iosxr_acl_interfaces:
config:
state: replaced
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state replaced'
- name: Overridden with empty config should give appropriate error message
iosxr_acl_interfaces:
config:
state: overridden
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state overridden'
- name: Rendered with empty config should give appropriate error message
iosxr_acl_interfaces:
config:
state: rendered
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state rendered'
- name: Parsed with empty config should give appropriate error message
iosxr_acl_interfaces:
running_config:
state: parsed
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of running_config parameter must not be empty for state parsed'

@ -1,14 +0,0 @@
interface MgmtEth0/0/CPU0/0
ipv4 address dhcp
!
interface GigabitEthernet0/0/0/0
shutdown
ipv4 access-group acl_1 ingress
ipv4 access-group acl_2 egress
ipv6 access-group acl6_1 ingress
ipv6 access-group acl6_2 egress
!
interface GigabitEthernet0/0/0/1
shutdown
ipv4 access-group acl_1 egress
!

@ -1,57 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces gathered integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- name: Populate the device with ACLs
iosxr_config:
lines: |
ipv4 access-list acl_1
10 permit ipv4 any any
ipv4 access-list acl_2
10 permit ipv4 any any
ipv6 access-list acl6_1
10 permit ipv6 any any
ipv6 access-list acl6_2
10 permit ipv6 any any
- block:
- name: Merge the provided configuration with the existing running configuration
iosxr_acl_interfaces: &merged
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
- afi: ipv6
acls:
- name: acl6_1
direction: in
- name: acl6_2
direction: out
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: out
state: merged
register: result
- name: Gather ACL interfaces facts using gathered state
iosxr_acl_interfaces:
state: gathered
register: result
- name: Assert that facts were correctly generated
assert:
that: "{{ merged['after'] | symmetric_difference(result['gathered']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,115 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces merged integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- name: Populate the device with ACLs
iosxr_config:
lines: |
ipv4 access-list acl_1
10 permit ipv4 any any
ipv4 access-list acl_2
10 permit ipv4 any any
ipv6 access-list acl6_1
10 permit ipv6 any any
ipv6 access-list acl6_2
10 permit ipv6 any any
- block:
- name: Merge the provided configuration with the existing running configuration
iosxr_acl_interfaces: &merged
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
- afi: ipv6
acls:
- name: acl6_1
direction: in
- name: acl6_2
direction: out
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: out
state: merged
register: result
- name: Assert that before dicts were correctly generated
assert:
that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that after dicts was correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
iosxr_acl_interfaces: *merged
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
- name: Assert that before dicts were correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Update acl_interfaces configuration using merged
iosxr_acl_interfaces: &merged_update
config:
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_2
direction: out
- name: acl_1
direction: in
state: merged
register: result
- name: Assert that before dicts were correctly generated
assert:
that: "{{ merged['update_before'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ merged['update_commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that after dicts was correctly generated
assert:
that:
- "{{ merged['update_after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Update acl_interfaces configuration using merged (IDEMPOTENT)
iosxr_acl_interfaces: *merged_update
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
always:
- include_tasks: _remove_config.yaml

@ -1,64 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces overridden integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Overridde all interface ACL configuration with provided configuration
iosxr_acl_interfaces: &overridden
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv6
acls:
- name: acl6_3
direction: in
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_2
direction: in
- afi: ipv6
acls:
- name: acl6_3
direction: out
state: overridden
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Overridde all interface LACP configuration with provided configuration (IDEMPOTENT)
iosxr_acl_interfaces: *overridden
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
- name: Assert that before dict is correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,14 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces parsed integration tests on connection={{ ansible_connection }}"
- name: Parse externally provided ACL interfaces config to agnostic model
iosxr_acl_interfaces:
running_config: "{{ lookup('file', './fixtures/parsed.cfg') }}"
state: parsed
register: result
- name: Assert that config was correctly parsed
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['parsed']) |length == 0 }}"

@ -1,35 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces rendered integration tests on connection={{ ansible_connection }}"
- name: Render platform specific commands from task input using rendered state
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
- afi: ipv6
acls:
- name: acl6_1
direction: in
- name: acl6_2
direction: out
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: out
state: rendered
register: result
- name: Assert that correct set of commands were rendered
assert:
that:
- "{{ merged['commands'] | symmetric_difference(result['rendered']) |length == 0 }}"

@ -1,53 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces replaced integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Replace device configurations of listed interface with provided configurations
iosxr_acl_interfaces: &replaced
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv6
acls:
- name: acl6_3
direction: in
state: replaced
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Replace device configurations of listed interfaces with provided configurarions (IDEMPOTENT)
iosxr_acl_interfaces: *replaced
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
- name: Assert that before dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,102 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces round trip integration tests on connection={{ ansible_connection }}"
- block:
- include_tasks: _remove_config.yaml
- name: Populate the device with ACLs
iosxr_config:
lines: |
ipv4 access-list acl_1
10 permit ipv4 any any
ipv4 access-list acl_2
10 permit ipv4 any any
ipv4 access-list acl_3
10 permit ipv4 any any
ipv6 access-list acl6_1
10 permit ipv6 any any
ipv6 access-list acl6_2
10 permit ipv6 any any
ipv6 access-list acl6_3
10 permit ipv6 any any
- name: Apply the provided configuration (base config)
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
- afi: ipv6
acls:
- name: acl6_1
direction: in
- name: acl6_2
direction: out
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: out
state: merged
- name: Gather interfaces facts
iosxr_facts:
gather_subset:
- "!all"
- "!min"
gather_network_resources:
- acl_interfaces
- name: Apply the provided configuration (config to be reverted)
iosxr_acl_interfaces:
config:
- name: GigabitEthernet0/0/0/1
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
- afi: ipv6
acls:
- name: acl6_1
direction: in
- name: acl6_2
direction: out
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv4
acls:
- name: acl_1
direction: out
state: overridden
register: result
- name: Assert that changes were applied
assert:
that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Revert back to base config using facts round trip
iosxr_acl_interfaces:
config: "{{ ansible_facts['network_resources']['acl_interfaces'] }}"
state: overridden
register: revert
- name: Assert that config was reverted
assert:
that: "{{ merged['after'] | symmetric_difference(revert['after']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,205 +0,0 @@
---
merged:
before:
- name: MgmtEth0/0/CPU0/0
- name: GigabitEthernet0/0/0/0
- name: GigabitEthernet0/0/0/1
commands:
- interface GigabitEthernet0/0/0/0
- ipv4 access-group acl_1 ingress
- ipv4 access-group acl_2 egress
- ipv6 access-group acl6_1 ingress
- ipv6 access-group acl6_2 egress
- interface GigabitEthernet0/0/0/1
- ipv4 access-group acl_1 egress
update_commands:
- interface GigabitEthernet0/0/0/1
- ipv4 access-group acl_2 egress
- ipv4 access-group acl_1 ingress
after:
- name: MgmtEth0/0/CPU0/0
- name: GigabitEthernet0/0/0/0
access_groups:
- acls:
- name: acl_1
direction: in
- name: acl_2
direction: out
afi: ipv4
- acls:
- name: acl6_1
direction: in
- name: acl6_2
direction: out
afi: ipv6
- name: GigabitEthernet0/0/0/1
access_groups:
- acls:
- name: acl_1
direction: out
afi: ipv4
update_before:
- name: MgmtEth0/0/CPU0/0
- access_groups:
- acls:
- direction: in
name: acl_1
- direction: out
name: acl_2
afi: ipv4
- acls:
- direction: in
name: acl6_1
- direction: out
name: acl6_2
afi: ipv6
name: GigabitEthernet0/0/0/0
- access_groups:
- acls:
- direction: out
name: acl_1
afi: ipv4
name: GigabitEthernet0/0/0/1
update_after:
- name: MgmtEth0/0/CPU0/0
- access_groups:
- acls:
- direction: in
name: acl_1
- direction: out
name: acl_2
afi: ipv4
- acls:
- direction: in
name: acl6_1
- direction: out
name: acl6_2
afi: ipv6
name: GigabitEthernet0/0/0/0
- access_groups:
- acls:
- direction: in
name: acl_1
- direction: out
name: acl_2
afi: ipv4
name: GigabitEthernet0/0/0/1
replaced:
commands:
- interface GigabitEthernet0/0/0/0
- no ipv4 access-group acl_1 ingress
- no ipv4 access-group acl_2 egress
- no ipv6 access-group acl6_1 ingress
- no ipv6 access-group acl6_2 egress
- ipv6 access-group acl6_3 ingress
after:
- name: MgmtEth0/0/CPU0/0
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv6
acls:
- name: acl6_3
direction: in
- name: GigabitEthernet0/0/0/1
access_groups:
- acls:
- name: acl_1
direction: out
afi: ipv4
overridden:
commands:
- interface GigabitEthernet0/0/0/0
- no ipv4 access-group acl_1 ingress
- no ipv4 access-group acl_2 egress
- no ipv6 access-group acl6_1 ingress
- no ipv6 access-group acl6_2 egress
- ipv6 access-group acl6_3 ingress
- interface GigabitEthernet0/0/0/1
- no ipv4 access-group acl_1 egress
- ipv4 access-group acl_2 ingress
- ipv6 access-group acl6_3 egress
after:
- name: MgmtEth0/0/CPU0/0
- name: GigabitEthernet0/0/0/0
access_groups:
- afi: ipv6
acls:
- name: acl6_3
direction: in
- name: GigabitEthernet0/0/0/1
access_groups:
- acls:
- name: acl_2
direction: in
afi: ipv4
- acls:
- name: acl6_3
direction: out
afi: ipv6
deleted:
commands:
- interface GigabitEthernet0/0/0/0
- no ipv4 access-group acl_1 ingress
- no ipv4 access-group acl_2 egress
- no ipv6 access-group acl6_1 ingress
- no ipv6 access-group acl6_2 egress
- interface GigabitEthernet0/0/0/1
- no ipv4 access-group acl_1 egress
after:
- name: MgmtEth0/0/CPU0/0
- name: GigabitEthernet0/0/0/0
- name: GigabitEthernet0/0/0/1
round_trip:
after:
- name: MgmtEth0/0/CPU0/0
- access_groups:
- acls:
- direction: out
name: acl_1
afi: ipv4
name: GigabitEthernet0/0/0/0
- access_groups:
- acls:
- direction: in
name: acl_1
- direction: out
name: acl_2
afi: ipv4
- acls:
- direction: in
name: acl6_1
- direction: out
name: acl6_2
afi: ipv6
name: GigabitEthernet0/0/0/1

@ -1,3 +0,0 @@
---
testcase: "[^_].*"
test_items: []

@ -1,20 +0,0 @@
---
- name: Collect all cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yaml"
use_regex: true
register: test_cases
delegate_to: localhost
- name: Set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
delegate_to: localhost
- name: Run test case (connection=network_cli)
include: "{{ test_case_to_run }}"
vars:
ansible_connection: network_cli
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

@ -1,2 +0,0 @@
---
- { include: cli.yaml, tags: ['cli'] }

@ -1,13 +0,0 @@
---
- name: Setup
iosxr_config:
lines: |
ipv6 access-list acl6_1
10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 authen routing log
20 permit icmpv6 any any router-advertisement precedence network destopts
ipv4 access-list acl_1
16 remark TEST_ACL_1_REMARK
21 permit tcp host 192.0.2.10 range pop3 121 198.51.100.0 0.0.0.15 rst
23 deny icmp any 198.51.100.0 0.0.0.15 reassembly-timeout dscp lt af12
ipv4 access-list acl_2
10 remark TEST_ACL_2_REMARK

@ -1,14 +0,0 @@
---
- name: Remove ACLs
cli_config:
config: "{{ lines }}"
vars:
lines: |
no ipv4 access-list acl_1
no ipv4 access-list acl_2
no ipv4 access-list acl_3
no ipv4 access-list acl_3
no ipv6 access-list acl6_1
no ipv6 access-list acl6_2
no ipv6 access-list acl6_3
ignore_errors: yes

@ -1,116 +0,0 @@
---
- debug:
msg: "Start iosxr_lag_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Delete a single ACE
iosxr_acls: &deleted_1
config:
- afi: ipv4
acls:
- name: acl_1
aces:
- sequence: 23
state: deleted
register: result
- assert:
that:
- '"ipv4 access-list acl_1" in result.commands'
- '"no 23" in result.commands'
- "result.commands|length == 2"
- name: Delete a single ACE (IDEMPOTENT)
iosxr_acls: *deleted_1
register: result
- name: Assert that the previous task was idempotent
assert: &unchanged
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Delete a single ACL
iosxr_acls: &deleted_2
config:
- afi: ipv6
acls:
- name: acl6_1
state: deleted
register: result
- assert:
that:
- '"no ipv6 access-list acl6_1" in result.commands'
- "result.commands|length == 1"
- name: Delete a single ACL (IDEMPOTENT)
iosxr_acls: *deleted_2
register: result
- name: Assert that the previous task was idempotent
assert: *unchanged
- name: Delete all ACLs under one AFI
iosxr_acls: &deleted_3
config:
- afi: ipv4
state: deleted
register: result
- assert:
that:
- '"no ipv4 access-list acl_1" in result.commands'
- '"no ipv4 access-list acl_2" in result.commands'
- "result.commands|length == 2"
- name: Delete all ACLs under one AFI (IDEMPOTENT)
iosxr_acls: *deleted_3
register: result
- name: Assert that the previous task was idempotent
assert: *unchanged
- include_tasks: _populate_config.yaml
- name: Delete all ACLs from the device
iosxr_acls: &deleted_4
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that the correct set of commands were generated
assert:
that:
- "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that the after dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete all ACLs from the device (IDEMPOTENT)
iosxr_lag_interfaces: *deleted_4
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,57 +0,0 @@
- debug:
msg: "START iosxr_acls empty_config integration tests on connection={{ ansible_connection }}"
- name: Merged with empty config should give appropriate error message
iosxr_acls:
config:
state: merged
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state merged'
- name: Replaced with empty config should give appropriate error message
iosxr_acls:
config:
state: replaced
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state replaced'
- name: Overridden with empty config should give appropriate error message
iosxr_acls:
config:
state: overridden
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state overridden'
- name: Rendered with empty config should give appropriate error message
iosxr_acls:
config:
state: rendered
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state rendered'
- name: Parsed with empty config should give appropriate error message
iosxr_acls:
running_config:
state: parsed
register: result
ignore_errors: True
- assert:
that:
- result.msg == 'value of running_config parameter must not be empty for state parsed'

@ -1,8 +0,0 @@
ipv4 access-list acl_1
10 remark TEST_ACL_2_REMARK
ipv4 access-list acl_2
11 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 authen routing log
21 permit icmpv6 any any router-advertisement precedence network packet-length eq 576 destopts
ipv6 access-list acl6_1
10 deny tcp 2001:db8:1234::/48 range ftp telnet any syn ttl range 180 250 routing authen log
20 permit icmpv6 any any router-advertisement precedence network packet-length eq 576 destopts

@ -1,20 +0,0 @@
---
- debug:
msg: "START iosxr_acls gathered integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Gather ACL interfaces facts using gathered state
iosxr_acls:
state: gathered
register: result
- name: Assert that facts were correctly generated
assert:
that: "{{ merged['after'] | symmetric_difference(result['gathered']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,168 +0,0 @@
---
- debug:
msg: "START iosxr_acls merged integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
iosxr_acls: &merged
config:
- afi: ipv6
acls:
- name: acl6_1
aces:
- sequence: 10
grant: deny
protocol: tcp
source:
prefix: 2001:db8:1234::/48
port_protocol:
range:
start: ftp
end: telnet
destination:
any: True
protocol_options:
tcp:
syn: True
ttl:
range:
start: 180
end: 250
routing: True
authen: True
log: True
- sequence: 20
grant: permit
protocol: icmpv6
source:
any: True
destination:
any: True
protocol_options:
icmpv6:
router_advertisement: True
precedence: network
destopts: True
- afi: ipv4
acls:
- name: acl_1
aces:
- sequence: 16
remark: TEST_ACL_1_REMARK
- sequence: 21
grant: permit
protocol: tcp
source:
host: 192.0.2.10
port_protocol:
range:
start: pop3
end: 121
destination:
address: 198.51.100.0
wildcard_bits: 0.0.0.15
protocol_options:
tcp:
rst: True
- sequence: 23
grant: deny
protocol: icmp
source:
any: True
destination:
prefix: 198.51.100.0/28
protocol_options:
icmp:
reassembly_timeout: True
dscp:
lt: af12
- name: acl_2
aces:
- sequence: 10
remark: TEST_ACL_2_REMARK
state: merged
register: result
- name: Assert that before dicts were correctly generated
assert:
that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that after dicts was correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
iosxr_acls: *merged
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
- name: Assert that before dicts were correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Update existing ACEs
iosxr_acls: &update
config:
- afi: ipv4
acls:
- name: acl_1
aces:
- sequence: 21
source:
prefix: 198.51.100.32/28
port_protocol:
range:
start: pop3
end: 121
protocol_options:
tcp:
syn: True
- sequence: 23
protocol_options:
icmp:
router_advertisement: True
dscp:
eq: af23
state: merged
register: result
- name: Assert that the correct set of commands were generated
assert:
that:
- '"ipv4 access-list acl_1" in result.commands'
- '"21 permit tcp 198.51.100.32 0.0.0.15 range pop3 121 198.51.100.0 0.0.0.15 syn" in result.commands'
- '"23 deny icmp any 198.51.100.0 0.0.0.15 router-advertisement dscp eq af23" in result.commands'
- "result.commands|length == 3"
- name: Update existing ACEs (IDEMPOTENT)
iosxr_acls: *update
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
always:
- include_tasks: _remove_config.yaml

@ -1,68 +0,0 @@
---
- debug:
msg: "START iosxr_acls overridden integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Overridde all ACLs configuration with provided configuration
iosxr_acls: &overridden
config:
- afi: ipv4
acls:
- name: acl_1
aces:
- sequence: 10
grant: permit
source:
any: True
destination:
any: True
protocol: tcp
- name: acl_2
aces:
- sequence: 20
grant: permit
source:
any: True
destination:
any: True
protocol: igmp
state: overridden
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Overridde all interface LAG interface configuration with provided configuration (IDEMPOTENT)
iosxr_acls: *overridden
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
- name: Assert that before dict is correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

@ -1,14 +0,0 @@
---
- debug:
msg: "START iosxr_acls parsed integration tests on connection={{ ansible_connection }}"
- name: Parse externally provided ACL config to agnostic model
iosxr_acls:
running_config: "{{ lookup('file', './fixtures/parsed.cfg') }}"
state: parsed
register: result
- name: Assert that config was correctly parsed
assert:
that:
- "{{ parsed | symmetric_difference(result['parsed']) |length == 0 }}"

@ -1,93 +0,0 @@
---
- debug:
msg: "START iosxr_acls rendered integration tests on connection={{ ansible_connection }}"
- name: Render platform specific commands from task input using rendered state
iosxr_acls:
config:
- afi: ipv6
acls:
- name: acl6_1
aces:
- sequence: 10
grant: deny
protocol: tcp
source:
prefix: 2001:db8:1234::/48
port_protocol:
range:
start: ftp
end: telnet
destination:
any: True
protocol_options:
tcp:
syn: True
ttl:
range:
start: 180
end: 250
routing: True
authen: True
log: True
- sequence: 20
grant: permit
protocol: icmpv6
source:
any: True
destination:
any: True
protocol_options:
icmpv6:
router_advertisement: True
precedence: network
destopts: True
- afi: ipv4
acls:
- name: acl_1
aces:
- sequence: 16
remark: TEST_ACL_1_REMARK
- sequence: 21
grant: permit
protocol: tcp
source:
host: 192.0.2.10
port_protocol:
range:
start: pop3
end: 121
destination:
address: 198.51.100.0
wildcard_bits: 0.0.0.15
protocol_options:
tcp:
rst: True
- sequence: 23
grant: deny
protocol: icmp
source:
any: True
destination:
prefix: 198.51.100.0/28
protocol_options:
icmp:
reassembly_timeout: True
dscp:
lt: af12
- name: acl_2
aces:
- sequence: 10
remark: TEST_ACL_2_REMARK
state: rendered
register: result
- name: Assert that correct set of commands were rendered
assert:
that:
- "{{ merged['commands'] | symmetric_difference(result['rendered']) |length == 0 }}"

@ -1,68 +0,0 @@
---
- debug:
msg: "START iosxr_acl_interfaces replaced integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Replace device configurations of listed ACL with provided configurations
iosxr_acls: &replaced
config:
- afi: ipv4
acls:
- name: acl_2
aces:
- sequence: 11
grant: permit
protocol: igmp
source:
host: 198.51.100.130
destination:
any: True
ttl:
eq: 100
- sequence: 12
grant: deny
source:
any: True
destination:
any: True
protocol: icmp
state: replaced
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Replace device configurations of listed interfaces with provided configurarions (IDEMPOTENT)
iosxr_acls: *replaced
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
- "result.commands|length == 0"
- name: Assert that before dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save