Migrated to cisco.ios

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

@ -1,66 +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_acl_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Acl_InterfacesArgs(object):
"""The arg spec for the ios_acl_interfaces module
"""
argument_spec = {
'config': {
'elements': 'dict',
'options': {
'name': {'required': True, 'type': 'str'},
'access_groups': {
'type': 'list',
'elements': 'dict',
'options': {
'afi': {'required': True, 'choices': ['ipv4', 'ipv6'], 'type': 'str'},
'acls': {
'type': 'list',
'elements': 'dict',
'options': {
'name': {'required': True, 'type': 'str'},
'direction': {'required': True, 'choices': ['in', 'out'], 'type': 'str'}
}
}
}
}
},
'type': 'list'
},
'running_config': {'type': 'str'},
'state': {
'choices': ['merged', 'replaced', 'overridden', 'deleted', 'gathered', 'rendered', 'parsed'],
'default': 'merged',
'type': 'str'
}
}

@ -1,593 +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_acls module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class AclsArgs(object):
"""The arg spec for the ios_acls module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
'config': {
'elements': 'dict',
'options': {
'afi': {
'required': True,
'choices': ['ipv4', 'ipv6'],
'type': 'str'
},
'acls': {
'elements': 'dict',
'type': 'list',
'options': {
'name': {
'required': True,
'type': 'str'
},
'acl_type': {
'choices': ['extended', 'standard'],
'type': 'str'
},
'aces': {
'elements': 'dict',
'type': 'list',
'options': {
'grant': {
'choices': ['permit', 'deny'],
'type': 'str'
},
'sequence': {
'type': 'int'
},
'source': {
'type':
'dict',
'mutually_exclusive':
[['address', 'any', 'host'],
['wildcard_bits', 'any', 'host']],
'options': {
'address': {
'type': 'str'
},
'wildcard_bits': {
'type': 'str'
},
'any': {
'type': 'bool'
},
'host': {
'type': 'str'
},
'port_protocol': {
'type': 'dict',
'options': {
'eq': {
'type': 'str'
},
'gt': {
'type': 'str'
},
'lt': {
'type': 'str'
},
'neq': {
'type': 'str'
},
'range': {
'type': 'dict',
'options': {
'start': {
'type': 'int'
},
'end': {
'type': 'int'
}
}
}
}
}
},
},
'destination': {
'type':
'dict',
'mutually_exclusive':
[['address', 'any', 'host'],
['wildcard_bits', 'any', 'host']],
'options': {
'address': {
'type': 'str'
},
'wildcard_bits': {
'type': 'str'
},
'any': {
'type': 'bool'
},
'host': {
'type': 'str'
},
'port_protocol': {
'type': 'dict',
'options': {
'eq': {
'type': 'str'
},
'gt': {
'type': 'str'
},
'lt': {
'type': 'str'
},
'neq': {
'type': 'str'
},
'range': {
'type': 'dict',
'options': {
'start': {
'type': 'int'
},
'end': {
'type': 'int'
}
}
}
}
}
}
},
'protocol': {
'type': 'str'
},
'protocol_options': {
'type': 'dict',
'options': {
'protocol_number': {
'type': 'int'
},
'ahp': {
'type': 'bool'
},
'eigrp': {
'type': 'bool'
},
'esp': {
'type': 'bool'
},
'gre': {
'type': 'bool'
},
'hbh': {
'type': 'bool'
},
'icmp': {
'type': 'dict',
'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'
},
}
},
'igmp': {
'type': 'dict',
'options': {
'dvmrp': {
'type': 'bool'
},
'host_query': {
'type': 'bool'
},
'mtrace_resp': {
'type': 'bool'
},
'mtrace_route': {
'type': 'bool'
},
'pim': {
'type': 'bool'
},
'trace': {
'type': 'bool'
},
'v1host_report': {
'type': 'bool'
},
'v2host_report': {
'type': 'bool'
},
'v2leave_group': {
'type': 'bool'
},
'v3host_report': {
'type': 'bool'
}
}
},
'ip': {
'type': 'bool'
},
'ipv6': {
'type': 'bool'
},
'ipinip': {
'type': 'bool'
},
'nos': {
'type': 'bool'
},
'ospf': {
'type': 'bool'
},
'pcp': {
'type': 'bool'
},
'pim': {
'type': 'bool'
},
'sctp': {
'type': 'bool'
},
'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'
},
'udp': {
'type': 'bool'
}
}
},
'dscp': {
'type': 'str'
},
'fragments': {
'type': 'str'
},
'log': {
'type': 'str'
},
'log_input': {
'type': 'str'
},
'option': {
'type': 'dict',
'options': {
'add_ext': {
'type': 'bool'
},
'any_options': {
'type': 'bool'
},
'com_security': {
'type': 'bool'
},
'dps': {
'type': 'bool'
},
'encode': {
'type': 'bool'
},
'eool': {
'type': 'bool'
},
'ext_ip': {
'type': 'bool'
},
'ext_security': {
'type': 'bool'
},
'finn': {
'type': 'bool'
},
'imitd': {
'type': 'bool'
},
'lsr': {
'type': 'bool'
},
'mtup': {
'type': 'bool'
},
'mtur': {
'type': 'bool'
},
'no_op': {
'type': 'bool'
},
'nsapa': {
'type': 'bool'
},
'record_route': {
'type': 'bool'
},
'router_alert': {
'type': 'bool'
},
'sdb': {
'type': 'bool'
},
'security': {
'type': 'bool'
},
'ssr': {
'type': 'bool'
},
'stream_id': {
'type': 'bool'
},
'timestamp': {
'type': 'bool'
},
'traceroute': {
'type': 'bool'
},
'ump': {
'type': 'bool'
},
'visa': {
'type': 'bool'
},
'zsu': {
'type': 'bool'
}
}
},
'precedence': {
'type': 'int'
},
'time_range': {
'type': 'str'
},
'tos': {
'type': 'dict',
'options': {
'service_value': {
'type': 'int'
},
'max_reliability': {
'type': 'bool'
},
'max_throughput': {
'type': 'bool'
},
'min_delay': {
'type': 'bool'
},
'min_monetary_cost': {
'type': 'bool'
},
'normal': {
'type': 'bool'
}
}
},
'ttl': {
'type': 'dict',
'options': {
'eq': {
'type': 'int'
},
'gt': {
'type': 'int'
},
'lt': {
'type': 'int'
},
'neq': {
'type': 'int'
},
'range': {
'type': 'dict',
'options': {
'start': {
'type': 'int'
},
'end': {
'type': 'int'
}
}
}
}
},
}
}
}
},
},
'type': 'list'
},
'running_config': {
'type': 'str'
},
'state': {
'choices': [
'merged', 'replaced', 'overridden', 'deleted', 'gathered',
'rendered', 'parsed'
],
'default':
'merged',
'type':
'str'
}
}

@ -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 ios facts module.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class FactsArgs(object):
""" The arg spec for the ios 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': 'str'},
'mtu': {'type': 'int'},
'duplex': {'type': 'str', 'choices': ['full', 'half', 'auto']}},
'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 ios_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},
'mode': {'type': 'str', 'choices': ['access', 'trunk']},
'access': {'type': 'dict',
'options': {'vlan': {'type': 'int'}}
},
'voice': {'type': 'dict',
'options': {'vlan': {'type': 'int'}}
},
'trunk': {'type': 'dict',
'options': {'allowed_vlans': {'type': 'list'},
'encapsulation': {'type': 'str',
'choices':
['dot1q', 'isl', 'negotiate']},
'native_vlan': {'type': 'int'},
'pruning_vlans': {'type': 'list'}}
}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

@ -1,53 +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'},
'dhcp_client': {'type': 'int'},
'dhcp_hostname': {'type': 'str'}}},
'ipv6': {'element': 'dict',
'type': 'list',
'options': {'address': {'type': 'str'}}}
},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

@ -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_lacp module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class LacpArgs(object):
"""The arg spec for the ios_lacp module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
'config': {'options': {'system': {'options': {'priority': {'required': True, 'type': 'int'}},
'type': 'dict'}
}, 'type': 'dict'
},
'state': {'choices': ['merged', 'replaced', 'deleted'], 'default': 'merged',
'type': 'str'}
}

@ -1,48 +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_lacp_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lacp_InterfacesArgs(object):
"""The arg spec for the ios_lacp_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'name': {'required': True, 'type': 'str'},
'port_priority': {'type': 'int'},
'fast_switchover': {'type': 'bool'},
'max_bundle': {'type': 'int'}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

@ -1,52 +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_lag_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lag_interfacesArgs(object):
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'name': {'required': True, 'type': 'str'},
'members': {'elements': 'dict',
'options': {
'member': {'type': 'str'},
'mode': {'choices': ['auto', 'on', 'desirable',
'active', 'passive'],
'type': 'str', 'required': True},
'link': {'type': 'int'}
},
'type': 'list'}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

@ -1,58 +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_lldp_global module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lldp_globalArgs(object):
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'options': {'holdtime': {'type': 'int'},
'reinit': {'type': 'int'},
'enabled': {'type': 'bool'},
'timer': {'type': 'int'},
'tlv_select': {
'options': {
'four_wire_power_management': {'type': 'bool'},
'mac_phy_cfg': {'type': 'bool'},
'management_address': {'type': 'bool'},
'port_description': {'type': 'bool'},
'port_vlan': {'type': 'bool'},
'power_management': {'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'}}

@ -1,52 +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_lldp_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lldp_InterfacesArgs(object):
"""The arg spec for the ios_lldp_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'name': {'required': True, 'type': 'str'},
'transmit': {'type': 'bool'},
'receive': {'type': 'bool'},
'med_tlv_select': {'options': {'inventory_management': {'type': 'bool'}},
'type': 'dict'},
'tlv_select': {'options': {'power_management': {'type': 'bool'}},
'type': 'dict'}
},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

@ -1,85 +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_static_routes module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Static_RoutesArgs(object):
"""The arg spec for the ios_static_routes module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
'config': {
'elements': 'dict',
'options': {
'vrf': {'type': 'str'},
'address_families': {
'elements': 'dict',
'type': 'list',
'options': {
'afi': {'required': True, 'choices': ['ipv4', 'ipv6'], 'type': 'str'},
'routes': {
'elements': 'dict',
'type': 'list',
'options': {
'dest': {'required': True, 'type': 'str'},
'topology': {'type': 'str'},
'next_hops': {
'elements': 'dict',
'type': 'list',
'options': {
'forward_router_address': {'type': 'str'},
'interface': {'type': 'str'},
'dhcp': {'type': 'bool'},
'distance_metric': {'type': 'int'},
'global': {'type': 'bool'},
'name': {'type': 'str'},
'multicast': {'type': 'bool'},
'permanent': {'type': 'bool'},
'tag': {'type': 'int'},
'track': {'type': 'int'}
}
}
}
}
}
}
},
'type': 'list'
},
'running_config': {'type': 'str'},
'state': {
'choices': ['merged', 'replaced', 'overridden', 'deleted', 'gathered', 'rendered', 'parsed'],
'default': 'merged',
'type': 'str'
}
}

@ -1,50 +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_vlans module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class VlansArgs(object):
"""The arg spec for the ios_vlans module
"""
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'name': {'type': 'str'},
'vlan_id': {'required': True, 'type': 'int'},
'mtu': {'type': 'int'},
'remote_span': {'type': 'bool'},
'state': {'type': 'str', 'choices': ['active', 'suspend']},
'shutdown': {'type': 'str', 'choices': ['enabled', 'disabled']}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

@ -1,405 +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 ios_acl_interfaces class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.module_utils.network.common.cfg.base import ConfigBase
from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.network.ios.facts.facts import Facts
from ansible.module_utils.six import iteritems
from ansible.module_utils.network.ios.utils.utils import remove_duplicate_interface, normalize_interface
class Acl_Interfaces(ConfigBase):
"""
The ios_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 moduel execution
"""
result = {'changed': False}
commands = list()
warnings = 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)
else:
changed_acl_interfaces_facts = []
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 deisred configuration
"""
want = self._module.params['config']
if want:
for item in want:
item['name'] = normalize_interface(item['name'])
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 deisred configuration
"""
commands = []
state = self._module.params['state']
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 = self._state_overridden(want, have)
elif state == 'deleted':
commands = self._state_deleted(want, have)
elif state == 'merged' or state == 'rendered':
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
: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 deisred configuration
"""
commands = []
for interface in want:
for each in have:
if each['name'] == interface['name']:
break
else:
continue
commands.extend(self._clear_config(interface, each, 'replaced'))
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
: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 = []
for each in have:
for interface in want:
if each['name'] == interface['name']:
break
else:
# We didn't find a matching desired state, which means we can
# pretend we recieved an empty desired state.
interface = dict(name=each['name'])
commands.extend(self._clear_config(interface, each))
continue
commands.extend(self._clear_config(interface, each, 'overridden'))
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
:param want: the additive configuration as a dictionary
:param have: the current configuration as a dictionary
: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
else:
# configuring non-existing interface
commands.extend(self._set_config(interface, dict()))
continue
commands.extend(self._set_config(interface, each))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:param want: the objects from which the configuration should be removed
:param have: the current configuration as a dictionary
: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
else:
continue
commands.extend(self._clear_config(interface, each))
else:
for each in have:
commands.extend(self._clear_config(dict(), each))
return commands
def dict_to_set(self, input_dict, test_set, final_set, count=0):
# recursive function to convert input dict to set for comparision
test_dict = dict()
if isinstance(input_dict, dict):
input_dict_len = len(input_dict)
for k, v in sorted(iteritems(input_dict)):
count += 1
if isinstance(v, list):
for each in v:
if isinstance(each, dict):
input_dict_len = len(each)
if [True for i in each.values() if type(i) == list]:
self.dict_to_set(each, set(), final_set, count)
else:
self.dict_to_set(each, test_set, final_set, 0)
else:
if v is not None:
test_dict.update({k: v})
if tuple(iteritems(test_dict)) not in test_set and count == input_dict_len:
test_set.add(tuple(iteritems(test_dict)))
count = 0
if count == input_dict_len + 1:
test_set.update(tuple(iteritems(test_dict)))
final_set.add(tuple(test_set))
def _set_config(self, want, have):
""" Function that sets the acls config based on the want and have config
:param want: want config
:param have: have config
:param acl_want: want acl config
:param afi: acl afi type
:rtype: A list
:returns: the commands generated based on input want/have params
"""
commands = []
want_set = set()
have_set = set()
self.dict_to_set(want, set(), want_set)
self.dict_to_set(have, set(), have_set)
for w in want_set:
want_afi = dict(w).get('afi')
if have_set:
def common_diff_config_code(diff_list, cmd, commands):
for each in diff_list:
try:
temp = dict(each)
temp_cmd = cmd + ' {0} {1}'.format(temp['name'], temp['direction'])
if temp_cmd not in commands:
commands.append(temp_cmd)
except ValueError:
continue
for h in have_set:
have_afi = dict(h).get('afi')
if have_afi == want_afi:
if want_afi == 'ipv4':
diff = set(w) - set(h)
if diff:
cmd = 'ip access-group'
common_diff_config_code(diff, cmd, commands)
if want_afi == 'ipv6':
diff = set(w) - set(h)
if diff:
cmd = 'ipv6 traffic-filter'
common_diff_config_code(diff, cmd, commands)
break
else:
if want_afi == 'ipv4':
diff = set(w) - set(h)
if diff:
cmd = 'ip access-group'
common_diff_config_code(diff, cmd, commands)
if want_afi == 'ipv6':
diff = set(w) - set(h)
if diff:
cmd = 'ipv6 traffic-filter'
common_diff_config_code(diff, cmd, commands)
else:
def common_want_config_code(want, cmd, commands):
for each in want:
if each[0] == 'afi':
continue
temp = dict(each)
temp_cmd = cmd + ' {0} {1}'.format(temp['name'], temp['direction'])
commands.append(temp_cmd)
if want_afi == 'ipv4':
cmd = 'ip access-group'
common_want_config_code(w, cmd, commands)
if want_afi == 'ipv6':
cmd = 'ipv6 traffic-filter'
common_want_config_code(w, cmd, commands)
commands.sort()
if commands:
interface = want.get('name')
commands.insert(0, 'interface {0}'.format(interface))
return commands
def _clear_config(self, want, have, state=''):
""" Function that deletes the acl config based on the want and have config
:param acl: acl config
:param config: config
:rtype: A list
:returns: the commands generated based on input acl/config params
"""
commands = []
if want.get('name'):
interface = 'interface ' + want['name']
else:
interface = 'interface ' + have['name']
w_access_group = want.get('access_groups')
temp_want_afi = []
temp_want_acl_name = []
if w_access_group:
# get the user input afi and acls
for each in w_access_group:
want_afi = each.get('afi')
want_acls = each.get('acls')
if want_afi:
temp_want_afi.append(want_afi)
if want_acls:
for each in want_acls:
temp_want_acl_name.append(each.get('name'))
h_access_group = have.get('access_groups')
if h_access_group:
for access_grp in h_access_group:
for acl in access_grp.get('acls'):
have_afi = access_grp.get('afi')
acl_name = acl.get('name')
acl_direction = acl.get('direction')
if temp_want_afi and state not in ['replaced', 'overridden']:
# if user want to delete acls based on afi
if 'ipv4' in temp_want_afi and have_afi == 'ipv4':
if acl_name in temp_want_acl_name:
continue
cmd = 'no ip access-group'
cmd += ' {0} {1}'.format(acl_name, acl_direction)
commands.append(cmd)
if 'ipv6' in temp_want_afi and have_afi == 'ipv6':
if acl_name in temp_want_acl_name:
continue
cmd = 'no ipv6 traffic-filter'
cmd += ' {0} {1}'.format(acl_name, acl_direction)
commands.append(cmd)
else:
# if user want to delete acls based on interface
if access_grp.get('afi') == 'ipv4':
if acl_name in temp_want_acl_name:
continue
cmd = 'no ip access-group'
cmd += ' {0} {1}'.format(acl_name, acl_direction)
commands.append(cmd)
elif access_grp.get('afi') == 'ipv6':
if acl_name in temp_want_acl_name:
continue
cmd = 'no ipv6 traffic-filter'
cmd += ' {0} {1}'.format(acl_name, acl_direction)
commands.append(cmd)
if commands:
# inserting the interface at first
commands.insert(0, interface)
return commands

@ -1,717 +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 ios_acls class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import copy
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.ios.facts.facts import Facts
from ansible.module_utils.six import iteritems
from ansible.module_utils.network.common.utils import remove_empties
from ansible.module_utils.network.ios.utils.utils import new_dict_to_set
class Acls(ConfigBase):
"""
The ios_acls class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'acls',
]
def __init__(self, module):
super(Acls, self).__init__(module)
def get_acl_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_facts = facts['ansible_network_resources'].get('acls')
if not acl_facts:
return []
return acl_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from moduel execution
"""
result = {'changed': False}
commands = list()
warnings = list()
if self.state in self.ACTION_STATES:
existing_acl_facts = self.get_acl_facts()
else:
existing_acl_facts = []
if self.state in self.ACTION_STATES or self.state == 'rendered':
commands.extend(self.set_config(existing_acl_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_facts = self.get_acl_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_facts(data=running_config)
else:
changed_acl_facts = []
if self.state in self.ACTION_STATES:
result['before'] = existing_acl_facts
if result['changed']:
result['after'] = changed_acl_facts
elif self.state == 'gathered':
result['gathered'] = changed_acl_facts
result['warnings'] = warnings
return result
def set_config(self, existing_acl_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 deisred configuration
"""
want = self._module.params['config']
have = existing_acl_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 deisred configuration
"""
commands = []
state = self._module.params['state']
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 = self._state_overridden(want, have)
elif state == 'deleted':
commands = self._state_deleted(want, have)
elif state == 'merged' or state == 'rendered':
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
: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 deisred configuration
"""
commands = []
for config_want in want:
for acls_want in config_want.get('acls'):
for ace_want in acls_want.get('aces'):
check = False
for config_have in have:
for acls_have in config_have.get('acls'):
for ace_have in acls_have.get('aces'):
if acls_want.get('name') == acls_have.get('name'):
ace_want = remove_empties(ace_want)
acls_want = remove_empties(acls_want)
cmd, change = self._set_config(ace_want,
ace_have,
acls_want,
config_want['afi'])
if cmd:
for temp_acls_have in config_have.get('acls'):
for temp_ace_have in temp_acls_have.get('aces'):
if acls_want.get('name') == temp_acls_have.get('name'):
commands.extend(
self._clear_config(temp_acls_have,
config_have,
temp_ace_have.get('sequence')))
commands.extend(cmd)
check = True
if check:
break
if check:
break
if not check:
# For configuring any non-existing want config
ace_want = remove_empties(ace_want)
cmd, change = self._set_config(ace_want,
{},
acls_want,
config_want['afi'])
commands.extend(cmd)
# Split and arrange the config commands
commands = self.split_set_cmd(commands)
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
: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 = []
# Creating a copy of want, so that want dict is intact even after delete operation
# performed during override want n have comparison
temp_want = copy.deepcopy(want)
for config_have in have:
for acls_have in config_have.get('acls'):
for ace_have in acls_have.get('aces'):
check = False
for config_want in temp_want:
count = 0
for acls_want in config_want.get('acls'):
for ace_want in acls_want.get('aces'):
if acls_want.get('name') == acls_have.get('name'):
ace_want = remove_empties(ace_want)
acls_want = remove_empties(acls_want)
cmd, change = self._set_config(ace_want, ace_have, acls_want, config_want['afi'])
if cmd:
for temp_acls_have in config_have.get('acls'):
for temp_ace_have in temp_acls_have.get('aces'):
if acls_want.get('name') == temp_acls_have.get('name'):
commands.extend(
self._clear_config(temp_acls_have,
config_have,
temp_ace_have.get('sequence')))
commands.extend(cmd)
check = True
if check:
del config_want.get('acls')[count]
else:
count += 1
if check:
break
if check:
break
if not check:
# Delete the config not present in want config
commands.extend(self._clear_config(acls_have, config_have))
# For configuring any non-existing want config
for config_want in temp_want:
for acls_want in config_want.get('acls'):
for ace_want in acls_want.get('aces'):
ace_want = remove_empties(ace_want)
cmd, change = self._set_config(ace_want,
{},
acls_want,
config_want['afi'])
commands.extend(cmd)
# Split and arrange the config commands
commands = self.split_set_cmd(commands)
# Arranging the cmds suct that all delete cmds are fired before all set cmds
negate_commands = [each for each in commands if 'no' in each and 'access-list' in each]
negate_commands.extend([each for each in commands if each not in negate_commands])
commands = negate_commands
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:param want: the additive configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
for config_want in want:
for acls_want in config_want.get('acls'):
for ace_want in acls_want.get('aces'):
check = False
for config_have in have:
for acls_have in config_have.get('acls'):
for ace_have in acls_have.get('aces'):
if acls_want.get('name') == acls_have.get('name') and \
ace_want.get('sequence') == ace_have.get('sequence'):
ace_want = remove_empties(ace_want)
cmd, change = self._set_config(ace_want,
ace_have,
acls_want,
config_want['afi'])
# clear config will be fired only when there's command wrt to config
if config_want.get('afi') == 'ipv4' and change:
# for ipv4 only inplace update cannot be done, so deleting the sequence ace
# and then updating the want ace changes
commands.extend(self._clear_config(acls_want,
config_want,
ace_want.get('sequence')))
commands.extend(cmd)
check = True
elif acls_want.get('name') == acls_have.get('name'):
ace_want = remove_empties(ace_want)
cmd, check = self.common_condition_check(ace_want,
ace_have,
acls_want,
config_want,
check,
acls_have)
if acls_have.get('acl_type') == 'standard':
check = True
commands.extend(cmd)
if check:
break
if check:
break
if not check:
# For configuring any non-existing want config
ace_want = remove_empties(ace_want)
cmd, change = self._set_config(ace_want,
{},
acls_want,
config_want['afi'])
commands.extend(cmd)
# Split and arrange the config commands
commands = self.split_set_cmd(commands)
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:param want: the objects from which the configuration should be removed
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
for config_want in want:
if config_want.get('acls'):
for acls_want in config_want.get('acls'):
if acls_want.get('aces'):
for ace_want in acls_want.get('aces'):
for config_have in have:
for acls_have in config_have.get('acls'):
if acls_want.get('name') == acls_have.get('name'):
if ace_want.get('sequence'):
commands.extend(self._clear_config(acls_want,
config_want,
ace_want.get('sequence')))
else:
commands.extend(self._clear_config(acls_want,
config_want))
else:
for config_have in have:
for acls_have in config_have.get('acls'):
if acls_want.get('name') == acls_have.get('name'):
commands.extend(self._clear_config(acls_want,
config_want))
else:
afi_want = config_want.get('afi')
for config_have in have:
if config_have.get('afi') == afi_want:
for acls_have in config_have.get('acls'):
commands.extend(self._clear_config(acls_have, config_want))
# Split and arrange the config commands
commands = self.split_set_cmd(commands)
else:
for config_have in have:
for acls_have in config_have.get('acls'):
commands.extend(self._clear_config(acls_have, config_have))
return commands
def common_condition_check(self, want, have, acls_want, config_want, check, state='', acls_have=None):
""" The command formatter from the generated command
:param want: want config
:param have: have config
:param acls_want: acls want config
:param config_want: want config list
:param check: for same acls in want and have config, check=True
:param state: operation state
:rtype: A list
:returns: commands generated from want n have config diff
"""
commands = []
if want.get('source') and want.get('destination') and have.get('source') and have.get('destination'):
if want.get('destination') and have.get('destination') or \
want.get('source').get('address') and have.get('source'):
if want.get('destination').get('address') == \
have.get('destination').get('address') and \
want.get('source').get('address') == \
have.get('source').get('address'):
cmd, change = self._set_config(want,
have,
acls_want,
config_want['afi'])
commands.extend(cmd)
check = True
if commands:
if state == 'replaced' or state == 'overridden':
commands.extend(self._clear_config(acls_want, config_want))
elif want.get('destination').get('any') == \
have.get('destination').get('any') and \
want.get('source').get('address') == \
have.get('source').get('address') and \
want.get('destination').get('any'):
cmd, change = self._set_config(want,
have,
acls_want,
config_want['afi'])
commands.extend(cmd)
check = True
if commands:
if state == 'replaced' or state == 'overridden':
commands.extend(self._clear_config(acls_want, config_want))
elif want.get('destination').get('address') == \
have.get('destination').get('address') and \
want.get('source').get('any') == have.get('source').get('any') and \
want.get('source').get('any'):
cmd, change = self._set_config(want,
have,
acls_want,
config_want['afi'])
commands.extend(cmd)
check = True
if commands:
if state == 'replaced' or state == 'overridden':
commands.extend(self._clear_config(acls_want, config_want))
elif want.get('destination').get('any') == \
have.get('destination').get('any') and \
want.get('source').get('any') == have.get('source').get('any') and \
want.get('destination').get('any'):
cmd, change = self._set_config(want,
have,
acls_want,
config_want['afi'])
commands.extend(cmd)
check = True
if commands:
if state == 'replaced' or state == 'overridden':
commands.extend(self._clear_config(acls_want, config_want))
elif acls_have and acls_have.get('acl_type') == 'standard':
check = True
if want.get('source') == have.get('source'):
cmd, change = self._set_config(want,
have,
acls_want,
config_want['afi'])
commands.extend(cmd)
return commands, check
def split_set_cmd(self, cmds):
""" The command formatter from the generated command
:param cmds: generated command
:rtype: A list
:returns: the formatted commands which is compliant and
actually fired on the device
"""
command = []
def common_code(access_grant, cmd, command):
cmd = cmd.split(access_grant)
access_list = cmd[0].strip(' ')
if access_list not in command:
command.append(access_list)
command_items = len(command)
# get the last index of the list and push the trimmed cmd at the end of list
index = command.index(access_list) + (command_items - command.index(access_list))
cmd = access_grant + cmd[1]
command.insert(index + 1, cmd)
def sequence_common_code(sequence_index, each_list, command):
# Command to split
def join_list_to_str(temp_list, cmd=''):
for item in temp_list:
cmd += item
cmd += ' '
return cmd
temp_list = each_list[:sequence_index]
cmd = join_list_to_str(temp_list).rstrip(' ')
if cmd not in command:
command.append(cmd)
temp_list = each_list[sequence_index:]
cmd = join_list_to_str(temp_list).rstrip(' ')
command.append(cmd)
def grant_common_code(cmd_list, grant_type, command):
index = cmd_list.index(grant_type)
if 'extended' in each_list:
if cmd_list.index('extended') == (index - 2):
common_code(grant_type, each, command)
else:
sequence_common_code((index - 1), each_list, command)
elif 'standard' in each_list:
if cmd_list.index('standard') == (index - 2):
common_code(grant_type, each, command)
else:
sequence_common_code((index - 1), each_list, command)
elif 'ipv6' in each_list:
if 'sequence' in each_list:
sequence_index = each_list.index('sequence')
sequence_common_code(sequence_index, each_list, command)
else:
common_code(grant_type, each, command)
return command
for each in cmds:
each_list = each.split(' ')
if 'no' in each:
if each_list.index('no') == 0:
command.append(each)
else:
common_code('no', each, command)
if 'deny' in each:
grant_common_code(each_list, 'deny', command)
if 'permit' in each:
grant_common_code(each_list, 'permit', command)
return command
def source_dest_config(self, config, cmd, protocol_option):
""" Function to populate source/destination address and port protocol options
:param config: want and have diff config
:param cmd: source/destination command
:param protocol_option: source/destination protocol option
:rtype: A list
:returns: the commands generated based on input source/destination params
"""
if 'ipv6' in cmd:
address = config.get('address')
host = config.get('host')
if (address and '::' not in address) or (host and '::' not in host):
self._module.fail_json(msg='Incorrect IPV6 address!')
else:
address = config.get('address')
wildcard = config.get('wildcard_bits')
host = config.get('host')
any = config.get('any')
if 'standard' in cmd and address and not wildcard:
cmd = cmd + ' {0}'.format(address)
elif address and wildcard:
cmd = cmd + ' {0} {1}'.format(address, wildcard)
elif host:
cmd = cmd + ' host {0}'.format(host)
if any:
cmd = cmd + ' {0}'.format('any')
port_protocol = config.get('port_protocol')
if port_protocol and (protocol_option.get('tcp') or protocol_option.get('udp')):
cmd = cmd + ' {0} {1}'.format(list(port_protocol)[0], list(port_protocol.values())[0])
elif port_protocol and not (protocol_option.get('tcp') or protocol_option.get('udp')):
self._module.fail_json(msg='Port Protocol option is valid only with TCP/UDP Protocol option!')
return cmd
def _set_config(self, want, have, acl_want, afi):
""" Function that sets the acls config based on the want and have config
:param want: want config
:param have: have config
:param acl_want: want acls config
:param afi: acls afi type
:rtype: A list
:returns: the commands generated based on input want/have params
"""
commands = []
change = False
want_set = set()
have_set = set()
# Convert the want and have dict to its respective set for taking the set diff
new_dict_to_set(want, [], want_set)
new_dict_to_set(have, [], have_set)
diff = want_set - have_set
# Populate the config only when there's a diff b/w want and have config
if diff:
name = acl_want.get('name')
if afi == 'ipv4':
try:
name = int(name)
# If name is numbered acls
if name <= 99:
cmd = 'ip access-list standard {0}'.format(name)
elif name >= 100:
cmd = 'ip access-list extended {0}'.format(name)
except ValueError:
# If name is named acls
acl_type = acl_want.get('acl_type')
if acl_type:
cmd = 'ip access-list {0} {1}'.format(acl_type, name)
else:
self._module.fail_json(msg='ACL type value is required for Named ACL!')
elif afi == 'ipv6':
cmd = 'ipv6 access-list {0}'.format(name)
# Get all of aces option values from diff dict
sequence = want.get('sequence')
grant = want.get('grant')
source = want.get('source')
destination = want.get('destination')
po = want.get('protocol_options')
protocol = want.get('protocol')
dscp = want.get('dscp')
fragments = want.get('fragments')
log = want.get('log')
log_input = want.get('log_input')
option = want.get('option')
precedence = want.get('precedence')
time_range = want.get('time_range')
tos = want.get('tos')
ttl = want.get('ttl')
if sequence:
if afi == 'ipv6':
cmd = cmd + ' sequence {0}'.format(sequence)
else:
cmd = cmd + ' {0}'.format(sequence)
if grant:
cmd = cmd + ' {0}'.format(grant)
if po and isinstance(po, dict):
po_key = list(po)[0]
if protocol and protocol != po_key:
self._module.fail_json(msg='Protocol value cannot be different from Protocol option protocol value!')
cmd = cmd + ' {0}'.format(po_key)
if po.get('icmp'):
po_val = po.get('icmp')
elif po.get('igmp'):
po_val = po.get('igmp')
elif po.get('tcp'):
po_val = po.get('tcp')
elif protocol:
cmd = cmd + ' {0}'.format(protocol)
if source:
cmd = self.source_dest_config(source, cmd, po)
if destination:
cmd = self.source_dest_config(destination, cmd, po)
if po:
cmd = cmd + ' {0}'.format(list(po_val)[0])
if dscp:
cmd = cmd + ' dscp {0}'.format(dscp)
if fragments:
cmd = cmd + ' fragments {0}'.format(fragments)
if log:
cmd = cmd + ' log {0}'.format(log)
if log_input:
cmd = cmd + ' log-input {0}'.format(log_input)
if option:
cmd = cmd + ' option {0}'.format(list(option)[0])
if precedence:
cmd = cmd + ' precedence {0}'.format(precedence)
if time_range:
cmd = cmd + ' time-range {0}'.format(time_range)
if tos:
for k, v in iteritems(tos):
if k == 'service_value':
cmd = cmd + ' tos {0}'.format(v)
else:
cmd = cmd + ' tos {0}'.format(v)
if ttl:
for k, v in iteritems(ttl):
if k == 'range' and v:
start = v.get('start')
end = v.get('start')
cmd = cmd + ' ttl {0} {1}'.format(start, end)
elif v:
cmd = cmd + ' ttl {0} {1}'.format(k, v)
commands.append(cmd)
if commands:
change = True
return commands, change
def _clear_config(self, acls, config, sequence=''):
""" Function that deletes the acls config based on the want and have config
:param acls: acls config
:param config: config
:rtype: A list
:returns: the commands generated based on input acls/config params
"""
commands = []
afi = config.get('afi')
name = acls.get('name')
if afi == 'ipv4' and name:
try:
name = int(name)
if name <= 99 and not sequence:
cmd = 'no ip access-list standard {0}'.format(name)
elif name >= 100 and not sequence:
cmd = 'no ip access-list extended {0}'.format(name)
elif sequence:
if name <= 99:
cmd = 'ip access-list standard {0} '.format(name)
elif name >= 100:
cmd = 'ip access-list extended {0} '.format(name)
cmd += 'no {0}'.format(sequence)
except ValueError:
acl_type = acls.get('acl_type')
if acl_type == 'extended' and not sequence:
cmd = 'no ip access-list extended {0}'.format(name)
elif acl_type == 'standard' and not sequence:
cmd = 'no ip access-list standard {0}'.format(name)
elif sequence:
if acl_type == 'extended':
cmd = 'ip access-list extended {0} '.format(name)
elif acl_type == 'standard':
cmd = 'ip access-list standard {0}'.format(name)
cmd += 'no {0}'.format(sequence)
else:
self._module.fail_json(msg="ACL type value is required for Named ACL!")
elif afi == 'ipv6' and name:
if sequence:
cmd = 'no sequence {0}'.format(sequence)
else:
cmd = 'no ipv6 access-list {0}'.format(name)
commands.append(cmd)
return commands

@ -1,295 +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 ios_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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import get_interface_type, dict_to_set
from ansible.module_utils.network.ios.utils.utils import remove_command_from_config_list, add_command_to_config_list
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
class Interfaces(ConfigBase):
"""
The ios_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 moduel 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 deisred 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 deisred 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
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:param interface_type: interface type
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the deisred configuration
"""
commands = []
for interface in want:
for each in have:
if each['name'] == interface['name']:
break
elif interface['name'] in each['name']:
break
else:
# configuring non-existing interface
commands.extend(self._set_config(interface, dict()))
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))
# 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
:param want: the desired configuration as a dictionary
:param obj_in_have: the current configuration as a dictionary
: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:
count = 0
if each['name'] == interface['name']:
break
elif interface['name'] in each['name']:
break
count += 1
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))
# as the pre-existing interface are now configured by
# above set_config call, deleting the respective
# interface entry from the want list
del want[count]
# Iterating through want list which now only have new interfaces to be
# configured
for each in want:
commands.extend(self._set_config(each, dict()))
# 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
:param want: the additive configuration as a dictionary
:param obj_in_have: the current configuration as a dictionary
: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
else:
# configuring non-existing interface
commands.extend(self._set_config(interface, dict()))
continue
commands.extend(self._set_config(interface, each))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:param want: the objects from which the configuration should be removed
:param obj_in_have: the current configuration as a dictionary
:param interface_type: interface type
: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
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,336 +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 ios_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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
from ansible.module_utils.network.ios.utils.utils import remove_command_from_config_list, add_command_to_config_list
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
class L2_Interfaces(ConfigBase):
"""
The ios_l2_interfaces class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'l2_interfaces',
]
access_cmds = {'access_vlan': 'switchport access vlan'}
voice_cmds = {'voice_vlan': 'switchport voice vlan'}
trunk_cmds = {'encapsulation': 'switchport trunk encapsulation', 'pruning_vlans': 'switchport trunk pruning vlan',
'native_vlan': 'switchport trunk native vlan', 'allowed_vlans': 'switchport trunk allowed vlan'}
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('l2_interfaces')
if not interfaces_facts:
return []
return interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from moduel execution
"""
result = {'changed': False}
commands = []
warnings = []
existing_facts = self.get_interfaces_facts()
commands.extend(self.set_config(existing_facts))
result['before'] = existing_facts
if commands:
if not self._module.check_mode:
self._connection.edit_config(commands)
result['changed'] = True
result['commands'] = commands
interfaces_facts = self.get_interfaces_facts()
if result['changed']:
result['after'] = interfaces_facts
result['warnings'] = warnings
return result
def set_config(self, existing_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 deisred configuration
"""
want = self._module.params['config']
have = existing_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 deisred 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
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:param interface_type: interface type
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the deisred configuration
"""
commands = []
for interface in want:
for each in have:
if each['name'] == interface['name']:
break
else:
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
:param want: the desired configuration as a dictionary
:param obj_in_have: the current configuration as a dictionary
: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
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))
# 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
:param want: the additive configuration as a dictionary
:param obj_in_have: the current configuration as a dictionary
: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
else:
continue
commands.extend(self._set_config(interface, each, module))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:param want: the objects from which the configuration should be removed
:param obj_in_have: the current configuration as a dictionary
:param interface_type: interface type
: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
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 _check_for_correct_vlan_range(self, vlan, module):
# Function to check if the VLAN range passed is Valid
for each in vlan:
vlan_range = each.split('-')
if len(vlan_range) > 1:
if vlan_range[0] < vlan_range[1]:
return True
else:
module.fail_json(msg='Command rejected: Bad VLAN list - end of range not larger than the'
' start of range!')
else:
return True
def _set_config(self, want, have, module):
# 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)
want_trunk = dict(want_dict).get('trunk')
have_trunk = dict(have_dict).get('trunk')
if want_trunk and have_trunk:
diff = set(tuple(dict(want_dict).get('trunk'))) - set(tuple(dict(have_dict).get('trunk')))
else:
diff = want_dict - have_dict
if diff:
diff = dict(diff)
mode = diff.get('mode')
access = diff.get('access')
trunk = diff.get('trunk')
if access:
cmd = 'switchport access vlan {0}'.format(access[0][1])
add_command_to_config_list(interface, cmd, commands)
if diff.get('voice'):
cmd = 'switchport voice vlan {0}'.format(diff.get('voice')[0][1])
add_command_to_config_list(interface, cmd, commands)
if want_trunk:
if trunk:
diff = dict(trunk)
if diff.get('encapsulation'):
cmd = self.trunk_cmds['encapsulation'] + ' {0}'.format(diff.get('encapsulation'))
add_command_to_config_list(interface, cmd, commands)
if diff.get('native_vlan'):
cmd = self.trunk_cmds['native_vlan'] + ' {0}'.format(diff.get('native_vlan'))
add_command_to_config_list(interface, cmd, commands)
allowed_vlans = diff.get('allowed_vlans')
pruning_vlans = diff.get('pruning_vlans')
if allowed_vlans and self._check_for_correct_vlan_range(allowed_vlans, module):
allowed_vlans = ','.join(allowed_vlans)
cmd = self.trunk_cmds['allowed_vlans'] + ' {0}'.format(allowed_vlans)
add_command_to_config_list(interface, cmd, commands)
if pruning_vlans and self._check_for_correct_vlan_range(pruning_vlans, module):
pruning_vlans = ','.join(pruning_vlans)
cmd = self.trunk_cmds['pruning_vlans'] + ' {0}'.format(pruning_vlans)
add_command_to_config_list(interface, cmd, commands)
if mode:
cmd = 'switchport mode {0}'.format(mode)
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('mode') or want.get('mode'):
remove_command_from_config_list(interface, 'switchport mode', commands)
if have.get('access') and want.get('access') is None:
remove_command_from_config_list(interface, L2_Interfaces.access_cmds['access_vlan'], commands)
elif have.get('access') and want.get('access'):
if have.get('access').get('vlan') != want.get('access').get('vlan'):
remove_command_from_config_list(interface, L2_Interfaces.access_cmds['access_vlan'], commands)
if have.get('voice') and want.get('voice') is None:
remove_command_from_config_list(interface, L2_Interfaces.voice_cmds['voice_vlan'], commands)
elif have.get('voice') and want.get('voice'):
if have.get('voice').get('vlan') != want.get('voice').get('vlan'):
remove_command_from_config_list(interface, L2_Interfaces.voice_cmds['voice_vlan'], commands)
if have.get('trunk') and want.get('trunk') is None:
# Check when no config is passed
if have.get('trunk').get('encapsulation'):
remove_command_from_config_list(interface, self.trunk_cmds['encapsulation'], commands)
if have.get('trunk').get('native_vlan'):
remove_command_from_config_list(interface, self.trunk_cmds['native_vlan'], commands)
if have.get('trunk').get('allowed_vlans'):
remove_command_from_config_list(interface, self.trunk_cmds['allowed_vlans'], commands)
if have.get('trunk').get('pruning_vlans'):
remove_command_from_config_list(interface, self.trunk_cmds['pruning_vlans'], commands)
elif have.get('trunk') and want.get('trunk'):
# Check when config is passed, also used in replaced and override state
if have.get('trunk').get('encapsulation')\
and have.get('trunk').get('encapsulation') != want.get('trunk').get('encapsulation'):
remove_command_from_config_list(interface, self.trunk_cmds['encapsulation'], commands)
if have.get('trunk').get('native_vlan') \
and have.get('trunk').get('native_vlan') != want.get('trunk').get('native_vlan'):
remove_command_from_config_list(interface, self.trunk_cmds['native_vlan'], commands)
if have.get('trunk').get('allowed_vlans') \
and have.get('trunk').get('allowed_vlans') != want.get('trunk').get('allowed_vlans'):
remove_command_from_config_list(interface, self.trunk_cmds['allowed_vlans'], commands)
if have.get('trunk').get('pruning_vlans') \
and have.get('trunk').get('pruning_vlans') != want.get('trunk').get('pruning_vlans'):
remove_command_from_config_list(interface, self.trunk_cmds['pruning_vlans'], commands)
return commands

@ -1,328 +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 ios_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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
from ansible.module_utils.network.ios.utils.utils import remove_command_from_config_list, add_command_to_config_list
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
from ansible.module_utils.network.ios.utils.utils import validate_n_expand_ipv4, validate_ipv6
class L3_Interfaces(ConfigBase):
"""
The ios_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:
for each in have:
if each['name'] == interface['name']:
break
else:
if '.' in interface['name']:
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 = []
for each in have:
for interface in want:
if each['name'] == interface['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'])
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))
# 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:
for each in have:
if each['name'] == interface['name']:
break
else:
if '.' in interface['name']:
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:
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('dhcp_client') != every_have.get('dhcp_client') and each_want.get(
'dhcp_client') is not None:
diff = True
break
elif each_want.get('dhcp_hostname') != every_have.get('dhcp_hostname') and each_want.get(
'dhcp_hostname') is not None:
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
# Convert the want and have dict to set
want_dict = dict_to_set(want)
have_dict = dict_to_set(have)
# To handle L3 IPV4 configuration
if want.get('ipv4'):
# Get the diff b/w want and have IPV4
if have.get('ipv4'):
ipv4 = tuple(set(dict(want_dict).get('ipv4')) - set(dict(have_dict).get('ipv4')))
if ipv4:
ipv4 = ipv4 if self.verify_diff_again(dict(want_dict).get('ipv4'), dict(have_dict).get('ipv4')) else ()
else:
diff = want_dict - have_dict
ipv4 = dict(diff).get('ipv4')
if ipv4:
for each in ipv4:
ipv4_dict = dict(each)
if ipv4_dict.get('address') != 'dhcp':
cmd = "ip address {0}".format(ipv4_dict['address'])
if ipv4_dict.get("secondary"):
cmd += " secondary"
elif ipv4_dict.get('address') == 'dhcp':
cmd = "ip address dhcp"
if ipv4_dict.get('dhcp_client') is not None and ipv4_dict.get('dhcp_hostname'):
cmd = "ip address dhcp client-id GigabitEthernet 0/{0} hostname {1}"\
.format(ipv4_dict.get('dhcp_client'), ipv4_dict.get('dhcp_hostname'))
elif ipv4_dict.get('dhcp_client') and not ipv4_dict.get('dhcp_hostname'):
cmd = "ip address dhcp client-id GigabitEthernet 0/{0}"\
.format(ipv4_dict.get('dhcp_client'))
elif not ipv4_dict.get('dhcp_client') and ipv4_dict.get('dhcp_hostname'):
cmd = "ip address dhcp hostname {0}".format(ipv4_dict.get('dhcp_client'))
add_command_to_config_list(interface, cmd, commands)
# To handle L3 IPV6 configuration
if want.get('ipv6'):
# Get the diff b/w want and have IPV6
if have.get('ipv6'):
ipv6 = tuple(set(dict(want_dict).get('ipv6')) - set(dict(have_dict).get('ipv6')))
else:
diff = want_dict - have_dict
ipv6 = dict(diff).get('ipv6')
if ipv6:
for each in 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, 'ip address', commands)
if have.get('ipv6') and not want.get('ipv6'):
remove_command_from_config_list(interface, 'ipv6 address', commands)
return commands

@ -1,189 +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 ios_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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
class Lacp(ConfigBase):
"""
The ios_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['config']
have = existing_lacp_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
state = self._module.params['state']
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._set_config(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 = []
commands.extend(self._set_config(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 want:
commands.extend(self._clear_config(have))
else:
commands.extend(self._clear_config(have))
return commands
def _remove_command_from_config_list(self, cmd, commands):
commands.append('no %s' % cmd)
return commands
def _add_command_to_config_list(self, cmd, commands):
if cmd not in commands:
commands.append(cmd)
def _set_config(self, want, have):
# Set the interface config based on the want and have config
commands = []
want_dict = dict_to_set(want)
have_dict = dict_to_set(have)
diff = want_dict - have_dict
if diff:
cmd = 'lacp system-priority {0}'.format(want.get('system').get('priority'))
self._add_command_to_config_list(cmd, commands)
return commands
def _clear_config(self, have):
# Delete the interface config based on the want and have config
commands = []
if have.get('system').get('priority') and have.get('system').get('priority') != 32768:
cmd = 'lacp system-priority'
self._remove_command_from_config_list(cmd, commands)
return commands

@ -1,260 +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 ios_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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
from ansible.module_utils.network.ios.utils.utils import remove_command_from_config_list, add_command_to_config_list
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
class Lacp_Interfaces(ConfigBase):
"""
The ios_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 = 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
else:
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))
# 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
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))
# 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 interface['name'] == 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
else:
continue
interface = dict(name=interface['name'])
commands.extend(self._clear_config(interface, each))
else:
for each in have:
commands.extend(self._clear_config(dict(), each))
return commands
def _set_config(self, want, have):
# Set the interface config based on the want and have config
commands = []
interface = 'interface ' + have['name']
want_dict = dict_to_set(want)
have_dict = dict_to_set(have)
diff = want_dict - have_dict
if diff:
port_priotity = dict(diff).get('port_priority')
max_bundle = dict(diff).get('max_bundle')
fast_switchover = dict(diff).get('fast_switchover')
if port_priotity:
cmd = 'lacp port-priority {0}'.format(port_priotity)
add_command_to_config_list(interface, cmd, commands)
if max_bundle:
cmd = 'lacp max-bundle {0}'.format(max_bundle)
add_command_to_config_list(interface, cmd, commands)
if fast_switchover:
cmd = 'lacp fast-switchover'
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('port_priority') and have.get('port_priority') != want.get('port_priority'):
cmd = 'lacp port-priority'
remove_command_from_config_list(interface, cmd, commands)
if have.get('max_bundle') and have.get('max_bundle') != want.get('max_bundle'):
cmd = 'lacp max-bundle'
remove_command_from_config_list(interface, cmd, commands)
if have.get('fast_switchover'):
cmd = 'lacp fast-switchover'
remove_command_from_config_list(interface, cmd, commands)
return commands

@ -1,296 +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 ios_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
import re
from ansible.module_utils.network.common import utils
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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
class Lag_interfaces(ConfigBase):
"""
The ios_lag_interfaces class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'lag_interfaces',
]
def __init__(self, module):
super(Lag_interfaces, self).__init__(module)
def get_lag_interfaces_facts(self):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
lag_interfaces_facts = facts['ansible_network_resources'].get('lag_interfaces')
if not lag_interfaces_facts:
return []
return lag_interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {'changed': False}
commands = list()
warnings = list()
existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
commands.extend(self.set_config(existing_lag_interfaces_facts))
if commands:
if not self._module.check_mode:
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']
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']
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))
module = self._module
if state == 'overridden':
commands = self._state_overridden(want, have, module)
elif state == 'deleted':
commands = self._state_deleted(want, have)
elif state == 'merged':
commands = self._state_merged(want, have, module)
elif state == 'replaced':
commands = self._state_replaced(want, have, 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:
for each_interface in interface.get('members'):
for each in have:
if each.get('members'):
for every in each.get('members'):
match = False
if every['member'] == each_interface['member']:
match = True
break
else:
continue
if match:
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))
elif each.get('name') == each_interface['member']:
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))
break
# 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 = []
for interface in want:
for each_interface in interface.get('members'):
for each in have:
if each.get('members'):
for every in each.get('members'):
match = False
if every['member'] == each_interface['member']:
match = True
break
else:
commands.extend(self._clear_config(interface, each))
continue
if match:
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))
elif each.get('name') == each_interface['member']:
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))
break
# 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:
for each_interface in interface.get('members'):
for each in have:
if each.get('members'):
for every in each.get('members'):
if every['member'] == each_interface['member']:
break
elif each.get('name') == each_interface['member']:
break
else:
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:
for each in have:
if each.get('name') == interface['name']:
break
else:
continue
commands.extend(self._clear_config(interface, each))
else:
for each in have:
commands.extend(self._clear_config(dict(), each))
return commands
def remove_command_from_config_list(self, interface, cmd, commands):
# To delete the passed config
if interface not in commands:
commands.append(interface)
commands.append('no %s' % cmd)
return commands
def add_command_to_config_list(self, interface, cmd, commands):
# To set the passed config
if interface not in commands:
commands.append(interface)
commands.append(cmd)
return commands
def _set_config(self, want, have, module):
# Set the interface config based on the want and have config
commands = []
# To remove keys with None values from want dict
want = utils.remove_empties(want)
# 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
# To get the channel-id from lag port-channel name
lag_config = dict(diff).get('members')
channel_name = re.search(r'(\d+)', want.get('name'))
if channel_name:
channel_id = channel_name.group()
else:
module.fail_json(msg="Lag Interface Name is not correct!")
if lag_config:
for each in lag_config:
each = dict(each)
each_interface = 'interface {0}'.format(each.get('member'))
if have.get('name') == want['members'][0]['member'] or have.get('name').lower().startswith('po'):
if each.get('mode'):
cmd = 'channel-group {0} mode {1}'.format(channel_id, each.get('mode'))
self.add_command_to_config_list(each_interface, cmd, commands)
elif each.get('link'):
cmd = 'channel-group {0} link {1}'.format(channel_id, each.get('link'))
self.add_command_to_config_list(each_interface, cmd, commands)
return commands
def _clear_config(self, want, have):
# Delete the interface config based on the want and have config
commands = []
if have.get('members'):
for each in have['members']:
interface = 'interface ' + each['member']
if want.get('members'):
if each.get('member') and each.get('member') != want['members'][0]['member']:
self.remove_command_from_config_list(interface, 'channel-group', commands)
elif each.get('member'):
self.remove_command_from_config_list(interface, 'channel-group', commands)
return commands

@ -1,238 +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 ios_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 its desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.module_utils.six import iteritems
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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value
class Lldp_global(ConfigBase):
"""
The ios_lldp_global class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'lldp_global',
]
tlv_select_params = {'four_wire_power_management': '4-wire-power-management', 'mac_phy_cfg': 'mac-phy-cfg',
'management_address': 'management-address', 'port_description': 'port-description',
'port_vlan': 'port-vlan', 'power_management': 'power-management',
'system_capabilities': 'system-capabilities', 'system_description': 'system-description',
'system_name': 'system-name'}
def __init__(self, module):
super(Lldp_global, self).__init__(module)
def get_lldp_global_facts(self):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
lldp_global_facts = facts['ansible_network_resources'].get('lldp_global')
if not lldp_global_facts:
return {}
return lldp_global_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from moduel execution
"""
result = {'changed': False}
commands = list()
warnings = list()
existing_lldp_global_facts = self.get_lldp_global_facts()
commands.extend(self.set_config(existing_lldp_global_facts))
if commands:
if not self._module.check_mode:
self._connection.edit_config(commands)
result['changed'] = True
result['commands'] = commands
changed_lldp_global_facts = self.get_lldp_global_facts()
result['before'] = 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 deisred configuration
"""
want = self._module.params['config']
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 deisred configuration
"""
commands = []
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 == '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
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:param interface_type: interface type
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the deisred configuration
"""
commands = []
have_dict = filter_dict_having_none_value(want, have)
commands.extend(self._clear_config(have_dict))
commands.extend(self._set_config(want, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:param want: the additive configuration as a dictionary
:param obj_in_have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
commands.extend(self._set_config(want, have))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:param want: the objects from which the configuration should be removed
:param obj_in_have: the current configuration as a dictionary
:param interface_type: interface type
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
commands.extend(self._clear_config(have))
return commands
def _remove_command_from_config_list(self, cmd, commands):
if cmd not in commands:
commands.append('no %s' % cmd)
def add_command_to_config_list(self, cmd, commands):
if cmd not in commands:
commands.append(cmd)
def _set_config(self, want, have):
# Set the interface config based on the want and have config
commands = []
# 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)
holdtime = diff.get('holdtime')
enabled = diff.get('enabled')
timer = diff.get('timer')
reinit = diff.get('reinit')
tlv_select = diff.get('tlv_select')
if holdtime:
cmd = 'lldp holdtime {0}'.format(holdtime)
self.add_command_to_config_list(cmd, commands)
if enabled:
cmd = 'lldp run'
self.add_command_to_config_list(cmd, commands)
if timer:
cmd = 'lldp timer {0}'.format(timer)
self.add_command_to_config_list(cmd, commands)
if reinit:
cmd = 'lldp reinit {0}'.format(reinit)
self.add_command_to_config_list(cmd, commands)
if tlv_select:
tlv_selec_dict = dict(tlv_select)
for k, v in iteritems(self.tlv_select_params):
if k in tlv_selec_dict and tlv_selec_dict[k]:
cmd = 'lldp tlv-select {0}'.format(v)
self.add_command_to_config_list(cmd, commands)
return commands
def _clear_config(self, have):
# Delete the interface config based on the want and have config
commands = []
if have.get('holdtime'):
cmd = 'lldp holdtime'
self._remove_command_from_config_list(cmd, commands)
if have.get('enabled'):
cmd = 'lldp run'
self._remove_command_from_config_list(cmd, commands)
if have.get('timer'):
cmd = 'lldp timer'
self._remove_command_from_config_list(cmd, commands)
if have.get('reinit'):
cmd = 'lldp reinit'
self._remove_command_from_config_list(cmd, commands)
return commands

@ -1,270 +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 ios_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 its 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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
from ansible.module_utils.network.ios.utils.utils import remove_command_from_config_list, add_command_to_config_list
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
class Lldp_Interfaces(ConfigBase):
"""
The ios_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}
commands = list()
warnings = 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']
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
else:
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))
# 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
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))
# 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 interface['name'] == 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
else:
continue
interface = dict(name=interface['name'])
commands.extend(self._clear_config(interface, each))
else:
for each in have:
commands.extend(self._clear_config(dict(), each))
return commands
def _set_config(self, want, have):
# Set the interface config based on the want and have config
commands = []
interface = 'interface ' + have['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)
receive = diff.get('receive')
transmit = diff.get('transmit')
med_tlv_select = diff.get('med_tlv_select')
tlv_select = diff.get('tlv_select')
if receive:
cmd = 'lldp receive'
add_command_to_config_list(interface, cmd, commands)
elif receive is False:
cmd = 'no lldp receive'
add_command_to_config_list(interface, cmd, commands)
if transmit:
cmd = 'lldp transmit'
add_command_to_config_list(interface, cmd, commands)
elif transmit is False:
cmd = 'no lldp transmit'
add_command_to_config_list(interface, cmd, commands)
if med_tlv_select:
med_tlv_select = dict(med_tlv_select)
if med_tlv_select.get('inventory_management'):
add_command_to_config_list(interface, 'lldp med-tlv-select inventory-management', commands)
if tlv_select:
tlv_select = dict(tlv_select)
if tlv_select.get('power_management'):
add_command_to_config_list(interface, 'lldp tlv-select power-management', 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('receive') and have.get('receive') != want.get('receive'):
cmd = 'lldp receive'
remove_command_from_config_list(interface, cmd, commands)
if have.get('transmit') and have.get('transmit') != want.get('transmit'):
cmd = 'lldp transmit'
remove_command_from_config_list(interface, cmd, commands)
return commands

@ -1,532 +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 ios_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
import copy
from ansible.module_utils.six import iteritems
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.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import new_dict_to_set, validate_n_expand_ipv4, filter_dict_having_none_value
class Static_Routes(ConfigBase):
"""
The ios_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}
commands = list()
warnings = 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)
else:
changed_static_routes_facts = []
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']
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))
commands = []
if state == 'overridden':
commands = self._state_overridden(want, have)
elif state == 'deleted':
commands = self._state_deleted(want, have)
elif state == 'merged' or state == 'rendered':
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 = []
# Drill each iteration of want n have and then based on dest and afi tyoe comparison take config call
for w in want:
for addr_want in w.get('address_families'):
for route_want in addr_want.get('routes'):
check = False
for h in have:
if h.get('address_families'):
for addr_have in h.get('address_families'):
for route_have in addr_have.get('routes'):
if route_want.get('dest') == route_have.get('dest')\
and addr_want['afi'] == addr_have['afi']:
check = True
have_set = set()
new_hops = []
for each in route_want.get('next_hops'):
want_set = set()
new_dict_to_set(each, [], want_set, 0)
new_hops.append(want_set)
new_dict_to_set(addr_have, [], have_set, 0)
# Check if the have dict next_hops value is diff from want dict next_hops
have_dict = filter_dict_having_none_value(route_want.get('next_hops')[0],
route_have.get('next_hops')[0])
# update the have_dict with forward_router_address
have_dict.update({'forward_router_address': route_have.get('next_hops')[0].
get('forward_router_address')})
# updating the have_dict with next_hops val that's not None
new_have_dict = {}
for k, v in have_dict.items():
if v is not None:
new_have_dict.update({k: v})
# Set the new config from the user provided want config
cmd = self._set_config(w, h, addr_want, route_want, route_have, new_hops, have_set)
if cmd:
# since inplace update isn't allowed for static routes, preconfigured
# static routes needs to be deleted before the new want static routes changes
# are applied
clear_route_have = copy.deepcopy(route_have)
# inplace update is allowed in case of ipv6 static routes, so not deleting it
# before applying the want changes
if ':' not in route_want.get('dest'):
commands.extend(self._clear_config({}, h, {}, addr_have,
{}, clear_route_have))
commands.extend(cmd)
if check:
break
if check:
break
if not check:
# For configuring any non-existing want config
new_hops = []
for each in route_want.get('next_hops'):
want_set = set()
new_dict_to_set(each, [], want_set, 0)
new_hops.append(want_set)
commands.extend(self._set_config(w, {}, addr_want, route_want, {}, new_hops, set()))
commands = [each for each in commands if 'no' in each] + \
[each for each in commands if 'no' not in each]
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 = []
# Creating a copy of want, so that want dict is intact even after delete operation
# performed during override want n have comparison
temp_want = copy.deepcopy(want)
# Drill each iteration of want n have and then based on dest and afi tyoe comparison take config call
for h in have:
if h.get('address_families'):
for addr_have in h.get('address_families'):
for route_have in addr_have.get('routes'):
check = False
for w in temp_want:
for addr_want in w.get('address_families'):
count = 0
for route_want in addr_want.get('routes'):
if route_want.get('dest') == route_have.get('dest') \
and addr_want['afi'] == addr_have['afi']:
check = True
have_set = set()
new_hops = []
for each in route_want.get('next_hops'):
want_set = set()
new_dict_to_set(each, [], want_set, 0)
new_hops.append(want_set)
new_dict_to_set(addr_have, [], have_set, 0)
commands.extend(self._clear_config(w, h, addr_want, addr_have,
route_want, route_have))
commands.extend(self._set_config(w, h, addr_want,
route_want, route_have, new_hops, have_set))
del addr_want.get('routes')[count]
count += 1
if check:
break
if check:
break
if not check:
commands.extend(self._clear_config({}, h, {}, addr_have, {}, route_have))
# For configuring any non-existing want config
for w in temp_want:
for addr_want in w.get('address_families'):
for route_want in addr_want.get('routes'):
new_hops = []
for each in route_want.get('next_hops'):
want_set = set()
new_dict_to_set(each, [], want_set, 0)
new_hops.append(want_set)
commands.extend(self._set_config(w, {}, addr_want, route_want, {}, new_hops, set()))
# Arranging the cmds suct that all delete cmds are fired before all set cmds
commands = [each for each in sorted(commands) if 'no' in each] + \
[each for each in sorted(commands) if 'no' not in each]
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 = []
# Drill each iteration of want n have and then based on dest and afi tyoe comparison take config call
for w in want:
for addr_want in w.get('address_families'):
for route_want in addr_want.get('routes'):
check = False
for h in have:
if h.get('address_families'):
for addr_have in h.get('address_families'):
for route_have in addr_have.get('routes'):
if route_want.get('dest') == route_have.get('dest')\
and addr_want['afi'] == addr_have['afi']:
check = True
have_set = set()
new_hops = []
for each in route_want.get('next_hops'):
want_set = set()
new_dict_to_set(each, [], want_set, 0)
new_hops.append(want_set)
new_dict_to_set(addr_have, [], have_set, 0)
commands.extend(self._set_config(w, h, addr_want,
route_want, route_have, new_hops, have_set))
if check:
break
if check:
break
if not check:
# For configuring any non-existing want config
new_hops = []
for each in route_want.get('next_hops'):
want_set = set()
new_dict_to_set(each, [], want_set, 0)
new_hops.append(want_set)
commands.extend(self._set_config(w, {}, addr_want, route_want, {}, new_hops, set()))
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:
# Drill each iteration of want n have and then based on dest and afi type comparison fire delete config call
for w in want:
if w.get('address_families'):
for addr_want in w.get('address_families'):
for route_want in addr_want.get('routes'):
check = False
for h in have:
if h.get('address_families'):
for addr_have in h.get('address_families'):
for route_have in addr_have.get('routes'):
if route_want.get('dest') == route_have.get('dest') \
and addr_want['afi'] == addr_have['afi']:
check = True
if route_want.get('next_hops'):
commands.extend(self._clear_config({}, w, {}, addr_want, {}, route_want))
else:
commands.extend(self._clear_config({}, h, {}, addr_have, {}, route_have))
if check:
break
if check:
break
else:
for h in have:
for addr_have in h.get('address_families'):
for route_have in addr_have.get('routes'):
if w.get('vrf') == h.get('vrf'):
commands.extend(self._clear_config({}, h, {}, addr_have, {}, route_have))
else:
# Drill each iteration of have and then based on dest and afi type comparison fire delete config call
for h in have:
for addr_have in h.get('address_families'):
for route_have in addr_have.get('routes'):
commands.extend(self._clear_config({}, h, {}, addr_have, {}, route_have))
return commands
def prepare_config_commands(self, config_dict, cmd):
"""
function to parse the input dict and form the prepare the config commands
:rtype: A str
:returns: The command necessary to configure the static routes
"""
dhcp = config_dict.get('dhcp')
distance_metric = config_dict.get('distance_metric')
forward_router_address = config_dict.get('forward_router_address')
global_route_config = config_dict.get('global')
interface = config_dict.get('interface')
multicast = config_dict.get('multicast')
name = config_dict.get('name')
permanent = config_dict.get('permanent')
tag = config_dict.get('tag')
track = config_dict.get('track')
dest = config_dict.get('dest')
temp_dest = dest.split('/')
if temp_dest and ':' not in dest:
dest = validate_n_expand_ipv4(self._module, {'address': dest})
cmd = cmd + dest
if interface:
cmd = cmd + ' {0}'.format(interface)
if forward_router_address:
cmd = cmd + ' {0}'.format(forward_router_address)
if dhcp:
cmd = cmd + ' DHCP'
if distance_metric:
cmd = cmd + ' {0}'.format(distance_metric)
if global_route_config:
cmd = cmd + ' global'
if multicast:
cmd = cmd + ' multicast'
if name:
cmd = cmd + ' name {0}'.format(name)
if permanent:
cmd = cmd + ' permanent'
elif track:
cmd = cmd + ' track {0}'.format(track)
if tag:
cmd = cmd + ' tag {0}'.format(tag)
return cmd
def _set_config(self, want, have, addr_want, route_want, route_have, hops, have_set):
"""
Set the interface config based on the want and have config
:rtype: A list
:returns: The commands necessary to configure the static routes
"""
commands = []
cmd = None
vrf_diff = False
topology_diff = False
want_vrf = want.get('vrf')
have_vrf = have.get('vrf')
if want_vrf != have_vrf:
vrf_diff = True
want_topology = want.get('topology')
have_topology = have.get('topology')
if want_topology != have_topology:
topology_diff = True
have_dest = route_have.get('dest')
if have_dest:
have_set.add(tuple(iteritems({'dest': have_dest})))
# configure set cmd for each hops under the same destination
for each in hops:
diff = each - have_set
if vrf_diff:
each.add(tuple(iteritems({'vrf': want_vrf})))
if topology_diff:
each.add(tuple(iteritems({'topology': want_topology})))
if diff or vrf_diff or topology_diff:
if want_vrf and not vrf_diff:
each.add(tuple(iteritems({'vrf': want_vrf})))
if want_topology and not vrf_diff:
each.add(tuple(iteritems({'topology': want_topology})))
each.add(tuple(iteritems({'afi': addr_want.get('afi')})))
each.add(tuple(iteritems({'dest': route_want.get('dest')})))
temp_want = {}
for each_want in each:
temp_want.update(dict(each_want))
if temp_want.get('afi') == 'ipv4':
cmd = 'ip route '
vrf = temp_want.get('vrf')
if vrf:
cmd = cmd + 'vrf {0} '.format(vrf)
cmd = self.prepare_config_commands(temp_want, cmd)
elif temp_want.get('afi') == 'ipv6':
cmd = 'ipv6 route '
cmd = self.prepare_config_commands(temp_want, cmd)
commands.append(cmd)
return commands
def _clear_config(self, want, have, addr_want, addr_have, route_want, route_have):
"""
Delete the interface config based on the want and have config
:rtype: A list
:returns: The commands necessary to configure the static routes
"""
commands = []
cmd = None
vrf_diff = False
topology_diff = False
want_vrf = want.get('vrf')
have_vrf = have.get('vrf')
if want_vrf != have_vrf:
vrf_diff = True
want_topology = want.get('topology')
have_topology = have.get('topology')
if want_topology != have_topology:
topology_diff = True
want_set = set()
new_dict_to_set(addr_want, [], want_set, 0)
have_hops = []
for each in route_have.get('next_hops'):
temp_have_set = set()
new_dict_to_set(each, [], temp_have_set, 0)
have_hops.append(temp_have_set)
# configure delete cmd for each hops under the same destination
for each in have_hops:
diff = each - want_set
if vrf_diff:
each.add(tuple(iteritems({'vrf': have_vrf})))
if topology_diff:
each.add(tuple(iteritems({'topology': want_topology})))
if diff or vrf_diff or topology_diff:
if want_vrf and not vrf_diff:
each.add(tuple(iteritems({'vrf': want_vrf})))
if want_topology and not vrf_diff:
each.add(tuple(iteritems({'topology': want_topology})))
if addr_want:
each.add(tuple(iteritems({'afi': addr_want.get('afi')})))
else:
each.add(tuple(iteritems({'afi': addr_have.get('afi')})))
if route_want:
each.add(tuple(iteritems({'dest': route_want.get('dest')})))
else:
each.add(tuple(iteritems({'dest': route_have.get('dest')})))
temp_want = {}
for each_want in each:
temp_want.update(dict(each_want))
if temp_want.get('afi') == 'ipv4':
cmd = 'no ip route '
vrf = temp_want.get('vrf')
if vrf:
cmd = cmd + 'vrf {0} '.format(vrf)
cmd = self.prepare_config_commands(temp_want, cmd)
elif temp_want.get('afi') == 'ipv6':
cmd = 'no ipv6 route '
cmd = self.prepare_config_commands(temp_want, cmd)
commands.append(cmd)
return commands

@ -1,292 +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 ios_vlans class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.module_utils.network.common.cfg.base import ConfigBase
from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.network.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
class Vlans(ConfigBase):
"""
The ios_vlans class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'vlans',
]
def __init__(self, module):
super(Vlans, 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('vlans')
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
"""
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, state)
elif state == 'deleted':
commands = self._state_deleted(want, have, state)
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 = []
check = False
for each in want:
for every in have:
if every['vlan_id'] == each['vlan_id']:
check = True
break
else:
continue
if check:
commands.extend(self._set_config(each, every))
else:
commands.extend(self._set_config(each, dict()))
return commands
def _state_overridden(self, want, have, state):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
want_local = want
for each in have:
count = 0
for every in want_local:
if each['vlan_id'] == every['vlan_id']:
break
count += 1
else:
# We didn't find a matching desired state, which means we can
# pretend we received an empty desired state.
commands.extend(self._clear_config(every, each, state))
continue
commands.extend(self._set_config(every, each))
# as the pre-existing VLAN are now configured by
# above set_config call, deleting the respective
# VLAN entry from the want_local list
del want_local[count]
# Iterating through want_local list which now only have new VLANs to be
# configured
for each in want_local:
commands.extend(self._set_config(each, dict()))
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 = []
check = False
for each in want:
for every in have:
if each.get('vlan_id') == every.get('vlan_id'):
check = True
break
else:
continue
if check:
commands.extend(self._set_config(each, every))
else:
commands.extend(self._set_config(each, dict()))
return commands
def _state_deleted(self, want, have, state):
""" 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:
check = False
for each in want:
for every in have:
if each.get('vlan_id') == every.get('vlan_id'):
check = True
break
else:
check = False
continue
if check:
commands.extend(self._clear_config(each, every, state))
else:
for each in have:
commands.extend(self._clear_config(dict(), each, state))
return commands
def remove_command_from_config_list(self, vlan, cmd, commands):
if vlan not in commands and cmd != 'vlan':
commands.insert(0, vlan)
elif cmd == 'vlan':
commands.append('no %s' % vlan)
return commands
commands.append('no %s' % cmd)
return commands
def add_command_to_config_list(self, vlan_id, cmd, commands):
if vlan_id not in commands:
commands.insert(0, vlan_id)
if cmd not in commands:
commands.append(cmd)
def _set_config(self, want, have):
# Set the interface config based on the want and have config
commands = []
vlan = 'vlan {0}'.format(want.get('vlan_id'))
# Get the diff b/w want n have
want_dict = dict_to_set(want)
have_dict = dict_to_set(have)
diff = want_dict - have_dict
if diff:
name = dict(diff).get('name')
state = dict(diff).get('state')
shutdown = dict(diff).get('shutdown')
mtu = dict(diff).get('mtu')
remote_span = dict(diff).get('remote_span')
if name:
cmd = 'name {0}'.format(name)
self.add_command_to_config_list(vlan, cmd, commands)
if state:
cmd = 'state {0}'.format(state)
self.add_command_to_config_list(vlan, cmd, commands)
if mtu:
cmd = 'mtu {0}'.format(mtu)
self.add_command_to_config_list(vlan, cmd, commands)
if remote_span:
self.add_command_to_config_list(vlan, 'remote-span', commands)
if shutdown == 'enabled':
self.add_command_to_config_list(vlan, 'shutdown', commands)
elif shutdown == 'disabled':
self.add_command_to_config_list(vlan, 'no shutdown', commands)
return commands
def _clear_config(self, want, have, state):
# Delete the interface config based on the want and have config
commands = []
vlan = 'vlan {0}'.format(have.get('vlan_id'))
if have.get('vlan_id') and 'default' not in have.get('name')\
and (have.get('vlan_id') != want.get('vlan_id') or state == 'deleted'):
self.remove_command_from_config_list(vlan, 'vlan', commands)
elif 'default' not in have.get('name'):
if have.get('mtu') != want.get('mtu'):
self.remove_command_from_config_list(vlan, 'mtu', commands)
if have.get('remote_span') != want.get('remote_span') and want.get('remote_span'):
self.remove_command_from_config_list(vlan, 'remote-span', commands)
if have.get('shutdown') != want.get('shutdown') and want.get('shutdown'):
self.remove_command_from_config_list(vlan, 'shutdown', commands)
if have.get('state') != want.get('state') and want.get('state'):
self.remove_command_from_config_list(vlan, 'state', commands)
return commands

@ -1,122 +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 ios_acl_interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import re
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.ios.utils.utils import get_interface_type
from ansible.module_utils.network.ios.argspec.acl_interfaces.acl_interfaces import Acl_InterfacesArgs
class Acl_InterfacesFacts(object):
""" The ios_acl_interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = Acl_InterfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def get_acl_interfaces_data(self, connection):
return connection.get('sh running-config | include interface|ip access-group|ipv6 traffic-filter')
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 = self.get_acl_interfaces_data(connection)
# 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['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)
match = re.search(r'^(\S+)', conf)
intf = match.group(1)
if get_interface_type(intf) == 'unknown':
return {}
config['name'] = intf
config['access_groups'] = []
acl_v4_config = {}
acl_v6_config = {}
def common_iter_code(cmd, conf):
# Common code for IPV4 and IPV6 config parsing
acls = []
re_cmd = cmd + ' (\\S+.*)'
ip_all = re.findall(re_cmd, conf)
for each in ip_all:
acl = {}
access_grp_config = each.split(' ')
acl['name'] = access_grp_config[0]
acl['direction'] = access_grp_config[1]
acls.append(acl)
return acls
if 'ip' in conf:
acls = common_iter_code('ip access-group', conf)
acl_v4_config['afi'] = 'ipv4'
acl_v4_config['acls'] = acls
config['access_groups'].append(acl_v4_config)
if 'ipv6' in conf:
acls = common_iter_code('ipv6 traffic-filter', conf)
acl_v6_config['afi'] = 'ipv6'
acl_v6_config['acls'] = acls
config['access_groups'].append(acl_v6_config)
return utils.remove_empties(config)

@ -1,498 +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 ios_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
import re
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.ios.utils.utils import check_n_return_valid_ipv6_addr
from ansible.module_utils.network.ios.argspec.acls.acls import AclsArgs
class AclsFacts(object):
""" The ios_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_acl_data(self, connection):
# Get the access-lists from the ios router
return connection.get('sh access-list')
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for acls
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = self.get_acl_data(connection)
# operate on a collection of resource x
config = data.split('\n')
spec = {'acls': list(), 'afi': None}
if config:
objs = self.render_config(spec, config)
# check if rendered config list has only empty dict
if len(objs) == 1 and objs[0] == {}:
objs = []
facts = {}
if objs:
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 create_config_dict(self, config):
""" Function that parse the acls config and convert to module usable config
:param config: config
:rtype: A dict
:returns: the config generated based on have config params
"""
conf = {}
temp_list = []
access_list_name = ''
count = 0
if len(config) >= 1 and config[0] != '':
for each in config:
if 'access-list' in each:
temp = each.split('access-list ')[1].split(' ')[0]
if temp == 'extended' or temp == 'standard':
temp = each.split('access-list ')[1]
if not access_list_name:
access_list_name = temp
if 'access-list' not in each:
if 'extended' in temp or 'standard' in temp:
temp_list.append('ipv4 access-list ' + temp + each)
else:
temp_list.append('ipv6 access-list ' + temp + each)
if temp == access_list_name and 'access-list' in each and \
not ('extended' in access_list_name or 'standard' in access_list_name):
temp_list.append(each)
elif temp != access_list_name:
conf[access_list_name] = temp_list
temp_list = list()
if 'permit' in each or 'deny' in each:
temp_list.append(each)
access_list_name = temp
count += 1
if len(config) == count:
conf[access_list_name] = temp_list
temp_list = []
return conf
def populate_port_protocol(self, source, destination, each_list):
""" Function Populates port portocol wrt to source and destination
:param acls: source config
:param config: destination config
:param each_list: config
:rtype: A list
:returns: the commands generated based on source and destination params
"""
operators = ['eq', 'gt', 'lt', 'neq', 'range']
for item in operators:
if item in each_list:
index = each_list.index(item)
if source.get('address') or source.get('any') or source.get('host') and not source.get('port_protocol'):
try:
source_index = each_list.index(source.get('address'))
except ValueError:
try:
source_index = each_list.index('any')
except ValueError:
source_index = each_list.index('host')
if source.get('address'):
if (source_index + 2) == index and 'ipv6' not in each_list:
source['port_protocol'] = {item: each_list[index + 1]}
each_list.remove(item)
del each_list[index]
elif (source_index + 1) == index and 'ipv6' in each_list:
source['port_protocol'] = {item: each_list[index + 1]}
each_list.remove(item)
del each_list[source_index]
del each_list[index - 1]
elif source.get('any'):
if (source_index + 1) == index:
source['port_protocol'] = {item: each_list[index + 1]}
each_list.remove(item)
del each_list[index - 1]
del each_list[source_index]
elif source.get('host'):
if (source_index + 1) == index:
source['port_protocol'] = {item: each_list[index + 1]}
each_list.remove(item)
del each_list[index - 1]
del each_list[source_index]
if destination.get('address') or destination.get('any') or destination.get('host'):
try:
destination_index = each_list.index(destination.get('address'))
except ValueError:
try:
destination_index = each_list.index('any')
except ValueError:
destination_index = each_list.index('host') + 1
index -= 1
if (destination_index + 1) == index or (destination_index + 2) == index:
destination['port_protocol'] = {item: each_list[index + 1]}
each_list.remove(item)
del each_list[index]
break
if 'eq' in each_list or 'gt' in each_list or 'lt' in each_list or 'neq' in each_list or 'range' in each_list:
self.populate_port_protocol(source, destination, each_list)
def populate_source_destination(self, each, config, source, destination):
any = []
if 'any' in each:
any = re.findall('any', each)
if len(any) == 2:
source['any'] = True
destination['any'] = True
elif 'host' in each:
host = re.findall('host', each)
each = each.split(' ')
if len(host) == 2:
host_index = each.index('host')
source['host'] = each[host_index + 1]
del each[host_index]
host_index = each.index('host')
destination['host'] = each[host_index + 1]
else:
ip_n_wildcard_bits = re.findall(r'[0-9]+(?:\.[0-9]+){3}', each)
ip_index = None
if ip_n_wildcard_bits:
ip_index = each.index(ip_n_wildcard_bits[0])
host_index = each.index('host')
if ip_index:
if host_index < ip_index:
source['host'] = each(host_index + 1)
destination['address'] = ip_n_wildcard_bits[0]
destination['wildcard_bits'] = ip_n_wildcard_bits[1]
elif host_index > ip_index:
destination['host'] = each(host_index + 1)
source['address'] = ip_n_wildcard_bits[0]
source['wildcard_bits'] = ip_n_wildcard_bits[1]
else:
if config['afi'] == 'ipv4':
ip_n_wildcard_bits = re.findall(r'[0-9]+(?:\.[0-9]+){3}', each)
each = each.split(' ')
if len(ip_n_wildcard_bits) == 0 and len(any) == 1:
source['any'] = True
elif len(ip_n_wildcard_bits) == 1:
source['address'] = ip_n_wildcard_bits[0]
elif len(ip_n_wildcard_bits) == 2:
if 'any' in each:
if each.index('any') > each.index(ip_n_wildcard_bits[0]):
source['address'] = ip_n_wildcard_bits[0]
source['wildcard_bits'] = ip_n_wildcard_bits[1]
destination['any'] = True
elif each.index('any') < each.index(ip_n_wildcard_bits[0]):
source['any'] = True
destination['address'] = ip_n_wildcard_bits[0]
destination['wildcard_bits'] = ip_n_wildcard_bits[1]
else:
source['address'] = ip_n_wildcard_bits[0]
source['wildcard_bits'] = ip_n_wildcard_bits[1]
elif len(ip_n_wildcard_bits) == 4:
source['address'] = ip_n_wildcard_bits[0]
source['wildcard_bits'] = ip_n_wildcard_bits[1]
destination['address'] = ip_n_wildcard_bits[2]
destination['wildcard_bits'] = ip_n_wildcard_bits[3]
elif config['afi'] == 'ipv6':
temp_ipv6 = []
each = each.split(' ')
check_n_return_valid_ipv6_addr(self._module, each, temp_ipv6)
count = 0
for every in each:
if len(temp_ipv6) == 2:
if temp_ipv6[0] in every or temp_ipv6[1] in every:
temp_ipv6[count] = every
count += 1
elif len(temp_ipv6) == 1:
if temp_ipv6[0] in every:
temp_ipv6[count] = every
if 'any' in each:
if each.index('any') > each.index(temp_ipv6[0]):
source['address'] = temp_ipv6[0]
destination['any'] = True
elif each.index('any') < each.index(temp_ipv6[0]):
source['any'] = True
destination['address'] = temp_ipv6[0]
elif len(temp_ipv6) == 2:
source['address'] = temp_ipv6[0]
destination['address'] = temp_ipv6[1]
def parsed_config_facts(self, have_config):
"""
For parsed config have_config is string of commands which
need to be splitted before passing it through render_config
from spec for null values
:param have_config: The configuration
:rtype: list of have config
:returns: The splitted generated config
"""
split_config = re.split('ip|ipv6 access-list', have_config[0])
temp_config = []
# common piece of code for populating the temp_config list
def common_config_code(each, grant, temp_config):
temp = re.split(grant, each)
temp_config.append(temp[0])
temp_config.extend([grant + item for item in temp if 'access-list' not in item])
for each in split_config:
if 'v6' in each:
each = 'ipv6 ' + each.split('v6 ')[1]
if 'permit' in each:
common_config_code(each, 'permit', temp_config)
elif 'deny' in each:
common_config_code(each, 'deny', temp_config)
else:
each = 'ip' + each
if 'permit' in each:
common_config_code(each, 'permit', temp_config)
if 'deny' in each:
common_config_code(each, 'deny', temp_config)
return temp_config
def render_config(self, spec, have_config):
"""
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
"""
# for parsed scnenario where commands are passed to generate the acls facts
if len(have_config) == 1:
have_config = self.parsed_config_facts(have_config)
config = deepcopy(spec)
render_config = list()
acls = dict()
aces = list()
temp_name = ''
for each in have_config:
each_list = [val for val in each.split(' ') if val != '']
if 'IPv6' in each or 'ipv6' in each:
if aces:
config['acls'].append(acls)
ip_config = config
if ip_config.get('acls'):
render_config.append(ip_config)
if not config['afi'] or config['afi'] == 'ipv4':
config = deepcopy(spec)
config['afi'] = 'ipv6'
acls = dict()
aces = list()
elif not config['afi'] and ('IP' in each or 'ip' in each):
config['afi'] = 'ipv4'
if 'access list' in each or 'access-list' in each:
try:
temp_index = each_list.index('list')
name = (each_list[temp_index + 1])
except ValueError:
name = each_list[-1]
if temp_name != name:
if aces:
config['acls'].append(acls)
acls = dict()
aces = list()
temp_name = name
acls['name'] = name
if 'Extended' in each:
acls['acl_type'] = 'extended'
continue
elif 'Standard' in each:
acls['acl_type'] = 'standard'
continue
ace_options = {}
try:
if config['afi'] == 'ipv4':
if 'deny' in each_list or 'permit' in each_list:
ace_options['sequence'] = int(each_list[0])
elif config['afi'] == 'ipv6':
if 'sequence' in each_list:
ace_options['sequence'] = int(each_list[each_list.index('sequence') + 1])
except ValueError:
pass
if utils.parse_conf_arg(each, 'permit'):
ace_options['grant'] = 'permit'
each_list.remove('permit')
elif utils.parse_conf_arg(each, 'deny'):
ace_options['grant'] = 'deny'
each_list.remove('deny')
protocol_option = ['ahp', 'eigrp', 'esp', 'gre', 'hbh', 'icmp', 'igmp', 'ip', 'ipv6', 'ipinip', 'nos',
'ospf', 'pcp', 'pim', 'sctp', 'tcp', 'udp']
tcp_flags = ['ack', 'established', 'fin', 'psh', 'rst', 'syn', 'urg']
icmp_options = ['administratively_prohibited', 'alternate_address', 'conversion_error',
'dod_host_prohibited', 'dod_net_prohibited', 'echo', 'echo_reply',
'general_parameter_problem', 'host_isolated', 'host_precedence_unreachable',
'host_redirect', 'host_tos_redirect', 'host_tos_unreachable', 'host_unknown',
'host_unreachable', 'information_reply', 'information_request', 'mask_reply',
'mask_request', 'mobile_redirect', 'net_redirect', 'net_tos_redirect',
'net_tos_unreachable', 'net_unreachable', 'network_unknown', 'no_room_for_option',
'option_missing', 'packet_too_big', 'parameter_problem', 'port_unreachable',
'precedence_unreachable', 'protocol_unreachable', 'reassembly_timeout', 'redirect',
'router_advertisement', 'router_solicitation', 'source_quench', 'source_route_failed',
'time_exceeded', 'timestamp_reply', 'timestamp_request', 'traceroute', 'ttl_exceeded',
'unreachable']
igmp_options = ['dvmrp', 'host_query', 'mtrace_resp', 'mtrace_route', 'pim', 'trace', 'v1host_report',
'v2host_report', 'v2leave_group', 'v3host_report']
temp_option = ''
for option in protocol_option:
if option in each_list and 'access' not in each_list[each_list.index(option) + 1]:
temp_option = option
each_list.remove(temp_option)
if temp_option == 'tcp':
temp_flag = [each_flag for each_flag in tcp_flags if each_flag in each]
if temp_flag:
flag = temp_flag[0]
if flag in each_list:
each_list.remove(flag)
temp_flag = flag
if temp_option == 'icmp':
temp_flag = [each_option for each_option in icmp_options if each_option in each]
if temp_flag:
flag = temp_flag[0]
if flag in each_list:
each_list.remove(flag)
temp_flag = flag
if temp_option == 'igmp':
temp_flag = [each_option for each_option in igmp_options if each_option in each]
if temp_flag:
flag = temp_flag[0]
if flag in each_list:
each_list.remove(flag)
temp_flag = flag
break
dscp = utils.parse_conf_arg(each, 'dscp')
if dscp:
ace_options['dscp'] = dscp.split(' ')[0]
fragments = utils.parse_conf_arg(each, 'fragments')
if fragments:
ace_options['fragments'] = fragments.split(' ')[0]
log = utils.parse_conf_arg(each, 'log')
if log:
ace_options['log'] = log.split(' ')[0]
log_input = utils.parse_conf_arg(each, 'log_input')
if log_input:
ace_options['log_input'] = log_input.split(' ')[0]
option = utils.parse_conf_arg(each, 'option')
if option:
option = option.split(' ')[0]
option_dict = {}
option_dict[option] = True
ace_options['option'] = option_dict
precedence = utils.parse_conf_arg(each, 'precedence')
if precedence:
ace_options['precedence'] = precedence.split(' ')[0]
time_range = utils.parse_conf_arg(each, 'time_range')
if time_range:
ace_options['time_range'] = time_range.split(' ')[0]
tos = utils.parse_conf_arg(each, 'tos')
if tos:
tos_val = dict()
try:
tos_val['service_value'] = int(tos)
except ValueError:
tos = tos.replace('-', '_')
tos_val[tos] = True
ace_options['tos'] = tos_val
ttl = utils.parse_conf_arg(each, 'ttl')
if ttl:
temp_ttl = ttl.split(' ')
ttl = {}
ttl[temp_ttl[0]] = temp_ttl[1]
each_list = [item for item in each_list[:each_list.index('ttl')]]
ace_options['ttl'] = ttl
source = {}
destination = {}
self.populate_source_destination(each, config, source, destination)
if source.get('address') and source.get('address') == destination.get('address'):
self._module.fail_json(msg='Source and Destination address cannot be same!')
else:
self.populate_port_protocol(source, destination, each_list)
if source:
ace_options['source'] = source
if destination:
ace_options['destination'] = destination
if temp_option:
protocol_options = {}
ace_options['protocol'] = temp_option
if temp_option == 'tcp':
tcp = {}
if temp_flag:
tcp[temp_flag] = True
else:
tcp['set'] = True
protocol_options[temp_option] = tcp
elif temp_option == 'icmp':
icmp = dict()
if temp_flag:
icmp[temp_flag] = True
else:
icmp['set'] = True
protocol_options[temp_option] = icmp
elif temp_option == 'igmp':
igmp = dict()
if temp_flag:
igmp[temp_flag] = True
else:
igmp['set'] = True
protocol_options[temp_option] = igmp
else:
protocol_options[temp_option] = True
ace_options['protocol_options'] = protocol_options
if ace_options:
aces.append(ace_options)
acls['aces'] = aces
if acls:
if not config.get('acls'):
config['acls'] = list()
config['acls'].append(acls)
if config not in render_config:
render_config.append(utils.remove_empties(config))
# delete the populated config
del config
return render_config

@ -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)
"""
The facts class for ios
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.ios.facts.interfaces.interfaces import InterfacesFacts
from ansible.module_utils.network.ios.facts.l2_interfaces.l2_interfaces import L2_InterfacesFacts
from ansible.module_utils.network.ios.facts.vlans.vlans import VlansFacts
from ansible.module_utils.network.ios.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts
from ansible.module_utils.network.ios.facts.lacp.lacp import LacpFacts
from ansible.module_utils.network.ios.facts.lacp_interfaces.lacp_interfaces import Lacp_InterfacesFacts
from ansible.module_utils.network.ios.facts.lldp_global.lldp_global import Lldp_globalFacts
from ansible.module_utils.network.ios.facts.lldp_interfaces.lldp_interfaces import Lldp_InterfacesFacts
from ansible.module_utils.network.ios.facts.l3_interfaces.l3_interfaces import L3_InterfacesFacts
from ansible.module_utils.network.ios.facts.acl_interfaces.acl_interfaces import Acl_InterfacesFacts
from ansible.module_utils.network.ios.facts.static_routes.static_routes import Static_RoutesFacts
from ansible.module_utils.network.ios.facts.acls.acls import AclsFacts
from ansible.module_utils.network.ios.facts.legacy.base import Default, Hardware, Interfaces, Config
FACT_LEGACY_SUBSETS = dict(
default=Default,
hardware=Hardware,
interfaces=Interfaces,
config=Config
)
FACT_RESOURCE_SUBSETS = dict(
interfaces=InterfacesFacts,
l2_interfaces=L2_InterfacesFacts,
vlans=VlansFacts,
lag_interfaces=Lag_interfacesFacts,
lacp=LacpFacts,
lacp_interfaces=Lacp_InterfacesFacts,
lldp_global=Lldp_globalFacts,
lldp_interfaces=Lldp_InterfacesFacts,
l3_interfaces=L3_InterfacesFacts,
acl_interfaces=Acl_InterfacesFacts,
static_routes=Static_RoutesFacts,
acls=AclsFacts,
)
class Facts(FactsBase):
""" The fact class for ios
"""
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 ios
: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,97 +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 ios 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.ios.utils.utils import get_interface_type, normalize_interface
from ansible.module_utils.network.ios.argspec.interfaces.interfaces import InterfacesArgs
class InterfacesFacts(object):
""" The ios interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = InterfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
objs = []
if not data:
data = connection.get('show running-config | section ^interface')
# 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 get_interface_type(intf) == 'unknown':
return {}
# populate the facts from the configuration
config['name'] = normalize_interface(intf)
config['description'] = utils.parse_conf_arg(conf, 'description')
config['speed'] = 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,114 +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 ios 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.ios.utils.utils import get_interface_type, normalize_interface
from ansible.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs
class L2_InterfacesFacts(object):
""" The ios 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 interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
objs = []
if not data:
data = connection.get('show running-config | section ^interface')
# 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 get_interface_type(intf) == 'unknown':
return {}
if intf.upper()[:2] in ('HU', 'FO', 'TW', 'TE', 'GI', 'FA', 'ET', 'PO'):
# populate the facts from the configuration
config['name'] = normalize_interface(intf)
has_mode = utils.parse_conf_arg(conf, 'switchport mode')
if has_mode:
config['mode'] = has_mode
has_access = utils.parse_conf_arg(conf, 'switchport access vlan')
if has_access:
config["access"] = {"vlan": int(has_access)}
has_voice = utils.parse_conf_arg(conf, 'switchport voice vlan')
if has_voice:
config["voice"] = {"vlan": int(has_voice)}
trunk = dict()
trunk["encapsulation"] = utils.parse_conf_arg(conf, 'encapsulation')
native_vlan = utils.parse_conf_arg(conf, 'native vlan')
if native_vlan:
trunk["native_vlan"] = int(native_vlan)
allowed_vlan = utils.parse_conf_arg(conf, 'allowed vlan')
if allowed_vlan:
trunk["allowed_vlans"] = allowed_vlan.split(',')
pruning_vlan = utils.parse_conf_arg(conf, 'pruning vlan')
if pruning_vlan:
trunk['pruning_vlans'] = pruning_vlan.split(',')
config['trunk'] = trunk
return utils.remove_empties(config)

@ -1,124 +0,0 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The ios_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.ios.utils.utils import get_interface_type, normalize_interface
from ansible.module_utils.network.ios.argspec.l3_interfaces.l3_interfaces import L3_InterfacesArgs
class L3_InterfacesFacts(object):
""" The ios l3 interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = L3_InterfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for l3 interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
objs = []
if not data:
data = connection.get('show running-config | section ^interface')
# 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 get_interface_type(intf) == 'unknown':
return {}
# populate the facts from the configuration
config['name'] = normalize_interface(intf)
ipv4 = []
ipv4_all = re.findall(r"ip address (\S+.*)", conf)
for each in ipv4_all:
each_ipv4 = dict()
if 'secondary' not in each and 'dhcp' not in each:
each_ipv4['address'] = each
elif 'secondary' in each:
each_ipv4['address'] = each.split(' secondary')[0]
each_ipv4['secondary'] = True
elif 'dhcp' in each:
each_ipv4['address'] = 'dhcp'
if 'client-id' in each:
each_ipv4['dhcp_client'] = int(each.split(' hostname ')[0].split('/')[-1])
if 'hostname' in each:
each_ipv4["dhcp_hostname"] = each.split(' hostname ')[-1]
if 'client-id' in each and each_ipv4['dhcp_client'] is None:
each_ipv4['dhcp_client'] = int(each.split('/')[-1])
if 'hostname' in each and not each_ipv4["dhcp_hostname"]:
each_ipv4["dhcp_hostname"] = each.split(' hostname ')[-1]
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()
if 'autoconfig' in each:
each_ipv6['autoconfig'] = True
if 'dhcp' in each:
each_ipv6['dhcp'] = True
each_ipv6['address'] = each.lower()
ipv6.append(each_ipv6)
config['ipv6'] = ipv6
return utils.remove_empties(config)

@ -1,83 +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 ios 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.ios.argspec.lacp.lacp import LacpArgs
class LacpFacts(object):
""" The ios 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 connection:
pass
if not data:
data = connection.get('show lacp sys-id')
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)
config['system']['priority'] = int(conf.split(',')[0])
return utils.remove_empties(config)

@ -1,102 +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 ios_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.ios.utils.utils import get_interface_type, normalize_interface
from ansible.module_utils.network.ios.argspec.lacp_interfaces.lacp_interfaces import Lacp_InterfacesArgs
class Lacp_InterfacesFacts(object):
""" The ios_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 connection:
pass
objs = []
if not data:
data = connection.get('show running-config | section ^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['lacp_interfaces'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['lacp_interfaces'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def render_config(self, spec, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
match = re.search(r'^(\S+)', conf)
intf = match.group(1)
if get_interface_type(intf) == 'unknown':
return {}
config['name'] = normalize_interface(intf)
port_priority = utils.parse_conf_arg(conf, 'lacp port-priority')
max_bundle = utils.parse_conf_arg(conf, 'lacp max-bundle')
if port_priority:
config['port_priority'] = int(port_priority)
if 'lacp fast-switchover' in conf:
config['fast_switchover'] = True
if max_bundle:
config['max_bundle'] = int(max_bundle)
return utils.remove_empties(config)

@ -1,118 +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 ios 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.ios.utils.utils import get_interface_type, normalize_interface
from ansible.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
class Lag_interfacesFacts(object):
""" The ios_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 interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
objs = []
if not data:
data = connection.get('show running-config | section ^interface')
# 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:
if not obj.get('members'):
obj.update({'members': []})
objs.append(obj)
# for appending members configured with same channel-group
for each in range(len(objs)):
if each < (len(objs) - 1):
if objs[each]['name'] == objs[each + 1]['name']:
objs[each]['members'].append(objs[each + 1]['members'][0])
del objs[each + 1]
facts = {}
if objs:
facts['lag_interfaces'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['lag_interfaces'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def render_config(self, spec, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
match = re.search(r'^(\S+)', conf)
intf = match.group(1)
if get_interface_type(intf) == 'unknown':
return {}
member_config = {}
channel_group = utils.parse_conf_arg(conf, 'channel-group')
if intf.startswith('Gi'):
config['name'] = intf
config['members'] = []
if channel_group:
channel_group = channel_group.split(' ')
id = channel_group[0]
config['name'] = 'Port-channel{0}'.format(str(id))
if 'mode' in channel_group:
mode = channel_group[2]
member_config.update({'mode': mode})
if 'link' in channel_group:
link = channel_group[2]
member_config.update({'link': link})
if member_config.get('mode') or member_config.get('link'):
member_config['member'] = normalize_interface(intf)
config['members'].append(member_config)
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 ios 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.ios.ios import run_commands, get_capabilities
from ansible.module_utils.network.ios.ios import normalize_interface
from ansible.module_utils.six import iteritems
from ansible.module_utils.six.moves import zip
class FactsBase(object):
COMMANDS = list()
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, commands=self.COMMANDS, check_rc=False)
def run(self, cmd):
return run_commands(self.module, commands=cmd, check_rc=False)
class Default(FactsBase):
COMMANDS = ['show version']
def populate(self):
super(Default, self).populate()
self.facts.update(self.platform_facts())
data = self.responses[0]
if data:
self.facts['iostype'] = self.parse_iostype(data)
self.facts['serialnum'] = self.parse_serialnum(data)
self.parse_stacks(data)
def parse_iostype(self, data):
match = re.search(r'\S+(X86_64_LINUX_IOSD-UNIVERSALK9-M)(\S+)', data)
if match:
return "IOS-XE"
else:
return "IOS"
def parse_serialnum(self, data):
match = re.search(r'board ID (\S+)', data)
if match:
return match.group(1)
def parse_stacks(self, data):
match = re.findall(r'^Model [Nn]umber\s+: (\S+)', data, re.M)
if match:
self.facts['stacked_models'] = match
match = re.findall(r'^System [Ss]erial [Nn]umber\s+: (\S+)', data, re.M)
if match:
self.facts['stacked_serialnums'] = match
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',
'show memory statistics'
]
def populate(self):
warnings = list()
super(Hardware, self).populate()
data = self.responses[0]
if data:
self.facts['filesystems'] = self.parse_filesystems(data)
self.facts['filesystems_info'] = self.parse_filesystems_info(data)
data = self.responses[1]
if data:
if 'Invalid input detected' in data:
warnings.append('Unable to gather memory statistics')
else:
processor_line = [l for l in data.splitlines()
if 'Processor' in l].pop()
match = re.findall(r'\s(\d+)\s', processor_line)
if match:
self.facts['memtotal_mb'] = int(match[0]) / 1024
self.facts['memfree_mb'] = int(match[3]) / 1024
def parse_filesystems(self, data):
return re.findall(r'^Directory of (\S+)/', data, re.M)
def parse_filesystems_info(self, data):
facts = dict()
fs = ''
for line in data.split('\n'):
match = re.match(r'^Directory of (\S+)/', line)
if match:
fs = match.group(1)
facts[fs] = dict()
continue
match = re.match(r'^(\d+) bytes total \((\d+) bytes free\)', line)
if match:
facts[fs]['spacetotal_kb'] = int(match.group(1)) / 1024
facts[fs]['spacefree_kb'] = int(match.group(2)) / 1024
return facts
class Config(FactsBase):
COMMANDS = ['show running-config']
def populate(self):
super(Config, self).populate()
data = self.responses[0]
if data:
data = re.sub(
r'^Building configuration...\s+Current configuration : \d+ bytes\n',
'', data, flags=re.MULTILINE)
self.facts['config'] = data
class Interfaces(FactsBase):
COMMANDS = [
'show interfaces',
'show ip interface',
'show ipv6 interface',
'show lldp',
'show cdp'
]
def populate(self):
super(Interfaces, self).populate()
self.facts['all_ipv4_addresses'] = list()
self.facts['all_ipv6_addresses'] = list()
self.facts['neighbors'] = {}
data = self.responses[0]
if data:
interfaces = self.parse_interfaces(data)
self.facts['interfaces'] = self.populate_interfaces(interfaces)
data = self.responses[1]
if data:
data = self.parse_interfaces(data)
self.populate_ipv4_interfaces(data)
data = self.responses[2]
if data:
data = self.parse_interfaces(data)
self.populate_ipv6_interfaces(data)
data = self.responses[3]
lldp_errs = ['Invalid input', 'LLDP is not enabled']
if data and not any(err in data for err in lldp_errs):
neighbors = self.run(['show lldp neighbors detail'])
if neighbors:
self.facts['neighbors'].update(self.parse_neighbors(neighbors[0]))
data = self.responses[4]
cdp_errs = ['CDP is not enabled']
if data and not any(err in data for err in cdp_errs):
cdp_neighbors = self.run(['show cdp neighbors detail'])
if cdp_neighbors:
self.facts['neighbors'].update(self.parse_cdp_neighbors(cdp_neighbors[0]))
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)
intf['mtu'] = self.parse_mtu(value)
intf['bandwidth'] = self.parse_bandwidth(value)
intf['mediatype'] = self.parse_mediatype(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_ipv4_interfaces(self, data):
for key, value in data.items():
self.facts['interfaces'][key]['ipv4'] = list()
primary_address = addresses = []
primary_address = re.findall(r'Internet address is (.+)$', value, re.M)
addresses = re.findall(r'Secondary address (.+)$', value, re.M)
if len(primary_address) == 0:
continue
addresses.append(primary_address[0])
for address in addresses:
addr, subnet = address.split("/")
ipv4 = dict(address=addr.strip(), subnet=subnet.strip())
self.add_ip_address(addr.strip(), 'ipv4')
self.facts['interfaces'][key]['ipv4'].append(ipv4)
def populate_ipv6_interfaces(self, data):
for key, value in iteritems(data):
try:
self.facts['interfaces'][key]['ipv6'] = list()
except KeyError:
self.facts['interfaces'][key] = dict()
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()
for entry in neighbors.split('------------------------------------------------'):
if entry == '':
continue
intf = self.parse_lldp_intf(entry)
if intf is None:
return facts
intf = normalize_interface(intf)
if intf not in facts:
facts[intf] = list()
fact = dict()
fact['host'] = self.parse_lldp_host(entry)
fact['port'] = self.parse_lldp_port(entry)
facts[intf].append(fact)
return facts
def parse_cdp_neighbors(self, neighbors):
facts = dict()
for entry in neighbors.split('-------------------------'):
if entry == '':
continue
intf_port = self.parse_cdp_intf_port(entry)
if intf_port is None:
return facts
intf, port = intf_port
if intf not in facts:
facts[intf] = list()
fact = dict()
fact['host'] = self.parse_cdp_host(entry)
fact['port'] = port
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'Hardware is (?:.*), address is (\S+)', data)
if match:
return match.group(1)
def parse_ipv4(self, data):
match = re.search(r'Internet address is (\S+)', data)
if match:
addr, masklen = match.group(1).split('/')
return dict(address=addr, masklen=int(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+) Duplex', data, re.M)
if match:
return match.group(1)
def parse_mediatype(self, data):
match = re.search(r'media type is (.+)$', 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+)\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 Intf: (.+)$', 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)
def parse_cdp_intf_port(self, data):
match = re.search(r'^Interface: (.+), Port ID \(outgoing port\): (.+)$', data, re.M)
if match:
return match.group(1), match.group(2)
def parse_cdp_host(self, data):
match = re.search(r'^Device ID: (.+)$', data, re.M)
if match:
return match.group(1)

@ -1,90 +0,0 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The ios lldp_global fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.ios.argspec.lldp_global.lldp_global import Lldp_globalArgs
class Lldp_globalFacts(object):
""" The ios lldp_global fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = Lldp_globalArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for lldp_global
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
objs = dict()
if not data:
data = connection.get('show running-config | section ^lldp')
# operate on a collection of resource x
config = data.split('\n')
for conf in config:
if conf:
obj = self.render_config(self.generated_spec, conf)
if obj:
objs.update(obj)
facts = {}
if objs:
params = utils.validate_config(self.argument_spec, {'config': utils.remove_empties(objs)})
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)
holdtime = utils.parse_conf_arg(conf, 'lldp holdtime')
timer = utils.parse_conf_arg(conf, 'lldp timer')
reinit = utils.parse_conf_arg(conf, 'lldp reinit')
if holdtime:
config['holdtime'] = int(holdtime)
if 'lldp run' in conf:
config['enabled'] = True
if timer:
config['timer'] = int(timer)
if reinit:
config['reinit'] = int(reinit)
return utils.remove_empties(config)

@ -1,108 +0,0 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The ios_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.ios.utils.utils import get_interface_type, normalize_interface
from ansible.module_utils.network.ios.argspec.lldp_interfaces.lldp_interfaces import Lldp_InterfacesArgs
class Lldp_InterfacesFacts(object):
""" The ios_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 connection:
pass
objs = []
if not data:
data = connection.get('show lldp interface')
# operate on a collection of resource x
config = data.split('\n\n')
for conf in config:
if conf:
obj = self.render_config(self.generated_spec, conf)
if obj:
objs.append(obj)
facts = {}
if objs:
facts['lldp_interfaces'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['lldp_interfaces'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def render_config(self, spec, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
match = re.search(r'^(\S+)(:)', conf)
intf = ''
if match:
intf = match.group(1)
if get_interface_type(intf) == 'unknown':
return {}
if intf.lower().startswith('gi'):
config['name'] = normalize_interface(intf)
receive = utils.parse_conf_arg(conf, 'Rx:')
transmit = utils.parse_conf_arg(conf, 'Tx:')
if receive == 'enabled':
config['receive'] = True
elif receive == 'disabled':
config['receive'] = False
if transmit == 'enabled':
config['transmit'] = True
elif transmit == 'disabled':
config['transmit'] = False
return utils.remove_empties(config)

@ -1,225 +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 ios_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
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.ios.utils.utils import netmask_to_cidr
from ansible.module_utils.network.ios.argspec.static_routes.static_routes import Static_RoutesArgs
class Static_RoutesFacts(object):
""" The ios_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_static_routes_data(self, connection):
return connection.get('sh running-config | include ip route|ipv6 route')
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
"""
objs = []
if not data:
data = self.get_static_routes_data(connection)
# operate on a collection of resource x
config = data.split('\n')
same_dest = self.populate_destination(config)
for key in same_dest.keys():
if key:
obj = self.render_config(self.generated_spec, key, same_dest[key])
if obj:
objs.append(obj)
facts = {}
# append all static routes address_family with NO VRF together
no_vrf_address_family = {
'address_families': [each.get('address_families')[0] for each in objs if each.get('vrf') is None]
}
temp_objs = [each for each in objs if each.get('vrf') is not None]
temp_objs.append(no_vrf_address_family)
objs = temp_objs
if objs:
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 update_netmask_to_cidr(self, filter, pos, del_pos):
netmask = filter.split(' ')
dest = netmask[pos] + '/' + netmask_to_cidr(netmask[del_pos])
netmask[pos] = dest
del netmask[del_pos]
filter_vrf = ' '
return filter_vrf.join(netmask), dest
def populate_destination(self, config):
same_dest = {}
ip_str = ''
for i in sorted(config):
if i:
if '::' in i and 'vrf' in i:
ip_str = 'ipv6 route vrf'
elif '::' in i and 'vrf' not in i:
ip_str = 'ipv6 route'
elif '.' in i and 'vrf' in i:
ip_str = 'ip route vrf'
elif '.' in i and 'vrf' not in i:
ip_str = 'ip route'
if 'vrf' in i:
filter_vrf = utils.parse_conf_arg(i, ip_str)
if '/' not in filter_vrf and '::' not in filter_vrf:
filter_vrf, dest_vrf = self.update_netmask_to_cidr(filter_vrf, 1, 2)
dest_vrf = dest_vrf + '_vrf'
else:
dest_vrf = filter_vrf.split(' ')[1]
if dest_vrf not in same_dest.keys():
same_dest[dest_vrf] = []
same_dest[dest_vrf].append('vrf ' + filter_vrf)
elif 'vrf' not in same_dest[dest_vrf][0]:
same_dest[dest_vrf] = []
same_dest[dest_vrf].append('vrf ' + filter_vrf)
else:
same_dest[dest_vrf].append(('vrf ' + filter_vrf))
else:
filter = utils.parse_conf_arg(i, ip_str)
if '/' not in filter and '::' not in filter:
filter, dest = self.update_netmask_to_cidr(filter, 0, 1)
else:
dest = filter.split(' ')[0]
if dest not in same_dest.keys():
same_dest[dest] = []
same_dest[dest].append(filter)
elif 'vrf' in same_dest[dest][0]:
same_dest[dest] = []
same_dest[dest].append(filter)
else:
same_dest[dest].append(filter)
return same_dest
def render_config(self, spec, conf, conf_val):
"""
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['address_families'] = []
route_dict = dict()
final_route = dict()
afi = dict()
final_route['routes'] = []
next_hops = []
hops = {}
vrf = ''
address_family = dict()
for each in conf_val:
route = each.split(' ')
if 'vrf' in conf_val[0]:
vrf = route[route.index('vrf') + 1]
route_dict['dest'] = conf.split('_')[0]
else:
route_dict['dest'] = conf
if 'vrf' in conf_val[0]:
hops = {}
if '::' in conf:
hops['forward_router_address'] = route[3]
afi['afi'] = 'ipv6'
elif '.' in conf:
hops['forward_router_address'] = route[3]
afi['afi'] = "ipv4"
else:
hops['interface'] = conf
else:
if '::' in conf:
hops['forward_router_address'] = route[1]
afi['afi'] = 'ipv6'
elif '.' in conf:
hops['forward_router_address'] = route[1]
afi['afi'] = "ipv4"
else:
hops['interface'] = route[1]
try:
temp_list = each.split(' ')
if 'tag' in temp_list:
del temp_list[temp_list.index('tag') + 1]
if 'track' in temp_list:
del temp_list[temp_list.index('track') + 1]
# find distance metric
dist_metrics = int(
[i for i in temp_list if '.' not in i and ':' not in i and ord(i[0]) > 48 and ord(i[0]) < 57][0]
)
except IndexError:
dist_metrics = None
if dist_metrics:
hops['distance_metric'] = dist_metrics
if 'name' in route:
hops['name'] = route[route.index('name') + 1]
if 'multicast' in route:
hops['multicast'] = True
if 'dhcp' in route:
hops['dhcp'] = True
if 'global' in route:
hops['global'] = True
if 'permanent' in route:
hops['permanent'] = True
if 'tag' in route:
hops['tag'] = route[route.index('tag') + 1]
if 'track' in route:
hops['track'] = route[route.index('track') + 1]
next_hops.append(hops)
hops = {}
route_dict['next_hops'] = next_hops
if route_dict:
final_route['routes'].append(route_dict)
address_family.update(afi)
address_family.update(final_route)
config['address_families'].append(address_family)
if vrf:
config['vrf'] = vrf
return utils.remove_empties(config)

@ -1,144 +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 ios vlans fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.ios.argspec.vlans.vlans import VlansArgs
class VlansFacts(object):
""" The ios vlans fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = VlansArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for vlans
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if connection:
pass
objs = []
mtu_objs = []
remote_objs = []
final_objs = []
if not data:
data = connection.get('show vlan')
# operate on a collection of resource x
config = data.split('\n')
# Get individual vlan configs separately
vlan_info = ''
for conf in config:
if 'Name' in conf:
vlan_info = 'Name'
elif 'Type' in conf:
vlan_info = 'Type'
elif 'Remote' in conf:
vlan_info = 'Remote'
if conf and ' ' not in filter(None, conf.split('-')):
obj = self.render_config(self.generated_spec, conf, vlan_info)
if 'mtu' in obj:
mtu_objs.append(obj)
elif 'remote_span' in obj:
remote_objs = obj
elif obj:
objs.append(obj)
# Appending MTU value to the retrieved dictionary
for o, m in zip(objs, mtu_objs):
o.update(m)
final_objs.append(o)
# Appending Remote Span value to related VLAN:
if remote_objs:
if remote_objs.get('remote_span'):
for each in remote_objs.get('remote_span'):
for every in final_objs:
if each == every.get('vlan_id'):
every.update({'remote_span': True})
break
facts = {}
if final_objs:
facts['vlans'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['vlans'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def render_config(self, spec, conf, vlan_info):
"""
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)
if vlan_info == 'Name' and 'Name' not in conf:
conf = list(filter(None, conf.split(' ')))
config['vlan_id'] = int(conf[0])
config['name'] = conf[1]
if len(conf[2].split('/')) > 1:
if conf[2].split('/')[0] == 'sus':
config['state'] = 'suspend'
elif conf[2].split('/')[0] == 'act':
config['state'] = 'active'
config['shutdown'] = 'enabled'
else:
if conf[2] == 'suspended':
config['state'] = 'suspend'
elif conf[2] == 'active':
config['state'] = 'active'
config['shutdown'] = 'disabled'
elif vlan_info == 'Type' and 'Type' not in conf:
conf = list(filter(None, conf.split(' ')))
config['mtu'] = int(conf[3])
elif vlan_info == 'Remote':
if len(conf.split(',')) > 1 or conf.isdigit():
remote_span_vlan = []
if len(conf.split(',')) > 1:
remote_span_vlan = conf.split(',')
else:
remote_span_vlan.append(conf)
remote_span = []
for each in remote_span_vlan:
remote_span.append(int(each))
config['remote_span'] = remote_span
return utils.remove_empties(config)

@ -1,183 +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.
#
# (c) 2016 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
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.connection import Connection, ConnectionError
_DEVICE_CONFIGS = {}
ios_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'),
'authorize': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']), type='bool'),
'auth_pass': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS']), no_log=True),
'timeout': dict(type='int')
}
ios_argument_spec = {
'provider': dict(type='dict', options=ios_provider_spec, removed_in_version=2.14),
}
def get_provider_argspec():
return ios_provider_spec
def get_connection(module):
if hasattr(module, '_ios_connection'):
return module._ios_connection
capabilities = get_capabilities(module)
network_api = capabilities.get('network_api')
if network_api == 'cliconf':
module._ios_connection = Connection(module._socket_path)
else:
module.fail_json(msg='Invalid connection type %s' % network_api)
return module._ios_connection
def get_capabilities(module):
if hasattr(module, '_ios_capabilities'):
return module._ios_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._ios_capabilities = json.loads(capabilities)
return module._ios_capabilities
def get_defaults_flag(module):
connection = get_connection(module)
try:
out = connection.get_defaults_flag()
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
return to_text(out, errors='surrogate_then_replace').strip()
def get_config(module, flags=None):
flags = to_list(flags)
section_filter = False
if flags and 'section' in flags[-1]:
section_filter = True
flag_str = ' '.join(flags)
try:
return _DEVICE_CONFIGS[flag_str]
except KeyError:
connection = get_connection(module)
try:
out = connection.get_config(flags=flags)
except ConnectionError as exc:
if section_filter:
# Some ios devices don't understand `| section foo`
out = get_config(module, flags=flags[:-1])
else:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
cfg = to_text(out, errors='surrogate_then_replace').strip()
_DEVICE_CONFIGS[flag_str] = cfg
return cfg
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 load_config(module, commands):
connection = get_connection(module)
try:
resp = connection.edit_config(commands)
return resp.get('response')
except ConnectionError as exc:
module.fail_json(msg=to_text(exc))
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('te'):
if_type = 'TenGigabitEthernet'
elif name.lower().startswith('fa'):
if_type = 'FastEthernet'
elif name.lower().startswith('fo'):
if_type = 'FortyGigabitEthernet'
elif name.lower().startswith('et'):
if_type = 'Ethernet'
elif name.lower().startswith('vl'):
if_type = 'Vlan'
elif name.lower().startswith('lo'):
if_type = 'loopback'
elif name.lower().startswith('po'):
if_type = 'port-channel'
elif name.lower().startswith('nv'):
if_type = 'nve'
elif name.lower().startswith('twe'):
if_type = 'TwentyFiveGigE'
elif name.lower().startswith('hu'):
if_type = 'HundredGigE'
else:
if_type = None
number_list = name.split(' ')
if len(number_list) == 2:
if_number = number_list[-1].strip()
else:
if_number = _get_number(name)
if if_type:
proper_interface = if_type + if_number
else:
proper_interface = name
return proper_interface

@ -1,77 +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.six import iteritems
from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.network.common.config import NetworkConfig
class ConfigBase(object):
argument_spec = {}
mutually_exclusive = []
identifier = ()
def __init__(self, **kwargs):
self.values = {}
self._rendered_configuration = {}
self.active_configuration = None
for item in self.identifier:
self.values[item] = kwargs.pop(item)
for key, value in iteritems(kwargs):
if key in self.argument_spec:
setattr(self, key, value)
for key, value in iteritems(self.argument_spec):
if value.get('default'):
if not getattr(self, key, None):
setattr(self, key, value.get('default'))
def __getattr__(self, key):
if key in self.argument_spec:
return self.values.get(key)
def __setattr__(self, key, value):
if key in self.argument_spec:
if key in self.identifier:
raise TypeError('cannot set value')
elif value is not None:
self.values[key] = value
else:
super(ConfigBase, self).__setattr__(key, value)
def context_config(self, cmd):
if 'context' not in self._rendered_configuration:
self._rendered_configuration['context'] = list()
self._rendered_configuration['context'].extend(to_list(cmd))
def global_config(self, cmd):
if 'global' not in self._rendered_configuration:
self._rendered_configuration['global'] = list()
self._rendered_configuration['global'].extend(to_list(cmd))
def get_rendered_configuration(self):
config = list()
for section in ('context', 'global'):
config.extend(self._rendered_configuration.get(section, []))
return config
def set_active_configuration(self, config):
self.active_configuration = config
def render(self, config=None):
raise NotImplementedError
def get_section(self, config, section):
if config is not None:
netcfg = NetworkConfig(indent=1, contents=config)
try:
config = netcfg.get_block_config(to_list(section))
except ValueError:
config = None
return config

@ -1,140 +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.ios.providers.providers import CliProvider
from ansible.module_utils.network.ios.providers.cli.config.bgp.neighbors import AFNeighbors
from ansible.module_utils.common.network import to_netmask
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' % item['afi']
if item['safi'] != 'unicast':
context += ' %s' % 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-address-family')
safe_list.append(context)
if self.params['operation'] == 'replace':
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_auto_summary(self, item, config=None):
cmd = 'auto-summary'
if item['auto_summary'] is False:
cmd = 'no %s' % cmd
if not config or cmd not in config:
return cmd
def _render_synchronization(self, item, config=None):
cmd = 'synchronization'
if item['synchronization'] is False:
cmd = 'no %s' % cmd
if not config or cmd not in config:
return cmd
def _render_networks(self, item, config=None):
commands = list()
safe_list = list()
for entry in item['networks']:
network = entry['prefix']
cmd = 'network %s' % network
if entry['masklen']:
cmd += ' mask %s' % to_netmask(entry['masklen'])
network += ' mask %s' % to_netmask(entry['masklen'])
if entry['route_map']:
cmd += ' route-map %s' % entry['route_map']
network += ' route-map %s' % entry['route_map']
safe_list.append(network)
if not config or cmd not in config:
commands.append(cmd)
if self.params['operation'] == 'replace':
if config:
matches = re.findall(r'network (.*)', 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', 'ospfv3', 'eigrp'):
cmd += ' %s' % entry['id']
option += ' %s' % entry['id']
if entry['metric']:
cmd += ' metric %s' % entry['metric']
if entry['route_map']:
cmd += ' route-map %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
def _render_neighbors(self, item, config):
""" generate bgp neighbor configuration
"""
return AFNeighbors(self.params).render(config, nbr_list=item['neighbors'])

@ -1,196 +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.ios.providers.providers import CliProvider
class Neighbors(CliProvider):
def render(self, config=None, nbr_list=None):
commands = list()
safe_list = list()
if not nbr_list:
nbr_list = self.get_value('config.neighbors')
for item in nbr_list:
neighbor_commands = list()
context = 'neighbor %s' % item['neighbor']
cmd = '%s remote-as %s' % (context, item['remote_as'])
if not config or cmd not in config:
neighbor_commands.append(cmd)
for key, value in iteritems(item):
if value is not None:
meth = getattr(self, '_render_%s' % key, None)
if meth:
resp = meth(item, config)
if resp:
neighbor_commands.extend(to_list(resp))
commands.extend(neighbor_commands)
safe_list.append(context)
if self.params['operation'] == 'replace':
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_local_as(self, item, config=None):
cmd = 'neighbor %s local-as %s' % (item['neighbor'], item['local_as'])
if not config or cmd not in config:
return cmd
def _render_port(self, item, config=None):
cmd = 'neighbor %s port %s' % (item['neighbor'], item['port'])
if not config or cmd not in config:
return cmd
def _render_description(self, item, config=None):
cmd = 'neighbor %s description %s' % (item['neighbor'], item['description'])
if not config or cmd not in config:
return cmd
def _render_enabled(self, item, config=None):
cmd = 'neighbor %s shutdown' % item['neighbor']
if item['enabled'] is True:
if not config or cmd in config:
cmd = 'no %s' % cmd
return cmd
elif not config or cmd not in config:
return cmd
def _render_update_source(self, item, config=None):
cmd = 'neighbor %s update-source %s' % (item['neighbor'], item['update_source'])
if not config or cmd not in config:
return cmd
def _render_password(self, item, config=None):
cmd = 'neighbor %s password %s' % (item['neighbor'], item['password'])
if not config or cmd not in config:
return cmd
def _render_ebgp_multihop(self, item, config=None):
cmd = 'neighbor %s ebgp-multihop %s' % (item['neighbor'], item['ebgp_multihop'])
if not config or cmd not in config:
return cmd
def _render_peer_group(self, item, config=None):
cmd = 'neighbor %s peer-group %s' % (item['neighbor'], item['peer_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']
neighbor = item['neighbor']
if keepalive and holdtime:
cmd = 'neighbor %s timers %s %s' % (neighbor, keepalive, holdtime)
if min_neighbor_holdtime:
cmd += ' %s' % min_neighbor_holdtime
if not config or cmd not in config:
return cmd
class AFNeighbors(CliProvider):
def render(self, config=None, nbr_list=None):
commands = list()
if not nbr_list:
return
for item in nbr_list:
neighbor_commands = list()
for key, value in iteritems(item):
if value is not None:
meth = getattr(self, '_render_%s' % key, None)
if meth:
resp = meth(item, config)
if resp:
neighbor_commands.extend(to_list(resp))
commands.extend(neighbor_commands)
return commands
def _render_advertisement_interval(self, item, config=None):
cmd = 'neighbor %s advertisement-interval %s' % (item['neighbor'], item['advertisement_interval'])
if not config or cmd not in config:
return cmd
def _render_route_reflector_client(self, item, config=None):
cmd = 'neighbor %s route-reflector-client' % item['neighbor']
if item['route_reflector_client'] is False:
if not config or cmd in config:
cmd = 'no %s' % cmd
return cmd
elif not config or cmd not in config:
return cmd
def _render_route_server_client(self, item, config=None):
cmd = 'neighbor %s route-server-client' % item['neighbor']
if item['route_server_client'] is False:
if not config or cmd in config:
cmd = 'no %s' % cmd
return cmd
elif not config or cmd not in config:
return cmd
def _render_remove_private_as(self, item, config=None):
cmd = 'neighbor %s remove-private-as' % item['neighbor']
if item['remove_private_as'] is False:
if not config or cmd in config:
cmd = 'no %s' % cmd
return cmd
elif not config or cmd not in config:
return cmd
def _render_next_hop_self(self, item, config=None):
cmd = 'neighbor %s next-hop-self' % item['neighbor']
if item['next_hop_self'] is False:
if not config or cmd in config:
cmd = 'no %s' % cmd
return cmd
elif not config or cmd not in config:
return cmd
def _render_activate(self, item, config=None):
cmd = 'neighbor %s activate' % item['neighbor']
if item['activate'] is False:
if not config or cmd in config:
cmd = 'no %s' % cmd
return cmd
elif not config or cmd not in config:
return cmd
def _render_maximum_prefix(self, item, config=None):
cmd = 'neighbor %s maximum-prefix %s' % (item['neighbor'], item['maximum_prefix'])
if not config or cmd not in config:
return cmd
def _render_prefix_list_in(self, item, config=None):
cmd = 'neighbor %s prefix-list %s in' % (item['neighbor'], item['prefix_list_in'])
if not config or cmd not in config:
return cmd
def _render_prefix_list_out(self, item, config=None):
cmd = 'neighbor %s prefix-list %s out' % (item['neighbor'], item['prefix_list_out'])
if not config or cmd not in config:
return cmd

@ -1,140 +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.ios.providers.providers import register_provider
from ansible.module_utils.network.ios.providers.providers import CliProvider
from ansible.module_utils.network.ios.providers.cli.config.bgp.neighbors import Neighbors
from ansible.module_utils.network.ios.providers.cli.config.bgp.address_family import AddressFamily
from ansible.module_utils.common.network import to_netmask
REDISTRIBUTE_PROTOCOLS = frozenset(['ospf', 'ospfv3', 'eigrp', 'isis', 'static', 'connected',
'odr', 'lisp', 'mobile', 'rip'])
@register_provider('ios', 'ios_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:
self._validate_input(config)
if operation == 'replace':
if existing_as and int(existing_as) != self.get_value('config.bgp_as'):
commands.append('no router bgp %s' % existing_as)
config = None
elif operation == 'override':
if existing_as:
commands.append('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 cmd
elif log_neighbor_changes is False:
if config and cmd in config:
return 'no %s' % cmd
def _render_networks(self, config=None):
commands = list()
safe_list = list()
for entry in self.get_value('config.networks'):
network = entry['prefix']
cmd = 'network %s' % network
if entry['masklen'] and entry['masklen'] not in (24, 16, 8):
cmd += ' mask %s' % to_netmask(entry['masklen'])
network += ' mask %s' % to_netmask(entry['masklen'])
if entry['route_map']:
cmd += ' route-map %s' % entry['route_map']
network += ' route-map %s' % entry['route_map']
safe_list.append(network)
if not config or cmd not in config:
commands.append(cmd)
if self.params['operation'] == 'replace':
if config:
matches = re.findall(r'network (.*)', config, re.M)
for entry in set(matches).difference(safe_list):
commands.append('no network %s' % entry)
return commands
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)
def _validate_input(self, config=None):
def device_has_AF(config):
return re.search(r'address-family (?:.*)', config)
address_family = self.get_value('config.address_family')
root_networks = self.get_value('config.networks')
operation = self.params['operation']
if operation == 'replace':
if address_family and root_networks:
for item in address_family:
if item['networks']:
raise ValueError('operation is replace but provided both root level network(s) and network(s) under %s %s address family'
% (item['afi'], item['safi']))
if root_networks and config and device_has_AF(config):
raise ValueError('operation is replace and device has one or more address family activated but root level network(s) provided')

@ -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.ios.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,328 +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
import socket
from ansible.module_utils.six import iteritems
from ansible.module_utils.network.common.utils import is_masklen, to_netmask
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 check_n_return_valid_ipv6_addr(module, input_list, filtered_ipv6_list):
# To verify the valid ipv6 address
try:
for each in input_list:
if '::' in each:
if '/' in each:
each = each.split('/')[0]
if socket.inet_pton(socket.AF_INET6, each):
filtered_ipv6_list.append(each)
return filtered_ipv6_list
except socket.error:
module.fail_json(msg='Incorrect IPV6 address!')
def new_dict_to_set(input_dict, temp_list, test_set, count=0):
# recursive function to convert input dict to set for comparision
test_dict = dict()
if isinstance(input_dict, dict):
input_dict_len = len(input_dict)
for k, v in sorted(iteritems(input_dict)):
count += 1
if isinstance(v, list):
temp_list.append(k)
for each in v:
if isinstance(each, dict):
if [True for i in each.values() if type(i) == list]:
new_dict_to_set(each, temp_list, test_set, count)
else:
new_dict_to_set(each, temp_list, test_set, 0)
else:
if v is not None:
test_dict.update({k: v})
try:
if tuple(iteritems(test_dict)) not in test_set and count == input_dict_len:
test_set.add(tuple(iteritems(test_dict)))
count = 0
except TypeError:
temp_dict = {}
def expand_dict(dict_to_expand):
temp = dict()
for k, v in iteritems(dict_to_expand):
if isinstance(v, dict):
expand_dict(v)
else:
if v is not None:
temp.update({k: v})
temp_dict.update(tuple(iteritems(temp)))
new_dict = {k: v}
expand_dict(new_dict)
if tuple(iteritems(temp_dict)) not in test_set:
test_set.add(tuple(iteritems(temp_dict)))
def dict_to_set(sample_dict):
# Generate a set with passed dictionary for comparison
test_dict = 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()
name = want.get('name')
if name:
test_dict['name'] = name
diff_ip = False
for k, v in iteritems(want):
if isinstance(v, dict):
for key, value in iteritems(v):
test_key_dict = dict()
if value is None:
dict_val = have.get(k).get(key)
test_key_dict.update({key: dict_val})
elif k == 'ipv6' and value.lower() != have.get(k)[0].get(key).lower():
# as multiple IPV6 address can be configured on same
# interface, for replace state in place update will
# actually create new entry, which isn't as expected
# for replace state, so in case of IPV6 address
# every time 1st delete the existing IPV6 config and
# then apply the new change
dict_val = have.get(k)[0].get(key)
test_key_dict.update({key: dict_val})
if test_key_dict:
test_dict.update({k: test_key_dict})
if isinstance(v, list):
for key, value in iteritems(v[0]):
test_key_dict = dict()
if value is None:
dict_val = have.get(k).get(key)
test_key_dict.update({key: dict_val})
elif k == 'ipv6' and value.lower() != have.get(k)[0].get(key).lower():
dict_val = have.get(k)[0].get(key)
test_key_dict.update({key: dict_val})
if test_key_dict:
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')
if len(want_ip) > 1 and have_ip and have_ip[0].get('secondary'):
have_ip = have_ip[0]['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})
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 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 netmask_to_cidr(netmask):
bit_range = [128, 64, 32, 16, 8, 4, 2, 1]
count = 0
cidr = 0
netmask_list = netmask.split('.')
netmask_calc = [i for i in netmask_list if int(i) != 255 and int(i) != 0]
if netmask_calc:
netmask_calc_index = netmask_list.index(netmask_calc[0])
elif sum(list(map(int, netmask_list))) == 0:
return '32'
else:
return '24'
for each in bit_range:
if cidr == int(netmask.split('.')[2]):
if netmask_calc_index == 1:
return str(8 + count)
elif netmask_calc_index == 2:
return str(8 * 2 + count)
elif netmask_calc_index == 3:
return str(8 * 3 + count)
break
cidr += each
count += 1
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('te'):
if_type = 'TenGigabitEthernet'
elif name.lower().startswith('fa'):
if_type = 'FastEthernet'
elif name.lower().startswith('fo'):
if_type = 'FortyGigabitEthernet'
elif name.lower().startswith('long'):
if_type = 'LongReachEthernet'
elif name.lower().startswith('et'):
if_type = 'Ethernet'
elif name.lower().startswith('vl'):
if_type = 'Vlan'
elif name.lower().startswith('lo'):
if_type = 'loopback'
elif name.lower().startswith('po'):
if_type = 'Port-channel'
elif name.lower().startswith('nv'):
if_type = 'nve'
elif name.lower().startswith('twe'):
if_type = 'TwentyFiveGigE'
elif name.lower().startswith('hu'):
if_type = 'HundredGigE'
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('TE'):
return 'TenGigabitEthernet'
elif interface.upper().startswith('FA'):
return 'FastEthernet'
elif interface.upper().startswith('FO'):
return 'FortyGigabitEthernet'
elif interface.upper().startswith('LON'):
return 'LongReachEthernet'
elif interface.upper().startswith('ET'):
return 'Ethernet'
elif interface.upper().startswith('VL'):
return 'Vlan'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('PO'):
return 'Port-channel'
elif interface.upper().startswith('NV'):
return 'nve'
elif interface.upper().startswith('TWE'):
return 'TwentyFiveGigE'
elif interface.upper().startswith('HU'):
return 'HundredGigE'
else:
return 'unknown'

@ -1,495 +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': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_interface
version_added: "2.4"
author: "Ganesh Nalawade (@ganeshrn)"
short_description: Manage Interface on Cisco IOS network devices
description:
- This module provides declarative management of Interfaces
on Cisco IOS network devices.
deprecated:
removed_in: '2.13'
alternative: ios_interfaces
why: Newer and updated modules released with more functionality in Ansible 2.9
notes:
- Tested against IOS 15.6
options:
name:
description:
- Name of the Interface.
required: true
description:
description:
- Description of Interface.
enabled:
description:
- Interface link status.
type: bool
speed:
description:
- Interface link speed.
mtu:
description:
- Maximum size of transmit packet.
duplex:
description:
- Interface link status
default: auto
choices: ['full', 'half', 'auto']
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)
neighbors:
description:
- Check the operational state of given interface C(name) for CDP/LLDP neighbor.
- The following suboptions are available.
suboptions:
host:
description:
- "CDP/LLDP neighbor host for given interface C(name)."
port:
description:
- "CDP/LLDP neighbor port to which given interface C(name) is connected."
aggregate:
description: List of Interfaces definitions.
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']
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: configure interface
ios_interface:
name: GigabitEthernet0/2
description: test-interface
speed: 100
duplex: half
mtu: 512
- name: remove interface
ios_interface:
name: Loopback9
state: absent
- name: make interface up
ios_interface:
name: GigabitEthernet0/2
enabled: True
- name: make interface down
ios_interface:
name: GigabitEthernet0/2
enabled: False
- name: Check intent arguments
ios_interface:
name: GigabitEthernet0/2
state: up
tx_rate: ge(0)
rx_rate: le(0)
- name: Check neighbors intent arguments
ios_interface:
name: Gi0/0
neighbors:
- port: eth0
host: netdev
- name: Config + intent
ios_interface:
name: GigabitEthernet0/2
enabled: False
state: down
- name: Add interface using aggregate
ios_interface:
aggregate:
- { name: GigabitEthernet0/1, mtu: 256, description: test-interface-1 }
- { name: GigabitEthernet0/2, mtu: 516, description: test-interface-2 }
duplex: full
speed: 100
state: present
- name: Delete interface using aggregate
ios_interface:
aggregate:
- name: Loopback9
- name: Loopback10
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device.
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- interface GigabitEthernet0/2
- description test-interface
- duplex half
- mtu 512
"""
import re
from copy import deepcopy
from time import sleep
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import exec_command
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
from ansible.module_utils.network.common.config import NetworkConfig
from ansible.module_utils.network.common.utils import conditional, remove_default_spec
def validate_mtu(value, module):
if value and not 64 <= int(value) <= 9600:
module.fail_json(msg='mtu must be between 64 and 9600')
def validate_param_values(module, obj, param=None):
if param is None:
param = module.params
for key in obj:
# validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key)
if callable(validator):
validator(param.get(key), module)
def parse_shutdown(configobj, name):
cfg = configobj['interface %s' % name]
cfg = '\n'.join(cfg.children)
match = re.search(r'^shutdown', cfg, re.M)
if match:
return True
else:
return False
def parse_config_argument(configobj, name, arg=None):
cfg = configobj['interface %s' % name]
cfg = '\n'.join(cfg.children)
match = re.search(r'%s (.+)$' % arg, cfg, re.M)
if match:
return match.group(1)
def search_obj_in_list(name, lst):
for o in lst:
if o['name'] == name:
return o
return None
def add_command_to_interface(interface, cmd, commands):
if interface not in commands:
commands.append(interface)
commands.append(cmd)
def map_config_to_obj(module):
config = get_config(module)
configobj = NetworkConfig(indent=1, contents=config)
match = re.findall(r'^interface (\S+)', config, re.M)
if not match:
return list()
instances = list()
for item in set(match):
obj = {
'name': item,
'description': parse_config_argument(configobj, item, 'description'),
'speed': parse_config_argument(configobj, item, 'speed'),
'duplex': parse_config_argument(configobj, item, 'duplex'),
'mtu': parse_config_argument(configobj, item, 'mtu'),
'disable': True if parse_shutdown(configobj, item) else False,
'state': 'present'
}
instances.append(obj)
return instances
def map_params_to_obj(module):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
validate_param_values(module, item, item)
d = item.copy()
if d['enabled']:
d['disable'] = False
else:
d['disable'] = True
obj.append(d)
else:
params = {
'name': module.params['name'],
'description': module.params['description'],
'speed': module.params['speed'],
'mtu': module.params['mtu'],
'duplex': module.params['duplex'],
'state': module.params['state'],
'delay': module.params['delay'],
'tx_rate': module.params['tx_rate'],
'rx_rate': module.params['rx_rate'],
'neighbors': module.params['neighbors']
}
validate_param_values(module, params)
if module.params['enabled']:
params.update({'disable': False})
else:
params.update({'disable': True})
obj.append(params)
return obj
def map_obj_to_commands(updates):
commands = list()
want, have = updates
args = ('speed', 'description', 'duplex', 'mtu')
for w in want:
name = w['name']
disable = w['disable']
state = w['state']
obj_in_have = search_obj_in_list(name, have)
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 = w.get(item)
running = obj_in_have.get(item)
if candidate != running:
if candidate:
cmd = item + ' ' + str(candidate)
add_command_to_interface(interface, cmd, commands)
if disable and not obj_in_have.get('disable', False):
add_command_to_interface(interface, 'shutdown', commands)
elif not disable and obj_in_have.get('disable', False):
add_command_to_interface(interface, 'no shutdown', commands)
else:
commands.append(interface)
for item in args:
value = w.get(item)
if value:
commands.append(item + ' ' + str(value))
if disable:
commands.append('no shutdown')
return commands
def check_declarative_intent_params(module, want, result):
failed_conditions = []
have_neighbors_lldp = None
have_neighbors_cdp = None
for w in want:
want_state = w.get('state')
want_tx_rate = w.get('tx_rate')
want_rx_rate = w.get('rx_rate')
want_neighbors = w.get('neighbors')
if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate and not want_neighbors:
continue
if result['changed']:
sleep(w['delay'])
command = 'show interfaces %s' % w['name']
rc, out, err = exec_command(module, command)
if rc != 0:
module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc)
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 is None or not conditional(want_state, have_state.strip()):
failed_conditions.append('state ' + 'eq(%s)' % 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 want_neighbors:
have_host = []
have_port = []
# Process LLDP neighbors
if have_neighbors_lldp is None:
rc, have_neighbors_lldp, err = exec_command(module, 'show lldp neighbors detail')
if rc != 0:
module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc)
if have_neighbors_lldp:
lines = have_neighbors_lldp.strip().split('Local Intf: ')
for line in lines:
field = line.split('\n')
if field[0].strip() == w['name']:
for item in field:
if item.startswith('System Name:'):
have_host.append(item.split(':')[1].strip())
if item.startswith('Port Description:'):
have_port.append(item.split(':')[1].strip())
# Process CDP neighbors
if have_neighbors_cdp is None:
rc, have_neighbors_cdp, err = exec_command(module, 'show cdp neighbors detail')
if rc != 0:
module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc)
if have_neighbors_cdp:
neighbors_cdp = re.findall('Device ID: (.*?)\n.*?Interface: (.*?), Port ID .outgoing port.: (.*?)\n', have_neighbors_cdp, re.S)
for host, localif, remoteif in neighbors_cdp:
if localif == w['name']:
have_host.append(host)
have_port.append(remoteif)
for item in want_neighbors:
host = item.get('host')
port = item.get('port')
if host and host not in have_host:
failed_conditions.append('host ' + host)
if port and port not in have_port:
failed_conditions.append('port ' + port)
return failed_conditions
def main():
""" main entry point for module execution
"""
neighbors_spec = dict(
host=dict(),
port=dict()
)
element_spec = dict(
name=dict(),
description=dict(),
speed=dict(),
mtu=dict(),
duplex=dict(choices=['full', 'half', 'auto']),
enabled=dict(default=True, type='bool'),
tx_rate=dict(),
rx_rate=dict(),
neighbors=dict(type='list', elements='dict', options=neighbors_spec),
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(ios_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)
warnings = list()
result = {'changed': False}
if warnings:
result['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:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
failed_conditions = check_declarative_intent_params(module, want, result)
if failed_conditions:
msg = 'One or more conditional statements have not been satisfied'
module.fail_json(msg=msg, failed_conditions=failed_conditions)
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,499 +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': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_l2_interface
extends_documentation_fragment: ios
version_added: "2.5"
short_description: Manage Layer-2 interface on Cisco IOS devices.
description:
- This module provides declarative management of Layer-2 interfaces on
Cisco IOS devices.
deprecated:
removed_in: '2.13'
alternative: ios_l2_interfaces
why: Newer and updated modules released with more functionality in Ansible 2.9
author:
- Nathaniel Case (@Qalthos)
options:
name:
description:
- Full name of the interface excluding any logical
unit number, i.e. GigabitEthernet0/1.
required: true
aliases: ['interface']
mode:
description:
- Mode in which interface needs to be configured.
default: access
choices: ['access', 'trunk']
access_vlan:
description:
- Configure given VLAN in access port.
If C(mode=access), used as the access VLAN ID.
trunk_vlans:
description:
- List of VLANs to be configured in trunk port.
If C(mode=trunk), used as the VLAN range to ADD or REMOVE
from the trunk.
native_vlan:
description:
- Native VLAN to be configured in trunk port.
If C(mode=trunk), used as the trunk native VLAN ID.
trunk_allowed_vlans:
description:
- List of allowed VLANs in a given trunk port.
If C(mode=trunk), these are the only VLANs that will be
configured on the trunk, i.e. "2-10,15".
aggregate:
description:
- List of Layer-2 interface definitions.
state:
description:
- Manage the state of the Layer-2 Interface configuration.
default: present
choices: ['present','absent', 'unconfigured']
"""
EXAMPLES = """
- name: Ensure GigabitEthernet0/5 is in its default l2 interface state
ios_l2_interface:
name: GigabitEthernet0/5
state: unconfigured
- name: Ensure GigabitEthernet0/5 is configured for access vlan 20
ios_l2_interface:
name: GigabitEthernet0/5
mode: access
access_vlan: 20
- name: Ensure GigabitEthernet0/5 only has vlans 5-10 as trunk vlans
ios_l2_interface:
name: GigabitEthernet0/5
mode: trunk
native_vlan: 10
trunk_allowed_vlans: 5-10
- name: Ensure GigabitEthernet0/5 is a trunk port and ensure 2-50 are being tagged (doesn't mean others aren't also being tagged)
ios_l2_interface:
name: GigabitEthernet0/5
mode: trunk
native_vlan: 10
trunk_vlans: 2-50
- name: Ensure these VLANs are not being tagged on the trunk
ios_l2_interface:
name: GigabitEthernet0/5
mode: trunk
trunk_vlans: 51-4094
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- interface GigabitEthernet0/5
- switchport access vlan 20
"""
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.ios.ios import load_config, run_commands
from ansible.module_utils.network.ios.ios import ios_argument_spec
def get_interface_type(interface):
intf_type = 'unknown'
if interface.upper()[:2] in ('ET', 'GI', 'FA', 'TE', 'FO', 'HU', 'TWE', 'TW'):
intf_type = 'ethernet'
elif interface.upper().startswith('VL'):
intf_type = 'svi'
elif interface.upper().startswith('LO'):
intf_type = 'loopback'
elif interface.upper()[:2] in ('MG', 'MA'):
intf_type = 'management'
elif interface.upper().startswith('PO'):
intf_type = 'portchannel'
elif interface.upper().startswith('NV'):
intf_type = 'nve'
return intf_type
def is_switchport(name, module):
intf_type = get_interface_type(name)
if intf_type in ('ethernet', 'portchannel'):
config = run_commands(module, ['show interface {0} switchport'.format(name)])[0]
match = re.search(r'Switchport: Enabled', config)
return bool(match)
return False
def interface_is_portchannel(name, module):
if get_interface_type(name) == 'ethernet':
config = run_commands(module, ['show run interface {0}'.format(name)])[0]
if any(c in config for c in ['channel group', 'channel-group']):
return True
return False
def get_switchport(name, module):
config = run_commands(module, ['show interface {0} switchport'.format(name)])[0]
mode = re.search(r'Administrative Mode: (?:.* )?(\w+)$', config, re.M)
access = re.search(r'Access Mode VLAN: (\d+)', config)
native = re.search(r'Trunking Native Mode VLAN: (\d+)', config)
trunk = re.search(r'Trunking VLANs Enabled: (.+)$', config, re.M)
if mode:
mode = mode.group(1)
if access:
access = access.group(1)
if native:
native = native.group(1)
if trunk:
trunk = trunk.group(1)
if trunk == 'ALL':
trunk = '1-4094'
switchport_config = {
"interface": name,
"mode": mode,
"access_vlan": access,
"native_vlan": native,
"trunk_vlans": trunk,
}
return switchport_config
def remove_switchport_config_commands(name, existing, proposed, module):
mode = proposed.get('mode')
commands = []
command = None
if mode == 'access':
av_check = existing.get('access_vlan') == proposed.get('access_vlan')
if av_check:
command = 'no switchport access vlan {0}'.format(existing.get('access_vlan'))
commands.append(command)
elif mode == 'trunk':
# Supported Remove Scenarios for trunk_vlans_list
# 1) Existing: 1,2,3 Proposed: 1,2,3 - Remove all
# 2) Existing: 1,2,3 Proposed: 1,2 - Remove 1,2 Leave 3
# 3) Existing: 1,2,3 Proposed: 2,3 - Remove 2,3 Leave 1
# 4) Existing: 1,2,3 Proposed: 4,5,6 - None removed.
# 5) Existing: None Proposed: 1,2,3 - None removed.
existing_vlans = existing.get('trunk_vlans_list')
proposed_vlans = proposed.get('trunk_vlans_list')
vlans_to_remove = set(proposed_vlans).intersection(existing_vlans)
if vlans_to_remove:
proposed_allowed_vlans = proposed.get('trunk_allowed_vlans')
remove_trunk_allowed_vlans = proposed.get('trunk_vlans', proposed_allowed_vlans)
command = 'switchport trunk allowed vlan remove {0}'.format(remove_trunk_allowed_vlans)
commands.append(command)
native_check = existing.get('native_vlan') == proposed.get('native_vlan')
if native_check and proposed.get('native_vlan'):
command = 'no switchport trunk native vlan {0}'.format(existing.get('native_vlan'))
commands.append(command)
if commands:
commands.insert(0, 'interface ' + name)
return commands
def get_switchport_config_commands(name, existing, proposed, module):
"""Gets commands required to config a given switchport interface
"""
proposed_mode = proposed.get('mode')
existing_mode = existing.get('mode')
commands = []
command = None
if proposed_mode != existing_mode:
if proposed_mode == 'trunk':
command = 'switchport mode trunk'
elif proposed_mode == 'access':
command = 'switchport mode access'
if command:
commands.append(command)
if proposed_mode == 'access':
av_check = str(existing.get('access_vlan')) == str(proposed.get('access_vlan'))
if not av_check:
command = 'switchport access vlan {0}'.format(proposed.get('access_vlan'))
commands.append(command)
elif proposed_mode == 'trunk':
tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list')
if not tv_check:
if proposed.get('allowed'):
command = 'switchport trunk allowed vlan {0}'.format(proposed.get('trunk_allowed_vlans'))
commands.append(command)
else:
existing_vlans = existing.get('trunk_vlans_list')
proposed_vlans = proposed.get('trunk_vlans_list')
vlans_to_add = set(proposed_vlans).difference(existing_vlans)
if vlans_to_add:
command = 'switchport trunk allowed vlan add {0}'.format(proposed.get('trunk_vlans'))
commands.append(command)
native_check = str(existing.get('native_vlan')) == str(proposed.get('native_vlan'))
if not native_check and proposed.get('native_vlan'):
command = 'switchport trunk native vlan {0}'.format(proposed.get('native_vlan'))
commands.append(command)
if commands:
commands.insert(0, 'interface ' + name)
return commands
def is_switchport_default(existing):
"""Determines if switchport has a default config based on mode
Args:
existing (dict): existing switchport configuration from Ansible mod
Returns:
boolean: True if switchport has OOB Layer 2 config, i.e.
vlan 1 and trunk all and mode is access
"""
c1 = str(existing['access_vlan']) == '1'
c2 = str(existing['native_vlan']) == '1'
c3 = existing['trunk_vlans'] == '1-4094'
c4 = existing['mode'] == 'access'
default = c1 and c2 and c3 and c4
return default
def default_switchport_config(name):
commands = []
commands.append('interface ' + name)
commands.append('switchport mode access')
commands.append('switch access vlan 1')
commands.append('switchport trunk native vlan 1')
commands.append('switchport trunk allowed vlan all')
return commands
def vlan_range_to_list(vlans):
result = []
if vlans:
for part in vlans.split(','):
if part.lower() == 'none':
break
if part:
if '-' in part:
start, stop = (int(i) for i in part.split('-'))
result.extend(range(start, stop + 1))
else:
result.append(int(part))
return sorted(result)
def get_list_of_vlans(module):
config = run_commands(module, ['show vlan'])[0]
vlans = set()
lines = config.strip().splitlines()
for line in lines:
line_parts = line.split()
if line_parts:
try:
int(line_parts[0])
except ValueError:
continue
vlans.add(line_parts[0])
return list(vlans)
def flatten_list(commands):
flat_list = []
for command in commands:
if isinstance(command, list):
flat_list.extend(command)
else:
flat_list.append(command)
return flat_list
def map_params_to_obj(module):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append({
'name': module.params['name'],
'mode': module.params['mode'],
'access_vlan': module.params['access_vlan'],
'native_vlan': module.params['native_vlan'],
'trunk_vlans': module.params['trunk_vlans'],
'trunk_allowed_vlans': module.params['trunk_allowed_vlans'],
'state': module.params['state']
})
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(type='str', aliases=['interface']),
mode=dict(choices=['access', 'trunk']),
access_vlan=dict(type='str'),
native_vlan=dict(type='str'),
trunk_vlans=dict(type='str'),
trunk_allowed_vlans=dict(type='str'),
state=dict(choices=['absent', 'present', 'unconfigured'], default='present')
)
aggregate_spec = deepcopy(element_spec)
# 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(ios_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=[['access_vlan', 'trunk_vlans'],
['access_vlan', 'native_vlan'],
['access_vlan', 'trunk_allowed_vlans']],
supports_check_mode=True)
warnings = list()
commands = []
result = {'changed': False, 'warnings': warnings}
want = map_params_to_obj(module)
for w in want:
name = w['name']
mode = w['mode']
access_vlan = w['access_vlan']
state = w['state']
trunk_vlans = w['trunk_vlans']
native_vlan = w['native_vlan']
trunk_allowed_vlans = w['trunk_allowed_vlans']
args = dict(name=name, mode=mode, access_vlan=access_vlan,
native_vlan=native_vlan, trunk_vlans=trunk_vlans,
trunk_allowed_vlans=trunk_allowed_vlans)
proposed = dict((k, v) for k, v in args.items() if v is not None)
name = name.lower()
if mode == 'access' and state == 'present' and not access_vlan:
module.fail_json(msg='access_vlan param is required when mode=access && state=present')
if mode == 'trunk' and access_vlan:
module.fail_json(msg='access_vlan param not supported when using mode=trunk')
if not is_switchport(name, module):
module.fail_json(msg='Ensure interface is configured to be a L2'
'\nport first before using this module. You can use'
'\nthe ios_interface module for this.')
if interface_is_portchannel(name, module):
module.fail_json(msg='Cannot change L2 config on physical '
'\nport because it is in a portchannel. '
'\nYou should update the portchannel config.')
# existing will never be null for Eth intfs as there is always a default
existing = get_switchport(name, module)
# Safeguard check
# If there isn't an existing, something is wrong per previous comment
if not existing:
module.fail_json(msg='Make sure you are using the FULL interface name')
if trunk_vlans or trunk_allowed_vlans:
if trunk_vlans:
trunk_vlans_list = vlan_range_to_list(trunk_vlans)
elif trunk_allowed_vlans:
trunk_vlans_list = vlan_range_to_list(trunk_allowed_vlans)
proposed['allowed'] = True
existing_trunks_list = vlan_range_to_list((existing['trunk_vlans']))
existing['trunk_vlans_list'] = existing_trunks_list
proposed['trunk_vlans_list'] = trunk_vlans_list
current_vlans = get_list_of_vlans(module)
if state == 'present':
if access_vlan and access_vlan not in current_vlans:
module.fail_json(msg='You are trying to configure a VLAN'
' on an interface that\ndoes not exist on the '
' switch yet!', vlan=access_vlan)
elif native_vlan and native_vlan not in current_vlans:
module.fail_json(msg='You are trying to configure a VLAN'
' on an interface that\ndoes not exist on the '
' switch yet!', vlan=native_vlan)
else:
command = get_switchport_config_commands(name, existing, proposed, module)
commands.append(command)
elif state == 'unconfigured':
is_default = is_switchport_default(existing)
if not is_default:
command = default_switchport_config(name)
commands.append(command)
elif state == 'absent':
command = remove_switchport_config_commands(name, existing, proposed, module)
commands.append(command)
if trunk_vlans or trunk_allowed_vlans:
existing.pop('trunk_vlans_list')
proposed.pop('trunk_vlans_list')
cmds = flatten_list(commands)
if cmds:
if module.check_mode:
module.exit_json(changed=True, commands=cmds)
else:
result['changed'] = True
load_config(module, cmds)
if 'configure' in cmds:
cmds.pop(0)
result['commands'] = cmds
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,327 +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: ios_l3_interface
version_added: "2.5"
author: "Ganesh Nalawade (@ganeshrn)"
short_description: Manage Layer-3 interfaces on Cisco IOS network devices.
description:
- This module provides declarative management of Layer-3 interfaces
on IOS network devices.
deprecated:
removed_in: '2.13'
alternative: ios_l3_interfaces
why: Newer and updated modules released with more functionality in Ansible 2.9
notes:
- Tested against IOS 15.2
options:
name:
description:
- Name of the Layer-3 interface to be configured eg. GigabitEthernet0/2
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
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
aggregate:
description:
- List of Layer-3 interfaces definitions. Each of the entry in aggregate list should
define name of interface C(name) and a optional C(ipv4) or C(ipv6) address.
state:
description:
- State of the Layer-3 interface configuration. It indicates if the configuration should
be present or absent on remote device.
default: present
choices: ['present', 'absent']
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: Remove GigabitEthernet0/3 IPv4 and IPv6 address
ios_l3_interface:
name: GigabitEthernet0/3
state: absent
- name: Set GigabitEthernet0/3 IPv4 address
ios_l3_interface:
name: GigabitEthernet0/3
ipv4: 192.168.0.1/24
- name: Set GigabitEthernet0/3 IPv6 address
ios_l3_interface:
name: GigabitEthernet0/3
ipv6: "fd5d:12c9:2201:1::1/64"
- name: Set GigabitEthernet0/3 in dhcp
ios_l3_interface:
name: GigabitEthernet0/3
ipv4: dhcp
ipv6: dhcp
- name: Set interface Vlan1 (SVI) IPv4 address
ios_l3_interface:
name: Vlan1
ipv4: 192.168.0.5/24
- name: Set IP addresses on aggregate
ios_l3_interface:
aggregate:
- { name: GigabitEthernet0/3, ipv4: 192.168.2.10/24 }
- { name: GigabitEthernet0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" }
- name: Remove IP addresses on aggregate
ios_l3_interface:
aggregate:
- { name: GigabitEthernet0/3, ipv4: 192.168.2.10/24 }
- { name: GigabitEthernet0/3, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" }
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- interface GigabitEthernet0/2
- ip address 192.168.0.1 255.255.255.0
- ipv6 address fd5d:12c9:2201:1::1/64
"""
import re
from copy import deepcopy
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
from ansible.module_utils.network.common.config import NetworkConfig
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.common.utils import is_netmask, is_masklen, to_netmask, to_masklen
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 %s' % value)
if not is_masklen(address[1]):
module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-32' % 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 %s' % value)
else:
if not 0 <= int(address[1]) <= 128:
module.fail_json(msg='invalid value for mask: %s, mask should be in range 0-128' % address[1])
def validate_param_values(module, obj, param=None):
if param is None:
param = module.params
for key in obj:
# validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key)
if callable(validator):
validator(param.get(key), module)
def parse_config_argument(configobj, name, arg=None):
cfg = configobj['interface %s' % name]
cfg = '\n'.join(cfg.children)
values = []
matches = re.finditer(r'%s (.+)$' % arg, cfg, re.M)
for match in matches:
match_str = match.group(1).strip()
if arg == 'ipv6 address':
values.append(match_str)
else:
values = match_str
break
return values or None
def search_obj_in_list(name, lst):
for o in lst:
if o['name'] == name:
return o
return None
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w['name']
ipv4 = w['ipv4']
ipv6 = w['ipv6']
state = w['state']
interface = 'interface ' + name
commands.append(interface)
obj_in_have = search_obj_in_list(name, have)
if state == 'absent' and obj_in_have:
if obj_in_have['ipv4']:
if ipv4:
address = ipv4.split('/')
if len(address) == 2:
ipv4 = '{0} {1}'.format(address[0], to_netmask(address[1]))
commands.append('no ip address {0}'.format(ipv4))
else:
commands.append('no ip address')
if obj_in_have['ipv6']:
if ipv6:
commands.append('no ipv6 address {0}'.format(ipv6))
else:
commands.append('no ipv6 address')
if 'dhcp' in obj_in_have['ipv6']:
commands.append('no ipv6 address dhcp')
elif state == 'present':
if ipv4:
if obj_in_have is None or obj_in_have.get('ipv4') is None or ipv4 != obj_in_have['ipv4']:
address = ipv4.split('/')
if len(address) == 2:
ipv4 = '{0} {1}'.format(address[0], to_netmask(address[1]))
commands.append('ip address {0}'.format(ipv4))
if ipv6:
if obj_in_have is None or obj_in_have.get('ipv6') is None or ipv6.lower() not in [addr.lower() for addr in obj_in_have['ipv6']]:
commands.append('ipv6 address {0}'.format(ipv6))
if commands[-1] == interface:
commands.pop(-1)
return commands
def map_config_to_obj(module):
config = get_config(module)
configobj = NetworkConfig(indent=1, contents=config)
match = re.findall(r'^interface (\S+)', config, re.M)
if not match:
return list()
instances = list()
for item in set(match):
ipv4 = parse_config_argument(configobj, item, 'ip address')
if ipv4:
# eg. 192.168.2.10 255.255.255.0 -> 192.168.2.10/24
address = ipv4.strip().split(' ')
if len(address) == 2 and is_netmask(address[1]):
ipv4 = '{0}/{1}'.format(address[0], to_text(to_masklen(address[1])))
obj = {
'name': item,
'ipv4': ipv4,
'ipv6': parse_config_argument(configobj, item, 'ipv6 address'),
'state': 'present'
}
instances.append(obj)
return instances
def map_params_to_obj(module):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
validate_param_values(module, item, item)
obj.append(item.copy())
else:
obj.append({
'name': module.params['name'],
'ipv4': module.params['ipv4'],
'ipv6': module.params['ipv6'],
'state': module.params['state']
})
validate_param_values(module, obj)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
ipv4=dict(),
ipv6=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)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(ios_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)
warnings = list()
result = {'changed': False}
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result['commands'] = commands
if commands:
if not module.check_mode:
resp = load_config(module, commands)
warnings.extend((out for out in resp if out))
result['changed'] = True
if warnings:
result['warnings'] = warnings
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,350 +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': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_vlan
version_added: "2.5"
author: "Trishna Guha (@trishnaguha)"
short_description: Manage VLANs on IOS network devices
description:
- This module provides declarative management of VLANs
on Cisco IOS network devices.
deprecated:
removed_in: '2.13'
alternative: ios_vlans
why: Newer and updated modules released with more functionality in Ansible 2.9
notes:
- Tested against IOS 15.2
options:
name:
description:
- Name of the VLAN.
vlan_id:
description:
- ID of the VLAN. Range 1-4094.
required: true
interfaces:
description:
- List of interfaces that should be associated to the VLAN.
required: true
associated_interfaces:
description:
- This is a intent option and checks the operational state of the for given vlan C(name)
for associated interfaces. If the value in the C(associated_interfaces) does not match with
the operational state of vlan interfaces on device it will result in failure.
version_added: "2.5"
delay:
description:
- Delay the play should wait to check for declarative intent params values.
default: 10
aggregate:
description: List of VLANs definitions.
purge:
description:
- Purge VLANs not defined in the I(aggregate) parameter.
default: no
type: bool
state:
description:
- State of the VLAN configuration.
default: present
choices: ['present', 'absent', 'active', 'suspend']
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: Create vlan
ios_vlan:
vlan_id: 100
name: test-vlan
state: present
- name: Add interfaces to VLAN
ios_vlan:
vlan_id: 100
interfaces:
- GigabitEthernet0/0
- GigabitEthernet0/1
- name: Check if interfaces is assigned to VLAN
ios_vlan:
vlan_id: 100
associated_interfaces:
- GigabitEthernet0/0
- GigabitEthernet0/1
- name: Delete vlan
ios_vlan:
vlan_id: 100
state: absent
- name: Add vlan using aggregate
ios_vlan:
aggregate:
- { vlan_id: 100, name: test-vlan, interfaces: [GigabitEthernet0/1, GigabitEthernet0/2], delay: 15, state: suspend }
- { vlan_id: 101, name: test-vlan, interfaces: GigabitEthernet0/3 }
- name: Move interfaces to a different VLAN
ios_vlan:
vlan_id: 102
interfaces:
- GigabitEthernet0/0
- GigabitEthernet0/1
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- vlan 100
- name test-vlan
"""
import re
import time
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.ios.ios import load_config, run_commands, normalize_interface
from ansible.module_utils.network.ios.ios import ios_argument_spec
def search_obj_in_list(vlan_id, lst):
for o in lst:
if o['vlan_id'] == vlan_id:
return o
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
purge = module.params['purge']
for w in want:
vlan_id = w['vlan_id']
name = w['name']
interfaces = w['interfaces']
state = w['state']
obj_in_have = search_obj_in_list(vlan_id, have)
if state == 'absent':
if obj_in_have:
commands.append('no vlan {0}'.format(vlan_id))
elif state == 'present':
if not obj_in_have:
commands.append('vlan {0}'.format(vlan_id))
if name:
commands.append('name {0}'.format(name))
if interfaces:
for i in interfaces:
commands.append('interface {0}'.format(i))
commands.append('switchport mode access')
commands.append('switchport access vlan {0}'.format(vlan_id))
else:
if name:
if name != obj_in_have['name']:
commands.append('vlan {0}'.format(vlan_id))
commands.append('name {0}'.format(name))
if interfaces:
if not obj_in_have['interfaces']:
for i in interfaces:
commands.append('vlan {0}'.format(vlan_id))
commands.append('interface {0}'.format(i))
commands.append('switchport mode access')
commands.append('switchport access vlan {0}'.format(vlan_id))
elif set(interfaces) != set(obj_in_have['interfaces']):
missing_interfaces = list(set(interfaces) - set(obj_in_have['interfaces']))
for i in missing_interfaces:
commands.append('vlan {0}'.format(vlan_id))
commands.append('interface {0}'.format(i))
commands.append('switchport mode access')
commands.append('switchport access vlan {0}'.format(vlan_id))
superfluous_interfaces = list(set(obj_in_have['interfaces']) - set(interfaces))
for i in superfluous_interfaces:
commands.append('vlan {0}'.format(vlan_id))
commands.append('interface {0}'.format(i))
commands.append('switchport mode access')
commands.append('no switchport access vlan {0}'.format(vlan_id))
else:
commands.append('vlan {0}'.format(vlan_id))
if name:
commands.append('name {0}'.format(name))
commands.append('state {0}'.format(state))
if purge:
for h in have:
obj_in_want = search_obj_in_list(h['vlan_id'], want)
if not obj_in_want and h['vlan_id'] != '1':
commands.append('no vlan {0}'.format(h['vlan_id']))
return commands
def map_params_to_obj(module):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
d = item.copy()
d['vlan_id'] = str(d['vlan_id'])
obj.append(d)
else:
obj.append({
'vlan_id': str(module.params['vlan_id']),
'name': module.params['name'],
'interfaces': module.params['interfaces'],
'associated_interfaces': module.params['associated_interfaces'],
'state': module.params['state']
})
return obj
def parse_to_logical_rows(out):
started_yielding = False
cur_row = []
for l in out.splitlines()[2:]:
if not l:
"""Skip empty lines."""
continue
if '0' < l[0] <= '9':
"""Line starting with a number."""
if started_yielding:
yield cur_row
cur_row = [] # Reset it to hold a next chunk
started_yielding = True
cur_row.append(l)
# Return the rest of it:
yield cur_row
def map_ports_str_to_list(ports_str):
return list(filter(bool, (normalize_interface(p.strip()) for p in ports_str.split(', '))))
def parse_to_obj(logical_rows):
first_row = logical_rows[0]
rest_rows = logical_rows[1:]
obj = re.match(r'(?P<vlan_id>\d+)\s+(?P<name>[^\s]+)\s+(?P<state>[^\s]+)\s*(?P<interfaces>.*)', first_row).groupdict()
if obj['state'] == 'suspended':
obj['state'] = 'suspend'
obj['interfaces'] = map_ports_str_to_list(obj['interfaces'])
obj['interfaces'].extend(prts_r for prts in rest_rows for prts_r in map_ports_str_to_list(prts))
return obj
def parse_vlan_brief(vlan_out):
return [parse_to_obj(r) for r in parse_to_logical_rows(vlan_out)]
def map_config_to_obj(module):
return parse_vlan_brief(run_commands(module, ['show vlan brief'])[0])
def check_declarative_intent_params(want, module, result):
have = None
is_delay = False
for w in want:
if w.get('associated_interfaces') is None:
continue
if result['changed'] and not is_delay:
time.sleep(module.params['delay'])
is_delay = True
if have is None:
have = map_config_to_obj(module)
for i in w['associated_interfaces']:
obj_in_have = search_obj_in_list(w['vlan_id'], have)
if obj_in_have and 'interfaces' in obj_in_have and i not in obj_in_have['interfaces']:
module.fail_json(msg="Interface %s not configured on vlan %s" % (i, w['vlan_id']))
def main():
""" main entry point for module execution
"""
element_spec = dict(
vlan_id=dict(type='int'),
name=dict(),
interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
state=dict(default='present',
choices=['present', 'absent', 'active', 'suspend'])
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec['vlan_id'] = 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),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(ios_argument_spec)
required_one_of = [['vlan_id', 'aggregate']]
mutually_exclusive = [['vlan_id', 'aggregate']]
module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
warnings = list()
result = {'changed': False}
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
check_declarative_intent_params(want, module, result)
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,633 +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 ios_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: ios_acl_interfaces
version_added: '2.10'
short_description: Configure and manage access-control (ACL) attributes of interfaces on IOS devices.
description: This module configures and manages the access-control (ACL) attributes of interfaces on IOS platforms.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of ACL interfaces 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
access_groups:
description: Specify access-group for IP access list (standard or extended).
type: list
elements: dict
suboptions:
afi:
description: Specifies the AFI for the ACLs to be configured on this interface.
type: str
required: True
choices:
- ipv4
- ipv6
acls:
description: Specifies the ACLs for the provided AFI.
type: list
elements: dict
suboptions:
name:
description: Specifies the name of the IPv4/IPv4 ACL for the interface.
type: str
required: True
direction:
description:
- Specifies the direction of packets that the ACL will be applied on.
- With one direction already assigned, other acl direction cannot be same.
type: str
required: True
choices:
- in
- out
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.
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:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# interface GigabitEthernet0/2
# ip access-group 123 out
- name: "Merge module attributes of given access-groups"
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
- afi: ipv6
acls:
- name: test_v6
direction: out
- name: temp_v6
direction: in
- name: GigabitEthernet0/2
access_groups:
- afi: ipv4
acls:
- name: 100
direction: in
state: merged
# Commands Fired:
# ---------------
#
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 100 in
# ip access-group 123 out
# After state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
# Using Replaced
# Before state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
- name: "Replace module attributes of given access-groups"
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 100
direction: out
- name: 110
direction: in
state: replaced
# Commands Fired:
# ---------------
#
# interface GigabitEthernet0/1
# no ip access-group 123 out
# no ipv6 traffic-filter temp_v6 in
# no ipv6 traffic-filter test_v6 out
# ip access-group 100 out
# After state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 100 out
# ip access-group 110 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
# Using Overridden
# Before state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
- name: "Overridden module attributes of given access-groups"
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 100
direction: out
- name: 110
direction: in
state: overridden
# Commands Fired:
# ---------------
#
# interface GigabitEthernet0/1
# no ip access-group 123 out
# no ipv6 traffic-filter test_v6 out
# no ipv6 traffic-filter temp_v6 in
# ip access-group 100 out
# interface GigabitEthernet0/2
# no ip access-group 110 in
# no ip access-group 123 out
# After state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 100 out
# ip access-group 110 in
# interface GigabitEthernet0/2
# Using Deleted
# Before state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
- name: "Delete module attributes of given Interface"
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
state: deleted
# Commands Fired:
# ---------------
#
# interface GigabitEthernet0/1
# no ip access-group 110 in
# no ip access-group 123 out
# no ipv6 traffic-filter test_v6 out
# no ipv6 traffic-filter temp_v6 in
# After state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
# Before state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
- name: "Delete module attributes of given Interface based on AFI"
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
state: deleted
# Commands Fired:
# ---------------
#
# interface GigabitEthernet0/1
# no ip access-group 110 in
# no ip access-group 123 out
# After state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
# Using DELETED without any config passed
#"(NOTE: This will delete all of configured resource module attributes from each configured interface)"
# Before state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
- name: "Delete module attributes of given access-groups from ALL Interfaces"
ios_acl_interfaces:
config:
state: deleted
# Commands Fired:
# ---------------
#
# interface GigabitEthernet0/1
# no ip access-group 110 in
# no ip access-group 123 out
# no ipv6 traffic-filter test_v6 out
# no ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# no ip access-group 110 out
# no ip access-group 123 out
# After state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# interface GigabitEthernet0/2
# Using Gathered
# Before state:
# -------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
- name: Gather listed acl interfaces with provided configurations
ios_acl_interfaces:
config:
state: gathered
# Module Execution Result:
# ------------------------
#
# "gathered": [
# {
# "name": "Loopback888"
# },
# {
# "name": "GigabitEthernet0/0"
# },
# {
# "access_groups": [
# {
# "acls": [
# {
# "direction": "in",
# "name": "110"
# },
# {
# "direction": "out",
# "name": "123"
# }
# ],
# "afi": "ipv4"
# },
# {
# "acls": [
# {
# "direction": "in",
# "name": "temp_v6"
# },
# {
# "direction": "out",
# "name": "test_v6"
# }
# ],
# "afi": "ipv6"
# }
# ],
# "name": "GigabitEthernet0/1"
# },
# {
# "access_groups": [
# {
# "acls": [
# {
# "direction": "in",
# "name": "100"
# },
# {
# "direction": "out",
# "name": "123"
# }
# ],
# "afi": "ipv4"
# }
# ],
# "name": "GigabitEthernet0/2"
# }
# ]
# After state:
# ------------
#
# vios#sh running-config | include interface|ip access-group|ipv6 traffic-filter
# interface Loopback888
# interface GigabitEthernet0/0
# interface GigabitEthernet0/1
# ip access-group 110 in
# ip access-group 123 out
# ipv6 traffic-filter test_v6 out
# ipv6 traffic-filter temp_v6 in
# interface GigabitEthernet0/2
# ip access-group 110 in
# ip access-group 123 out
# Using Rendered
- name: Render the commands for provided configuration
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
- afi: ipv6
acls:
- name: test_v6
direction: out
- name: temp_v6
direction: in
state: rendered
# Module Execution Result:
# ------------------------
#
# "rendered": [
# "interface GigabitEthernet0/1",
# "ip access-group 110 in",
# "ip access-group 123 out",
# "ipv6 traffic-filter temp_v6 in",
# "ipv6 traffic-filter test_v6 out"
# ]
# Using Parsed
- name: Parse the commands for provided configuration
ios_acl_interfaces:
running_config:
"interface GigabitEthernet0/1
ip access-group 110 in
ip access-group 123 out
ipv6 traffic-filter temp_v6 in
ipv6 traffic-filter test_v6 out"
state: parsed
# Module Execution Result:
# ------------------------
#
# "parsed": [
# {
# "access_groups": [
# {
# "acls": [
# {
# "direction": "in",
# "name": "110"
# }
# ],
# "afi": "ipv4"
# },
# {
# "acls": [
# {
# "direction": "in",
# "name": "temp_v6"
# }
# ],
# "afi": "ipv6"
# }
# ],
# "name": "GigabitEthernet0/1"
# }
# ]
"""
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/1', 'ip access-group 110 in', 'ipv6 traffic-filter test_v6 out']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.acl_interfaces.acl_interfaces import Acl_InterfacesArgs
from ansible.module_utils.network.ios.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',))]
mutually_exclusive = [('config', 'running_config')]
module = AnsibleModule(argument_spec=Acl_InterfacesArgs.argument_spec,
required_if=required_if,
mutually_exclusive=mutually_exclusive,
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,186 +0,0 @@
#!/usr/bin/python
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_banner
version_added: "2.3"
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
short_description: Manage multiline banners on Cisco IOS devices
description:
- This will configure both login and motd banners on remote devices
running Cisco IOS. It allows playbooks to add or remote
banner text from the active running configuration.
extends_documentation_fragment: ios
notes:
- Tested against IOS 15.6
options:
banner:
description:
- Specifies which banner should be configured on the remote device.
In Ansible 2.4 and earlier only I(login) and I(motd) were supported.
required: true
choices: ['login', 'motd', 'exec', 'incoming', 'slip-ppp']
text:
description:
- The banner text that should be
present in the remote device running configuration. This argument
accepts a multiline string, with no empty lines. Requires I(state=present).
state:
description:
- Specifies whether or not the configuration is
present in the current devices active running configuration.
default: present
choices: ['present', 'absent']
"""
EXAMPLES = """
- name: configure the login banner
ios_banner:
banner: login
text: |
this is my login banner
that contains a multiline
string
state: present
- name: remove the motd banner
ios_banner:
banner: motd
state: absent
- name: Configure banner from file
ios_banner:
banner: motd
text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}"
state: present
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- banner login
- this is my login banner
- that contains a multiline
- string
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
from re import search, M
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
state = module.params['state']
if state == 'absent' and 'text' in have.keys() and have['text']:
commands.append('no banner %s' % module.params['banner'])
elif state == 'present':
if want['text'] and (want['text'] != have.get('text')):
banner_cmd = 'banner %s' % module.params['banner']
banner_cmd += ' @\n'
banner_cmd += want['text'].strip('\n')
banner_cmd += '\n@'
commands.append(banner_cmd)
return commands
def map_config_to_obj(module):
"""
This function gets the banner config without stripping any whitespaces,
and then fetches the required banner from it.
:param module:
:return: banner config dict object.
"""
out = get_config(module, flags='| begin banner %s' % module.params['banner'])
if out:
regex = 'banner ' + module.params['banner'] + ' ^C\n'
if search('banner ' + module.params['banner'], out, M):
output = str((out.split(regex))[1].split("^C\n")[0])
else:
output = None
else:
output = None
obj = {'banner': module.params['banner'], 'state': 'absent'}
if output:
obj['text'] = output
obj['state'] = 'present'
return obj
def map_params_to_obj(module):
text = module.params['text']
return {
'banner': module.params['banner'],
'text': text,
'state': module.params['state']
}
def main():
""" main entry point for module execution
"""
argument_spec = dict(
banner=dict(required=True, choices=['login', 'motd', 'exec', 'incoming', 'slip-ppp']),
text=dict(),
state=dict(default='present', choices=['present', 'absent'])
)
argument_spec.update(ios_argument_spec)
required_if = [('state', 'present', ('text',))]
module = AnsibleModule(argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True)
warnings = list()
result = {'changed': False}
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,438 +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: ios_bgp
version_added: "2.8"
author: "Nilashish Chakraborty (@NilashishC)"
short_description: Configure global BGP protocol settings on Cisco IOS.
description:
- This module provides configuration management of global BGP parameters
on devices running Cisco IOS
notes:
- Tested against Cisco IOS Version 15.6(3)M2
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.
ebgp_multihop:
description:
- Specifies the maximum hop count for EBGP neighbors not on directly connected networks.
- The range is from 1 to 255.
type: int
peer_group:
description:
- Name of the peer group that the neighbor is a member of.
timers:
description:
- Specifies BGP neighbor timer related configurations.
suboptions:
keepalive:
description:
- Frequency (in seconds) with which the device sends keepalive messages to its peer.
- The range is from 0 to 65535.
type: int
required: True
holdtime:
description:
- Interval (in seconds) after not receiving a keepalive message that IOS declares a peer dead.
- The range is from 0 to 65535.
type: int
required: True
min_neighbor_holdtime:
description:
- Interval (in seconds) 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 0 to 65535.
type: int
local_as:
description:
- The local AS number for the neighbor.
type: int
networks:
description:
- Specify Networks to announce via BGP.
- For operation replace, this option is mutually exclusive with networks option under address_family.
- For operation replace, if the device already has an address family activated, this option is not allowed.
suboptions:
prefix:
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.
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
synchronization:
description:
- Enable/disable IGP synchronization.
type: bool
auto_summary:
description:
- Enable/disable automatic network number summarization.
type: bool
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', 'odr', 'lisp', 'mobile', 'rip']
required: True
id:
description:
- Identifier for the routing protocol for configuring redistribute information.
- Valid for protocols 'ospf', 'ospfv3' and 'eigrp'.
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:
prefix:
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.
neighbors:
description:
- Specifies BGP neighbor related configurations in Address Family configuration mode.
suboptions:
neighbor:
description:
- Neighbor router address.
required: True
advertisement_interval:
description:
- Minimum interval between sending BGP routing updates for this neighbor.
type: int
route_reflector_client:
description:
- Specify a neighbor as a route reflector client.
type: bool
route_server_client:
description:
- Specify a neighbor as a route server client.
type: bool
activate:
description:
- Enable the Address Family for this Neighbor.
type: bool
remove_private_as:
description:
- Remove the private AS number from outbound updates.
type: bool
next_hop_self:
description:
- Enable/disable the next hop calculation for this neighbor.
type: bool
next_hop_unchanged:
description:
- Propagate next hop unchanged for iBGP paths to this neighbor.
type: bool
maximum_prefix:
description:
- Maximum number of prefixes to accept from this peer.
- The range is from 1 to 2147483647.
type: int
prefix_list_in:
description:
- Name of ip prefix-list to apply to incoming prefixes.
prefix_list_out:
description:
- Name of ip prefix-list to apply to outgoing prefixes.
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 64496
ios_bgp:
config:
bgp_as: 64496
router_id: 192.0.2.1
log_neighbor_changes: True
neighbors:
- neighbor: 203.0.113.5
remote_as: 64511
timers:
keepalive: 300
holdtime: 360
min_neighbor_holdtime: 360
- neighbor: 198.51.100.2
remote_as: 64498
networks:
- prefix: 198.51.100.0
route_map: RMAP_1
- prefix: 192.0.2.0
masklen: 23
address_family:
- afi: ipv4
safi: unicast
redistribute:
- protocol: ospf
id: 223
metric: 10
operation: merge
- name: Configure BGP neighbors
ios_bgp:
config:
bgp_as: 64496
neighbors:
- neighbor: 192.0.2.10
remote_as: 64496
password: ansible
description: IBGP_NBR_1
ebgp_multihop: 100
timers:
keepalive: 300
holdtime: 360
min_neighbor_holdtime: 360
- neighbor: 192.0.2.15
remote_as: 64496
description: IBGP_NBR_2
ebgp_multihop: 150
operation: merge
- name: Configure root-level networks for BGP
ios_bgp:
config:
bgp_as: 64496
networks:
- prefix: 203.0.113.0
masklen: 27
route_map: RMAP_1
- prefix: 203.0.113.32
masklen: 27
route_map: RMAP_2
operation: merge
- name: Configure BGP neighbors under address family mode
ios_bgp:
config:
bgp_as: 64496
address_family:
- afi: ipv4
safi: unicast
neighbors:
- neighbor: 203.0.113.10
activate: yes
maximum_prefix: 250
advertisement_interval: 120
- neighbor: 192.0.2.15
activate: yes
route_reflector_client: True
operation: merge
- name: remove bgp as 64496 from config
ios_bgp:
config:
bgp_as: 64496
operation: delete
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- router bgp 64496
- bgp router-id 192.0.2.1
- bgp log-neighbor-changes
- neighbor 203.0.113.5 remote-as 64511
- neighbor 203.0.113.5 timers 300 360 360
- neighbor 198.51.100.2 remote-as 64498
- network 198.51.100.0 route-map RMAP_1
- network 192.0.2.0 mask 255.255.254.0
- address-family ipv4
- redistribute ospf 223 metric 70
- exit-address-family
"""
from ansible.module_utils._text import to_text
from ansible.module_utils.network.ios.providers.module import NetworkModule
from ansible.module_utils.network.ios.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'),
'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', required=True),
'holdtime': dict(type='int', required=True),
'min_neighbor_holdtime': dict(type='int'),
}
neighbor_spec = {
'neighbor': dict(required=True),
'remote_as': dict(type='int', required=True),
'local_as': dict(type='int'),
'update_source': dict(),
'password': dict(no_log=True),
'enabled': dict(type='bool'),
'description': dict(),
'ebgp_multihop': dict(type='int'),
'timers': dict(type='dict', options=timer_spec),
'peer_group': dict(),
}
af_neighbor_spec = {
'neighbor': dict(required=True),
'activate': dict(type='bool'),
'advertisement_interval': dict(type='int'),
'remove_private_as': dict(type='bool'),
'next_hop_self': dict(type='bool'),
'route_reflector_client': dict(type='bool'),
'route_server_client': dict(type='bool'),
'maximum_prefix': dict(type='int'),
'prefix_list_in': dict(),
'prefix_list_out': dict()
}
address_family_spec = {
'afi': dict(choices=['ipv4', 'ipv6'], required=True),
'safi': dict(choices=['flowspec', 'labeled-unicast', 'multicast', 'unicast'], default='unicast'),
'auto_summary': dict(type='bool'),
'synchronization': dict(type='bool'),
'networks': dict(type='list', elements='dict', options=network_spec),
'redistribute': dict(type='list', elements='dict', options=redistribute_spec),
'neighbors': dict(type='list', elements='dict', options=af_neighbor_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),
'networks': dict(type='list', elements='dict', options=network_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='| section ^router bgp')
except Exception as exc:
module.fail_json(msg=to_text(exc))
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,230 +0,0 @@
#!/usr/bin/python
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_command
version_added: "2.1"
author: "Peter Sprygada (@privateip)"
short_description: Run commands on remote devices running Cisco IOS
description:
- Sends arbitrary commands to an ios 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(ios_config) to configure IOS devices.
extends_documentation_fragment: ios
notes:
- Tested against IOS 15.6
options:
commands:
description:
- List of commands to send to the remote ios 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. If a command sent to the
device requires answering a prompt, it is possible to pass
a dict containing I(command), I(answer) and I(prompt).
Common answers are 'y' or "\\r" (carriage return, must be
double quotes). See examples.
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 = r"""
tasks:
- name: run show version on remote devices
ios_command:
commands: show version
- name: run show version and check to see if output contains IOS
ios_command:
commands: show version
wait_for: result[0] contains IOS
- name: run multiple commands on remote nodes
ios_command:
commands:
- show version
- show interfaces
- name: run multiple commands and evaluate the output
ios_command:
commands:
- show version
- show interfaces
wait_for:
- result[0] contains IOS
- result[1] contains Loopback0
- name: run commands that require answering a prompt
ios_command:
commands:
- command: 'clear counters GigabitEthernet0/1'
prompt: 'Clear "show interface" counters on this interface \[confirm\]'
answer: 'y'
- command: 'clear counters GigabitEthernet0/2'
prompt: '[confirm]'
answer: "\r"
"""
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 transform_commands, to_lines
from ansible.module_utils.network.ios.ios import run_commands
from ansible.module_utils.network.ios.ios import ios_argument_spec
def parse_commands(module, warnings):
commands = transform_commands(module)
if module.check_mode:
for item in list(commands):
if not item['command'].startswith('show'):
warnings.append(
'Only show commands are supported when using check mode, not '
'executing %s' % item['command']
)
commands.remove(item)
return commands
def main():
"""main entry point for module execution
"""
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(ios_argument_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,567 +0,0 @@
#!/usr/bin/python
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_config
version_added: "2.1"
author: "Peter Sprygada (@privateip)"
short_description: Manage Cisco IOS configuration sections
description:
- Cisco IOS configurations use a simple block indent file syntax
for segmenting configuration into sections. This module provides
an implementation for working with IOS configuration sections in
a deterministic way.
extends_documentation_fragment: ios
notes:
- Tested against IOS 15.6
- 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).
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.
choices: ['line', 'strict', 'exact', 'none']
default: line
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']
multiline_delimiter:
description:
- This argument is used when pushing a multiline configuration
element to the IOS device. It specifies the character to use
as the delimiting character. This only applies to the
configuration action.
default: "@"
version_added: "2.3"
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"
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.
aliases: ['config']
version_added: "2.4"
defaults:
description:
- This argument specifies whether or not to collect all defaults
when getting the remote device running config. When enabled,
the module will get the current config by issuing the command
C(show running-config all).
type: bool
default: 'no'
version_added: "2.2"
save_when:
description:
- When changes are made to the device running-configuration, the
changes are not copied to non-volatile storage by default. Using
this argument will change that before. If the argument is set to
I(always), then the running-config will always be copied to the
startup-config and the I(modified) flag will always be set to
True. If the argument is set to I(modified), then the running-config
will only be copied to the startup-config if it has changed since
the last save to startup-config. If the argument is set to
I(never), the running-config will never be copied to the
startup-config. If the argument is set to I(changed), then the running-config
will only be copied to the startup-config if the task has made a change.
I(changed) was added in Ansible 2.5.
default: never
choices: ['always', 'never', 'modified', 'changed']
version_added: "2.4"
diff_against:
description:
- When using the C(ansible-playbook --diff) command line argument
the module can generate diffs against different sources.
- When this option is configure as I(startup), the module will return
the diff of the running-config against the startup-config.
- When this option is configured as I(intended), the module will
return the diff of the running-config against the configuration
provided in the C(intended_config) argument.
- When this option is configured as I(running), the module will
return the before and after diff of the running-config with respect
to any changes made to the device configuration.
choices: ['running', 'startup', 'intended']
version_added: "2.4"
diff_ignore_lines:
description:
- Use this argument to specify one or more lines that should be
ignored during the diff. This is used for lines in the configuration
that are automatically updated by the system. This argument takes
a list of regular expressions or exact line matches.
version_added: "2.4"
intended_config:
description:
- The C(intended_config) provides the master configuration that
the node should conform to and is used to check the final
running-config against. This argument will not modify any settings
on the remote device and is strictly used to check the compliance
of the current device's configuration against. When specifying this
argument, the task should also modify the C(diff_against) value and
set it to I(intended).
version_added: "2.4"
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"
"""
EXAMPLES = """
- name: configure top level configuration
ios_config:
lines: hostname {{ inventory_hostname }}
- name: configure interface settings
ios_config:
lines:
- description test interface
- ip address 172.31.1.1 255.255.255.0
parents: interface Ethernet1
- name: configure ip helpers on multiple interfaces
ios_config:
lines:
- ip helper-address 172.26.1.10
- ip helper-address 172.26.3.8
parents: "{{ item }}"
with_items:
- interface Ethernet1
- interface Ethernet2
- interface GigabitEthernet1
- name: configure policer in Scavenger class
ios_config:
lines:
- conform-action transmit
- exceed-action drop
parents:
- policy-map Foo
- class Scavenger
- police cir 64000
- name: load new acl into device
ios_config:
lines:
- 10 permit ip host 192.0.2.1 any log
- 20 permit ip host 192.0.2.2 any log
- 30 permit ip host 192.0.2.3 any log
- 40 permit ip host 192.0.2.4 any log
- 50 permit ip host 192.0.2.5 any log
parents: ip access-list extended test
before: no ip access-list extended test
match: exact
- name: check the running-config against master config
ios_config:
diff_against: intended
intended_config: "{{ lookup('file', 'master.cfg') }}"
- name: check the startup-config against the running-config
ios_config:
diff_against: startup
diff_ignore_lines:
- ntp clock .*
- name: save running to startup when modified
ios_config:
save_when: modified
- name: for idempotency, use full-form commands
ios_config:
lines:
# - shut
- shutdown
# parents: int gig1/0/11
parents: interface GigabitEthernet1/0/11
# Set boot image based on comparison to a group_var (version) and the version
# that is returned from the `ios_facts` module
- name: SETTING BOOT IMAGE
ios_config:
lines:
- no boot system
- boot system flash bootflash:{{new_image}}
host: "{{ inventory_hostname }}"
when: ansible_net_version != version
- name: render a Jinja2 template onto an IOS device
ios_config:
backup: yes
src: ios_template.j2
- name: configurable backup path
ios_config:
src: ios_template.j2
backup: yes
backup_options:
filename: backup.cfg
dir_path: /home/user
"""
RETURN = """
updates:
description: The set of commands that will be pushed to the remote device
returned: always
type: list
sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1']
commands:
description: The set of commands that will be pushed to the remote device
returned: always
type: list
sample: ['hostname foo', 'router ospf 1', 'router-id 192.0.2.1']
backup_path:
description: The full path to the backup file
returned: when backup is yes
type: str
sample: /playbooks/ansible/backup/ios_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: ios_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/ios_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 json
from ansible.module_utils._text import to_text
from ansible.module_utils.connection import ConnectionError
from ansible.module_utils.network.ios.ios import run_commands, get_config
from ansible.module_utils.network.ios.ios import get_defaults_flag, get_connection
from ansible.module_utils.network.ios.ios import ios_argument_spec
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.config import NetworkConfig, dumps
def check_args(module, warnings):
if module.params['multiline_delimiter']:
if len(module.params['multiline_delimiter']) != 1:
module.fail_json(msg='multiline_delimiter value can only be a '
'single character')
def edit_config_or_macro(connection, commands):
# only catch the macro configuration command,
# not negated 'no' variation.
if commands[0].startswith("macro name"):
connection.edit_macro(candidate=commands)
else:
connection.edit_config(candidate=commands)
def get_candidate_config(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 get_running_config(module, current_config=None, flags=None):
running = module.params['running_config']
if not running:
if not module.params['defaults'] and current_config:
running = current_config
else:
running = get_config(module, flags=flags)
return running
def save_config(module, result):
result['changed'] = True
if not module.check_mode:
run_commands(module, 'copy running-config startup-config\r')
else:
module.warn('Skipping command `copy running-config startup-config` '
'due to check_mode. Configuration not copied to '
'non-volatile storage')
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']),
multiline_delimiter=dict(default='@'),
running_config=dict(aliases=['config']),
intended_config=dict(),
defaults=dict(type='bool', default=False),
backup=dict(type='bool', default=False),
backup_options=dict(type='dict', options=backup_spec),
save_when=dict(choices=['always', 'never', 'modified', 'changed'], default='never'),
diff_against=dict(choices=['startup', 'intended', 'running']),
diff_ignore_lines=dict(type='list'),
)
argument_spec.update(ios_argument_spec)
mutually_exclusive = [('lines', 'src'),
('parents', 'src')]
required_if = [('match', 'strict', ['lines']),
('match', 'exact', ['lines']),
('replace', 'block', ['lines']),
('diff_against', 'intended', ['intended_config'])]
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
required_if=required_if,
supports_check_mode=True)
result = {'changed': False}
warnings = list()
check_args(module, warnings)
result['warnings'] = warnings
diff_ignore_lines = module.params['diff_ignore_lines']
config = None
contents = None
flags = get_defaults_flag(module) if module.params['defaults'] else []
connection = get_connection(module)
if module.params['backup'] or (module._diff and module.params['diff_against'] == 'running'):
contents = get_config(module, flags=flags)
config = NetworkConfig(indent=1, contents=contents)
if module.params['backup']:
result['__backup__'] = contents
if any((module.params['lines'], module.params['src'])):
match = module.params['match']
replace = module.params['replace']
path = module.params['parents']
candidate = get_candidate_config(module)
running = get_running_config(module, contents, flags=flags)
try:
response = connection.get_diff(candidate=candidate, running=running, diff_match=match, diff_ignore_lines=diff_ignore_lines, path=path,
diff_replace=replace)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
config_diff = response['config_diff']
banner_diff = response['banner_diff']
if config_diff or banner_diff:
commands = config_diff.split('\n')
if module.params['before']:
commands[:0] = module.params['before']
if module.params['after']:
commands.extend(module.params['after'])
result['commands'] = commands
result['updates'] = commands
result['banners'] = banner_diff
# send the configuration commands to the device and merge
# them with the current running config
if not module.check_mode:
if commands:
edit_config_or_macro(connection, commands)
if banner_diff:
connection.edit_banner(candidate=json.dumps(banner_diff), multiline_delimiter=module.params['multiline_delimiter'])
result['changed'] = True
running_config = module.params['running_config']
startup_config = None
if module.params['save_when'] == 'always':
save_config(module, result)
elif module.params['save_when'] == 'modified':
output = run_commands(module, ['show running-config', 'show startup-config'])
running_config = NetworkConfig(indent=1, contents=output[0], ignore_lines=diff_ignore_lines)
startup_config = NetworkConfig(indent=1, contents=output[1], ignore_lines=diff_ignore_lines)
if running_config.sha1 != startup_config.sha1:
save_config(module, result)
elif module.params['save_when'] == 'changed' and result['changed']:
save_config(module, result)
if module._diff:
if not running_config:
output = run_commands(module, 'show running-config')
contents = output[0]
else:
contents = running_config
# recreate the object in order to process diff_ignore_lines
running_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines)
if module.params['diff_against'] == 'running':
if module.check_mode:
module.warn("unable to perform diff against running-config due to check mode")
contents = None
else:
contents = config.config_text
elif module.params['diff_against'] == 'startup':
if not startup_config:
output = run_commands(module, 'show startup-config')
contents = output[0]
else:
contents = startup_config.config_text
elif module.params['diff_against'] == 'intended':
contents = module.params['intended_config']
if contents is not None:
base_config = NetworkConfig(indent=1, contents=contents, ignore_lines=diff_ignore_lines)
if running_config.sha1 != base_config.sha1:
if module.params['diff_against'] == 'intended':
before = running_config
after = base_config
elif module.params['diff_against'] in ('startup', 'running'):
before = base_config
after = running_config
result.update({
'changed': True,
'diff': {'before': str(before), 'after': str(after)}
})
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,239 +0,0 @@
#!/usr/bin/python
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_facts
version_added: "2.2"
author:
- "Peter Sprygada (@privateip)"
- "Sumit Jaiswal (@justjais)"
short_description: Collect facts from remote devices running Cisco IOS
description:
- Collects a base set of device facts from a remote device that
is running IOS. This module prepends all of the
base network fact keys with C(ansible_net_<fact>). The facts
module will always collect a base set of facts from the device
and can enable or disable collection of additional facts.
extends_documentation_fragment: ios
notes:
- Tested against IOS 15.6
options:
gather_subset:
description:
- When supplied, this argument restricts the facts collected
to a given subset.
- Possible values for this argument include
C(all), C(min), C(hardware), C(config), and C(interfaces).
- Specify a list of values to include a larger subset.
- Use a value with an initial C(!) to collect all facts except that subset.
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, vlans 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', 'interfaces', 'l2_interfaces', 'vlans',
'lag_interfaces', 'lacp', 'lacp_interfaces', 'lldp_global',
'lldp_interfaces', 'l3_interfaces', 'acl_interfaces', 'static_routes', 'acls'.
version_added: "2.9"
"""
EXAMPLES = """
- name: Gather all legacy facts
ios_facts:
gather_subset: all
- name: Gather only the config and default facts
ios_facts:
gather_subset:
- config
- name: Do not gather hardware facts
ios_facts:
gather_subset:
- "!hardware"
- name: Gather legacy and resource facts
ios_facts:
gather_subset: all
gather_network_resources: all
- name: Gather only the interfaces resource facts and no legacy facts
ios_facts:
gather_subset:
- '!all'
- '!min'
gather_network_resources:
- interfaces
- name: Gather interfaces resource and minimal legacy facts
ios_facts:
gather_subset: min
gather_network_resources: interfaces
- name: Gather L2 interfaces resource and minimal legacy facts
ios_facts:
gather_subset: min
gather_network_resources: l2_interfaces
- name: Gather L3 interfaces resource and minimal legacy facts
ios_facts:
gather_subset: min
gather_network_resources: l3_interfaces
"""
RETURN = """
ansible_net_gather_subset:
description: The list of fact subsets collected from the device
returned: always
type: list
ansible_net_gather_network_resources:
description: The list of fact for network resource subsets collected from the device
returned: when the resource is configured
type: list
# default
ansible_net_model:
description: The model name returned from the device
returned: always
type: str
ansible_net_serialnum:
description: The serial number of the remote device
returned: always
type: str
ansible_net_version:
description: The operating system version running on the remote device
returned: always
type: str
ansible_net_iostype:
description: The operating system type (IOS or IOS-XE) 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_stacked_models:
description: The model names of each device in the stack
returned: when multiple devices are configured in a stack
type: list
ansible_net_stacked_serialnums:
description: The serial numbers of each device in the stack
returned: when multiple devices are configured in a stack
type: list
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
# hardware
ansible_net_filesystems:
description: All file system names available on the device
returned: when hardware is configured
type: list
ansible_net_filesystems_info:
description: A hash of all file systems containing info about each file system (e.g. free and total space)
returned: when hardware is configured
type: dict
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 CDP and LLDP neighbors from the remote device. If both,
CDP and LLDP neighbor data is present on one port, CDP is preferred.
returned: when interfaces is configured
type: dict
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.facts.facts import FactsArgs
from ansible.module_utils.network.ios.facts.facts import Facts
from ansible.module_utils.network.ios.ios import ios_argument_spec
def main():
"""
Main entry point for module execution
:returns: ansible_facts
"""
argument_spec = FactsArgs.argument_spec
argument_spec.update(ios_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,405 +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 ios_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_interfaces
version_added: 2.9
short_description: Manages interface attributes of Cisco IOS network devices
description: This module manages the interface attributes of Cisco IOS network devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of interface options
type: list
suboptions:
name:
description:
- Full name of interface, e.g. GigabitEthernet0/2, loopback999.
type: str
required: True
description:
description:
- Interface description.
type: str
enabled:
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
default: True
speed:
description:
- Interface link speed. Applicable for Ethernet interfaces only.
type: str
mtu:
description:
- MTU for a specific interface. Applicable for Ethernet interfaces only.
- Refer to vendor documentation for valid values.
type: int
duplex:
description:
- Interface link status. Applicable for Ethernet interfaces only, either in half duplex,
full duplex or in automatic state which negotiates the duplex automatically.
type: str
choices: ['full', 'half', 'auto']
state:
choices:
- merged
- replaced
- overridden
- deleted
default: merged
description:
- The state of the configuration after module completion
type: str
"""
EXAMPLES = """
---
# Using merged
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# no ip address
# duplex auto
# speed auto
- name: Merge provided configuration with device configuration
ios_interfaces:
config:
- name: GigabitEthernet0/2
description: 'Configured and Merged by Ansible Network'
enabled: True
- name: GigabitEthernet0/3
description: 'Configured and Merged by Ansible Network'
mtu: 2800
enabled: False
speed: 100
duplex: full
state: merged
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured and Merged by Ansible Network
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured and Merged by Ansible Network
# mtu 2800
# no ip address
# shutdown
# duplex full
# speed 100
# Using replaced
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# mtu 2000
# no ip address
# shutdown
# duplex full
# speed 100
- name: Replaces device configuration of listed interfaces with provided configuration
ios_interfaces:
config:
- name: GigabitEthernet0/3
description: 'Configured and Replaced by Ansible Network'
enabled: False
duplex: auto
mtu: 2500
speed: 1000
state: replaced
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured and Replaced by Ansible Network
# mtu 2500
# no ip address
# shutdown
# duplex full
# speed 1000
# Using overridden
# Before state:
# -------------
#
# vios#show running-config | section ^interface#
# interface GigabitEthernet0/1
# description Configured by Ansible
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible
# mtu 2800
# no ip address
# shutdown
# duplex full
# speed 100
- name: Override device configuration of all interfaces with provided configuration
ios_interfaces:
config:
- name: GigabitEthernet0/2
description: 'Configured and Overridden by Ansible Network'
speed: 1000
- name: GigabitEthernet0/3
description: 'Configured and Overridden by Ansible Network'
enabled: False
duplex: full
mtu: 2000
state: overridden
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured and Overridden by Ansible Network
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured and Overridden by Ansible Network
# mtu 2000
# no ip address
# shutdown
# duplex full
# speed 100
# Using Deleted
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# mtu 2500
# no ip address
# shutdown
# duplex full
# speed 1000
- name: "Delete module attributes of given interfaces (Note: This won't delete the interface itself)"
ios_interfaces:
config:
- name: GigabitEthernet0/2
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# mtu 2500
# no ip address
# shutdown
# duplex full
# speed 1000
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured resource module attributes from each configured interface)"
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# mtu 2500
# no ip address
# shutdown
# duplex full
# speed 1000
- name: "Delete module attributes of all interfaces (Note: This won't delete the interface itself)"
ios_interfaces:
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/3
# no ip address
# duplex auto
# speed auto
"""
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 GigabitEthernet 0/1', 'description This is test', 'speed 100']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.interfaces.interfaces import InterfacesArgs
from ansible.module_utils.network.ios.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,390 +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 ios_l2_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_l2_interfaces
version_added: 2.9
short_description: Manage Layer-2 interface on Cisco IOS devices.
description: This module provides declarative management of Layer-2 interface on Cisco IOS devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of Layer-2 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
access:
description:
- Switchport mode access command to configure the interface as a layer 2 access.
type: dict
suboptions:
vlan:
description:
- Configure given VLAN in access port. It's used as the access VLAN ID.
type: int
voice:
description:
- Switchport mode voice command to configure the interface with a voice vlan.
type: dict
suboptions:
vlan:
description:
- Configure given voice VLAN on access port. It's used as the voice VLAN ID.
type: int
trunk:
description:
- Switchport mode trunk command to configure the interface as a Layer 2 trunk.
Note The encapsulation is always set to dot1q.
type: dict
suboptions:
allowed_vlans:
description:
- List of allowed VLANs in a given trunk port. These are the only VLANs that will be
configured on the trunk.
type: list
native_vlan:
description:
- Native VLAN to be configured in trunk port. It's used as the trunk native VLAN ID.
type: int
encapsulation:
description:
- Trunking encapsulation when interface is in trunking mode.
choices: ['dot1q','isl','negotiate']
type: str
pruning_vlans:
description:
- Pruning VLAN to be configured in trunk port. It's used as the trunk pruning VLAN ID.
type: list
mode:
description:
- Mode in which interface needs to be configured.
- An interface whose trunk encapsulation is "Auto" can not be configured to "trunk" mode.
version_added: '2.10'
type: str
choices: ['access', 'trunk']
state:
choices:
- merged
- replaced
- overridden
- deleted
default: merged
description:
- The state of the configuration after module completion
type: str
"""
EXAMPLES = """
---
# Using merged
# Before state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport access vlan 20
# media-type rj45
# negotiation auto
- name: Merge provided configuration with device configuration
ios_l2_interfaces:
config:
- name: GigabitEthernet0/1
mode: access
access:
vlan: 10
voice:
vlan: 40
- name: GigabitEthernet0/2
mode: trunk
trunk:
allowed_vlans: 10-20,40
native_vlan: 20
pruning_vlans: 10,20
encapsulation: dot1q
state: merged
# After state:
# ------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# switchport access vlan 10
# switchport access vlan 40
# switchport mode access
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport trunk allowed vlan 10-20,40
# switchport trunk encapsulation dot1q
# switchport trunk native vlan 20
# switchport trunk pruning vlan 10,20
# switchport mode trunk
# media-type rj45
# negotiation auto
# Using replaced
# Before state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# switchport access vlan 20
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport access vlan 20
# media-type rj45
# negotiation auto
- name: Replaces device configuration of listed l2 interfaces with provided configuration
ios_l2_interfaces:
config:
- name: GigabitEthernet0/2
trunk:
- allowed_vlans: 20-25,40
native_vlan: 20
pruning_vlans: 10
encapsulation: isl
state: replaced
# After state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# switchport access vlan 20
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport trunk allowed vlan 20-25,40
# switchport trunk encapsulation isl
# switchport trunk native vlan 20
# switchport trunk pruning vlan 10
# media-type rj45
# negotiation auto
# Using overridden
# Before state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# switchport trunk encapsulation dot1q
# switchport trunk native vlan 20
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport access vlan 20
# switchport trunk encapsulation dot1q
# switchport trunk native vlan 20
# media-type rj45
# negotiation auto
- name: Override device configuration of all l2 interfaces with provided configuration
ios_l2_interfaces:
config:
- name: GigabitEthernet0/2
access:
vlan: 20
voice:
vlan: 40
state: overridden
# After state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport access vlan 20
# switchport voice vlan 40
# media-type rj45
# negotiation auto
# Using Deleted
# Before state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# switchport access vlan 20
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport access vlan 20
# switchport trunk allowed vlan 20-40,60,80
# switchport trunk encapsulation dot1q
# switchport trunk native vlan 10
# switchport trunk pruning vlan 10
# media-type rj45
# negotiation auto
- name: Delete IOS L2 interfaces as in given arguments
ios_l2_interfaces:
config:
- name: GigabitEthernet0/1
state: deleted
# After state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport access vlan 20
# switchport trunk allowed vlan 20-40,60,80
# switchport trunk encapsulation dot1q
# switchport trunk native vlan 10
# switchport trunk pruning vlan 10
# media-type rj45
# negotiation auto
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured resource module attributes from each configured interface)"
# Before state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# switchport access vlan 20
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# switchport access vlan 20
# switchport trunk allowed vlan 20-40,60,80
# switchport trunk encapsulation dot1q
# switchport trunk native vlan 10
# switchport trunk pruning vlan 10
# media-type rj45
# negotiation auto
- name: Delete IOS L2 interfaces as in given arguments
ios_l2_interfaces:
state: deleted
# After state:
# -------------
#
# viosl2#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# negotiation auto
# interface GigabitEthernet0/2
# description This is test
# media-type rj45
# negotiation auto
"""
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/1', 'switchport access vlan 20']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs
from ansible.module_utils.network.ios.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,442 +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: ios_l3_interfaces
version_added: 2.9
short_description: Manage Layer-3 interface on Cisco IOS devices.
description:
- This module provides declarative management of Layer-3 interface
on Cisco IOS devices.
author: Sumit Jaiswal (@justjais)
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
elements: dict
suboptions:
address:
description:
- Configures the IPv4 address for Interface.
type: str
secondary:
description:
- Configures the IP address as a secondary address.
type: bool
dhcp_client:
description:
- Configures and specifies client-id to use over DHCP ip.
Note, This option shall work only when dhcp is configured
as IP.
- GigabitEthernet interface number
type: int
dhcp_hostname:
description:
- Configures and specifies value for hostname option over
DHCP ip. Note, This option shall work only when dhcp is
configured as IP.
type: str
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
elements: dict
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:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# ip address 10.1.1.1 255.255.255.0
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# no ip address
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
- name: Merge provided configuration with device configuration
ios_l3_interfaces:
config:
- name: GigabitEthernet0/1
ipv4:
- address: 192.168.0.1/24
secondary: True
- name: GigabitEthernet0/2
ipv4:
- address: 192.168.0.2/24
- name: GigabitEthernet0/3
ipv6:
- address: fd5d:12c9:2201:1::1/64
- name: GigabitEthernet0/3.100
ipv4:
- address: 192.168.0.3/24
state: merged
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# ip address 10.1.1.1 255.255.255.0
# ip address 192.168.0.1 255.255.255.0 secondary
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# ip address 192.168.0.2 255.255.255.0
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# ipv6 address FD5D:12C9:2201:1::1/64
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# ip address 192.168.0.3 255.255.255.0
# Using replaced
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# ip address 10.1.1.1 255.255.255.0
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# no ip address
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# ip address 192.168.2.0 255.255.255.0
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# ip address 192.168.0.2 255.255.255.0
- name: Replaces device configuration of listed interfaces with provided configuration
ios_l3_interfaces:
config:
- name: GigabitEthernet0/2
ipv4:
- address: 192.168.2.0/24
- name: GigabitEthernet0/3
ipv4:
- address: dhcp
dhcp_client: 2
dhcp_hostname: test.com
- name: GigabitEthernet0/3.100
ipv4:
- address: 192.168.0.3/24
secondary: True
state: replaced
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# ip address 10.1.1.1 255.255.255.0
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# ip address 192.168.2.1 255.255.255.0
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# ip address dhcp client-id GigabitEthernet0/2 hostname test.com
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# ip address 192.168.0.2 255.255.255.0
# ip address 192.168.0.3 255.255.255.0 secondary
# Using overridden
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# ip address 10.1.1.1 255.255.255.0
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# ip address 192.168.2.1 255.255.255.0
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# ipv6 address FD5D:12C9:2201:1::1/64
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# ip address 192.168.0.2 255.255.255.0
- name: Override device configuration of all interfaces with provided configuration
ios_l3_interfaces:
config:
- name: GigabitEthernet0/2
ipv4:
- address: 192.168.0.1/24
- name: GigabitEthernet0/3.100
ipv6:
- address: autoconfig
state: overridden
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# description Configured by Ansible
# no ip address
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description This is test
# ip address 192.168.0.1 255.255.255.0
# duplex auto
# speed 1000
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# ipv6 address autoconfig
# Using Deleted
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# ip address 192.0.2.10 255.255.255.0
# shutdown
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# ip address 192.168.1.0 255.255.255.0
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# ip address 192.168.0.1 255.255.255.0
# shutdown
# duplex full
# speed 10
# ipv6 address FD5D:12C9:2201:1::1/64
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# ip address 192.168.0.2 255.255.255.0
- name: "Delete attributes of given interfaces (NOTE: This won't delete the interface itself)"
ios_l3_interfaces:
config:
- name: GigabitEthernet0/2
- name: GigabitEthernet0/3.100
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# shutdown
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# no ip address
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# ip address 192.168.0.1 255.255.255.0
# shutdown
# duplex full
# speed 10
# ipv6 address FD5D:12C9:2201:1::1/64
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured L3 resource module attributes from each configured interface)"
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# ip address 192.0.2.10 255.255.255.0
# shutdown
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# ip address 192.168.1.0 255.255.255.0
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# ip address 192.168.0.1 255.255.255.0
# shutdown
# duplex full
# speed 10
# ipv6 address FD5D:12C9:2201:1::1/64
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
# ip address 192.168.0.2 255.255.255.0
- name: "Delete L3 attributes of ALL interfaces together (NOTE: This won't delete the interface itself)"
ios_l3_interfaces:
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface GigabitEthernet0/1
# no ip address
# shutdown
# duplex auto
# speed auto
# interface GigabitEthernet0/2
# description Configured by Ansible Network
# no ip address
# interface GigabitEthernet0/3
# description Configured by Ansible Network
# shutdown
# duplex full
# speed 10
# interface GigabitEthernet0/3.100
# encapsulation dot1Q 20
"""
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/1', 'ip address 192.168.0.2 255.255.255.0']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.l3_interfaces.l3_interfaces import L3_InterfacesArgs
from ansible.module_utils.network.ios.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,185 +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 ios_lacp
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: ios_lacp
version_added: 2.9
short_description: Manage Global Link Aggregation Control Protocol (LACP) on Cisco IOS devices.
description: This module provides declarative management of Global LACP on Cisco IOS network devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli),
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: The provided configurations.
type: dict
suboptions:
system:
description: This option sets the default system parameters for LACP.
type: dict
suboptions:
priority:
description:
- LACP priority for the system.
- Refer to vendor documentation for valid values.
type: int
required: True
state:
description:
- The state of the configuration after module completion
type: str
choices:
- merged
- replaced
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vios#show lacp sys-id
# 32768, 5e00.0000.8000
- name: Merge provided configuration with device configuration
ios_lacp:
config:
system:
priority: 123
state: merged
# After state:
# ------------
#
# vios#show lacp sys-id
# 123, 5e00.0000.8000
# Using replaced
#
# Before state:
# -------------
#
# vios#show lacp sys-id
# 500, 5e00.0000.8000
- name: Replaces Global LACP configuration
ios_lacp:
config:
system:
priority: 123
state: replaced
# After state:
# ------------
#
# vios#show lacp sys-id
# 123, 5e00.0000.8000
# Using Deleted
#
# Before state:
# -------------
#
# vios#show lacp sys-id
# 500, 5e00.0000.8000
- name: Delete Global LACP attribute
ios_lacp:
state: deleted
# After state:
# -------------
#
# vios#show lacp sys-id
# 32768, 5e00.0000.8000
"""
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: ['lacp system-priority 10']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.lacp.lacp import LacpArgs
from ansible.module_utils.network.ios.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,363 +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 ios_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: ios_lacp_interfaces
version_added: 2.9
short_description: Manage Link Aggregation Control Protocol (LACP) on Cisco IOS devices interface.
description: This module provides declarative management of LACP on Cisco IOS network devices lacp_interfaces.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL.
- This module works with connection C(network_cli),
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of LACP lacp_interfaces option
type: list
elements: dict
suboptions:
name:
description:
- Name of the Interface for configuring LACP.
type: str
required: True
port_priority:
description:
- LACP priority on this interface.
- Refer to vendor documentation for valid port values.
type: int
fast_switchover:
description:
- LACP fast switchover supported on this port channel.
type: bool
max_bundle:
description:
- LACP maximum number of ports to bundle in this port channel.
- Refer to vendor documentation for valid port values.
type: int
state:
description:
- The state of the configuration after module completion
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
- name: Merge provided configuration with device configuration
ios_lacp_interfaces:
config:
- name: GigabitEthernet0/1
port_priority: 10
- name: GigabitEthernet0/2
port_priority: 20
- name: GigabitEthernet0/3
port_priority: 30
- name: Port-channel10
fast_switchover: True
max_bundle: 5
state: merged
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# lacp fast-switchover
# lacp max-bundle 5
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# lacp port-priority 10
# interface GigabitEthernet0/2
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/3
# shutdown
# lacp port-priority 30
# Using overridden
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# lacp fast-switchover
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# lacp port-priority 10
# interface GigabitEthernet0/2
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/3
# shutdown
# lacp port-priority 30
- name: Override device configuration of all lacp_interfaces with provided configuration
ios_lacp_interfaces:
config:
- name: GigabitEthernet0/1
port_priority: 20
- name: Port-channel10
max_bundle: 2
state: overridden
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# lacp max-bundle 2
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
# Using replaced
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# lacp max-bundle 5
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# lacp port-priority 10
# interface GigabitEthernet0/2
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/3
# shutdown
# lacp port-priority 30
- name: Replaces device configuration of listed lacp_interfaces with provided configuration
ios_lacp_interfaces:
config:
- name: GigabitEthernet0/3
port_priority: 40
- name: Port-channel10
fast_switchover: True
max_bundle: 2
state: replaced
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# lacp fast-switchover
# lacp max-bundle 2
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# lacp port-priority 10
# interface GigabitEthernet0/2
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/3
# shutdown
# lacp port-priority 40
# Using Deleted
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# flowcontrol receive on
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# lacp port-priority 10
# interface GigabitEthernet0/2
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/3
# shutdown
# lacp port-priority 30
- name: "Delete LACP attributes of given interfaces (Note: This won't delete the interface itself)"
ios_lacp_interfaces:
config:
- name: GigabitEthernet0/1
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/3
# shutdown
# lacp port-priority 30
# Using Deleted without any config passed
# "(NOTE: This will delete all of configured LLDP module attributes)"
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# lacp fast-switchover
# interface Port-channel20
# lacp max-bundle 2
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# lacp port-priority 10
# interface GigabitEthernet0/2
# shutdown
# lacp port-priority 20
# interface GigabitEthernet0/3
# shutdown
# lacp port-priority 30
- name: "Delete LACP attributes for all configured interfaces (Note: This won't delete the interface itself)"
ios_lacp_interfaces:
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# 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 GigabitEthernet 0/1', 'lacp port-priority 30']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.lacp_interfaces.lacp_interfaces import Lacp_InterfacesArgs
from ansible.module_utils.network.ios.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,390 +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: ios_lag_interfaces
version_added: 2.9
short_description: Manage Link Aggregation on Cisco IOS devices.
description: This module manages properties of Link Aggregation Group on Cisco IOS devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A list of link aggregation group configurations.
type: list
elements: dict
suboptions:
name:
description:
- ID of Ethernet Channel of interfaces.
- Refer to vendor documentation for valid port values.
type: str
required: True
members:
description:
- Interface options for the link aggregation group.
type: list
suboptions:
member:
description:
- Interface member of the link aggregation group.
type: str
mode:
description:
- Etherchannel Mode of the interface for link aggregation.
type: str
choices:
- 'auto'
- 'on'
- 'desirable'
- 'active'
- 'passive'
link:
description:
- Assign a link identifier used for load-balancing.
- Refer to vendor documentation for valid values.
- NOTE, parameter only supported on Cisco IOS XE platform.
type: int
state:
description:
- The state of the configuration after module completion
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
---
# Using merged
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
# interface GigabitEthernet0/4
# shutdown
- name: Merge provided configuration with device configuration
ios_lag_interfaces:
config:
- name: 10
members:
- member: GigabitEthernet0/1
mode: auto
- member: GigabitEthernet0/2
mode: auto
- name: 20
members:
- member: GigabitEthernet0/3
mode: on
- name: 30
members:
- member: GigabitEthernet0/4
mode: active
state: merged
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
# Using overridden
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: Override device configuration of all interfaces with provided configuration
ios_lag_interfaces:
config:
- name: 20
members:
- member: GigabitEthernet0/2
mode: auto
- member: GigabitEthernet0/3
mode: auto
state: overridden
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# channel-group 20 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode auto
# interface GigabitEthernet0/4
# shutdown
# Using replaced
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: Replaces device configuration of listed interfaces with provided configuration
ios_lag_interfaces:
config:
- name: 40
members:
- member: GigabitEthernet0/3
mode: auto
state: replaced
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface Port-channel40
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 40 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
# Using Deleted
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: "Delete LAG attributes of given interfaces (Note: This won't delete the interface itself)"
ios_lag_interfaces:
config:
- name: 10
- name: 20
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured LLDP module attributes)"
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: "Delete all configured LAG attributes for interfaces (Note: This won't delete the interface itself)"
ios_lag_interfaces:
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
# interface GigabitEthernet0/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/1', 'channel-group 1 mode active']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
from ansible.module_utils.network.ios.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,318 +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: ios_linkagg
version_added: "2.5"
author: "Trishna Guha (@trishnaguha)"
short_description: Manage link aggregation groups on Cisco IOS network devices
description:
- This module provides declarative management of link aggregation groups
on Cisco IOS network devices.
notes:
- Tested against IOS 15.2
options:
group:
description:
- Channel-group number for the port-channel
Link aggregation group. Range 1-255.
mode:
description:
- Mode of the link aggregation group.
choices: ['active', 'on', 'passive', 'auto', 'desirable']
members:
description:
- List of members of the link aggregation group.
aggregate:
description: List of link aggregation definitions.
state:
description:
- State of the link aggregation group.
default: present
choices: ['present', 'absent']
purge:
description:
- Purge links not defined in the I(aggregate) parameter.
default: no
type: bool
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: create link aggregation group
ios_linkagg:
group: 10
state: present
- name: delete link aggregation group
ios_linkagg:
group: 10
state: absent
- name: set link aggregation group to members
ios_linkagg:
group: 200
mode: active
members:
- GigabitEthernet0/0
- GigabitEthernet0/1
- name: remove link aggregation group from GigabitEthernet0/0
ios_linkagg:
group: 200
mode: active
members:
- GigabitEthernet0/1
- name: Create aggregate of linkagg definitions
ios_linkagg:
aggregate:
- { group: 3, mode: on, members: [GigabitEthernet0/1] }
- { group: 100, mode: passive, members: [GigabitEthernet0/2] }
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- interface port-channel 30
- interface GigabitEthernet0/3
- channel-group 30 mode on
- no interface port-channel 30
"""
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.config import CustomNetworkConfig
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
def search_obj_in_list(group, lst):
for o in lst:
if o['group'] == group:
return o
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
purge = module.params['purge']
for w in want:
group = w['group']
mode = w['mode']
members = w.get('members') or []
state = w['state']
del w['state']
obj_in_have = search_obj_in_list(group, have)
if state == 'absent':
if obj_in_have:
commands.append('no interface port-channel {0}'.format(group))
elif state == 'present':
cmd = ['interface port-channel {0}'.format(group),
'end']
if not obj_in_have:
if not group:
module.fail_json(msg='group is a required option')
commands.extend(cmd)
if members:
for m in members:
commands.append('interface {0}'.format(m))
commands.append('channel-group {0} mode {1}'.format(group, mode))
else:
if members:
if 'members' not in obj_in_have.keys():
for m in members:
commands.extend(cmd)
commands.append('interface {0}'.format(m))
commands.append('channel-group {0} mode {1}'.format(group, mode))
elif set(members) != set(obj_in_have['members']):
missing_members = list(set(members) - set(obj_in_have['members']))
for m in missing_members:
commands.extend(cmd)
commands.append('interface {0}'.format(m))
commands.append('channel-group {0} mode {1}'.format(group, mode))
superfluous_members = list(set(obj_in_have['members']) - set(members))
for m in superfluous_members:
commands.extend(cmd)
commands.append('interface {0}'.format(m))
commands.append('no channel-group {0} mode {1}'.format(group, mode))
if purge:
for h in have:
obj_in_want = search_obj_in_list(h['group'], want)
if not obj_in_want:
commands.append('no interface port-channel {0}'.format(h['group']))
return commands
def map_params_to_obj(module):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
d = item.copy()
d['group'] = str(d['group'])
obj.append(d)
else:
obj.append({
'group': str(module.params['group']),
'mode': module.params['mode'],
'members': module.params['members'],
'state': module.params['state']
})
return obj
def parse_mode(module, config, group, member):
mode = None
netcfg = CustomNetworkConfig(indent=1, contents=config)
parents = ['interface {0}'.format(member)]
body = netcfg.get_section(parents)
match_int = re.findall(r'interface {0}\n'.format(member), body, re.M)
if match_int:
match = re.search(r'channel-group {0} mode (\S+)'.format(group), body, re.M)
if match:
mode = match.group(1)
return mode
def parse_members(module, config, group):
members = []
for line in config.strip().split('!'):
l = line.strip()
if l.startswith('interface'):
match_group = re.findall(r'channel-group {0} mode'.format(group), l, re.M)
if match_group:
match = re.search(r'interface (\S+)', l, re.M)
if match:
members.append(match.group(1))
return members
def get_channel(module, config, group):
match = re.findall(r'^interface (\S+)', config, re.M)
if not match:
return {}
channel = {}
for item in set(match):
member = item
channel['mode'] = parse_mode(module, config, group, member)
channel['members'] = parse_members(module, config, group)
return channel
def map_config_to_obj(module):
objs = list()
config = get_config(module)
for line in config.split('\n'):
l = line.strip()
match = re.search(r'interface Port-channel(\S+)', l, re.M)
if match:
obj = {}
group = match.group(1)
obj['group'] = group
obj.update(get_channel(module, config, group))
objs.append(obj)
return objs
def main():
""" main entry point for module execution
"""
element_spec = dict(
group=dict(type='int'),
mode=dict(choices=['active', 'on', 'passive', 'auto', 'desirable']),
members=dict(type='list'),
state=dict(default='present',
choices=['present', 'absent'])
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec['group'] = dict(required=True)
required_one_of = [['group', 'aggregate']]
required_together = [['members', 'mode']]
mutually_exclusive = [['group', 'aggregate']]
# 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,
required_together=required_together),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(ios_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of,
required_together=required_together,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
warnings = list()
result = {'changed': False}
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,112 +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: ios_lldp
version_added: "2.5"
author: "Ganesh Nalawade (@ganeshrn)"
short_description: Manage LLDP configuration on Cisco IOS network devices.
description:
- This module provides declarative management of LLDP service
on Cisco IOS network devices.
notes:
- Tested against IOS 15.2
options:
state:
description:
- State of the LLDP configuration. If value is I(present) lldp will be enabled
else if it is I(absent) it will be disabled.
default: present
choices: ['present', 'absent']
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: Enable LLDP service
ios_lldp:
state: present
- name: Disable LLDP service
ios_lldp:
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- lldp run
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.ios import load_config, run_commands
from ansible.module_utils.network.ios.ios import ios_argument_spec
def has_lldp(module):
output = run_commands(module, ['show lldp'])
is_lldp_enable = False
if len(output) > 0 and "LLDP is not enabled" not in output[0]:
is_lldp_enable = True
return is_lldp_enable
def main():
""" main entry point for module execution
"""
argument_spec = dict(
state=dict(default='present',
choices=['present', 'absent',
'enabled', 'disabled'])
)
argument_spec.update(ios_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
warnings = list()
result = {'changed': False}
if warnings:
result['warnings'] = warnings
HAS_LLDP = has_lldp(module)
commands = []
if module.params['state'] == 'absent' and HAS_LLDP:
commands.append('no lldp run')
elif module.params['state'] == 'present' and not HAS_LLDP:
commands.append('lldp run')
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,256 +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 ios_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: ios_lldp_global
version_added: 2.9
short_description: Configure and manage Link Layer Discovery Protocol(LLDP) attributes on IOS platforms.
description: This module configures and manages the Link Layer Discovery Protocol(LLDP) attributes on IOS platforms.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli),
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of LLDP options
type: dict
suboptions:
holdtime:
description:
- LLDP holdtime (in sec) to be sent in packets.
- Refer to vendor documentation for valid values.
type: int
reinit:
description:
- Specify the delay (in secs) for LLDP to initialize.
- Refer to vendor documentation for valid values.
- NOTE, if LLDP reinit is configured with a starting
value, idempotency won't be maintained as the Cisco
device doesn't record the starting reinit configured
value. As such, Ansible cannot verify if the respective
starting reinit value is already configured or not from
the device side. If you try to apply starting reinit
value in every play run, Ansible will show changed as True.
For any other reinit value, idempotency will be maintained
since any other reinit value is recorded in the Cisco device.
type: int
enabled:
description:
- Enable LLDP
type: bool
timer:
description:
- Specify the rate at which LLDP packets are sent (in sec).
- Refer to vendor documentation for valid values.
type: int
tlv_select:
description:
- Selection of LLDP TLVs i.e. type-length-value to send
- NOTE, if tlv-select is configured idempotency won't be maintained
as Cisco device doesn't record configured tlv-select options. As
such, Ansible cannot verify if the respective tlv-select options is
already configured or not from the device side. If you try to apply
tlv-select option in every play run, Ansible will show changed as True.
type: dict
suboptions:
four_wire_power_management:
description:
- Cisco 4-wire Power via MDI TLV
type: bool
mac_phy_cfg:
description:
- IEEE 802.3 MAC/Phy Configuration/status TLV
type: bool
management_address:
description:
- Management Address TLV
type: bool
port_description:
description:
- Port Description TLV
type: bool
port_vlan:
description:
- Port VLAN ID TLV
type: bool
power_management:
description:
- IEEE 802.3 DTE Power via MDI TLV
type: bool
system_capabilities:
description:
- System Capabilities TLV
type: bool
system_description:
description:
- System Description TLV
type: bool
system_name:
description:
- 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:
# -------------
# vios#sh running-config | section ^lldp
# vios1#
- name: Merge provided configuration with device configuration
ios_lldp_global:
config:
holdtime: 10
enabled: True
reinit: 3
timer: 10
state: merged
# After state:
# ------------
# vios#sh running-config | section ^lldp
# lldp timer 10
# lldp holdtime 10
# lldp reinit 3
# lldp run
# Using replaced
# Before state:
# -------------
# vios#sh running-config | section ^lldp
# lldp timer 10
# lldp holdtime 10
# lldp reinit 3
# lldp run
- name: Replaces LLDP device configuration with provided configuration
ios_lldp_global:
config:
holdtime: 20
reinit: 5
state: replaced
# After state:
# -------------
# vios#sh running-config | section ^lldp
# lldp holdtime 20
# lldp reinit 5
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured LLDP module attributes)"
# Before state:
# -------------
# vios#sh running-config | section ^lldp
# lldp timer 10
# lldp holdtime 10
# lldp reinit 3
# lldp run
- name: "Delete LLDP attributes (Note: This won't delete the interface itself)"
ios_lldp_global:
state: deleted
# After state:
# -------------
# vios#sh running-config | section ^lldp
# vios1#
"""
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 holdtime 10', 'lldp run', 'lldp timer 10']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.lldp_global.lldp_global import Lldp_globalArgs
from ansible.module_utils.network.ios.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,501 +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 ios_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: ios_lldp_interfaces
version_added: 2.9
short_description: Manage link layer discovery protocol (LLDP) attributes of interfaces on Cisco IOS devices.
description: This module manages link layer discovery protocol (LLDP) attributes of interfaces on Cisco IOS devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli),
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of LLDP 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
receive:
description:
- Enable LLDP reception on interface.
type: bool
transmit:
description:
- Enable LLDP transmission on interface.
type: bool
med_tlv_select:
description:
- Selection of LLDP MED TLVs to send
- NOTE, if med-tlv-select is configured idempotency won't be maintained
as Cisco device doesn't record configured med-tlv-select options. As
such, Ansible cannot verify if the respective med-tlv-select options is
already configured or not from the device side. If you try to apply
med-tlv-select option in every play run, Ansible will show changed as
True.
type: dict
suboptions:
inventory_management:
description:
- LLDP MED Inventory Management TLV
type: bool
tlv_select:
description:
- Selection of LLDP type-length-value i.e. TLVs to send
- NOTE, if tlv-select is configured idempotency won't be maintained
as Cisco device doesn't record configured tlv-select options. As
such, Ansible cannot verify if the respective tlv-select options is
already configured or not from the device side. If you try to apply
tlv-select option in every play run, Ansible will show changed as True.
type: dict
suboptions:
power_management:
description:
- IEEE 802.3 DTE Power via MDI TLV
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:
# -------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: enabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
- name: Merge provided configuration with device configuration
ios_lldp_interfaces:
config:
- name: GigabitEthernet0/1
receive: True
transmit: True
- name: GigabitEthernet0/2
receive: True
- name: GigabitEthernet0/3
transmit: True
state: merged
# After state:
# ------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: enabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: enabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: enabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# Using overridden
#
# Before state:
# -------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
- name: Override device configuration of all lldp_interfaces with provided configuration
ios_lldp_interfaces:
config:
- name: GigabitEthernet0/2
receive: True
transmit: True
state: overridden
# After state:
# ------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
# Using replaced
#
# Before state:
# -------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
- name: Replaces device configuration of listed lldp_interfaces with provided configuration
ios_lldp_interfaces:
config:
- name: GigabitEthernet0/2
receive: True
transmit: True
- name: GigabitEthernet0/3
receive: True
state: replaced
# After state:
# ------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: disabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# Using Deleted
#
# Before state:
# -------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
- name: "Delete LLDP attributes of given interfaces (Note: This won't delete the interface itself)"
ios_lldp_interfaces:
config:
- name: GigabitEthernet0/1
state: deleted
# After state:
# -------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# Using Deleted without any config passed
# "(NOTE: This will delete all of configured LLDP module attributes)"
#
# Before state:
# -------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: enabled
# Rx: enabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
- name: "Delete LLDP attributes for all configured interfaces (Note: This won't delete the interface itself)"
ios_lldp_interfaces:
state: deleted
# After state:
# -------------
#
# vios#sh lldp interface
# GigabitEthernet0/0:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/1:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
# GigabitEthernet0/2:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: INIT
#
# GigabitEthernet0/3:
# Tx: disabled
# Rx: disabled
# Tx state: IDLE
# Rx state: WAIT FOR FRAME
#
"""
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 GigabitEthernet 0/1', 'lldp transmit', 'lldp receive']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.lldp_interfaces.lldp_interfaces import Lldp_InterfacesArgs
from ansible.module_utils.network.ios.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,429 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_logging
version_added: "2.4"
author: "Trishna Guha (@trishnaguha)"
short_description: Manage logging on network devices
description:
- This module provides declarative management of logging
on Cisco Ios devices.
notes:
- Tested against IOS 15.6
options:
dest:
description:
- Destination of the logs.
choices: ['on', 'host', 'console', 'monitor', 'buffered', 'trap']
name:
description:
- The hostname or IP address of the destination.
- Required when I(dest=host).
size:
description:
- Size of buffer. The acceptable value is in range from 4096 to
4294967295 bytes.
default: 4096
facility:
description:
- Set logging facility.
level:
description:
- Set logging severity levels.
default: debugging
choices: ['emergencies', 'alerts', 'critical', 'errors', 'warnings', 'notifications', 'informational', 'debugging']
aggregate:
description: List of logging definitions.
state:
description:
- State of the logging configuration.
default: present
choices: ['present', 'absent']
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: configure host logging
ios_logging:
dest: host
name: 172.16.0.1
state: present
- name: remove host logging configuration
ios_logging:
dest: host
name: 172.16.0.1
state: absent
- name: configure console logging level and facility
ios_logging:
dest: console
facility: local7
level: debugging
state: present
- name: enable logging to all
ios_logging:
dest : on
- name: configure buffer size
ios_logging:
dest: buffered
size: 5000
- name: Configure logging using aggregate
ios_logging:
aggregate:
- { dest: console, level: notifications }
- { dest: buffered, size: 9000 }
- name: remove logging using aggregate
ios_logging:
aggregate:
- { dest: console, level: notifications }
- { dest: buffered, size: 9000 }
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- logging facility local7
- logging host 172.16.0.1
"""
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec, validate_ip_address
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import get_capabilities
from ansible.module_utils.network.ios.ios import ios_argument_spec
def validate_size(value, module):
if value:
if not int(4096) <= int(value) <= int(4294967295):
module.fail_json(msg='size must be between 4096 and 4294967295')
else:
return value
def map_obj_to_commands(updates, module, os_version):
dest_group = ('console', 'monitor', 'buffered', 'on', 'trap')
commands = list()
want, have = updates
for w in want:
dest = w['dest']
name = w['name']
size = w['size']
facility = w['facility']
level = w['level']
state = w['state']
del w['state']
if facility:
w['dest'] = 'facility'
if state == 'absent' and w in have:
if dest:
if dest == 'host':
if '12.' in os_version:
commands.append('no logging {0}'.format(name))
else:
commands.append('no logging host {0}'.format(name))
elif dest in dest_group:
commands.append('no logging {0}'.format(dest))
else:
module.fail_json(msg='dest must be among console, monitor, buffered, host, on, trap')
if facility:
commands.append('no logging facility {0}'.format(facility))
if state == 'present' and w not in have:
if facility:
present = False
for entry in have:
if entry['dest'] == 'facility' and entry['facility'] == facility:
present = True
if not present:
commands.append('logging facility {0}'.format(facility))
if dest == 'host':
if '12.' in os_version:
commands.append('logging {0}'.format(name))
else:
commands.append('logging host {0}'.format(name))
elif dest == 'on':
commands.append('logging on')
elif dest == 'buffered' and size:
present = False
for entry in have:
if entry['dest'] == 'buffered' and entry['size'] == size and entry['level'] == level:
present = True
if not present:
if level and level != 'debugging':
commands.append('logging buffered {0} {1}'.format(size, level))
else:
commands.append('logging buffered {0}'.format(size))
else:
if dest:
dest_cmd = 'logging {0}'.format(dest)
if level:
dest_cmd += ' {0}'.format(level)
commands.append(dest_cmd)
return commands
def parse_facility(line, dest):
facility = None
if dest == 'facility':
match = re.search(r'logging facility (\S+)', line, re.M)
if match:
facility = match.group(1)
return facility
def parse_size(line, dest):
size = None
if dest == 'buffered':
match = re.search(r'logging buffered(?: (\d+))?(?: [a-z]+)?', line, re.M)
if match:
if match.group(1) is not None:
size = match.group(1)
else:
size = "4096"
return size
def parse_name(line, dest):
if dest == 'host':
match = re.search(r'logging host (\S+)', line, re.M)
if match:
name = match.group(1)
else:
name = None
return name
def parse_level(line, dest):
level_group = ('emergencies', 'alerts', 'critical', 'errors', 'warnings',
'notifications', 'informational', 'debugging')
if dest == 'host':
level = 'debugging'
else:
if dest == 'buffered':
match = re.search(r'logging buffered(?: \d+)?(?: ([a-z]+))?', line, re.M)
else:
match = re.search(r'logging {0} (\S+)'.format(dest), line, re.M)
if match and match.group(1) in level_group:
level = match.group(1)
else:
level = 'debugging'
return level
def map_config_to_obj(module):
obj = []
dest_group = ('console', 'host', 'monitor', 'buffered', 'on', 'facility', 'trap')
data = get_config(module, flags=['| include logging'])
for line in data.split('\n'):
match = re.search(r'^logging (\S+)', line, re.M)
if match:
if match.group(1) in dest_group:
dest = match.group(1)
obj.append({
'dest': dest,
'name': parse_name(line, dest),
'size': parse_size(line, dest),
'facility': parse_facility(line, dest),
'level': parse_level(line, dest)
})
elif validate_ip_address(match.group(1)):
dest = 'host'
obj.append({
'dest': dest,
'name': match.group(1),
'size': parse_size(line, dest),
'facility': parse_facility(line, dest),
'level': parse_level(line, dest)
})
else:
ip_match = re.search(r'\d+\.\d+\.\d+\.\d+', match.group(1), re.M)
if ip_match:
dest = 'host'
obj.append({
'dest': dest,
'name': match.group(1),
'size': parse_size(line, dest),
'facility': parse_facility(line, dest),
'level': parse_level(line, dest)
})
return obj
def map_params_to_obj(module, required_if=None):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
module._check_required_if(required_if, item)
d = item.copy()
if d['dest'] != 'host':
d['name'] = None
if d['dest'] == 'buffered':
if 'size' in d:
d['size'] = str(validate_size(d['size'], module))
elif 'size' not in d:
d['size'] = str(4096)
else:
pass
if d['dest'] != 'buffered':
d['size'] = None
obj.append(d)
else:
if module.params['dest'] != 'host':
module.params['name'] = None
if module.params['dest'] == 'buffered':
if not module.params['size']:
module.params['size'] = str(4096)
else:
module.params['size'] = None
if module.params['size'] is None:
obj.append({
'dest': module.params['dest'],
'name': module.params['name'],
'size': module.params['size'],
'facility': module.params['facility'],
'level': module.params['level'],
'state': module.params['state']
})
else:
obj.append({
'dest': module.params['dest'],
'name': module.params['name'],
'size': str(validate_size(module.params['size'], module)),
'facility': module.params['facility'],
'level': module.params['level'],
'state': module.params['state']
})
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
dest=dict(type='str', choices=['on', 'host', 'console', 'monitor', 'buffered', 'trap']),
name=dict(type='str'),
size=dict(type='int'),
facility=dict(type='str'),
level=dict(type='str', default='debugging', choices=['emergencies', 'alerts', 'critical', 'errors', 'warnings',
'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)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(ios_argument_spec)
required_if = [('dest', 'host', ['name'])]
module = AnsibleModule(argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True)
device_info = get_capabilities(module)
os_version = device_info['device_info']['network_os_version']
warnings = list()
result = {'changed': False}
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module, required_if=required_if)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module, os_version)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,308 +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)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: ios_ntp
extends_documentation_fragment: ios
version_added: "2.8"
short_description: Manages core NTP configuration.
description:
- Manages core NTP configuration.
author:
- Federico Olivieri (@Federico87)
options:
server:
description:
- Network address of NTP server.
source_int:
description:
- Source interface for NTP packets.
acl:
description:
- ACL for peer/server access restricition.
logging:
description:
- Enable NTP logs. Data type boolean.
type: bool
default: False
auth:
description:
- Enable NTP authentication. Data type boolean.
type: bool
default: False
auth_key:
description:
- md5 NTP authentication key of tye 7.
key_id:
description:
- auth_key id. Data type string
state:
description:
- Manage the state of the resource.
default: present
choices: ['present', 'absent']
'''
EXAMPLES = '''
# Set new NTP server and source interface
- ios_ntp:
server: 10.0.255.10
source_int: Loopback0
logging: false
state: present
# Remove NTP ACL and logging
- ios_ntp:
acl: NTP_ACL
logging: true
state: absent
# Set NTP authentication
- ios_ntp:
key_id: 10
auth_key: 15435A030726242723273C21181319000A
auth: true
state: present
# Set new NTP configuration
- ios_ntp:
server: 10.0.255.10
source_int: Loopback0
acl: NTP_ACL
logging: true
key_id: 10
auth_key: 15435A030726242723273C21181319000A
auth: true
state: present
'''
RETURN = '''
commands:
description: command sent to the device
returned: always
type: list
sample: ["no ntp server 10.0.255.10", "no ntp source Loopback0"]
'''
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
def parse_server(line, dest):
if dest == 'server':
match = re.search(r'(ntp server )(\d+\.\d+\.\d+\.\d+)', line, re.M)
if match:
server = match.group(2)
return server
def parse_source_int(line, dest):
if dest == 'source':
match = re.search(r'(ntp source )(\S+)', line, re.M)
if match:
source = match.group(2)
return source
def parse_acl(line, dest):
if dest == 'access-group':
match = re.search(r'ntp access-group (?:peer|serve)(?:\s+)(\S+)', line, re.M)
if match:
acl = match.group(1)
return acl
def parse_logging(line, dest):
if dest == 'logging':
logging = dest
return logging
def parse_auth_key(line, dest):
if dest == 'authentication-key':
match = re.search(r'(ntp authentication-key \d+ md5 )(\w+)', line, re.M)
if match:
auth_key = match.group(2)
return auth_key
def parse_key_id(line, dest):
if dest == 'trusted-key':
match = re.search(r'(ntp trusted-key )(\d+)', line, re.M)
if match:
auth_key = match.group(2)
return auth_key
def parse_auth(dest):
if dest == 'authenticate':
return dest
def map_config_to_obj(module):
obj_dict = {}
obj = []
server_list = []
config = get_config(module, flags=['| include ntp'])
for line in config.splitlines():
match = re.search(r'ntp (\S+)', line, re.M)
if match:
dest = match.group(1)
server = parse_server(line, dest)
source_int = parse_source_int(line, dest)
acl = parse_acl(line, dest)
logging = parse_logging(line, dest)
auth = parse_auth(dest)
auth_key = parse_auth_key(line, dest)
key_id = parse_key_id(line, dest)
if server:
server_list.append(server)
if source_int:
obj_dict['source_int'] = source_int
if acl:
obj_dict['acl'] = acl
if logging:
obj_dict['logging'] = True
if auth:
obj_dict['auth'] = True
if auth_key:
obj_dict['auth_key'] = auth_key
if key_id:
obj_dict['key_id'] = key_id
obj_dict['server'] = server_list
obj.append(obj_dict)
return obj
def map_params_to_obj(module):
obj = []
obj.append({
'state': module.params['state'],
'server': module.params['server'],
'source_int': module.params['source_int'],
'logging': module.params['logging'],
'acl': module.params['acl'],
'auth': module.params['auth'],
'auth_key': module.params['auth_key'],
'key_id': module.params['key_id']
})
return obj
def map_obj_to_commands(want, have, module):
commands = list()
server_have = have[0].get('server', None)
source_int_have = have[0].get('source_int', None)
acl_have = have[0].get('acl', None)
logging_have = have[0].get('logging', None)
auth_have = have[0].get('auth', None)
auth_key_have = have[0].get('auth_key', None)
key_id_have = have[0].get('key_id', None)
for w in want:
server = w['server']
source_int = w['source_int']
acl = w['acl']
logging = w['logging']
state = w['state']
auth = w['auth']
auth_key = w['auth_key']
key_id = w['key_id']
if state == 'absent':
if server_have and server in server_have:
commands.append('no ntp server {0}'.format(server))
if source_int and source_int_have:
commands.append('no ntp source {0}'.format(source_int))
if acl and acl_have:
commands.append('no ntp access-group peer {0}'.format(acl))
if logging is True and logging_have:
commands.append('no ntp logging')
if auth is True and auth_have:
commands.append('no ntp authenticate')
if key_id and key_id_have:
commands.append('no ntp trusted-key {0}'.format(key_id))
if auth_key and auth_key_have:
if key_id and key_id_have:
commands.append('no ntp authentication-key {0} md5 {1} 7'.format(key_id, auth_key))
elif state == 'present':
if server is not None and server not in server_have:
commands.append('ntp server {0}'.format(server))
if source_int is not None and source_int != source_int_have:
commands.append('ntp source {0}'.format(source_int))
if acl is not None and acl != acl_have:
commands.append('ntp access-group peer {0}'.format(acl))
if logging is not None and logging != logging_have and logging is not False:
commands.append('ntp logging')
if auth is not None and auth != auth_have and auth is not False:
commands.append('ntp authenticate')
if key_id is not None and key_id != key_id_have:
commands.append('ntp trusted-key {0}'.format(key_id))
if auth_key is not None and auth_key != auth_key_have:
if key_id is not None:
commands.append('ntp authentication-key {0} md5 {1} 7'.format(key_id, auth_key))
return commands
def main():
argument_spec = dict(
server=dict(),
source_int=dict(),
acl=dict(),
logging=dict(type='bool', default=False),
auth=dict(type='bool', default=False),
auth_key=dict(),
key_id=dict(),
state=dict(choices=['absent', 'present'], default='present')
)
argument_spec.update(ios_argument_spec)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True
)
result = {'changed': False}
warnings = list()
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands(want, have, module)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,210 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 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': 'community'}
DOCUMENTATION = r'''
---
module: ios_ping
short_description: Tests reachability using ping from Cisco IOS network devices
description:
- Tests reachability using ping from switch to a remote destination.
- For a general purpose network module, see the M(net_ping) module.
- For Windows targets, use the M(win_ping) module instead.
- For targets running Python, use the M(ping) module instead.
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
extends_documentation_fragment: ios
options:
count:
description:
- Number of packets to send.
default: 5
dest:
description:
- The IP Address or hostname (resolvable by switch) of the remote node.
required: true
source:
description:
- The source IP Address.
state:
description:
- Determines if the expected result is success or fail.
choices: [ absent, present ]
default: present
vrf:
description:
- The VRF to use for forwarding.
default: default
notes:
- For a general purpose network module, see the M(net_ping) module.
- For Windows targets, use the M(win_ping) module instead.
- For targets running Python, use the M(ping) module instead.
'''
EXAMPLES = r'''
- name: Test reachability to 10.10.10.10 using default vrf
ios_ping:
dest: 10.10.10.10
- name: Test reachability to 10.20.20.20 using prod vrf
ios_ping:
dest: 10.20.20.20
vrf: prod
- name: Test unreachability to 10.30.30.30 using default vrf
ios_ping:
dest: 10.30.30.30
state: absent
- name: Test reachability to 10.40.40.40 using prod vrf and setting count and source
ios_ping:
dest: 10.40.40.40
source: loopback0
vrf: prod
count: 20
'''
RETURN = '''
commands:
description: Show the command sent.
returned: always
type: list
sample: ["ping vrf prod 10.40.40.40 count 20 source loopback0"]
packet_loss:
description: Percentage of packets lost.
returned: always
type: str
sample: "0%"
packets_rx:
description: Packets successfully received.
returned: always
type: int
sample: 20
packets_tx:
description: Packets successfully transmitted.
returned: always
type: int
sample: 20
rtt:
description: Show RTT stats.
returned: always
type: dict
sample: {"avg": 2, "max": 8, "min": 1}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.ios import run_commands
from ansible.module_utils.network.ios.ios import ios_argument_spec
import re
def main():
""" main entry point for module execution
"""
argument_spec = dict(
count=dict(type="int"),
dest=dict(type="str", required=True),
source=dict(type="str"),
state=dict(type="str", choices=["absent", "present"], default="present"),
vrf=dict(type="str")
)
argument_spec.update(ios_argument_spec)
module = AnsibleModule(argument_spec=argument_spec)
count = module.params["count"]
dest = module.params["dest"]
source = module.params["source"]
vrf = module.params["vrf"]
warnings = list()
results = {}
if warnings:
results["warnings"] = warnings
results["commands"] = [build_ping(dest, count, source, vrf)]
ping_results = run_commands(module, commands=results["commands"])
ping_results_list = ping_results[0].split("\n")
stats = ""
for line in ping_results_list:
if line.startswith('Success'):
stats = line
success, rx, tx, rtt = parse_ping(stats)
loss = abs(100 - int(success))
results["packet_loss"] = str(loss) + "%"
results["packets_rx"] = int(rx)
results["packets_tx"] = int(tx)
# Convert rtt values to int
for k, v in rtt.items():
if rtt[k] is not None:
rtt[k] = int(v)
results["rtt"] = rtt
validate_results(module, loss, results)
module.exit_json(**results)
def build_ping(dest, count=None, source=None, vrf=None):
"""
Function to build the command to send to the terminal for the switch
to execute. All args come from the module's unique params.
"""
if vrf is not None:
cmd = "ping vrf {0} {1}".format(vrf, dest)
else:
cmd = "ping {0}".format(dest)
if count is not None:
cmd += " repeat {0}".format(str(count))
if source is not None:
cmd += " source {0}".format(source)
return cmd
def parse_ping(ping_stats):
"""
Function used to parse the statistical information from the ping response.
Example: "Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/8 ms"
Returns the percent of packet loss, received packets, transmitted packets, and RTT dict.
"""
rate_re = re.compile(r"^\w+\s+\w+\s+\w+\s+(?P<pct>\d+)\s+\w+\s+\((?P<rx>\d+)/(?P<tx>\d+)\)")
rtt_re = re.compile(r".*,\s+\S+\s+\S+\s+=\s+(?P<min>\d+)/(?P<avg>\d+)/(?P<max>\d+)\s+\w+\s*$|.*\s*$")
rate = rate_re.match(ping_stats)
rtt = rtt_re.match(ping_stats)
return rate.group("pct"), rate.group("rx"), rate.group("tx"), rtt.groupdict()
def validate_results(module, loss, results):
"""
This function is used to validate whether the ping results were unexpected per "state" param.
"""
state = module.params["state"]
if state == "present" and loss == 100:
module.fail_json(msg="Ping failed unexpectedly", **results)
elif state == "absent" and loss < 100:
module.fail_json(msg="Ping succeeded unexpectedly", **results)
if __name__ == "__main__":
main()

@ -1,313 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_static_route
version_added: "2.4"
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
short_description: Manage static IP routes on Cisco IOS network devices
description:
- This module provides declarative management of static
IP routes on Cisco IOS network devices.
notes:
- Tested against IOS 15.6
options:
prefix:
description:
- Network prefix of the static route.
mask:
description:
- Network prefix mask of the static route.
next_hop:
description:
- Next hop IP of the static route.
vrf:
description:
- VRF of the static route.
version_added: "2.8"
interface:
description:
- Interface of the static route.
version_added: "2.8"
name:
description:
- Name of the static route
aliases: ['description']
version_added: "2.8"
admin_distance:
description:
- Admin distance of the static route.
tag:
description:
- Set tag of the static route.
version_added: "2.8"
track:
description:
- Tracked item to depend on for the static route.
version_added: "2.8"
aggregate:
description: List of static route definitions.
state:
description:
- State of the static route configuration.
default: present
choices: ['present', 'absent']
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: configure static route
ios_static_route:
prefix: 192.168.2.0
mask: 255.255.255.0
next_hop: 10.0.0.1
- name: configure black hole in vrf blue depending on tracked item 10
ios_static_route:
prefix: 192.168.2.0
mask: 255.255.255.0
vrf: blue
interface: null0
track: 10
- name: configure ultimate route with name and tag
ios_static_route:
prefix: 192.168.2.0
mask: 255.255.255.0
interface: GigabitEthernet1
name: hello world
tag: 100
- name: remove configuration
ios_static_route:
prefix: 192.168.2.0
mask: 255.255.255.0
next_hop: 10.0.0.1
state: absent
- name: Add static route aggregates
ios_static_route:
aggregate:
- { prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8 }
- { prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8 }
- name: Remove static route aggregates
ios_static_route:
aggregate:
- { prefix: 172.16.32.0, mask: 255.255.255.0, next_hop: 10.0.0.8 }
- { prefix: 172.16.33.0, mask: 255.255.255.0, next_hop: 10.0.0.8 }
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- ip route 192.168.2.0 255.255.255.0 10.0.0.1
"""
from copy import deepcopy
from re import findall
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec, validate_ip_address
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
def map_obj_to_commands(want, have):
commands = list()
for w in want:
state = w['state']
del w['state']
# Try to match an existing config with the desired config
for h in have:
# To delete admin_distance param from have if not it want before comparing both fields
if not w.get('admin_distance') and h.get('admin_distance'):
del h['admin_distance']
diff = list(set(w.items()) ^ set(h.items()))
if not diff:
break
# if route is present with name or name already starts with wanted name it will not change
elif len(diff) == 2 and diff[0][0] == diff[1][0] == 'name' and (not w['name'] or h['name'].startswith(w['name'])):
break
# If no matches found, clear `h`
else:
h = None
command = 'ip route'
prefix = w['prefix']
mask = w['mask']
vrf = w.get('vrf')
if vrf:
command = ' '.join((command, 'vrf', vrf, prefix, mask))
else:
command = ' '.join((command, prefix, mask))
for key in ['interface', 'next_hop', 'admin_distance', 'tag', 'name', 'track']:
if w.get(key):
if key == 'name' and len(w.get(key).split()) > 1:
command = ' '.join((command, key, '"%s"' % w.get(key))) # name with multiple words needs to be quoted
elif key in ('name', 'tag', 'track'):
command = ' '.join((command, key, w.get(key)))
else:
command = ' '.join((command, w.get(key)))
if state == 'absent' and h:
commands.append('no %s' % command)
elif state == 'present' and not h:
commands.append(command)
return commands
def map_config_to_obj(module):
obj = []
out = get_config(module, flags='| include ip route')
for line in out.splitlines():
splitted_line = findall(r'[^"\s]\S*|".+?"', line) # Split by whitespace but do not split quotes, needed for name parameter
if splitted_line[2] == 'vrf':
route = {'vrf': splitted_line[3]}
del splitted_line[:4] # Removes the words ip route vrf vrf_name
else:
route = {}
del splitted_line[:2] # Removes the words ip route
prefix = splitted_line[0]
mask = splitted_line[1]
route.update({'prefix': prefix, 'mask': mask, 'admin_distance': '1'})
next_word = None
for word in splitted_line[2:]:
if next_word:
route[next_word] = word.strip('"') # Remove quotes which is needed for name
next_word = None
elif validate_ip_address(word):
route.update(next_hop=word)
elif word.isdigit():
route.update(admin_distance=word)
elif word in ('tag', 'name', 'track'):
next_word = word
else:
route.update(interface=word)
obj.append(route)
return obj
def map_params_to_obj(module, required_together=None):
keys = ['prefix', 'mask', 'state', 'next_hop', 'vrf', 'interface', 'name', 'admin_distance', 'track', 'tag']
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
route = item.copy()
for key in keys:
if route.get(key) is None:
route[key] = module.params.get(key)
route = dict((k, v) for k, v in route.items() if v is not None)
module._check_required_together(required_together, route)
obj.append(route)
else:
module._check_required_together(required_together, module.params)
route = dict()
for key in keys:
if module.params.get(key) is not None:
route[key] = module.params.get(key)
obj.append(route)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
prefix=dict(type='str'),
mask=dict(type='str'),
next_hop=dict(type='str'),
vrf=dict(type='str'),
interface=dict(type='str'),
name=dict(type='str', aliases=['description']),
admin_distance=dict(type='str'),
track=dict(type='str'),
tag=dict(type='str'),
state=dict(default='present', choices=['present', 'absent'])
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec['prefix'] = 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(ios_argument_spec)
required_one_of = [['aggregate', 'prefix']]
required_together = [['prefix', 'mask']]
mutually_exclusive = [['aggregate', 'prefix']]
module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
warnings = list()
result = {'changed': False}
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module, required_together=required_together)
have = map_config_to_obj(module)
commands = map_obj_to_commands(want, have)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,710 +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 ios_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: ios_static_routes
version_added: "2.10"
short_description: Configure and manage static routes on IOS devices.
description: This module configures and manages the static routes on IOS platforms.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of static route options
type: list
elements: dict
suboptions:
vrf:
description:
- IP VPN Routing/Forwarding instance name.
- NOTE, In case of IPV4/IPV6 VRF routing table should pre-exist before
configuring.
- NOTE, if the vrf information is not provided then the routes shall be
configured under global vrf.
type: str
address_families:
elements: dict
description:
- Address family to use for the static routes
type: list
suboptions:
afi:
description:
- Top level address family indicator.
required: true
type: str
choices:
- ipv4
- ipv6
routes:
description: Configuring static route
type: list
elements: dict
suboptions:
dest:
description: Destination prefix with its subnet mask
type: str
required: true
topology:
description:
- Configure static route for a Topology Routing/Forwarding instance
- NOTE, VRF and Topology can be used together only with Multicast and
Topology should pre-exist before it can be used
type: str
next_hops:
description:
- next hop address or interface
type: list
elements: dict
suboptions:
forward_router_address:
description: Forwarding router's address
type: str
interface:
description: Interface for directly connected static routes
type: str
dhcp:
description: Default gateway obtained from DHCP
type: bool
distance_metric:
description: Distance metric for this route
type: int
global:
description: Next hop address is global
type: bool
name:
description: Specify name of the next hop
type: str
multicast:
description: multicast route
type: bool
permanent:
description: permanent route
type: bool
tag:
description:
- Set tag for this route
- Refer to vendor documentation for valid values.
type: int
track:
description:
- Install route depending on tracked item with tracked object number.
- Tracking does not support multicast
- Refer to vendor documentation for valid values.
type: int
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
C(show configuration commands | grep 'static route')
type: str
state:
description:
- The state the configuration should be left in
- The states I(rendered), I(gathered) and I(parsed) does not perform any change on the
device.
- The state I(rendered) will transform the configuration in C(config) option to platform
specific CLI commands which will be returned in the I(rendered) key within the result.
For state I(rendered) active connection to remote host is not required.
- The state I(gathered) will fetch the running configuration from device and transform
it into structured data in the format as per the resource module argspec and the
value is returned in the I(gathered) key within the result.
- The state I(parsed) reads the configuration from C(running_config) option and transforms
it into JSON format as per the resource module parameters and the value is returned in
the I(parsed) key within the result. The value of C(running_config) option should be the
same format as the output of command I(show running-config | include ip route|ipv6 route)
executed on device. For state I(parsed) active connection to remote host is not required.
type: str
choices:
- merged
- replaced
- overridden
- deleted
- gathered
- rendered
- parsed
default: merged
"""
EXAMPLES = """
---
# Using merged
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
- name: Merge provided configuration with device configuration
ios_static_routes:
config:
- vrf: blue
address_families:
- afi: ipv4
routes:
- dest: 192.0.2.0/24
next_hops:
- forward_router_address: 192.0.2.1
name: merged_blue
tag: 50
track: 150
- address_families:
- afi: ipv4
routes:
- dest: 198.51.100.0/24
next_hops:
- forward_router_address: 198.51.101.1
name: merged_route_1
distance_metric: 110
tag: 40
multicast: True
- forward_router_address: 198.51.101.2
name: merged_route_2
distance_metric: 30
- forward_router_address: 198.51.101.3
name: merged_route_3
- afi: ipv6
routes:
- dest: 2001:DB8:0:3::/64
next_hops:
- forward_router_address: 2001:DB8:0:3::2
name: merged_v6
tag: 105
state: merged
# Commands fired:
# ---------------
# ip route vrf blue 192.0.2.0 255.255.255.0 10.0.0.8 name merged_blue track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name merged_route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name merged_v6 tag 105
# After state:
# ------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name merged_blue track 150
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name merged_route_3
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name merged_route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name merged_route_1 multicast
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name merged_v6
# Using replaced
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
- name: Replace provided configuration with device configuration
ios_static_routes:
config:
- address_families:
- afi: ipv4
routes:
- dest: 198.51.100.0/24
next_hops:
- forward_router_address: 198.51.101.1
name: replaced_route
distance_metric: 175
tag: 70
multicast: True
state: replaced
# Commands fired:
# ---------------
# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 name replaced_route track 150 tag 70
# After state:
# ------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 175 name replaced_route track 150 tag 70
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6
# Using overridden
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
- name: Override provided configuration with device configuration
ios_static_routes:
config:
- vrf: blue
address_families:
- afi: ipv4
routes:
- dest: 192.0.2.0/24
next_hops:
- forward_router_address: 192.0.2.1
name: override_vrf
tag: 50
track: 150
state: overridden
# Commands fired:
# ---------------
# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 198.51.101.8 name test_vrf track 150 tag 50
# no ipv6 route FD5D:12C9:2201:1::/64 FD5D:12C9:2202::2 name test_v6 tag 105
# ip route vrf blue 192.0.2.0 255.255.255.0 198.51.101.4 name override_vrf track 150 tag 50
# After state:
# ------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf blue 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name override_vrf track 150
# Using Deleted
# Example 1:
# ----------
# To delete the exact static routes, with all the static routes explicitly mentioned in want
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
- name: Delete provided configuration from the device configuration
ios_static_routes:
config:
- vrf: ansible_temp_vrf
address_families:
- afi: ipv4
routes:
- dest: 192.0.2.0/24
next_hops:
- forward_router_address: 192.0.2.1
name: test_vrf
tag: 50
track: 150
- address_families:
- afi: ipv4
routes:
- dest: 198.51.100.0/24
next_hops:
- forward_router_address: 198.51.101.1
name: route_1
distance_metric: 110
tag: 40
multicast: True
- forward_router_address: 198.51.101.2
name: route_2
distance_metric: 30
- forward_router_address: 198.51.101.3
name: route_3
- afi: ipv6
routes:
- dest: 2001:DB8:0:3::/64
next_hops:
- forward_router_address: 2001:DB8:0:3::2
name: test_v6
tag: 105
state: deleted
# Commands fired:
# ---------------
# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 198.51.101.8 name test_vrf track 150 tag 50
# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# no ipv6 route FD5D:12C9:2201:1::/64 FD5D:12C9:2202::2 name test_v6 tag 105
# After state:
# ------------
#
# vios#show running-config | include ip route|ipv6 route
# Example 2:
# ----------
# To delete the destination specific static routes
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
- name: Delete provided configuration from the device configuration
ios_static_routes:
config:
- address_families:
- afi: ipv4
routes:
- dest: 198.51.100.0/24
state: deleted
# Commands fired:
# ---------------
# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast
# After state:
# ------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6
# Example 3:
# ----------
# To delete the vrf specific static routes
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
- name: Delete provided configuration from the device configuration
ios_static_routes:
config:
- vrf: ansible_temp_vrf
state: deleted
# Commands fired:
# ---------------
# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# After state:
# ------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured resource module attributes from each configured interface)"
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
- name: Delete ALL configured IOS static routes
ios_static_routes:
state: deleted
# Commands fired:
# ---------------
# no ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 tag 50 name test_vrf track 150
# no ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# no ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# no ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 tag 40 name route_1 multicast
# no ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 tag 105 name test_v6
# After state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
#
# Using gathered
# Before state:
# -------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
- name: Gather listed static routes with provided configurations
ios_static_routes:
config:
state: gathered
# Module Execution Result:
# ------------------------
#
# "gathered": [
# {
# "address_families": [
# {
# "afi": "ipv4",
# "routes": [
# {
# "dest": "192.0.2.0/24",
# "next_hops": [
# {
# "forward_router_address": "192.0.2.1",
# "name": "test_vrf",
# "tag": 50,
# "track": 150
# }
# ]
# }
# ]
# }
# ],
# "vrf": "ansible_temp_vrf"
# },
# {
# "address_families": [
# {
# "afi": "ipv6",
# "routes": [
# {
# "dest": "2001:DB8:0:3::/64",
# "next_hops": [
# {
# "forward_router_address": "2001:DB8:0:3::2",
# "name": "test_v6",
# "tag": 105
# }
# ]
# }
# ]
# },
# {
# "afi": "ipv4",
# "routes": [
# {
# "dest": "198.51.100.0/24",
# "next_hops": [
# {
# "distance_metric": 110,
# "forward_router_address": "198.51.101.1",
# "multicast": true,
# "name": "route_1",
# "tag": 40
# },
# {
# "distance_metric": 30,
# "forward_router_address": "198.51.101.2",
# "name": "route_2"
# },
# {
# "forward_router_address": "198.51.101.3",
# "name": "route_3"
# }
# ]
# }
# ]
# }
# ]
# }
# ]
# After state:
# ------------
#
# vios#show running-config | include ip route|ipv6 route
# ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50
# ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40
# ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2
# ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3
# ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105
# Using rendered
- name: Render the commands for provided configuration
ios_static_routes:
config:
- vrf: ansible_temp_vrf
address_families:
- afi: ipv4
routes:
- dest: 192.0.2.0/24
next_hops:
- forward_router_address: 192.0.2.1
name: test_vrf
tag: 50
track: 150
- address_families:
- afi: ipv4
routes:
- dest: 198.51.100.0/24
next_hops:
- forward_router_address: 198.51.101.1
name: route_1
distance_metric: 110
tag: 40
multicast: True
- forward_router_address: 198.51.101.2
name: route_2
distance_metric: 30
- forward_router_address: 198.51.101.3
name: route_3
- afi: ipv6
routes:
- dest: 2001:DB8:0:3::/64
next_hops:
- forward_router_address: 2001:DB8:0:3::2
name: test_v6
tag: 105
state: rendered
# Module Execution Result:
# ------------------------
#
# "rendered": [
# "ip route vrf ansible_temp_vrf 192.0.2.0 255.255.255.0 192.0.2.1 name test_vrf track 150 tag 50",
# "ip route 198.51.100.0 255.255.255.0 198.51.101.1 110 multicast name route_1 tag 40",
# "ip route 198.51.100.0 255.255.255.0 198.51.101.2 30 name route_2",
# "ip route 198.51.100.0 255.255.255.0 198.51.101.3 name route_3",
# "ipv6 route 2001:DB8:0:3::/64 2001:DB8:0:3::2 name test_v6 tag 105"
# ]
"""
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: ['ip route vrf test 172.31.10.0 255.255.255.0 10.10.10.2 name new_test multicast']
rendered:
description: The set of CLI commands generated from the value in C(config) option
returned: When C(state) is I(rendered)
type: list
sample: ['interface Ethernet1/1', 'mtu 1800']
gathered:
description:
- The configuration as structured data transformed for the running configuration
fetched from remote host
returned: When C(state) is I(gathered)
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
parsed:
description:
- The configuration as structured data transformed for the value of
C(running_config) option
returned: When C(state) is I(parsed)
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.static_routes.static_routes import Static_RoutesArgs
from ansible.module_utils.network.ios.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', 'rendered', ('config',)),
('state', 'parsed', ('running_config',))]
mutually_exclusive = [('config', 'running_config')]
module = AnsibleModule(argument_spec=Static_RoutesArgs.argument_spec,
required_if=required_if,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive)
result = Static_Routes(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,380 +0,0 @@
#!/usr/bin/python
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_system
version_added: "2.3"
author: "Peter Sprygada (@privateip)"
short_description: Manage the system attributes on Cisco IOS devices
description:
- This module provides declarative management of node system attributes
on Cisco IOS devices. It provides an option to configure host system
parameters or remove those parameters from the device active
configuration.
extends_documentation_fragment: ios
notes:
- Tested against IOS 15.6
options:
hostname:
description:
- Configure the device hostname parameter. This option takes an ASCII string value.
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:
- 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:
- 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:
- List of DNS name servers by IP address to use to perform name resolution
lookups. This argument accepts either 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
ios_system:
hostname: ios01
domain_name: test.example.com
domain_search:
- ansible.com
- redhat.com
- cisco.com
- name: remove configuration
ios_system:
state: absent
- name: configure DNS lookup sources
ios_system:
lookup_source: MgmtEth0/0/CPU0/0
lookup_enabled: yes
- name: configure name servers
ios_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 ios01
- ip domain name test.example.com
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
from ansible.module_utils.network.common.utils import ComplexList
_CONFIGURED_VRFS = None
def has_vrf(module, vrf):
global _CONFIGURED_VRFS
if _CONFIGURED_VRFS is not None:
return vrf in _CONFIGURED_VRFS
config = get_config(module)
_CONFIGURED_VRFS = re.findall(r'vrf definition (\S+)', config)
return vrf in _CONFIGURED_VRFS
def requires_vrf(module, vrf):
if not has_vrf(module, vrf):
module.fail_json(msg='vrf %s is not configured' % vrf)
def diff_list(want, have):
adds = [w for w in want if w not in have]
removes = [h for h in have if h not in want]
return (adds, removes)
def map_obj_to_commands(want, have, module):
commands = list()
state = module.params['state']
def needs_update(x):
return want.get(x) is not None and (want.get(x) != have.get(x))
if state == 'absent':
if have['hostname'] != 'Router':
commands.append('no hostname')
if have['lookup_source']:
commands.append('no ip domain lookup source-interface %s' % have['lookup_source'])
if have['lookup_enabled'] is False:
commands.append('ip domain lookup')
vrfs = set()
for item in have['domain_name']:
if item['vrf'] and item['vrf'] not in vrfs:
vrfs.add(item['vrf'])
commands.append('no ip domain name vrf %s' % item['vrf'])
elif None not in vrfs:
vrfs.add(None)
commands.append('no ip domain name')
vrfs = set()
for item in have['domain_search']:
if item['vrf'] and item['vrf'] not in vrfs:
vrfs.add(item['vrf'])
commands.append('no ip domain list vrf %s' % item['vrf'])
elif None not in vrfs:
vrfs.add(None)
commands.append('no ip domain list')
vrfs = set()
for item in have['name_servers']:
if item['vrf'] and item['vrf'] not in vrfs:
vrfs.add(item['vrf'])
commands.append('no ip name-server vrf %s' % item['vrf'])
elif None not in vrfs:
vrfs.add(None)
commands.append('no ip name-server')
elif state == 'present':
if needs_update('hostname'):
commands.append('hostname %s' % want['hostname'])
if needs_update('lookup_source'):
commands.append('ip domain lookup source-interface %s' % want['lookup_source'])
if needs_update('lookup_enabled'):
cmd = 'ip domain lookup'
if want['lookup_enabled'] is False:
cmd = 'no %s' % cmd
commands.append(cmd)
if want['domain_name']:
adds, removes = diff_list(want['domain_name'], have['domain_name'])
for item in removes:
if item['vrf']:
commands.append('no ip domain name vrf %s %s' % (item['vrf'], item['name']))
else:
commands.append('no ip domain name %s' % item['name'])
for item in adds:
if item['vrf']:
requires_vrf(module, item['vrf'])
commands.append('ip domain name vrf %s %s' % (item['vrf'], item['name']))
else:
commands.append('ip domain name %s' % item['name'])
if want['domain_search']:
adds, removes = diff_list(want['domain_search'], have['domain_search'])
for item in removes:
if item['vrf']:
commands.append('no ip domain list vrf %s %s' % (item['vrf'], item['name']))
else:
commands.append('no ip domain list %s' % item['name'])
for item in adds:
if item['vrf']:
requires_vrf(module, item['vrf'])
commands.append('ip domain list vrf %s %s' % (item['vrf'], item['name']))
else:
commands.append('ip domain list %s' % item['name'])
if want['name_servers']:
adds, removes = diff_list(want['name_servers'], have['name_servers'])
for item in removes:
if item['vrf']:
commands.append('no ip name-server vrf %s %s' % (item['vrf'], item['server']))
else:
commands.append('no ip name-server %s' % item['server'])
for item in adds:
if item['vrf']:
requires_vrf(module, item['vrf'])
commands.append('ip name-server vrf %s %s' % (item['vrf'], item['server']))
else:
commands.append('ip name-server %s' % item['server'])
return commands
def parse_hostname(config):
match = re.search(r'^hostname (\S+)', config, re.M)
return match.group(1)
def parse_domain_name(config):
match = re.findall(r'^ip domain[- ]name (?:vrf (\S+) )*(\S+)', config, re.M)
matches = list()
for vrf, name in match:
if not vrf:
vrf = None
matches.append({'name': name, 'vrf': vrf})
return matches
def parse_domain_search(config):
match = re.findall(r'^ip domain[- ]list (?:vrf (\S+) )*(\S+)', config, re.M)
matches = list()
for vrf, name in match:
if not vrf:
vrf = None
matches.append({'name': name, 'vrf': vrf})
return matches
def parse_name_servers(config):
match = re.findall(r'^ip name-server (?:vrf (\S+) )*(.*)', config, re.M)
matches = list()
for vrf, servers in match:
if not vrf:
vrf = None
for server in servers.split():
matches.append({'server': server, 'vrf': vrf})
return matches
def parse_lookup_source(config):
match = re.search(r'ip domain[- ]lookup source-interface (\S+)', config, re.M)
if match:
return match.group(1)
def map_config_to_obj(module):
config = get_config(module)
return {
'hostname': parse_hostname(config),
'domain_name': parse_domain_name(config),
'domain_search': parse_domain_search(config),
'lookup_source': parse_lookup_source(config),
'lookup_enabled': 'no ip domain lookup' not in config and 'no ip domain-lookup' not in config,
'name_servers': parse_name_servers(config)
}
def map_params_to_obj(module):
obj = {
'hostname': module.params['hostname'],
'lookup_source': module.params['lookup_source'],
'lookup_enabled': module.params['lookup_enabled'],
}
domain_name = ComplexList(dict(
name=dict(key=True),
vrf=dict()
), module)
domain_search = ComplexList(dict(
name=dict(key=True),
vrf=dict()
), module)
name_servers = ComplexList(dict(
server=dict(key=True),
vrf=dict()
), module)
for arg, cast in [('domain_name', domain_name),
('domain_search', domain_search),
('name_servers', name_servers)]:
if module.params[arg]:
obj[arg] = cast(module.params[arg])
else:
obj[arg] = None
return obj
def main():
""" Main entry point for Ansible module execution
"""
argument_spec = dict(
hostname=dict(),
domain_name=dict(type='list'),
domain_search=dict(type='list'),
name_servers=dict(type='list'),
lookup_source=dict(),
lookup_enabled=dict(type='bool'),
state=dict(choices=['present', 'absent'], default='present')
)
argument_spec.update(ios_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
result = {'changed': False}
warnings = list()
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands(want, have, module)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == "__main__":
main()

@ -1,533 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_user
version_added: "2.4"
author: "Trishna Guha (@trishnaguha)"
short_description: Manage the aggregate of local users on Cisco IOS 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.
notes:
- Tested against IOS 15.6
options:
aggregate:
description:
- The set of username objects to be configured on the remote
Cisco IOS 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 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 device. The
password needs to be provided in clear and it will be encrypted
on the device.
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']
password_type:
description:
- This argument determines whether a 'password' or 'secret' will be
configured.
default: secret
choices: ['secret', 'password']
version_added: "2.8"
hashed_password:
description:
- This option allows configuring hashed passwords on Cisco IOS devices.
suboptions:
type:
description:
- Specifies the type of hash (e.g., 5 for MD5, 8 for PBKDF2, etc.)
- For this to work, the device needs to support the desired hash type
type: int
required: True
value:
description:
- The actual hashed password to be configured on the device
required: True
version_added: "2.8"
privilege:
description:
- The C(privilege) argument configures the privilege level of the
user when logged into the system. This argument accepts integer
values in the range of 1 to 15.
view:
description:
- Configures the view for the username in the
device running configuration. The argument accepts a string value
defining the view name. This argument does not check if the view
has been configured on the device.
aliases: ['role']
sshkey:
description:
- Specifies one or more SSH public key(s) to configure
for the given username.
- This argument accepts a valid SSH key value.
version_added: "2.7"
nopassword:
description:
- Defines the username without assigning
a password. This will allow the user to login to the system
without being authenticated by a password.
type: bool
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 (the current defined set of users).
type: bool
default: false
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']
extends_documentation_fragment: ios
"""
EXAMPLES = """
- name: create a new user
ios_user:
name: ansible
nopassword: True
sshkey: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: present
- name: create a new user with multiple keys
ios_user:
name: ansible
sshkey:
- "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
- "{{ lookup('file', '~/path/to/public_key') }}"
state: present
- name: remove all users except admin
ios_user:
purge: yes
- name: remove all users except admin and these listed users
ios_user:
aggregate:
- name: testuser1
- name: testuser2
- name: testuser3
purge: yes
- name: set multiple users to privilege level 15
ios_user:
aggregate:
- name: netop
- name: netend
privilege: 15
state: present
- name: set user view/role
ios_user:
name: netop
view: network-operator
state: present
- name: Change Password for User netop
ios_user:
name: netop
configured_password: "{{ new_password }}"
update_password: always
state: present
- name: Aggregate of users
ios_user:
aggregate:
- name: ansibletest2
- name: ansibletest3
view: network-admin
- name: Add a user specifying password type
ios_user:
name: ansibletest4
configured_password: "{{ new_password }}"
password_type: password
- name: Add a user with MD5 hashed password
ios_user:
name: ansibletest5
hashed_password:
type: 5
value: $3$8JcDilcYgFZi.yz4ApaqkHG2.8/
- name: Delete users with aggregate
ios_user:
aggregate:
- name: ansibletest1
- name: ansibletest2
- name: ansibletest3
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- username ansible secret password
- username admin secret admin
"""
import base64
import hashlib
import re
from copy import deepcopy
from functools import partial
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.ios.ios import get_config, load_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
from ansible.module_utils.six import iteritems
def validate_privilege(value, module):
if value and not 1 <= value <= 15:
module.fail_json(msg='privilege must be between 1 and 15, got %s' % value)
def user_del_cmd(username):
return {
'command': 'no username %s' % username,
'prompt': 'This operation will remove all username related configurations with same name',
'answer': 'y',
'newline': False,
}
def sshkey_fingerprint(sshkey):
# IOS will accept a MD5 fingerprint of the public key
# and is easier to configure in a single line
# we calculate this fingerprint here
if not sshkey:
return None
if ' ' in sshkey:
# ssh-rsa AAA...== comment
keyparts = sshkey.split(' ')
keyparts[1] = hashlib.md5(base64.b64decode(keyparts[1])).hexdigest().upper()
return ' '.join(keyparts)
else:
# just the key, assume rsa type
return 'ssh-rsa %s' % hashlib.md5(base64.b64decode(sshkey)).hexdigest().upper()
def map_obj_to_commands(updates, module):
commands = list()
update_password = module.params['update_password']
password_type = module.params['password_type']
def needs_update(want, have, x):
return want.get(x) and (want.get(x) != have.get(x))
def add(command, want, x):
command.append('username %s %s' % (want['name'], x))
def add_hashed_password(command, want, x):
command.append('username %s secret %s %s' % (want['name'], x.get('type'),
x.get('value')))
def add_ssh(command, want, x=None):
command.append('ip ssh pubkey-chain')
if x:
command.append('username %s' % want['name'])
for item in x:
command.append('key-hash %s' % item)
command.append('exit')
else:
command.append('no username %s' % want['name'])
command.append('exit')
for update in updates:
want, have = update
if want['state'] == 'absent':
if have['sshkey']:
add_ssh(commands, want)
else:
commands.append(user_del_cmd(want['name']))
if needs_update(want, have, 'view'):
add(commands, want, 'view %s' % want['view'])
if needs_update(want, have, 'privilege'):
add(commands, want, 'privilege %s' % want['privilege'])
if needs_update(want, have, 'sshkey'):
add_ssh(commands, want, want['sshkey'])
if needs_update(want, have, 'configured_password'):
if update_password == 'always' or not have:
if have and password_type != have['password_type']:
module.fail_json(msg='Can not have both a user password and a user secret.' +
' Please choose one or the other.')
add(commands, want, '%s %s' % (password_type, want['configured_password']))
if needs_update(want, have, 'hashed_password'):
add_hashed_password(commands, want, want['hashed_password'])
if needs_update(want, have, 'nopassword'):
if want['nopassword']:
add(commands, want, 'nopassword')
else:
add(commands, want, user_del_cmd(want['name']))
return commands
def parse_view(data):
match = re.search(r'view (\S+)', data, re.M)
if match:
return match.group(1)
def parse_sshkey(data, user):
sshregex = r'username %s(\n\s+key-hash .+$)+' % user
sshcfg = re.search(sshregex, data, re.M)
key_list = []
if sshcfg:
match = re.findall(r'key-hash (\S+ \S+(?: .+)?)$', sshcfg.group(), re.M)
if match:
key_list = match
return key_list
def parse_privilege(data):
match = re.search(r'privilege (\S+)', data, re.M)
if match:
return int(match.group(1))
def parse_password_type(data):
type = None
if data and data.split()[-3] in ['password', 'secret']:
type = data.split()[-3]
return type
def map_config_to_obj(module):
data = get_config(module, flags=['| section username'])
match = re.findall(r'(?:^(?:u|\s{2}u))sername (\S+)', data, re.M)
if not match:
return list()
instances = list()
for user in set(match):
regex = r'username %s .+$' % user
cfg = re.findall(regex, data, re.M)
cfg = '\n'.join(cfg)
obj = {
'name': user,
'state': 'present',
'nopassword': 'nopassword' in cfg,
'configured_password': None,
'hashed_password': None,
'password_type': parse_password_type(cfg),
'sshkey': parse_sshkey(data, user),
'privilege': parse_privilege(cfg),
'view': parse_view(cfg)
}
instances.append(obj)
return instances
def get_param_value(key, item, module):
# if key doesn't exist in the item, get it from module.params
if not item.get(key):
value = module.params[key]
# if key does exist, do a type check on it to validate it
else:
value_type = module.argument_spec[key].get('type', 'str')
type_checker = 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, module)
return value
def map_params_to_obj(module):
users = module.params['aggregate']
if not users:
if not module.params['name'] and module.params['purge']:
return list()
elif not module.params['name']:
module.fail_json(msg='username is required')
else:
aggregate = [{'name': module.params['name']}]
else:
aggregate = list()
for item in users:
if not isinstance(item, dict):
aggregate.append({'name': item})
elif 'name' not in item:
module.fail_json(msg='name is required')
else:
aggregate.append(item)
objects = list()
for item in aggregate:
get_value = partial(get_param_value, item=item, module=module)
item['configured_password'] = get_value('configured_password')
item['hashed_password'] = get_value('hashed_password')
item['nopassword'] = get_value('nopassword')
item['privilege'] = get_value('privilege')
item['view'] = get_value('view')
item['sshkey'] = render_key_list(get_value('sshkey'))
item['state'] = get_value('state')
objects.append(item)
return objects
def render_key_list(ssh_keys):
key_list = []
if ssh_keys:
for item in ssh_keys:
key_list.append(sshkey_fingerprint(item))
return key_list
def update_objects(want, have):
updates = list()
for entry in want:
item = next((i for i in have if i['name'] == entry['name']), None)
if all((item is None, entry['state'] == 'present')):
updates.append((entry, {}))
elif item:
for key, value in iteritems(entry):
if value and value != item[key]:
updates.append((entry, item))
return updates
def main():
""" main entry point for module execution
"""
hashed_password_spec = dict(
type=dict(type='int', required=True),
value=dict(no_log=True, required=True)
)
element_spec = dict(
name=dict(),
configured_password=dict(no_log=True),
hashed_password=dict(no_log=True, type='dict', options=hashed_password_spec),
nopassword=dict(type='bool'),
update_password=dict(default='always', choices=['on_create', 'always']),
password_type=dict(default='secret', choices=['secret', 'password']),
privilege=dict(type='int'),
view=dict(aliases=['role']),
sshkey=dict(type='list'),
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)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec, aliases=['users', 'collection']),
purge=dict(type='bool', default=False)
)
argument_spec.update(element_spec)
argument_spec.update(ios_argument_spec)
mutually_exclusive = [('name', 'aggregate'), ('nopassword', 'hashed_password', 'configured_password')]
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
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(update_objects(want, have), module)
if module.params['purge']:
want_users = [x['name'] for x in want]
have_users = [x['name'] for x in have]
for item in set(have_users).difference(want_users):
if item != 'admin':
commands.append(user_del_cmd(item))
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,464 +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 ios_vlans
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
module: ios_vlans
version_added: 2.9
short_description: Manage VLANs on Cisco IOS devices.
description: This module provides declarative management of VLANs on Cisco IOS network devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of VLANs options
type: list
elements: dict
suboptions:
name:
description:
- Ascii name of the VLAN.
- NOTE, I(name) should not be named/appended with I(default) as it is reserved for device default vlans.
type: str
vlan_id:
description:
- ID of the VLAN. Range 1-4094
type: int
required: True
mtu:
description:
- VLAN Maximum Transmission Unit.
- Refer to vendor documentation for valid values.
type: int
state:
description:
- Operational state of the VLAN
type: str
choices:
- active
- suspend
remote_span:
description:
- Configure as Remote SPAN VLAN
type: bool
shutdown:
description:
- Shutdown VLAN switching.
type: str
choices:
- enabled
- disabled
state:
description:
- The state of the configuration after module completion
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
---
# Using merged
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
- name: Merge provided configuration with device configuration
ios_vlans:
config:
- name: Vlan_10
vlan_id: 10
state: active
shutdown: disabled
remote_span: 10
- name: Vlan_20
vlan_id: 20
mtu: 610
state: active
shutdown: enabled
- name: Vlan_30
vlan_id: 30
state: suspend
shutdown: enabled
state: merged
# After state:
# ------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
# Using overridden
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Override device configuration of all VLANs with provided configuration
ios_vlans:
config:
- name: Vlan_10
vlan_id: 10
mtu: 1000
state: overridden
# After state:
# ------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 10 Vlan_10 active
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 10 enet 100010 1000 - - - - - 0 0
# Using replaced
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Replaces device configuration of listed VLANs with provided configuration
ios_vlans:
config:
- vlan_id: 20
name: Test_VLAN20
mtu: 700
shutdown: disabled
- vlan_id: 30
name: Test_VLAN30
mtu: 1000
state: replaced
# After state:
# ------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 Test_VLAN20 active
# 30 Test_VLAN30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 700 - - - - - 0 0
# 30 enet 100030 1000 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
# Using deleted
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Delete attributes of given VLANs
ios_vlans:
config:
- vlan_id: 10
- vlan_id: 20
state: deleted
# After state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured vlans attributes)"
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Delete attributes of ALL VLANs
ios_vlans:
state: deleted
# After state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
"""
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: ['vlan 20', 'name vlan_20', 'mtu 600', 'remote-span']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.vlans.vlans import VlansArgs
from ansible.module_utils.network.ios.config.vlans.vlans import Vlans
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=VlansArgs.argument_spec,
required_if=required_if,
supports_check_mode=True)
result = Vlans(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,719 +0,0 @@
#!/usr/bin/python
#
# 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/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_vrf
version_added: "2.3"
author: "Peter Sprygada (@privateip)"
short_description: Manage the collection of VRF definitions on Cisco IOS devices
description:
- This module provides declarative management of VRF definitions on
Cisco IOS devices. It allows playbooks to manage individual or
the entire VRF collection. It also supports purging VRF definitions from
the configuration that are not explicitly defined.
extends_documentation_fragment: ios
notes:
- Tested against IOS 15.6
options:
vrfs:
description:
- The set of VRF definition objects to be configured on the remote
IOS device. Ths list entries can either be the VRF name or a hash
of VRF definitions and attributes. This argument is mutually
exclusive with the C(name) argument.
name:
description:
- The name of the VRF definition to be managed on the remote IOS
device. The VRF definition name is an ASCII string name used
to uniquely identify the VRF. This argument is mutually exclusive
with the C(vrfs) argument
description:
description:
- Provides a short description of the VRF definition in the
current active configuration. The VRF definition value accepts
alphanumeric characters used to provide additional information
about the VRF.
rd:
description:
- The router-distinguisher value uniquely identifies the VRF to
routing processes on the remote IOS system. The RD value takes
the form of C(A:B) where C(A) and C(B) are both numeric values.
interfaces:
description:
- Identifies the set of interfaces that
should be configured in the VRF. Interfaces must be routed
interfaces in order to be placed into a VRF.
associated_interfaces:
description:
- This is a intent option and checks the operational state of the for given vrf C(name)
for associated interfaces. If the value in the C(associated_interfaces) does not match with
the operational state of vrf interfaces on device it will result in failure.
version_added: "2.5"
delay:
description:
- Time in seconds to wait before checking for the operational state on remote
device.
version_added: "2.4"
default: 10
purge:
description:
- Instructs the module to consider the
VRF definition absolute. It will remove any previously configured
VRFs on the device.
default: false
type: bool
state:
description:
- Configures the state of the VRF definition
as it relates to the device operational configuration. When set
to I(present), the VRF should be configured in the device active
configuration and when set to I(absent) the VRF should not be
in the device active configuration
default: present
choices: ['present', 'absent']
route_both:
description:
- Adds an export and import list of extended route target communities to the VRF.
version_added: "2.5"
route_export:
description:
- Adds an export list of extended route target communities to the VRF.
version_added: "2.5"
route_import:
description:
- Adds an import list of extended route target communities to the VRF.
version_added: "2.5"
route_both_ipv4:
description:
- Adds an export and import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_export_ipv4:
description:
- Adds an export list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_import_ipv4:
description:
- Adds an import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_both_ipv6:
description:
- Adds an export and import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_export_ipv6:
description:
- Adds an export list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
route_import_ipv6:
description:
- Adds an import list of extended route target communities in address-family configuration submode to the VRF.
version_added: "2.7"
"""
EXAMPLES = """
- name: configure a vrf named management
ios_vrf:
name: management
description: oob mgmt vrf
interfaces:
- Management1
- name: remove a vrf named test
ios_vrf:
name: test
state: absent
- name: configure set of VRFs and purge any others
ios_vrf:
vrfs:
- red
- blue
- green
purge: yes
- name: Creates a list of import RTs for the VRF with the same parameters
ios_vrf:
name: test_import
rd: 1:100
route_import:
- 1:100
- 3:100
- name: Creates a list of import RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_import_ipv4
rd: 1:100
route_import_ipv4:
- 1:100
- 3:100
- name: Creates a list of import RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_import_ipv6
rd: 1:100
route_import_ipv6:
- 1:100
- 3:100
- name: Creates a list of export RTs for the VRF with the same parameters
ios_vrf:
name: test_export
rd: 1:100
route_export:
- 1:100
- 3:100
- name: Creates a list of export RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_export_ipv4
rd: 1:100
route_export_ipv4:
- 1:100
- 3:100
- name: Creates a list of export RTs in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_export_ipv6
rd: 1:100
route_export_ipv6:
- 1:100
- 3:100
- name: Creates a list of import and export route targets for the VRF with the same parameters
ios_vrf:
name: test_both
rd: 1:100
route_both:
- 1:100
- 3:100
- name: Creates a list of import and export route targets in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_both_ipv4
rd: 1:100
route_both_ipv4:
- 1:100
- 3:100
- name: Creates a list of import and export route targets in address-family configuration submode for the VRF with the same parameters
ios_vrf:
name: test_both_ipv6
rd: 1:100
route_both_ipv6:
- 1:100
- 3:100
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- vrf definition ansible
- description management vrf
- rd: 1:100
start:
description: The time the job started
returned: always
type: str
sample: "2016-11-16 10:38:15.126146"
end:
description: The time the job ended
returned: always
type: str
sample: "2016-11-16 10:38:25.595612"
delta:
description: The time elapsed to perform all operations
returned: always
type: str
sample: "0:00:10.469466"
"""
import re
import time
from functools import partial
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import exec_command
from ansible.module_utils.network.ios.ios import load_config, get_config
from ansible.module_utils.network.ios.ios import ios_argument_spec
from ansible.module_utils.network.common.config import NetworkConfig
from ansible.module_utils.six import iteritems
def get_interface_type(interface):
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
elif interface.upper().startswith('NV'):
return 'nve'
else:
return 'unknown'
def add_command_to_vrf(name, cmd, commands):
if 'vrf definition %s' % name not in commands:
commands.extend(['vrf definition %s' % name])
commands.append(cmd)
def map_obj_to_commands(updates, module):
commands = list()
for update in updates:
want, have = update
def needs_update(want, have, x):
if isinstance(want.get(x), list) and isinstance(have.get(x), list):
return want.get(x) and (want.get(x) != have.get(x)) and not all(elem in have.get(x) for elem in want.get(x))
return want.get(x) and (want.get(x) != have.get(x))
if want['state'] == 'absent':
commands.append('no vrf definition %s' % want['name'])
continue
if not have.get('state'):
commands.extend(['vrf definition %s' % want['name']])
ipv6 = len([k for k, v in module.params.items() if (k.endswith('_ipv6') or k.endswith('_both')) and v]) != 0
ipv4 = len([k for k, v in module.params.items() if (k.endswith('_ipv4') or k.endswith('_both')) and v]) != 0
if ipv4:
commands.extend(['address-family ipv4', 'exit'])
if ipv6:
commands.extend(['address-family ipv6', 'exit'])
if needs_update(want, have, 'description'):
cmd = 'description %s' % want['description']
add_command_to_vrf(want['name'], cmd, commands)
if needs_update(want, have, 'rd'):
cmd = 'rd %s' % want['rd']
add_command_to_vrf(want['name'], cmd, commands)
if needs_update(want, have, 'route_import'):
for route in want['route_import']:
cmd = 'route-target import %s' % route
add_command_to_vrf(want['name'], cmd, commands)
if needs_update(want, have, 'route_export'):
for route in want['route_export']:
cmd = 'route-target export %s' % route
add_command_to_vrf(want['name'], cmd, commands)
if needs_update(want, have, 'route_import_ipv4'):
cmd = 'address-family ipv4'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_import_ipv4']:
cmd = 'route-target import %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)
if needs_update(want, have, 'route_export_ipv4'):
cmd = 'address-family ipv4'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_export_ipv4']:
cmd = 'route-target export %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)
if needs_update(want, have, 'route_import_ipv6'):
cmd = 'address-family ipv6'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_import_ipv6']:
cmd = 'route-target import %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)
if needs_update(want, have, 'route_export_ipv6'):
cmd = 'address-family ipv6'
add_command_to_vrf(want['name'], cmd, commands)
for route in want['route_export_ipv6']:
cmd = 'route-target export %s' % route
add_command_to_vrf(want['name'], cmd, commands)
cmd = 'exit-address-family'
add_command_to_vrf(want['name'], cmd, commands)
if want['interfaces'] is not None:
# handle the deletes
for intf in set(have.get('interfaces', [])).difference(want['interfaces']):
commands.extend(['interface %s' % intf,
'no vrf forwarding %s' % want['name']])
# handle the adds
for intf in set(want['interfaces']).difference(have.get('interfaces', [])):
cfg = get_config(module)
configobj = NetworkConfig(indent=1, contents=cfg)
children = configobj['interface %s' % intf].children
intf_config = '\n'.join(children)
commands.extend(['interface %s' % intf,
'vrf forwarding %s' % want['name']])
match = re.search('ip address .+', intf_config, re.M)
if match:
commands.append(match.group())
return commands
def parse_description(configobj, name):
cfg = configobj['vrf definition %s' % name]
cfg = '\n'.join(cfg.children)
match = re.search(r'description (.+)$', cfg, re.M)
if match:
return match.group(1)
def parse_rd(configobj, name):
cfg = configobj['vrf definition %s' % name]
cfg = '\n'.join(cfg.children)
match = re.search(r'rd (.+)$', cfg, re.M)
if match:
return match.group(1)
def parse_interfaces(configobj):
vrf_cfg = 'vrf forwarding'
interfaces = dict()
for intf in set(re.findall('^interface .+', str(configobj), re.M)):
for line in configobj[intf].children:
if vrf_cfg in line:
try:
interfaces[line.split()[-1]].append(intf.split(' ')[1])
except KeyError:
interfaces[line.split()[-1]] = [intf.split(' ')[1]]
return interfaces
def parse_import(configobj, name):
cfg = configobj['vrf definition %s' % name]
cfg = '\n'.join(cfg.children)
matches = re.findall(r'route-target\s+import\s+(.+)', cfg, re.M)
return matches
def parse_export(configobj, name):
cfg = configobj['vrf definition %s' % name]
cfg = '\n'.join(cfg.children)
matches = re.findall(r'route-target\s+export\s+(.+)', cfg, re.M)
return matches
def parse_both(configobj, name, address_family='global'):
rd_pattern = re.compile('(?P<rd>.+:.+)')
matches = list()
export_match = None
import_match = None
if address_family == "global":
export_match = parse_export(configobj, name)
import_match = parse_import(configobj, name)
elif address_family == "ipv4":
export_match = parse_export_ipv4(configobj, name)
import_match = parse_import_ipv4(configobj, name)
elif address_family == "ipv6":
export_match = parse_export_ipv6(configobj, name)
import_match = parse_import_ipv6(configobj, name)
if import_match and export_match:
for ex in export_match:
exrd = rd_pattern.search(ex)
exrd = exrd.groupdict().get('rd')
for im in import_match:
imrd = rd_pattern.search(im)
imrd = imrd.groupdict().get('rd')
if exrd == imrd:
matches.extend([exrd]) if exrd not in matches else None
matches.extend([imrd]) if imrd not in matches else None
return matches
def parse_import_ipv4(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv4']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+import\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass
def parse_export_ipv4(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv4']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+export\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass
def parse_import_ipv6(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv6']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+import\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass
def parse_export_ipv6(configobj, name):
cfg = configobj['vrf definition %s' % name]
try:
subcfg = cfg['address-family ipv6']
subcfg = '\n'.join(subcfg.children)
matches = re.findall(r'route-target\s+export\s+(.+)', subcfg, re.M)
return matches
except KeyError:
pass
def map_config_to_obj(module):
config = get_config(module)
configobj = NetworkConfig(indent=1, contents=config)
match = re.findall(r'^vrf definition (\S+)', config, re.M)
if not match:
return list()
instances = list()
interfaces = parse_interfaces(configobj)
for item in set(match):
obj = {
'name': item,
'state': 'present',
'description': parse_description(configobj, item),
'rd': parse_rd(configobj, item),
'interfaces': interfaces.get(item),
'route_import': parse_import(configobj, item),
'route_export': parse_export(configobj, item),
'route_both': parse_both(configobj, item),
'route_import_ipv4': parse_import_ipv4(configobj, item),
'route_export_ipv4': parse_export_ipv4(configobj, item),
'route_both_ipv4': parse_both(configobj, item, address_family='ipv4'),
'route_import_ipv6': parse_import_ipv6(configobj, item),
'route_export_ipv6': parse_export_ipv6(configobj, item),
'route_both_ipv6': parse_both(configobj, item, address_family='ipv6'),
}
instances.append(obj)
return instances
def get_param_value(key, item, module):
# if key doesn't exist in the item, get it from module.params
if not item.get(key):
value = module.params[key]
# if key does exist, do a type check on it to validate it
else:
value_type = module.argument_spec[key].get('type', 'str')
type_checker = 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 validator:
validator(value, module)
return value
def map_params_to_obj(module):
vrfs = module.params.get('vrfs')
if not vrfs:
if not module.params['name'] and module.params['purge']:
return list()
elif not module.params['name']:
module.fail_json(msg='name is required')
collection = [{'name': module.params['name']}]
else:
collection = list()
for item in vrfs:
if not isinstance(item, dict):
collection.append({'name': item})
elif 'name' not in item:
module.fail_json(msg='name is required')
else:
collection.append(item)
objects = list()
for item in collection:
get_value = partial(get_param_value, item=item, module=module)
item['description'] = get_value('description')
item['rd'] = get_value('rd')
item['interfaces'] = get_value('interfaces')
item['state'] = get_value('state')
item['route_import'] = get_value('route_import')
item['route_export'] = get_value('route_export')
item['route_both'] = get_value('route_both')
item['route_import_ipv4'] = get_value('route_import_ipv4')
item['route_export_ipv4'] = get_value('route_export_ipv4')
item['route_both_ipv4'] = get_value('route_both_ipv4')
item['route_import_ipv6'] = get_value('route_import_ipv6')
item['route_export_ipv6'] = get_value('route_export_ipv6')
item['route_both_ipv6'] = get_value('route_both_ipv6')
both_addresses_family = ["", "_ipv6", "_ipv4"]
for address_family in both_addresses_family:
if item["route_both%s" % address_family]:
if not item["route_export%s" % address_family]:
item["route_export%s" % address_family] = list()
if not item["route_import%s" % address_family]:
item["route_import%s" % address_family] = list()
item["route_export%s" % address_family].extend(get_value("route_both%s" % address_family))
item["route_import%s" % address_family].extend(get_value("route_both%s" % address_family))
item['associated_interfaces'] = get_value('associated_interfaces')
objects.append(item)
return objects
def update_objects(want, have):
updates = list()
for entry in want:
item = next((i for i in have if i['name'] == entry['name']), None)
if all((item is None, entry['state'] == 'present')):
updates.append((entry, {}))
else:
for key, value in iteritems(entry):
if value:
try:
if isinstance(value, list):
if sorted(value) != sorted(item[key]):
if (entry, item) not in updates:
updates.append((entry, item))
elif value != item[key]:
if (entry, item) not in updates:
updates.append((entry, item))
except TypeError:
pass
return updates
def check_declarative_intent_params(want, module, result):
if module.params['associated_interfaces']:
if result['changed']:
time.sleep(module.params['delay'])
name = module.params['name']
rc, out, err = exec_command(module, 'show vrf | include {0}'.format(name))
if rc == 0:
data = out.strip().split()
# data will be empty if the vrf was just added
if not data:
return
vrf = data[0]
interface = data[-1]
for w in want:
if w['name'] == vrf:
if w.get('associated_interfaces') is None:
continue
for i in w['associated_interfaces']:
if get_interface_type(i) is not get_interface_type(interface):
module.fail_json(msg="Interface %s not configured on vrf %s" % (interface, name))
def main():
""" main entry point for module execution
"""
argument_spec = dict(
vrfs=dict(type='list'),
name=dict(),
description=dict(),
rd=dict(),
route_export=dict(type='list'),
route_import=dict(type='list'),
route_both=dict(type='list'),
route_export_ipv4=dict(type='list'),
route_import_ipv4=dict(type='list'),
route_both_ipv4=dict(type='list'),
route_export_ipv6=dict(type='list'),
route_import_ipv6=dict(type='list'),
route_both_ipv6=dict(type='list'),
interfaces=dict(type='list'),
associated_interfaces=dict(type='list'),
delay=dict(default=10, type='int'),
purge=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'absent'])
)
argument_spec.update(ios_argument_spec)
mutually_exclusive = [('name', 'vrfs')]
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
result = {'changed': False}
warnings = list()
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands(update_objects(want, have), module)
if module.params['purge']:
want_vrfs = [x['name'] for x in want]
have_vrfs = [x['name'] for x in have]
for item in set(have_vrfs).difference(want_vrfs):
cmd = 'no vrf definition %s' % item
if cmd not in commands:
commands.append(cmd)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
check_declarative_intent_params(want, module, result)
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,95 +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.plugins.action.network import ActionModule as ActionNetworkModule
from ansible.module_utils.network.common.utils import load_provider
from ansible.module_utils.network.ios.ios import ios_provider_spec
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 == 'ios_config' else False
persistent_connection = self._play_context.connection.split('.')[-1]
warnings = []
if persistent_connection == 'network_cli':
provider = self._task.args.get('provider', {})
if any(provider.values()):
display.warning('provider is unnecessary when using network_cli and will be ignored')
del self._task.args['provider']
elif self._play_context.connection == 'local':
provider = load_provider(ios_provider_spec, self._task.args)
pc = copy.deepcopy(self._play_context)
pc.connection = 'ansible.netcommon.network_cli'
pc.network_os = 'cisco.ios.ios'
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
pc.private_key_file = provider['ssh_keyfile'] or self._play_context.private_key_file
pc.become = provider['authorize'] or False
if pc.become:
pc.become_method = 'enable'
pc.become_pass = provider['auth_pass']
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.connection = 'network_cli'
pc.network_os = 'ios'
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])
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,387 +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: ios
short_description: Use ios cliconf to run command on Cisco IOS platform
description:
- This ios plugin provides low level abstraction apis for
sending and receiving CLI commands from Cisco IOS network devices.
version_added: "2.4"
"""
import re
import time
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.six import iteritems
from ansible.module_utils.network.common.config import NetworkConfig, dumps
from ansible.module_utils.network.common.utils import to_list
from ansible.plugins.cliconf import CliconfBase, enable_mode
class Cliconf(CliconfBase):
@enable_mode
def get_config(self, source='running', flags=None, format=None):
if source not in ('running', 'startup'):
raise ValueError("fetching configuration from %s is not supported" % source)
if format:
raise ValueError("'format' value %s is not supported for get_config" % format)
if not flags:
flags = []
if source == 'running':
cmd = 'show running-config '
else:
cmd = 'show startup-config '
cmd += ' '.join(to_list(flags))
cmd = cmd.strip()
return self.send_command(cmd)
def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace='line'):
"""
Generate diff between candidate and running configuration. If the
remote host supports onbox diff capabilities ie. supports_onbox_diff in that case
candidate and running configurations are not required to be passed as argument.
In case if onbox diff capability is not supported candidate argument is mandatory
and running argument is optional.
:param candidate: The configuration which is expected to be present on remote host.
:param running: The base configuration which is used to generate diff.
:param diff_match: Instructs how to match the candidate configuration with current device configuration
Valid values are 'line', 'strict', 'exact', 'none'.
'line' - commands are matched line by line
'strict' - command lines are matched with respect to position
'exact' - command lines must be an equal match
'none' - will not compare the candidate configuration with the running configuration
:param diff_ignore_lines: Use this argument to specify one or more lines that should be
ignored during the diff. This is used for lines in the configuration
that are automatically updated by the system. This argument takes
a list of regular expressions or exact line matches.
:param path: 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.
:param diff_replace: Instructs 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.
:return: Configuration diff in json format.
{
'config_diff': '',
'banner_diff': {}
}
"""
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
candidate_obj = NetworkConfig(indent=1)
want_src, want_banners = self._extract_banners(candidate)
candidate_obj.load(want_src)
if running and diff_match != 'none':
# running configuration
have_src, have_banners = self._extract_banners(running)
running_obj = NetworkConfig(indent=1, contents=have_src, ignore_lines=diff_ignore_lines)
configdiffobjs = candidate_obj.difference(running_obj, path=path, match=diff_match, replace=diff_replace)
else:
configdiffobjs = candidate_obj.items
have_banners = {}
diff['config_diff'] = dumps(configdiffobjs, 'commands') if configdiffobjs else ''
banners = self._diff_banners(want_banners, have_banners)
diff['banner_diff'] = banners if banners else {}
return diff
@enable_mode
def edit_config(self, candidate=None, commit=True, replace=None, comment=None):
resp = {}
operations = self.get_device_operations()
self.check_edit_config_capability(operations, candidate, commit, replace, comment)
results = []
requests = []
if commit:
self.send_command('configure terminal')
for line in to_list(candidate):
if not isinstance(line, Mapping):
line = {'command': line}
cmd = line['command']
if cmd != 'end' and cmd[0] != '!':
results.append(self.send_command(**line))
requests.append(cmd)
self.send_command('end')
else:
raise ValueError('check mode is not supported')
resp['request'] = requests
resp['response'] = results
return resp
def edit_macro(self, candidate=None, commit=True, replace=None, comment=None):
"""
ios_config:
lines: "{{ macro_lines }}"
parents: "macro name {{ macro_name }}"
after: '@'
match: line
replace: block
"""
resp = {}
operations = self.get_device_operations()
self.check_edit_config_capability(operations, candidate, commit, replace, comment)
results = []
requests = []
if commit:
commands = ''
self.send_command('config terminal')
time.sleep(0.1)
# first item: macro command
commands += (candidate.pop(0) + '\n')
multiline_delimiter = candidate.pop(-1)
for line in candidate:
commands += (' ' + line + '\n')
commands += (multiline_delimiter + '\n')
obj = {'command': commands, 'sendonly': True}
results.append(self.send_command(**obj))
requests.append(commands)
time.sleep(0.1)
self.send_command('end', sendonly=True)
time.sleep(0.1)
results.append(self.send_command('\n'))
requests.append('\n')
resp['request'] = requests
resp['response'] = results
return resp
def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None, newline=True, check_all=False):
if not command:
raise ValueError('must provide value of command to execute')
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 get_device_info(self):
device_info = {}
device_info['network_os'] = 'ios'
reply = self.get(command='show version')
data = to_text(reply, errors='surrogate_or_strict').strip()
match = re.search(r'Version (\S+)', data)
if match:
device_info['network_os_version'] = match.group(1).strip(',')
model_search_strs = [r'^[Cc]isco (.+) \(revision', r'^[Cc]isco (\S+).+bytes of .*memory']
for item in model_search_strs:
match = re.search(item, data, re.M)
if match:
version = match.group(1).split(' ')
device_info['network_os_model'] = version[0]
break
match = re.search(r'^(.+) uptime', data, re.M)
if match:
device_info['network_os_hostname'] = match.group(1)
match = re.search(r'image file is "(.+)"', data)
if match:
device_info['network_os_image'] = match.group(1)
return device_info
def get_device_operations(self):
return {
'supports_diff_replace': True,
'supports_commit': False,
'supports_rollback': False,
'supports_defaults': True,
'supports_onbox_diff': False,
'supports_commit_comment': False,
'supports_multiline_delimiter': True,
'supports_diff_match': True,
'supports_diff_ignore_lines': True,
'supports_generate_diff': True,
'supports_replace': False
}
def get_option_values(self):
return {
'format': ['text'],
'diff_match': ['line', 'strict', 'exact', 'none'],
'diff_replace': ['line', 'block'],
'output': []
}
def get_capabilities(self):
result = super(Cliconf, self).get_capabilities()
result['rpc'] += ['edit_banner', 'get_diff', 'run_commands', 'get_defaults_flag']
result['device_operations'] = self.get_device_operations()
result.update(self.get_option_values())
return json.dumps(result)
def edit_banner(self, candidate=None, multiline_delimiter="@", commit=True):
"""
Edit banner on remote device
:param banners: Banners to be loaded in json format
:param multiline_delimiter: Line delimiter for banner
:param commit: Boolean value that indicates if the device candidate
configuration should be pushed in the running configuration or discarded.
:param diff: Boolean flag to indicate if configuration that is applied on remote host should
generated and returned in response or not
:return: Returns response of executing the configuration command received
from remote host
"""
resp = {}
banners_obj = json.loads(candidate)
results = []
requests = []
if commit:
for key, value in iteritems(banners_obj):
key += ' %s' % multiline_delimiter
self.send_command('config terminal', sendonly=True)
for cmd in [key, value, multiline_delimiter]:
obj = {'command': cmd, 'sendonly': True}
results.append(self.send_command(**obj))
requests.append(cmd)
self.send_command('end', sendonly=True)
time.sleep(0.1)
results.append(self.send_command('\n'))
requests.append('\n')
resp['request'] = requests
resp['response'] = results
return resp
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', to_text(e))
responses.append(out)
return responses
def get_defaults_flag(self):
"""
The method identifies the filter that should be used to fetch running-configuration
with defaults.
:return: valid default filter
"""
out = self.get('show running-config ?')
out = to_text(out, errors='surrogate_then_replace')
commands = set()
for line in out.splitlines():
if line.strip():
commands.add(line.strip().split()[0])
if 'all' in commands:
return 'all'
else:
return 'full'
def set_cli_prompt_context(self):
"""
Make sure we are in the operational cli mode
:return: None
"""
if self._connection.connected:
out = self._connection.get_prompt()
if out is None:
raise AnsibleConnectionFailure(message=u'cli prompt is not identified from the last received'
u' response window: %s' % self._connection._last_recv_window)
if re.search(r'config.*\)#', to_text(out, errors='surrogate_then_replace').strip()):
self._connection.queue_message('vvvv', 'wrong context, sending end to device')
self._connection.send_command('end')
def _extract_banners(self, config):
banners = {}
banner_cmds = re.findall(r'^banner (\w+)', config, re.M)
for cmd in banner_cmds:
regex = r'banner %s \^C(.+?)(?=\^C)' % cmd
match = re.search(regex, config, re.S)
if match:
key = 'banner %s' % cmd
banners[key] = match.group(1).strip()
for cmd in banner_cmds:
regex = r'banner %s \^C(.+?)(?=\^C)' % cmd
match = re.search(regex, config, re.S)
if match:
config = config.replace(str(match.group(1)), '')
config = re.sub(r'banner \w+ \^C\^C', '!! banner removed', config)
return config, banners
def _diff_banners(self, want, have):
candidate = {}
for key, value in iteritems(want):
if value != have.get(key):
candidate[key] = value
return candidate

@ -1,81 +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(IOS Platform Options guide, ../network/user_guide/platform_ios.html).
- 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
authorize:
description:
- Instructs the module to enter privileged mode on the remote device
before sending any commands. If not specified, the device will
attempt to execute all commands in non-privileged mode. If the value
is not specified in the task, the value of environment variable
C(ANSIBLE_NET_AUTHORIZE) will be used instead.
type: bool
default: no
auth_pass:
description:
- Specifies the password to use if required to enter privileged mode
on the remote device. If I(authorize) is false, then this argument
does nothing. If the value is not specified in the task, the value of
environment variable C(ANSIBLE_NET_AUTH_PASS) will be used instead.
type: str
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,102 +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 json
import re
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_text, to_bytes
from ansible.plugins.terminal import TerminalBase
from ansible.utils.display import Display
display = Display()
class TerminalModule(TerminalBase):
terminal_stdout_re = [
re.compile(br"[\r\n]?[\w\+\-\.:\/\[\]]+(?:\([^\)]+\)){0,3}(?:[>#]) ?$")
]
terminal_stderr_re = [
re.compile(br"% ?Error"),
# re.compile(br"^% \w+", re.M),
re.compile(br"% ?Bad secret"),
re.compile(br"[\r\n%] Bad passwords"),
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.compile(br"'[^']' +returned error code: ?\d+"),
re.compile(br"Bad mask", re.I),
re.compile(br"% ?(\S+) ?overlaps with ?(\S+)", re.I),
re.compile(br"[%\S] ?Error: ?[\s]+", re.I),
re.compile(br"[%\S] ?Informational: ?[\s]+", re.I),
re.compile(br"Command authorization failed")
]
def on_open_shell(self):
try:
self._exec_cli_command(b'terminal length 0')
except AnsibleConnectionFailure:
raise AnsibleConnectionFailure('unable to set terminal parameters')
try:
self._exec_cli_command(b'terminal width 512')
try:
self._exec_cli_command(b'terminal width 0')
except AnsibleConnectionFailure:
pass
except AnsibleConnectionFailure:
display.display('WARNING: Unable to set terminal width, command responses may be truncated')
def on_become(self, passwd=None):
if self._get_prompt().endswith(b'#'):
return
cmd = {u'command': u'enable'}
if passwd:
# Note: python-3.5 cannot combine u"" and r"" together. Thus make
# an r string and use to_text to ensure it's text on both py2 and py3.
cmd[u'prompt'] = to_text(r"[\r\n]?(?:.*)?[Pp]assword: ?$", errors='surrogate_or_strict')
cmd[u'answer'] = passwd
cmd[u'prompt_retry_check'] = True
try:
self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict'))
prompt = self._get_prompt()
if prompt is None or not prompt.endswith(b'#'):
raise AnsibleConnectionFailure('failed to elevate privilege to enable mode still at prompt [%s]' % prompt)
except AnsibleConnectionFailure as e:
prompt = self._get_prompt()
raise AnsibleConnectionFailure('unable to elevate privilege to enable mode, at prompt [%s] with error: %s' % (prompt, e.message))
def on_unbecome(self):
prompt = self._get_prompt()
if prompt is None:
# if prompt is None most likely the terminal is hung up at a prompt
return
if b'(config' in prompt:
self._exec_cli_command(b'end')
self._exec_cli_command(b'disable')
elif prompt.endswith(b'#'):
self._exec_cli_command(b'disable')

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

@ -1,21 +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
tags: connection_network_cli

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

@ -1,8 +0,0 @@
interface GigabitEthernet0/1
ip access-group 110 in
ip access-group 123 out
ipv6 traffic-filter temp_v6 in
ipv6 traffic-filter test_v6 out
interface GigabitEthernet0/2
ip access-group 110 in
ip access-group 123 out

@ -1,14 +0,0 @@
---
- name: Populate Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
interface GigabitEthernet 0/1
ip access-group 110 in
ip access-group 123 out
ipv6 traffic-filter temp_v6 in
ipv6 traffic-filter test_v6 out
interface GigabitEthernet 0/2
ip access-group 110 in
ip access-group 123 out

@ -1,15 +0,0 @@
---
- name: Remove Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
interface GigabitEthernet 0/1
no ip access-group 110 in
no ip access-group 100 out
no ip access-group 123 out
no ipv6 traffic-filter temp_v6 in
no ipv6 traffic-filter test_v6 out
interface GigabitEthernet 0/2
no ip access-group 110 in
no ip access-group 123 out

@ -1,65 +0,0 @@
---
- debug:
msg: "Start ios_acl_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Delete module attributes of given Interface based on AFI
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv6
state: deleted
register: result
- assert:
that:
- "result.commands|length == 3"
- "result.changed == true"
- "'no ipv6 traffic-filter temp_v6 in' in result.commands"
- "'no ipv6 traffic-filter test_v6 out' in result.commands"
- name: Delete module attributes of given Interface based on AFI (IDEMPOTENT)
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv6
state: deleted
register: result
- assert:
that:
- "result.changed == false"
- name: Delete module attributes of given Interface.
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
state: deleted
register: result
- assert:
that:
- "result.commands|length == 3"
- "result.changed == true"
- "'no ip access-group 110 in' in result.commands"
- "'no ip access-group 123 out' in result.commands"
- name: Delete module attributes of given Interface (IDEMPOTENT)
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
state: deleted
register: result
- assert:
that:
- "result.changed == false"
always:
- include_tasks: _remove_config.yaml

@ -1,58 +0,0 @@
---
- debug:
msg: "START ios_acl_interfaces empty_config.yaml integration tests on connection={{ ansible_connection }}"
- name: Merged with empty config should give appropriate error message
ios_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
ios_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
ios_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
ios_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
ios_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,21 +0,0 @@
---
- debug:
msg: "START ios_acl_interfaces gathered integration tests on connection={{ ansible_connection }}"
- include_tasks: _populate_config.yaml
- block:
- name: Gather the provided configuration with the exisiting running configuration
ios_acl_interfaces: &gathered
config:
state: gathered
register: result
- name: Assert
assert:
that:
- "gathered['config'] | symmetric_difference(result.gathered) == []"
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml

@ -1,53 +0,0 @@
---
- debug:
msg: "Start ios_acl_interfaces merged integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
ios_acl_interfaces: &merged
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
- afi: ipv6
acls:
- name: temp_v6
direction: in
- name: test_v6
direction: out
- name: GigabitEthernet0/2
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
state: merged
register: result
- assert:
that:
- "result.commands|length == 8"
- "result.changed == true"
- "result.commands|symmetric_difference(merged.commands) == []"
- name: Merge the provided configuration with the exisiting running configuration (IDEMPOTENT)
ios_acl_interfaces: *merged
register: result
- assert:
that:
- "result.commands|length == 0"
- "result.changed == false"
always:
- include_tasks: _remove_config.yaml

@ -1,41 +0,0 @@
---
- debug:
msg: "Start ios_acl_interfaces overridden integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate_config.yaml
- block:
- name: Override device configuration of all acl_interfaces with provided configuration
ios_acl_interfaces: &overridden
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 100
direction: out
- name: 110
direction: in
state: overridden
become: yes
register: result
- assert:
that:
- "result.commands|length == 8"
- "result.changed == true"
- "result.commands|symmetric_difference(overridden.commands) == []"
- name: Override device configuration of all acl_interfaces with provided configuration (IDEMPOTENT)
ios_acl_interfaces: *overridden
become: yes
register: result
- assert:
that:
- "result.commands|length == 0"
- "result.changed == false"
always:
- include_tasks: _remove_config.yaml

@ -1,16 +0,0 @@
---
- debug:
msg: "START ios_acl_interfaces parsed integration tests on connection={{ ansible_connection }}"
- name: Parse the commands for provided configuration
ios_acl_interfaces: &parsed
running_config:
"{{ lookup('file', '_parsed.cfg') }}"
state: parsed
become: yes
register: result
- assert:
that:
- "result.changed == false"
- "parsed['config']|symmetric_difference(result.parsed) == []"

@ -1,39 +0,0 @@
---
- debug:
msg: "Start ios_acl_interfaces rendered integration tests ansible_connection={{ ansible_connection }}"
- block:
- name: Render the commands for provided configuration
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
- afi: ipv6
acls:
- name: test_v6
direction: out
- name: temp_v6
direction: in
- name: GigabitEthernet0/2
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
state: rendered
become: yes
register: result
- assert:
that:
- "result.changed == false"
- "result.rendered|symmetric_difference(merged.commands) == []"

@ -1,41 +0,0 @@
---
- debug:
msg: "Start ios_acl_interfaces replced integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate_config.yaml
- block:
- name: Replaces device configuration of listed acl_interfaces with provided configuration
ios_acl_interfaces: &replaced
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 100
direction: out
- name: 110
direction: in
state: replaced
become: yes
register: result
- assert:
that:
- "result.commands|length == 5"
- "result.changed == true"
- "result.commands|symmetric_difference(replaced.commands) == []"
- name: Replaces device configuration of listed acl_interfaces with provided configuration (IDEMPOTENT)
ios_acl_interfaces: *replaced
become: yes
register: result
- assert:
that:
- "result.commands|length == 0"
- "result.changed == false"
always:
- include_tasks: _remove_config.yaml

@ -1,78 +0,0 @@
---
- debug:
msg: "START ios_acl_interfaces round trip integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Apply the provided configuration (Base config)
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
- afi: ipv6
acls:
- name: temp_v6
direction: in
- name: test_v6
direction: out
- name: GigabitEthernet0/2
access_groups:
- afi: ipv4
acls:
- name: 110
direction: in
- name: 123
direction: out
state: merged
register: base_config
- name: Gather acl interfaces facts
ios_facts:
gather_subset:
- "!all"
- "!min"
gather_network_resources:
- acl_interfaces
- name: Apply the provided configuration (config to be reverted)
ios_acl_interfaces:
config:
- name: GigabitEthernet0/1
access_groups:
- afi: ipv4
acls:
- name: 100
direction: out
- name: 110
direction: in
state: overridden
register: result
- assert:
that:
- "result.commands|length == 8"
- "result.changed == true"
- "result.commands|symmetric_difference(overridden.commands) == []"
- name: Revert back to base config using facts round trip
ios_acl_interfaces:
config: "{{ ansible_facts['network_resources']['acl_interfaces'] }}"
state: overridden
register: revert
- assert:
that:
- "revert.commands|length == 8"
- "revert.changed == true"
- "revert.commands|symmetric_difference(rtt.commands) == []"
always:
- include_tasks: _remove_config.yaml

@ -1,99 +0,0 @@
---
interfaces:
int1:
GigabitEthernet0/1
int2:
GigabitEthernet0/2
merged:
commands:
- interface GigabitEthernet0/1
- ip access-group 110 in
- ip access-group 123 out
- ipv6 traffic-filter temp_v6 in
- ipv6 traffic-filter test_v6 out
- interface GigabitEthernet0/2
- ip access-group 110 in
- ip access-group 123 out
replaced:
commands:
- interface GigabitEthernet0/1
- no ip access-group 123 out
- no ipv6 traffic-filter temp_v6 in
- no ipv6 traffic-filter test_v6 out
- ip access-group 100 out
overridden:
commands:
- interface GigabitEthernet0/1
- no ip access-group 123 out
- no ipv6 traffic-filter test_v6 out
- no ipv6 traffic-filter temp_v6 in
- ip access-group 100 out
- interface GigabitEthernet0/2
- no ip access-group 110 in
- no ip access-group 123 out
gathered:
config:
- name: GigabitEthernet0/0
- access_groups:
- acls:
- direction: 'in'
name: '110'
- direction: 'out'
name: '123'
afi: 'ipv4'
- acls:
- direction: 'in'
name: 'temp_v6'
- direction: 'out'
name: 'test_v6'
afi: 'ipv6'
name: GigabitEthernet0/1
- access_groups:
- acls:
- direction: 'in'
name: '110'
- direction: 'out'
name: '123'
afi: ipv4
name: GigabitEthernet0/2
parsed:
config:
- access_groups:
- acls:
- direction: in
name: '110'
- direction: out
name: '123'
afi: ipv4
- acls:
- direction: in
name: temp_v6
- direction: out
name: test_v6
afi: ipv6
name: GigabitEthernet0/1
- access_groups:
- acls:
- direction: in
name: '110'
- direction: out
name: '123'
afi: ipv4
name: GigabitEthernet0/2
rtt:
commands:
- interface GigabitEthernet0/1
- no ip access-group 100 out
- ip access-group 123 out
- ipv6 traffic-filter temp_v6 in
- ipv6 traffic-filter test_v6 out
- interface GigabitEthernet0/2
- ip access-group 110 in
- ip access-group 123 out

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

@ -1,21 +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
tags: connection_network_cli

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

Loading…
Cancel
Save