mirror of https://github.com/ansible/ansible.git
Migrated to vyos.vyos
parent
d101834d28
commit
cc2feea51b
@ -1,21 +0,0 @@
|
|||||||
# 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 vyos facts module.
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class FactsArgs(object): # pylint: disable=R0903
|
|
||||||
""" The arg spec for the vyos facts module
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'gather_subset': dict(default=['!config'], type='list'),
|
|
||||||
'gather_network_resources': dict(type='list'),
|
|
||||||
}
|
|
@ -1,197 +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 vyos_firewall_global module
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_globalArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_firewall_global module
|
|
||||||
"""
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'config': {
|
|
||||||
'options': {
|
|
||||||
'config_trap': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'group': {
|
|
||||||
'options': {
|
|
||||||
'address_group': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'description': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'members': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'network_group': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'description': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'members': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'port_group': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'description': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'members': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'port': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'log_martians': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'ping': {
|
|
||||||
'options': {
|
|
||||||
'all': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'broadcast': {
|
|
||||||
'type': 'bool'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'route_redirects': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'afi': {
|
|
||||||
'choices': ['ipv4', 'ipv6'],
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'icmp_redirects': {
|
|
||||||
'options': {
|
|
||||||
'receive': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'send': {
|
|
||||||
'type': 'bool'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'ip_src_route': {
|
|
||||||
'type': 'bool'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'state_policy': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'action': {
|
|
||||||
'choices': ['accept', 'drop', 'reject'],
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'connection_type': {
|
|
||||||
'choices': ['established', 'invalid', 'related'],
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'log': {
|
|
||||||
'type': 'bool'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'syn_cookies': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'twa_hazards_protection': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'validation': {
|
|
||||||
'choices': ['strict', 'loose', 'disable'],
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'running_config': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'state': {
|
|
||||||
'choices': [
|
|
||||||
'merged', 'replaced', 'deleted', 'gathered', 'rendered',
|
|
||||||
'parsed'
|
|
||||||
],
|
|
||||||
'default':
|
|
||||||
'merged',
|
|
||||||
'type':
|
|
||||||
'str'
|
|
||||||
}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -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 vyos_firewall_interfaces module
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_interfacesArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_firewall_interfaces module
|
|
||||||
"""
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'config': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'access_rules': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'afi': {
|
|
||||||
'choices': ['ipv4', 'ipv6'],
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'rules': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'direction': {
|
|
||||||
'choices': ['in', 'local', 'out'],
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'running_config': {'type': 'str'},
|
|
||||||
'state': {
|
|
||||||
'choices': [
|
|
||||||
'merged', 'replaced', 'overridden', 'deleted', 'parsed',
|
|
||||||
'rendered', 'gathered'
|
|
||||||
],
|
|
||||||
'default':
|
|
||||||
'merged',
|
|
||||||
'type':
|
|
||||||
'str'
|
|
||||||
}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,318 +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 vyos_firewall_rules module
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_rulesArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_firewall_rules module
|
|
||||||
"""
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'config': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'afi': {
|
|
||||||
'choices': ['ipv4', 'ipv6'],
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'rule_sets': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'default_action': {
|
|
||||||
'choices': ['drop', 'reject', 'accept'],
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'description': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'enable_default_log': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'rules': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'action': {
|
|
||||||
'choices':
|
|
||||||
['drop', 'reject', 'accept', 'inspect'],
|
|
||||||
'type':
|
|
||||||
'str'
|
|
||||||
},
|
|
||||||
'description': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'destination': {
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'group': {
|
|
||||||
'options': {
|
|
||||||
'address_group': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'network_group': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'port_group': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'port': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'disabled': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'fragment': {
|
|
||||||
'choices':
|
|
||||||
['match-frag', 'match-non-frag'],
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'icmp': {
|
|
||||||
'options': {
|
|
||||||
'code': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'type': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'type_name': {
|
|
||||||
'choices': [
|
|
||||||
'any', 'echo-reply',
|
|
||||||
'destination-unreachable',
|
|
||||||
'network-unreachable',
|
|
||||||
'host-unreachable',
|
|
||||||
'protocol-unreachable',
|
|
||||||
'port-unreachable',
|
|
||||||
'fragmentation-needed',
|
|
||||||
'source-route-failed',
|
|
||||||
'network-unknown',
|
|
||||||
'host-unknown',
|
|
||||||
'network-prohibited',
|
|
||||||
'host-prohibited',
|
|
||||||
'TOS-network-unreachable',
|
|
||||||
'TOS-host-unreachable',
|
|
||||||
'communication-prohibited',
|
|
||||||
'host-precedence-violation',
|
|
||||||
'precedence-cutoff',
|
|
||||||
'source-quench', 'redirect',
|
|
||||||
'network-redirect',
|
|
||||||
'host-redirect',
|
|
||||||
'TOS-network-redirect',
|
|
||||||
'TOS-host-redirect',
|
|
||||||
'echo-request',
|
|
||||||
'router-advertisement',
|
|
||||||
'router-solicitation',
|
|
||||||
'time-exceeded',
|
|
||||||
'ttl-zero-during-transit',
|
|
||||||
'ttl-zero-during-reassembly',
|
|
||||||
'parameter-problem',
|
|
||||||
'ip-header-bad',
|
|
||||||
'required-option-missing',
|
|
||||||
'timestamp-request',
|
|
||||||
'timestamp-reply',
|
|
||||||
'address-mask-request',
|
|
||||||
'address-mask-reply', 'ping',
|
|
||||||
'pong', 'ttl-exceeded'
|
|
||||||
],
|
|
||||||
'type':
|
|
||||||
'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'ipsec': {
|
|
||||||
'choices': ['match-ipsec', 'match-none'],
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'limit': {
|
|
||||||
'options': {
|
|
||||||
'burst': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'rate': {
|
|
||||||
'options': {
|
|
||||||
'number': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'unit': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'number': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'p2p': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'application': {
|
|
||||||
'choices': [
|
|
||||||
'all', 'applejuice',
|
|
||||||
'bittorrent', 'directconnect',
|
|
||||||
'edonkey', 'gnutella', 'kazaa'
|
|
||||||
],
|
|
||||||
'type':
|
|
||||||
'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'protocol': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'recent': {
|
|
||||||
'options': {
|
|
||||||
'count': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'time': {
|
|
||||||
'type': 'int'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'source': {
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'group': {
|
|
||||||
'options': {
|
|
||||||
'address_group': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'network_group': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'port_group': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'mac_address': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'port': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'state': {
|
|
||||||
'options': {
|
|
||||||
'established': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'invalid': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'new': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'related': {
|
|
||||||
'type': 'bool'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'tcp': {
|
|
||||||
'options': {
|
|
||||||
'flags': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'time': {
|
|
||||||
'options': {
|
|
||||||
'monthdays': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'startdate': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'starttime': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'stopdate': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'stoptime': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'utc': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'weekdays': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'running_config': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'state': {
|
|
||||||
'choices': [
|
|
||||||
'merged', 'replaced', 'overridden', 'deleted', 'gathered',
|
|
||||||
'rendered', 'parsed'
|
|
||||||
],
|
|
||||||
'default':
|
|
||||||
'merged',
|
|
||||||
'type':
|
|
||||||
'str'
|
|
||||||
}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,67 +0,0 @@
|
|||||||
# 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 vyos_interfaces module
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class InterfacesArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_interfaces module
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = \
|
|
||||||
{
|
|
||||||
'config': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'description': {'type': 'str'},
|
|
||||||
'duplex': {'choices': ['full', 'half', 'auto']},
|
|
||||||
'enabled': {'default': True, 'type': 'bool'},
|
|
||||||
'mtu': {'type': 'int'},
|
|
||||||
'name': {'required': True, 'type': 'str'},
|
|
||||||
'speed': {'choices': ['auto', '10', '100', '1000', '2500',
|
|
||||||
'10000'],
|
|
||||||
'type': 'str'},
|
|
||||||
'vifs': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'vlan_id': {'type': 'int'},
|
|
||||||
'description': {'type': 'str'},
|
|
||||||
'enabled': {'default': True, 'type': 'bool'},
|
|
||||||
'mtu': {'type': 'int'}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'state': {'choices': ['merged', 'replaced',
|
|
||||||
'overridden', 'deleted'],
|
|
||||||
'default': 'merged',
|
|
||||||
'type': 'str'}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,101 +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 vyos_l3_interfaces module
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class L3_interfacesArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_l3_interfaces module
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'config': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'ipv4': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'ipv6': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'vifs': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'ipv4': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'ipv6': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'vlan_id': {
|
|
||||||
'type': 'int'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'state': {
|
|
||||||
'choices': ['merged', 'replaced', 'overridden', 'deleted'],
|
|
||||||
'default': 'merged',
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,68 +0,0 @@
|
|||||||
# 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 vyos_lag_interfaces module
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Lag_interfacesArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_lag_interfaces module
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = \
|
|
||||||
{
|
|
||||||
'config': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'arp_monitor': {
|
|
||||||
'options': {
|
|
||||||
'interval': {'type': 'int'},
|
|
||||||
'target': {'type': 'list'}
|
|
||||||
},
|
|
||||||
'type': 'dict'},
|
|
||||||
'hash_policy': {'choices': ['layer2', 'layer2+3', 'layer3+4'],
|
|
||||||
'type': 'str'},
|
|
||||||
'members': {'elements': 'dict', 'options': {
|
|
||||||
'member': {'type': 'str'}}, 'type': 'list'},
|
|
||||||
'mode': {'choices': ['802.3ad',
|
|
||||||
'active-backup',
|
|
||||||
'broadcast',
|
|
||||||
'round-robin',
|
|
||||||
'transmit-load-balance',
|
|
||||||
'adaptive-load-balance',
|
|
||||||
'xor-hash'],
|
|
||||||
'type': 'str'},
|
|
||||||
'name': {'required': True, 'type': 'str'},
|
|
||||||
'primary': {'type': 'str'}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
|
|
||||||
'default': 'merged',
|
|
||||||
'type': 'str'}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,61 +0,0 @@
|
|||||||
# 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 vyos_lldp_global module
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Lldp_globalArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_lldp_global module
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'config': {
|
|
||||||
'options': {
|
|
||||||
'address': {
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'enable': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'legacy_protocols': {
|
|
||||||
'choices': ['cdp', 'edp', 'fdp', 'sonmp'],
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'snmp': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'state': {
|
|
||||||
'choices': ['merged', 'replaced', 'deleted'],
|
|
||||||
'default': 'merged',
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,107 +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 vyos_lldp_interfaces module
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Lldp_interfacesArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_lldp_interfaces module
|
|
||||||
"""
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'config': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'enable': {
|
|
||||||
'default': True,
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'location': {
|
|
||||||
'options': {
|
|
||||||
'civic_based': {
|
|
||||||
'options': {
|
|
||||||
'ca_info': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'ca_type': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'ca_value': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'country_code': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'coordinate_based': {
|
|
||||||
'options': {
|
|
||||||
'altitude': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'datum': {
|
|
||||||
'choices': ['WGS84', 'NAD83', 'MLLW'],
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'latitude': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'longitude': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'elin': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'state': {
|
|
||||||
'choices': ['merged', 'replaced', 'overridden', 'deleted'],
|
|
||||||
'default': 'merged',
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,107 +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 vyos_static_routes module
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
class Static_routesArgs(object): # pylint: disable=R0903
|
|
||||||
"""The arg spec for the vyos_static_routes module
|
|
||||||
"""
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
argument_spec = {
|
|
||||||
'config': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'address_families': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'afi': {
|
|
||||||
'choices': ['ipv4', 'ipv6'],
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'routes': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'blackhole_config': {
|
|
||||||
'options': {
|
|
||||||
'distance': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'type': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'dict'
|
|
||||||
},
|
|
||||||
'dest': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'next_hops': {
|
|
||||||
'elements': 'dict',
|
|
||||||
'options': {
|
|
||||||
'admin_distance': {
|
|
||||||
'type': 'int'
|
|
||||||
},
|
|
||||||
'enabled': {
|
|
||||||
'type': 'bool'
|
|
||||||
},
|
|
||||||
'forward_router_address': {
|
|
||||||
'required': True,
|
|
||||||
'type': 'str'
|
|
||||||
},
|
|
||||||
'interface': {
|
|
||||||
'type': 'str'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'type': 'list'
|
|
||||||
},
|
|
||||||
'running_config': {'type': 'str'},
|
|
||||||
'state': {
|
|
||||||
'choices': [
|
|
||||||
'merged', 'replaced', 'overridden', 'deleted', 'gathered',
|
|
||||||
'rendered', 'parsed'
|
|
||||||
],
|
|
||||||
'default':
|
|
||||||
'merged',
|
|
||||||
'type':
|
|
||||||
'str'
|
|
||||||
}
|
|
||||||
} # pylint: disable=C0301
|
|
@ -1,611 +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 vyos_firewall_global class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, remove_empties
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network.vyos.utils.utils import list_diff_want_only
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_global(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_firewall_global class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'firewall_global',
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
super(Firewall_global, self).__init__(module)
|
|
||||||
|
|
||||||
def get_firewall_global_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)
|
|
||||||
firewall_global_facts = facts['ansible_network_resources'].get('firewall_global')
|
|
||||||
if not firewall_global_facts:
|
|
||||||
return []
|
|
||||||
return firewall_global_facts
|
|
||||||
|
|
||||||
def execute_module(self):
|
|
||||||
""" Execute the module
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The result from module execution
|
|
||||||
"""
|
|
||||||
result = {'changed': False}
|
|
||||||
warnings = list()
|
|
||||||
commands = list()
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
existing_firewall_global_facts = self.get_firewall_global_facts()
|
|
||||||
else:
|
|
||||||
existing_firewall_global_facts = []
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES or self.state == 'rendered':
|
|
||||||
commands.extend(self.set_config(existing_firewall_global_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_firewall_global_facts = self.get_firewall_global_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_firewall_global_facts(data=running_config)
|
|
||||||
else:
|
|
||||||
changed_firewall_global_facts = []
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
result['before'] = existing_firewall_global_facts
|
|
||||||
if result['changed']:
|
|
||||||
result['after'] = changed_firewall_global_facts
|
|
||||||
elif self.state == 'gathered':
|
|
||||||
result['gathered'] = changed_firewall_global_facts
|
|
||||||
|
|
||||||
result['warnings'] = warnings
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_config(self, existing_firewall_global_facts):
|
|
||||||
""" Collect the configuration from the args passed to the module,
|
|
||||||
collect the current configuration (as a dict from facts)
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
want = self._module.params['config']
|
|
||||||
have = existing_firewall_global_facts
|
|
||||||
resp = self.set_state(want, have)
|
|
||||||
return to_list(resp)
|
|
||||||
|
|
||||||
def set_state(self, w, h):
|
|
||||||
""" 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 = []
|
|
||||||
if self.state in ('merged', 'replaced', 'rendered') and not w:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(self.state))
|
|
||||||
if self.state == 'deleted':
|
|
||||||
commands.extend(self._state_deleted(want=None, have=h))
|
|
||||||
elif w:
|
|
||||||
if self.state == 'merged' or self.state == 'rendered':
|
|
||||||
commands.extend(self._state_merged(w, h))
|
|
||||||
elif self.state == 'replaced':
|
|
||||||
commands.extend(self._state_replaced(w, h))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._state_deleted(have, want))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
commands.extend(self._add_global_attr(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 = []
|
|
||||||
b_set = ('config_trap',
|
|
||||||
'validation',
|
|
||||||
'log_martians',
|
|
||||||
'syn_cookies',
|
|
||||||
'twa_hazards_protection')
|
|
||||||
if want:
|
|
||||||
for key, val in iteritems(want):
|
|
||||||
if val and key in b_set and not have:
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, opr=False))
|
|
||||||
elif val and key in b_set and have and key in have and have[key] != val:
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, opr=False))
|
|
||||||
else:
|
|
||||||
commands.extend(self._render_attr_config(want, have, key))
|
|
||||||
elif not want and have:
|
|
||||||
commands.append(self._compute_command(opr=False))
|
|
||||||
elif have:
|
|
||||||
for key, val in iteritems(have):
|
|
||||||
if val and key in b_set:
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, opr=False))
|
|
||||||
else:
|
|
||||||
commands.extend(self._render_attr_config(want, have, key))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_attr_config(self, w, h, key, opr=False):
|
|
||||||
"""
|
|
||||||
This function invoke the function to extend commands
|
|
||||||
based on the key.
|
|
||||||
:param w: the desired configuration.
|
|
||||||
:param h: the current configuration.
|
|
||||||
:param key: attribute name
|
|
||||||
:param opr: operation
|
|
||||||
:return: list of commands
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if key == 'ping':
|
|
||||||
commands.extend(self._render_ping(key, w, h, opr=opr))
|
|
||||||
elif key == 'group':
|
|
||||||
commands.extend(self._render_group(key, w, h, opr=opr))
|
|
||||||
elif key == 'state_policy':
|
|
||||||
commands.extend(self._render_state_policy(key, w, h, opr=opr))
|
|
||||||
elif key == 'route_redirects':
|
|
||||||
commands.extend(self._render_route_redirects(key, w, h, opr=opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_global_attr(self, w, h, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the set/delete commands based on the 'opr' type
|
|
||||||
for firewall_global attributes.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
w_fg = deepcopy(remove_empties(w))
|
|
||||||
l_set = ('config_trap',
|
|
||||||
'validation',
|
|
||||||
'log_martians',
|
|
||||||
'syn_cookies',
|
|
||||||
'twa_hazards_protection')
|
|
||||||
if w_fg:
|
|
||||||
for key, val in iteritems(w_fg):
|
|
||||||
if opr and key in l_set and not (h and self._is_w_same(w_fg, h, key)):
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr))
|
|
||||||
elif not opr:
|
|
||||||
if key and self._is_del(l_set, h):
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, key=self._bool_to_str(val), opr=opr))
|
|
||||||
continue
|
|
||||||
elif key in l_set and not (h and self._in_target(h, key)) and not self._is_del(l_set, h):
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr))
|
|
||||||
else:
|
|
||||||
commands.extend(self._render_attr_config(w_fg, h, key, opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_ping(self, attr, w, h, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'ping' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: the desired configuration.
|
|
||||||
:param h: the target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_ping = {}
|
|
||||||
l_set = ('all', 'broadcast')
|
|
||||||
if h:
|
|
||||||
h_ping = h.get(attr) or {}
|
|
||||||
if self._is_root_del(w[attr], h_ping, attr):
|
|
||||||
for item, value in iteritems(h[attr]):
|
|
||||||
if not opr and item in l_set:
|
|
||||||
commands.append(self._form_attr_cmd(attr=item, opr=opr))
|
|
||||||
elif w[attr]:
|
|
||||||
if h and attr in h.keys():
|
|
||||||
h_ping = h.get(attr) or {}
|
|
||||||
for item, value in iteritems(w[attr]):
|
|
||||||
if opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)):
|
|
||||||
commands.append(self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr))
|
|
||||||
elif not opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)):
|
|
||||||
commands.append(self._form_attr_cmd(attr=item, opr=opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_group(self, attr, w, h, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'group' attribute based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_grp = {}
|
|
||||||
if not opr and self._is_root_del(h, w, attr):
|
|
||||||
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
|
|
||||||
else:
|
|
||||||
if h:
|
|
||||||
h_grp = h.get('group') or {}
|
|
||||||
if w:
|
|
||||||
commands.extend(self._render_grp_mem('port-group', w['group'], h_grp, opr))
|
|
||||||
commands.extend(self._render_grp_mem('address_group', w['group'], h_grp, opr))
|
|
||||||
commands.extend(self._render_grp_mem('network_group', w['group'], h_grp, opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_grp_mem(self, attr, w, h, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for group list/members attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_grp = []
|
|
||||||
w_grp = []
|
|
||||||
l_set = ('name', 'description')
|
|
||||||
if w:
|
|
||||||
w_grp = w.get(attr) or []
|
|
||||||
if h:
|
|
||||||
h_grp = h.get(attr) or []
|
|
||||||
|
|
||||||
if w_grp:
|
|
||||||
for want in w_grp:
|
|
||||||
cmd = self._compute_command(key='group', attr=attr, opr=opr)
|
|
||||||
h = self.search_attrib_in_have(h_grp, want, 'name')
|
|
||||||
for key, val in iteritems(want):
|
|
||||||
if val:
|
|
||||||
if opr and key in l_set and not (h and self._is_w_same(want, h, key)):
|
|
||||||
if key == 'name':
|
|
||||||
commands.append(cmd + ' ' + str(val))
|
|
||||||
else:
|
|
||||||
commands.append(cmd + ' ' + want['name'] + ' ' + key + " '" + str(want[key]) + "'")
|
|
||||||
elif not opr and key in l_set:
|
|
||||||
if key == 'name' and self._is_grp_del(h, want, key):
|
|
||||||
commands.append(cmd + ' ' + want['name'])
|
|
||||||
continue
|
|
||||||
elif not (h and self._in_target(h, key)) and not self._is_grp_del(h, want, key):
|
|
||||||
commands.append(cmd + ' ' + want['name'] + ' ' + key)
|
|
||||||
elif key == 'members':
|
|
||||||
commands.extend(self._render_ports_addrs(key, want, h, opr, cmd, want['name'], attr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_ports_addrs(self, attr, w, h, opr, cmd, name, type):
|
|
||||||
"""
|
|
||||||
This function forms the commands for port/address/network group members
|
|
||||||
based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:param name: name of group.
|
|
||||||
:param type: group type.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
have = []
|
|
||||||
if w:
|
|
||||||
want = w.get(attr) or []
|
|
||||||
if h:
|
|
||||||
have = h.get(attr) or []
|
|
||||||
|
|
||||||
if want:
|
|
||||||
if opr:
|
|
||||||
members = list_diff_want_only(want, have)
|
|
||||||
for member in members:
|
|
||||||
commands.append(
|
|
||||||
cmd + ' ' + name + ' ' + self._grp_type(type) + ' ' + member[self._get_mem_type(type)]
|
|
||||||
)
|
|
||||||
elif not opr and have:
|
|
||||||
members = list_diff_want_only(want, have)
|
|
||||||
for member in members:
|
|
||||||
commands.append(
|
|
||||||
cmd + ' ' + name + ' ' + self._grp_type(type) + ' ' + member[self._get_mem_type(type)]
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _get_mem_type(self, group):
|
|
||||||
"""
|
|
||||||
This function returns the member type
|
|
||||||
based on the type of group.
|
|
||||||
"""
|
|
||||||
return 'port' if group == 'port_group' else 'address'
|
|
||||||
|
|
||||||
def _render_state_policy(self, attr, w, h, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'state-policy' attributes
|
|
||||||
based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
have = []
|
|
||||||
l_set = ('log', 'action', 'connection_type')
|
|
||||||
if not opr and self._is_root_del(h, w, attr):
|
|
||||||
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
|
|
||||||
else:
|
|
||||||
w_sp = deepcopy(remove_empties(w))
|
|
||||||
want = w_sp.get(attr) or []
|
|
||||||
if h:
|
|
||||||
have = h.get(attr) or []
|
|
||||||
if want:
|
|
||||||
for w in want:
|
|
||||||
h = self.search_attrib_in_have(have, w, 'connection_type')
|
|
||||||
for key, val in iteritems(w):
|
|
||||||
if val and key != 'connection_type':
|
|
||||||
if opr and key in l_set and not (h and self._is_w_same(w, h, key)):
|
|
||||||
commands.append(self._form_attr_cmd(key=attr + ' ' + w['connection_type'], attr=key, val=self._bool_to_str(val), opr=opr))
|
|
||||||
elif not opr and key in l_set:
|
|
||||||
if not (h and self._in_target(h, key)) and not self._is_del(l_set, h):
|
|
||||||
if key == 'action':
|
|
||||||
commands.append(self._form_attr_cmd(attr=attr + ' ' + w['connection_type'], opr=opr))
|
|
||||||
else:
|
|
||||||
commands.append(self._form_attr_cmd(attr=attr + ' ' + w['connection_type'], val=self._bool_to_str(val), opr=opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_route_redirects(self, attr, w, h, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'route_redirects' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
have = []
|
|
||||||
l_set = ('afi', 'ip_src_route')
|
|
||||||
|
|
||||||
if w:
|
|
||||||
want = w.get(attr) or []
|
|
||||||
if h:
|
|
||||||
have = h.get(attr) or []
|
|
||||||
|
|
||||||
if want:
|
|
||||||
for w in want:
|
|
||||||
h = self.search_attrib_in_have(have, w, 'afi')
|
|
||||||
for key, val in iteritems(w):
|
|
||||||
if val and key != 'afi':
|
|
||||||
if opr and key in l_set and not (h and self._is_w_same(w, h, key)):
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr))
|
|
||||||
elif not opr and key in l_set:
|
|
||||||
if self._is_del(l_set, h):
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr))
|
|
||||||
continue
|
|
||||||
elif not (h and self._in_target(h, key)) and not self._is_del(l_set, h):
|
|
||||||
commands.append(self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr))
|
|
||||||
elif key == 'icmp_redirects':
|
|
||||||
commands.extend(self._render_icmp_redirects(key, w, h, opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_icmp_redirects(self, attr, w, h, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'icmp_redirects' attributes
|
|
||||||
based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_red = {}
|
|
||||||
l_set = ('send', 'receive')
|
|
||||||
if w[attr]:
|
|
||||||
if h and attr in h.keys():
|
|
||||||
h_red = h.get(attr) or {}
|
|
||||||
for item, value in iteritems(w[attr]):
|
|
||||||
if opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)):
|
|
||||||
commands.append(self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr))
|
|
||||||
elif not opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)):
|
|
||||||
commands.append(self._form_attr_cmd(attr=item, opr=opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def search_attrib_in_have(self, have, want, attr):
|
|
||||||
"""
|
|
||||||
This function returns the attribute if it is present in target config.
|
|
||||||
:param have: the target config.
|
|
||||||
:param want: the desired config.
|
|
||||||
:param attr: attribute name .
|
|
||||||
:return: attribute/None
|
|
||||||
"""
|
|
||||||
if have:
|
|
||||||
for h in have:
|
|
||||||
if h[attr] == want[attr]:
|
|
||||||
return h
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the command for leaf attribute.
|
|
||||||
:param key: parent key.
|
|
||||||
:param attr: attribute name
|
|
||||||
:param value: value
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated command.
|
|
||||||
"""
|
|
||||||
command = self._compute_command(key=key, attr=self._map_attrib(attr), val=val, opr=opr)
|
|
||||||
return command
|
|
||||||
|
|
||||||
def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True):
|
|
||||||
"""
|
|
||||||
This function construct the add/delete command based on passed attributes.
|
|
||||||
:param key: parent key.
|
|
||||||
:param attr: attribute name
|
|
||||||
:param value: value
|
|
||||||
:param remove: True/False.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated command.
|
|
||||||
"""
|
|
||||||
if remove or not opr:
|
|
||||||
cmd = 'delete firewall '
|
|
||||||
else:
|
|
||||||
cmd = 'set firewall '
|
|
||||||
if key:
|
|
||||||
cmd += (key.replace("_", "-") + " ")
|
|
||||||
if attr:
|
|
||||||
cmd += (attr.replace("_", "-"))
|
|
||||||
if val and opr:
|
|
||||||
cmd += (" '" + str(val) + "'")
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
def _bool_to_str(self, val):
|
|
||||||
"""
|
|
||||||
This function converts the bool value into string.
|
|
||||||
:param val: bool value.
|
|
||||||
:return: enable/disable.
|
|
||||||
"""
|
|
||||||
return 'enable' if str(val) == 'True' else 'disable' if str(val) == 'False' else val
|
|
||||||
|
|
||||||
def _grp_type(self, val):
|
|
||||||
"""
|
|
||||||
This function returns the group member type based on value argument.
|
|
||||||
:param val: value.
|
|
||||||
:return: member type.
|
|
||||||
"""
|
|
||||||
return 'address' if val == 'address_group' else 'network' if val == 'network_group' else 'port'
|
|
||||||
|
|
||||||
def _is_w_same(self, w, h, key):
|
|
||||||
"""
|
|
||||||
This function checks whether the key value is same in desired and
|
|
||||||
target config dictionary.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param key:attribute name.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return True if h and key in h and h[key] == w[key] else False
|
|
||||||
|
|
||||||
def _in_target(self, h, key):
|
|
||||||
"""
|
|
||||||
This function checks whether the target exist and key present in target config.
|
|
||||||
:param h: target config.
|
|
||||||
:param key: attribute name.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return True if h and key in h else False
|
|
||||||
|
|
||||||
def _is_grp_del(self, w, h, key):
|
|
||||||
"""
|
|
||||||
This function checks whether group needed to be deleted based on
|
|
||||||
desired and target configs.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param key: group name.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return True if h and key in h and (not w or key not in w or not w[key]) else False
|
|
||||||
|
|
||||||
def _is_root_del(self, w, h, key):
|
|
||||||
"""
|
|
||||||
This function checks whether a root attribute which can have
|
|
||||||
further child attributes needed to be deleted.
|
|
||||||
:param w: the desired config.
|
|
||||||
:param h: the target config.
|
|
||||||
:param key: attribute name.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return True if h and key in h and (not w or key not in w or not w[key]) else False
|
|
||||||
|
|
||||||
def _is_del(self, b_set, h, key='number'):
|
|
||||||
"""
|
|
||||||
This function checks whether attribute needs to be deleted
|
|
||||||
when operation is false and attribute present in present target config.
|
|
||||||
:param b_set: attribute set.
|
|
||||||
:param h: target config.
|
|
||||||
:param key: number.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return key in b_set and not (h and self._in_target(h, key))
|
|
||||||
|
|
||||||
def _map_attrib(self, attrib, type=None):
|
|
||||||
"""
|
|
||||||
- This function construct the regex string.
|
|
||||||
- replace the underscore with hyphen.
|
|
||||||
:param attrib: attribute
|
|
||||||
:return: regex string
|
|
||||||
"""
|
|
||||||
regex = attrib.replace("_", "-")
|
|
||||||
if attrib == 'send':
|
|
||||||
if type == 'ipv6':
|
|
||||||
regex = 'ipv6-send-redirects'
|
|
||||||
else:
|
|
||||||
regex = 'send-redirects'
|
|
||||||
elif attrib == 'ip_src_route':
|
|
||||||
if type == 'ipv6':
|
|
||||||
regex = 'ipv6-src-route'
|
|
||||||
elif attrib == 'receive':
|
|
||||||
if type == 'ipv6':
|
|
||||||
regex = 'ipv6-receive-redirects'
|
|
||||||
else:
|
|
||||||
regex = 'receive-redirects'
|
|
||||||
elif attrib == 'disabled':
|
|
||||||
regex = 'disable'
|
|
||||||
elif attrib == 'all':
|
|
||||||
regex = 'all-ping'
|
|
||||||
elif attrib == 'broadcast':
|
|
||||||
regex = 'broadcast-ping'
|
|
||||||
elif attrib == 'validation':
|
|
||||||
regex = 'source-validation'
|
|
||||||
return regex
|
|
@ -1,364 +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 vyos_firewall_interfaces class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, dict_diff, remove_empties, search_obj_in_list
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_interfaces(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_firewall_interfaces class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'firewall_interfaces',
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
super(Firewall_interfaces, self).__init__(module)
|
|
||||||
|
|
||||||
def get_firewall_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)
|
|
||||||
firewall_interfaces_facts = facts['ansible_network_resources'].get('firewall_interfaces')
|
|
||||||
if not firewall_interfaces_facts:
|
|
||||||
return []
|
|
||||||
return firewall_interfaces_facts
|
|
||||||
|
|
||||||
def execute_module(self):
|
|
||||||
""" Execute the module
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The result from module execution
|
|
||||||
"""
|
|
||||||
result = {'changed': False}
|
|
||||||
warnings = list()
|
|
||||||
commands = list()
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
existing_firewall_interfaces_facts = self.get_firewall_interfaces_facts()
|
|
||||||
else:
|
|
||||||
existing_firewall_interfaces_facts = []
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES or self.state == 'rendered':
|
|
||||||
commands.extend(self.set_config(existing_firewall_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_firewall_interfaces_facts = self.get_firewall_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_firewall_interfaces_facts(data=running_config)
|
|
||||||
else:
|
|
||||||
changed_firewall_interfaces_facts = []
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
result['before'] = existing_firewall_interfaces_facts
|
|
||||||
if result['changed']:
|
|
||||||
result['after'] = changed_firewall_interfaces_facts
|
|
||||||
elif self.state == 'gathered':
|
|
||||||
result['gathered'] = changed_firewall_interfaces_facts
|
|
||||||
|
|
||||||
result['warnings'] = warnings
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_config(self, existing_firewall_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_firewall_interfaces_facts
|
|
||||||
resp = self.set_state(want, have)
|
|
||||||
return to_list(resp)
|
|
||||||
|
|
||||||
def set_state(self, w, h):
|
|
||||||
""" 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 = []
|
|
||||||
if self.state in ('merged', 'replaced', 'overridden', 'rendered') and not w:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(self.state))
|
|
||||||
if self.state == 'overridden':
|
|
||||||
commands.extend(self._state_overridden(w, h))
|
|
||||||
elif self.state == 'deleted':
|
|
||||||
commands.extend(self._state_deleted(w, h))
|
|
||||||
elif w:
|
|
||||||
if self.state == 'merged' or self.state == 'rendered':
|
|
||||||
commands.extend(self._state_merged(w, h))
|
|
||||||
elif self.state == 'replaced':
|
|
||||||
commands.extend(self._state_replaced(w, h))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
for h in have:
|
|
||||||
w = search_obj_in_list(h['name'], want)
|
|
||||||
commands.extend(self._render_access_rules(h, w, opr=False))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_overridden(self, want, have):
|
|
||||||
""" The command generator when state is overridden
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
for h_ar in have:
|
|
||||||
w_ar = search_obj_in_list(h_ar['name'], want)
|
|
||||||
if not w_ar and 'access_rules' in h_ar:
|
|
||||||
commands.append(self._compute_command(name=h_ar['name'], opr=False))
|
|
||||||
else:
|
|
||||||
h_rules = h_ar.get('access_rules') or []
|
|
||||||
key = 'direction'
|
|
||||||
if w_ar:
|
|
||||||
w_rules = w_ar.get('access_rules') or []
|
|
||||||
if not w_rules and h_rules:
|
|
||||||
commands.append(self._compute_command(name=h_ar['name'], opr=False))
|
|
||||||
if h_rules:
|
|
||||||
for h_rule in h_rules:
|
|
||||||
w_rule = search_obj_in_list(h_rule['afi'], w_rules, key='afi')
|
|
||||||
have_rules = h_rule.get('rules') or []
|
|
||||||
if w_rule:
|
|
||||||
want_rules = w_rule.get('rules') or []
|
|
||||||
for h in have_rules:
|
|
||||||
if key in h:
|
|
||||||
w = search_obj_in_list(h[key], want_rules, key=key)
|
|
||||||
if not w or key not in w or ('name' in h and w and 'name' not in w):
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
afi=h_rule['afi'], name=h_ar['name'], attrib=h[key], opr=False
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
for w in want:
|
|
||||||
h = search_obj_in_list(w['name'], have)
|
|
||||||
commands.extend(self._render_access_rules(w, h))
|
|
||||||
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 w in want:
|
|
||||||
h = search_obj_in_list(w['name'], have)
|
|
||||||
if h and 'access_rules' in h:
|
|
||||||
commands.extend(self._delete_access_rules(w, h, opr=False))
|
|
||||||
elif have:
|
|
||||||
for h in have:
|
|
||||||
if 'access_rules' in h:
|
|
||||||
commands.append(self._compute_command(name=h['name'], opr=False))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _delete_access_rules(self, want, have, opr=False):
|
|
||||||
"""
|
|
||||||
This function forms the delete commands based on the 'opr' type
|
|
||||||
for 'access_rules' attributes.
|
|
||||||
:param want: desired config.
|
|
||||||
:param have: target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_rules = {}
|
|
||||||
w_rs = deepcopy(remove_empties(want))
|
|
||||||
w_rules = w_rs.get('access_rules') or []
|
|
||||||
if have:
|
|
||||||
h_rs = deepcopy(remove_empties(have))
|
|
||||||
h_rules = h_rs.get('access_rules') or []
|
|
||||||
|
|
||||||
# if all firewall config needed to be deleted for specific interface
|
|
||||||
# when operation is delete.
|
|
||||||
if not w_rules and h_rules:
|
|
||||||
commands.append(self._compute_command(name=want['name'], opr=opr))
|
|
||||||
if w_rules:
|
|
||||||
for w in w_rules:
|
|
||||||
h = search_obj_in_list(w['afi'], h_rules, key='afi')
|
|
||||||
commands.extend(self._delete_rules(want['name'], w, h))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _delete_rules(self, name, want, have, opr=False):
|
|
||||||
"""
|
|
||||||
This function forms the delete commands based on the 'opr' type
|
|
||||||
for rules attributes.
|
|
||||||
:param name: interface id/name.
|
|
||||||
:param want: desired config.
|
|
||||||
:param have: target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_rules = []
|
|
||||||
key = 'direction'
|
|
||||||
w_rules = want.get('rules') or []
|
|
||||||
if have:
|
|
||||||
h_rules = have.get('rules') or []
|
|
||||||
# when rule set needed to be removed on
|
|
||||||
# (inbound|outbound|local interface)
|
|
||||||
if h_rules and not w_rules:
|
|
||||||
for h in h_rules:
|
|
||||||
if key in h:
|
|
||||||
commands.append(self._compute_command(afi=want['afi'], name=name, attrib=h[key], opr=opr))
|
|
||||||
for w in w_rules:
|
|
||||||
h = search_obj_in_list(w[key], h_rules, key=key)
|
|
||||||
if key in w and h and key in h and 'name' in w and 'name' in h and w['name'] == h['name']:
|
|
||||||
commands.append(self._compute_command(
|
|
||||||
afi=want['afi'],
|
|
||||||
name=name,
|
|
||||||
attrib=w[key],
|
|
||||||
value=w['name'],
|
|
||||||
opr=opr)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_access_rules(self, want, have, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the set/delete commands based on the 'opr' type
|
|
||||||
for 'access_rules' attributes.
|
|
||||||
:param want: desired config.
|
|
||||||
:param have: target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_rules = {}
|
|
||||||
w_rs = deepcopy(remove_empties(want))
|
|
||||||
w_rules = w_rs.get('access_rules') or []
|
|
||||||
if have:
|
|
||||||
h_rs = deepcopy(remove_empties(have))
|
|
||||||
h_rules = h_rs.get('access_rules') or []
|
|
||||||
if w_rules:
|
|
||||||
for w in w_rules:
|
|
||||||
h = search_obj_in_list(w['afi'], h_rules, key='afi')
|
|
||||||
commands.extend(self._render_rules(want['name'], w, h, opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_rules(self, name, want, have, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the set/delete commands based on the 'opr' type
|
|
||||||
for rules attributes.
|
|
||||||
:param name: interface id/name.
|
|
||||||
:param want: desired config.
|
|
||||||
:param have: target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_rules = []
|
|
||||||
key = 'direction'
|
|
||||||
w_rules = want.get('rules') or []
|
|
||||||
if have:
|
|
||||||
h_rules = have.get('rules') or []
|
|
||||||
for w in w_rules:
|
|
||||||
h = search_obj_in_list(w[key], h_rules, key=key)
|
|
||||||
if key in w:
|
|
||||||
if opr:
|
|
||||||
if 'name' in w and not (h and h[key] == w[key] and h['name'] == w['name']):
|
|
||||||
commands.append(self._compute_command(afi=want['afi'], name=name, attrib=w[key], value=w['name']))
|
|
||||||
elif not (h and key in h):
|
|
||||||
commands.append(self._compute_command(afi=want['afi'], name=name, attrib=w[key]))
|
|
||||||
elif not opr:
|
|
||||||
if not h or key not in h or ('name' in w and h and 'name' not in h):
|
|
||||||
commands.append(self._compute_command(afi=want['afi'], name=name, attrib=w[key], opr=opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _compute_command(self, afi=None, name=None, attrib=None, value=None, opr=True):
|
|
||||||
"""
|
|
||||||
This function construct the add/delete command based on passed attributes.
|
|
||||||
:param afi: address type.
|
|
||||||
:param name: interface name.
|
|
||||||
:param attrib: attribute name.
|
|
||||||
:param value: attribute value.
|
|
||||||
:param opr: operation flag.
|
|
||||||
:return: generated command.
|
|
||||||
"""
|
|
||||||
if not opr:
|
|
||||||
cmd = 'delete interfaces ethernet' + ' ' + name + ' firewall'
|
|
||||||
else:
|
|
||||||
cmd = 'set interfaces ethernet' + ' ' + name + ' firewall'
|
|
||||||
if attrib:
|
|
||||||
cmd += (' ' + attrib)
|
|
||||||
if afi:
|
|
||||||
cmd += ' ' + self._get_fw_type(afi)
|
|
||||||
if value:
|
|
||||||
cmd += (" '" + str(value) + "'")
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
def _get_fw_type(self, afi):
|
|
||||||
"""
|
|
||||||
This function returns the firewall rule-set type based on IP address.
|
|
||||||
:param afi: address type
|
|
||||||
:return: rule-set type.
|
|
||||||
"""
|
|
||||||
return 'ipv6-name' if afi == 'ipv6' else 'name'
|
|
@ -1,706 +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 vyos_firewall_rules class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, remove_empties
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network.vyos.utils.utils import list_diff_want_only
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_rules(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_firewall_rules class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'firewall_rules',
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
super(Firewall_rules, self).__init__(module)
|
|
||||||
|
|
||||||
def get_firewall_rules_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)
|
|
||||||
firewall_rules_facts = facts['ansible_network_resources'].get('firewall_rules')
|
|
||||||
if not firewall_rules_facts:
|
|
||||||
return []
|
|
||||||
return firewall_rules_facts
|
|
||||||
|
|
||||||
def execute_module(self):
|
|
||||||
""" Execute the module
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The result from module execution
|
|
||||||
"""
|
|
||||||
result = {'changed': False}
|
|
||||||
warnings = list()
|
|
||||||
commands = list()
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
existing_firewall_rules_facts = self.get_firewall_rules_facts()
|
|
||||||
else:
|
|
||||||
existing_firewall_rules_facts = []
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES or self.state == 'rendered':
|
|
||||||
commands.extend(self.set_config(existing_firewall_rules_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_firewall_rules_facts = self.get_firewall_rules_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_firewall_rules_facts(data=running_config)
|
|
||||||
else:
|
|
||||||
changed_firewall_rules_facts = []
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
result['before'] = existing_firewall_rules_facts
|
|
||||||
if result['changed']:
|
|
||||||
result['after'] = changed_firewall_rules_facts
|
|
||||||
elif self.state == 'gathered':
|
|
||||||
result['gathered'] = changed_firewall_rules_facts
|
|
||||||
|
|
||||||
result['warnings'] = warnings
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_config(self, existing_firewall_rules_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_firewall_rules_facts
|
|
||||||
resp = self.set_state(want, have)
|
|
||||||
return to_list(resp)
|
|
||||||
|
|
||||||
def set_state(self, w, h):
|
|
||||||
""" 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 = []
|
|
||||||
if self.state in ('merged', 'replaced', 'overridden', 'rendered') and not w:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(self.state))
|
|
||||||
if self.state == 'overridden':
|
|
||||||
commands.extend(self._state_overridden(w, h))
|
|
||||||
elif self.state == 'deleted':
|
|
||||||
commands.extend(self._state_deleted(w, h))
|
|
||||||
elif w:
|
|
||||||
if self.state == 'merged' or self.state == 'rendered':
|
|
||||||
commands.extend(self._state_merged(w, h))
|
|
||||||
elif self.state == 'replaced':
|
|
||||||
commands.extend(self._state_replaced(w, h))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
for h in have:
|
|
||||||
r_sets = self._get_r_sets(h)
|
|
||||||
for rs in r_sets:
|
|
||||||
w = self.search_r_sets_in_have(want, rs['name'], 'r_list')
|
|
||||||
commands.extend(self._add_r_sets(h['afi'], rs, w, opr=False))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_overridden(self, want, have):
|
|
||||||
""" The command generator when state is overridden
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
for h in have:
|
|
||||||
r_sets = self._get_r_sets(h)
|
|
||||||
for rs in r_sets:
|
|
||||||
w = self.search_r_sets_in_have(want, rs['name'], 'r_list')
|
|
||||||
if not w:
|
|
||||||
commands.append(self._compute_command(h['afi'], rs['name'], remove=True))
|
|
||||||
else:
|
|
||||||
commands.extend(self._add_r_sets(h['afi'], rs, w, opr=False))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
for w in want:
|
|
||||||
r_sets = self._get_r_sets(w)
|
|
||||||
for rs in r_sets:
|
|
||||||
h = self.search_r_sets_in_have(have, rs['name'], 'r_list')
|
|
||||||
commands.extend(self._add_r_sets(w['afi'], rs, h))
|
|
||||||
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 w in want:
|
|
||||||
r_sets = self._get_r_sets(w)
|
|
||||||
if r_sets:
|
|
||||||
for rs in r_sets:
|
|
||||||
h = self.search_r_sets_in_have(have, rs['name'], 'r_list')
|
|
||||||
if h:
|
|
||||||
w_rules = rs.get('rules') or []
|
|
||||||
h_rules = h.get('rules') or []
|
|
||||||
if w_rules and h_rules:
|
|
||||||
for rule in w_rules:
|
|
||||||
if self.search_r_sets_in_have(h_rules, rule['number'], 'rules'):
|
|
||||||
commands.append(self._add_r_base_attrib(w['afi'], rs['name'], 'number', rule, opr=False))
|
|
||||||
else:
|
|
||||||
commands.append(self._compute_command(w['afi'], h['name'], remove=True))
|
|
||||||
elif have:
|
|
||||||
for h in have:
|
|
||||||
if h['afi'] == w['afi']:
|
|
||||||
commands.append(self._compute_command(w['afi'], remove=True))
|
|
||||||
elif have:
|
|
||||||
for h in have:
|
|
||||||
r_sets = self._get_r_sets(h)
|
|
||||||
if r_sets:
|
|
||||||
commands.append(self._compute_command(afi=h['afi'], remove=True))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_r_sets(self, afi, want, have, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the set/delete commands based on the 'opr' type
|
|
||||||
for rule-sets attributes.
|
|
||||||
:param afi: address type.
|
|
||||||
:param want: desired config.
|
|
||||||
:param have: target config.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
l_set = ('description',
|
|
||||||
'default_action',
|
|
||||||
'enable_default_log')
|
|
||||||
h_rs = {}
|
|
||||||
h_rules = {}
|
|
||||||
w_rs = deepcopy(remove_empties(want))
|
|
||||||
w_rules = w_rs.pop('rules', None)
|
|
||||||
if have:
|
|
||||||
h_rs = deepcopy(remove_empties(have))
|
|
||||||
h_rules = h_rs.pop('rules', None)
|
|
||||||
if w_rs:
|
|
||||||
for key, val in iteritems(w_rs):
|
|
||||||
if opr and key in l_set and not (h_rs and self._is_w_same(w_rs, h_rs, key)):
|
|
||||||
if key == 'enable_default_log':
|
|
||||||
if val and (not h_rs or key not in h_rs or not h_rs[key]):
|
|
||||||
commands.append(self._add_rs_base_attrib(afi, want['name'], key, w_rs))
|
|
||||||
else:
|
|
||||||
commands.append(self._add_rs_base_attrib(afi, want['name'], key, w_rs))
|
|
||||||
elif not opr and key in l_set:
|
|
||||||
if key == 'enable_default_log' and val and h_rs and (key not in h_rs or not h_rs[key]):
|
|
||||||
commands.append(self._add_rs_base_attrib(afi, want['name'], key, w_rs, opr))
|
|
||||||
elif not (h_rs and self._in_target(h_rs, key)):
|
|
||||||
commands.append(self._add_rs_base_attrib(afi, want['name'], key, w_rs, opr))
|
|
||||||
commands.extend(self._add_rules(afi, want['name'], w_rules, h_rules, opr))
|
|
||||||
if h_rules:
|
|
||||||
have['rules'] = h_rules
|
|
||||||
if w_rules:
|
|
||||||
want['rules'] = w_rules
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_rules(self, afi, name, w_rules, h_rules, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the set/delete commands based on the 'opr' type
|
|
||||||
for rules attributes.
|
|
||||||
:param want: desired config.
|
|
||||||
:param have: target config.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
l_set = ('ipsec',
|
|
||||||
'action',
|
|
||||||
'number',
|
|
||||||
'protocol',
|
|
||||||
'fragment',
|
|
||||||
'disabled',
|
|
||||||
'description')
|
|
||||||
if w_rules:
|
|
||||||
for w in w_rules:
|
|
||||||
cmd = self._compute_command(afi, name, w['number'], opr=opr)
|
|
||||||
h = self.search_r_sets_in_have(h_rules, w['number'], type='rules')
|
|
||||||
for key, val in iteritems(w):
|
|
||||||
if val:
|
|
||||||
if opr and key in l_set and not (h and self._is_w_same(w, h, key)):
|
|
||||||
if key == 'disabled':
|
|
||||||
if not (not val and (not h or key not in h or not h[key])):
|
|
||||||
commands.append(self._add_r_base_attrib(afi, name, key, w))
|
|
||||||
else:
|
|
||||||
commands.append(self._add_r_base_attrib(afi, name, key, w))
|
|
||||||
elif not opr:
|
|
||||||
if key == 'number' and self._is_del(l_set, h):
|
|
||||||
commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr))
|
|
||||||
continue
|
|
||||||
elif key == 'disabled' and val and h and (key not in h or not h[key]):
|
|
||||||
commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr))
|
|
||||||
elif key in l_set and not (h and self._in_target(h, key)) and not self._is_del(l_set, h):
|
|
||||||
commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr))
|
|
||||||
elif key == 'p2p':
|
|
||||||
commands.extend(self._add_p2p(key, w, h, cmd, opr))
|
|
||||||
elif key == 'tcp':
|
|
||||||
commands.extend(self._add_tcp(key, w, h, cmd, opr))
|
|
||||||
elif key == 'time':
|
|
||||||
commands.extend(self._add_time(key, w, h, cmd, opr))
|
|
||||||
elif key == 'icmp':
|
|
||||||
commands.extend(self._add_icmp(key, w, h, cmd, opr))
|
|
||||||
elif key == 'state':
|
|
||||||
commands.extend(self._add_state(key, w, h, cmd, opr))
|
|
||||||
elif key == 'limit':
|
|
||||||
commands.extend(self._add_limit(key, w, h, cmd, opr))
|
|
||||||
elif key == 'recent':
|
|
||||||
commands.extend(self._add_recent(key, w, h, cmd, opr))
|
|
||||||
elif key == 'destination' or key == 'source':
|
|
||||||
commands.extend(self._add_src_or_dest(key, w, h, cmd, opr))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_p2p(self, attr, w, h, cmd, opr):
|
|
||||||
"""
|
|
||||||
This function forms the set/delete commands based on the 'opr' type
|
|
||||||
for p2p applications attributes.
|
|
||||||
:param want: desired config.
|
|
||||||
:param have: target config.
|
|
||||||
:return: generated commands list.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
have = []
|
|
||||||
if w:
|
|
||||||
want = w.get(attr) or []
|
|
||||||
if h:
|
|
||||||
have = h.get(attr) or []
|
|
||||||
if want:
|
|
||||||
if opr:
|
|
||||||
applications = list_diff_want_only(want, have)
|
|
||||||
for app in applications:
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + app['application']))
|
|
||||||
elif not opr and have:
|
|
||||||
applications = list_diff_want_only(want, have)
|
|
||||||
for app in applications:
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + app['application']))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_state(self, attr, w, h, cmd, opr):
|
|
||||||
"""
|
|
||||||
This function forms the command for 'state' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
h_state = {}
|
|
||||||
commands = []
|
|
||||||
l_set = ('new',
|
|
||||||
'invalid',
|
|
||||||
'related',
|
|
||||||
'established')
|
|
||||||
if w[attr]:
|
|
||||||
if h and attr in h.keys():
|
|
||||||
h_state = h.get(attr) or {}
|
|
||||||
for item, val in iteritems(w[attr]):
|
|
||||||
if opr and item in l_set and not (h_state and self._is_w_same(w[attr], h_state, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item + ' ' + self._bool_to_str(val)))
|
|
||||||
elif not opr and item in l_set and not (h_state and self._in_target(h_state, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_recent(self, attr, w, h, cmd, opr):
|
|
||||||
"""
|
|
||||||
This function forms the command for 'recent' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_recent = {}
|
|
||||||
l_set = ('count', 'time')
|
|
||||||
if w[attr]:
|
|
||||||
if h and attr in h.keys():
|
|
||||||
h_recent = h.get(attr) or {}
|
|
||||||
for item, val in iteritems(w[attr]):
|
|
||||||
if opr and item in l_set and not (h_recent and self._is_w_same(w[attr], h_recent, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item + ' ' + str(val)))
|
|
||||||
elif not opr and item in l_set and not (h_recent and self._in_target(h_recent, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_icmp(self, attr, w, h, cmd, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'icmp' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_icmp = {}
|
|
||||||
l_set = ('code', 'type', 'type_name')
|
|
||||||
if w[attr]:
|
|
||||||
if h and attr in h.keys():
|
|
||||||
h_icmp = h.get(attr) or {}
|
|
||||||
for item, val in iteritems(w[attr]):
|
|
||||||
if opr and item in l_set and not (h_icmp and self._is_w_same(w[attr], h_icmp, item)):
|
|
||||||
if item == 'type_name':
|
|
||||||
if 'ipv6-name' in cmd:
|
|
||||||
commands.append(cmd + (' ' + 'icmpv6' + ' ' + 'type' + ' ' + val))
|
|
||||||
else:
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item.replace("_", "-") + ' ' + val))
|
|
||||||
else:
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item + ' ' + str(val)))
|
|
||||||
elif not opr and item in l_set and not (h_icmp and self._in_target(h_icmp, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_time(self, attr, w, h, cmd, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'time' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_time = {}
|
|
||||||
l_set = ('utc',
|
|
||||||
'stopdate',
|
|
||||||
'stoptime',
|
|
||||||
'weekdays',
|
|
||||||
'monthdays',
|
|
||||||
'startdate',
|
|
||||||
'starttime')
|
|
||||||
if w[attr]:
|
|
||||||
if h and attr in h.keys():
|
|
||||||
h_time = h.get(attr) or {}
|
|
||||||
for item, val in iteritems(w[attr]):
|
|
||||||
if opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)):
|
|
||||||
if item == 'utc':
|
|
||||||
if not (not val and (not h_time or item not in h_time)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item))
|
|
||||||
else:
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item + ' ' + val))
|
|
||||||
elif not opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + item))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_tcp(self, attr, w, h, cmd, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'tcp' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
h_tcp = {}
|
|
||||||
commands = []
|
|
||||||
if w[attr]:
|
|
||||||
key = 'flags'
|
|
||||||
flags = w[attr].get(key) or {}
|
|
||||||
if flags:
|
|
||||||
if h and key in h[attr].keys():
|
|
||||||
h_tcp = h[attr].get(key) or {}
|
|
||||||
if flags:
|
|
||||||
if opr and not (h_tcp and self._is_w_same(w[attr], h[attr], key)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key + ' ' + flags))
|
|
||||||
if not opr and not (h_tcp and self._is_w_same(w[attr], h[attr], key)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key + ' ' + flags))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_limit(self, attr, w, h, cmd, opr):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'limit' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
h_limit = {}
|
|
||||||
commands = []
|
|
||||||
if w[attr]:
|
|
||||||
key = 'burst'
|
|
||||||
if opr and key in w[attr].keys() and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key + ' ' + str(w[attr].get(key))))
|
|
||||||
elif not opr and key in w[attr].keys() and not (h and attr in h.keys() and self._in_target(h[attr], key)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key + ' ' + str(w[attr].get(key))))
|
|
||||||
key = 'rate'
|
|
||||||
rate = w[attr].get(key) or {}
|
|
||||||
if rate:
|
|
||||||
if h and key in h[attr].keys():
|
|
||||||
h_limit = h[attr].get(key) or {}
|
|
||||||
if 'unit' in rate and 'number' in rate:
|
|
||||||
if opr and not (h_limit and self._is_w_same(rate, h_limit, 'unit') and self.is_w_same(rate, h_limit, 'number')):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key + ' ' + str(rate['number']) + '/' + rate['unit']))
|
|
||||||
if not opr and not (h_limit and self._is_w_same(rate, h_limit, 'unit') and self._is_w_same(rate, h_limit, 'number')):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_src_or_dest(self, attr, w, h, cmd, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the commands for 'src/dest' attributes based on the 'opr'.
|
|
||||||
:param attr: attribute name.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param cmd: commands to be prepend.
|
|
||||||
:return: generated list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
h_group = {}
|
|
||||||
g_set = ('port_group',
|
|
||||||
'address_group',
|
|
||||||
'network_group')
|
|
||||||
if w[attr]:
|
|
||||||
keys = ('address', 'mac_address', 'port')
|
|
||||||
for key in keys:
|
|
||||||
if opr and key in w[attr].keys() and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key.replace("_", "-") + ' ' + w[attr].get(key)))
|
|
||||||
elif not opr and key in w[attr].keys() and not (h and attr in h.keys() and self._in_target(h[attr], key)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key))
|
|
||||||
|
|
||||||
key = 'group'
|
|
||||||
group = w[attr].get(key) or {}
|
|
||||||
if group:
|
|
||||||
if h and key in h[attr].keys():
|
|
||||||
h_group = h[attr].get(key) or {}
|
|
||||||
for item, val in iteritems(group):
|
|
||||||
if val:
|
|
||||||
if opr and item in g_set and not (h_group and self._is_w_same(group, h_group, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key + ' ' + item.replace("_", "-") + ' ' + val))
|
|
||||||
elif not opr and item in g_set and not (h_group and self._in_target(h_group, item)):
|
|
||||||
commands.append(cmd + (' ' + attr + ' ' + key + ' ' + item.replace("_", "-")))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def search_r_sets_in_have(self, have, w_name, type='rule_sets'):
|
|
||||||
"""
|
|
||||||
This function returns the rule-set/rule if it is present in target config.
|
|
||||||
:param have: target config.
|
|
||||||
:param w_name: rule-set name.
|
|
||||||
:param type: rule_sets/rule/r_list.
|
|
||||||
:return: rule-set/rule.
|
|
||||||
"""
|
|
||||||
if have:
|
|
||||||
key = 'name'
|
|
||||||
if type == 'rules':
|
|
||||||
key = 'number'
|
|
||||||
for r in have:
|
|
||||||
if r[key] == w_name:
|
|
||||||
return r
|
|
||||||
elif type == 'r_list':
|
|
||||||
for h in have:
|
|
||||||
r_sets = self._get_r_sets(h)
|
|
||||||
for rs in r_sets:
|
|
||||||
if rs[key] == w_name:
|
|
||||||
return rs
|
|
||||||
else:
|
|
||||||
for rs in have:
|
|
||||||
if rs[key] == w_name:
|
|
||||||
return rs
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _get_r_sets(self, item, type='rule_sets'):
|
|
||||||
"""
|
|
||||||
This function returns the list of rule-sets/rules.
|
|
||||||
:param item: config dictionary.
|
|
||||||
:param type: rule_sets/rule/r_list.
|
|
||||||
:return: list of rule-sets/rules.
|
|
||||||
"""
|
|
||||||
rs_list = []
|
|
||||||
r_sets = item[type]
|
|
||||||
if r_sets:
|
|
||||||
for rs in r_sets:
|
|
||||||
rs_list.append(rs)
|
|
||||||
return rs_list
|
|
||||||
|
|
||||||
def _compute_command(self, afi, name=None, number=None, attrib=None, value=None, remove=False, opr=True):
|
|
||||||
"""
|
|
||||||
This function construct the add/delete command based on passed attributes.
|
|
||||||
:param afi: address type.
|
|
||||||
:param name: rule-set name.
|
|
||||||
:param number: rule-number.
|
|
||||||
:param attrib: attribute name.
|
|
||||||
:param value: value.
|
|
||||||
:param remove: True if delete command needed to be construct.
|
|
||||||
:param opr: opeeration flag.
|
|
||||||
:return: generated command.
|
|
||||||
"""
|
|
||||||
if remove or not opr:
|
|
||||||
cmd = 'delete firewall ' + self._get_fw_type(afi)
|
|
||||||
else:
|
|
||||||
cmd = 'set firewall ' + self._get_fw_type(afi)
|
|
||||||
if name:
|
|
||||||
cmd += (' ' + name)
|
|
||||||
if number:
|
|
||||||
cmd += (' rule ' + str(number))
|
|
||||||
if attrib:
|
|
||||||
cmd += (' ' + attrib.replace("_", "-"))
|
|
||||||
if value and opr and attrib != 'enable_default_log' and attrib != 'disabled':
|
|
||||||
cmd += (" '" + str(value) + "'")
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
def _add_r_base_attrib(self, afi, name, attr, rule, opr=True):
|
|
||||||
"""
|
|
||||||
This function forms the command for 'rules' attributes which doesn't
|
|
||||||
have further sub attributes.
|
|
||||||
:param afi: address type.
|
|
||||||
:param name: rule-set name
|
|
||||||
:param attrib: attribute name
|
|
||||||
:param rule: rule config dictionary.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated command.
|
|
||||||
"""
|
|
||||||
if attr == 'number':
|
|
||||||
command = self._compute_command(
|
|
||||||
afi=afi, name=name, number=rule['number'], opr=opr
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
command = self._compute_command(
|
|
||||||
afi=afi, name=name, number=rule['number'], attrib=attr, value=rule[attr], opr=opr
|
|
||||||
)
|
|
||||||
return command
|
|
||||||
|
|
||||||
def _add_rs_base_attrib(self, afi, name, attrib, rule, opr=True):
|
|
||||||
"""
|
|
||||||
|
|
||||||
This function forms the command for 'rule-sets' attributes which doesn't
|
|
||||||
have further sub attributes.
|
|
||||||
:param afi: address type.
|
|
||||||
:param name: rule-set name
|
|
||||||
:param attrib: attribute name
|
|
||||||
:param rule: rule config dictionary.
|
|
||||||
:param opr: True/False.
|
|
||||||
:return: generated command.
|
|
||||||
"""
|
|
||||||
command = self._compute_command(afi=afi, name=name, attrib=attrib, value=rule[attrib], opr=opr)
|
|
||||||
return command
|
|
||||||
|
|
||||||
def _bool_to_str(self, val):
|
|
||||||
"""
|
|
||||||
This function converts the bool value into string.
|
|
||||||
:param val: bool value.
|
|
||||||
:return: enable/disable.
|
|
||||||
"""
|
|
||||||
return 'enable' if val else 'disable'
|
|
||||||
|
|
||||||
def _get_fw_type(self, afi):
|
|
||||||
"""
|
|
||||||
This function returns the firewall rule-set type based on IP address.
|
|
||||||
:param afi: address type
|
|
||||||
:return: rule-set type.
|
|
||||||
"""
|
|
||||||
return 'ipv6-name' if afi == 'ipv6' else 'name'
|
|
||||||
|
|
||||||
def _is_del(self, l_set, h, key='number'):
|
|
||||||
"""
|
|
||||||
This function checks whether rule needs to be deleted based on
|
|
||||||
the rule number.
|
|
||||||
:param l_set: attribute set.
|
|
||||||
:param h: target config.
|
|
||||||
:param key: number.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return key in l_set and not (h and self._in_target(h, key))
|
|
||||||
|
|
||||||
def _is_w_same(self, w, h, key):
|
|
||||||
"""
|
|
||||||
This function checks whether the key value is same in base and
|
|
||||||
target config dictionary.
|
|
||||||
:param w: base config.
|
|
||||||
:param h: target config.
|
|
||||||
:param key:attribute name.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return True if h and key in h and h[key] == w[key] else False
|
|
||||||
|
|
||||||
def _in_target(self, h, key):
|
|
||||||
"""
|
|
||||||
This function checks whether the target nexist and key present in target config.
|
|
||||||
:param h: target config.
|
|
||||||
:param key: attribute name.
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
return True if h and key in h else False
|
|
||||||
|
|
||||||
def _is_base_attrib(self, key):
|
|
||||||
"""
|
|
||||||
This function checks whether key is present in predefined
|
|
||||||
based attribute set.
|
|
||||||
:param key:
|
|
||||||
:return: True/False.
|
|
||||||
"""
|
|
||||||
r_set = ('p2p',
|
|
||||||
'ipsec',
|
|
||||||
'action',
|
|
||||||
'fragment',
|
|
||||||
'protocol',
|
|
||||||
'disabled',
|
|
||||||
'description',
|
|
||||||
'mac_address',
|
|
||||||
'default_action',
|
|
||||||
'enable_default_log')
|
|
||||||
return True if key in r_set else False
|
|
@ -1,284 +0,0 @@
|
|||||||
# Copyright 2019 Red Hat
|
|
||||||
# GNU General Public License v3.0+
|
|
||||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""
|
|
||||||
The vyos_interfaces class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, dict_diff, remove_empties
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.network. \
|
|
||||||
vyos.utils.utils import search_obj_in_list, get_interface_type, dict_delete
|
|
||||||
|
|
||||||
|
|
||||||
class Interfaces(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_interfaces class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'interfaces'
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
super(Interfaces, self).__init__(module)
|
|
||||||
|
|
||||||
def get_interfaces_facts(self):
|
|
||||||
""" Get the 'facts' (the current configuration)
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The current configuration as a dictionary
|
|
||||||
"""
|
|
||||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset,
|
|
||||||
self.gather_network_resources)
|
|
||||||
interfaces_facts = facts['ansible_network_resources'].get('interfaces')
|
|
||||||
if not interfaces_facts:
|
|
||||||
return []
|
|
||||||
return interfaces_facts
|
|
||||||
|
|
||||||
def execute_module(self):
|
|
||||||
""" Execute the module
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The result from module execution
|
|
||||||
"""
|
|
||||||
result = {'changed': False}
|
|
||||||
commands = list()
|
|
||||||
warnings = list()
|
|
||||||
|
|
||||||
existing_interfaces_facts = self.get_interfaces_facts()
|
|
||||||
commands.extend(self.set_config(existing_interfaces_facts))
|
|
||||||
if commands:
|
|
||||||
if self._module.check_mode:
|
|
||||||
resp = self._connection.edit_config(commands, commit=False)
|
|
||||||
else:
|
|
||||||
resp = self._connection.edit_config(commands)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if self._module._diff:
|
|
||||||
result['diff'] = resp['diff'] if result['changed'] else None
|
|
||||||
|
|
||||||
changed_interfaces_facts = self.get_interfaces_facts()
|
|
||||||
|
|
||||||
result['before'] = existing_interfaces_facts
|
|
||||||
if result['changed']:
|
|
||||||
result['after'] = changed_interfaces_facts
|
|
||||||
|
|
||||||
result['warnings'] = warnings
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_config(self, existing_interfaces_facts):
|
|
||||||
""" Collect the configuration from the args passed to the module,
|
|
||||||
collect the current configuration (as a dict from facts)
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
want = self._module.params['config']
|
|
||||||
have = existing_interfaces_facts
|
|
||||||
resp = self.set_state(want, have)
|
|
||||||
return to_list(resp)
|
|
||||||
|
|
||||||
def set_state(self, want, have):
|
|
||||||
""" Select the appropriate function based on the state provided
|
|
||||||
|
|
||||||
:param want: the desired configuration as a dictionary
|
|
||||||
:param have: the current configuration as a dictionary
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
state = self._module.params['state']
|
|
||||||
|
|
||||||
if state in ('merged', 'replaced', 'overridden') and not want:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state))
|
|
||||||
|
|
||||||
if state == 'overridden':
|
|
||||||
commands.extend(self._state_overridden(want=want, have=have))
|
|
||||||
|
|
||||||
elif state == 'deleted':
|
|
||||||
if not want:
|
|
||||||
for intf in have:
|
|
||||||
commands.extend(
|
|
||||||
self._state_deleted(
|
|
||||||
{'name': intf['name']},
|
|
||||||
intf
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for item in want:
|
|
||||||
obj_in_have = search_obj_in_list(item['name'], have)
|
|
||||||
commands.extend(
|
|
||||||
self._state_deleted(
|
|
||||||
item, obj_in_have
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for item in want:
|
|
||||||
name = item['name']
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
|
|
||||||
if not obj_in_have:
|
|
||||||
obj_in_have = {'name': item['name']}
|
|
||||||
|
|
||||||
elif state == 'merged':
|
|
||||||
commands.extend(
|
|
||||||
self._state_merged(
|
|
||||||
item, obj_in_have
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif state == 'replaced':
|
|
||||||
commands.extend(
|
|
||||||
self._state_replaced(
|
|
||||||
item, obj_in_have
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._state_deleted(want, have))
|
|
||||||
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_overridden(self, want, have):
|
|
||||||
""" The command generator when state is overridden
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
for intf in have:
|
|
||||||
intf_in_want = search_obj_in_list(intf['name'], want)
|
|
||||||
if not intf_in_want:
|
|
||||||
commands.extend(self._state_deleted({'name': intf['name']}, intf))
|
|
||||||
|
|
||||||
for intf in want:
|
|
||||||
intf_in_have = search_obj_in_list(intf['name'], have)
|
|
||||||
commands.extend(self._state_replaced(intf, intf_in_have))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(have)
|
|
||||||
|
|
||||||
want_vifs = want_copy.pop('vifs', [])
|
|
||||||
have_vifs = have_copy.pop('vifs', [])
|
|
||||||
|
|
||||||
updates = dict_diff(have_copy, want_copy)
|
|
||||||
|
|
||||||
if updates:
|
|
||||||
for key, value in iteritems(updates):
|
|
||||||
commands.append(self._compute_commands(key=key, value=value, interface=want_copy['name']))
|
|
||||||
|
|
||||||
if want_vifs:
|
|
||||||
for want_vif in want_vifs:
|
|
||||||
have_vif = search_obj_in_list(want_vif['vlan_id'], have_vifs, key='vlan_id')
|
|
||||||
if not have_vif:
|
|
||||||
have_vif = {'vlan_id': want_vif['vlan_id'], 'enabled': True}
|
|
||||||
|
|
||||||
vif_updates = dict_diff(have_vif, want_vif)
|
|
||||||
if vif_updates:
|
|
||||||
for key, value in iteritems(vif_updates):
|
|
||||||
commands.append(self._compute_commands(key=key, value=value, interface=want_copy['name'], vif=want_vif['vlan_id']))
|
|
||||||
|
|
||||||
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 = []
|
|
||||||
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(have)
|
|
||||||
|
|
||||||
want_vifs = want_copy.pop('vifs', [])
|
|
||||||
have_vifs = have_copy.pop('vifs', [])
|
|
||||||
|
|
||||||
for key in dict_delete(have_copy, want_copy).keys():
|
|
||||||
if key == 'enabled':
|
|
||||||
continue
|
|
||||||
commands.append(self._compute_commands(key=key, interface=want_copy['name'], remove=True))
|
|
||||||
if have_copy['enabled'] is False:
|
|
||||||
commands.append(self._compute_commands(key='enabled', value=True, interface=want_copy['name']))
|
|
||||||
|
|
||||||
if have_vifs:
|
|
||||||
for have_vif in have_vifs:
|
|
||||||
want_vif = search_obj_in_list(have_vif['vlan_id'], want_vifs, key='vlan_id')
|
|
||||||
if not want_vif:
|
|
||||||
want_vif = {'vlan_id': have_vif['vlan_id'], 'enabled': True}
|
|
||||||
|
|
||||||
for key in dict_delete(have_vif, want_vif).keys():
|
|
||||||
if key == 'enabled':
|
|
||||||
continue
|
|
||||||
commands.append(self._compute_commands(key=key, interface=want_copy['name'], vif=want_vif['vlan_id'], remove=True))
|
|
||||||
if have_vif['enabled'] is False:
|
|
||||||
commands.append(self._compute_commands(key='enabled', value=True, interface=want_copy['name'], vif=want_vif['vlan_id']))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _compute_commands(self, interface, key, vif=None, value=None, remove=False):
|
|
||||||
intf_context = 'interfaces {0} {1}'.format(get_interface_type(interface), interface)
|
|
||||||
set_cmd = 'set {0}'.format(intf_context)
|
|
||||||
del_cmd = 'delete {0}'.format(intf_context)
|
|
||||||
|
|
||||||
if vif:
|
|
||||||
set_cmd = set_cmd + (' vif {0}'.format(vif))
|
|
||||||
del_cmd = del_cmd + (' vif {0}'.format(vif))
|
|
||||||
|
|
||||||
if key == 'enabled':
|
|
||||||
if not value:
|
|
||||||
command = "{0} disable".format(set_cmd)
|
|
||||||
else:
|
|
||||||
command = "{0} disable".format(del_cmd)
|
|
||||||
else:
|
|
||||||
if not remove:
|
|
||||||
command = "{0} {1} '{2}'".format(set_cmd, key, value)
|
|
||||||
else:
|
|
||||||
command = "{0} {1}".format(del_cmd, key)
|
|
||||||
|
|
||||||
return command
|
|
@ -1,278 +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 vyos_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 copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, remove_empties
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.network. \
|
|
||||||
vyos.utils.utils import search_obj_in_list, get_interface_type, diff_list_of_dicts
|
|
||||||
|
|
||||||
|
|
||||||
class L3_interfaces(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_l3_interfaces class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'l3_interfaces',
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
super(L3_interfaces, self).__init__(module)
|
|
||||||
|
|
||||||
def get_l3_interfaces_facts(self):
|
|
||||||
""" Get the 'facts' (the current configuration)
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The current configuration as a dictionary
|
|
||||||
"""
|
|
||||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
|
||||||
l3_interfaces_facts = facts['ansible_network_resources'].get('l3_interfaces')
|
|
||||||
if not l3_interfaces_facts:
|
|
||||||
return []
|
|
||||||
return l3_interfaces_facts
|
|
||||||
|
|
||||||
def execute_module(self):
|
|
||||||
""" Execute the module
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The result from module execution
|
|
||||||
"""
|
|
||||||
result = {'changed': False}
|
|
||||||
warnings = list()
|
|
||||||
commands = list()
|
|
||||||
|
|
||||||
existing_l3_interfaces_facts = self.get_l3_interfaces_facts()
|
|
||||||
commands.extend(self.set_config(existing_l3_interfaces_facts))
|
|
||||||
if commands:
|
|
||||||
if self._module.check_mode:
|
|
||||||
resp = self._connection.edit_config(commands, commit=False)
|
|
||||||
else:
|
|
||||||
resp = self._connection.edit_config(commands)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if self._module._diff:
|
|
||||||
result['diff'] = resp['diff'] if result['changed'] else None
|
|
||||||
|
|
||||||
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 ('merged', 'replaced', 'overridden') and not want:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state))
|
|
||||||
|
|
||||||
if state == 'overridden':
|
|
||||||
commands.extend(self._state_overridden(want=want, have=have))
|
|
||||||
|
|
||||||
elif state == 'deleted':
|
|
||||||
if not want:
|
|
||||||
for intf in have:
|
|
||||||
commands.extend(
|
|
||||||
self._state_deleted(
|
|
||||||
{'name': intf['name']},
|
|
||||||
intf
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for item in want:
|
|
||||||
obj_in_have = search_obj_in_list(item['name'], have)
|
|
||||||
commands.extend(
|
|
||||||
self._state_deleted(
|
|
||||||
item, obj_in_have
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for item in want:
|
|
||||||
name = item['name']
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
|
|
||||||
if not obj_in_have:
|
|
||||||
obj_in_have = {'name': item['name']}
|
|
||||||
|
|
||||||
if state == 'merged':
|
|
||||||
commands.extend(
|
|
||||||
self._state_merged(
|
|
||||||
item, obj_in_have
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif state == 'replaced':
|
|
||||||
commands.extend(
|
|
||||||
self._state_replaced(
|
|
||||||
item, obj_in_have
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._state_deleted(want, have))
|
|
||||||
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_overridden(self, want, have):
|
|
||||||
""" The command generator when state is overridden
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
for intf in have:
|
|
||||||
intf_in_want = search_obj_in_list(intf['name'], want)
|
|
||||||
if not intf_in_want:
|
|
||||||
commands.extend(self._state_deleted({'name': intf['name']}, intf))
|
|
||||||
|
|
||||||
for intf in want:
|
|
||||||
intf_in_have = search_obj_in_list(intf['name'], have)
|
|
||||||
commands.extend(self._state_replaced(intf, intf_in_have))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(remove_empties(have))
|
|
||||||
|
|
||||||
want_vifs = want_copy.pop('vifs', [])
|
|
||||||
have_vifs = have_copy.pop('vifs', [])
|
|
||||||
|
|
||||||
for update in self._get_updates(want_copy, have_copy):
|
|
||||||
for key, value in iteritems(update):
|
|
||||||
commands.append(self._compute_commands(key=key, value=value, interface=want_copy['name']))
|
|
||||||
|
|
||||||
if want_vifs:
|
|
||||||
for want_vif in want_vifs:
|
|
||||||
have_vif = search_obj_in_list(want_vif['vlan_id'], have_vifs, key='vlan_id')
|
|
||||||
if not have_vif:
|
|
||||||
have_vif = {}
|
|
||||||
|
|
||||||
for update in self._get_updates(want_vif, have_vif):
|
|
||||||
for key, value in iteritems(update):
|
|
||||||
commands.append(self._compute_commands(key=key, value=value, interface=want_copy['name'], vif=want_vif['vlan_id']))
|
|
||||||
|
|
||||||
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 = []
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(have)
|
|
||||||
|
|
||||||
want_vifs = want_copy.pop('vifs', [])
|
|
||||||
have_vifs = have_copy.pop('vifs', [])
|
|
||||||
|
|
||||||
for update in self._get_updates(have_copy, want_copy):
|
|
||||||
for key, value in iteritems(update):
|
|
||||||
commands.append(self._compute_commands(key=key, value=value, interface=want_copy['name'], remove=True))
|
|
||||||
|
|
||||||
if have_vifs:
|
|
||||||
for have_vif in have_vifs:
|
|
||||||
want_vif = search_obj_in_list(have_vif['vlan_id'], want_vifs, key='vlan_id')
|
|
||||||
if not want_vif:
|
|
||||||
want_vif = {'vlan_id': have_vif['vlan_id']}
|
|
||||||
|
|
||||||
for update in self._get_updates(have_vif, want_vif):
|
|
||||||
for key, value in iteritems(update):
|
|
||||||
commands.append(self._compute_commands(key=key, interface=want_copy['name'], value=value, vif=want_vif['vlan_id'], remove=True))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _compute_commands(self, interface, key, vif=None, value=None, remove=False):
|
|
||||||
intf_context = 'interfaces {0} {1}'.format(get_interface_type(interface), interface)
|
|
||||||
set_cmd = 'set {0}'.format(intf_context)
|
|
||||||
del_cmd = 'delete {0}'.format(intf_context)
|
|
||||||
|
|
||||||
if vif:
|
|
||||||
set_cmd = set_cmd + (' vif {0}'.format(vif))
|
|
||||||
del_cmd = del_cmd + (' vif {0}'.format(vif))
|
|
||||||
|
|
||||||
if remove:
|
|
||||||
command = "{0} {1} '{2}'".format(del_cmd, key, value)
|
|
||||||
else:
|
|
||||||
command = "{0} {1} '{2}'".format(set_cmd, key, value)
|
|
||||||
|
|
||||||
return command
|
|
||||||
|
|
||||||
def _get_updates(self, want, have):
|
|
||||||
updates = []
|
|
||||||
|
|
||||||
updates = diff_list_of_dicts(want.get('ipv4', []), have.get('ipv4', []))
|
|
||||||
updates.extend(diff_list_of_dicts(want.get('ipv6', []), have.get('ipv6', [])))
|
|
||||||
|
|
||||||
return updates
|
|
@ -1,389 +0,0 @@
|
|||||||
# Copyright 2019 Red Hat
|
|
||||||
# GNU General Public License v3.0+
|
|
||||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""
|
|
||||||
The vyos_lag_interfaces class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, dict_diff
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network. \
|
|
||||||
vyos.utils.utils import search_obj_in_list, \
|
|
||||||
get_lst_diff_for_dicts, list_diff_want_only, list_diff_have_only
|
|
||||||
|
|
||||||
|
|
||||||
class Lag_interfaces(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_lag_interfaces class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'lag_interfaces',
|
|
||||||
]
|
|
||||||
|
|
||||||
params = ['arp_monitor', 'hash_policy', 'members', 'mode', 'name', 'primary']
|
|
||||||
|
|
||||||
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 self._module.check_mode:
|
|
||||||
resp = self._connection.edit_config(commands, commit=False)
|
|
||||||
else:
|
|
||||||
resp = self._connection.edit_config(commands)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if self._module._diff:
|
|
||||||
result['diff'] = resp['diff'] if result['changed'] else None
|
|
||||||
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
state = self._module.params['state']
|
|
||||||
if state in ('merged', 'replaced', 'overridden') and not want:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state))
|
|
||||||
if state == 'overridden':
|
|
||||||
commands.extend(self._state_overridden(want, have))
|
|
||||||
elif state == 'deleted':
|
|
||||||
if want:
|
|
||||||
for want_item in want:
|
|
||||||
name = want_item['name']
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
commands.extend(self._state_deleted(obj_in_have))
|
|
||||||
else:
|
|
||||||
for have_item in have:
|
|
||||||
commands.extend(self._state_deleted(have_item))
|
|
||||||
else:
|
|
||||||
for want_item in want:
|
|
||||||
name = want_item['name']
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
if state == 'merged':
|
|
||||||
commands.extend(self._state_merged(want_item, obj_in_have))
|
|
||||||
elif state == 'replaced':
|
|
||||||
commands.extend(self._state_replaced(want_item, obj_in_have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._render_del_commands(want, have))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_overridden(self, want, have):
|
|
||||||
""" The command generator when state is overridden
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
for have_item in have:
|
|
||||||
lag_name = have_item['name']
|
|
||||||
obj_in_want = search_obj_in_list(lag_name, want)
|
|
||||||
if not obj_in_want:
|
|
||||||
commands.extend(self._purge_attribs(have_item))
|
|
||||||
|
|
||||||
for want_item in want:
|
|
||||||
name = want_item['name']
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
commands.extend(self._state_replaced(want_item, obj_in_have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._render_updates(want, have))
|
|
||||||
else:
|
|
||||||
commands.extend(self._render_set_commands(want))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_deleted(self, have):
|
|
||||||
""" The command generator when state is deleted
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to remove the current configuration
|
|
||||||
of the provided objects
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._purge_attribs(have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_updates(self, want, have):
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
temp_have_members = have.pop('members', None)
|
|
||||||
temp_want_members = want.pop('members', None)
|
|
||||||
|
|
||||||
updates = dict_diff(have, want)
|
|
||||||
|
|
||||||
if temp_have_members:
|
|
||||||
have['members'] = temp_have_members
|
|
||||||
if temp_want_members:
|
|
||||||
want['members'] = temp_want_members
|
|
||||||
|
|
||||||
commands.extend(self._add_bond_members(want, have))
|
|
||||||
|
|
||||||
if updates:
|
|
||||||
for key, value in iteritems(updates):
|
|
||||||
if value:
|
|
||||||
if key == 'arp_monitor':
|
|
||||||
commands.extend(
|
|
||||||
self._add_arp_monitor(updates, key, want, have)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(self._compute_command(have['name'], key, str(value)))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_set_commands(self, want):
|
|
||||||
commands = []
|
|
||||||
have = []
|
|
||||||
|
|
||||||
params = Lag_interfaces.params
|
|
||||||
|
|
||||||
for attrib in params:
|
|
||||||
value = want[attrib]
|
|
||||||
if value:
|
|
||||||
if attrib == 'arp_monitor':
|
|
||||||
commands.extend(
|
|
||||||
self._add_arp_monitor(want, attrib, want, have)
|
|
||||||
)
|
|
||||||
elif attrib == 'members':
|
|
||||||
commands.extend(
|
|
||||||
self._add_bond_members(want, have)
|
|
||||||
)
|
|
||||||
elif attrib != 'name':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(want['name'], attrib, value=str(value))
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _purge_attribs(self, have):
|
|
||||||
commands = []
|
|
||||||
for item in Lag_interfaces.params:
|
|
||||||
if have.get(item):
|
|
||||||
if item == 'members':
|
|
||||||
commands.extend(
|
|
||||||
self._delete_bond_members(have)
|
|
||||||
)
|
|
||||||
elif item != 'name':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(have['name'], attrib=item, remove=True)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_del_commands(self, want, have):
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
params = Lag_interfaces.params
|
|
||||||
for attrib in params:
|
|
||||||
if attrib == 'members':
|
|
||||||
commands.extend(
|
|
||||||
self._update_bond_members(attrib, want, have)
|
|
||||||
)
|
|
||||||
elif attrib == 'arp_monitor':
|
|
||||||
commands.extend(
|
|
||||||
self._update_arp_monitor(attrib, want, have)
|
|
||||||
)
|
|
||||||
elif have.get(attrib) and not want.get(attrib):
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(have['name'], attrib, remove=True)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_bond_members(self, want, have):
|
|
||||||
commands = []
|
|
||||||
diff_members = get_lst_diff_for_dicts(want, have, 'members')
|
|
||||||
if diff_members:
|
|
||||||
for key in diff_members:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(key['member'], 'bond-group', want['name'], type='ethernet')
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_arp_monitor(self, updates, key, want, have):
|
|
||||||
commands = []
|
|
||||||
arp_monitor = updates.get(key) or {}
|
|
||||||
diff_targets = self._get_arp_monitor_target_diff(want, have, key, 'target')
|
|
||||||
|
|
||||||
if 'interval' in arp_monitor:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
key=want['name'] + ' arp-monitor', attrib='interval', value=str(arp_monitor['interval'])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if diff_targets:
|
|
||||||
for target in diff_targets:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(key=want['name'] + ' arp-monitor', attrib='target', value=target)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _delete_bond_members(self, have):
|
|
||||||
commands = []
|
|
||||||
for member in have['members']:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
member['member'], 'bond-group', have['name'], remove=True, type='ethernet'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _update_arp_monitor(self, key, want, have):
|
|
||||||
commands = []
|
|
||||||
want_arp_target = []
|
|
||||||
have_arp_target = []
|
|
||||||
want_arp_monitor = want.get(key) or {}
|
|
||||||
have_arp_monitor = have.get(key) or {}
|
|
||||||
|
|
||||||
if want_arp_monitor and 'target' in want_arp_monitor:
|
|
||||||
want_arp_target = want_arp_monitor['target']
|
|
||||||
|
|
||||||
if have_arp_monitor and 'target' in have_arp_monitor:
|
|
||||||
have_arp_target = have_arp_monitor['target']
|
|
||||||
|
|
||||||
if 'interval' in have_arp_monitor and not want_arp_monitor:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
key=have['name'] + ' arp-monitor', attrib='interval', remove=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if 'target' in have_arp_monitor:
|
|
||||||
target_diff = list_diff_have_only(want_arp_target, have_arp_target)
|
|
||||||
if target_diff:
|
|
||||||
for target in target_diff:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
key=have['name'] + ' arp-monitor', attrib='target', value=target, remove=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _update_bond_members(self, key, want, have):
|
|
||||||
commands = []
|
|
||||||
want_members = want.get(key) or []
|
|
||||||
have_members = have.get(key) or []
|
|
||||||
|
|
||||||
members_diff = list_diff_have_only(want_members, have_members)
|
|
||||||
if members_diff:
|
|
||||||
for member in members_diff:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
member['member'], 'bond-group', have['name'], True, 'ethernet'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _get_arp_monitor_target_diff(self, want_list, have_list, dict_name, lst):
|
|
||||||
want_arp_target = []
|
|
||||||
have_arp_target = []
|
|
||||||
|
|
||||||
want_arp_monitor = want_list.get(dict_name) or {}
|
|
||||||
if want_arp_monitor and lst in want_arp_monitor:
|
|
||||||
want_arp_target = want_arp_monitor[lst]
|
|
||||||
|
|
||||||
if not have_list:
|
|
||||||
diff = want_arp_target
|
|
||||||
else:
|
|
||||||
have_arp_monitor = have_list.get(dict_name) or {}
|
|
||||||
if have_arp_monitor and lst in have_arp_monitor:
|
|
||||||
have_arp_target = have_arp_monitor[lst]
|
|
||||||
|
|
||||||
diff = list_diff_want_only(want_arp_target, have_arp_target)
|
|
||||||
return diff
|
|
||||||
|
|
||||||
def _compute_command(self, key, attrib, value=None, remove=False, type='bonding'):
|
|
||||||
if remove:
|
|
||||||
cmd = 'delete interfaces ' + type
|
|
||||||
else:
|
|
||||||
cmd = 'set interfaces ' + type
|
|
||||||
cmd += (' ' + key)
|
|
||||||
if attrib == 'arp_monitor':
|
|
||||||
attrib = 'arp-monitor'
|
|
||||||
elif attrib == 'hash_policy':
|
|
||||||
attrib = 'hash-policy'
|
|
||||||
cmd += (' ' + attrib)
|
|
||||||
if value:
|
|
||||||
cmd += (" '" + value + "'")
|
|
||||||
return cmd
|
|
@ -1,240 +0,0 @@
|
|||||||
# Copyright 2019 Red Hat
|
|
||||||
# GNU General Public License v3.0+
|
|
||||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""
|
|
||||||
The vyos_lldp_global class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, dict_diff
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network.vyos.utils.utils import get_lst_diff_for_dicts, list_diff_have_only
|
|
||||||
|
|
||||||
|
|
||||||
class Lldp_global(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_lldp_global class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'lldp_global',
|
|
||||||
]
|
|
||||||
|
|
||||||
params = ['enable', 'address', 'snmp', 'legacy_protocols']
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
super(Lldp_global, self).__init__(module)
|
|
||||||
|
|
||||||
def get_lldp_global_facts(self):
|
|
||||||
""" Get the 'facts' (the current configuration)
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The current configuration as a dictionary
|
|
||||||
"""
|
|
||||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset,
|
|
||||||
self.gather_network_resources)
|
|
||||||
lldp_global_facts = facts['ansible_network_resources'].get('lldp_global')
|
|
||||||
if not lldp_global_facts:
|
|
||||||
return {}
|
|
||||||
return lldp_global_facts
|
|
||||||
|
|
||||||
def execute_module(self):
|
|
||||||
""" Execute the module
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The result from module execution
|
|
||||||
"""
|
|
||||||
result = {'changed': False}
|
|
||||||
commands = list()
|
|
||||||
warnings = list()
|
|
||||||
|
|
||||||
existing_lldp_global_facts = self.get_lldp_global_facts()
|
|
||||||
commands.extend(self.set_config(existing_lldp_global_facts))
|
|
||||||
if commands:
|
|
||||||
if not self._module.check_mode:
|
|
||||||
self._connection.edit_config(commands)
|
|
||||||
result['changed'] = True
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
changed_lldp_global_facts = self.get_lldp_global_facts()
|
|
||||||
|
|
||||||
result['before'] = existing_lldp_global_facts
|
|
||||||
if result['changed']:
|
|
||||||
result['after'] = changed_lldp_global_facts
|
|
||||||
|
|
||||||
result['warnings'] = warnings
|
|
||||||
return result
|
|
||||||
|
|
||||||
def set_config(self, existing_lldp_global_facts):
|
|
||||||
""" Collect the configuration from the args passed to the module,
|
|
||||||
collect the current configuration (as a dict from facts)
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
want = self._module.params['config']
|
|
||||||
have = existing_lldp_global_facts
|
|
||||||
resp = self.set_state(want, have)
|
|
||||||
return to_list(resp)
|
|
||||||
|
|
||||||
def set_state(self, want, have):
|
|
||||||
""" Select the appropriate function based on the state provided
|
|
||||||
|
|
||||||
:param want: the desired configuration as a dictionary
|
|
||||||
:param have: the current configuration as a dictionary
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
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 == 'deleted':
|
|
||||||
commands.extend(self._state_deleted(want=None, have=have))
|
|
||||||
elif state == 'merged':
|
|
||||||
commands.extend(self._state_merged(want=want, have=have))
|
|
||||||
elif state == 'replaced':
|
|
||||||
commands.extend(self._state_replaced(want=want, have=have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._state_deleted(want, have))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
commands.extend(self._render_updates(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_deleted(self, want, have):
|
|
||||||
""" The command generator when state is deleted
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to remove the current configuration
|
|
||||||
of the provided objects
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if want:
|
|
||||||
for item in Lldp_global.params:
|
|
||||||
if item == 'legacy_protocols':
|
|
||||||
commands.extend(self._update_lldp_protocols(want, have))
|
|
||||||
elif have.get(item) and not want.get(item) and item != 'enable':
|
|
||||||
commands.append(Lldp_global.del_cmd + item)
|
|
||||||
elif have:
|
|
||||||
for item in Lldp_global.params:
|
|
||||||
if have.get(item):
|
|
||||||
if item == 'legacy_protocols':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command('legacy-protocols', remove=True)
|
|
||||||
)
|
|
||||||
elif item == 'address':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command('management-address', remove=True)
|
|
||||||
)
|
|
||||||
elif item == 'snmp':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(item, remove=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_updates(self, want, have):
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
temp_have_legacy_protos = have.pop('legacy_protocols', None)
|
|
||||||
else:
|
|
||||||
have = {}
|
|
||||||
temp_want_legacy_protos = want.pop('legacy_protocols', None)
|
|
||||||
|
|
||||||
updates = dict_diff(have, want)
|
|
||||||
|
|
||||||
if have and temp_have_legacy_protos:
|
|
||||||
have['legacy_protocols'] = temp_have_legacy_protos
|
|
||||||
if not have and temp_want_legacy_protos:
|
|
||||||
want['legacy_protocols'] = temp_want_legacy_protos
|
|
||||||
|
|
||||||
commands.extend(self._add_lldp_protocols(want, have))
|
|
||||||
|
|
||||||
if updates:
|
|
||||||
for key, value in iteritems(updates):
|
|
||||||
if value:
|
|
||||||
if key == 'enable':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command()
|
|
||||||
)
|
|
||||||
elif key == 'address':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command('management-address', str(value))
|
|
||||||
)
|
|
||||||
elif key == 'snmp':
|
|
||||||
if value == 'disable':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(key, remove=True)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(key, str(value))
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_lldp_protocols(self, want, have):
|
|
||||||
commands = []
|
|
||||||
diff_members = get_lst_diff_for_dicts(want, have, 'legacy_protocols')
|
|
||||||
for key in diff_members:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command('legacy-protocols', key)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _update_lldp_protocols(self, want_item, have_item):
|
|
||||||
commands = []
|
|
||||||
want_protocols = want_item.get('legacy_protocols') or []
|
|
||||||
have_protocols = have_item.get('legacy_protocols') or []
|
|
||||||
|
|
||||||
members_diff = list_diff_have_only(want_protocols, have_protocols)
|
|
||||||
if members_diff:
|
|
||||||
for member in members_diff:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command('legacy-protocols', member, remove=True)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _compute_command(self, key=None, value=None, remove=False):
|
|
||||||
if remove:
|
|
||||||
cmd = 'delete service lldp'
|
|
||||||
else:
|
|
||||||
cmd = 'set service lldp'
|
|
||||||
if key:
|
|
||||||
cmd += (' ' + key)
|
|
||||||
|
|
||||||
if value:
|
|
||||||
cmd += (" '" + value + "'")
|
|
||||||
return cmd
|
|
@ -1,396 +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 vyos_lldp_interfaces class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, dict_diff
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network. vyos.utils.utils import search_obj_in_list, \
|
|
||||||
search_dict_tv_in_list, key_value_in_dict, is_dict_element_present
|
|
||||||
|
|
||||||
|
|
||||||
class Lldp_interfaces(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_lldp_interfaces class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'lldp_interfaces',
|
|
||||||
]
|
|
||||||
|
|
||||||
params = ['enable', 'location', 'name']
|
|
||||||
|
|
||||||
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 self._module.check_mode:
|
|
||||||
resp = self._connection.edit_config(commands, commit=False)
|
|
||||||
else:
|
|
||||||
resp = self._connection.edit_config(commands)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if self._module._diff:
|
|
||||||
result['diff'] = resp['diff'] if result['changed'] else None
|
|
||||||
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
state = self._module.params['state']
|
|
||||||
if state in ('merged', 'replaced', 'overridden') and not want:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state))
|
|
||||||
if state == 'overridden':
|
|
||||||
commands.extend(self._state_overridden(want=want, have=have))
|
|
||||||
elif state == 'deleted':
|
|
||||||
if want:
|
|
||||||
for item in want:
|
|
||||||
name = item['name']
|
|
||||||
have_item = search_obj_in_list(name, have)
|
|
||||||
commands.extend(self._state_deleted(want=None, have=have_item))
|
|
||||||
else:
|
|
||||||
for have_item in have:
|
|
||||||
commands.extend(self._state_deleted(want=None, have=have_item))
|
|
||||||
else:
|
|
||||||
for want_item in want:
|
|
||||||
name = want_item['name']
|
|
||||||
have_item = search_obj_in_list(name, have)
|
|
||||||
if state == 'merged':
|
|
||||||
commands.extend(self._state_merged(want=want_item, have=have_item))
|
|
||||||
else:
|
|
||||||
commands.extend(self._state_replaced(want=want_item, have=have_item))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._state_deleted(want, have))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_overridden(self, want, have):
|
|
||||||
""" The command generator when state is overridden
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
for have_item in have:
|
|
||||||
lldp_name = have_item['name']
|
|
||||||
lldp_in_want = search_obj_in_list(lldp_name, want)
|
|
||||||
if not lldp_in_want:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(have_item['name'], remove=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
for want_item in want:
|
|
||||||
name = want_item['name']
|
|
||||||
lldp_in_have = search_obj_in_list(name, have)
|
|
||||||
commands.extend(self._state_replaced(want_item, lldp_in_have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._render_updates(want, have))
|
|
||||||
else:
|
|
||||||
commands.extend(self._render_set_commands(want))
|
|
||||||
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:
|
|
||||||
params = Lldp_interfaces.params
|
|
||||||
for attrib in params:
|
|
||||||
if attrib == 'location':
|
|
||||||
commands.extend(self._update_location(have['name'], want, have))
|
|
||||||
|
|
||||||
elif have:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(have['name'], remove=True)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_updates(self, want, have):
|
|
||||||
commands = []
|
|
||||||
lldp_name = have['name']
|
|
||||||
commands.extend(self._configure_status(lldp_name, want, have))
|
|
||||||
commands.extend(self._add_location(lldp_name, want, have))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_set_commands(self, want):
|
|
||||||
commands = []
|
|
||||||
have = {}
|
|
||||||
lldp_name = want['name']
|
|
||||||
params = Lldp_interfaces.params
|
|
||||||
|
|
||||||
commands.extend(self._add_location(lldp_name, want, have))
|
|
||||||
for attrib in params:
|
|
||||||
value = want[attrib]
|
|
||||||
if value:
|
|
||||||
if attrib == 'location':
|
|
||||||
commands.extend(self._add_location(lldp_name, want, have))
|
|
||||||
elif attrib == 'enable':
|
|
||||||
if not value:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(lldp_name, value='disable')
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(lldp_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _configure_status(self, name, want_item, have_item):
|
|
||||||
commands = []
|
|
||||||
if is_dict_element_present(have_item, 'enable'):
|
|
||||||
temp_have_item = False
|
|
||||||
else:
|
|
||||||
temp_have_item = True
|
|
||||||
if want_item['enable'] != temp_have_item:
|
|
||||||
if want_item['enable']:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(name, value='disable', remove=True)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(name, value='disable')
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_location(self, name, want_item, have_item):
|
|
||||||
commands = []
|
|
||||||
have_dict = {}
|
|
||||||
have_ca = {}
|
|
||||||
set_cmd = name + ' location '
|
|
||||||
want_location_type = want_item.get('location') or {}
|
|
||||||
have_location_type = have_item.get('location') or {}
|
|
||||||
|
|
||||||
if want_location_type['coordinate_based']:
|
|
||||||
want_dict = want_location_type.get('coordinate_based') or {}
|
|
||||||
if is_dict_element_present(have_location_type, 'coordinate_based'):
|
|
||||||
have_dict = have_location_type.get('coordinate_based') or {}
|
|
||||||
location_type = 'coordinate-based'
|
|
||||||
updates = dict_diff(have_dict, want_dict)
|
|
||||||
for key, value in iteritems(updates):
|
|
||||||
if value:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(set_cmd + location_type, key, str(value))
|
|
||||||
)
|
|
||||||
|
|
||||||
elif want_location_type['civic_based']:
|
|
||||||
location_type = 'civic-based'
|
|
||||||
want_dict = want_location_type.get('civic_based') or {}
|
|
||||||
want_ca = want_dict.get('ca_info') or []
|
|
||||||
if is_dict_element_present(have_location_type, 'civic_based'):
|
|
||||||
have_dict = have_location_type.get('civic_based') or {}
|
|
||||||
have_ca = have_dict.get('ca_info') or []
|
|
||||||
if want_dict['country_code'] != have_dict['country_code']:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
set_cmd + location_type, 'country-code', str(want_dict['country_code'])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
set_cmd + location_type, 'country-code', str(want_dict['country_code'])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
commands.extend(self._add_civic_address(name, want_ca, have_ca))
|
|
||||||
|
|
||||||
elif want_location_type['elin']:
|
|
||||||
location_type = 'elin'
|
|
||||||
if is_dict_element_present(have_location_type, 'elin'):
|
|
||||||
if want_location_type.get('elin') != have_location_type.get('elin'):
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
set_cmd + location_type, value=str(want_location_type['elin'])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
set_cmd + location_type, value=str(want_location_type['elin'])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _update_location(self, name, want_item, have_item):
|
|
||||||
commands = []
|
|
||||||
del_cmd = name + ' location'
|
|
||||||
want_location_type = want_item.get('location') or {}
|
|
||||||
have_location_type = have_item.get('location') or {}
|
|
||||||
|
|
||||||
if want_location_type['coordinate_based']:
|
|
||||||
want_dict = want_location_type.get('coordinate_based') or {}
|
|
||||||
if is_dict_element_present(have_location_type, 'coordinate_based'):
|
|
||||||
have_dict = have_location_type.get('coordinate_based') or {}
|
|
||||||
location_type = 'coordinate-based'
|
|
||||||
for key, value in iteritems(have_dict):
|
|
||||||
only_in_have = key_value_in_dict(key, value, want_dict)
|
|
||||||
if not only_in_have:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(del_cmd + location_type, key, str(value), True)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(del_cmd, remove=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif want_location_type['civic_based']:
|
|
||||||
want_dict = want_location_type.get('civic_based') or {}
|
|
||||||
want_ca = want_dict.get('ca_info') or []
|
|
||||||
if is_dict_element_present(have_location_type, 'civic_based'):
|
|
||||||
have_dict = have_location_type.get('civic_based') or {}
|
|
||||||
have_ca = have_dict.get('ca_info')
|
|
||||||
commands.extend(self._update_civic_address(name, want_ca, have_ca))
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(del_cmd, remove=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
if is_dict_element_present(have_location_type, 'elin'):
|
|
||||||
if want_location_type.get('elin') != have_location_type.get('elin'):
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(del_cmd, remove=True)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(del_cmd, remove=True)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_civic_address(self, name, want, have):
|
|
||||||
commands = []
|
|
||||||
for item in want:
|
|
||||||
ca_type = item['ca_type']
|
|
||||||
ca_value = item['ca_value']
|
|
||||||
obj_in_have = search_dict_tv_in_list(ca_type, ca_value, have, 'ca_type', 'ca_value')
|
|
||||||
if not obj_in_have:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
key=name + ' location civic-based ca-type',
|
|
||||||
attrib=str(ca_type) + ' ca-value', value=ca_value)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _update_civic_address(self, name, want, have):
|
|
||||||
commands = []
|
|
||||||
for item in have:
|
|
||||||
ca_type = item['ca_type']
|
|
||||||
ca_value = item['ca_value']
|
|
||||||
in_want = search_dict_tv_in_list(ca_type, ca_value, want, 'ca_type', 'ca_value')
|
|
||||||
if not in_want:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(
|
|
||||||
name, 'location civic-based ca-type', str(ca_type), remove=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _compute_command(self, key, attrib=None, value=None, remove=False):
|
|
||||||
if remove:
|
|
||||||
cmd = 'delete service lldp interface '
|
|
||||||
else:
|
|
||||||
cmd = 'set service lldp interface '
|
|
||||||
cmd += (key)
|
|
||||||
if attrib:
|
|
||||||
cmd += (' ' + attrib)
|
|
||||||
if value:
|
|
||||||
cmd += (" '" + value + "'")
|
|
||||||
return cmd
|
|
@ -1,523 +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 vyos_static_routes class
|
|
||||||
It is in this file where the current configuration (as dict)
|
|
||||||
is compared to the provided configuration (as dict) and the command set
|
|
||||||
necessary to bring the current configuration to it's desired end-state is
|
|
||||||
created
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
|
||||||
from ansible.module_utils.network.common.utils import to_list, dict_diff, remove_empties
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network. vyos.utils.utils import get_route_type, \
|
|
||||||
get_lst_diff_for_dicts, get_lst_same_for_dicts, dict_delete
|
|
||||||
|
|
||||||
|
|
||||||
class Static_routes(ConfigBase):
|
|
||||||
"""
|
|
||||||
The vyos_static_routes class
|
|
||||||
"""
|
|
||||||
|
|
||||||
gather_subset = [
|
|
||||||
'!all',
|
|
||||||
'!min',
|
|
||||||
]
|
|
||||||
|
|
||||||
gather_network_resources = [
|
|
||||||
'static_routes',
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
super(Static_routes, self).__init__(module)
|
|
||||||
|
|
||||||
def get_static_routes_facts(self, data=None):
|
|
||||||
""" Get the 'facts' (the current configuration)
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The current configuration as a dictionary
|
|
||||||
"""
|
|
||||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources, data=data)
|
|
||||||
static_routes_facts = facts['ansible_network_resources'].get('static_routes')
|
|
||||||
if not static_routes_facts:
|
|
||||||
return []
|
|
||||||
return static_routes_facts
|
|
||||||
|
|
||||||
def execute_module(self):
|
|
||||||
""" Execute the module
|
|
||||||
|
|
||||||
:rtype: A dictionary
|
|
||||||
:returns: The result from module execution
|
|
||||||
"""
|
|
||||||
result = {'changed': False}
|
|
||||||
warnings = list()
|
|
||||||
commands = list()
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
existing_static_routes_facts = self.get_static_routes_facts()
|
|
||||||
else:
|
|
||||||
existing_static_routes_facts = []
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES or self.state == 'rendered':
|
|
||||||
commands.extend(self.set_config(existing_static_routes_facts))
|
|
||||||
|
|
||||||
if commands and self.state in self.ACTION_STATES:
|
|
||||||
if not self._module.check_mode:
|
|
||||||
self._connection.edit_config(commands)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES:
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if self.state in self.ACTION_STATES or self.state == 'gathered':
|
|
||||||
changed_static_routes_facts = self.get_static_routes_facts()
|
|
||||||
elif self.state == 'rendered':
|
|
||||||
result['rendered'] = commands
|
|
||||||
elif self.state == 'parsed':
|
|
||||||
running_config = self._module.params['running_config']
|
|
||||||
if not running_config:
|
|
||||||
self._module.fail_json(
|
|
||||||
msg="value of running_config parameter must not be empty for state parsed"
|
|
||||||
)
|
|
||||||
result['parsed'] = self.get_static_routes_facts(data=running_config)
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if self.state in ('merged', 'replaced', 'overridden', 'rendered') and not want:
|
|
||||||
self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(self.state))
|
|
||||||
if self.state == 'overridden':
|
|
||||||
commands.extend(self._state_overridden(want=want, have=have))
|
|
||||||
elif self.state == 'deleted':
|
|
||||||
commands.extend(self._state_deleted(want=want, have=have))
|
|
||||||
elif want:
|
|
||||||
routes = self._get_routes(want)
|
|
||||||
for r in routes:
|
|
||||||
h_item = self.search_route_in_have(have, r['dest'])
|
|
||||||
if self.state == 'merged' or self.state == 'rendered':
|
|
||||||
commands.extend(self._state_merged(want=r, have=h_item))
|
|
||||||
elif self.state == 'replaced':
|
|
||||||
commands.extend(self._state_replaced(want=r, have=h_item))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def search_route_in_have(self, have, want_dest):
|
|
||||||
"""
|
|
||||||
This function returns the route if its found in
|
|
||||||
have config.
|
|
||||||
:param have:
|
|
||||||
:param dest:
|
|
||||||
:return: the matched route
|
|
||||||
"""
|
|
||||||
routes = self._get_routes(have)
|
|
||||||
for r in routes:
|
|
||||||
if r['dest'] == want_dest:
|
|
||||||
return r
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _state_replaced(self, want, have):
|
|
||||||
""" The command generator when state is replaced
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
for key, value in iteritems(want):
|
|
||||||
if value:
|
|
||||||
if key == 'next_hops':
|
|
||||||
commands.extend(self._update_next_hop(want, have))
|
|
||||||
elif key == 'blackhole_config':
|
|
||||||
commands.extend(self._update_blackhole(key, want, have))
|
|
||||||
commands.extend(self._state_merged(want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_overridden(self, want, have):
|
|
||||||
""" The command generator when state is overridden
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to migrate the current configuration
|
|
||||||
to the desired configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
routes = self._get_routes(have)
|
|
||||||
for r in routes:
|
|
||||||
route_in_want = self.search_route_in_have(want, r['dest'])
|
|
||||||
if not route_in_want:
|
|
||||||
commands.append(self._compute_command(r['dest'], remove=True))
|
|
||||||
routes = self._get_routes(want)
|
|
||||||
for r in routes:
|
|
||||||
route_in_have = self.search_route_in_have(have, r['dest'])
|
|
||||||
commands.extend(self._state_replaced(r, route_in_have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _state_merged(self, want, have, opr=True):
|
|
||||||
""" The command generator when state is merged
|
|
||||||
|
|
||||||
:rtype: A list
|
|
||||||
:returns: the commands necessary to merge the provided into
|
|
||||||
the current configuration
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
if have:
|
|
||||||
commands.extend(self._render_updates(want, have))
|
|
||||||
else:
|
|
||||||
commands.extend(self._render_set_commands(want))
|
|
||||||
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:
|
|
||||||
routes = self._get_routes(want)
|
|
||||||
if not routes:
|
|
||||||
for w in want:
|
|
||||||
af = w['address_families']
|
|
||||||
for item in af:
|
|
||||||
if self.afi_in_have(have, item):
|
|
||||||
commands.append(self._compute_command(afi=item['afi'], remove=True))
|
|
||||||
for r in routes:
|
|
||||||
h_route = self.search_route_in_have(have, r['dest'])
|
|
||||||
if h_route:
|
|
||||||
commands.extend(self._render_updates(r, h_route, opr=False))
|
|
||||||
else:
|
|
||||||
routes = self._get_routes(have)
|
|
||||||
if self._is_ip_route_exist(routes):
|
|
||||||
commands.append(self._compute_command(afi='ipv4', remove=True))
|
|
||||||
if self._is_ip_route_exist(routes, 'route6'):
|
|
||||||
commands.append(self._compute_command(afi='ipv6', remove=True))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_set_commands(self, want):
|
|
||||||
"""
|
|
||||||
This function returns the list of commands to add attributes which are
|
|
||||||
present in want
|
|
||||||
:param want:
|
|
||||||
:return: list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
have = {}
|
|
||||||
for key, value in iteritems(want):
|
|
||||||
if value:
|
|
||||||
if key == 'dest':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'])
|
|
||||||
)
|
|
||||||
elif key == 'blackhole_config':
|
|
||||||
commands.extend(self._add_blackhole(key, want, have))
|
|
||||||
|
|
||||||
elif key == 'next_hops':
|
|
||||||
commands.extend(self._add_next_hop(want, have))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_blackhole(self, key, want, have):
|
|
||||||
"""
|
|
||||||
This function gets the diff for blackhole config specific attributes
|
|
||||||
and form the commands for attributes which are present in want but not in have.
|
|
||||||
:param key:
|
|
||||||
:param want:
|
|
||||||
:param have:
|
|
||||||
:return: list of commands
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(remove_empties(have))
|
|
||||||
|
|
||||||
want_blackhole = want_copy.get(key) or {}
|
|
||||||
have_blackhole = have_copy.get(key) or {}
|
|
||||||
|
|
||||||
updates = dict_delete(want_blackhole, have_blackhole)
|
|
||||||
if updates:
|
|
||||||
for attrib, value in iteritems(updates):
|
|
||||||
if value:
|
|
||||||
if attrib == 'distance':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'], key='blackhole',
|
|
||||||
attrib=attrib, remove=False, value=str(value))
|
|
||||||
)
|
|
||||||
elif attrib == 'type':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'], key='blackhole')
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _add_next_hop(self, want, have, opr=True):
|
|
||||||
"""
|
|
||||||
This function gets the diff for next hop specific attributes
|
|
||||||
and form the commands to add attributes which are present in want but not in have.
|
|
||||||
:param want:
|
|
||||||
:param have:
|
|
||||||
:return: list of commands.
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(remove_empties(have))
|
|
||||||
if not opr:
|
|
||||||
diff_next_hops = get_lst_same_for_dicts(want_copy, have_copy, 'next_hops')
|
|
||||||
else:
|
|
||||||
diff_next_hops = get_lst_diff_for_dicts(want_copy, have_copy, 'next_hops')
|
|
||||||
if diff_next_hops:
|
|
||||||
for hop in diff_next_hops:
|
|
||||||
for element in hop:
|
|
||||||
if element == 'forward_router_address':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'],
|
|
||||||
key='next-hop',
|
|
||||||
value=hop[element],
|
|
||||||
opr=opr)
|
|
||||||
)
|
|
||||||
elif element == 'enabled' and not hop[element]:
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'],
|
|
||||||
key='next-hop',
|
|
||||||
attrib=hop['forward_router_address'],
|
|
||||||
value='disable',
|
|
||||||
opr=opr)
|
|
||||||
)
|
|
||||||
elif element == 'admin_distance':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'],
|
|
||||||
key='next-hop',
|
|
||||||
attrib=hop['forward_router_address'] + " " + element,
|
|
||||||
value=str(hop[element]),
|
|
||||||
opr=opr)
|
|
||||||
)
|
|
||||||
elif element == 'interface':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'],
|
|
||||||
key='next-hop',
|
|
||||||
attrib=hop['forward_router_address'] + " " + element,
|
|
||||||
value=hop[element],
|
|
||||||
opr=opr)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _update_blackhole(self, key, want, have):
|
|
||||||
"""
|
|
||||||
This function gets the difference for blackhole dict and
|
|
||||||
form the commands to delete the attributes which are present in have but not in want.
|
|
||||||
:param want:
|
|
||||||
:param have:
|
|
||||||
:return: list of commands
|
|
||||||
:param key:
|
|
||||||
:param want:
|
|
||||||
:param have:
|
|
||||||
:return: list of commands
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(remove_empties(have))
|
|
||||||
|
|
||||||
want_blackhole = want_copy.get(key) or {}
|
|
||||||
have_blackhole = have_copy.get(key) or {}
|
|
||||||
updates = dict_delete(have_blackhole, want_blackhole)
|
|
||||||
if updates:
|
|
||||||
for attrib, value in iteritems(updates):
|
|
||||||
if value:
|
|
||||||
if attrib == 'distance':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'], key='blackhole',
|
|
||||||
attrib=attrib, remove=True, value=str(value))
|
|
||||||
)
|
|
||||||
elif attrib == 'type' and 'distance' not in want_blackhole.keys():
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'], key='blackhole', remove=True)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _update_next_hop(self, want, have, opr=True):
|
|
||||||
"""
|
|
||||||
This function gets the difference for next_hops list and
|
|
||||||
form the commands to delete the attributes which are present in have but not in want.
|
|
||||||
:param want:
|
|
||||||
:param have:
|
|
||||||
:return: list of commands
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
want_copy = deepcopy(remove_empties(want))
|
|
||||||
have_copy = deepcopy(remove_empties(have))
|
|
||||||
|
|
||||||
diff_next_hops = get_lst_diff_for_dicts(have_copy, want_copy, 'next_hops')
|
|
||||||
if diff_next_hops:
|
|
||||||
for hop in diff_next_hops:
|
|
||||||
for element in hop:
|
|
||||||
if element == 'forward_router_address':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'], key='next-hop', value=hop[element], remove=True)
|
|
||||||
)
|
|
||||||
elif element == 'enabled':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'],
|
|
||||||
key='next-hop', attrib=hop['forward_router_address'], value='disable', remove=True)
|
|
||||||
)
|
|
||||||
elif element == 'admin_distance':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'], key='next-hop',
|
|
||||||
attrib=hop['forward_router_address'] + " " + element, value=str(hop[element]), remove=True)
|
|
||||||
)
|
|
||||||
elif element == 'interface':
|
|
||||||
commands.append(
|
|
||||||
self._compute_command(dest=want['dest'], key='next-hop',
|
|
||||||
attrib=hop['forward_router_address'] + " " + element, value=hop[element], remove=True)
|
|
||||||
)
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _render_updates(self, want, have, opr=True):
|
|
||||||
"""
|
|
||||||
This function takes the diff between want and have and
|
|
||||||
invokes the appropriate functions to create the commands
|
|
||||||
to update the attributes.
|
|
||||||
:param want:
|
|
||||||
:param have:
|
|
||||||
:return: list of commands
|
|
||||||
"""
|
|
||||||
commands = []
|
|
||||||
want_nh = want.get('next_hops') or []
|
|
||||||
# delete static route operation per destination
|
|
||||||
if not opr and not want_nh:
|
|
||||||
commands.append(self._compute_command(dest=want['dest'], remove=True))
|
|
||||||
|
|
||||||
else:
|
|
||||||
temp_have_next_hops = have.pop('next_hops', None)
|
|
||||||
temp_want_next_hops = want.pop('next_hops', None)
|
|
||||||
updates = dict_diff(have, want)
|
|
||||||
if temp_have_next_hops:
|
|
||||||
have['next_hops'] = temp_have_next_hops
|
|
||||||
if temp_want_next_hops:
|
|
||||||
want['next_hops'] = temp_want_next_hops
|
|
||||||
commands.extend(self._add_next_hop(want, have, opr=opr))
|
|
||||||
|
|
||||||
if opr and updates:
|
|
||||||
for key, value in iteritems(updates):
|
|
||||||
if value:
|
|
||||||
if key == 'blackhole_config':
|
|
||||||
commands.extend(self._add_blackhole(key, want, have))
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def _compute_command(self, dest=None, key=None, attrib=None, value=None, remove=False, afi=None, opr=True):
|
|
||||||
"""
|
|
||||||
This functions construct the required command based on the passed arguments.
|
|
||||||
:param dest:
|
|
||||||
:param key:
|
|
||||||
:param attrib:
|
|
||||||
:param value:
|
|
||||||
:param remove:
|
|
||||||
:return: constructed command
|
|
||||||
"""
|
|
||||||
if remove or not opr:
|
|
||||||
cmd = 'delete protocols static ' + self.get_route_type(dest, afi)
|
|
||||||
else:
|
|
||||||
cmd = 'set protocols static ' + self.get_route_type(dest, afi)
|
|
||||||
if dest:
|
|
||||||
cmd += (' ' + dest)
|
|
||||||
if key:
|
|
||||||
cmd += (' ' + key)
|
|
||||||
if attrib:
|
|
||||||
cmd += (' ' + attrib)
|
|
||||||
if value:
|
|
||||||
cmd += (" '" + value + "'")
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
def afi_in_have(self, have, w_item):
|
|
||||||
"""
|
|
||||||
This functions checks for the afi
|
|
||||||
list in have
|
|
||||||
:param have:
|
|
||||||
:param w_item:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if have:
|
|
||||||
for h in have:
|
|
||||||
af = h.get('address_families') or []
|
|
||||||
for item in af:
|
|
||||||
if w_item['afi'] == item['afi']:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_route_type(self, dest=None, afi=None):
|
|
||||||
"""
|
|
||||||
This function returns the route type based on
|
|
||||||
destination ip address or afi
|
|
||||||
:param address:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if dest:
|
|
||||||
return get_route_type(dest)
|
|
||||||
elif afi == 'ipv4':
|
|
||||||
return 'route'
|
|
||||||
elif afi == 'ipv6':
|
|
||||||
return 'route6'
|
|
||||||
|
|
||||||
def _is_ip_route_exist(self, routes, type='route'):
|
|
||||||
"""
|
|
||||||
This functions checks for the type of route.
|
|
||||||
:param routes:
|
|
||||||
:param type:
|
|
||||||
:return: True/False
|
|
||||||
"""
|
|
||||||
for r in routes:
|
|
||||||
if type == self.get_route_type(r['dest']):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _get_routes(self, lst):
|
|
||||||
"""
|
|
||||||
This function returns the list of routes
|
|
||||||
:param lst: list of address families
|
|
||||||
:return: list of routes
|
|
||||||
"""
|
|
||||||
r_list = []
|
|
||||||
for item in lst:
|
|
||||||
af = item['address_families']
|
|
||||||
for element in af:
|
|
||||||
routes = element.get('routes') or []
|
|
||||||
for r in routes:
|
|
||||||
r_list.append(r)
|
|
||||||
return r_list
|
|
@ -1,65 +0,0 @@
|
|||||||
# 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 vyos
|
|
||||||
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.vyos.facts.interfaces.interfaces import InterfacesFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import L3_interfacesFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.lldp_global.lldp_global import Lldp_globalFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.firewall_rules.firewall_rules import Firewall_rulesFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.static_routes.static_routes import Static_routesFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.firewall_global.firewall_global import Firewall_globalFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces import Firewall_interfacesFacts
|
|
||||||
from ansible.module_utils.network.vyos.facts.legacy.base import Default, Neighbors, Config
|
|
||||||
|
|
||||||
|
|
||||||
FACT_LEGACY_SUBSETS = dict(
|
|
||||||
default=Default,
|
|
||||||
neighbors=Neighbors,
|
|
||||||
config=Config
|
|
||||||
)
|
|
||||||
FACT_RESOURCE_SUBSETS = dict(
|
|
||||||
interfaces=InterfacesFacts,
|
|
||||||
l3_interfaces=L3_interfacesFacts,
|
|
||||||
lag_interfaces=Lag_interfacesFacts,
|
|
||||||
lldp_global=Lldp_globalFacts,
|
|
||||||
lldp_interfaces=Lldp_interfacesFacts,
|
|
||||||
static_routes=Static_routesFacts,
|
|
||||||
firewall_rules=Firewall_rulesFacts,
|
|
||||||
firewall_global=Firewall_globalFacts,
|
|
||||||
firewall_interfaces=Firewall_interfacesFacts
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Facts(FactsBase):
|
|
||||||
""" The fact class for vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
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 vyos
|
|
||||||
: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,360 +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 vyos firewall_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 re import findall, search, M
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.firewall_global.firewall_global import Firewall_globalArgs
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_globalFacts(object):
|
|
||||||
""" The vyos firewall_global fact class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, module, subspec='config', options='options'):
|
|
||||||
self._module = module
|
|
||||||
self.argument_spec = Firewall_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 get_device_data(self, connection):
|
|
||||||
return connection.get_config()
|
|
||||||
|
|
||||||
def populate_facts(self, connection, ansible_facts, data=None):
|
|
||||||
""" Populate the facts for firewall_global
|
|
||||||
:param connection: the device connection
|
|
||||||
:param ansible_facts: Facts dictionary
|
|
||||||
:param data: previously collected conf
|
|
||||||
:rtype: dictionary
|
|
||||||
:returns: facts
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
# typically data is populated from the current device configuration
|
|
||||||
# data = connection.get('show running-config | section ^interface')
|
|
||||||
# using mock data instead
|
|
||||||
data = self.get_device_data(connection)
|
|
||||||
objs = {}
|
|
||||||
firewalls = findall(r'^set firewall .*$', data, M)
|
|
||||||
if firewalls:
|
|
||||||
objs = self.render_config(firewalls)
|
|
||||||
facts = {}
|
|
||||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
|
||||||
facts['firewall_global'] = utils.remove_empties(params['config'])
|
|
||||||
ansible_facts['ansible_network_resources'].update(facts)
|
|
||||||
return ansible_facts
|
|
||||||
|
|
||||||
def render_config(self, 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
|
|
||||||
"""
|
|
||||||
conf = '\n'.join(filter(lambda x: ('firewall ipv6-name' and 'firewall name' not in x), conf))
|
|
||||||
|
|
||||||
a_lst = ['config_trap', 'validation', 'log_martians', 'syn_cookies', 'twa_hazards_protection']
|
|
||||||
firewall = self.parse_attr(conf, a_lst)
|
|
||||||
f_sub = {'ping': self.parse_ping(conf),
|
|
||||||
'group': self.parse_group(conf),
|
|
||||||
'route_redirects': self.route_redirects(conf),
|
|
||||||
'state_policy': self.parse_state_policy(conf)}
|
|
||||||
firewall.update(f_sub)
|
|
||||||
return firewall
|
|
||||||
|
|
||||||
def route_redirects(self, conf):
|
|
||||||
"""
|
|
||||||
This function forms the regex to fetch the afi and invoke
|
|
||||||
functions to fetch route redirects and source routes
|
|
||||||
:param conf: configuration data.
|
|
||||||
:return: generated rule list configuration.
|
|
||||||
"""
|
|
||||||
rr_lst = []
|
|
||||||
|
|
||||||
v6_attr = findall(r'^set firewall (?:ipv6-src-route|ipv6-receive-redirects) (\S+)', conf, M)
|
|
||||||
if v6_attr:
|
|
||||||
obj = self.parse_rr_attrib(conf, 'ipv6')
|
|
||||||
if obj:
|
|
||||||
rr_lst.append(obj)
|
|
||||||
|
|
||||||
v4_attr = findall(r'^set firewall (?:ip-src-route|receive-redirects|send-redirects) (\S+)', conf, M)
|
|
||||||
if v4_attr:
|
|
||||||
obj = self.parse_rr_attrib(conf, 'ipv4')
|
|
||||||
if obj:
|
|
||||||
rr_lst.append(obj)
|
|
||||||
return rr_lst
|
|
||||||
|
|
||||||
def parse_rr_attrib(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function fetches the 'ip_src_route'
|
|
||||||
invoke function to parse icmp redirects.
|
|
||||||
:param conf: configuration to be parsed.
|
|
||||||
:param attrib: 'ipv4/ipv6'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
|
|
||||||
cfg_dict = self.parse_attr(conf, ['ip_src_route'], type=attrib)
|
|
||||||
cfg_dict['icmp_redirects'] = self.parse_icmp_redirects(conf, attrib)
|
|
||||||
cfg_dict['afi'] = attrib
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_icmp_redirects(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'icmp_redirects' attributes.
|
|
||||||
:param conf: configuration to be parsed.
|
|
||||||
:param attrib: 'ipv4/ipv6'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['send', 'receive']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, type=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_ping(self, conf):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'ping' attributes.
|
|
||||||
:param conf: configuration to be parsed.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['all', 'broadcast']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_state_policy(self, conf):
|
|
||||||
"""
|
|
||||||
This function fetched the connecton type and invoke
|
|
||||||
function to parse other state-policy attributes.
|
|
||||||
:param conf: configuration data.
|
|
||||||
:return: generated rule list configuration.
|
|
||||||
"""
|
|
||||||
sp_lst = []
|
|
||||||
attrib = 'state-policy'
|
|
||||||
policies = findall(r'^set firewall ' + attrib + ' (\\S+)', conf, M)
|
|
||||||
|
|
||||||
if policies:
|
|
||||||
rules_lst = []
|
|
||||||
for sp in set(policies):
|
|
||||||
sp_regex = r' %s .+$' % sp
|
|
||||||
cfg = '\n'.join(findall(sp_regex, conf, M))
|
|
||||||
obj = self.parse_policies(cfg, sp)
|
|
||||||
obj['connection_type'] = sp
|
|
||||||
if obj:
|
|
||||||
rules_lst.append(obj)
|
|
||||||
sp_lst = sorted(rules_lst, key=lambda i: i['connection_type'])
|
|
||||||
return sp_lst
|
|
||||||
|
|
||||||
def parse_policies(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of policy attributes
|
|
||||||
action and log.
|
|
||||||
:param conf: configuration
|
|
||||||
:param attrib: connection type.
|
|
||||||
:return: generated rule configuration dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['action', 'log']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_group(self, conf):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'group' attributes.
|
|
||||||
:param conf: configuration.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
cfg_dict = {}
|
|
||||||
cfg_dict['port_group'] = self.parse_group_lst(conf, 'port-group')
|
|
||||||
cfg_dict['address_group'] = self.parse_group_lst(conf, 'address-group')
|
|
||||||
cfg_dict['network_group'] = self.parse_group_lst(conf, 'network-group')
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_group_lst(self, conf, type):
|
|
||||||
"""
|
|
||||||
This function fetches the name of group and invoke function to
|
|
||||||
parse group attributes'.
|
|
||||||
:param conf: configuration data.
|
|
||||||
:param type: type of group.
|
|
||||||
:return: generated group list configuration.
|
|
||||||
"""
|
|
||||||
g_lst = []
|
|
||||||
|
|
||||||
groups = findall(r'^set firewall group ' + type + ' (\\S+)', conf, M)
|
|
||||||
if groups:
|
|
||||||
rules_lst = []
|
|
||||||
for gr in set(groups):
|
|
||||||
gr_regex = r' %s .+$' % gr
|
|
||||||
cfg = '\n'.join(findall(gr_regex, conf, M))
|
|
||||||
obj = self.parse_groups(cfg, type, gr)
|
|
||||||
obj['name'] = gr.strip("'")
|
|
||||||
if obj:
|
|
||||||
rules_lst.append(obj)
|
|
||||||
g_lst = sorted(rules_lst, key=lambda i: i['name'])
|
|
||||||
return g_lst
|
|
||||||
|
|
||||||
def parse_groups(self, conf, type, name):
|
|
||||||
"""
|
|
||||||
This function fetches the description and invoke
|
|
||||||
the parsing of group members.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param type: type of group.
|
|
||||||
:param name: name of group.
|
|
||||||
:return: generated configuration dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['name', 'description']
|
|
||||||
group = self.parse_attr(conf, a_lst)
|
|
||||||
key = self.get_key(type)
|
|
||||||
r_sub = {key[0]: self.parse_address_port_lst(conf, name, key[1])}
|
|
||||||
group.update(r_sub)
|
|
||||||
return group
|
|
||||||
|
|
||||||
def parse_address_port_lst(self, conf, name, key):
|
|
||||||
"""
|
|
||||||
This function forms the regex to fetch the
|
|
||||||
group members attributes.
|
|
||||||
:param conf: configuration data.
|
|
||||||
:param name: name of group.
|
|
||||||
:param key: key value.
|
|
||||||
:return: generated member list configuration.
|
|
||||||
"""
|
|
||||||
l_lst = []
|
|
||||||
attribs = findall(r'^.*' + name + ' ' + key + ' (\\S+)', conf, M)
|
|
||||||
if attribs:
|
|
||||||
for attr in attribs:
|
|
||||||
if key == 'port':
|
|
||||||
l_lst.append({"port": attr.strip("'")})
|
|
||||||
else:
|
|
||||||
l_lst.append({"address": attr.strip("'")})
|
|
||||||
return l_lst
|
|
||||||
|
|
||||||
def parse_attr(self, conf, attr_list, match=None, type=None):
|
|
||||||
"""
|
|
||||||
This function peforms the following:
|
|
||||||
- Form the regex to fetch the required attribute config.
|
|
||||||
- Type cast the output in desired format.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attr_list: list of attributes.
|
|
||||||
:param match: parent node/attribute name.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
config = {}
|
|
||||||
for attrib in attr_list:
|
|
||||||
regex = self.map_regex(attrib, type)
|
|
||||||
if match:
|
|
||||||
regex = match + ' ' + regex
|
|
||||||
if conf:
|
|
||||||
if self.is_bool(attrib):
|
|
||||||
attr = self.map_regex(attrib, type)
|
|
||||||
out = conf.find(attr.replace("_", "-"))
|
|
||||||
dis = conf.find(attr.replace("_", "-") + " 'disable'")
|
|
||||||
if out >= 1:
|
|
||||||
if dis >= 1:
|
|
||||||
config[attrib] = False
|
|
||||||
else:
|
|
||||||
config[attrib] = True
|
|
||||||
else:
|
|
||||||
out = search(r'^.*' + regex + ' (.+)', conf, M)
|
|
||||||
if out:
|
|
||||||
val = out.group(1).strip("'")
|
|
||||||
if self.is_num(attrib):
|
|
||||||
val = int(val)
|
|
||||||
config[attrib] = val
|
|
||||||
return config
|
|
||||||
|
|
||||||
def get_key(self, type):
|
|
||||||
"""
|
|
||||||
This function map the group type to
|
|
||||||
member type
|
|
||||||
:param type:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
key = ()
|
|
||||||
if type == 'port-group':
|
|
||||||
key = ('members', 'port')
|
|
||||||
elif type == 'address-group':
|
|
||||||
key = ('members', 'address')
|
|
||||||
elif type == 'network-group':
|
|
||||||
key = ('members', 'network')
|
|
||||||
return key
|
|
||||||
|
|
||||||
def map_regex(self, attrib, type=None):
|
|
||||||
"""
|
|
||||||
- This function construct the regex string.
|
|
||||||
- replace the underscore with hyphen.
|
|
||||||
:param attrib: attribute
|
|
||||||
:return: regex string
|
|
||||||
"""
|
|
||||||
regex = attrib.replace("_", "-")
|
|
||||||
if attrib == 'all':
|
|
||||||
regex = 'all-ping'
|
|
||||||
elif attrib == 'disabled':
|
|
||||||
regex = 'disable'
|
|
||||||
elif attrib == 'broadcast':
|
|
||||||
regex = 'broadcast-ping'
|
|
||||||
elif attrib == 'send':
|
|
||||||
if type == 'ipv6':
|
|
||||||
regex = 'ipv6-send-redirects'
|
|
||||||
else:
|
|
||||||
regex = 'send-redirects'
|
|
||||||
elif attrib == 'ip_src_route':
|
|
||||||
if type == 'ipv6':
|
|
||||||
regex = 'ipv6-src-route'
|
|
||||||
elif attrib == 'receive':
|
|
||||||
if type == 'ipv6':
|
|
||||||
regex = 'ipv6-receive-redirects'
|
|
||||||
else:
|
|
||||||
regex = 'receive-redirects'
|
|
||||||
return regex
|
|
||||||
|
|
||||||
def is_num(self, attrib):
|
|
||||||
"""
|
|
||||||
This function looks for the attribute in predefined integer type set.
|
|
||||||
:param attrib: attribute.
|
|
||||||
:return: True/false.
|
|
||||||
"""
|
|
||||||
num_set = ('time', 'code', 'type', 'count', 'burst', 'number')
|
|
||||||
return True if attrib in num_set else False
|
|
||||||
|
|
||||||
def get_src_route(self, attrib):
|
|
||||||
"""
|
|
||||||
This function looks for the attribute in predefined integer type set.
|
|
||||||
:param attrib: attribute.
|
|
||||||
:return: True/false.
|
|
||||||
"""
|
|
||||||
return 'ipv6_src_route' if attrib == 'ipv6' else 'ip_src_route'
|
|
||||||
|
|
||||||
def is_bool(self, attrib):
|
|
||||||
"""
|
|
||||||
This function looks for the attribute in predefined bool type set.
|
|
||||||
:param attrib: attribute.
|
|
||||||
:return: True/False
|
|
||||||
"""
|
|
||||||
bool_set = ('all',
|
|
||||||
'log',
|
|
||||||
'send',
|
|
||||||
'receive',
|
|
||||||
'broadcast',
|
|
||||||
'config_trap',
|
|
||||||
'log_martians',
|
|
||||||
'syn_cookies',
|
|
||||||
'ip_src_route',
|
|
||||||
'twa_hazards_protection')
|
|
||||||
return True if attrib in bool_set else False
|
|
@ -1,183 +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 vyos firewall_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 re import findall, search, M
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.firewall_interfaces.firewall_interfaces import Firewall_interfacesArgs
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_interfacesFacts(object):
|
|
||||||
""" The vyos firewall_interfaces fact class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, module, subspec='config', options='options'):
|
|
||||||
self._module = module
|
|
||||||
self.argument_spec = Firewall_interfacesArgs.argument_spec
|
|
||||||
spec = deepcopy(self.argument_spec)
|
|
||||||
if subspec:
|
|
||||||
if options:
|
|
||||||
facts_argument_spec = spec[subspec][options]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec[subspec]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec
|
|
||||||
|
|
||||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
|
||||||
|
|
||||||
def get_device_data(self, connection):
|
|
||||||
return connection.get_config()
|
|
||||||
|
|
||||||
def populate_facts(self, connection, ansible_facts, data=None):
|
|
||||||
""" Populate the facts for firewall_interfaces
|
|
||||||
:param connection: the device connection
|
|
||||||
:param ansible_facts: Facts dictionary
|
|
||||||
:param data: previously collected conf
|
|
||||||
:rtype: dictionary
|
|
||||||
:returns: facts
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
# typically data is populated from the current device configuration
|
|
||||||
# data = connection.get('show running-config | section ^interface')
|
|
||||||
# using mock data instead
|
|
||||||
data = self.get_device_data(connection)
|
|
||||||
objs = []
|
|
||||||
interfaces = findall(r'^set interfaces ethernet (?:\'*)(\S+)(?:\'*)', data, M)
|
|
||||||
if interfaces:
|
|
||||||
objs = self.get_names(data, interfaces)
|
|
||||||
ansible_facts['ansible_network_resources'].pop('firewall_interfaces', None)
|
|
||||||
facts = {}
|
|
||||||
if objs:
|
|
||||||
facts['firewall_interfaces'] = []
|
|
||||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
|
||||||
for cfg in params['config']:
|
|
||||||
facts['firewall_interfaces'].append(utils.remove_empties(cfg))
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].update(facts)
|
|
||||||
return ansible_facts
|
|
||||||
|
|
||||||
def get_names(self, data, interfaces):
|
|
||||||
"""
|
|
||||||
This function performs following:
|
|
||||||
- Form regex to fetch 'interface name' from interfaces firewall data.
|
|
||||||
- Form the name list.
|
|
||||||
:param data: configuration.
|
|
||||||
:param rules: list of interfaces.
|
|
||||||
:return: generated firewall interfaces configuration.
|
|
||||||
"""
|
|
||||||
names = []
|
|
||||||
for r in set(interfaces):
|
|
||||||
int_regex = r' %s .+$' % r.strip("'")
|
|
||||||
cfg = findall(int_regex, data, M)
|
|
||||||
fi = self.render_config(cfg)
|
|
||||||
fi['name'] = r.strip("'")
|
|
||||||
names.append(fi)
|
|
||||||
if names:
|
|
||||||
names = sorted(names, key=lambda i: i['name'])
|
|
||||||
return names
|
|
||||||
|
|
||||||
def render_config(self, 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
|
|
||||||
"""
|
|
||||||
conf = '\n'.join(filter(lambda x: 'firewall' in x, conf))
|
|
||||||
config = {'access_rules': self.parse_access_rules(conf)}
|
|
||||||
return config
|
|
||||||
|
|
||||||
def parse_access_rules(self, conf):
|
|
||||||
"""
|
|
||||||
This function forms the regex to fetch the 'access-rules'
|
|
||||||
for specific interface.
|
|
||||||
:param conf: configuration data.
|
|
||||||
:return: generated access-rules list configuration.
|
|
||||||
"""
|
|
||||||
ar_lst = []
|
|
||||||
v4_ar = findall(r'^.*(in|out|local) name .*$', conf, M)
|
|
||||||
v6_ar = findall(r'^.*(in|out|local) ipv6-name .*$', conf, M)
|
|
||||||
if v4_ar:
|
|
||||||
v4_conf = "\n".join(findall(r"(^.*?%s.*?$)" % ' name', conf, M))
|
|
||||||
config = self.parse_int_rules(v4_conf, 'ipv4')
|
|
||||||
if config:
|
|
||||||
ar_lst.append(config)
|
|
||||||
if v6_ar:
|
|
||||||
v6_conf = "\n".join(findall(r"(^.*?%s.*?$)" % ' ipv6-name', conf, M))
|
|
||||||
config = self.parse_int_rules(v6_conf, 'ipv6')
|
|
||||||
if config:
|
|
||||||
ar_lst.append(config)
|
|
||||||
if ar_lst:
|
|
||||||
ar_lst = sorted(ar_lst, key=lambda i: i['afi'])
|
|
||||||
else:
|
|
||||||
empty_rules = findall(r'^.*(in|out|local).*', conf, M)
|
|
||||||
if empty_rules:
|
|
||||||
ar_lst.append({'afi': 'ipv4', 'rules': []})
|
|
||||||
ar_lst.append({'afi': 'ipv6', 'rules': []})
|
|
||||||
return ar_lst
|
|
||||||
|
|
||||||
def parse_int_rules(self, conf, afi):
|
|
||||||
"""
|
|
||||||
This function forms the regex to fetch the 'access-rules'
|
|
||||||
for specific interface based on ip-type.
|
|
||||||
:param conf: configuration data.
|
|
||||||
:param rules: rules configured per interface.
|
|
||||||
:param afi: ip address type.
|
|
||||||
:return: generated rule configuration dictionary.
|
|
||||||
"""
|
|
||||||
r_lst = []
|
|
||||||
config = {}
|
|
||||||
rules = ['in', 'out', 'local']
|
|
||||||
for r in set(rules):
|
|
||||||
fr = {}
|
|
||||||
r_regex = r' %s .+$' % r
|
|
||||||
cfg = '\n'.join(findall(r_regex, conf, M))
|
|
||||||
if cfg:
|
|
||||||
fr = self.parse_rules(cfg, afi)
|
|
||||||
else:
|
|
||||||
out = search(r'^.*firewall ' + "'" + r + "'" + '(.*)', conf, M)
|
|
||||||
if out:
|
|
||||||
fr = {'direction': r}
|
|
||||||
if fr:
|
|
||||||
r_lst.append(fr)
|
|
||||||
if r_lst:
|
|
||||||
r_lst = sorted(r_lst, key=lambda i: i['direction'])
|
|
||||||
config = {'afi': afi, 'rules': r_lst}
|
|
||||||
return config
|
|
||||||
|
|
||||||
def parse_rules(self, conf, afi):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'rule' attributes.
|
|
||||||
a_lst is a list having rule attributes which doesn't
|
|
||||||
have further sub attributes.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param afi: ip address type.
|
|
||||||
:return: generated rule configuration dictionary.
|
|
||||||
"""
|
|
||||||
cfg = {}
|
|
||||||
out = findall(r'[^\s]+', conf, M)
|
|
||||||
if out:
|
|
||||||
cfg['direction'] = out[0].strip("'")
|
|
||||||
if afi == 'ipv6':
|
|
||||||
out = findall(r'[^\s]+ ipv6-name (?:\'*)(\S+)(?:\'*)', conf, M)
|
|
||||||
if out:
|
|
||||||
cfg['name'] = str(out[0]).strip("'")
|
|
||||||
else:
|
|
||||||
out = findall(r'[^\s]+ name (?:\'*)(\S+)(?:\'*)', conf, M)
|
|
||||||
if out:
|
|
||||||
cfg['name'] = out[-1].strip("'")
|
|
||||||
return cfg
|
|
@ -1,348 +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 vyos firewall_rules 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 re import findall, search, M
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import Firewall_rulesArgs
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall_rulesFacts(object):
|
|
||||||
""" The vyos firewall_rules fact class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, module, subspec='config', options='options'):
|
|
||||||
self._module = module
|
|
||||||
self.argument_spec = Firewall_rulesArgs.argument_spec
|
|
||||||
spec = deepcopy(self.argument_spec)
|
|
||||||
if subspec:
|
|
||||||
if options:
|
|
||||||
facts_argument_spec = spec[subspec][options]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec[subspec]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec
|
|
||||||
|
|
||||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
|
||||||
|
|
||||||
def get_device_data(self, connection):
|
|
||||||
return connection.get_config()
|
|
||||||
|
|
||||||
def populate_facts(self, connection, ansible_facts, data=None):
|
|
||||||
""" Populate the facts for firewall_rules
|
|
||||||
:param connection: the device connection
|
|
||||||
:param ansible_facts: Facts dictionary
|
|
||||||
:param data: previously collected conf
|
|
||||||
:rtype: dictionary
|
|
||||||
:returns: facts
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
# typically data is populated from the current device configuration
|
|
||||||
# data = connection.get('show running-config | section ^interface')
|
|
||||||
# using mock data instead
|
|
||||||
data = self.get_device_data(connection)
|
|
||||||
# split the config into instances of the resource
|
|
||||||
objs = []
|
|
||||||
v6_rules = findall(r'^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)', data, M)
|
|
||||||
v4_rules = findall(r'^set firewall name (?:\'*)(\S+)(?:\'*)', data, M)
|
|
||||||
if v6_rules:
|
|
||||||
config = self.get_rules(data, v6_rules, type='ipv6')
|
|
||||||
if config:
|
|
||||||
config = utils.remove_empties(config)
|
|
||||||
objs.append(config)
|
|
||||||
if v4_rules:
|
|
||||||
config = self.get_rules(data, v4_rules, type='ipv4')
|
|
||||||
if config:
|
|
||||||
config = utils.remove_empties(config)
|
|
||||||
objs.append(config)
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].pop('firewall_rules', None)
|
|
||||||
facts = {}
|
|
||||||
if objs:
|
|
||||||
facts['firewall_rules'] = []
|
|
||||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
|
||||||
for cfg in params['config']:
|
|
||||||
facts['firewall_rules'].append(utils.remove_empties(cfg))
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].update(facts)
|
|
||||||
return ansible_facts
|
|
||||||
|
|
||||||
def get_rules(self, data, rules, type):
|
|
||||||
"""
|
|
||||||
This function performs following:
|
|
||||||
- Form regex to fetch 'rule-sets' specific config from data.
|
|
||||||
- Form the rule-set list based on ip address.
|
|
||||||
:param data: configuration.
|
|
||||||
:param rules: list of rule-sets.
|
|
||||||
:param type: ip address type.
|
|
||||||
:return: generated rule-sets configuration.
|
|
||||||
"""
|
|
||||||
r_v4 = []
|
|
||||||
r_v6 = []
|
|
||||||
for r in set(rules):
|
|
||||||
rule_regex = r' %s .+$' % r.strip("'")
|
|
||||||
cfg = findall(rule_regex, data, M)
|
|
||||||
fr = self.render_config(cfg, r.strip("'"))
|
|
||||||
fr['name'] = r.strip("'")
|
|
||||||
if type == 'ipv6':
|
|
||||||
r_v6.append(fr)
|
|
||||||
else:
|
|
||||||
r_v4.append(fr)
|
|
||||||
if r_v4:
|
|
||||||
config = {'afi': 'ipv4', 'rule_sets': r_v4}
|
|
||||||
if r_v6:
|
|
||||||
config = {'afi': 'ipv6', 'rule_sets': r_v6}
|
|
||||||
return config
|
|
||||||
|
|
||||||
def render_config(self, conf, match):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
conf = '\n'.join(filter(lambda x: x, conf))
|
|
||||||
a_lst = ['description', 'default_action', 'enable_default_log']
|
|
||||||
config = self.parse_attr(conf, a_lst, match)
|
|
||||||
if not config:
|
|
||||||
config = {}
|
|
||||||
config['rules'] = self.parse_rules_lst(conf)
|
|
||||||
return config
|
|
||||||
|
|
||||||
def parse_rules_lst(self, conf):
|
|
||||||
"""
|
|
||||||
This function forms the regex to fetch the 'rules' with in
|
|
||||||
'rule-sets'
|
|
||||||
:param conf: configuration data.
|
|
||||||
:return: generated rule list configuration.
|
|
||||||
"""
|
|
||||||
r_lst = []
|
|
||||||
rules = findall(r'rule (?:\'*)(\d+)(?:\'*)', conf, M)
|
|
||||||
if rules:
|
|
||||||
rules_lst = []
|
|
||||||
for r in set(rules):
|
|
||||||
r_regex = r' %s .+$' % r
|
|
||||||
cfg = '\n'.join(findall(r_regex, conf, M))
|
|
||||||
obj = self.parse_rules(cfg)
|
|
||||||
obj['number'] = int(r)
|
|
||||||
if obj:
|
|
||||||
rules_lst.append(obj)
|
|
||||||
r_lst = sorted(rules_lst, key=lambda i: i['number'])
|
|
||||||
return r_lst
|
|
||||||
|
|
||||||
def parse_rules(self, conf):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'rule' attributes.
|
|
||||||
a_lst is a list having rule attributes which doesn't
|
|
||||||
have further sub attributes.
|
|
||||||
:param conf: configuration
|
|
||||||
:return: generated rule configuration dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['ipsec', 'action', 'protocol', 'fragment', 'disabled', 'description']
|
|
||||||
rule = self.parse_attr(conf, a_lst)
|
|
||||||
r_sub = {'p2p': self.parse_p2p(conf),
|
|
||||||
'tcp': self.parse_tcp(conf, 'tcp'),
|
|
||||||
'icmp': self.parse_icmp(conf, 'icmp'),
|
|
||||||
'time': self.parse_time(conf, 'time'),
|
|
||||||
'limit': self.parse_limit(conf, 'limit'),
|
|
||||||
'state': self.parse_state(conf, 'state'),
|
|
||||||
'recent': self.parse_recent(conf, 'recent'),
|
|
||||||
'source': self.parse_src_or_dest(conf, 'source'),
|
|
||||||
'destination': self.parse_src_or_dest(conf, 'destination')}
|
|
||||||
rule.update(r_sub)
|
|
||||||
return rule
|
|
||||||
|
|
||||||
def parse_p2p(self, conf):
|
|
||||||
"""
|
|
||||||
This function forms the regex to fetch the 'p2p' with in
|
|
||||||
'rules'
|
|
||||||
:param conf: configuration data.
|
|
||||||
:return: generated rule list configuration.
|
|
||||||
"""
|
|
||||||
a_lst = []
|
|
||||||
applications = findall(r'p2p (?:\'*)(\d+)(?:\'*)', conf, M)
|
|
||||||
if applications:
|
|
||||||
app_lst = []
|
|
||||||
for r in set(applications):
|
|
||||||
obj = {'application': r.strip("'")}
|
|
||||||
app_lst.append(obj)
|
|
||||||
a_lst = sorted(app_lst, key=lambda i: i['application'])
|
|
||||||
return a_lst
|
|
||||||
|
|
||||||
def parse_src_or_dest(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'source or
|
|
||||||
destination' attributes.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attrib:'source/destination'.
|
|
||||||
:return:generated source/destination configuration dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['port', 'address', 'mac_address']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
cfg_dict['group'] = self.parse_group(conf, attrib + ' group')
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_recent(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'recent' attributes
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attrib: 'recent'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['time', 'count']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_tcp(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'tcp' attributes.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attrib: 'tcp'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
cfg_dict = self.parse_attr(conf, ['flags'], match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_time(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'time' attributes.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attrib: 'time'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['stopdate', 'stoptime', 'weekdays', 'monthdays', 'startdate', 'starttime']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_state(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'state' attributes.
|
|
||||||
:param conf: configuration
|
|
||||||
:param attrib: 'state'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['new', 'invalid', 'related', 'established']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_group(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'group' attributes.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attrib: 'group'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['port_group', 'address_group', 'network_group']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_icmp(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'icmp' attributes.
|
|
||||||
:param conf: configuration to be parsed.
|
|
||||||
:param attrib: 'icmp'.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['code', 'type', 'type_name']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_limit(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'limit' attributes.
|
|
||||||
:param conf: configuration to be parsed.
|
|
||||||
:param attrib: 'limit'
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
cfg_dict = self.parse_attr(conf, ['burst'], match=attrib)
|
|
||||||
cfg_dict['rate'] = self.parse_rate(conf, 'rate')
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_rate(self, conf, attrib=None):
|
|
||||||
"""
|
|
||||||
This function triggers the parsing of 'rate' attributes.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attrib: 'rate'
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
a_lst = ['unit', 'number']
|
|
||||||
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
|
|
||||||
return cfg_dict
|
|
||||||
|
|
||||||
def parse_attr(self, conf, attr_list, match=None):
|
|
||||||
"""
|
|
||||||
This function peforms the following:
|
|
||||||
- Form the regex to fetch the required attribute config.
|
|
||||||
- Type cast the output in desired format.
|
|
||||||
:param conf: configuration.
|
|
||||||
:param attr_list: list of attributes.
|
|
||||||
:param match: parent node/attribute name.
|
|
||||||
:return: generated config dictionary.
|
|
||||||
"""
|
|
||||||
config = {}
|
|
||||||
for attrib in attr_list:
|
|
||||||
regex = self.map_regex(attrib)
|
|
||||||
if match:
|
|
||||||
regex = match + ' ' + regex
|
|
||||||
if conf:
|
|
||||||
if self.is_bool(attrib):
|
|
||||||
out = conf.find(attrib.replace("_", "-"))
|
|
||||||
|
|
||||||
dis = conf.find(attrib.replace("_", "-") + " 'disable'")
|
|
||||||
if out >= 1:
|
|
||||||
if dis >= 1:
|
|
||||||
config[attrib] = False
|
|
||||||
else:
|
|
||||||
config[attrib] = True
|
|
||||||
else:
|
|
||||||
out = search(r'^.*' + regex + ' (.+)', conf, M)
|
|
||||||
if out:
|
|
||||||
val = out.group(1).strip("'")
|
|
||||||
if self.is_num(attrib):
|
|
||||||
val = int(val)
|
|
||||||
config[attrib] = val
|
|
||||||
return config
|
|
||||||
|
|
||||||
def map_regex(self, attrib):
|
|
||||||
"""
|
|
||||||
- This function construct the regex string.
|
|
||||||
- replace the underscore with hyphen.
|
|
||||||
:param attrib: attribute
|
|
||||||
:return: regex string
|
|
||||||
"""
|
|
||||||
regex = attrib.replace("_", "-")
|
|
||||||
if attrib == 'disabled':
|
|
||||||
regex = 'disable'
|
|
||||||
return regex
|
|
||||||
|
|
||||||
def is_bool(self, attrib):
|
|
||||||
"""
|
|
||||||
This function looks for the attribute in predefined bool type set.
|
|
||||||
:param attrib: attribute.
|
|
||||||
:return: True/False
|
|
||||||
"""
|
|
||||||
bool_set = ('new', 'invalid', 'related', 'disabled', 'established', 'enable_default_log')
|
|
||||||
return True if attrib in bool_set else False
|
|
||||||
|
|
||||||
def is_num(self, attrib):
|
|
||||||
"""
|
|
||||||
This function looks for the attribute in predefined integer type set.
|
|
||||||
:param attrib: attribute.
|
|
||||||
:return: True/false.
|
|
||||||
"""
|
|
||||||
num_set = ('time', 'code', 'type', 'count', 'burst', 'number')
|
|
||||||
return True if attrib in num_set else False
|
|
@ -1,123 +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 vyos 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 re import findall, M
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.interfaces.interfaces import InterfacesArgs
|
|
||||||
|
|
||||||
|
|
||||||
class InterfacesFacts(object):
|
|
||||||
""" The vyos 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
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
data = connection.get_config(flags=['| grep interfaces'])
|
|
||||||
|
|
||||||
objs = []
|
|
||||||
interface_names = findall(r'^set interfaces (?:ethernet|bonding|vti|loopback|vxlan) (?:\'*)(\S+)(?:\'*)',
|
|
||||||
data, M)
|
|
||||||
if interface_names:
|
|
||||||
for interface in set(interface_names):
|
|
||||||
intf_regex = r' %s .+$' % interface.strip("'")
|
|
||||||
cfg = findall(intf_regex, data, M)
|
|
||||||
obj = self.render_config(cfg)
|
|
||||||
obj['name'] = interface.strip("'")
|
|
||||||
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, 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
|
|
||||||
"""
|
|
||||||
vif_conf = '\n'.join(filter(lambda x: ('vif' in x), conf))
|
|
||||||
eth_conf = '\n'.join(filter(lambda x: ('vif' not in x), conf))
|
|
||||||
config = self.parse_attribs(
|
|
||||||
['description', 'speed', 'mtu', 'duplex'], eth_conf)
|
|
||||||
config['vifs'] = self.parse_vifs(vif_conf)
|
|
||||||
|
|
||||||
return utils.remove_empties(config)
|
|
||||||
|
|
||||||
def parse_vifs(self, conf):
|
|
||||||
vif_names = findall(r'vif (?:\'*)(\d+)(?:\'*)', conf, M)
|
|
||||||
vifs_list = None
|
|
||||||
|
|
||||||
if vif_names:
|
|
||||||
vifs_list = []
|
|
||||||
for vif in set(vif_names):
|
|
||||||
vif_regex = r' %s .+$' % vif
|
|
||||||
cfg = '\n'.join(findall(vif_regex, conf, M))
|
|
||||||
obj = self.parse_attribs(['description', 'mtu'], cfg)
|
|
||||||
obj['vlan_id'] = int(vif)
|
|
||||||
if obj:
|
|
||||||
vifs_list.append(obj)
|
|
||||||
vifs_list = sorted(vifs_list, key=lambda i: i['vlan_id'])
|
|
||||||
|
|
||||||
return vifs_list
|
|
||||||
|
|
||||||
def parse_attribs(self, attribs, conf):
|
|
||||||
config = {}
|
|
||||||
for item in attribs:
|
|
||||||
value = utils.parse_conf_arg(conf, item)
|
|
||||||
if value and item == 'mtu':
|
|
||||||
config[item] = int(value.strip("'"))
|
|
||||||
elif value:
|
|
||||||
config[item] = value.strip("'")
|
|
||||||
else:
|
|
||||||
config[item] = None
|
|
||||||
if 'disable' in conf:
|
|
||||||
config['enabled'] = False
|
|
||||||
else:
|
|
||||||
config['enabled'] = True
|
|
||||||
|
|
||||||
return utils.remove_empties(config)
|
|
@ -1,130 +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 vyos l3_interfaces fact class
|
|
||||||
It is in this file the configuration is collected from the device
|
|
||||||
for a given resource, parsed, and the facts tree is populated
|
|
||||||
based on the configuration.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
import re
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.compat import ipaddress
|
|
||||||
from ansible.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs
|
|
||||||
|
|
||||||
|
|
||||||
class L3_interfacesFacts(object):
|
|
||||||
""" The vyos 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
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
data = connection.get_config()
|
|
||||||
|
|
||||||
# operate on a collection of resource x
|
|
||||||
objs = []
|
|
||||||
interface_names = re.findall(r'set interfaces (?:ethernet|bonding|vti|vxlan) (?:\'*)(\S+)(?:\'*)', data, re.M)
|
|
||||||
if interface_names:
|
|
||||||
for interface in set(interface_names):
|
|
||||||
intf_regex = r' %s .+$' % interface
|
|
||||||
cfg = re.findall(intf_regex, data, re.M)
|
|
||||||
obj = self.render_config(cfg)
|
|
||||||
obj['name'] = interface.strip("'")
|
|
||||||
if obj:
|
|
||||||
objs.append(obj)
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].pop('l3_interfaces', None)
|
|
||||||
facts = {}
|
|
||||||
if objs:
|
|
||||||
facts['l3_interfaces'] = []
|
|
||||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
|
||||||
for cfg in params['config']:
|
|
||||||
facts['l3_interfaces'].append(utils.remove_empties(cfg))
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].update(facts)
|
|
||||||
return ansible_facts
|
|
||||||
|
|
||||||
def render_config(self, 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
|
|
||||||
"""
|
|
||||||
vif_conf = '\n'.join(filter(lambda x: ('vif' in x), conf))
|
|
||||||
eth_conf = '\n'.join(filter(lambda x: ('vif' not in x), conf))
|
|
||||||
config = self.parse_attribs(eth_conf)
|
|
||||||
config['vifs'] = self.parse_vifs(vif_conf)
|
|
||||||
|
|
||||||
return utils.remove_empties(config)
|
|
||||||
|
|
||||||
def parse_vifs(self, conf):
|
|
||||||
vif_names = re.findall(r'vif (\d+)', conf, re.M)
|
|
||||||
vifs_list = None
|
|
||||||
if vif_names:
|
|
||||||
vifs_list = []
|
|
||||||
for vif in set(vif_names):
|
|
||||||
vif_regex = r' %s .+$' % vif
|
|
||||||
cfg = '\n'.join(re.findall(vif_regex, conf, re.M))
|
|
||||||
obj = self.parse_attribs(cfg)
|
|
||||||
obj['vlan_id'] = vif
|
|
||||||
if obj:
|
|
||||||
vifs_list.append(obj)
|
|
||||||
|
|
||||||
return vifs_list
|
|
||||||
|
|
||||||
def parse_attribs(self, conf):
|
|
||||||
config = {}
|
|
||||||
ipaddrs = re.findall(r'address (\S+)', conf, re.M)
|
|
||||||
config['ipv4'] = []
|
|
||||||
config['ipv6'] = []
|
|
||||||
|
|
||||||
for item in ipaddrs:
|
|
||||||
item = item.strip("'")
|
|
||||||
if item == 'dhcp':
|
|
||||||
config['ipv4'].append({'address': item})
|
|
||||||
elif item == 'dhcpv6':
|
|
||||||
config['ipv6'].append({'address': item})
|
|
||||||
else:
|
|
||||||
ip_version = ipaddress.ip_address(item.split("/")[0]).version
|
|
||||||
if ip_version == 4:
|
|
||||||
config['ipv4'].append({'address': item})
|
|
||||||
else:
|
|
||||||
config['ipv6'].append({'address': item})
|
|
||||||
|
|
||||||
for key, value in iteritems(config):
|
|
||||||
if value == []:
|
|
||||||
config[key] = None
|
|
||||||
|
|
||||||
return utils.remove_empties(config)
|
|
@ -1,142 +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 vyos 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
|
|
||||||
from re import findall, search, M
|
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.lag_interfaces. \
|
|
||||||
lag_interfaces import Lag_interfacesArgs
|
|
||||||
|
|
||||||
|
|
||||||
class Lag_interfacesFacts(object):
|
|
||||||
""" The vyos lag_interfaces fact class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, module, subspec='config', options='options'):
|
|
||||||
self._module = module
|
|
||||||
self.argument_spec = Lag_interfacesArgs.argument_spec
|
|
||||||
spec = deepcopy(self.argument_spec)
|
|
||||||
if subspec:
|
|
||||||
if options:
|
|
||||||
facts_argument_spec = spec[subspec][options]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec[subspec]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec
|
|
||||||
|
|
||||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
|
||||||
|
|
||||||
def populate_facts(self, connection, ansible_facts, data=None):
|
|
||||||
""" Populate the facts for lag_interfaces
|
|
||||||
:param module: the module instance
|
|
||||||
:param connection: the device connection
|
|
||||||
:param data: previously collected conf
|
|
||||||
:rtype: dictionary
|
|
||||||
:returns: facts
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
data = connection.get_config()
|
|
||||||
|
|
||||||
objs = []
|
|
||||||
lag_names = findall(r'^set interfaces bonding (\S+)', data, M)
|
|
||||||
if lag_names:
|
|
||||||
for lag in set(lag_names):
|
|
||||||
lag_regex = r' %s .+$' % lag
|
|
||||||
cfg = findall(lag_regex, data, M)
|
|
||||||
obj = self.render_config(cfg)
|
|
||||||
|
|
||||||
output = connection.run_commands(['show interfaces bonding ' + lag + ' slaves'])
|
|
||||||
lines = output[0].splitlines()
|
|
||||||
members = []
|
|
||||||
member = {}
|
|
||||||
if len(lines) > 1:
|
|
||||||
for line in lines[2:]:
|
|
||||||
splitted_line = line.split()
|
|
||||||
|
|
||||||
if len(splitted_line) > 1:
|
|
||||||
member['member'] = splitted_line[0]
|
|
||||||
members.append(member)
|
|
||||||
else:
|
|
||||||
members = []
|
|
||||||
member = {}
|
|
||||||
obj['name'] = lag.strip("'")
|
|
||||||
if members:
|
|
||||||
obj['members'] = members
|
|
||||||
|
|
||||||
if obj:
|
|
||||||
objs.append(obj)
|
|
||||||
|
|
||||||
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, 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
|
|
||||||
"""
|
|
||||||
arp_monitor_conf = '\n'.join(filter(lambda x: ('arp-monitor' in x), conf))
|
|
||||||
hash_policy_conf = '\n'.join(filter(lambda x: ('hash-policy' in x), conf))
|
|
||||||
lag_conf = '\n'.join(filter(lambda x: ('bond' in x), conf))
|
|
||||||
config = self.parse_attribs(
|
|
||||||
['mode', 'primary'], lag_conf
|
|
||||||
)
|
|
||||||
config['arp_monitor'] = self.parse_arp_monitor(arp_monitor_conf)
|
|
||||||
config['hash_policy'] = self.parse_hash_policy(hash_policy_conf)
|
|
||||||
|
|
||||||
return utils.remove_empties(config)
|
|
||||||
|
|
||||||
def parse_attribs(self, attribs, conf):
|
|
||||||
config = {}
|
|
||||||
for item in attribs:
|
|
||||||
value = utils.parse_conf_arg(conf, item)
|
|
||||||
if value:
|
|
||||||
config[item] = value.strip("'")
|
|
||||||
else:
|
|
||||||
config[item] = None
|
|
||||||
return utils.remove_empties(config)
|
|
||||||
|
|
||||||
def parse_arp_monitor(self, conf):
|
|
||||||
arp_monitor = None
|
|
||||||
if conf:
|
|
||||||
arp_monitor = {}
|
|
||||||
target_list = []
|
|
||||||
interval = search(r'^.*arp-monitor interval (.+)', conf, M)
|
|
||||||
targets = findall(r"^.*arp-monitor target '(.+)'", conf, M)
|
|
||||||
if targets:
|
|
||||||
for target in targets:
|
|
||||||
target_list.append(target)
|
|
||||||
arp_monitor['target'] = target_list
|
|
||||||
if interval:
|
|
||||||
value = interval.group(1).strip("'")
|
|
||||||
arp_monitor['interval'] = int(value)
|
|
||||||
return arp_monitor
|
|
||||||
|
|
||||||
def parse_hash_policy(self, conf):
|
|
||||||
hash_policy = None
|
|
||||||
if conf:
|
|
||||||
hash_policy = search(r'^.*hash-policy (.+)', conf, M)
|
|
||||||
hash_policy = hash_policy.group(1).strip("'")
|
|
||||||
return hash_policy
|
|
@ -1,159 +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 VyOS 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 platform
|
|
||||||
import re
|
|
||||||
from ansible.module_utils. \
|
|
||||||
network.vyos.vyos import run_commands, get_capabilities
|
|
||||||
|
|
||||||
|
|
||||||
class LegacyFactsBase(object):
|
|
||||||
|
|
||||||
COMMANDS = frozenset()
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
self.module = module
|
|
||||||
self.facts = dict()
|
|
||||||
self.warnings = list()
|
|
||||||
self.responses = None
|
|
||||||
|
|
||||||
def populate(self):
|
|
||||||
self.responses = run_commands(self.module, list(self.COMMANDS))
|
|
||||||
|
|
||||||
|
|
||||||
class Default(LegacyFactsBase):
|
|
||||||
|
|
||||||
COMMANDS = [
|
|
||||||
'show version',
|
|
||||||
]
|
|
||||||
|
|
||||||
def populate(self):
|
|
||||||
super(Default, self).populate()
|
|
||||||
data = self.responses[0]
|
|
||||||
self.facts['serialnum'] = self.parse_serialnum(data)
|
|
||||||
self.facts.update(self.platform_facts())
|
|
||||||
|
|
||||||
def parse_serialnum(self, data):
|
|
||||||
match = re.search(r'HW S/N:\s+(\S+)', data)
|
|
||||||
if match:
|
|
||||||
return match.group(1)
|
|
||||||
|
|
||||||
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 Config(LegacyFactsBase):
|
|
||||||
|
|
||||||
COMMANDS = [
|
|
||||||
'show configuration commands',
|
|
||||||
'show system commit',
|
|
||||||
]
|
|
||||||
|
|
||||||
def populate(self):
|
|
||||||
super(Config, self).populate()
|
|
||||||
|
|
||||||
self.facts['config'] = self.responses
|
|
||||||
|
|
||||||
commits = self.responses[1]
|
|
||||||
entries = list()
|
|
||||||
entry = None
|
|
||||||
|
|
||||||
for line in commits.split('\n'):
|
|
||||||
match = re.match(r'(\d+)\s+(.+)by(.+)via(.+)', line)
|
|
||||||
if match:
|
|
||||||
if entry:
|
|
||||||
entries.append(entry)
|
|
||||||
|
|
||||||
entry = dict(revision=match.group(1),
|
|
||||||
datetime=match.group(2),
|
|
||||||
by=str(match.group(3)).strip(),
|
|
||||||
via=str(match.group(4)).strip(),
|
|
||||||
comment=None)
|
|
||||||
else:
|
|
||||||
entry['comment'] = line.strip()
|
|
||||||
|
|
||||||
self.facts['commits'] = entries
|
|
||||||
|
|
||||||
|
|
||||||
class Neighbors(LegacyFactsBase):
|
|
||||||
|
|
||||||
COMMANDS = [
|
|
||||||
'show lldp neighbors',
|
|
||||||
'show lldp neighbors detail',
|
|
||||||
]
|
|
||||||
|
|
||||||
def populate(self):
|
|
||||||
super(Neighbors, self).populate()
|
|
||||||
|
|
||||||
all_neighbors = self.responses[0]
|
|
||||||
if 'LLDP not configured' not in all_neighbors:
|
|
||||||
neighbors = self.parse(
|
|
||||||
self.responses[1]
|
|
||||||
)
|
|
||||||
self.facts['neighbors'] = self.parse_neighbors(neighbors)
|
|
||||||
|
|
||||||
def parse(self, data):
|
|
||||||
parsed = list()
|
|
||||||
values = None
|
|
||||||
for line in data.split('\n'):
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
elif line[0] == ' ':
|
|
||||||
values += '\n%s' % line
|
|
||||||
elif line.startswith('Interface'):
|
|
||||||
if values:
|
|
||||||
parsed.append(values)
|
|
||||||
values = line
|
|
||||||
if values:
|
|
||||||
parsed.append(values)
|
|
||||||
return parsed
|
|
||||||
|
|
||||||
def parse_neighbors(self, data):
|
|
||||||
facts = dict()
|
|
||||||
for item in data:
|
|
||||||
interface = self.parse_interface(item)
|
|
||||||
host = self.parse_host(item)
|
|
||||||
port = self.parse_port(item)
|
|
||||||
if interface not in facts:
|
|
||||||
facts[interface] = list()
|
|
||||||
facts[interface].append(dict(host=host, port=port))
|
|
||||||
return facts
|
|
||||||
|
|
||||||
def parse_interface(self, data):
|
|
||||||
match = re.search(r'^Interface:\s+(\S+),', data)
|
|
||||||
return match.group(1)
|
|
||||||
|
|
||||||
def parse_host(self, data):
|
|
||||||
match = re.search(r'SysName:\s+(.+)$', data, re.M)
|
|
||||||
if match:
|
|
||||||
return match.group(1)
|
|
||||||
|
|
||||||
def parse_port(self, data):
|
|
||||||
match = re.search(r'PortDescr:\s+(.+)$', data, re.M)
|
|
||||||
if match:
|
|
||||||
return match.group(1)
|
|
@ -1,109 +0,0 @@
|
|||||||
#
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright 2019 Red Hat
|
|
||||||
# GNU General Public License v3.0+
|
|
||||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""
|
|
||||||
The vyos 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 re import findall, M
|
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.lldp_global.lldp_global import Lldp_globalArgs
|
|
||||||
|
|
||||||
|
|
||||||
class Lldp_globalFacts(object):
|
|
||||||
""" The vyos lldp_global fact class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, module, subspec='config', options='options'):
|
|
||||||
self._module = module
|
|
||||||
self.argument_spec = Lldp_globalArgs.argument_spec
|
|
||||||
spec = deepcopy(self.argument_spec)
|
|
||||||
if subspec:
|
|
||||||
if options:
|
|
||||||
facts_argument_spec = spec[subspec][options]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec[subspec]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec
|
|
||||||
|
|
||||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
|
||||||
|
|
||||||
def populate_facts(self, connection, ansible_facts, data=None):
|
|
||||||
""" Populate the facts for lldp_global
|
|
||||||
:param connection: the device connection
|
|
||||||
:param ansible_facts: Facts dictionary
|
|
||||||
:param data: previously collected conf
|
|
||||||
:rtype: dictionary
|
|
||||||
:returns: facts
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
data = connection.get_config()
|
|
||||||
|
|
||||||
objs = {}
|
|
||||||
lldp_output = findall(r'^set service lldp (\S+)', data, M)
|
|
||||||
if lldp_output:
|
|
||||||
for item in set(lldp_output):
|
|
||||||
lldp_regex = r' %s .+$' % item
|
|
||||||
cfg = findall(lldp_regex, data, M)
|
|
||||||
obj = self.render_config(cfg)
|
|
||||||
if obj:
|
|
||||||
objs.update(obj)
|
|
||||||
lldp_service = findall(r"^set service (lldp)?('lldp')", data, M)
|
|
||||||
if lldp_service or lldp_output:
|
|
||||||
lldp_obj = {}
|
|
||||||
lldp_obj['enable'] = True
|
|
||||||
objs.update(lldp_obj)
|
|
||||||
|
|
||||||
facts = {}
|
|
||||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
|
||||||
facts['lldp_global'] = utils.remove_empties(params['config'])
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].update(facts)
|
|
||||||
|
|
||||||
return ansible_facts
|
|
||||||
|
|
||||||
def render_config(self, 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
|
|
||||||
"""
|
|
||||||
protocol_conf = '\n'.join(filter(lambda x: ('legacy-protocols' in x), conf))
|
|
||||||
att_conf = '\n'.join(filter(lambda x: ('legacy-protocols' not in x), conf))
|
|
||||||
config = self.parse_attribs(
|
|
||||||
['snmp', 'address'], att_conf
|
|
||||||
)
|
|
||||||
config['legacy_protocols'] = self.parse_protocols(protocol_conf)
|
|
||||||
return utils.remove_empties(config)
|
|
||||||
|
|
||||||
def parse_protocols(self, conf):
|
|
||||||
protocol_support = None
|
|
||||||
if conf:
|
|
||||||
protocols = findall(r'^.*legacy-protocols (.+)', conf, M)
|
|
||||||
if protocols:
|
|
||||||
protocol_support = []
|
|
||||||
for protocol in protocols:
|
|
||||||
protocol_support.append(protocol.strip("'"))
|
|
||||||
return protocol_support
|
|
||||||
|
|
||||||
def parse_attribs(self, attribs, conf):
|
|
||||||
config = {}
|
|
||||||
for item in attribs:
|
|
||||||
value = utils.parse_conf_arg(conf, item)
|
|
||||||
if value:
|
|
||||||
config[item] = value.strip("'")
|
|
||||||
else:
|
|
||||||
config[item] = None
|
|
||||||
return utils.remove_empties(config)
|
|
@ -1,147 +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 vyos 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
|
|
||||||
|
|
||||||
|
|
||||||
from re import findall, search, M
|
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.lldp_interfaces. \
|
|
||||||
lldp_interfaces import Lldp_interfacesArgs
|
|
||||||
|
|
||||||
|
|
||||||
class Lldp_interfacesFacts(object):
|
|
||||||
""" The vyos lldp_interfaces fact class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, module, subspec='config', options='options'):
|
|
||||||
self._module = module
|
|
||||||
self.argument_spec = Lldp_interfacesArgs.argument_spec
|
|
||||||
spec = deepcopy(self.argument_spec)
|
|
||||||
if subspec:
|
|
||||||
if options:
|
|
||||||
facts_argument_spec = spec[subspec][options]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec[subspec]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec
|
|
||||||
|
|
||||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
|
||||||
|
|
||||||
def populate_facts(self, connection, ansible_facts, data=None):
|
|
||||||
""" Populate the facts for lldp_interfaces
|
|
||||||
:param connection: the device connection
|
|
||||||
:param ansible_facts: Facts dictionary
|
|
||||||
:param data: previously collected conf
|
|
||||||
:rtype: dictionary
|
|
||||||
:returns: facts
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
data = connection.get_config()
|
|
||||||
|
|
||||||
objs = []
|
|
||||||
lldp_names = findall(r'^set service lldp interface (\S+)', data, M)
|
|
||||||
if lldp_names:
|
|
||||||
for lldp in set(lldp_names):
|
|
||||||
lldp_regex = r' %s .+$' % lldp
|
|
||||||
cfg = findall(lldp_regex, data, M)
|
|
||||||
obj = self.render_config(cfg)
|
|
||||||
obj['name'] = lldp.strip("'")
|
|
||||||
if obj:
|
|
||||||
objs.append(obj)
|
|
||||||
facts = {}
|
|
||||||
if objs:
|
|
||||||
facts['lldp_interfaces'] = objs
|
|
||||||
ansible_facts['ansible_network_resources'].update(facts)
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].update(facts)
|
|
||||||
return ansible_facts
|
|
||||||
|
|
||||||
def render_config(self, 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 = {}
|
|
||||||
location = {}
|
|
||||||
|
|
||||||
civic_conf = '\n'.join(filter(lambda x: ('civic-based' in x), conf))
|
|
||||||
elin_conf = '\n'.join(filter(lambda x: ('elin' in x), conf))
|
|
||||||
coordinate_conf = '\n'.join(filter(lambda x: ('coordinate-based' in x), conf))
|
|
||||||
disable = '\n'.join(filter(lambda x: ('disable' in x), conf))
|
|
||||||
|
|
||||||
coordinate_based_conf = self.parse_attribs(
|
|
||||||
['altitude', 'datum', 'longitude', 'latitude'], coordinate_conf
|
|
||||||
)
|
|
||||||
elin_based_conf = self.parse_lldp_elin_based(elin_conf)
|
|
||||||
civic_based_conf = self.parse_lldp_civic_based(civic_conf)
|
|
||||||
if disable:
|
|
||||||
config['enable'] = False
|
|
||||||
if coordinate_conf:
|
|
||||||
location['coordinate_based'] = coordinate_based_conf
|
|
||||||
config['location'] = location
|
|
||||||
elif civic_based_conf:
|
|
||||||
location['civic_based'] = civic_based_conf
|
|
||||||
config['location'] = location
|
|
||||||
elif elin_conf:
|
|
||||||
location['elin'] = elin_based_conf
|
|
||||||
config['location'] = location
|
|
||||||
|
|
||||||
return utils.remove_empties(config)
|
|
||||||
|
|
||||||
def parse_attribs(self, attribs, conf):
|
|
||||||
config = {}
|
|
||||||
for item in attribs:
|
|
||||||
value = utils.parse_conf_arg(conf, item)
|
|
||||||
if value:
|
|
||||||
value = value.strip("'")
|
|
||||||
if item == 'altitude':
|
|
||||||
value = int(value)
|
|
||||||
config[item] = value
|
|
||||||
else:
|
|
||||||
config[item] = None
|
|
||||||
return utils.remove_empties(config)
|
|
||||||
|
|
||||||
def parse_lldp_civic_based(self, conf):
|
|
||||||
civic_based = None
|
|
||||||
if conf:
|
|
||||||
civic_info_list = []
|
|
||||||
civic_add_list = findall(r"^.*civic-based ca-type (.+)", conf, M)
|
|
||||||
if civic_add_list:
|
|
||||||
for civic_add in civic_add_list:
|
|
||||||
ca = civic_add.split(' ')
|
|
||||||
c_add = {}
|
|
||||||
c_add['ca_type'] = int(ca[0].strip("'"))
|
|
||||||
c_add['ca_value'] = ca[2].strip("'")
|
|
||||||
civic_info_list.append(c_add)
|
|
||||||
|
|
||||||
country_code = search(r'^.*civic-based country-code (.+)', conf, M)
|
|
||||||
civic_based = {}
|
|
||||||
civic_based['ca_info'] = civic_info_list
|
|
||||||
civic_based['country_code'] = country_code.group(1).strip("'")
|
|
||||||
return civic_based
|
|
||||||
|
|
||||||
def parse_lldp_elin_based(self, conf):
|
|
||||||
elin_based = None
|
|
||||||
if conf:
|
|
||||||
e_num = search(r'^.* elin (.+)', conf, M)
|
|
||||||
elin_based = e_num.group(1).strip("'")
|
|
||||||
|
|
||||||
return elin_based
|
|
@ -1,161 +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 vyos 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 re import findall, search, M
|
|
||||||
from copy import deepcopy
|
|
||||||
from ansible.module_utils.network.common import utils
|
|
||||||
from ansible.module_utils.network.vyos.argspec.static_routes.static_routes import Static_routesArgs
|
|
||||||
from ansible.module_utils.network. vyos.utils.utils import get_route_type
|
|
||||||
|
|
||||||
|
|
||||||
class Static_routesFacts(object):
|
|
||||||
""" The vyos static_routes fact class
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, module, subspec='config', options='options'):
|
|
||||||
self._module = module
|
|
||||||
self.argument_spec = Static_routesArgs.argument_spec
|
|
||||||
spec = deepcopy(self.argument_spec)
|
|
||||||
if subspec:
|
|
||||||
if options:
|
|
||||||
facts_argument_spec = spec[subspec][options]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec[subspec]
|
|
||||||
else:
|
|
||||||
facts_argument_spec = spec
|
|
||||||
|
|
||||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
|
||||||
|
|
||||||
def get_device_data(self, connection):
|
|
||||||
return connection.get_config()
|
|
||||||
|
|
||||||
def populate_facts(self, connection, ansible_facts, data=None):
|
|
||||||
""" Populate the facts for static_routes
|
|
||||||
:param connection: the device connection
|
|
||||||
:param ansible_facts: Facts dictionary
|
|
||||||
:param data: previously collected conf
|
|
||||||
:rtype: dictionary
|
|
||||||
:returns: facts
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
data = self.get_device_data(connection)
|
|
||||||
# typically data is populated from the current device configuration
|
|
||||||
# data = connection.get('show running-config | section ^interface')
|
|
||||||
# using mock data instead
|
|
||||||
objs = []
|
|
||||||
r_v4 = []
|
|
||||||
r_v6 = []
|
|
||||||
af = []
|
|
||||||
static_routes = findall(r'set protocols static route(6)? (\S+)', data, M)
|
|
||||||
if static_routes:
|
|
||||||
for route in set(static_routes):
|
|
||||||
route_regex = r' %s .+$' % route[1]
|
|
||||||
cfg = findall(route_regex, data, M)
|
|
||||||
sr = self.render_config(cfg)
|
|
||||||
sr['dest'] = route[1].strip("'")
|
|
||||||
afi = self.get_afi(sr['dest'])
|
|
||||||
if afi == 'ipv4':
|
|
||||||
r_v4.append(sr)
|
|
||||||
else:
|
|
||||||
r_v6.append(sr)
|
|
||||||
if r_v4:
|
|
||||||
afi_v4 = {'afi': 'ipv4', 'routes': r_v4}
|
|
||||||
af.append(afi_v4)
|
|
||||||
if r_v6:
|
|
||||||
afi_v6 = {'afi': 'ipv6', 'routes': r_v6}
|
|
||||||
af.append(afi_v6)
|
|
||||||
config = {'address_families': af}
|
|
||||||
if config:
|
|
||||||
objs.append(config)
|
|
||||||
|
|
||||||
ansible_facts['ansible_network_resources'].pop('static_routes', None)
|
|
||||||
facts = {}
|
|
||||||
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 render_config(self, 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
|
|
||||||
"""
|
|
||||||
next_hops_conf = '\n'.join(filter(lambda x: ('next-hop' in x), conf))
|
|
||||||
blackhole_conf = '\n'.join(filter(lambda x: ('blackhole' in x), conf))
|
|
||||||
routes_dict = {'blackhole_config': self.parse_blackhole(blackhole_conf),
|
|
||||||
'next_hops': self.parse_next_hop(next_hops_conf)}
|
|
||||||
return routes_dict
|
|
||||||
|
|
||||||
def parse_blackhole(self, conf):
|
|
||||||
blackhole = None
|
|
||||||
if conf:
|
|
||||||
distance = search(r'^.*blackhole distance (.\S+)', conf, M)
|
|
||||||
bh = conf.find('blackhole')
|
|
||||||
if distance is not None:
|
|
||||||
blackhole = {}
|
|
||||||
value = distance.group(1).strip("'")
|
|
||||||
blackhole['distance'] = int(value)
|
|
||||||
elif bh:
|
|
||||||
blackhole = {}
|
|
||||||
blackhole['type'] = 'blackhole'
|
|
||||||
return blackhole
|
|
||||||
|
|
||||||
def get_afi(self, address):
|
|
||||||
route_type = get_route_type(address)
|
|
||||||
if route_type == 'route':
|
|
||||||
return 'ipv4'
|
|
||||||
elif route_type == 'route6':
|
|
||||||
return 'ipv6'
|
|
||||||
|
|
||||||
def parse_next_hop(self, conf):
|
|
||||||
nh_list = None
|
|
||||||
if conf:
|
|
||||||
nh_list = []
|
|
||||||
hop_list = findall(r"^.*next-hop (.+)", conf, M)
|
|
||||||
if hop_list:
|
|
||||||
for hop in hop_list:
|
|
||||||
distance = search(r'^.*distance (.\S+)', hop, M)
|
|
||||||
interface = search(r'^.*interface (.\S+)', hop, M)
|
|
||||||
|
|
||||||
dis = hop.find('disable')
|
|
||||||
hop_info = hop.split(' ')
|
|
||||||
nh_info = {'forward_router_address': hop_info[0].strip("'")}
|
|
||||||
if interface:
|
|
||||||
nh_info['interface'] = interface.group(1).strip("'")
|
|
||||||
if distance:
|
|
||||||
value = distance.group(1).strip("'")
|
|
||||||
nh_info['admin_distance'] = int(value)
|
|
||||||
elif dis >= 1:
|
|
||||||
nh_info['enabled'] = False
|
|
||||||
for element in nh_list:
|
|
||||||
if element['forward_router_address'] == nh_info['forward_router_address']:
|
|
||||||
if 'interface' in nh_info.keys():
|
|
||||||
element['interface'] = nh_info['interface']
|
|
||||||
if 'admin_distance' in nh_info.keys():
|
|
||||||
element['admin_distance'] = nh_info['admin_distance']
|
|
||||||
if 'enabled' in nh_info.keys():
|
|
||||||
element['enabled'] = nh_info['enabled']
|
|
||||||
nh_info = None
|
|
||||||
if nh_info is not None:
|
|
||||||
nh_list.append(nh_info)
|
|
||||||
return nh_list
|
|
@ -1,210 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright 2019 Red Hat
|
|
||||||
# GNU General Public License v3.0+
|
|
||||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
|
|
||||||
# utils
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.compat import ipaddress
|
|
||||||
|
|
||||||
|
|
||||||
def search_obj_in_list(name, lst, key='name'):
|
|
||||||
if lst:
|
|
||||||
for item in lst:
|
|
||||||
if item[key] == name:
|
|
||||||
return item
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_interface_type(interface):
|
|
||||||
"""Gets the type of interface
|
|
||||||
"""
|
|
||||||
if interface.startswith('eth'):
|
|
||||||
return 'ethernet'
|
|
||||||
elif interface.startswith('bond'):
|
|
||||||
return 'bonding'
|
|
||||||
elif interface.startswith('vti'):
|
|
||||||
return 'vti'
|
|
||||||
elif interface.startswith('lo'):
|
|
||||||
return 'loopback'
|
|
||||||
|
|
||||||
|
|
||||||
def dict_delete(base, comparable):
|
|
||||||
"""
|
|
||||||
This function generates a dict containing key, value pairs for keys
|
|
||||||
that are present in the `base` dict but not present in the `comparable`
|
|
||||||
dict.
|
|
||||||
|
|
||||||
:param base: dict object to base the diff on
|
|
||||||
:param comparable: dict object to compare against base
|
|
||||||
:returns: new dict object with key, value pairs that needs to be deleted.
|
|
||||||
|
|
||||||
"""
|
|
||||||
to_delete = dict()
|
|
||||||
|
|
||||||
for key in base:
|
|
||||||
if isinstance(base[key], dict):
|
|
||||||
sub_diff = dict_delete(base[key], comparable.get(key, {}))
|
|
||||||
if sub_diff:
|
|
||||||
to_delete[key] = sub_diff
|
|
||||||
else:
|
|
||||||
if key not in comparable:
|
|
||||||
to_delete[key] = base[key]
|
|
||||||
|
|
||||||
return to_delete
|
|
||||||
|
|
||||||
|
|
||||||
def diff_list_of_dicts(want, have):
|
|
||||||
diff = []
|
|
||||||
|
|
||||||
set_w = set(tuple(d.items()) for d in want)
|
|
||||||
set_h = set(tuple(d.items()) for d in have)
|
|
||||||
difference = set_w.difference(set_h)
|
|
||||||
|
|
||||||
for element in difference:
|
|
||||||
diff.append(dict((x, y) for x, y in element))
|
|
||||||
|
|
||||||
return diff
|
|
||||||
|
|
||||||
|
|
||||||
def get_lst_diff_for_dicts(want, have, lst):
|
|
||||||
"""
|
|
||||||
This function generates a list containing values
|
|
||||||
that are only in want and not in list in have dict
|
|
||||||
:param want: dict object to want
|
|
||||||
:param have: dict object to have
|
|
||||||
:param lst: list the diff on
|
|
||||||
:return: new list object with values which are only in want.
|
|
||||||
"""
|
|
||||||
if not have:
|
|
||||||
diff = want.get(lst) or []
|
|
||||||
|
|
||||||
else:
|
|
||||||
want_elements = want.get(lst) or {}
|
|
||||||
have_elements = have.get(lst) or {}
|
|
||||||
diff = list_diff_want_only(want_elements, have_elements)
|
|
||||||
return diff
|
|
||||||
|
|
||||||
|
|
||||||
def get_lst_same_for_dicts(want, have, lst):
|
|
||||||
"""
|
|
||||||
This function generates a list containing values
|
|
||||||
that are common for list in want and list in have dict
|
|
||||||
:param want: dict object to want
|
|
||||||
:param have: dict object to have
|
|
||||||
:param lst: list the comparison on
|
|
||||||
:return: new list object with values which are common in want and have.
|
|
||||||
"""
|
|
||||||
diff = None
|
|
||||||
if want and have:
|
|
||||||
want_list = want.get(lst) or {}
|
|
||||||
have_list = have.get(lst) or {}
|
|
||||||
diff = [i for i in want_list and have_list if i in have_list and i in want_list]
|
|
||||||
return diff
|
|
||||||
|
|
||||||
|
|
||||||
def list_diff_have_only(want_list, have_list):
|
|
||||||
"""
|
|
||||||
This function generated the list containing values
|
|
||||||
that are only in have list.
|
|
||||||
:param want_list:
|
|
||||||
:param have_list:
|
|
||||||
:return: new list with values which are only in have list
|
|
||||||
"""
|
|
||||||
if have_list and not want_list:
|
|
||||||
diff = have_list
|
|
||||||
elif not have_list:
|
|
||||||
diff = None
|
|
||||||
else:
|
|
||||||
diff = [i for i in have_list + want_list if i in have_list and i not in want_list]
|
|
||||||
return diff
|
|
||||||
|
|
||||||
|
|
||||||
def list_diff_want_only(want_list, have_list):
|
|
||||||
"""
|
|
||||||
This function generated the list containing values
|
|
||||||
that are only in want list.
|
|
||||||
:param want_list:
|
|
||||||
:param have_list:
|
|
||||||
:return: new list with values which are only in want list
|
|
||||||
"""
|
|
||||||
if have_list and not want_list:
|
|
||||||
diff = None
|
|
||||||
elif not have_list:
|
|
||||||
diff = want_list
|
|
||||||
else:
|
|
||||||
diff = [i for i in have_list + want_list if i in want_list and i not in have_list]
|
|
||||||
return diff
|
|
||||||
|
|
||||||
|
|
||||||
def search_dict_tv_in_list(d_val1, d_val2, lst, key1, key2):
|
|
||||||
"""
|
|
||||||
This function return the dict object if it exist in list.
|
|
||||||
:param d_val1:
|
|
||||||
:param d_val2:
|
|
||||||
:param lst:
|
|
||||||
:param key1:
|
|
||||||
:param key2:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
obj = next((item for item in lst if item[key1] == d_val1 and item[key2] == d_val2), None)
|
|
||||||
if obj:
|
|
||||||
return obj
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def key_value_in_dict(have_key, have_value, want_dict):
|
|
||||||
"""
|
|
||||||
This function checks whether the key and values exist in dict
|
|
||||||
:param have_key:
|
|
||||||
:param have_value:
|
|
||||||
:param want_dict:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
for key, value in iteritems(want_dict):
|
|
||||||
if key == have_key and value == have_value:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_dict_element_present(dict, key):
|
|
||||||
"""
|
|
||||||
This function checks whether the key is present in dict.
|
|
||||||
:param dict:
|
|
||||||
:param key:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
for item in dict:
|
|
||||||
if item == key:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def get_ip_address_version(address):
|
|
||||||
"""
|
|
||||||
This function returns the version of IP address
|
|
||||||
:param address: IP address
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
address = unicode(address)
|
|
||||||
except NameError:
|
|
||||||
address = str(address)
|
|
||||||
version = ipaddress.ip_address(address.split("/")[0]).version
|
|
||||||
return version
|
|
||||||
|
|
||||||
|
|
||||||
def get_route_type(address):
|
|
||||||
"""
|
|
||||||
This function returns the route type based on IP address
|
|
||||||
:param address:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
version = get_ip_address_version(address)
|
|
||||||
if version == 6:
|
|
||||||
return 'route6'
|
|
||||||
elif version == 4:
|
|
||||||
return 'route'
|
|
@ -1,116 +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.connection import Connection, ConnectionError
|
|
||||||
|
|
||||||
_DEVICE_CONFIGS = {}
|
|
||||||
|
|
||||||
vyos_provider_spec = {
|
|
||||||
'host': dict(),
|
|
||||||
'port': dict(type='int'),
|
|
||||||
|
|
||||||
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
|
|
||||||
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
|
|
||||||
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'),
|
|
||||||
|
|
||||||
'timeout': dict(type='int'),
|
|
||||||
}
|
|
||||||
vyos_argument_spec = {
|
|
||||||
'provider': dict(type='dict', options=vyos_provider_spec, removed_in_version=2.14),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_provider_argspec():
|
|
||||||
return vyos_provider_spec
|
|
||||||
|
|
||||||
|
|
||||||
def get_connection(module):
|
|
||||||
if hasattr(module, '_vyos_connection'):
|
|
||||||
return module._vyos_connection
|
|
||||||
|
|
||||||
capabilities = get_capabilities(module)
|
|
||||||
network_api = capabilities.get('network_api')
|
|
||||||
if network_api == 'cliconf':
|
|
||||||
module._vyos_connection = Connection(module._socket_path)
|
|
||||||
else:
|
|
||||||
module.fail_json(msg='Invalid connection type %s' % network_api)
|
|
||||||
|
|
||||||
return module._vyos_connection
|
|
||||||
|
|
||||||
|
|
||||||
def get_capabilities(module):
|
|
||||||
if hasattr(module, '_vyos_capabilities'):
|
|
||||||
return module._vyos_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._vyos_capabilities = json.loads(capabilities)
|
|
||||||
return module._vyos_capabilities
|
|
||||||
|
|
||||||
|
|
||||||
def get_config(module, flags=None, format=None):
|
|
||||||
flags = [] if flags is None else flags
|
|
||||||
global _DEVICE_CONFIGS
|
|
||||||
|
|
||||||
if _DEVICE_CONFIGS != {}:
|
|
||||||
return _DEVICE_CONFIGS
|
|
||||||
else:
|
|
||||||
connection = get_connection(module)
|
|
||||||
try:
|
|
||||||
out = connection.get_config(flags=flags, format=format)
|
|
||||||
except ConnectionError as exc:
|
|
||||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
|
||||||
cfg = to_text(out, errors='surrogate_then_replace').strip()
|
|
||||||
_DEVICE_CONFIGS = cfg
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
|
|
||||||
def run_commands(module, commands, check_rc=True):
|
|
||||||
connection = get_connection(module)
|
|
||||||
try:
|
|
||||||
response = connection.run_commands(commands=commands, check_rc=check_rc)
|
|
||||||
except ConnectionError as exc:
|
|
||||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def load_config(module, commands, commit=False, comment=None):
|
|
||||||
connection = get_connection(module)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = connection.edit_config(candidate=commands, commit=commit, comment=comment)
|
|
||||||
except ConnectionError as exc:
|
|
||||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
|
||||||
|
|
||||||
return response.get('diff')
|
|
@ -1,442 +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': ['deprecated'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_interface
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Ganesh Nalawade (@ganeshrn)"
|
|
||||||
short_description: Manage Interface on VyOS network devices
|
|
||||||
description:
|
|
||||||
- This module provides declarative management of Interfaces
|
|
||||||
on VyOS network devices.
|
|
||||||
deprecated:
|
|
||||||
removed_in: '2.13'
|
|
||||||
alternative: vyos_interfaces
|
|
||||||
why: Updated modules released with more functionality.
|
|
||||||
notes:
|
|
||||||
- Tested against VYOS 1.1.7
|
|
||||||
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']
|
|
||||||
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) and I(neighbors).
|
|
||||||
default: 10
|
|
||||||
neighbors:
|
|
||||||
description:
|
|
||||||
- Check the operational state of given interface C(name) for LLDP neighbor.
|
|
||||||
- The following suboptions are available.
|
|
||||||
suboptions:
|
|
||||||
host:
|
|
||||||
description:
|
|
||||||
- "LLDP neighbor host for given interface C(name)."
|
|
||||||
port:
|
|
||||||
description:
|
|
||||||
- "LLDP neighbor port to which given interface C(name) is connected."
|
|
||||||
version_added: 2.5
|
|
||||||
aggregate:
|
|
||||||
description: List of Interfaces definitions.
|
|
||||||
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: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: configure interface
|
|
||||||
vyos_interface:
|
|
||||||
name: eth0
|
|
||||||
description: test-interface
|
|
||||||
|
|
||||||
- name: remove interface
|
|
||||||
vyos_interface:
|
|
||||||
name: eth0
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: make interface down
|
|
||||||
vyos_interface:
|
|
||||||
name: eth0
|
|
||||||
enabled: False
|
|
||||||
|
|
||||||
- name: make interface up
|
|
||||||
vyos_interface:
|
|
||||||
name: eth0
|
|
||||||
enabled: True
|
|
||||||
|
|
||||||
- name: Configure interface speed, mtu, duplex
|
|
||||||
vyos_interface:
|
|
||||||
name: eth5
|
|
||||||
state: present
|
|
||||||
speed: 100
|
|
||||||
mtu: 256
|
|
||||||
duplex: full
|
|
||||||
|
|
||||||
- name: Set interface using aggregate
|
|
||||||
vyos_interface:
|
|
||||||
aggregate:
|
|
||||||
- { name: eth1, description: test-interface-1, speed: 100, duplex: half, mtu: 512}
|
|
||||||
- { name: eth2, description: test-interface-2, speed: 1000, duplex: full, mtu: 256}
|
|
||||||
|
|
||||||
- name: Disable interface on aggregate
|
|
||||||
net_interface:
|
|
||||||
aggregate:
|
|
||||||
- name: eth1
|
|
||||||
- name: eth2
|
|
||||||
enabled: False
|
|
||||||
|
|
||||||
- name: Delete interface using aggregate
|
|
||||||
net_interface:
|
|
||||||
aggregate:
|
|
||||||
- name: eth1
|
|
||||||
- name: eth2
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Check lldp neighbors intent arguments
|
|
||||||
vyos_interface:
|
|
||||||
name: eth0
|
|
||||||
neighbors:
|
|
||||||
- port: eth0
|
|
||||||
host: netdev
|
|
||||||
|
|
||||||
- name: Config + intent
|
|
||||||
vyos_interface:
|
|
||||||
name: eth1
|
|
||||||
enabled: False
|
|
||||||
state: down
|
|
||||||
"""
|
|
||||||
|
|
||||||
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:
|
|
||||||
- set interfaces ethernet eth0 description "test-interface"
|
|
||||||
- set interfaces ethernet eth0 speed 100
|
|
||||||
- set interfaces ethernet eth0 mtu 256
|
|
||||||
- set interfaces ethernet eth0 duplex full
|
|
||||||
"""
|
|
||||||
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.common.utils import conditional, remove_default_spec
|
|
||||||
from ansible.module_utils.network.vyos.vyos import load_config, get_config
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
|
||||||
commands = list()
|
|
||||||
want, have = updates
|
|
||||||
|
|
||||||
params = ('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)
|
|
||||||
set_interface = 'set interfaces ethernet ' + name
|
|
||||||
delete_interface = 'delete interfaces ethernet ' + name
|
|
||||||
|
|
||||||
if state == 'absent' and obj_in_have:
|
|
||||||
commands.append(delete_interface)
|
|
||||||
elif state in ('present', 'up', 'down'):
|
|
||||||
if obj_in_have:
|
|
||||||
for item in params:
|
|
||||||
value = w.get(item)
|
|
||||||
|
|
||||||
if value and value != obj_in_have.get(item):
|
|
||||||
if item == 'description':
|
|
||||||
value = "\'" + str(value) + "\'"
|
|
||||||
commands.append(set_interface + ' ' + item + ' ' + str(value))
|
|
||||||
|
|
||||||
if disable and not obj_in_have.get('disable', False):
|
|
||||||
commands.append(set_interface + ' disable')
|
|
||||||
elif not disable and obj_in_have.get('disable', False):
|
|
||||||
commands.append(delete_interface + ' disable')
|
|
||||||
else:
|
|
||||||
commands.append(set_interface)
|
|
||||||
for item in params:
|
|
||||||
value = w.get(item)
|
|
||||||
if value:
|
|
||||||
if item == 'description':
|
|
||||||
value = "\'" + str(value) + "\'"
|
|
||||||
commands.append(set_interface + ' ' + item + ' ' + str(value))
|
|
||||||
|
|
||||||
if disable:
|
|
||||||
commands.append(set_interface + ' disable')
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def map_config_to_obj(module):
|
|
||||||
data = get_config(module, flags=['| grep interface'])
|
|
||||||
obj = []
|
|
||||||
for line in data.split('\n'):
|
|
||||||
if line.startswith('set interfaces ethernet'):
|
|
||||||
match = re.search(r'set interfaces ethernet (\S+)', line, re.M)
|
|
||||||
name = match.group(1)
|
|
||||||
if name:
|
|
||||||
interface = {}
|
|
||||||
for item in obj:
|
|
||||||
if item['name'] == name:
|
|
||||||
interface = item
|
|
||||||
break
|
|
||||||
|
|
||||||
if not interface:
|
|
||||||
interface = {'name': name}
|
|
||||||
obj.append(interface)
|
|
||||||
|
|
||||||
match = re.search(r'%s (\S+)' % name, line, re.M)
|
|
||||||
if match:
|
|
||||||
param = match.group(1)
|
|
||||||
if param == 'description':
|
|
||||||
match = re.search(r'description (.+)', line, re.M)
|
|
||||||
description = match.group(1).strip("'")
|
|
||||||
interface['description'] = description
|
|
||||||
elif param == 'speed':
|
|
||||||
match = re.search(r'speed (\S+)', line, re.M)
|
|
||||||
speed = match.group(1).strip("'")
|
|
||||||
interface['speed'] = speed
|
|
||||||
elif param == 'mtu':
|
|
||||||
match = re.search(r'mtu (\S+)', line, re.M)
|
|
||||||
mtu = match.group(1).strip("'")
|
|
||||||
interface['mtu'] = int(mtu)
|
|
||||||
elif param == 'duplex':
|
|
||||||
match = re.search(r'duplex (\S+)', line, re.M)
|
|
||||||
duplex = match.group(1).strip("'")
|
|
||||||
interface['duplex'] = duplex
|
|
||||||
elif param.strip("'") == 'disable':
|
|
||||||
interface['disable'] = True
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
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'],
|
|
||||||
'delay': module.params['delay'],
|
|
||||||
'state': module.params['state'],
|
|
||||||
'neighbors': module.params['neighbors']
|
|
||||||
}
|
|
||||||
|
|
||||||
if module.params['enabled']:
|
|
||||||
params.update({'disable': False})
|
|
||||||
else:
|
|
||||||
params.update({'disable': True})
|
|
||||||
|
|
||||||
obj.append(params)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def check_declarative_intent_params(module, want, result):
|
|
||||||
failed_conditions = []
|
|
||||||
have_neighbors = None
|
|
||||||
for w in want:
|
|
||||||
want_state = w.get('state')
|
|
||||||
want_neighbors = w.get('neighbors')
|
|
||||||
|
|
||||||
if want_state not in ('up', 'down') and not want_neighbors:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if result['changed']:
|
|
||||||
sleep(w['delay'])
|
|
||||||
|
|
||||||
command = 'show interfaces ethernet %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+)' % 'state', 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().lower()):
|
|
||||||
failed_conditions.append('state ' + 'eq(%s)' % want_state)
|
|
||||||
|
|
||||||
if want_neighbors:
|
|
||||||
have_host = []
|
|
||||||
have_port = []
|
|
||||||
if have_neighbors is None:
|
|
||||||
rc, have_neighbors, 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:
|
|
||||||
lines = have_neighbors.strip().split('Interface: ')
|
|
||||||
for line in lines:
|
|
||||||
field = line.split('\n')
|
|
||||||
if field[0].split(',')[0].strip() == w['name']:
|
|
||||||
for item in field:
|
|
||||||
if item.strip().startswith('SysName:'):
|
|
||||||
have_host.append(item.split(':')[1].strip())
|
|
||||||
if item.strip().startswith('PortDescr:'):
|
|
||||||
have_port.append(item.split(':')[1].strip())
|
|
||||||
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(type='int'),
|
|
||||||
duplex=dict(choices=['full', 'half', 'auto']),
|
|
||||||
enabled=dict(default=True, type='bool'),
|
|
||||||
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(vyos_argument_spec)
|
|
||||||
|
|
||||||
required_one_of = [['name', 'aggregate']]
|
|
||||||
mutually_exclusive = [['name', 'aggregate']]
|
|
||||||
|
|
||||||
required_together = [['speed', 'duplex']]
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
|
||||||
required_one_of=required_one_of,
|
|
||||||
mutually_exclusive=mutually_exclusive,
|
|
||||||
required_together=required_together,
|
|
||||||
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:
|
|
||||||
commit = not module.check_mode
|
|
||||||
diff = load_config(module, commands, commit=commit)
|
|
||||||
if diff:
|
|
||||||
if module._diff:
|
|
||||||
result['diff'] = {'prepared': diff}
|
|
||||||
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,289 +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': ['deprecated'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_l3_interface
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
|
|
||||||
short_description: Manage L3 interfaces on VyOS network devices
|
|
||||||
description:
|
|
||||||
- This module provides declarative management of L3 interfaces
|
|
||||||
on VyOS network devices.
|
|
||||||
deprecated:
|
|
||||||
removed_in: '2.13'
|
|
||||||
alternative: vyos_l3_interfaces
|
|
||||||
why: Updated modules released with more functionality.
|
|
||||||
notes:
|
|
||||||
- Tested against VYOS 1.1.7
|
|
||||||
options:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Name of the L3 interface.
|
|
||||||
ipv4:
|
|
||||||
description:
|
|
||||||
- IPv4 of the L3 interface.
|
|
||||||
ipv6:
|
|
||||||
description:
|
|
||||||
- IPv6 of the L3 interface.
|
|
||||||
aggregate:
|
|
||||||
description: List of L3 interfaces definitions
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- State of the L3 interface configuration.
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: Set eth0 IPv4 address
|
|
||||||
vyos_l3_interface:
|
|
||||||
name: eth0
|
|
||||||
ipv4: 192.168.0.1/24
|
|
||||||
|
|
||||||
- name: Remove eth0 IPv4 address
|
|
||||||
vyos_l3_interface:
|
|
||||||
name: eth0
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Set IP addresses on aggregate
|
|
||||||
vyos_l3_interface:
|
|
||||||
aggregate:
|
|
||||||
- { name: eth1, ipv4: 192.168.2.10/24 }
|
|
||||||
- { name: eth2, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" }
|
|
||||||
|
|
||||||
- name: Remove IP addresses on aggregate
|
|
||||||
vyos_l3_interface:
|
|
||||||
aggregate:
|
|
||||||
- { name: eth1, ipv4: 192.168.2.10/24 }
|
|
||||||
- { name: eth2, 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:
|
|
||||||
- set interfaces ethernet eth0 address '192.168.0.1/24'
|
|
||||||
"""
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import re
|
|
||||||
|
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.common.utils import is_masklen, validate_ip_address
|
|
||||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
|
||||||
from ansible.module_utils.network.vyos.vyos import load_config, run_commands
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def is_ipv4(value):
|
|
||||||
if value:
|
|
||||||
address = value.split('/')
|
|
||||||
if is_masklen(address[1]) and validate_ip_address(address[0]):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_ipv6(value):
|
|
||||||
if value:
|
|
||||||
address = value.split('/')
|
|
||||||
if 0 <= int(address[1]) <= 128:
|
|
||||||
try:
|
|
||||||
socket.inet_pton(socket.AF_INET6, address[0])
|
|
||||||
except socket.error:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
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']
|
|
||||||
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
|
|
||||||
if state == 'absent' and obj_in_have:
|
|
||||||
if not ipv4 and not ipv6 and (obj_in_have['ipv4'] or obj_in_have['ipv6']):
|
|
||||||
if name == "lo":
|
|
||||||
commands.append('delete interfaces loopback lo address')
|
|
||||||
else:
|
|
||||||
commands.append('delete interfaces ethernet ' + name + ' address')
|
|
||||||
else:
|
|
||||||
if ipv4 and ipv4 in obj_in_have['ipv4']:
|
|
||||||
if name == "lo":
|
|
||||||
commands.append('delete interfaces loopback lo address ' + ipv4)
|
|
||||||
else:
|
|
||||||
commands.append('delete interfaces ethernet ' + name + ' address ' + ipv4)
|
|
||||||
if ipv6 and ipv6 in obj_in_have['ipv6']:
|
|
||||||
if name == "lo":
|
|
||||||
commands.append('delete interfaces loopback lo address ' + ipv6)
|
|
||||||
else:
|
|
||||||
commands.append('delete interfaces ethernet ' + name + ' address ' + ipv6)
|
|
||||||
elif (state == 'present' and obj_in_have):
|
|
||||||
if ipv4 and ipv4 not in obj_in_have['ipv4']:
|
|
||||||
if name == "lo":
|
|
||||||
commands.append('set interfaces loopback lo address ' + ipv4)
|
|
||||||
else:
|
|
||||||
commands.append('set interfaces ethernet ' + name + ' address ' + ipv4)
|
|
||||||
|
|
||||||
if ipv6 and ipv6 not in obj_in_have['ipv6']:
|
|
||||||
if name == "lo":
|
|
||||||
commands.append('set interfaces loopback lo address ' + ipv6)
|
|
||||||
else:
|
|
||||||
commands.append('set interfaces ethernet ' + name + ' address ' + ipv6)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def map_config_to_obj(module):
|
|
||||||
obj = []
|
|
||||||
output = run_commands(module, ['show interfaces'])
|
|
||||||
lines = re.split(r'\n[e|l]', output[0])[1:]
|
|
||||||
|
|
||||||
if len(lines) > 0:
|
|
||||||
for line in lines:
|
|
||||||
splitted_line = line.split()
|
|
||||||
|
|
||||||
if len(splitted_line) > 0:
|
|
||||||
ipv4 = []
|
|
||||||
ipv6 = []
|
|
||||||
|
|
||||||
if splitted_line[0].lower().startswith('th'):
|
|
||||||
name = 'e' + splitted_line[0].lower()
|
|
||||||
elif splitted_line[0].lower().startswith('o'):
|
|
||||||
name = 'l' + splitted_line[0].lower()
|
|
||||||
|
|
||||||
for i in splitted_line[1:]:
|
|
||||||
if (('.' in i or ':' in i) and '/' in i):
|
|
||||||
value = i.split(r'\n')[0]
|
|
||||||
if is_ipv4(value):
|
|
||||||
ipv4.append(value)
|
|
||||||
elif is_ipv6(value):
|
|
||||||
ipv6.append(value)
|
|
||||||
|
|
||||||
obj.append({'name': name,
|
|
||||||
'ipv4': ipv4,
|
|
||||||
'ipv6': ipv6})
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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'],
|
|
||||||
'ipv4': module.params['ipv4'],
|
|
||||||
'ipv6': module.params['ipv6'],
|
|
||||||
'state': module.params['state']
|
|
||||||
})
|
|
||||||
|
|
||||||
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(vyos_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), module)
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,274 +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': ['deprecated'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_linkagg
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
|
|
||||||
short_description: Manage link aggregation groups on VyOS network devices
|
|
||||||
description:
|
|
||||||
- This module provides declarative management of link aggregation groups
|
|
||||||
on VyOS network devices.
|
|
||||||
deprecated:
|
|
||||||
removed_in: '2.13'
|
|
||||||
alternative: vyos_lag_interfaces
|
|
||||||
why: Updated modules released with more functionality.
|
|
||||||
notes:
|
|
||||||
- Tested against VYOS 1.1.7
|
|
||||||
options:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Name of the link aggregation group.
|
|
||||||
required: true
|
|
||||||
type: str
|
|
||||||
mode:
|
|
||||||
description:
|
|
||||||
- Mode of the link aggregation group.
|
|
||||||
choices: ['802.3ad', 'active-backup', 'broadcast',
|
|
||||||
'round-robin', 'transmit-load-balance',
|
|
||||||
'adaptive-load-balance', 'xor-hash', 'on']
|
|
||||||
type: str
|
|
||||||
members:
|
|
||||||
description:
|
|
||||||
- List of members of the link aggregation group.
|
|
||||||
type: list
|
|
||||||
aggregate:
|
|
||||||
description: List of link aggregation definitions.
|
|
||||||
type: list
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- State of the link aggregation group.
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent', 'up', 'down']
|
|
||||||
type: str
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: configure link aggregation group
|
|
||||||
vyos_linkagg:
|
|
||||||
name: bond0
|
|
||||||
members:
|
|
||||||
- eth0
|
|
||||||
- eth1
|
|
||||||
|
|
||||||
- name: remove configuration
|
|
||||||
vyos_linkagg:
|
|
||||||
name: bond0
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Create aggregate of linkagg definitions
|
|
||||||
vyos_linkagg:
|
|
||||||
aggregate:
|
|
||||||
- { name: bond0, members: [eth1] }
|
|
||||||
- { name: bond1, members: [eth2] }
|
|
||||||
|
|
||||||
- name: Remove aggregate of linkagg definitions
|
|
||||||
vyos_linkagg:
|
|
||||||
aggregate:
|
|
||||||
- name: bond0
|
|
||||||
- name: bond1
|
|
||||||
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:
|
|
||||||
- set interfaces bonding bond0
|
|
||||||
- set interfaces ethernet eth0 bond-group 'bond0'
|
|
||||||
- set interfaces ethernet eth1 bond-group 'bond0'
|
|
||||||
"""
|
|
||||||
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.vyos.vyos import load_config, run_commands
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
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']
|
|
||||||
members = w.get('members') or []
|
|
||||||
mode = w['mode']
|
|
||||||
|
|
||||||
if mode == 'on':
|
|
||||||
mode = '802.3ad'
|
|
||||||
|
|
||||||
state = w['state']
|
|
||||||
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
if obj_in_have:
|
|
||||||
for m in obj_in_have['members']:
|
|
||||||
commands.append('delete interfaces ethernet ' + m + ' bond-group')
|
|
||||||
|
|
||||||
commands.append('delete interfaces bonding ' + name)
|
|
||||||
else:
|
|
||||||
if not obj_in_have:
|
|
||||||
commands.append('set interfaces bonding ' + name + ' mode ' + mode)
|
|
||||||
|
|
||||||
for m in members:
|
|
||||||
commands.append('set interfaces ethernet ' + m + ' bond-group ' + name)
|
|
||||||
|
|
||||||
if state == 'down':
|
|
||||||
commands.append('set interfaces bonding ' + name + ' disable')
|
|
||||||
else:
|
|
||||||
if mode != obj_in_have['mode']:
|
|
||||||
commands.append('set interfaces bonding ' + name + ' mode ' + mode)
|
|
||||||
|
|
||||||
missing_members = list(set(members) - set(obj_in_have['members']))
|
|
||||||
for m in missing_members:
|
|
||||||
commands.append('set interfaces ethernet ' + m + ' bond-group ' + name)
|
|
||||||
|
|
||||||
if state == 'down' and obj_in_have['state'] == 'up':
|
|
||||||
commands.append('set interfaces bonding ' + name + ' disable')
|
|
||||||
elif state == 'up' and obj_in_have['state'] == 'down':
|
|
||||||
commands.append('delete interfaces bonding ' + name + ' disable')
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def map_config_to_obj(module):
|
|
||||||
obj = []
|
|
||||||
output = run_commands(module, ['show interfaces bonding slaves'])
|
|
||||||
lines = output[0].splitlines()
|
|
||||||
|
|
||||||
if len(lines) > 1:
|
|
||||||
for line in lines[1:]:
|
|
||||||
splitted_line = line.split()
|
|
||||||
|
|
||||||
name = splitted_line[0]
|
|
||||||
mode = splitted_line[1]
|
|
||||||
state = splitted_line[2]
|
|
||||||
|
|
||||||
if len(splitted_line) > 4:
|
|
||||||
members = splitted_line[4:]
|
|
||||||
else:
|
|
||||||
members = []
|
|
||||||
|
|
||||||
obj.append({'name': name,
|
|
||||||
'mode': mode,
|
|
||||||
'members': members,
|
|
||||||
'state': state})
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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'],
|
|
||||||
'members': module.params['members'],
|
|
||||||
'state': module.params['state']
|
|
||||||
})
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" main entry point for module execution
|
|
||||||
"""
|
|
||||||
element_spec = dict(
|
|
||||||
name=dict(),
|
|
||||||
mode=dict(choices=['802.3ad', 'active-backup', 'broadcast',
|
|
||||||
'round-robin', 'transmit-load-balance',
|
|
||||||
'adaptive-load-balance', 'xor-hash', 'on'],
|
|
||||||
default='802.3ad'),
|
|
||||||
members=dict(type='list'),
|
|
||||||
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(vyos_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), module)
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,134 +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/>.
|
|
||||||
#
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
||||||
'status': ['deprecated'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_lldp
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
|
|
||||||
short_description: Manage LLDP configuration on VyOS network devices
|
|
||||||
description:
|
|
||||||
- This module provides declarative management of LLDP service
|
|
||||||
on VyOS network devices.
|
|
||||||
deprecated:
|
|
||||||
removed_in: '2.13'
|
|
||||||
alternative: vyos_lldp_global
|
|
||||||
why: Updated modules released with more functionality.
|
|
||||||
notes:
|
|
||||||
- Tested against VYOS 1.1.7
|
|
||||||
options:
|
|
||||||
interfaces:
|
|
||||||
description:
|
|
||||||
- Name of the interfaces.
|
|
||||||
type: list
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- State of the link aggregation group.
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent', 'enabled', 'disabled']
|
|
||||||
type: str
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: Enable LLDP service
|
|
||||||
vyos_lldp:
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Disable LLDP service
|
|
||||||
vyos_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:
|
|
||||||
- set service lldp
|
|
||||||
"""
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.vyos import get_config, load_config
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def has_lldp(module):
|
|
||||||
config = get_config(module).splitlines()
|
|
||||||
|
|
||||||
if "set service 'lldp'" in config or 'set service lldp' in config:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" main entry point for module execution
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
interfaces=dict(type='list'),
|
|
||||||
state=dict(default='present',
|
|
||||||
choices=['present', 'absent',
|
|
||||||
'enabled', 'disabled'])
|
|
||||||
)
|
|
||||||
|
|
||||||
argument_spec.update(vyos_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('delete service lldp')
|
|
||||||
elif module.params['state'] == 'present' and not HAS_LLDP:
|
|
||||||
commands.append('set service lldp')
|
|
||||||
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,238 +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': ['deprecated'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_lldp_interface
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
|
|
||||||
short_description: Manage LLDP interfaces configuration on VyOS network devices
|
|
||||||
description:
|
|
||||||
- This module provides declarative management of LLDP interfaces
|
|
||||||
configuration on VyOS network devices.
|
|
||||||
deprecated:
|
|
||||||
removed_in: '2.13'
|
|
||||||
alternative: vyos_lldp_interfaces
|
|
||||||
why: Updated modules released with more functionality.
|
|
||||||
notes:
|
|
||||||
- Tested against VYOS 1.1.7
|
|
||||||
options:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Name of the interface LLDP should be configured on.
|
|
||||||
type: str
|
|
||||||
aggregate:
|
|
||||||
description: List of interfaces LLDP should be configured on.
|
|
||||||
type: list
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- State of the LLDP configuration.
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent', 'enabled', 'disabled']
|
|
||||||
type: str
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: Enable LLDP on eth1
|
|
||||||
net_lldp_interface:
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Enable LLDP on specific interfaces
|
|
||||||
net_lldp_interface:
|
|
||||||
interfaces:
|
|
||||||
- eth1
|
|
||||||
- eth2
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Disable LLDP globally
|
|
||||||
net_lldp_interface:
|
|
||||||
state: disabled
|
|
||||||
|
|
||||||
- name: Create aggregate of LLDP interface configurations
|
|
||||||
vyos_lldp_interface:
|
|
||||||
aggregate:
|
|
||||||
- name: eth1
|
|
||||||
- name: eth2
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Delete aggregate of LLDP interface configurations
|
|
||||||
vyos_lldp_interface:
|
|
||||||
aggregate:
|
|
||||||
- name: eth1
|
|
||||||
- name: eth2
|
|
||||||
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:
|
|
||||||
- set service lldp eth1
|
|
||||||
- set service lldp eth2 disable
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
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.vyos.vyos import get_config, load_config
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
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']
|
|
||||||
state = w['state']
|
|
||||||
|
|
||||||
obj_in_have = search_obj_in_list(name, have)
|
|
||||||
|
|
||||||
if state == 'absent' and obj_in_have:
|
|
||||||
commands.append('delete service lldp interface ' + name)
|
|
||||||
elif state in ('present', 'enabled'):
|
|
||||||
if not obj_in_have:
|
|
||||||
commands.append('set service lldp interface ' + name)
|
|
||||||
elif obj_in_have and obj_in_have['state'] == 'disabled' and state == 'enabled':
|
|
||||||
commands.append('delete service lldp interface ' + name + ' disable')
|
|
||||||
elif state == 'disabled':
|
|
||||||
if not obj_in_have:
|
|
||||||
commands.append('set service lldp interface ' + name)
|
|
||||||
commands.append('set service lldp interface ' + name + ' disable')
|
|
||||||
elif obj_in_have and obj_in_have['state'] != 'disabled':
|
|
||||||
commands.append('set service lldp interface ' + name + ' disable')
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def map_config_to_obj(module):
|
|
||||||
obj = []
|
|
||||||
config = get_config(module).splitlines()
|
|
||||||
|
|
||||||
output = [c for c in config if c.startswith("set service lldp interface")]
|
|
||||||
|
|
||||||
for i in output:
|
|
||||||
splitted_line = i.split()
|
|
||||||
|
|
||||||
if len(splitted_line) > 5:
|
|
||||||
new_obj = {'name': splitted_line[4]}
|
|
||||||
|
|
||||||
if splitted_line[5] == "'disable'":
|
|
||||||
new_obj['state'] = 'disabled'
|
|
||||||
else:
|
|
||||||
new_obj = {'name': splitted_line[4][1:-1]}
|
|
||||||
new_obj['state'] = 'present'
|
|
||||||
|
|
||||||
obj.append(new_obj)
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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'], 'state': module.params['state']})
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" main entry point for module execution
|
|
||||||
"""
|
|
||||||
element_spec = dict(
|
|
||||||
name=dict(),
|
|
||||||
state=dict(default='present',
|
|
||||||
choices=['present', 'absent',
|
|
||||||
'enabled', 'disabled'])
|
|
||||||
)
|
|
||||||
|
|
||||||
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(vyos_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), module)
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,276 +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': ['deprecated'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_static_route
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Trishna Guha (@trishnaguha)"
|
|
||||||
short_description: Manage static IP routes on Vyatta VyOS network devices
|
|
||||||
description:
|
|
||||||
- This module provides declarative management of static
|
|
||||||
IP routes on Vyatta VyOS network devices.
|
|
||||||
deprecated:
|
|
||||||
removed_in: '2.13'
|
|
||||||
alternative: vyos_static_routes
|
|
||||||
why: Updated modules released with more functionality.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
prefix:
|
|
||||||
description:
|
|
||||||
- Network prefix of the static route.
|
|
||||||
C(mask) param should be ignored if C(prefix) is provided
|
|
||||||
with C(mask) value C(prefix/mask).
|
|
||||||
type: str
|
|
||||||
mask:
|
|
||||||
description:
|
|
||||||
- Network prefix mask of the static route.
|
|
||||||
type: str
|
|
||||||
next_hop:
|
|
||||||
description:
|
|
||||||
- Next hop IP of the static route.
|
|
||||||
type: str
|
|
||||||
admin_distance:
|
|
||||||
description:
|
|
||||||
- Admin distance of the static route.
|
|
||||||
type: int
|
|
||||||
aggregate:
|
|
||||||
description: List of static route definitions
|
|
||||||
type: list
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- State of the static route configuration.
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
type: str
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: configure static route
|
|
||||||
vyos_static_route:
|
|
||||||
prefix: 192.168.2.0
|
|
||||||
mask: 24
|
|
||||||
next_hop: 10.0.0.1
|
|
||||||
|
|
||||||
- name: configure static route prefix/mask
|
|
||||||
vyos_static_route:
|
|
||||||
prefix: 192.168.2.0/16
|
|
||||||
next_hop: 10.0.0.1
|
|
||||||
|
|
||||||
- name: remove configuration
|
|
||||||
vyos_static_route:
|
|
||||||
prefix: 192.168.2.0
|
|
||||||
mask: 16
|
|
||||||
next_hop: 10.0.0.1
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: configure aggregates of static routes
|
|
||||||
vyos_static_route:
|
|
||||||
aggregate:
|
|
||||||
- { prefix: 192.168.2.0, mask: 24, next_hop: 10.0.0.1 }
|
|
||||||
- { prefix: 192.168.3.0, mask: 16, next_hop: 10.0.2.1 }
|
|
||||||
- { prefix: 192.168.3.0/16, next_hop: 10.0.2.1 }
|
|
||||||
|
|
||||||
- name: Remove static route collections
|
|
||||||
vyos_static_route:
|
|
||||||
aggregate:
|
|
||||||
- { prefix: 172.24.1.0/24, next_hop: 192.168.42.64 }
|
|
||||||
- { prefix: 172.24.3.0/24, next_hop: 192.168.42.64 }
|
|
||||||
state: absent
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: The list of configuration mode commands to send to the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample:
|
|
||||||
- set protocols static route 192.168.2.0/16 next-hop 10.0.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
|
|
||||||
from ansible.module_utils.network.vyos.vyos import get_config, load_config
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def spec_to_commands(updates, module):
|
|
||||||
commands = list()
|
|
||||||
want, have = updates
|
|
||||||
for w in want:
|
|
||||||
prefix = w['prefix']
|
|
||||||
mask = w['mask']
|
|
||||||
next_hop = w['next_hop']
|
|
||||||
admin_distance = w['admin_distance']
|
|
||||||
state = w['state']
|
|
||||||
del w['state']
|
|
||||||
|
|
||||||
if state == 'absent' and w in have:
|
|
||||||
commands.append('delete protocols static route %s/%s' % (prefix, mask))
|
|
||||||
elif state == 'present' and w not in have:
|
|
||||||
cmd = 'set protocols static route %s/%s next-hop %s' % (prefix, mask, next_hop)
|
|
||||||
if admin_distance != 'None':
|
|
||||||
cmd += ' distance %s' % (admin_distance)
|
|
||||||
commands.append(cmd)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def config_to_dict(module):
|
|
||||||
data = get_config(module)
|
|
||||||
obj = []
|
|
||||||
|
|
||||||
for line in data.split('\n'):
|
|
||||||
if line.startswith('set protocols static route'):
|
|
||||||
match = re.search(r'static route (\S+)', line, re.M)
|
|
||||||
prefix = match.group(1).split('/')[0]
|
|
||||||
mask = match.group(1).split('/')[1]
|
|
||||||
if 'next-hop' in line:
|
|
||||||
match_hop = re.search(r'next-hop (\S+)', line, re.M)
|
|
||||||
next_hop = match_hop.group(1).strip("'")
|
|
||||||
|
|
||||||
match_distance = re.search(r'distance (\S+)', line, re.M)
|
|
||||||
if match_distance is not None:
|
|
||||||
admin_distance = match_distance.group(1)[1:-1]
|
|
||||||
else:
|
|
||||||
admin_distance = None
|
|
||||||
|
|
||||||
if admin_distance is not None:
|
|
||||||
obj.append({'prefix': prefix,
|
|
||||||
'mask': mask,
|
|
||||||
'next_hop': next_hop,
|
|
||||||
'admin_distance': admin_distance})
|
|
||||||
else:
|
|
||||||
obj.append({'prefix': prefix,
|
|
||||||
'mask': mask,
|
|
||||||
'next_hop': next_hop,
|
|
||||||
'admin_distance': 'None'})
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def map_params_to_obj(module, required_together=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_together(required_together, item)
|
|
||||||
d = item.copy()
|
|
||||||
if '/' in d['prefix']:
|
|
||||||
d['mask'] = d['prefix'].split('/')[1]
|
|
||||||
d['prefix'] = d['prefix'].split('/')[0]
|
|
||||||
|
|
||||||
if 'admin_distance' in d:
|
|
||||||
d['admin_distance'] = str(d['admin_distance'])
|
|
||||||
|
|
||||||
obj.append(d)
|
|
||||||
else:
|
|
||||||
prefix = module.params['prefix'].strip()
|
|
||||||
if '/' in prefix:
|
|
||||||
mask = prefix.split('/')[1]
|
|
||||||
prefix = prefix.split('/')[0]
|
|
||||||
else:
|
|
||||||
mask = module.params['mask'].strip()
|
|
||||||
next_hop = module.params['next_hop'].strip()
|
|
||||||
admin_distance = str(module.params['admin_distance'])
|
|
||||||
state = module.params['state']
|
|
||||||
|
|
||||||
obj.append({
|
|
||||||
'prefix': prefix,
|
|
||||||
'mask': mask,
|
|
||||||
'next_hop': next_hop,
|
|
||||||
'admin_distance': admin_distance,
|
|
||||||
'state': state
|
|
||||||
})
|
|
||||||
|
|
||||||
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'),
|
|
||||||
admin_distance=dict(type='int'),
|
|
||||||
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(vyos_argument_spec)
|
|
||||||
|
|
||||||
required_one_of = [['aggregate', 'prefix']]
|
|
||||||
required_together = [['prefix', 'next_hop']]
|
|
||||||
mutually_exclusive = [['aggregate', 'prefix']]
|
|
||||||
|
|
||||||
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, required_together=required_together)
|
|
||||||
have = config_to_dict(module)
|
|
||||||
|
|
||||||
commands = spec_to_commands((want, have), module)
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,179 +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: vyos_banner
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Trishna Guha (@trishnaguha)"
|
|
||||||
short_description: Manage multiline banners on VyOS devices
|
|
||||||
description:
|
|
||||||
- This will configure both pre-login and post-login banners on remote
|
|
||||||
devices running VyOS. It allows playbooks to add or remote
|
|
||||||
banner text from the active running configuration.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
banner:
|
|
||||||
description:
|
|
||||||
- Specifies which banner that should be
|
|
||||||
configured on the remote device.
|
|
||||||
required: true
|
|
||||||
choices: ['pre-login', 'post-login']
|
|
||||||
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']
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: configure the pre-login banner
|
|
||||||
vyos_banner:
|
|
||||||
banner: pre-login
|
|
||||||
text: |
|
|
||||||
this is my pre-login banner
|
|
||||||
that contains a multiline
|
|
||||||
string
|
|
||||||
state: present
|
|
||||||
- name: remove the post-login banner
|
|
||||||
vyos_banner:
|
|
||||||
banner: post-login
|
|
||||||
state: absent
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: The list of configuration mode commands to send to the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample:
|
|
||||||
- banner pre-login
|
|
||||||
- this is my pre-login banner
|
|
||||||
- that contains a multiline
|
|
||||||
- string
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.vyos import get_config, load_config
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def spec_to_commands(updates, module):
|
|
||||||
commands = list()
|
|
||||||
want, have = updates
|
|
||||||
state = module.params['state']
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
if have.get('state') != 'absent' or (have.get('state') != 'absent' and
|
|
||||||
'text' in have.keys() and have['text']):
|
|
||||||
commands.append('delete system login banner %s' % module.params['banner'])
|
|
||||||
|
|
||||||
elif state == 'present':
|
|
||||||
if want['text'] and want['text'].encode().decode('unicode_escape') != have.get('text'):
|
|
||||||
banner_cmd = 'set system login banner %s ' % module.params['banner']
|
|
||||||
banner_cmd += want['text'].strip()
|
|
||||||
commands.append(banner_cmd)
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def config_to_dict(module):
|
|
||||||
data = get_config(module)
|
|
||||||
output = None
|
|
||||||
obj = {'banner': module.params['banner'], 'state': 'absent'}
|
|
||||||
|
|
||||||
for line in data.split('\n'):
|
|
||||||
if line.startswith('set system login banner %s' % obj['banner']):
|
|
||||||
match = re.findall(r'%s (.*)' % obj['banner'], line, re.M)
|
|
||||||
output = match
|
|
||||||
if output:
|
|
||||||
obj['text'] = output[0].encode().decode('unicode_escape')
|
|
||||||
obj['state'] = 'present'
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def map_params_to_obj(module):
|
|
||||||
text = module.params['text']
|
|
||||||
if text:
|
|
||||||
text = "%r" % (str(text).strip())
|
|
||||||
|
|
||||||
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=['pre-login', 'post-login']),
|
|
||||||
text=dict(),
|
|
||||||
state=dict(default='present', choices=['present', 'absent'])
|
|
||||||
)
|
|
||||||
|
|
||||||
argument_spec.update(vyos_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 = config_to_dict(module)
|
|
||||||
|
|
||||||
commands = spec_to_commands((want, have), module)
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,223 +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: vyos_command
|
|
||||||
version_added: "2.2"
|
|
||||||
author: "Nathaniel Case (@Qalthos)"
|
|
||||||
short_description: Run one or more commands on VyOS devices
|
|
||||||
description:
|
|
||||||
- The command module allows running one or more commands on remote
|
|
||||||
devices running VyOS. This module can also be introspected
|
|
||||||
to validate key parameters before returning successfully. If the
|
|
||||||
conditional statements are not met in the wait period, the task
|
|
||||||
fails.
|
|
||||||
- Certain C(show) commands in VyOS produce many lines of output and
|
|
||||||
use a custom pager that can cause this module to hang. If the
|
|
||||||
value of the environment variable C(ANSIBLE_VYOS_TERMINAL_LENGTH)
|
|
||||||
is not set, the default number of 10000 is used.
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
options:
|
|
||||||
commands:
|
|
||||||
description:
|
|
||||||
- The ordered set of commands to execute on the remote device
|
|
||||||
running VyOS. The output from the command execution is
|
|
||||||
returned to the playbook. If the I(wait_for) argument is
|
|
||||||
provided, the module is not returned until the condition is
|
|
||||||
satisfied or the number of retries has been exceeded.
|
|
||||||
required: true
|
|
||||||
wait_for:
|
|
||||||
description:
|
|
||||||
- Specifies what to evaluate from the output of the command
|
|
||||||
and what conditionals to apply. This argument will cause
|
|
||||||
the task to wait for a particular conditional to be true
|
|
||||||
before moving forward. If the conditional is not true
|
|
||||||
by the configured I(retries), the task fails. See examples.
|
|
||||||
aliases: ['waitfor']
|
|
||||||
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']
|
|
||||||
retries:
|
|
||||||
description:
|
|
||||||
- Specifies the number of retries a command should be tried
|
|
||||||
before it is considered failed. The command is run on the
|
|
||||||
target device every retry and evaluated against the I(wait_for)
|
|
||||||
conditionals.
|
|
||||||
default: 10
|
|
||||||
interval:
|
|
||||||
description:
|
|
||||||
- Configures the interval in seconds to wait between I(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
|
|
||||||
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- Running C(show system boot-messages all) will cause the module to hang since
|
|
||||||
VyOS is using a custom pager setting to display the output of that command.
|
|
||||||
- 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). See examples.
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
tasks:
|
|
||||||
- name: show configuration on ethernet devices eth0 and eth1
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show interfaces ethernet {{ item }}
|
|
||||||
with_items:
|
|
||||||
- eth0
|
|
||||||
- eth1
|
|
||||||
|
|
||||||
- name: run multiple commands and check if version output contains specific version string
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show version
|
|
||||||
- show hardware cpu
|
|
||||||
wait_for:
|
|
||||||
- "result[0] contains 'VyOS 1.1.7'"
|
|
||||||
|
|
||||||
- name: run command that requires answering a prompt
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- command: 'rollback 1'
|
|
||||||
prompt: 'Proceed with reboot? [confirm][y]'
|
|
||||||
answer: y
|
|
||||||
"""
|
|
||||||
|
|
||||||
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
|
|
||||||
type: list
|
|
||||||
sample: [['...', '...'], ['...'], ['...']]
|
|
||||||
failed_conditions:
|
|
||||||
description: The list of conditionals that have failed
|
|
||||||
returned: failed
|
|
||||||
type: list
|
|
||||||
sample: ['...', '...']
|
|
||||||
warnings:
|
|
||||||
description: The list of warnings (if any) generated by module based on arguments
|
|
||||||
returned: always
|
|
||||||
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.vyos.vyos import run_commands
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_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():
|
|
||||||
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')
|
|
||||||
)
|
|
||||||
|
|
||||||
spec.update(vyos_argument_spec)
|
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=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']
|
|
||||||
|
|
||||||
for _ in range(retries):
|
|
||||||
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)
|
|
||||||
|
|
||||||
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,349 +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: vyos_config
|
|
||||||
version_added: "2.2"
|
|
||||||
author: "Nathaniel Case (@Qalthos)"
|
|
||||||
short_description: Manage VyOS configuration on remote device
|
|
||||||
description:
|
|
||||||
- This module provides configuration file management of VyOS
|
|
||||||
devices. It provides arguments for managing both the
|
|
||||||
configuration file and state of the active configuration. All
|
|
||||||
configuration statements are based on `set` and `delete` commands
|
|
||||||
in the device configuration.
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
lines:
|
|
||||||
description:
|
|
||||||
- The ordered set of configuration lines to be managed and
|
|
||||||
compared with the existing configuration on the remote
|
|
||||||
device.
|
|
||||||
src:
|
|
||||||
description:
|
|
||||||
- The C(src) argument specifies the path to the source config
|
|
||||||
file to load. The source config file can either be in
|
|
||||||
bracket format or set format. The source file can include
|
|
||||||
Jinja2 template variables.
|
|
||||||
match:
|
|
||||||
description:
|
|
||||||
- The C(match) argument controls the method used to match
|
|
||||||
against the current active configuration. By default, the
|
|
||||||
desired config is matched against the active config and the
|
|
||||||
deltas are loaded. If the C(match) argument is set to C(none)
|
|
||||||
the active configuration is ignored and the configuration is
|
|
||||||
always loaded.
|
|
||||||
default: line
|
|
||||||
choices: ['line', 'none']
|
|
||||||
backup:
|
|
||||||
description:
|
|
||||||
- The C(backup) argument will backup the current devices active
|
|
||||||
configuration to the Ansible control host prior to making any
|
|
||||||
changes. If the C(backup_options) value is not given, the
|
|
||||||
backup file will be located in the 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'
|
|
||||||
comment:
|
|
||||||
description:
|
|
||||||
- Allows a commit description to be specified to be included
|
|
||||||
when the configuration is committed. If the configuration is
|
|
||||||
not changed or committed, this argument is ignored.
|
|
||||||
default: 'configured by vyos_config'
|
|
||||||
config:
|
|
||||||
description:
|
|
||||||
- The C(config) argument specifies the base configuration to use
|
|
||||||
to compare against the desired configuration. If this value
|
|
||||||
is not specified, the module will automatically retrieve the
|
|
||||||
current active configuration from the remote device.
|
|
||||||
save:
|
|
||||||
description:
|
|
||||||
- The C(save) argument controls whether or not changes made
|
|
||||||
to the active configuration are saved to disk. This is
|
|
||||||
independent of committing the config. When set to True, the
|
|
||||||
active configuration is saved.
|
|
||||||
type: bool
|
|
||||||
default: 'no'
|
|
||||||
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 the remote device
|
|
||||||
vyos_config:
|
|
||||||
lines:
|
|
||||||
- set system host-name {{ inventory_hostname }}
|
|
||||||
- set service lldp
|
|
||||||
- delete service dhcp-server
|
|
||||||
|
|
||||||
- name: backup and load from file
|
|
||||||
vyos_config:
|
|
||||||
src: vyos.cfg
|
|
||||||
backup: yes
|
|
||||||
|
|
||||||
- name: render a Jinja2 template onto the VyOS router
|
|
||||||
vyos_config:
|
|
||||||
src: vyos_template.j2
|
|
||||||
|
|
||||||
- name: for idempotency, use full-form commands
|
|
||||||
vyos_config:
|
|
||||||
lines:
|
|
||||||
# - set int eth eth2 description 'OUTSIDE'
|
|
||||||
- set interface ethernet eth2 description 'OUTSIDE'
|
|
||||||
|
|
||||||
- name: configurable backup path
|
|
||||||
vyos_config:
|
|
||||||
backup: yes
|
|
||||||
backup_options:
|
|
||||||
filename: backup.cfg
|
|
||||||
dir_path: /home/user
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: The list of configuration commands sent to the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample: ['...', '...']
|
|
||||||
filtered:
|
|
||||||
description: The list of configuration commands removed to avoid a load failure
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample: ['...', '...']
|
|
||||||
backup_path:
|
|
||||||
description: The full path to the backup file
|
|
||||||
returned: when backup is yes
|
|
||||||
type: str
|
|
||||||
sample: /playbooks/ansible/backup/vyos_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: vyos_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/vyos_config
|
|
||||||
date:
|
|
||||||
description: The date extracted from the backup file name
|
|
||||||
returned: when backup is yes
|
|
||||||
type: str
|
|
||||||
sample: "2016-07-16"
|
|
||||||
time:
|
|
||||||
description: The time extracted from the backup file name
|
|
||||||
returned: when backup is yes
|
|
||||||
type: str
|
|
||||||
sample: "22:28:34"
|
|
||||||
"""
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils._text import to_text
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.connection import ConnectionError
|
|
||||||
from ansible.module_utils.network.vyos.vyos import load_config, get_config, run_commands
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec, get_connection
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_COMMENT = 'configured by vyos_config'
|
|
||||||
|
|
||||||
CONFIG_FILTERS = [
|
|
||||||
re.compile(r'set system login user \S+ authentication encrypted-password')
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def get_candidate(module):
|
|
||||||
contents = module.params['src'] or module.params['lines']
|
|
||||||
|
|
||||||
if module.params['src']:
|
|
||||||
contents = format_commands(contents.splitlines())
|
|
||||||
|
|
||||||
contents = '\n'.join(contents)
|
|
||||||
return contents
|
|
||||||
|
|
||||||
|
|
||||||
def format_commands(commands):
|
|
||||||
"""
|
|
||||||
This function format the input commands and removes the prepend white spaces
|
|
||||||
for command lines having 'set' or 'delete' and it skips empty lines.
|
|
||||||
:param commands:
|
|
||||||
:return: list of commands
|
|
||||||
"""
|
|
||||||
return [line.strip() if line.split()[0] in ('set', 'delete') else line for line in commands if len(line.strip()) > 0]
|
|
||||||
|
|
||||||
|
|
||||||
def diff_config(commands, config):
|
|
||||||
config = [str(c).replace("'", '') for c in config.splitlines()]
|
|
||||||
|
|
||||||
updates = list()
|
|
||||||
visited = set()
|
|
||||||
|
|
||||||
for line in commands:
|
|
||||||
item = str(line).replace("'", '')
|
|
||||||
|
|
||||||
if not item.startswith('set') and not item.startswith('delete'):
|
|
||||||
raise ValueError('line must start with either `set` or `delete`')
|
|
||||||
|
|
||||||
elif item.startswith('set') and item not in config:
|
|
||||||
updates.append(line)
|
|
||||||
|
|
||||||
elif item.startswith('delete'):
|
|
||||||
if not config:
|
|
||||||
updates.append(line)
|
|
||||||
else:
|
|
||||||
item = re.sub(r'delete', 'set', item)
|
|
||||||
for entry in config:
|
|
||||||
if entry.startswith(item) and line not in visited:
|
|
||||||
updates.append(line)
|
|
||||||
visited.add(line)
|
|
||||||
|
|
||||||
return list(updates)
|
|
||||||
|
|
||||||
|
|
||||||
def sanitize_config(config, result):
|
|
||||||
result['filtered'] = list()
|
|
||||||
index_to_filter = list()
|
|
||||||
for regex in CONFIG_FILTERS:
|
|
||||||
for index, line in enumerate(list(config)):
|
|
||||||
if regex.search(line):
|
|
||||||
result['filtered'].append(line)
|
|
||||||
index_to_filter.append(index)
|
|
||||||
# Delete all filtered configs
|
|
||||||
for filter_index in sorted(index_to_filter, reverse=True):
|
|
||||||
del config[filter_index]
|
|
||||||
|
|
||||||
|
|
||||||
def run(module, result):
|
|
||||||
# get the current active config from the node or passed in via
|
|
||||||
# the config param
|
|
||||||
config = module.params['config'] or get_config(module)
|
|
||||||
|
|
||||||
# create the candidate config object from the arguments
|
|
||||||
candidate = get_candidate(module)
|
|
||||||
|
|
||||||
# create loadable config that includes only the configuration updates
|
|
||||||
connection = get_connection(module)
|
|
||||||
try:
|
|
||||||
response = connection.get_diff(candidate=candidate, running=config, diff_match=module.params['match'])
|
|
||||||
except ConnectionError as exc:
|
|
||||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
|
||||||
|
|
||||||
commands = response.get('config_diff')
|
|
||||||
sanitize_config(commands, result)
|
|
||||||
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
commit = not module.check_mode
|
|
||||||
comment = module.params['comment']
|
|
||||||
|
|
||||||
diff = None
|
|
||||||
if commands:
|
|
||||||
diff = load_config(module, commands, commit=commit, comment=comment)
|
|
||||||
|
|
||||||
if result.get('filtered'):
|
|
||||||
result['warnings'].append('Some configuration commands were '
|
|
||||||
'removed, please see the filtered key')
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
if module._diff:
|
|
||||||
result['diff'] = {'prepared': diff}
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
backup_spec = dict(
|
|
||||||
filename=dict(),
|
|
||||||
dir_path=dict(type='path')
|
|
||||||
)
|
|
||||||
argument_spec = dict(
|
|
||||||
src=dict(type='path'),
|
|
||||||
lines=dict(type='list'),
|
|
||||||
|
|
||||||
match=dict(default='line', choices=['line', 'none']),
|
|
||||||
|
|
||||||
comment=dict(default=DEFAULT_COMMENT),
|
|
||||||
|
|
||||||
config=dict(),
|
|
||||||
|
|
||||||
backup=dict(type='bool', default=False),
|
|
||||||
backup_options=dict(type='dict', options=backup_spec),
|
|
||||||
save=dict(type='bool', default=False),
|
|
||||||
)
|
|
||||||
|
|
||||||
argument_spec.update(vyos_argument_spec)
|
|
||||||
|
|
||||||
mutually_exclusive = [('lines', 'src')]
|
|
||||||
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=argument_spec,
|
|
||||||
mutually_exclusive=mutually_exclusive,
|
|
||||||
supports_check_mode=True
|
|
||||||
)
|
|
||||||
|
|
||||||
warnings = list()
|
|
||||||
|
|
||||||
result = dict(changed=False, warnings=warnings)
|
|
||||||
|
|
||||||
if module.params['backup']:
|
|
||||||
result['__backup__'] = get_config(module=module)
|
|
||||||
|
|
||||||
if any((module.params['src'], module.params['lines'])):
|
|
||||||
run(module, result)
|
|
||||||
|
|
||||||
if module.params['save']:
|
|
||||||
diff = run_commands(module, commands=['configure', 'compare saved'])[1]
|
|
||||||
if diff != '[edit]':
|
|
||||||
run_commands(module, commands=['save'])
|
|
||||||
result['changed'] = True
|
|
||||||
run_commands(module, commands=['exit'])
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,171 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright 2019 Red Hat
|
|
||||||
# GNU General Public License v3.0+
|
|
||||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
"""
|
|
||||||
The module file for vyos_facts
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
||||||
'status': [u'preview'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_facts
|
|
||||||
version_added: 2.2
|
|
||||||
short_description: Get facts about vyos devices.
|
|
||||||
description:
|
|
||||||
- Collects facts from network devices running the vyos operating
|
|
||||||
system. This module places the facts gathered in the fact tree keyed by the
|
|
||||||
respective resource name. The facts module will always collect a
|
|
||||||
base set of facts from the device and can enable or disable
|
|
||||||
collection of additional facts.
|
|
||||||
author:
|
|
||||||
- Nathaniel Case (@qalthos)
|
|
||||||
- Nilashish Chakraborty (@Nilashishc)
|
|
||||||
- Rohit Thakur (@rohitthakur2590)
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
gather_subset:
|
|
||||||
description:
|
|
||||||
- When supplied, this argument will restrict the facts collected
|
|
||||||
to a given subset. Possible values for this argument include
|
|
||||||
all, default, config, and neighbors. Can specify a list of
|
|
||||||
values to include a larger subset. Values can also be used
|
|
||||||
with an initial C(M(!)) to specify that a specific subset should
|
|
||||||
not be collected.
|
|
||||||
required: false
|
|
||||||
default: "!config"
|
|
||||||
gather_network_resources:
|
|
||||||
description:
|
|
||||||
- When supplied, this argument will restrict the facts collected
|
|
||||||
to a given subset. Possible values for this argument include
|
|
||||||
all and the resources like interfaces.
|
|
||||||
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', 'l3_interfaces', 'lag_interfaces',
|
|
||||||
'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global', 'firewall_interfaces'.
|
|
||||||
required: false
|
|
||||||
version_added: "2.9"
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
# Gather all facts
|
|
||||||
- vyos_facts:
|
|
||||||
gather_subset: all
|
|
||||||
gather_network_resources: all
|
|
||||||
|
|
||||||
# collect only the config and default facts
|
|
||||||
- vyos_facts:
|
|
||||||
gather_subset: config
|
|
||||||
|
|
||||||
# collect everything exception the config
|
|
||||||
- vyos_facts:
|
|
||||||
gather_subset: "!config"
|
|
||||||
|
|
||||||
# Collect only the interfaces facts
|
|
||||||
- vyos_facts:
|
|
||||||
gather_subset:
|
|
||||||
- '!all'
|
|
||||||
- '!min'
|
|
||||||
gather_network_resources:
|
|
||||||
- interfaces
|
|
||||||
|
|
||||||
# Do not collect interfaces facts
|
|
||||||
- vyos_facts:
|
|
||||||
gather_network_resources:
|
|
||||||
- "!interfaces"
|
|
||||||
|
|
||||||
# Collect interfaces and minimal default facts
|
|
||||||
- vyos_facts:
|
|
||||||
gather_subset: min
|
|
||||||
gather_network_resources: interfaces
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
ansible_net_config:
|
|
||||||
description: The running-config from the device
|
|
||||||
returned: when config is configured
|
|
||||||
type: str
|
|
||||||
ansible_net_commits:
|
|
||||||
description: The set of available configuration revisions
|
|
||||||
returned: when present
|
|
||||||
type: list
|
|
||||||
ansible_net_hostname:
|
|
||||||
description: The configured system hostname
|
|
||||||
returned: always
|
|
||||||
type: str
|
|
||||||
ansible_net_model:
|
|
||||||
description: The device model string
|
|
||||||
returned: always
|
|
||||||
type: str
|
|
||||||
ansible_net_serialnum:
|
|
||||||
description: The serial number of the device
|
|
||||||
returned: always
|
|
||||||
type: str
|
|
||||||
ansible_net_version:
|
|
||||||
description: The version of the software running
|
|
||||||
returned: always
|
|
||||||
type: str
|
|
||||||
ansible_net_neighbors:
|
|
||||||
description: The set of LLDP neighbors
|
|
||||||
returned: when interface is configured
|
|
||||||
type: list
|
|
||||||
ansible_net_gather_subset:
|
|
||||||
description: The list of subsets gathered by the module
|
|
||||||
returned: always
|
|
||||||
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
|
|
||||||
ansible_net_gather_network_resources:
|
|
||||||
description: The list of fact resource subsets collected from the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
"""
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.argspec.facts.facts import FactsArgs
|
|
||||||
from ansible.module_utils.network.vyos.facts.facts import Facts
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
Main entry point for module execution
|
|
||||||
|
|
||||||
:returns: ansible_facts
|
|
||||||
"""
|
|
||||||
argument_spec = FactsArgs.argument_spec
|
|
||||||
argument_spec.update(vyos_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()
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,879 +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 vyos_interfaces
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {
|
|
||||||
'metadata_version': '1.1',
|
|
||||||
'status': ['preview'],
|
|
||||||
'supported_by': 'network'
|
|
||||||
}
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_interfaces
|
|
||||||
version_added: 2.9
|
|
||||||
short_description: Manages interface attributes of VyOS network devices.
|
|
||||||
description:
|
|
||||||
- This module manages the interface attributes on VyOS network devices.
|
|
||||||
- This module supports managing base attributes of Ethernet, Bonding,
|
|
||||||
VXLAN, Loopback and Virtual Tunnel Interfaces.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
author: Nilashish Chakraborty (@nilashishc)
|
|
||||||
options:
|
|
||||||
config:
|
|
||||||
description: The provided interfaces configuration.
|
|
||||||
type: list
|
|
||||||
suboptions:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Full name of the interface, e.g. eth0, eth1, bond0, vti1, vxlan2.
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
description:
|
|
||||||
description:
|
|
||||||
- Interface description.
|
|
||||||
type: str
|
|
||||||
duplex:
|
|
||||||
description:
|
|
||||||
- Interface duplex mode.
|
|
||||||
- Applicable for Ethernet interfaces only.
|
|
||||||
choices: ['full', 'half', 'auto']
|
|
||||||
type: str
|
|
||||||
enabled:
|
|
||||||
default: True
|
|
||||||
description:
|
|
||||||
- Administrative state of the interface.
|
|
||||||
- Set the value to C(true) to administratively enable
|
|
||||||
the interface or C(false) to disable it.
|
|
||||||
type: bool
|
|
||||||
mtu:
|
|
||||||
description:
|
|
||||||
- MTU for a specific interface. Refer to vendor documentation for valid values.
|
|
||||||
- Applicable for Ethernet, Bonding, VXLAN and Virtual Tunnel interfaces.
|
|
||||||
type: int
|
|
||||||
speed:
|
|
||||||
description:
|
|
||||||
- Interface link speed.
|
|
||||||
- Applicable for Ethernet interfaces only.
|
|
||||||
type: str
|
|
||||||
choices: ['auto', '10', '100', '1000', '2500', '10000']
|
|
||||||
vifs:
|
|
||||||
description:
|
|
||||||
- Virtual sub-interfaces related configuration.
|
|
||||||
- 802.1Q VLAN interfaces are represented as virtual sub-interfaces in VyOS.
|
|
||||||
type: list
|
|
||||||
suboptions:
|
|
||||||
vlan_id:
|
|
||||||
description:
|
|
||||||
- Identifier for the virtual sub-interface.
|
|
||||||
type: int
|
|
||||||
description:
|
|
||||||
description:
|
|
||||||
- Virtual sub-interface description.
|
|
||||||
type: str
|
|
||||||
enabled:
|
|
||||||
description:
|
|
||||||
- Administrative state of the virtual sub-interface.
|
|
||||||
- Set the value to C(true) to administratively enable
|
|
||||||
the interface or C(false) to disable it.
|
|
||||||
type: bool
|
|
||||||
default: True
|
|
||||||
mtu:
|
|
||||||
description:
|
|
||||||
- MTU for the virtual sub-interface.
|
|
||||||
- Refer to vendor documentation for valid 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:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep interfaces
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
|
|
||||||
# set interfaces ethernet eth1 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth2 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
|
|
||||||
- name: Merge provided configuration with device configuration
|
|
||||||
vyos_interfaces:
|
|
||||||
config:
|
|
||||||
- name: eth2
|
|
||||||
description: 'Configured by Ansible'
|
|
||||||
enabled: True
|
|
||||||
vifs:
|
|
||||||
- vlan_id: 200
|
|
||||||
description: "VIF 200 - ETH2"
|
|
||||||
|
|
||||||
- name: eth3
|
|
||||||
description: 'Configured by Ansible'
|
|
||||||
mtu: 1500
|
|
||||||
|
|
||||||
- name: bond1
|
|
||||||
description: 'Bond - 1'
|
|
||||||
mtu: 1200
|
|
||||||
|
|
||||||
- name: vti2
|
|
||||||
description: 'VTI - 2'
|
|
||||||
enabled: false
|
|
||||||
state: merged
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth3"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "set interfaces ethernet eth2 description 'Configured by Ansible'",
|
|
||||||
# "set interfaces ethernet eth2 vif 200",
|
|
||||||
# "set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2'",
|
|
||||||
# "set interfaces ethernet eth3 description 'Configured by Ansible'",
|
|
||||||
# "set interfaces ethernet eth3 mtu '1500'",
|
|
||||||
# "set interfaces bonding bond1",
|
|
||||||
# "set interfaces bonding bond1 description 'Bond - 1'",
|
|
||||||
# "set interfaces bonding bond1 mtu '1200'",
|
|
||||||
# "set interfaces vti vti2",
|
|
||||||
# "set interfaces vti vti2 description 'VTI - 2'",
|
|
||||||
# "set interfaces vti vti2 disable"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "description": "Bond - 1",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1200,
|
|
||||||
# "name": "bond1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "VTI - 2",
|
|
||||||
# "enabled": false,
|
|
||||||
# "name": "vti2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1500,
|
|
||||||
# "name": "eth3"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth2",
|
|
||||||
# "vifs": [
|
|
||||||
# {
|
|
||||||
# "description": "VIF 200 - ETH2",
|
|
||||||
# "enabled": true,
|
|
||||||
# "vlan_id": "200"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep interfaces
|
|
||||||
# set interfaces bonding bond1 description 'Bond - 1'
|
|
||||||
# set interfaces bonding bond1 mtu '1200'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
|
|
||||||
# set interfaces ethernet eth1 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 description 'Configured by Ansible'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth2 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2'
|
|
||||||
# set interfaces ethernet eth3 description 'Configured by Ansible'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 mtu '1500'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
# set interfaces vti vti2 description 'VTI - 2'
|
|
||||||
# set interfaces vti vti2 disable
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
# Using replaced
|
|
||||||
#
|
|
||||||
# -------------
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos:~$ show configuration commands | grep eth
|
|
||||||
# set interfaces bonding bond1 description 'Bond - 1'
|
|
||||||
# set interfaces bonding bond1 mtu '1400'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 description 'Management Interface for the Appliance'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5'
|
|
||||||
# set interfaces ethernet eth0 smp_affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 description 'Configured by Ansible Eng Team'
|
|
||||||
# set interfaces ethernet eth1 duplex 'full'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65'
|
|
||||||
# set interfaces ethernet eth1 smp_affinity 'auto'
|
|
||||||
# set interfaces ethernet eth1 speed '100'
|
|
||||||
# set interfaces ethernet eth2 description 'Configured by Ansible'
|
|
||||||
# set interfaces ethernet eth2 duplex 'full'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79'
|
|
||||||
# set interfaces ethernet eth2 mtu '500'
|
|
||||||
# set interfaces ethernet eth2 smp_affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 speed '100'
|
|
||||||
# set interfaces ethernet eth2 vif 200 description 'Configured by Ansible'
|
|
||||||
# set interfaces ethernet eth3 description 'Configured by Ansible'
|
|
||||||
# set interfaces ethernet eth3 duplex 'full'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85'
|
|
||||||
# set interfaces ethernet eth3 mtu '1500'
|
|
||||||
# set interfaces ethernet eth3 smp_affinity 'auto'
|
|
||||||
# set interfaces ethernet eth3 speed '100'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
#
|
|
||||||
#
|
|
||||||
- name: Replace device configurations of listed interfaces with provided configurations
|
|
||||||
vyos_interfaces:
|
|
||||||
config:
|
|
||||||
- name: eth2
|
|
||||||
description: "Replaced by Ansible"
|
|
||||||
|
|
||||||
- name: eth3
|
|
||||||
description: "Replaced by Ansible"
|
|
||||||
|
|
||||||
- name: eth1
|
|
||||||
description: "Replaced by Ansible"
|
|
||||||
state: replaced
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -----------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -----------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "description": "Bond - 1",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1400,
|
|
||||||
# "name": "bond1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible",
|
|
||||||
# "duplex": "full",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1500,
|
|
||||||
# "name": "eth3",
|
|
||||||
# "speed": "100"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible",
|
|
||||||
# "duplex": "full",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 500,
|
|
||||||
# "name": "eth2",
|
|
||||||
# "speed": "100",
|
|
||||||
# "vifs": [
|
|
||||||
# {
|
|
||||||
# "description": "VIF 200 - ETH2",
|
|
||||||
# "enabled": true,
|
|
||||||
# "vlan_id": "200"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible Eng Team",
|
|
||||||
# "duplex": "full",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth1",
|
|
||||||
# "speed": "100"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Management Interface for the Appliance",
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete interfaces ethernet eth2 speed",
|
|
||||||
# "delete interfaces ethernet eth2 duplex",
|
|
||||||
# "delete interfaces ethernet eth2 mtu",
|
|
||||||
# "delete interfaces ethernet eth2 vif 200 description",
|
|
||||||
# "set interfaces ethernet eth2 description 'Replaced by Ansible'",
|
|
||||||
# "delete interfaces ethernet eth3 speed",
|
|
||||||
# "delete interfaces ethernet eth3 duplex",
|
|
||||||
# "delete interfaces ethernet eth3 mtu",
|
|
||||||
# "set interfaces ethernet eth3 description 'Replaced by Ansible'",
|
|
||||||
# "delete interfaces ethernet eth1 speed",
|
|
||||||
# "delete interfaces ethernet eth1 duplex",
|
|
||||||
# "set interfaces ethernet eth1 description 'Replaced by Ansible'"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "description": "Bond - 1",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1400,
|
|
||||||
# "name": "bond1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Replaced by Ansible",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth3"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Replaced by Ansible",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth2",
|
|
||||||
# "vifs": [
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "vlan_id": "200"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Replaced by Ansible",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Management Interface for the Appliance",
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep interfaces
|
|
||||||
# set interfaces bonding bond1 description 'Bond - 1'
|
|
||||||
# set interfaces bonding bond1 mtu '1400'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 description 'Management Interface for the Appliance'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 description 'Replaced by Ansible'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
|
|
||||||
# set interfaces ethernet eth1 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 description 'Replaced by Ansible'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth2 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 vif 200
|
|
||||||
# set interfaces ethernet eth3 description 'Replaced by Ansible'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Using overridden
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# --------------
|
|
||||||
# Before state
|
|
||||||
# --------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep interfaces
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 description 'Ethernet Interface - 0'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 mtu '1200'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 description 'Configured by Ansible Eng Team'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
|
|
||||||
# set interfaces ethernet eth1 mtu '100'
|
|
||||||
# set interfaces ethernet eth1 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth1 vif 100 description 'VIF 100 - ETH1'
|
|
||||||
# set interfaces ethernet eth1 vif 100 disable
|
|
||||||
# set interfaces ethernet eth2 description 'Configured by Ansible Team (Admin Down)'
|
|
||||||
# set interfaces ethernet eth2 disable
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth2 mtu '600'
|
|
||||||
# set interfaces ethernet eth2 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth3 description 'Configured by Ansible Network'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
# set interfaces vti vti1 description 'Virtual Tunnel Interface - 1'
|
|
||||||
# set interfaces vti vti1 mtu '68'
|
|
||||||
#
|
|
||||||
#
|
|
||||||
- name: Overrides all device configuration with provided configuration
|
|
||||||
vyos_interfaces:
|
|
||||||
config:
|
|
||||||
- name: eth0
|
|
||||||
description: Outbound Interface For The Appliance
|
|
||||||
speed: auto
|
|
||||||
duplex: auto
|
|
||||||
|
|
||||||
- name: eth2
|
|
||||||
speed: auto
|
|
||||||
duplex: auto
|
|
||||||
|
|
||||||
- name: eth3
|
|
||||||
mtu: 1200
|
|
||||||
state: overridden
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# ------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Virtual Tunnel Interface - 1",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 68,
|
|
||||||
# "name": "vti1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible Network",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth3"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible Team (Admin Down)",
|
|
||||||
# "enabled": false,
|
|
||||||
# "mtu": 600,
|
|
||||||
# "name": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible Eng Team",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 100,
|
|
||||||
# "name": "eth1",
|
|
||||||
# "vifs": [
|
|
||||||
# {
|
|
||||||
# "description": "VIF 100 - ETH1",
|
|
||||||
# "enabled": false,
|
|
||||||
# "vlan_id": "100"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Ethernet Interface - 0",
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1200,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete interfaces vti vti1 description",
|
|
||||||
# "delete interfaces vti vti1 mtu",
|
|
||||||
# "delete interfaces ethernet eth1 description",
|
|
||||||
# "delete interfaces ethernet eth1 mtu",
|
|
||||||
# "delete interfaces ethernet eth1 vif 100 description",
|
|
||||||
# "delete interfaces ethernet eth1 vif 100 disable",
|
|
||||||
# "delete interfaces ethernet eth0 mtu",
|
|
||||||
# "set interfaces ethernet eth0 description 'Outbound Interface For The Appliance'",
|
|
||||||
# "delete interfaces ethernet eth2 description",
|
|
||||||
# "delete interfaces ethernet eth2 mtu",
|
|
||||||
# "set interfaces ethernet eth2 duplex 'auto'",
|
|
||||||
# "delete interfaces ethernet eth2 disable",
|
|
||||||
# "set interfaces ethernet eth2 speed 'auto'",
|
|
||||||
# "delete interfaces ethernet eth3 description",
|
|
||||||
# "set interfaces ethernet eth3 mtu '1200'"
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "vti1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1200,
|
|
||||||
# "name": "eth3"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth2",
|
|
||||||
# "speed": "auto"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth1",
|
|
||||||
# "vifs": [
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "vlan_id": "100"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Outbound Interface For The Appliance",
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep interfaces
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 description 'Outbound Interface For The Appliance'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
|
|
||||||
# set interfaces ethernet eth1 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth1 vif 100
|
|
||||||
# set interfaces ethernet eth2 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth2 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 speed 'auto'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 mtu '1200'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
# set interfaces vti vti1
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Using deleted
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------
|
|
||||||
# Before state
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep interfaces
|
|
||||||
# set interfaces bonding bond0 mtu '1300'
|
|
||||||
# set interfaces bonding bond1 description 'LAG - 1'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 description 'Outbound Interface for this appliance'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 description 'Configured by Ansible Network'
|
|
||||||
# set interfaces ethernet eth1 duplex 'full'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
|
|
||||||
# set interfaces ethernet eth1 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth1 speed '100'
|
|
||||||
# set interfaces ethernet eth2 description 'Configured by Ansible'
|
|
||||||
# set interfaces ethernet eth2 disable
|
|
||||||
# set interfaces ethernet eth2 duplex 'full'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth2 mtu '600'
|
|
||||||
# set interfaces ethernet eth2 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 speed '100'
|
|
||||||
# set interfaces ethernet eth3 description 'Configured by Ansible Network'
|
|
||||||
# set interfaces ethernet eth3 duplex 'full'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 speed '100'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
#
|
|
||||||
#
|
|
||||||
- name: Delete attributes of given interfaces (Note - This won't delete the interfaces themselves)
|
|
||||||
vyos_interfaces:
|
|
||||||
config:
|
|
||||||
- name: bond1
|
|
||||||
|
|
||||||
- name: eth1
|
|
||||||
|
|
||||||
- name: eth2
|
|
||||||
|
|
||||||
- name: eth3
|
|
||||||
state: deleted
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------------------
|
|
||||||
# Module Execution Results
|
|
||||||
# ------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1300,
|
|
||||||
# "name": "bond0"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "LAG - 1",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "bond1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible Network",
|
|
||||||
# "duplex": "full",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth3",
|
|
||||||
# "speed": "100"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible",
|
|
||||||
# "duplex": "full",
|
|
||||||
# "enabled": false,
|
|
||||||
# "mtu": 600,
|
|
||||||
# "name": "eth2",
|
|
||||||
# "speed": "100"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Configured by Ansible Network",
|
|
||||||
# "duplex": "full",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth1",
|
|
||||||
# "speed": "100"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Outbound Interface for this appliance",
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete interfaces bonding bond1 description",
|
|
||||||
# "delete interfaces ethernet eth1 speed",
|
|
||||||
# "delete interfaces ethernet eth1 duplex",
|
|
||||||
# "delete interfaces ethernet eth1 description",
|
|
||||||
# "delete interfaces ethernet eth2 speed",
|
|
||||||
# "delete interfaces ethernet eth2 disable",
|
|
||||||
# "delete interfaces ethernet eth2 duplex",
|
|
||||||
# "delete interfaces ethernet eth2 disable",
|
|
||||||
# "delete interfaces ethernet eth2 description",
|
|
||||||
# "delete interfaces ethernet eth2 disable",
|
|
||||||
# "delete interfaces ethernet eth2 mtu",
|
|
||||||
# "delete interfaces ethernet eth2 disable",
|
|
||||||
# "delete interfaces ethernet eth3 speed",
|
|
||||||
# "delete interfaces ethernet eth3 duplex",
|
|
||||||
# "delete interfaces ethernet eth3 description"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "mtu": 1300,
|
|
||||||
# "name": "bond0"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "bond1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "lo"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth3"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "description": "Outbound Interface for this appliance",
|
|
||||||
# "duplex": "auto",
|
|
||||||
# "enabled": true,
|
|
||||||
# "name": "eth0",
|
|
||||||
# "speed": "auto"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep interfaces
|
|
||||||
# set interfaces bonding bond0 mtu '1300'
|
|
||||||
# set interfaces bonding bond1
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 description 'Outbound Interface for this appliance'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
|
|
||||||
# set interfaces ethernet eth1 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth2 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces loopback lo
|
|
||||||
#
|
|
||||||
#
|
|
||||||
"""
|
|
||||||
RETURN = """
|
|
||||||
before:
|
|
||||||
description: The configuration as structured data prior to module invocation.
|
|
||||||
returned: always
|
|
||||||
sample: >
|
|
||||||
The configuration returned will always be in the same format
|
|
||||||
of the parameters above.
|
|
||||||
type: list
|
|
||||||
after:
|
|
||||||
description: The configuration as structured data after module completion.
|
|
||||||
returned: when changed
|
|
||||||
sample: >
|
|
||||||
The configuration returned will always be in the same format
|
|
||||||
of the parameters above.
|
|
||||||
type: list
|
|
||||||
commands:
|
|
||||||
description: The set of commands pushed to the remote device.
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample:
|
|
||||||
- 'set interfaces ethernet eth1 mtu 1200'
|
|
||||||
- 'set interfaces ethernet eth2 vif 100 description VIF 100'
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.argspec.interfaces.interfaces import InterfacesArgs
|
|
||||||
from ansible.module_utils.network.vyos.config.interfaces.interfaces import Interfaces
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
Main entry point for module execution
|
|
||||||
|
|
||||||
:returns: the result form module invocation
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(argument_spec=InterfacesArgs.argument_spec,
|
|
||||||
supports_check_mode=True)
|
|
||||||
|
|
||||||
result = Interfaces(module).execute_module()
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,375 +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 vyos_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: vyos_l3_interfaces
|
|
||||||
version_added: 2.9
|
|
||||||
short_description: Manages L3 interface attributes of VyOS network devices.
|
|
||||||
description: This module manages the L3 interface attributes on VyOS network devices.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
author: Nilashish Chakraborty (@NilashishC)
|
|
||||||
options:
|
|
||||||
config:
|
|
||||||
description: The provided L3 interfaces configuration.
|
|
||||||
type: list
|
|
||||||
elements: dict
|
|
||||||
suboptions:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Full name of the interface, e.g. eth0, eth1.
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
ipv4:
|
|
||||||
description:
|
|
||||||
- List of IPv4 addresses of the interface.
|
|
||||||
type: list
|
|
||||||
elements: dict
|
|
||||||
suboptions:
|
|
||||||
address:
|
|
||||||
description:
|
|
||||||
- IPv4 address of the interface.
|
|
||||||
type: str
|
|
||||||
ipv6:
|
|
||||||
description:
|
|
||||||
- List of IPv6 addresses of the interface.
|
|
||||||
type: list
|
|
||||||
elements: dict
|
|
||||||
suboptions:
|
|
||||||
address:
|
|
||||||
description:
|
|
||||||
- IPv6 address of the interface.
|
|
||||||
type: str
|
|
||||||
vifs:
|
|
||||||
description:
|
|
||||||
- Virtual sub-interfaces L3 configurations.
|
|
||||||
elements: dict
|
|
||||||
type: list
|
|
||||||
suboptions:
|
|
||||||
vlan_id:
|
|
||||||
description:
|
|
||||||
- Identifier for the virtual sub-interface.
|
|
||||||
type: int
|
|
||||||
ipv4:
|
|
||||||
description:
|
|
||||||
- List of IPv4 addresses of the virtual interface.
|
|
||||||
type: list
|
|
||||||
elements: dict
|
|
||||||
suboptions:
|
|
||||||
address:
|
|
||||||
description:
|
|
||||||
- IPv4 address of the virtual interface.
|
|
||||||
type: str
|
|
||||||
ipv6:
|
|
||||||
description:
|
|
||||||
- List of IPv6 addresses of the virtual interface.
|
|
||||||
type: list
|
|
||||||
elements: dict
|
|
||||||
suboptions:
|
|
||||||
address:
|
|
||||||
description:
|
|
||||||
- IPv6 address of the virtual interface.
|
|
||||||
type: str
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- The state of the configuration after module completion.
|
|
||||||
type: str
|
|
||||||
choices:
|
|
||||||
- merged
|
|
||||||
- replaced
|
|
||||||
- overridden
|
|
||||||
- deleted
|
|
||||||
default: merged
|
|
||||||
|
|
||||||
"""
|
|
||||||
EXAMPLES = """
|
|
||||||
# Using merged
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos:~$ show configuration commands | grep -e eth[2,3]
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 vif 101
|
|
||||||
# set interfaces ethernet eth3 vif 102
|
|
||||||
|
|
||||||
- name: Merge provided configuration with device configuration
|
|
||||||
vyos_l3_interfaces:
|
|
||||||
config:
|
|
||||||
- name: eth2
|
|
||||||
ipv4:
|
|
||||||
- address: 192.0.2.10/28
|
|
||||||
- address: 198.51.100.40/27
|
|
||||||
ipv6:
|
|
||||||
- address: 2001:db8:100::2/32
|
|
||||||
- address: 2001:db8:400::10/32
|
|
||||||
|
|
||||||
- name: eth3
|
|
||||||
ipv4:
|
|
||||||
- address: 203.0.113.65/26
|
|
||||||
vifs:
|
|
||||||
- vlan_id: 101
|
|
||||||
ipv4:
|
|
||||||
- address: 192.0.2.71/28
|
|
||||||
- address: 198.51.100.131/25
|
|
||||||
- vlan_id: 102
|
|
||||||
ipv6:
|
|
||||||
- address: 2001:db8:1000::5/38
|
|
||||||
- address: 2001:db8:1400::3/38
|
|
||||||
state: merged
|
|
||||||
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos:~$ show configuration commands | grep -e eth[2,3]
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.10/28'
|
|
||||||
# set interfaces ethernet eth2 address '198.51.100.40/27'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8:100::2/32'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8:400::10/32'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth3 address '203.0.113.65/26'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '192.0.2.71/28'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:1000::5/38'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:1400::3/38'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
|
|
||||||
|
|
||||||
|
|
||||||
# Using replaced
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos:~$ show configuration commands | grep eth
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
|
|
||||||
# set interfaces ethernet eth1 address '192.0.2.14/24'
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.10/24'
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.11/24'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8::10/32'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8::11/32'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth3 address '198.51.100.10/24'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '198.51.100.130/25'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
|
|
||||||
#
|
|
||||||
- name: Replace device configurations of listed interfaces with provided configurations
|
|
||||||
vyos_l3_interfaces:
|
|
||||||
config:
|
|
||||||
- name: eth2
|
|
||||||
ipv4:
|
|
||||||
- address: 192.0.2.10/24
|
|
||||||
|
|
||||||
- name: eth3
|
|
||||||
ipv6:
|
|
||||||
- address: 2001:db8::11/32
|
|
||||||
state: replaced
|
|
||||||
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos:~$ show configuration commands | grep eth
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
|
|
||||||
# set interfaces ethernet eth1 address '192.0.2.14/24'
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.10/24'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 address '2001:db8::11/32'
|
|
||||||
# set interfaces ethernet eth3 vif 101
|
|
||||||
# set interfaces ethernet eth3 vif 102
|
|
||||||
|
|
||||||
|
|
||||||
# Using overridden
|
|
||||||
#
|
|
||||||
# Before state
|
|
||||||
# --------------
|
|
||||||
#
|
|
||||||
# vyos@vyos-appliance:~$ show configuration commands | grep eth
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
|
|
||||||
# set interfaces ethernet eth1 address '192.0.2.14/24'
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.10/24'
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.11/24'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8::10/32'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8::11/32'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth3 address '198.51.100.10/24'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '198.51.100.130/25'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
|
|
||||||
|
|
||||||
- name: Overrides all device configuration with provided configuration
|
|
||||||
vyos_l3_interfaces:
|
|
||||||
config:
|
|
||||||
- name: eth0
|
|
||||||
ipv4:
|
|
||||||
- address: dhcp
|
|
||||||
ipv6:
|
|
||||||
- address: dhcpv6
|
|
||||||
state: overridden
|
|
||||||
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
#
|
|
||||||
# vyos@vyos-appliance:~$ show configuration commands | grep eth
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 address 'dhcpv6'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 vif 101
|
|
||||||
# set interfaces ethernet eth3 vif 102
|
|
||||||
|
|
||||||
|
|
||||||
# Using deleted
|
|
||||||
#
|
|
||||||
# Before state
|
|
||||||
# -------------
|
|
||||||
# vyos@vyos-appliance:~$ show configuration commands | grep eth
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
|
|
||||||
# set interfaces ethernet eth0 smp-affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:EA:0F:B9'
|
|
||||||
# set interfaces ethernet eth1 address '192.0.2.14/24'
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.10/24'
|
|
||||||
# set interfaces ethernet eth2 address '192.0.2.11/24'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8::10/32'
|
|
||||||
# set interfaces ethernet eth2 address '2001:db8::11/32'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
|
|
||||||
# set interfaces ethernet eth3 address '198.51.100.10/24'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '198.51.100.130/25'
|
|
||||||
# set interfaces ethernet eth3 vif 101 address '198.51.100.131/25'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::3/34'
|
|
||||||
# set interfaces ethernet eth3 vif 102 address '2001:db8:4000::2/34'
|
|
||||||
|
|
||||||
- name: Delete L3 attributes of given interfaces (Note - This won't delete the interface itself)
|
|
||||||
vyos_l3_interfaces:
|
|
||||||
config:
|
|
||||||
- name: eth1
|
|
||||||
- name: eth2
|
|
||||||
- name: eth3
|
|
||||||
state: deleted
|
|
||||||
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
# vyos@vyos-appliance:~$ show configuration commands | grep eth
|
|
||||||
# set interfaces ethernet eth0 address 'dhcp'
|
|
||||||
# set interfaces ethernet eth0 duplex 'auto'
|
|
||||||
# set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5'
|
|
||||||
# set interfaces ethernet eth0 smp_affinity 'auto'
|
|
||||||
# set interfaces ethernet eth0 speed 'auto'
|
|
||||||
# set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65'
|
|
||||||
# set interfaces ethernet eth1 smp_affinity 'auto'
|
|
||||||
# set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79'
|
|
||||||
# set interfaces ethernet eth2 smp_affinity 'auto'
|
|
||||||
# set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85'
|
|
||||||
# set interfaces ethernet eth3 smp_affinity '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: ['set interfaces ethernet eth1 192.0.2.14/2', 'set interfaces ethernet eth3 vif 101 address 198.51.100.130/25']
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs
|
|
||||||
from ansible.module_utils.network.vyos.config.l3_interfaces.l3_interfaces import L3_interfaces
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
Main entry point for module execution
|
|
||||||
|
|
||||||
:returns: the result form module invocation
|
|
||||||
"""
|
|
||||||
module = AnsibleModule(argument_spec=L3_interfacesArgs.argument_spec,
|
|
||||||
supports_check_mode=True)
|
|
||||||
|
|
||||||
result = L3_interfaces(module).execute_module()
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,561 +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 vyos_lag_interfaces
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {
|
|
||||||
'metadata_version': '1.1',
|
|
||||||
'status': ['preview'],
|
|
||||||
'supported_by': 'network'
|
|
||||||
}
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_lag_interfaces
|
|
||||||
version_added: 2.9
|
|
||||||
short_description: Manages attributes of link aggregation groups on VyOS network devices.
|
|
||||||
description: This module manages attributes of link aggregation groups on VyOS network devices.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
author: Rohit Thakur (@rohitthakur2590)
|
|
||||||
options:
|
|
||||||
config:
|
|
||||||
description: A list of link aggregation group configurations.
|
|
||||||
type: list
|
|
||||||
suboptions:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Name of the link aggregation group (LAG) or bond.
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
mode:
|
|
||||||
description:
|
|
||||||
- LAG or bond mode.
|
|
||||||
type: str
|
|
||||||
choices:
|
|
||||||
- 802.3ad
|
|
||||||
- active-backup
|
|
||||||
- broadcast
|
|
||||||
- round-robin
|
|
||||||
- transmit-load-balance
|
|
||||||
- adaptive-load-balance
|
|
||||||
- xor-hash
|
|
||||||
members:
|
|
||||||
description:
|
|
||||||
- List of member interfaces for the LAG (bond).
|
|
||||||
type: list
|
|
||||||
suboptions:
|
|
||||||
member:
|
|
||||||
description:
|
|
||||||
- Name of the member interface.
|
|
||||||
type: str
|
|
||||||
primary:
|
|
||||||
description:
|
|
||||||
- Primary device interfaces for the LAG (bond).
|
|
||||||
type: str
|
|
||||||
hash_policy:
|
|
||||||
description:
|
|
||||||
- LAG or bonding transmit hash policy.
|
|
||||||
type: str
|
|
||||||
choices:
|
|
||||||
- layer2
|
|
||||||
- layer2+3
|
|
||||||
- layer3+4
|
|
||||||
arp_monitor:
|
|
||||||
description:
|
|
||||||
- ARP Link monitoring parameters.
|
|
||||||
type: dict
|
|
||||||
suboptions:
|
|
||||||
interval:
|
|
||||||
description:
|
|
||||||
- ARP link monitoring frequency in milliseconds.
|
|
||||||
type: int
|
|
||||||
target:
|
|
||||||
description:
|
|
||||||
- IP address to use for ARP monitoring.
|
|
||||||
type: list
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- The state of the configuration after module completion.
|
|
||||||
type: str
|
|
||||||
choices:
|
|
||||||
- merged
|
|
||||||
- replaced
|
|
||||||
- overridden
|
|
||||||
- deleted
|
|
||||||
default: merged
|
|
||||||
|
|
||||||
"""
|
|
||||||
EXAMPLES = """
|
|
||||||
# Using merged
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2
|
|
||||||
# set interfaces bonding bond3
|
|
||||||
#
|
|
||||||
- name: Merge provided configuration with device configuration
|
|
||||||
vyos_lag_interfaces:
|
|
||||||
config:
|
|
||||||
- name: bond2
|
|
||||||
mode: active-backup
|
|
||||||
members:
|
|
||||||
- member: eth2
|
|
||||||
- member: eth1
|
|
||||||
hash_policy: layer2
|
|
||||||
primary: eth2
|
|
||||||
|
|
||||||
- name: 'bond3'
|
|
||||||
mode: 'active-backup'
|
|
||||||
hash_policy: 'layer2+3'
|
|
||||||
members:
|
|
||||||
- member: eth3
|
|
||||||
primary: 'eth3'
|
|
||||||
state: merged
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "name": "bond2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "name": "bond3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "set interfaces bonding bond2 hash-policy 'layer2'",
|
|
||||||
# "set interfaces bonding bond2 mode 'active-backup'",
|
|
||||||
# "set interfaces ethernet eth2 bond-group bond2",
|
|
||||||
# "set interfaces ethernet eth1 bond-group bond2",
|
|
||||||
# "set interfaces bonding bond2 primary 'eth2'",
|
|
||||||
# "set interfaces bonding bond3 hash-policy 'layer2+3'",
|
|
||||||
# "set interfaces bonding bond3 mode 'active-backup'",
|
|
||||||
# "set interfaces ethernet eth3 bond-group bond3",
|
|
||||||
# "set interfaces bonding bond3 primary 'eth3'"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "member": "eth2"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond2",
|
|
||||||
# "primary": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2+3",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond3",
|
|
||||||
# "primary": "eth3"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond2 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond2 primary 'eth2'
|
|
||||||
# set interfaces bonding bond3 hash-policy 'layer2+3'
|
|
||||||
# set interfaces bonding bond3 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond3 primary 'eth3'
|
|
||||||
# set interfaces ethernet eth1 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth2 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth3 bond-group 'bond3'
|
|
||||||
|
|
||||||
|
|
||||||
# Using replaced
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond2 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond2 primary 'eth2'
|
|
||||||
# set interfaces bonding bond3 hash-policy 'layer2+3'
|
|
||||||
# set interfaces bonding bond3 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond3 primary 'eth3'
|
|
||||||
# set interfaces ethernet eth1 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth2 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth3 bond-group 'bond3'
|
|
||||||
#
|
|
||||||
- name: Replace device configurations of listed LAGs with provided configurations
|
|
||||||
vyos_lag_interfaces:
|
|
||||||
config:
|
|
||||||
- name: bond3
|
|
||||||
mode: '802.3ad'
|
|
||||||
hash_policy: 'layer2'
|
|
||||||
members:
|
|
||||||
- member: eth3
|
|
||||||
state: replaced
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "member": "eth2"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond2",
|
|
||||||
# "primary": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2+3",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond3",
|
|
||||||
# "primary": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete interfaces bonding bond3 primary",
|
|
||||||
# "set interfaces bonding bond3 hash-policy 'layer2'",
|
|
||||||
# "set interfaces bonding bond3 mode '802.3ad'"
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "member": "eth2"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond2",
|
|
||||||
# "primary": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "802.3ad",
|
|
||||||
# "name": "bond3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond2 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond2 primary 'eth2'
|
|
||||||
# set interfaces bonding bond3 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond3 mode '802.3ad'
|
|
||||||
# set interfaces ethernet eth1 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth2 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth3 bond-group 'bond3'
|
|
||||||
|
|
||||||
|
|
||||||
# Using overridden
|
|
||||||
#
|
|
||||||
# Before state
|
|
||||||
# --------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond2 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond2 primary 'eth2'
|
|
||||||
# set interfaces bonding bond3 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond3 mode '802.3ad'
|
|
||||||
# set interfaces ethernet eth1 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth2 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth3 bond-group 'bond3'
|
|
||||||
#
|
|
||||||
- name: Overrides all device configuration with provided configuration
|
|
||||||
vyos_lag_interfaces:
|
|
||||||
config:
|
|
||||||
- name: bond3
|
|
||||||
mode: active-backup
|
|
||||||
members:
|
|
||||||
- member: eth1
|
|
||||||
- member: eth2
|
|
||||||
- member: eth3
|
|
||||||
primary: eth3
|
|
||||||
hash_policy: layer2
|
|
||||||
state: overridden
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "member": "eth2"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond2",
|
|
||||||
# "primary": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "802.3ad",
|
|
||||||
# "name": "bond3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete interfaces bonding bond2 hash-policy",
|
|
||||||
# "delete interfaces ethernet eth1 bond-group bond2",
|
|
||||||
# "delete interfaces ethernet eth2 bond-group bond2",
|
|
||||||
# "delete interfaces bonding bond2 mode",
|
|
||||||
# "delete interfaces bonding bond2 primary",
|
|
||||||
# "set interfaces bonding bond3 mode 'active-backup'",
|
|
||||||
# "set interfaces ethernet eth1 bond-group bond3",
|
|
||||||
# "set interfaces ethernet eth2 bond-group bond3",
|
|
||||||
# "set interfaces bonding bond3 primary 'eth3'"
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "name": "bond2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "member": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "member": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond3",
|
|
||||||
# "primary": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2
|
|
||||||
# set interfaces bonding bond3 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond3 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond3 primary 'eth3'
|
|
||||||
# set interfaces ethernet eth1 bond-group 'bond3'
|
|
||||||
# set interfaces ethernet eth2 bond-group 'bond3'
|
|
||||||
# set interfaces ethernet eth3 bond-group 'bond3'
|
|
||||||
|
|
||||||
|
|
||||||
# Using deleted
|
|
||||||
#
|
|
||||||
# Before state
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2 hash-policy 'layer2'
|
|
||||||
# set interfaces bonding bond2 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond2 primary 'eth2'
|
|
||||||
# set interfaces bonding bond3 hash-policy 'layer2+3'
|
|
||||||
# set interfaces bonding bond3 mode 'active-backup'
|
|
||||||
# set interfaces bonding bond3 primary 'eth3'
|
|
||||||
# set interfaces ethernet eth1 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth2 bond-group 'bond2'
|
|
||||||
# set interfaces ethernet eth3 bond-group 'bond3'
|
|
||||||
#
|
|
||||||
- name: Delete LAG attributes of given interfaces (Note This won't delete the interface itself)
|
|
||||||
vyos_lag_interfaces:
|
|
||||||
config:
|
|
||||||
- name: bond2
|
|
||||||
- name: bond3
|
|
||||||
state: deleted
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------------------
|
|
||||||
# Module Execution Results
|
|
||||||
# ------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth1"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "member": "eth2"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond2",
|
|
||||||
# "primary": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "hash_policy": "layer2+3",
|
|
||||||
# "members": [
|
|
||||||
# {
|
|
||||||
# "member": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "mode": "active-backup",
|
|
||||||
# "name": "bond3",
|
|
||||||
# "primary": "eth3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "commands": [
|
|
||||||
# "delete interfaces bonding bond2 hash-policy",
|
|
||||||
# "delete interfaces ethernet eth1 bond-group bond2",
|
|
||||||
# "delete interfaces ethernet eth2 bond-group bond2",
|
|
||||||
# "delete interfaces bonding bond2 mode",
|
|
||||||
# "delete interfaces bonding bond2 primary",
|
|
||||||
# "delete interfaces bonding bond3 hash-policy",
|
|
||||||
# "delete interfaces ethernet eth3 bond-group bond3",
|
|
||||||
# "delete interfaces bonding bond3 mode",
|
|
||||||
# "delete interfaces bonding bond3 primary"
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "name": "bond2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "name": "bond3"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep bond
|
|
||||||
# set interfaces bonding bond2
|
|
||||||
# set interfaces bonding bond3
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
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:
|
|
||||||
- 'set interfaces bonding bond2'
|
|
||||||
- 'set interfaces bonding bond2 hash-policy layer2'
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.argspec.lag_interfaces. \
|
|
||||||
lag_interfaces import Lag_interfacesArgs
|
|
||||||
from ansible.module_utils.network.vyos.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,323 +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 vyos_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: vyos_lldp_global
|
|
||||||
version_added: 2.9
|
|
||||||
short_description: Manage link layer discovery protocol (LLDP) attributes on VyOS devices..
|
|
||||||
description: This module manages link layer discovery protocol (LLDP) attributes on VyOS devices.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
author:
|
|
||||||
- Rohit Thakur (@rohitthakur2590)
|
|
||||||
options:
|
|
||||||
config:
|
|
||||||
description: The provided link layer discovery protocol (LLDP) configuration.
|
|
||||||
type: dict
|
|
||||||
suboptions:
|
|
||||||
enable:
|
|
||||||
description:
|
|
||||||
- This argument is a boolean value to enable or disable LLDP.
|
|
||||||
type: bool
|
|
||||||
address:
|
|
||||||
description:
|
|
||||||
- This argument defines management-address.
|
|
||||||
type: str
|
|
||||||
snmp:
|
|
||||||
description:
|
|
||||||
- This argument enable the SNMP queries to LLDP database.
|
|
||||||
type: str
|
|
||||||
legacy_protocols:
|
|
||||||
description:
|
|
||||||
- List of the supported legacy protocols.
|
|
||||||
type: list
|
|
||||||
choices:
|
|
||||||
- cdp
|
|
||||||
- edp
|
|
||||||
- fdp
|
|
||||||
- sonmp
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- The state of the configuration after module completion.
|
|
||||||
type: str
|
|
||||||
choices:
|
|
||||||
- merged
|
|
||||||
- replaced
|
|
||||||
- deleted
|
|
||||||
default: merged
|
|
||||||
"""
|
|
||||||
EXAMPLES = """
|
|
||||||
# Using merged
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands|grep lldp
|
|
||||||
# vyos@vyos:~$
|
|
||||||
#
|
|
||||||
- name: Merge provided configuration with device configuration
|
|
||||||
vyos_lldp_global:
|
|
||||||
config:
|
|
||||||
legacy_protocols:
|
|
||||||
- 'fdp'
|
|
||||||
- 'cdp'
|
|
||||||
snmp: 'enable'
|
|
||||||
address: 192.0.2.11
|
|
||||||
state: merged
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------------------
|
|
||||||
# Module Execution Results
|
|
||||||
# ------------------------
|
|
||||||
#
|
|
||||||
# "before": []
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "set service lldp legacy-protocols fdp",
|
|
||||||
# "set service lldp legacy-protocols cdp",
|
|
||||||
# "set service lldp snmp enable",
|
|
||||||
# "set service lldp management-address '192.0.2.11'"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "snmp": "enable"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "address": "192.0.2.11"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "legacy_protocols": [
|
|
||||||
# "cdp",
|
|
||||||
# "fdp"
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
# {
|
|
||||||
# "enable": true
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# set service lldp legacy-protocols cdp
|
|
||||||
# set service lldp legacy-protocols fdp
|
|
||||||
# set service lldp management-address '192.0.2.11'
|
|
||||||
# set service lldp snmp enable
|
|
||||||
|
|
||||||
|
|
||||||
# Using replaced
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep lldp
|
|
||||||
# set service lldp legacy-protocols cdp
|
|
||||||
# set service lldp legacy-protocols fdp
|
|
||||||
# set service lldp management-address '192.0.2.11'
|
|
||||||
# set service lldp snmp enable
|
|
||||||
#
|
|
||||||
- name: Replace device configurations with provided configurations
|
|
||||||
vyos_lldp_global:
|
|
||||||
config:
|
|
||||||
legacy_protocols:
|
|
||||||
- 'edp'
|
|
||||||
- 'sonmp'
|
|
||||||
- 'cdp'
|
|
||||||
address: 192.0.2.14
|
|
||||||
state: replaced
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------------------
|
|
||||||
# Module Execution Results
|
|
||||||
# ------------------------
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "snmp": "enable"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "address": "192.0.2.11"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "legacy_protocols": [
|
|
||||||
# "cdp",
|
|
||||||
# "fdp"
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
# {
|
|
||||||
# "enable": true
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
# "commands": [
|
|
||||||
# "delete service lldp snmp",
|
|
||||||
# "delete service lldp legacy-protocols fdp",
|
|
||||||
# "set service lldp management-address '192.0.2.14'",
|
|
||||||
# "set service lldp legacy-protocols edp",
|
|
||||||
# "set service lldp legacy-protocols sonmp"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "address": "192.0.2.14"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "legacy_protocols": [
|
|
||||||
# "cdp",
|
|
||||||
# "edp",
|
|
||||||
# "sonmp"
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
# {
|
|
||||||
# "enable": true
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands|grep lldp
|
|
||||||
# set service lldp legacy-protocols cdp
|
|
||||||
# set service lldp legacy-protocols edp
|
|
||||||
# set service lldp legacy-protocols sonmp
|
|
||||||
# set service lldp management-address '192.0.2.14'
|
|
||||||
|
|
||||||
|
|
||||||
# Using deleted
|
|
||||||
#
|
|
||||||
# Before state
|
|
||||||
# -------------
|
|
||||||
# vyos@vyos:~$ show configuration commands|grep lldp
|
|
||||||
# set service lldp legacy-protocols cdp
|
|
||||||
# set service lldp legacy-protocols edp
|
|
||||||
# set service lldp legacy-protocols sonmp
|
|
||||||
# set service lldp management-address '192.0.2.14'
|
|
||||||
#
|
|
||||||
- name: Delete attributes of given lldp service (This won't delete the LLDP service itself)
|
|
||||||
vyos_lldp_global:
|
|
||||||
config:
|
|
||||||
state: deleted
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------------------
|
|
||||||
# Module Execution Results
|
|
||||||
# ------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "address": "192.0.2.14"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "legacy_protocols": [
|
|
||||||
# "cdp",
|
|
||||||
# "edp",
|
|
||||||
# "sonmp"
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
# {
|
|
||||||
# "enable": true
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete service lldp management-address",
|
|
||||||
# "delete service lldp legacy-protocols"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "enable": true
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep lldp
|
|
||||||
# set service lldp
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
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:
|
|
||||||
- set service lldp legacy-protocols sonmp
|
|
||||||
- set service lldp management-address '192.0.2.14'
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.argspec.lldp_global.lldp_global import Lldp_globalArgs
|
|
||||||
from ansible.module_utils.network.vyos.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,507 +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 vyos_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: vyos_lldp_interfaces
|
|
||||||
version_added: 2.9
|
|
||||||
short_description: Manages attributes of lldp interfaces on VyOS devices.
|
|
||||||
description: This module manages attributes of lldp interfaces on VyOS network devices.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
author:
|
|
||||||
- Rohit Thakur (@rohitthakur2590)
|
|
||||||
options:
|
|
||||||
config:
|
|
||||||
description: A list of lldp interfaces configurations.
|
|
||||||
type: list
|
|
||||||
suboptions:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Name of the lldp interface.
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
enable:
|
|
||||||
description:
|
|
||||||
- to disable lldp on the interface.
|
|
||||||
type: bool
|
|
||||||
default: True
|
|
||||||
location:
|
|
||||||
description:
|
|
||||||
- LLDP-MED location data.
|
|
||||||
type: dict
|
|
||||||
suboptions:
|
|
||||||
civic_based:
|
|
||||||
description:
|
|
||||||
- Civic-based location data.
|
|
||||||
type: dict
|
|
||||||
suboptions:
|
|
||||||
ca_info:
|
|
||||||
description: LLDP-MED address info
|
|
||||||
type: list
|
|
||||||
suboptions:
|
|
||||||
ca_type:
|
|
||||||
description: LLDP-MED Civic Address type.
|
|
||||||
type: int
|
|
||||||
required: True
|
|
||||||
ca_value:
|
|
||||||
description: LLDP-MED Civic Address value.
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
country_code:
|
|
||||||
description: Country Code
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
coordinate_based:
|
|
||||||
description:
|
|
||||||
- Coordinate-based location.
|
|
||||||
type: dict
|
|
||||||
suboptions:
|
|
||||||
altitude:
|
|
||||||
description: Altitude in meters.
|
|
||||||
type: int
|
|
||||||
datum:
|
|
||||||
description: Coordinate datum type.
|
|
||||||
type: str
|
|
||||||
choices:
|
|
||||||
- WGS84
|
|
||||||
- NAD83
|
|
||||||
- MLLW
|
|
||||||
latitude:
|
|
||||||
description: Latitude.
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
longitude:
|
|
||||||
description: Longitude.
|
|
||||||
type: str
|
|
||||||
required: True
|
|
||||||
elin:
|
|
||||||
description: Emergency Call Service ELIN number (between 10-25 numbers).
|
|
||||||
type: str
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- The state of the configuration after module completion.
|
|
||||||
type: str
|
|
||||||
choices:
|
|
||||||
- merged
|
|
||||||
- replaced
|
|
||||||
- overridden
|
|
||||||
- deleted
|
|
||||||
default: merged
|
|
||||||
|
|
||||||
"""
|
|
||||||
EXAMPLES = """
|
|
||||||
# Using merged
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep lldp
|
|
||||||
#
|
|
||||||
- name: Merge provided configuration with device configuration
|
|
||||||
vyos_lldp_interfaces:
|
|
||||||
config:
|
|
||||||
- name: 'eth1'
|
|
||||||
location:
|
|
||||||
civic_based:
|
|
||||||
country_code: 'US'
|
|
||||||
ca_info:
|
|
||||||
- ca_type: 0
|
|
||||||
ca_value: 'ENGLISH'
|
|
||||||
|
|
||||||
- name: 'eth2'
|
|
||||||
location:
|
|
||||||
coordinate_based:
|
|
||||||
altitude: 2200
|
|
||||||
datum: 'WGS84'
|
|
||||||
longitude: '222.267255W'
|
|
||||||
latitude: '33.524449N'
|
|
||||||
state: merged
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -------------------------
|
|
||||||
#
|
|
||||||
# before": []
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "set service lldp interface eth1 location civic-based country-code 'US'",
|
|
||||||
# "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'",
|
|
||||||
# "set service lldp interface eth1",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based latitude '33.524449N'",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based altitude '2200'",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based datum 'WGS84'",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based longitude '222.267255W'",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based latitude '33.524449N'",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based altitude '2200'",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based datum 'WGS84'",
|
|
||||||
# "set service lldp interface eth2 location coordinate-based longitude '222.267255W'",
|
|
||||||
# "set service lldp interface eth2"
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "location": {
|
|
||||||
# "coordinate_based": {
|
|
||||||
# "altitude": 2200,
|
|
||||||
# "datum": "WGS84",
|
|
||||||
# "latitude": "33.524449N",
|
|
||||||
# "longitude": "222.267255W"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "location": {
|
|
||||||
# "civic_based": {
|
|
||||||
# "ca_info": [
|
|
||||||
# {
|
|
||||||
# "ca_type": 0,
|
|
||||||
# "ca_value": "ENGLISH"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "country_code": "US"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth1"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
#
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep lldp
|
|
||||||
# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'
|
|
||||||
# set service lldp interface eth1 location civic-based country-code 'US'
|
|
||||||
# set service lldp interface eth2 location coordinate-based altitude '2200'
|
|
||||||
# set service lldp interface eth2 location coordinate-based datum 'WGS84'
|
|
||||||
# set service lldp interface eth2 location coordinate-based latitude '33.524449N'
|
|
||||||
# set service lldp interface eth2 location coordinate-based longitude '222.267255W'
|
|
||||||
|
|
||||||
|
|
||||||
# Using replaced
|
|
||||||
#
|
|
||||||
# Before state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep lldp
|
|
||||||
# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'
|
|
||||||
# set service lldp interface eth1 location civic-based country-code 'US'
|
|
||||||
# set service lldp interface eth2 location coordinate-based altitude '2200'
|
|
||||||
# set service lldp interface eth2 location coordinate-based datum 'WGS84'
|
|
||||||
# set service lldp interface eth2 location coordinate-based latitude '33.524449N'
|
|
||||||
# set service lldp interface eth2 location coordinate-based longitude '222.267255W'
|
|
||||||
#
|
|
||||||
- name: Replace device configurations of listed LLDP interfaces with provided configurations
|
|
||||||
vyos_lldp_interfaces:
|
|
||||||
config:
|
|
||||||
- name: 'eth2'
|
|
||||||
location:
|
|
||||||
civic_based:
|
|
||||||
country_code: 'US'
|
|
||||||
ca_info:
|
|
||||||
- ca_type: 0
|
|
||||||
ca_value: 'ENGLISH'
|
|
||||||
|
|
||||||
- name: 'eth1'
|
|
||||||
location:
|
|
||||||
coordinate_based:
|
|
||||||
altitude: 2200
|
|
||||||
datum: 'WGS84'
|
|
||||||
longitude: '222.267255W'
|
|
||||||
latitude: '33.524449N'
|
|
||||||
state: replaced
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "location": {
|
|
||||||
# "coordinate_based": {
|
|
||||||
# "altitude": 2200,
|
|
||||||
# "datum": "WGS84",
|
|
||||||
# "latitude": "33.524449N",
|
|
||||||
# "longitude": "222.267255W"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "location": {
|
|
||||||
# "civic_based": {
|
|
||||||
# "ca_info": [
|
|
||||||
# {
|
|
||||||
# "ca_type": 0,
|
|
||||||
# "ca_value": "ENGLISH"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "country_code": "US"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth1"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete service lldp interface eth2 location",
|
|
||||||
# "set service lldp interface eth2 'disable'",
|
|
||||||
# "set service lldp interface eth2 location civic-based country-code 'US'",
|
|
||||||
# "set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'",
|
|
||||||
# "delete service lldp interface eth1 location",
|
|
||||||
# "set service lldp interface eth1 'disable'",
|
|
||||||
# "set service lldp interface eth1 location coordinate-based latitude '33.524449N'",
|
|
||||||
# "set service lldp interface eth1 location coordinate-based altitude '2200'",
|
|
||||||
# "set service lldp interface eth1 location coordinate-based datum 'WGS84'",
|
|
||||||
# "set service lldp interface eth1 location coordinate-based longitude '222.267255W'"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "location": {
|
|
||||||
# "civic_based": {
|
|
||||||
# "ca_info": [
|
|
||||||
# {
|
|
||||||
# "ca_type": 0,
|
|
||||||
# "ca_value": "ENGLISH"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "country_code": "US"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "location": {
|
|
||||||
# "coordinate_based": {
|
|
||||||
# "altitude": 2200,
|
|
||||||
# "datum": "WGS84",
|
|
||||||
# "latitude": "33.524449N",
|
|
||||||
# "longitude": "222.267255W"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth1"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# After state:
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep lldp
|
|
||||||
# set service lldp interface eth1 'disable'
|
|
||||||
# set service lldp interface eth1 location coordinate-based altitude '2200'
|
|
||||||
# set service lldp interface eth1 location coordinate-based datum 'WGS84'
|
|
||||||
# set service lldp interface eth1 location coordinate-based latitude '33.524449N'
|
|
||||||
# set service lldp interface eth1 location coordinate-based longitude '222.267255W'
|
|
||||||
# set service lldp interface eth2 'disable'
|
|
||||||
# set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'
|
|
||||||
# set service lldp interface eth2 location civic-based country-code 'US'
|
|
||||||
|
|
||||||
|
|
||||||
# Using overridden
|
|
||||||
#
|
|
||||||
# Before state
|
|
||||||
# --------------
|
|
||||||
#
|
|
||||||
# vyos@vyos:~$ show configuration commands | grep lldp
|
|
||||||
# set service lldp interface eth1 'disable'
|
|
||||||
# set service lldp interface eth1 location coordinate-based altitude '2200'
|
|
||||||
# set service lldp interface eth1 location coordinate-based datum 'WGS84'
|
|
||||||
# set service lldp interface eth1 location coordinate-based latitude '33.524449N'
|
|
||||||
# set service lldp interface eth1 location coordinate-based longitude '222.267255W'
|
|
||||||
# set service lldp interface eth2 'disable'
|
|
||||||
# set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'
|
|
||||||
# set service lldp interface eth2 location civic-based country-code 'US'
|
|
||||||
#
|
|
||||||
- name: Overrides all device configuration with provided configuration
|
|
||||||
vyos_lag_interfaces:
|
|
||||||
config:
|
|
||||||
- name: 'eth2'
|
|
||||||
location:
|
|
||||||
elin: 0000000911
|
|
||||||
|
|
||||||
state: overridden
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# -------------------------
|
|
||||||
# Module Execution Result
|
|
||||||
# -------------------------
|
|
||||||
#
|
|
||||||
# "before": [
|
|
||||||
# {
|
|
||||||
# "enable": false,
|
|
||||||
# "location": {
|
|
||||||
# "civic_based": {
|
|
||||||
# "ca_info": [
|
|
||||||
# {
|
|
||||||
# "ca_type": 0,
|
|
||||||
# "ca_value": "ENGLISH"
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# "country_code": "US"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth2"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "enable": false,
|
|
||||||
# "location": {
|
|
||||||
# "coordinate_based": {
|
|
||||||
# "altitude": 2200,
|
|
||||||
# "datum": "WGS84",
|
|
||||||
# "latitude": "33.524449N",
|
|
||||||
# "longitude": "222.267255W"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "name": "eth1"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "commands": [
|
|
||||||
# "delete service lldp interface eth2 location",
|
|
||||||
# "delete service lldp interface eth2 disable",
|
|
||||||
# "set service lldp interface eth2 location elin 0000000911"
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# "after": [
|
|
||||||
# {
|
|
||||||
# "location": {
|
|
||||||
# "elin": 0000000911
|
|
||||||
# },
|
|
||||||
# "name": "eth2"
|
|
||||||
# }
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
#
|
|
||||||
# vyos@vyos# run show configuration commands | grep lldp
|
|
||||||
# set service lldp interface eth2 location elin '0000000911'
|
|
||||||
|
|
||||||
|
|
||||||
# Using deleted
|
|
||||||
#
|
|
||||||
# Before state
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# vyos@vyos# run show configuration commands | grep lldp
|
|
||||||
# set service lldp interface eth2 location elin '0000000911'
|
|
||||||
#
|
|
||||||
- name: Delete lldp interface attributes of given interfaces.
|
|
||||||
vyos_lag_interfaces:
|
|
||||||
config:
|
|
||||||
- name: 'eth2'
|
|
||||||
state: deleted
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# ------------------------
|
|
||||||
# Module Execution Results
|
|
||||||
# ------------------------
|
|
||||||
#
|
|
||||||
"before": [
|
|
||||||
{
|
|
||||||
"location": {
|
|
||||||
"elin": 0000000911
|
|
||||||
},
|
|
||||||
"name": "eth2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
# "commands": [
|
|
||||||
# "commands": [
|
|
||||||
# "delete service lldp interface eth2"
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# "after": []
|
|
||||||
# After state
|
|
||||||
# ------------
|
|
||||||
# vyos@vyos# run show configuration commands | grep lldp
|
|
||||||
# set service 'lldp'
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
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:
|
|
||||||
- "set service lldp interface eth2 'disable'"
|
|
||||||
- "delete service lldp interface eth1 location"
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs
|
|
||||||
from ansible.module_utils.network.vyos.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,264 +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: vyos_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 Vyatta Vyos devices.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
dest:
|
|
||||||
description:
|
|
||||||
- Destination of the logs.
|
|
||||||
choices: ['console', 'file', 'global', 'host', 'user']
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- If value of C(dest) is I(file) it indicates file-name,
|
|
||||||
for I(user) it indicates username and for I(host) indicates
|
|
||||||
the host name to be notified.
|
|
||||||
facility:
|
|
||||||
description:
|
|
||||||
- Set logging facility.
|
|
||||||
level:
|
|
||||||
description:
|
|
||||||
- Set logging severity levels.
|
|
||||||
aggregate:
|
|
||||||
description: List of logging definitions.
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- State of the logging configuration.
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: configure console logging
|
|
||||||
vyos_logging:
|
|
||||||
dest: console
|
|
||||||
facility: all
|
|
||||||
level: crit
|
|
||||||
|
|
||||||
- name: remove console logging configuration
|
|
||||||
vyos_logging:
|
|
||||||
dest: console
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: configure file logging
|
|
||||||
vyos_logging:
|
|
||||||
dest: file
|
|
||||||
name: test
|
|
||||||
facility: local3
|
|
||||||
level: err
|
|
||||||
|
|
||||||
- name: Add logging aggregate
|
|
||||||
vyos_logging:
|
|
||||||
aggregate:
|
|
||||||
- { dest: file, name: test1, facility: all, level: info }
|
|
||||||
- { dest: file, name: test2, facility: news, level: debug }
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Remove logging aggregate
|
|
||||||
vyos_logging:
|
|
||||||
aggregate:
|
|
||||||
- { dest: console, facility: all, level: info }
|
|
||||||
- { dest: console, facility: daemon, level: warning }
|
|
||||||
- { dest: file, name: test2, facility: news, level: debug }
|
|
||||||
state: absent
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: The list of configuration mode commands to send to the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample:
|
|
||||||
- set system syslog global facility all level notice
|
|
||||||
"""
|
|
||||||
|
|
||||||
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.vyos.vyos import get_config, load_config
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def spec_to_commands(updates, module):
|
|
||||||
commands = list()
|
|
||||||
want, have = updates
|
|
||||||
|
|
||||||
for w in want:
|
|
||||||
dest = w['dest']
|
|
||||||
name = w['name']
|
|
||||||
facility = w['facility']
|
|
||||||
level = w['level']
|
|
||||||
state = w['state']
|
|
||||||
del w['state']
|
|
||||||
|
|
||||||
if state == 'absent' and w in have:
|
|
||||||
if w['name']:
|
|
||||||
commands.append('delete system syslog {0} {1} facility {2} level {3}'.format(
|
|
||||||
dest, name, facility, level))
|
|
||||||
else:
|
|
||||||
commands.append('delete system syslog {0} facility {1} level {2}'.format(
|
|
||||||
dest, facility, level))
|
|
||||||
elif state == 'present' and w not in have:
|
|
||||||
if w['name']:
|
|
||||||
commands.append('set system syslog {0} {1} facility {2} level {3}'.format(
|
|
||||||
dest, name, facility, level))
|
|
||||||
else:
|
|
||||||
commands.append('set system syslog {0} facility {1} level {2}'.format(
|
|
||||||
dest, facility, level))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def config_to_dict(module):
|
|
||||||
data = get_config(module)
|
|
||||||
obj = []
|
|
||||||
|
|
||||||
for line in data.split('\n'):
|
|
||||||
if line.startswith('set system syslog'):
|
|
||||||
match = re.search(r'set system syslog (\S+)', line, re.M)
|
|
||||||
dest = match.group(1)
|
|
||||||
if dest == 'host':
|
|
||||||
match = re.search(r'host (\S+)', line, re.M)
|
|
||||||
name = match.group(1)
|
|
||||||
elif dest == 'file':
|
|
||||||
match = re.search(r'file (\S+)', line, re.M)
|
|
||||||
name = match.group(1)
|
|
||||||
elif dest == 'user':
|
|
||||||
match = re.search(r'user (\S+)', line, re.M)
|
|
||||||
name = match.group(1)
|
|
||||||
else:
|
|
||||||
name = None
|
|
||||||
|
|
||||||
if 'facility' in line:
|
|
||||||
match = re.search(r'facility (\S+)', line, re.M)
|
|
||||||
facility = match.group(1)
|
|
||||||
if 'level' in line:
|
|
||||||
match = re.search(r'level (\S+)', line, re.M)
|
|
||||||
level = match.group(1).strip("'")
|
|
||||||
|
|
||||||
obj.append({'dest': dest,
|
|
||||||
'name': name,
|
|
||||||
'facility': facility,
|
|
||||||
'level': level})
|
|
||||||
|
|
||||||
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)
|
|
||||||
obj.append(item.copy())
|
|
||||||
|
|
||||||
else:
|
|
||||||
if module.params['dest'] not in ('host', 'file', 'user'):
|
|
||||||
module.params['name'] = None
|
|
||||||
|
|
||||||
obj.append({
|
|
||||||
'dest': module.params['dest'],
|
|
||||||
'name': module.params['name'],
|
|
||||||
'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=['console', 'file', 'global', 'host', 'user']),
|
|
||||||
name=dict(type='str'),
|
|
||||||
facility=dict(type='str'),
|
|
||||||
level=dict(type='str'),
|
|
||||||
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(vyos_argument_spec)
|
|
||||||
required_if = [('dest', 'host', ['name', 'facility', 'level']),
|
|
||||||
('dest', 'file', ['name', 'facility', 'level']),
|
|
||||||
('dest', 'user', ['name', 'facility', 'level']),
|
|
||||||
('dest', 'console', ['facility', 'level']),
|
|
||||||
('dest', 'global', ['facility', 'level'])]
|
|
||||||
|
|
||||||
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, required_if=required_if)
|
|
||||||
have = config_to_dict(module)
|
|
||||||
|
|
||||||
commands = spec_to_commands((want, have), module)
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,248 +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/>.
|
|
||||||
#
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
||||||
'status': ['preview'],
|
|
||||||
'supported_by': 'community'}
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_ping
|
|
||||||
short_description: Tests reachability using ping from VyOS network devices
|
|
||||||
description:
|
|
||||||
- Tests reachability using ping from a VyOS device to a remote destination.
|
|
||||||
- Tested against VyOS 1.1.8 (helium)
|
|
||||||
- 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:
|
|
||||||
- Nilashish Chakraborty (@NilashishC)
|
|
||||||
version_added: '2.8'
|
|
||||||
options:
|
|
||||||
dest:
|
|
||||||
description:
|
|
||||||
- The IP Address or hostname (resolvable by the device) of the remote node.
|
|
||||||
required: true
|
|
||||||
count:
|
|
||||||
description:
|
|
||||||
- Number of packets to send to check reachability.
|
|
||||||
type: int
|
|
||||||
default: 5
|
|
||||||
source:
|
|
||||||
description:
|
|
||||||
- The source interface or IP Address to use while sending the ping packet(s).
|
|
||||||
ttl:
|
|
||||||
description:
|
|
||||||
- The time-to-live value for the ICMP packet(s).
|
|
||||||
type: int
|
|
||||||
size:
|
|
||||||
description:
|
|
||||||
- Determines the size (in bytes) of the ping packet(s).
|
|
||||||
type: int
|
|
||||||
interval:
|
|
||||||
description:
|
|
||||||
- Determines the interval (in seconds) between consecutive pings.
|
|
||||||
type: int
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Determines if the expected result is success or fail.
|
|
||||||
choices: [ absent, present ]
|
|
||||||
default: present
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- 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.
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: Test reachability to 10.10.10.10
|
|
||||||
vyos_ping:
|
|
||||||
dest: 10.10.10.10
|
|
||||||
|
|
||||||
- name: Test reachability to 10.20.20.20 using source and ttl set
|
|
||||||
vyos_ping:
|
|
||||||
dest: 10.20.20.20
|
|
||||||
source: eth0
|
|
||||||
ttl: 128
|
|
||||||
|
|
||||||
- name: Test unreachability to 10.30.30.30 using interval
|
|
||||||
vyos_ping:
|
|
||||||
dest: 10.30.30.30
|
|
||||||
interval: 3
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Test reachability to 10.40.40.40 setting count and source
|
|
||||||
vyos_ping:
|
|
||||||
dest: 10.40.40.40
|
|
||||||
source: eth1
|
|
||||||
count: 20
|
|
||||||
size: 512
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: List of commands sent.
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample: ["ping 10.8.38.44 count 10 interface eth0 ttl 128"]
|
|
||||||
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: The round trip time (RTT) stats.
|
|
||||||
returned: when ping succeeds
|
|
||||||
type: dict
|
|
||||||
sample: {"avg": 2, "max": 8, "min": 1, "mdev": 24}
|
|
||||||
"""
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.vyos import run_commands
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" main entry point for module execution
|
|
||||||
"""
|
|
||||||
argument_spec = dict(
|
|
||||||
count=dict(type="int", default=5),
|
|
||||||
dest=dict(type="str", required=True),
|
|
||||||
source=dict(type="str"),
|
|
||||||
ttl=dict(type='int'),
|
|
||||||
size=dict(type='int'),
|
|
||||||
interval=dict(type='int'),
|
|
||||||
state=dict(type="str", choices=["absent", "present"], default="present"),
|
|
||||||
)
|
|
||||||
|
|
||||||
argument_spec.update(vyos_argument_spec)
|
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec)
|
|
||||||
|
|
||||||
count = module.params["count"]
|
|
||||||
dest = module.params["dest"]
|
|
||||||
source = module.params["source"]
|
|
||||||
size = module.params["size"]
|
|
||||||
ttl = module.params["ttl"]
|
|
||||||
interval = module.params["interval"]
|
|
||||||
|
|
||||||
warnings = list()
|
|
||||||
|
|
||||||
results = {}
|
|
||||||
if warnings:
|
|
||||||
results["warnings"] = warnings
|
|
||||||
|
|
||||||
results["commands"] = [build_ping(dest, count, size, interval, source, ttl)]
|
|
||||||
|
|
||||||
ping_results = run_commands(module, commands=results["commands"])
|
|
||||||
ping_results_list = ping_results[0].split("\n")
|
|
||||||
|
|
||||||
rtt_info, rate_info = None, None
|
|
||||||
for line in ping_results_list:
|
|
||||||
if line.startswith('rtt'):
|
|
||||||
rtt_info = line
|
|
||||||
if line.startswith('%s packets transmitted' % count):
|
|
||||||
rate_info = line
|
|
||||||
|
|
||||||
if rtt_info:
|
|
||||||
rtt = parse_rtt(rtt_info)
|
|
||||||
for k, v in rtt.items():
|
|
||||||
if rtt[k] is not None:
|
|
||||||
rtt[k] = int(v)
|
|
||||||
results["rtt"] = rtt
|
|
||||||
|
|
||||||
pkt_loss, rx, tx = parse_rate(rate_info)
|
|
||||||
results["packet_loss"] = str(pkt_loss) + "%"
|
|
||||||
results["packets_rx"] = int(rx)
|
|
||||||
results["packets_tx"] = int(tx)
|
|
||||||
|
|
||||||
validate_results(module, pkt_loss, results)
|
|
||||||
|
|
||||||
module.exit_json(**results)
|
|
||||||
|
|
||||||
|
|
||||||
def build_ping(dest, count, size=None, interval=None, source=None, ttl=None):
|
|
||||||
cmd = "ping {0} count {1}".format(dest, str(count))
|
|
||||||
|
|
||||||
if source:
|
|
||||||
cmd += " interface {0}".format(source)
|
|
||||||
|
|
||||||
if ttl:
|
|
||||||
cmd += " ttl {0}".format(str(ttl))
|
|
||||||
|
|
||||||
if size:
|
|
||||||
cmd += " size {0}".format(str(size))
|
|
||||||
|
|
||||||
if interval:
|
|
||||||
cmd += " interval {0}".format(str(interval))
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
|
|
||||||
def parse_rate(rate_info):
|
|
||||||
rate_re = re.compile(
|
|
||||||
r"(?P<tx>\d+) (?:\w+) (?:\w+), (?P<rx>\d+) (?:\w+), (?P<pkt_loss>\d+)% (?:\w+) (?:\w+), (?:\w+) (?P<time>\d+)")
|
|
||||||
rate_err_re = re.compile(
|
|
||||||
r"(?P<tx>\d+) (?:\w+) (?:\w+), (?P<rx>\d+) (?:\w+), (?:[+-])(?P<err>\d+) (?:\w+), (?P<pkt_loss>\d+)% (?:\w+) (?:\w+), (?:\w+) (?P<time>\d+)")
|
|
||||||
|
|
||||||
if rate_re.match(rate_info):
|
|
||||||
rate = rate_re.match(rate_info)
|
|
||||||
elif rate_err_re.match(rate_info):
|
|
||||||
rate = rate_err_re.match(rate_info)
|
|
||||||
|
|
||||||
return rate.group("pkt_loss"), rate.group("rx"), rate.group("tx")
|
|
||||||
|
|
||||||
|
|
||||||
def parse_rtt(rtt_info):
|
|
||||||
rtt_re = re.compile(
|
|
||||||
r"rtt (?:.*)=(?:\s*)(?P<min>\d*).(?:\d*)/(?P<avg>\d*).(?:\d*)/(?P<max>\d+).(?:\d*)/(?P<mdev>\d*)")
|
|
||||||
rtt = rtt_re.match(rtt_info)
|
|
||||||
|
|
||||||
return rtt.groupdict()
|
|
||||||
|
|
||||||
|
|
||||||
def validate_results(module, loss, results):
|
|
||||||
state = module.params["state"]
|
|
||||||
if state == "present" and int(loss) == 100:
|
|
||||||
module.fail_json(msg="Ping failed unexpectedly", **results)
|
|
||||||
elif state == "absent" and int(loss) < 100:
|
|
||||||
module.fail_json(msg="Ping succeeded unexpectedly", **results)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
File diff suppressed because it is too large
Load Diff
@ -1,212 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# 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: "vyos_system"
|
|
||||||
version_added: "2.3"
|
|
||||||
author: "Nathaniel Case (@Qalthos)"
|
|
||||||
short_description: Run `set system` commands on VyOS devices
|
|
||||||
description:
|
|
||||||
- Runs one or more commands on remote devices running VyOS.
|
|
||||||
This module can also be introspected to validate key parameters before
|
|
||||||
returning successfully.
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
host_name:
|
|
||||||
description:
|
|
||||||
- Configure the device hostname parameter. This option takes an ASCII string value.
|
|
||||||
domain_name:
|
|
||||||
description:
|
|
||||||
- The new domain name to apply to the device.
|
|
||||||
name_servers:
|
|
||||||
description:
|
|
||||||
- A list of name servers to use with the device. Mutually exclusive with
|
|
||||||
I(domain_search)
|
|
||||||
aliases: ['name_server']
|
|
||||||
domain_search:
|
|
||||||
description:
|
|
||||||
- A list of domain names to search. Mutually exclusive with
|
|
||||||
I(name_server)
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- Whether to apply (C(present)) or remove (C(absent)) the settings.
|
|
||||||
default: present
|
|
||||||
choices: ['present', 'absent']
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: The list of configuration mode commands to send to the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample:
|
|
||||||
- set system hostname vyos01
|
|
||||||
- set system domain-name foo.example.com
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: configure hostname and domain-name
|
|
||||||
vyos_system:
|
|
||||||
host_name: vyos01
|
|
||||||
domain_name: test.example.com
|
|
||||||
|
|
||||||
- name: remove all configuration
|
|
||||||
vyos_system:
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: configure name servers
|
|
||||||
vyos_system:
|
|
||||||
name_servers
|
|
||||||
- 8.8.8.8
|
|
||||||
- 8.8.4.4
|
|
||||||
|
|
||||||
- name: configure domain search suffixes
|
|
||||||
vyos_system:
|
|
||||||
domain_search:
|
|
||||||
- sub1.example.com
|
|
||||||
- sub2.example.com
|
|
||||||
"""
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.network.vyos.vyos import get_config, load_config
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def spec_key_to_device_key(key):
|
|
||||||
device_key = key.replace('_', '-')
|
|
||||||
|
|
||||||
# domain-search is longer than just it's key
|
|
||||||
if device_key == 'domain-search':
|
|
||||||
device_key += ' domain'
|
|
||||||
|
|
||||||
return device_key
|
|
||||||
|
|
||||||
|
|
||||||
def config_to_dict(module):
|
|
||||||
data = get_config(module)
|
|
||||||
|
|
||||||
config = {'domain_search': [], 'name_server': []}
|
|
||||||
|
|
||||||
for line in data.split('\n'):
|
|
||||||
if line.startswith('set system host-name'):
|
|
||||||
config['host_name'] = line[22:-1]
|
|
||||||
elif line.startswith('set system domain-name'):
|
|
||||||
config['domain_name'] = line[24:-1]
|
|
||||||
elif line.startswith('set system domain-search domain'):
|
|
||||||
config['domain_search'].append(line[33:-1])
|
|
||||||
elif line.startswith('set system name-server'):
|
|
||||||
config['name_server'].append(line[24:-1])
|
|
||||||
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def spec_to_commands(want, have):
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
state = want.pop('state')
|
|
||||||
|
|
||||||
# state='absent' by itself has special meaning
|
|
||||||
if state == 'absent' and all(v is None for v in want.values()):
|
|
||||||
# Clear everything
|
|
||||||
for key in have:
|
|
||||||
commands.append('delete system %s' % spec_key_to_device_key(key))
|
|
||||||
|
|
||||||
for key in want:
|
|
||||||
if want[key] is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
current = have.get(key)
|
|
||||||
proposed = want[key]
|
|
||||||
device_key = spec_key_to_device_key(key)
|
|
||||||
|
|
||||||
# These keys are lists which may need to be reconciled with the device
|
|
||||||
if key in ['domain_search', 'name_server']:
|
|
||||||
if not proposed:
|
|
||||||
# Empty list was passed, delete all values
|
|
||||||
commands.append("delete system %s" % device_key)
|
|
||||||
for config in proposed:
|
|
||||||
if state == 'absent' and config in current:
|
|
||||||
commands.append("delete system %s '%s'" % (device_key, config))
|
|
||||||
elif state == 'present' and config not in current:
|
|
||||||
commands.append("set system %s '%s'" % (device_key, config))
|
|
||||||
else:
|
|
||||||
if state == 'absent' and current and proposed:
|
|
||||||
commands.append('delete system %s' % device_key)
|
|
||||||
elif state == 'present' and proposed and proposed != current:
|
|
||||||
commands.append("set system %s '%s'" % (device_key, proposed))
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def map_param_to_obj(module):
|
|
||||||
return {
|
|
||||||
'host_name': module.params['host_name'],
|
|
||||||
'domain_name': module.params['domain_name'],
|
|
||||||
'domain_search': module.params['domain_search'],
|
|
||||||
'name_server': module.params['name_server'],
|
|
||||||
'state': module.params['state']
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argument_spec = dict(
|
|
||||||
host_name=dict(type='str'),
|
|
||||||
domain_name=dict(type='str'),
|
|
||||||
domain_search=dict(type='list'),
|
|
||||||
name_server=dict(type='list', aliases=['name_servers']),
|
|
||||||
state=dict(type='str', default='present', choices=['present', 'absent']),
|
|
||||||
)
|
|
||||||
|
|
||||||
argument_spec.update(vyos_argument_spec)
|
|
||||||
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
mutually_exclusive=[('domain_name', 'domain_search')],
|
|
||||||
)
|
|
||||||
|
|
||||||
warnings = list()
|
|
||||||
|
|
||||||
result = {'changed': False, 'warnings': warnings}
|
|
||||||
|
|
||||||
want = map_param_to_obj(module)
|
|
||||||
have = config_to_dict(module)
|
|
||||||
|
|
||||||
commands = spec_to_commands(want, have)
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,332 +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: vyos_user
|
|
||||||
version_added: "2.4"
|
|
||||||
author: "Trishna Guha (@trishnaguha)"
|
|
||||||
short_description: Manage the collection of local users on VyOS 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 collection of usernames in the
|
|
||||||
current running config. It also supports purging usernames from the
|
|
||||||
configuration that are not explicitly defined.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
aggregate:
|
|
||||||
description:
|
|
||||||
- The set of username objects to be configured on the remote
|
|
||||||
VyOS 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 VyOS 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).
|
|
||||||
full_name:
|
|
||||||
description:
|
|
||||||
- The C(full_name) argument provides the full name of the user
|
|
||||||
account to be created on the remote device. This argument accepts
|
|
||||||
any text string value.
|
|
||||||
configured_password:
|
|
||||||
description:
|
|
||||||
- The password to be configured on the VyOS 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']
|
|
||||||
level:
|
|
||||||
description:
|
|
||||||
- The C(level) argument configures the level of the user when logged
|
|
||||||
into the system. This argument accepts string values admin or operator.
|
|
||||||
aliases: ['role']
|
|
||||||
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: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: create a new user
|
|
||||||
vyos_user:
|
|
||||||
name: ansible
|
|
||||||
configured_password: password
|
|
||||||
state: present
|
|
||||||
- name: remove all users except admin
|
|
||||||
vyos_user:
|
|
||||||
purge: yes
|
|
||||||
- name: set multiple users to level operator
|
|
||||||
vyos_user:
|
|
||||||
aggregate:
|
|
||||||
- name: netop
|
|
||||||
- name: netend
|
|
||||||
level: operator
|
|
||||||
state: present
|
|
||||||
- name: Change Password for User netop
|
|
||||||
vyos_user:
|
|
||||||
name: netop
|
|
||||||
configured_password: "{{ new_password }}"
|
|
||||||
update_password: always
|
|
||||||
state: present
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: The list of configuration mode commands to send to the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample:
|
|
||||||
- set system login user test level operator
|
|
||||||
- set system login user authentication plaintext-password password
|
|
||||||
"""
|
|
||||||
|
|
||||||
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.vyos.vyos import get_config, load_config
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def validate_level(value, module):
|
|
||||||
if value not in ('admin', 'operator'):
|
|
||||||
module.fail_json(msg='level must be either admin or operator, got %s' % value)
|
|
||||||
|
|
||||||
|
|
||||||
def spec_to_commands(updates, module):
|
|
||||||
commands = list()
|
|
||||||
update_password = module.params['update_password']
|
|
||||||
|
|
||||||
def needs_update(want, have, x):
|
|
||||||
return want.get(x) and (want.get(x) != have.get(x))
|
|
||||||
|
|
||||||
def add(command, want, x):
|
|
||||||
command.append('set system login user %s %s' % (want['name'], x))
|
|
||||||
|
|
||||||
for update in updates:
|
|
||||||
want, have = update
|
|
||||||
|
|
||||||
if want['state'] == 'absent':
|
|
||||||
commands.append('delete system login user %s' % want['name'])
|
|
||||||
continue
|
|
||||||
|
|
||||||
if needs_update(want, have, 'level'):
|
|
||||||
add(commands, want, "level %s" % want['level'])
|
|
||||||
|
|
||||||
if needs_update(want, have, 'full_name'):
|
|
||||||
add(commands, want, "full-name %s" % want['full_name'])
|
|
||||||
|
|
||||||
if needs_update(want, have, 'configured_password'):
|
|
||||||
if update_password == 'always' or not have:
|
|
||||||
add(commands, want, 'authentication plaintext-password %s' % want['configured_password'])
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
|
|
||||||
def parse_level(data):
|
|
||||||
match = re.search(r'level (\S+)', data, re.M)
|
|
||||||
if match:
|
|
||||||
level = match.group(1)[1:-1]
|
|
||||||
return level
|
|
||||||
|
|
||||||
|
|
||||||
def parse_full_name(data):
|
|
||||||
match = re.search(r'full-name (\S+)', data, re.M)
|
|
||||||
if match:
|
|
||||||
full_name = match.group(1)[1:-1]
|
|
||||||
return full_name
|
|
||||||
|
|
||||||
|
|
||||||
def config_to_dict(module):
|
|
||||||
data = get_config(module)
|
|
||||||
|
|
||||||
match = re.findall(r'^set system login user (\S+)', data, re.M)
|
|
||||||
if not match:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
instances = list()
|
|
||||||
|
|
||||||
for user in set(match):
|
|
||||||
regex = r' %s .+$' % user
|
|
||||||
cfg = re.findall(regex, data, re.M)
|
|
||||||
cfg = '\n'.join(cfg)
|
|
||||||
obj = {
|
|
||||||
'name': user,
|
|
||||||
'state': 'present',
|
|
||||||
'configured_password': None,
|
|
||||||
'level': parse_level(cfg),
|
|
||||||
'full_name': parse_full_name(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]
|
|
||||||
|
|
||||||
# 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):
|
|
||||||
aggregate = module.params['aggregate']
|
|
||||||
if not aggregate:
|
|
||||||
if not module.params['name'] and module.params['purge']:
|
|
||||||
return list()
|
|
||||||
else:
|
|
||||||
users = [{'name': module.params['name']}]
|
|
||||||
else:
|
|
||||||
users = list()
|
|
||||||
for item in aggregate:
|
|
||||||
if not isinstance(item, dict):
|
|
||||||
users.append({'name': item})
|
|
||||||
else:
|
|
||||||
users.append(item)
|
|
||||||
|
|
||||||
objects = list()
|
|
||||||
|
|
||||||
for item in users:
|
|
||||||
get_value = partial(get_param_value, item=item, module=module)
|
|
||||||
item['configured_password'] = get_value('configured_password')
|
|
||||||
item['full_name'] = get_value('full_name')
|
|
||||||
item['level'] = get_value('level')
|
|
||||||
item['state'] = get_value('state')
|
|
||||||
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 item is None:
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
element_spec = dict(
|
|
||||||
name=dict(),
|
|
||||||
|
|
||||||
full_name=dict(),
|
|
||||||
level=dict(aliases=['role']),
|
|
||||||
|
|
||||||
configured_password=dict(no_log=True),
|
|
||||||
update_password=dict(default='always', choices=['on_create', 'always']),
|
|
||||||
|
|
||||||
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(vyos_argument_spec)
|
|
||||||
|
|
||||||
mutually_exclusive = [('name', 'aggregate')]
|
|
||||||
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 = config_to_dict(module)
|
|
||||||
commands = spec_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):
|
|
||||||
commands.append('delete system login user %s' % item)
|
|
||||||
|
|
||||||
result['commands'] = commands
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,331 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright: (c) 2017, Ansible by Red Hat, inc
|
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
||||||
'status': ['preview'],
|
|
||||||
'supported_by': 'network'}
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
|
||||||
---
|
|
||||||
module: vyos_vlan
|
|
||||||
version_added: "2.5"
|
|
||||||
author: "Trishna Guha (@trishnaguha)"
|
|
||||||
short_description: Manage VLANs on VyOS network devices
|
|
||||||
description:
|
|
||||||
- This module provides declarative management of VLANs
|
|
||||||
on VyOS network devices.
|
|
||||||
notes:
|
|
||||||
- Tested against VyOS 1.1.8 (helium).
|
|
||||||
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
|
|
||||||
options:
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Name of the VLAN.
|
|
||||||
address:
|
|
||||||
description:
|
|
||||||
- Configure Virtual interface address.
|
|
||||||
vlan_id:
|
|
||||||
description:
|
|
||||||
- ID of the VLAN. Range 0-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 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']
|
|
||||||
extends_documentation_fragment: vyos
|
|
||||||
"""
|
|
||||||
|
|
||||||
EXAMPLES = """
|
|
||||||
- name: Create vlan
|
|
||||||
vyos_vlan:
|
|
||||||
vlan_id: 100
|
|
||||||
name: vlan-100
|
|
||||||
interfaces: eth1
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Add interfaces to VLAN
|
|
||||||
vyos_vlan:
|
|
||||||
vlan_id: 100
|
|
||||||
interfaces:
|
|
||||||
- eth1
|
|
||||||
- eth2
|
|
||||||
|
|
||||||
- name: Configure virtual interface address
|
|
||||||
vyos_vlan:
|
|
||||||
vlan_id: 100
|
|
||||||
interfaces: eth1
|
|
||||||
address: 172.26.100.37/24
|
|
||||||
|
|
||||||
- name: vlan interface config + intent
|
|
||||||
vyos_vlan:
|
|
||||||
vlan_id: 100
|
|
||||||
interfaces: eth0
|
|
||||||
associated_interfaces:
|
|
||||||
- eth0
|
|
||||||
|
|
||||||
- name: vlan intent check
|
|
||||||
vyos_vlan:
|
|
||||||
vlan_id: 100
|
|
||||||
associated_interfaces:
|
|
||||||
- eth3
|
|
||||||
- eth4
|
|
||||||
|
|
||||||
- name: Delete vlan
|
|
||||||
vyos_vlan:
|
|
||||||
vlan_id: 100
|
|
||||||
interfaces: eth1
|
|
||||||
state: absent
|
|
||||||
"""
|
|
||||||
|
|
||||||
RETURN = """
|
|
||||||
commands:
|
|
||||||
description: The list of configuration mode commands to send to the device
|
|
||||||
returned: always
|
|
||||||
type: list
|
|
||||||
sample:
|
|
||||||
- set interfaces ethernet eth1 vif 100 description VLAN 100
|
|
||||||
- set interfaces ethernet eth1 vif 100 address 172.26.100.37/24
|
|
||||||
- delete interfaces ethernet eth1 vif 100
|
|
||||||
"""
|
|
||||||
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.vyos.vyos import load_config, run_commands
|
|
||||||
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
|
|
||||||
|
|
||||||
|
|
||||||
def search_obj_in_list(vlan_id, lst):
|
|
||||||
obj = list()
|
|
||||||
for o in lst:
|
|
||||||
if o['vlan_id'] == vlan_id:
|
|
||||||
obj.append(o)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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']
|
|
||||||
address = w['address']
|
|
||||||
state = w['state']
|
|
||||||
|
|
||||||
obj_in_have = search_obj_in_list(vlan_id, have)
|
|
||||||
|
|
||||||
if state == 'absent':
|
|
||||||
if obj_in_have:
|
|
||||||
for obj in obj_in_have:
|
|
||||||
for i in obj['interfaces']:
|
|
||||||
commands.append('delete interfaces ethernet {0} vif {1}'.format(i, vlan_id))
|
|
||||||
|
|
||||||
elif state == 'present':
|
|
||||||
if not obj_in_have:
|
|
||||||
if w['interfaces'] and w['vlan_id']:
|
|
||||||
for i in w['interfaces']:
|
|
||||||
cmd = 'set interfaces ethernet {0} vif {1}'.format(i, vlan_id)
|
|
||||||
if w['name']:
|
|
||||||
commands.append(cmd + ' description {0}'.format(name))
|
|
||||||
elif w['address']:
|
|
||||||
commands.append(cmd + ' address {0}'.format(address))
|
|
||||||
else:
|
|
||||||
commands.append(cmd)
|
|
||||||
|
|
||||||
if purge:
|
|
||||||
for h in have:
|
|
||||||
obj_in_want = search_obj_in_list(h['vlan_id'], want)
|
|
||||||
if not obj_in_want:
|
|
||||||
for i in h['interfaces']:
|
|
||||||
commands.append('delete interfaces ethernet {0} vif {1}'.format(i, 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()
|
|
||||||
|
|
||||||
if not d['vlan_id']:
|
|
||||||
module.fail_json(msg='vlan_id is required')
|
|
||||||
|
|
||||||
d['vlan_id'] = str(d['vlan_id'])
|
|
||||||
module._check_required_one_of(module.required_one_of, item)
|
|
||||||
|
|
||||||
obj.append(d)
|
|
||||||
else:
|
|
||||||
obj.append({
|
|
||||||
'vlan_id': str(module.params['vlan_id']),
|
|
||||||
'name': module.params['name'],
|
|
||||||
'address': module.params['address'],
|
|
||||||
'state': module.params['state'],
|
|
||||||
'interfaces': module.params['interfaces'],
|
|
||||||
'associated_interfaces': module.params['associated_interfaces']
|
|
||||||
})
|
|
||||||
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def map_config_to_obj(module):
|
|
||||||
objs = []
|
|
||||||
|
|
||||||
output = run_commands(module, 'show interfaces')
|
|
||||||
lines = output[0].strip().splitlines()[3:]
|
|
||||||
|
|
||||||
for l in lines:
|
|
||||||
splitted_line = re.split(r'\s{2,}', l.strip())
|
|
||||||
obj = {}
|
|
||||||
|
|
||||||
eth = splitted_line[0].strip("'")
|
|
||||||
if eth.startswith('eth'):
|
|
||||||
obj['interfaces'] = []
|
|
||||||
if '.' in eth:
|
|
||||||
interface = eth.split('.')[0]
|
|
||||||
obj['interfaces'].append(interface)
|
|
||||||
obj['vlan_id'] = eth.split('.')[-1]
|
|
||||||
else:
|
|
||||||
obj['interfaces'].append(eth)
|
|
||||||
obj['vlan_id'] = None
|
|
||||||
|
|
||||||
if splitted_line[1].strip("'") != '-':
|
|
||||||
obj['address'] = splitted_line[1].strip("'")
|
|
||||||
|
|
||||||
if len(splitted_line) > 3:
|
|
||||||
obj['name'] = splitted_line[3].strip("'")
|
|
||||||
obj['state'] = 'present'
|
|
||||||
objs.append(obj)
|
|
||||||
|
|
||||||
return objs
|
|
||||||
|
|
||||||
|
|
||||||
def check_declarative_intent_params(want, module, result):
|
|
||||||
|
|
||||||
have = None
|
|
||||||
obj_interface = list()
|
|
||||||
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)
|
|
||||||
|
|
||||||
obj_in_have = search_obj_in_list(w['vlan_id'], have)
|
|
||||||
if obj_in_have:
|
|
||||||
for obj in obj_in_have:
|
|
||||||
obj_interface.extend(obj['interfaces'])
|
|
||||||
|
|
||||||
for w in want:
|
|
||||||
if w.get('associated_interfaces') is None:
|
|
||||||
continue
|
|
||||||
for i in w['associated_interfaces']:
|
|
||||||
if (set(obj_interface) - set(w['associated_interfaces'])) != set([]):
|
|
||||||
module.fail_json(msg='Interface {0} not configured on vlan {1}'.format(i, w['vlan_id']))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
""" main entry point for module execution
|
|
||||||
"""
|
|
||||||
element_spec = dict(
|
|
||||||
vlan_id=dict(type='int'),
|
|
||||||
name=dict(),
|
|
||||||
address=dict(),
|
|
||||||
interfaces=dict(type='list'),
|
|
||||||
associated_interfaces=dict(type='list'),
|
|
||||||
delay=dict(default=10, type='int'),
|
|
||||||
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),
|
|
||||||
purge=dict(default=False, type='bool')
|
|
||||||
)
|
|
||||||
|
|
||||||
argument_spec.update(element_spec)
|
|
||||||
argument_spec.update(vyos_argument_spec)
|
|
||||||
|
|
||||||
required_one_of = [['vlan_id', 'aggregate'],
|
|
||||||
['aggregate', 'interfaces', 'associated_interfaces']]
|
|
||||||
|
|
||||||
mutually_exclusive = [['vlan_id', 'aggregate']]
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
|
||||||
supports_check_mode=True,
|
|
||||||
required_one_of=required_one_of,
|
|
||||||
mutually_exclusive=mutually_exclusive)
|
|
||||||
|
|
||||||
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:
|
|
||||||
commit = not module.check_mode
|
|
||||||
load_config(module, commands, commit=commit)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
check_declarative_intent_params(want, module, result)
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,91 +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.vyos.vyos import vyos_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 == 'vyos_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(vyos_provider_spec, self._task.args)
|
|
||||||
pc = copy.deepcopy(self._play_context)
|
|
||||||
pc.connection = 'ansible.netcommon.network_cli'
|
|
||||||
pc.network_os = 'vyos.vyos.vyos'
|
|
||||||
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
|
|
||||||
|
|
||||||
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 = 'vyos'
|
|
||||||
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,274 +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: vyos
|
|
||||||
short_description: Use vyos cliconf to run command on VyOS platform
|
|
||||||
description:
|
|
||||||
- This vyos plugin provides low level abstraction apis for
|
|
||||||
sending and receiving CLI commands from VyOS network devices.
|
|
||||||
version_added: "2.4"
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import json
|
|
||||||
|
|
||||||
from ansible.errors import AnsibleConnectionFailure
|
|
||||||
from ansible.module_utils._text import to_text
|
|
||||||
from ansible.module_utils.common._collections_compat import Mapping
|
|
||||||
from ansible.module_utils.network.common.config import NetworkConfig
|
|
||||||
from ansible.module_utils.network.common.utils import to_list
|
|
||||||
from ansible.plugins.cliconf import CliconfBase
|
|
||||||
|
|
||||||
|
|
||||||
class Cliconf(CliconfBase):
|
|
||||||
|
|
||||||
def get_device_info(self):
|
|
||||||
device_info = {}
|
|
||||||
|
|
||||||
device_info['network_os'] = 'vyos'
|
|
||||||
reply = self.get('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)
|
|
||||||
|
|
||||||
match = re.search(r'HW model:\s*(\S+)', data)
|
|
||||||
if match:
|
|
||||||
device_info['network_os_model'] = match.group(1)
|
|
||||||
|
|
||||||
reply = self.get('show host name')
|
|
||||||
device_info['network_os_hostname'] = to_text(reply, errors='surrogate_or_strict').strip()
|
|
||||||
|
|
||||||
return device_info
|
|
||||||
|
|
||||||
def get_config(self, flags=None, format=None):
|
|
||||||
if format:
|
|
||||||
option_values = self.get_option_values()
|
|
||||||
if format not in option_values['format']:
|
|
||||||
raise ValueError("'format' value %s is invalid. Valid values of format are %s" % (format, ', '.join(option_values['format'])))
|
|
||||||
|
|
||||||
if not flags:
|
|
||||||
flags = []
|
|
||||||
|
|
||||||
if format == 'text':
|
|
||||||
command = 'show configuration'
|
|
||||||
else:
|
|
||||||
command = 'show configuration commands'
|
|
||||||
|
|
||||||
command += ' '.join(to_list(flags))
|
|
||||||
command = command.strip()
|
|
||||||
|
|
||||||
out = self.send_command(command)
|
|
||||||
return out
|
|
||||||
|
|
||||||
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 = []
|
|
||||||
self.send_command('configure')
|
|
||||||
for cmd in to_list(candidate):
|
|
||||||
if not isinstance(cmd, Mapping):
|
|
||||||
cmd = {'command': cmd}
|
|
||||||
|
|
||||||
results.append(self.send_command(**cmd))
|
|
||||||
requests.append(cmd['command'])
|
|
||||||
out = self.get('compare')
|
|
||||||
out = to_text(out, errors='surrogate_or_strict')
|
|
||||||
diff_config = out if not out.startswith('No changes') else None
|
|
||||||
|
|
||||||
if diff_config:
|
|
||||||
if commit:
|
|
||||||
try:
|
|
||||||
self.commit(comment)
|
|
||||||
except AnsibleConnectionFailure as e:
|
|
||||||
msg = 'commit failed: %s' % e.message
|
|
||||||
self.discard_changes()
|
|
||||||
raise AnsibleConnectionFailure(msg)
|
|
||||||
else:
|
|
||||||
self.send_command('exit')
|
|
||||||
else:
|
|
||||||
self.discard_changes()
|
|
||||||
else:
|
|
||||||
self.send_command('exit')
|
|
||||||
if to_text(self._connection.get_prompt(), errors='surrogate_or_strict').strip().endswith('#'):
|
|
||||||
self.discard_changes()
|
|
||||||
|
|
||||||
if diff_config:
|
|
||||||
resp['diff'] = diff_config
|
|
||||||
resp['response'] = results
|
|
||||||
resp['request'] = requests
|
|
||||||
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 commit(self, comment=None):
|
|
||||||
if comment:
|
|
||||||
command = 'commit comment "{0}"'.format(comment)
|
|
||||||
else:
|
|
||||||
command = 'commit'
|
|
||||||
self.send_command(command)
|
|
||||||
|
|
||||||
def discard_changes(self):
|
|
||||||
self.send_command('exit discard')
|
|
||||||
|
|
||||||
def get_diff(self, candidate=None, running=None, diff_match='line', diff_ignore_lines=None, path=None, diff_replace=None):
|
|
||||||
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:
|
|
||||||
raise ValueError("'replace' in diff is not supported")
|
|
||||||
|
|
||||||
if diff_ignore_lines:
|
|
||||||
raise ValueError("'diff_ignore_lines' in diff is not supported")
|
|
||||||
|
|
||||||
if path:
|
|
||||||
raise ValueError("'path' in diff is not supported")
|
|
||||||
|
|
||||||
set_format = candidate.startswith('set') or candidate.startswith('delete')
|
|
||||||
candidate_obj = NetworkConfig(indent=4, contents=candidate)
|
|
||||||
if not set_format:
|
|
||||||
config = [c.line for c in candidate_obj.items]
|
|
||||||
commands = list()
|
|
||||||
# this filters out less specific lines
|
|
||||||
for item in config:
|
|
||||||
for index, entry in enumerate(commands):
|
|
||||||
if item.startswith(entry):
|
|
||||||
del commands[index]
|
|
||||||
break
|
|
||||||
commands.append(item)
|
|
||||||
|
|
||||||
candidate_commands = ['set %s' % cmd.replace(' {', '') for cmd in commands]
|
|
||||||
|
|
||||||
else:
|
|
||||||
candidate_commands = str(candidate).strip().split('\n')
|
|
||||||
|
|
||||||
if diff_match == 'none':
|
|
||||||
diff['config_diff'] = list(candidate_commands)
|
|
||||||
return diff
|
|
||||||
|
|
||||||
running_commands = [str(c).replace("'", '') for c in running.splitlines()]
|
|
||||||
|
|
||||||
updates = list()
|
|
||||||
visited = set()
|
|
||||||
|
|
||||||
for line in candidate_commands:
|
|
||||||
item = str(line).replace("'", '')
|
|
||||||
|
|
||||||
if not item.startswith('set') and not item.startswith('delete'):
|
|
||||||
raise ValueError('line must start with either `set` or `delete`')
|
|
||||||
|
|
||||||
elif item.startswith('set') and item not in running_commands:
|
|
||||||
updates.append(line)
|
|
||||||
|
|
||||||
elif item.startswith('delete'):
|
|
||||||
if not running_commands:
|
|
||||||
updates.append(line)
|
|
||||||
else:
|
|
||||||
item = re.sub(r'delete', 'set', item)
|
|
||||||
for entry in running_commands:
|
|
||||||
if entry.startswith(item) and line not in visited:
|
|
||||||
updates.append(line)
|
|
||||||
visited.add(line)
|
|
||||||
|
|
||||||
diff['config_diff'] = list(updates)
|
|
||||||
return diff
|
|
||||||
|
|
||||||
def run_commands(self, commands=None, check_rc=True):
|
|
||||||
if commands is None:
|
|
||||||
raise ValueError("'commands' value is required")
|
|
||||||
|
|
||||||
responses = list()
|
|
||||||
for cmd in to_list(commands):
|
|
||||||
if not isinstance(cmd, Mapping):
|
|
||||||
cmd = {'command': cmd}
|
|
||||||
|
|
||||||
output = cmd.pop('output', None)
|
|
||||||
if output:
|
|
||||||
raise ValueError("'output' value %s is not supported for run_commands" % output)
|
|
||||||
|
|
||||||
try:
|
|
||||||
out = self.send_command(**cmd)
|
|
||||||
except AnsibleConnectionFailure as e:
|
|
||||||
if check_rc:
|
|
||||||
raise
|
|
||||||
out = getattr(e, 'err', e)
|
|
||||||
|
|
||||||
responses.append(out)
|
|
||||||
|
|
||||||
return responses
|
|
||||||
|
|
||||||
def get_device_operations(self):
|
|
||||||
return {
|
|
||||||
'supports_diff_replace': False,
|
|
||||||
'supports_commit': True,
|
|
||||||
'supports_rollback': False,
|
|
||||||
'supports_defaults': False,
|
|
||||||
'supports_onbox_diff': True,
|
|
||||||
'supports_commit_comment': True,
|
|
||||||
'supports_multiline_delimiter': False,
|
|
||||||
'supports_diff_match': True,
|
|
||||||
'supports_diff_ignore_lines': False,
|
|
||||||
'supports_generate_diff': False,
|
|
||||||
'supports_replace': False
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_option_values(self):
|
|
||||||
return {
|
|
||||||
'format': ['text', 'set'],
|
|
||||||
'diff_match': ['line', 'none'],
|
|
||||||
'diff_replace': [],
|
|
||||||
'output': []
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_capabilities(self):
|
|
||||||
result = super(Cliconf, self).get_capabilities()
|
|
||||||
result['rpc'] += ['commit', 'discard_changes', 'get_diff', 'run_commands']
|
|
||||||
result['device_operations'] = self.get_device_operations()
|
|
||||||
result.update(self.get_option_values())
|
|
||||||
return json.dumps(result)
|
|
||||||
|
|
||||||
def set_cli_prompt_context(self):
|
|
||||||
"""
|
|
||||||
Make sure we are in the operational cli mode
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
if self._connection.connected:
|
|
||||||
self._update_cli_prompt_context(config_context='#', exit_command='exit discard')
|
|
@ -1,65 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com>
|
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleDocFragment(object):
|
|
||||||
|
|
||||||
# Standard files documentation fragment
|
|
||||||
DOCUMENTATION = r'''
|
|
||||||
options:
|
|
||||||
provider:
|
|
||||||
description:
|
|
||||||
- B(Deprecated)
|
|
||||||
- "Starting with Ansible 2.5 we recommend using C(connection: network_cli)."
|
|
||||||
- For more information please see the L(Network Guide, ../network/getting_started/network_differences.html#multiple-communication-protocols).
|
|
||||||
- HORIZONTALLINE
|
|
||||||
- A dict object containing connection details.
|
|
||||||
type: dict
|
|
||||||
suboptions:
|
|
||||||
host:
|
|
||||||
description:
|
|
||||||
- Specifies the DNS host name or address for connecting to the remote
|
|
||||||
device over the specified transport. The value of host is used as
|
|
||||||
the destination address for the transport.
|
|
||||||
type: str
|
|
||||||
required: true
|
|
||||||
port:
|
|
||||||
description:
|
|
||||||
- Specifies the port to use when building the connection to the remote
|
|
||||||
device.
|
|
||||||
type: int
|
|
||||||
default: 22
|
|
||||||
username:
|
|
||||||
description:
|
|
||||||
- Configures the username to use to authenticate the connection to
|
|
||||||
the remote device. This value is used to authenticate
|
|
||||||
the SSH session. If the value is not specified in the task, the
|
|
||||||
value of environment variable C(ANSIBLE_NET_USERNAME) will be used instead.
|
|
||||||
type: str
|
|
||||||
password:
|
|
||||||
description:
|
|
||||||
- Specifies the password to use to authenticate the connection to
|
|
||||||
the remote device. This value is used to authenticate
|
|
||||||
the SSH session. If the value is not specified in the task, the
|
|
||||||
value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead.
|
|
||||||
type: str
|
|
||||||
timeout:
|
|
||||||
description:
|
|
||||||
- Specifies the timeout in seconds for communicating with the network device
|
|
||||||
for either connecting or sending commands. If the timeout is
|
|
||||||
exceeded before the operation is completed, the module will error.
|
|
||||||
type: int
|
|
||||||
default: 10
|
|
||||||
ssh_keyfile:
|
|
||||||
description:
|
|
||||||
- Specifies the SSH key to use to authenticate the connection to
|
|
||||||
the remote device. This value is the path to the
|
|
||||||
key used to authenticate the SSH session. If the value is not specified
|
|
||||||
in the task, the value of environment variable C(ANSIBLE_NET_SSH_KEYFILE)
|
|
||||||
will be used instead.
|
|
||||||
type: path
|
|
||||||
notes:
|
|
||||||
- For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide <network_guide>`
|
|
||||||
'''
|
|
@ -1,50 +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 os
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.plugins.terminal import TerminalBase
|
|
||||||
from ansible.errors import AnsibleConnectionFailure
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalModule(TerminalBase):
|
|
||||||
|
|
||||||
terminal_stdout_re = [
|
|
||||||
re.compile(br"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
|
|
||||||
re.compile(br"\@[\w\-\.]+:\S+?[>#\$] ?$")
|
|
||||||
]
|
|
||||||
|
|
||||||
terminal_stderr_re = [
|
|
||||||
re.compile(br"\n\s*Invalid command:"),
|
|
||||||
re.compile(br"\nCommit failed"),
|
|
||||||
re.compile(br"\n\s+Set failed"),
|
|
||||||
]
|
|
||||||
|
|
||||||
terminal_length = os.getenv('ANSIBLE_VYOS_TERMINAL_LENGTH', 10000)
|
|
||||||
|
|
||||||
def on_open_shell(self):
|
|
||||||
try:
|
|
||||||
for cmd in (b'set terminal length 0', b'set terminal width 512'):
|
|
||||||
self._exec_cli_command(cmd)
|
|
||||||
self._exec_cli_command(b'set terminal length %d' % self.terminal_length)
|
|
||||||
except AnsibleConnectionFailure:
|
|
||||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
|
@ -1 +0,0 @@
|
|||||||
shippable/vyos/group1
|
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
testcase: "*"
|
|
||||||
test_items: []
|
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
- name: collect all cli test cases
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/tests/cli"
|
|
||||||
patterns: "{{ testcase }}.yaml"
|
|
||||||
register: test_cases
|
|
||||||
delegate_to: localhost
|
|
||||||
|
|
||||||
- name: set test_items
|
|
||||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
|
||||||
|
|
||||||
- name: run test case (connection=network_cli)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=network_cli"
|
|
||||||
with_items: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
||||||
|
|
||||||
- name: run test case (connection=local)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
|
||||||
with_first_found: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
@ -1,2 +0,0 @@
|
|||||||
---
|
|
||||||
- {include: cli.yaml, tags: ['cli']}
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
- debug:
|
|
||||||
msg: "cli/basic-no-login.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: Setup
|
|
||||||
vyos_banner:
|
|
||||||
banner: pre-login
|
|
||||||
text: |
|
|
||||||
Junk pre-login banner
|
|
||||||
over multiple lines
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: remove pre-login
|
|
||||||
vyos_banner:
|
|
||||||
banner: pre-login
|
|
||||||
state: absent
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "{{ result }}"
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- "'delete system login banner pre-login' in result.commands"
|
|
||||||
|
|
||||||
- name: remove pre-login (idempotent)
|
|
||||||
vyos_banner:
|
|
||||||
banner: pre-login
|
|
||||||
state: absent
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
- "result.commands | length == 0"
|
|
||||||
|
|
||||||
|
|
||||||
# FIXME add in tests for everything defined in docs
|
|
||||||
# FIXME Test state:absent + test:
|
|
||||||
# FIXME Without powers ensure "privileged mode required"
|
|
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
- debug:
|
|
||||||
msg: "cli/basic-post-login.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup - remove post-login
|
|
||||||
vyos_banner:
|
|
||||||
banner: post-login
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Set post-login
|
|
||||||
vyos_banner:
|
|
||||||
banner: post-login
|
|
||||||
text: |
|
|
||||||
this is my post-login banner
|
|
||||||
that has a multiline
|
|
||||||
string
|
|
||||||
state: present
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "{{ result }}"
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- "'this is my post-login banner' in result.commands[0]"
|
|
||||||
- "'that has a multiline' in result.commands[0]"
|
|
||||||
|
|
||||||
- name: Set post-login again (idempotent)
|
|
||||||
vyos_banner:
|
|
||||||
banner: post-login
|
|
||||||
text: |
|
|
||||||
this is my post-login banner
|
|
||||||
that has a multiline
|
|
||||||
string
|
|
||||||
state: present
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
- "result.commands | length == 0"
|
|
||||||
|
|
||||||
|
|
||||||
# FIXME add in tests for everything defined in docs
|
|
||||||
# FIXME Test state:absent + test:
|
|
||||||
# FIXME Without powers ensure "privileged mode required"
|
|
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
- debug:
|
|
||||||
msg: "cli/basic-pre-login.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup - remove pre-login
|
|
||||||
vyos_banner:
|
|
||||||
banner: pre-login
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Set pre-login
|
|
||||||
vyos_banner:
|
|
||||||
banner: pre-login
|
|
||||||
text: |
|
|
||||||
this is my pre-login banner
|
|
||||||
that has a multiline
|
|
||||||
string
|
|
||||||
state: present
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- debug:
|
|
||||||
msg: "{{ result }}"
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- "'this is my pre-login banner' in result.commands[0]"
|
|
||||||
- "'that has a multiline' in result.commands[0]"
|
|
||||||
|
|
||||||
- name: Set pre-login again (idempotent)
|
|
||||||
vyos_banner:
|
|
||||||
banner: pre-login
|
|
||||||
text: |
|
|
||||||
this is my pre-login banner
|
|
||||||
that has a multiline
|
|
||||||
string
|
|
||||||
state: present
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
- "result.commands | length == 0"
|
|
||||||
|
|
||||||
|
|
||||||
# FIXME add in tests for everything defined in docs
|
|
||||||
# FIXME Test state:absent + test:
|
|
||||||
# FIXME Without powers ensure "privileged mode required"
|
|
@ -1 +0,0 @@
|
|||||||
shippable/vyos/group1
|
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
testcase: "*"
|
|
||||||
test_items: []
|
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
- name: collect all cli test cases
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/tests/cli"
|
|
||||||
patterns: "{{ testcase }}.yaml"
|
|
||||||
register: test_cases
|
|
||||||
delegate_to: localhost
|
|
||||||
|
|
||||||
- name: set test_items
|
|
||||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
|
||||||
|
|
||||||
- name: run test case (connection=network_cli)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=network_cli"
|
|
||||||
with_items: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
||||||
|
|
||||||
- name: run test case (connection=local)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
|
||||||
with_first_found: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
@ -1,2 +0,0 @@
|
|||||||
---
|
|
||||||
- {include: cli.yaml, tags: ['cli']}
|
|
@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/bad_operator.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: test bad operator
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show version
|
|
||||||
- show interfaces
|
|
||||||
wait_for:
|
|
||||||
- result[0] is 'VyOS'
|
|
||||||
register: result
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- result.failed == true
|
|
||||||
- result.msg is defined
|
|
||||||
|
|
||||||
- debug: msg="END cli/bad_operator.yaml on connection={{ ansible_connection }}"
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
- debug:
|
|
||||||
msg: "START cli/cli_command.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- block:
|
|
||||||
- name: get output for single command
|
|
||||||
cli_command:
|
|
||||||
command: show version
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
- "result.stdout is defined"
|
|
||||||
|
|
||||||
- name: send invalid command
|
|
||||||
cli_command:
|
|
||||||
command: 'show foo'
|
|
||||||
register: result
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.failed == true"
|
|
||||||
- "result.msg is defined"
|
|
||||||
when: "ansible_connection == 'network_cli'"
|
|
||||||
|
|
||||||
- block:
|
|
||||||
- name: test failure for local connection
|
|
||||||
cli_command:
|
|
||||||
command: show version
|
|
||||||
register: result
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- 'result.failed == true'
|
|
||||||
- "'Connection type local is not valid for this module' in result.msg"
|
|
||||||
when: "ansible_connection == 'local'"
|
|
||||||
|
|
||||||
- debug: msg="END cli/cli_command.yaml on connection={{ ansible_connection }}"
|
|
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/contains.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: test contains operator
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show version
|
|
||||||
- show interface
|
|
||||||
wait_for:
|
|
||||||
- result[0] contains VyOS
|
|
||||||
- result[1] contains eth0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- result.changed == false
|
|
||||||
- result.stdout is defined
|
|
||||||
- result.stdout_lines is defined
|
|
||||||
|
|
||||||
- debug: msg="END cli/contains.yaml on connection={{ ansible_connection }}"
|
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/invalid.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: run invalid command
|
|
||||||
vyos_command:
|
|
||||||
commands: show foo
|
|
||||||
register: result
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- assert: {that: result.failed}
|
|
||||||
|
|
||||||
- name: run commands that include invalid command
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show version
|
|
||||||
- show foo
|
|
||||||
register: result
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- assert: {that: result.failed}
|
|
||||||
|
|
||||||
- debug: msg="END cli/invalid.yaml on connection={{ ansible_connection }}"
|
|
@ -1,44 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/output.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: get output for single command
|
|
||||||
vyos_command:
|
|
||||||
commands: show version
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- result.changed == false
|
|
||||||
- result.stdout is defined
|
|
||||||
- result.stdout_lines is defined
|
|
||||||
|
|
||||||
- name: get output for multiple commands
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show version
|
|
||||||
- show interfaces
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- result.changed == false
|
|
||||||
- result.stdout is defined
|
|
||||||
- result.stdout | length == 2
|
|
||||||
|
|
||||||
- name: Get output for multiple commands that call less explicitly
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
# NOTE: We only test show commands that will output <ANSIBLE_VYOS_TERMINAL_LENGTH
|
|
||||||
# Otherwise you will get ": "command timeout triggered"
|
|
||||||
- show hardware cpu detail
|
|
||||||
- show hardware mem
|
|
||||||
- show license
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- result.changed == false
|
|
||||||
- result.stdout_lines is defined
|
|
||||||
- result.stdout_lines[2] | length >= 20
|
|
||||||
|
|
||||||
- debug: msg="END cli/output.yaml on connection={{ ansible_connection }}"
|
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/timeout.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: test bad condition
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show version
|
|
||||||
wait_for:
|
|
||||||
- result[0] contains bad_value_string
|
|
||||||
register: result
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- result.failed == true
|
|
||||||
- result.msg is defined
|
|
||||||
|
|
||||||
- debug: msg="END cli/timeout.yaml on connection={{ ansible_connection }}"
|
|
@ -1 +0,0 @@
|
|||||||
shippable/vyos/group1
|
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
testcase: "*"
|
|
||||||
test_items: []
|
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
- name: collect all cli test cases
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/tests/cli"
|
|
||||||
patterns: "{{ testcase }}.yaml"
|
|
||||||
register: test_cases
|
|
||||||
delegate_to: localhost
|
|
||||||
|
|
||||||
- name: set test_items
|
|
||||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
|
||||||
|
|
||||||
- name: run test case (connection=network_cli)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=network_cli"
|
|
||||||
with_items: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
||||||
|
|
||||||
- name: run test case (connection=local)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
|
||||||
with_first_found: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
- name: collect all cli_config test cases
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/tests/cli_config"
|
|
||||||
patterns: "{{ testcase }}.yaml"
|
|
||||||
register: test_cases
|
|
||||||
delegate_to: localhost
|
|
||||||
|
|
||||||
- name: set test_items
|
|
||||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
|
||||||
|
|
||||||
- name: run test case (connection=network_cli)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=network_cli"
|
|
||||||
with_items: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
- {include: cli.yaml, tags: ['cli']}
|
|
||||||
- {include: cli_config.yaml, tags: ['cli_config']}
|
|
@ -1,113 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START vyos/backup.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: collect any backup files
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup"
|
|
||||||
pattern: "{{ inventory_hostname_short }}_config*"
|
|
||||||
register: backup_files
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- name: delete backup files
|
|
||||||
file:
|
|
||||||
path: "{{ item.path }}"
|
|
||||||
state: absent
|
|
||||||
with_items: "{{backup_files.files|default([])}}"
|
|
||||||
|
|
||||||
- name: take configure backup
|
|
||||||
vyos_config:
|
|
||||||
backup: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: collect any backup files
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup"
|
|
||||||
pattern: "{{ inventory_hostname_short }}_config*"
|
|
||||||
register: backup_files
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_files.files is defined"
|
|
||||||
|
|
||||||
- name: delete configurable backup file path
|
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: absent
|
|
||||||
with_items:
|
|
||||||
- "{{ role_path }}/backup_test_dir/"
|
|
||||||
- "{{ role_path }}/backup/backup.cfg"
|
|
||||||
|
|
||||||
- name: take configuration backup in custom filename and directory path
|
|
||||||
vyos_config:
|
|
||||||
backup: true
|
|
||||||
backup_options:
|
|
||||||
filename: backup.cfg
|
|
||||||
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
|
|
||||||
become: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: check if the backup file-1 exist
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}/backup.cfg"
|
|
||||||
register: backup_file
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_file.files is defined"
|
|
||||||
|
|
||||||
- name: take configuration backup in custom filename
|
|
||||||
vyos_config:
|
|
||||||
backup: true
|
|
||||||
backup_options:
|
|
||||||
filename: backup.cfg
|
|
||||||
become: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: check if the backup file-2 exist
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup/backup.cfg"
|
|
||||||
register: backup_file
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_file.files is defined"
|
|
||||||
|
|
||||||
- name: take configuration backup in custom path and default filename
|
|
||||||
vyos_config:
|
|
||||||
backup: true
|
|
||||||
backup_options:
|
|
||||||
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
|
|
||||||
become: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: check if the backup file-3 exist
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
|
|
||||||
pattern: "{{ inventory_hostname_short }}_config*"
|
|
||||||
register: backup_file
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_file.files is defined"
|
|
||||||
|
|
||||||
- debug: msg="END vyos/backup.yaml on connection={{ ansible_connection }}"
|
|
@ -1,63 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/config_check.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup- ensure interface is not present
|
|
||||||
vyos_config:
|
|
||||||
lines: delete interfaces loopback lo
|
|
||||||
|
|
||||||
- name: setup- create interface
|
|
||||||
vyos_config:
|
|
||||||
lines:
|
|
||||||
- interfaces
|
|
||||||
- interfaces loopback lo
|
|
||||||
- interfaces loopback lo description test
|
|
||||||
register: result
|
|
||||||
|
|
||||||
# note collapsing the duplicate lines doesn't work if
|
|
||||||
# lines:
|
|
||||||
# - interfaces loopback lo description test
|
|
||||||
# - interfaces loopback lo
|
|
||||||
# - interfaces
|
|
||||||
|
|
||||||
- name: Check that multiple duplicate lines collapse into a single commands
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- "{{ result.commands|length }} == 1"
|
|
||||||
|
|
||||||
- name: Check that set is correctly prepended
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
- "result.commands[0] == 'set interfaces loopback lo description test'"
|
|
||||||
|
|
||||||
- name: configure config_check config command
|
|
||||||
vyos_config:
|
|
||||||
lines: delete interfaces loopback lo
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: check config_check config command idempontent
|
|
||||||
vyos_config:
|
|
||||||
lines: delete interfaces loopback lo
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
|
|
||||||
- name: check multiple line config filter is working
|
|
||||||
vyos_config:
|
|
||||||
lines:
|
|
||||||
- set system login user esa level admin
|
|
||||||
- set system login user esa authentication encrypted-password '!abc!'
|
|
||||||
- set system login user vyos level admin
|
|
||||||
- set system login user vyos authentication encrypted-password 'abc'
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "{{ result.filtered|length }} == 2"
|
|
||||||
|
|
||||||
- debug: msg="END cli/config_check.yaml on connection={{ ansible_connection }}"
|
|
@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/comment.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name {{ inventory_hostname_short }}
|
|
||||||
match: none
|
|
||||||
|
|
||||||
- name: configure using comment
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name foo
|
|
||||||
comment: this is a test
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- "'set system host-name foo' in result.commands"
|
|
||||||
|
|
||||||
- name: collect system commits
|
|
||||||
vyos_command:
|
|
||||||
commands: show system commit
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "'this is a test' in result.stdout_lines[0][1]"
|
|
||||||
|
|
||||||
- name: teardown
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name {{ inventory_hostname_short }}
|
|
||||||
match: none
|
|
||||||
|
|
||||||
- debug: msg="END cli/comment.yaml on connection={{ ansible_connection }}"
|
|
@ -1,3 +0,0 @@
|
|||||||
set service lldp
|
|
||||||
set protocols static
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/save.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name {{ inventory_hostname_short }}
|
|
||||||
match: none
|
|
||||||
|
|
||||||
- name: configure hostaname and save
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name foo
|
|
||||||
save: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- "'set system host-name foo' in result.commands"
|
|
||||||
|
|
||||||
- name: configure hostaname and don't save
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name bar
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- "'set system host-name bar' in result.commands"
|
|
||||||
|
|
||||||
- name: save config
|
|
||||||
vyos_config:
|
|
||||||
save: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: save config again
|
|
||||||
vyos_config:
|
|
||||||
save: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
|
|
||||||
- name: teardown
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name {{ inventory_hostname_short }}
|
|
||||||
match: none
|
|
||||||
save: true
|
|
||||||
|
|
||||||
- debug: msg="END cli/simple.yaml on connection={{ ansible_connection }}"
|
|
@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli/simple.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name {{ inventory_hostname_short }}
|
|
||||||
match: none
|
|
||||||
|
|
||||||
- name: configure simple config command
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name foo
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- "'set system host-name foo' in result.commands"
|
|
||||||
|
|
||||||
- name: check simple config command idempontent
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name foo
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
|
|
||||||
- name: Delete services
|
|
||||||
vyos_config: &del
|
|
||||||
lines:
|
|
||||||
- delete service lldp
|
|
||||||
- delete protocols static
|
|
||||||
|
|
||||||
- name: Configuring when commands starts with whitespaces
|
|
||||||
vyos_config:
|
|
||||||
src: "{{ role_path }}/tests/cli/config.cfg"
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
- '"set service lldp" in result.commands'
|
|
||||||
- '"set protocols static" in result.commands'
|
|
||||||
|
|
||||||
- name: Delete services
|
|
||||||
vyos_config: *del
|
|
||||||
|
|
||||||
- name: teardown
|
|
||||||
vyos_config:
|
|
||||||
lines: set system host-name {{ inventory_hostname_short }}
|
|
||||||
match: none
|
|
||||||
|
|
||||||
- debug: msg="END cli/simple.yaml on connection={{ ansible_connection }}"
|
|
@ -1,114 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="END cli_config/backup.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: delete configurable backup file path
|
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: absent
|
|
||||||
with_items:
|
|
||||||
- "{{ role_path }}/backup_test_dir/"
|
|
||||||
- "{{ role_path }}/backup/backup.cfg"
|
|
||||||
|
|
||||||
- name: collect any backup files
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup"
|
|
||||||
pattern: "{{ inventory_hostname_short }}_config*"
|
|
||||||
register: backup_files
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- name: delete backup files
|
|
||||||
file:
|
|
||||||
path: "{{ item.path }}"
|
|
||||||
state: absent
|
|
||||||
with_items: "{{backup_files.files|default([])}}"
|
|
||||||
|
|
||||||
- name: take config backup
|
|
||||||
cli_config:
|
|
||||||
backup: true
|
|
||||||
become: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: collect any backup files
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup"
|
|
||||||
pattern: "{{ inventory_hostname_short }}_config*"
|
|
||||||
register: backup_files
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_files.files is defined"
|
|
||||||
|
|
||||||
- name: take configuration backup in custom filename and directory path
|
|
||||||
cli_config:
|
|
||||||
backup: true
|
|
||||||
backup_options:
|
|
||||||
filename: backup.cfg
|
|
||||||
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
|
|
||||||
become: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: check if the backup file-1 exist
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}/backup.cfg"
|
|
||||||
register: backup_file
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_file.files is defined"
|
|
||||||
|
|
||||||
- name: take configuration backup in custom filename
|
|
||||||
cli_config:
|
|
||||||
backup: true
|
|
||||||
backup_options:
|
|
||||||
filename: backup.cfg
|
|
||||||
become: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: check if the backup file-2 exist
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup/backup.cfg"
|
|
||||||
register: backup_file
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_file.files is defined"
|
|
||||||
|
|
||||||
- name: take configuration backup in custom path and default filename
|
|
||||||
cli_config:
|
|
||||||
backup: true
|
|
||||||
backup_options:
|
|
||||||
dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
|
|
||||||
become: true
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: check if the backup file-3 exist
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}"
|
|
||||||
pattern: "{{ inventory_hostname_short }}_config*"
|
|
||||||
register: backup_file
|
|
||||||
connection: local
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "backup_file.files is defined"
|
|
||||||
|
|
||||||
- debug: msg="END cli_config/backup.yaml on connection={{ ansible_connection }}"
|
|
@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli_config/cli_basic.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup - remove interface description
|
|
||||||
cli_config: &rm
|
|
||||||
config: delete interfaces loopback lo description
|
|
||||||
|
|
||||||
- name: configure device with config
|
|
||||||
cli_config: &conf
|
|
||||||
config: set interfaces loopback lo description 'this is a test'
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: Idempotence
|
|
||||||
cli_config: *conf
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == false"
|
|
||||||
|
|
||||||
- name: teardown
|
|
||||||
cli_config: *rm
|
|
||||||
|
|
||||||
- debug: msg="END cli_config/cli_basic.yaml on connection={{ ansible_connection }}"
|
|
@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
- debug: msg="START cli_config/cli_comment.yaml on connection={{ ansible_connection }}"
|
|
||||||
|
|
||||||
- name: setup
|
|
||||||
cli_config: &rm
|
|
||||||
config: set system host-name {{ inventory_hostname_short }}
|
|
||||||
|
|
||||||
- name: configure using comment
|
|
||||||
cli_config:
|
|
||||||
config: set system host-name foo
|
|
||||||
commit_comment: this is a test
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "result.changed == true"
|
|
||||||
|
|
||||||
- name: collect system commits
|
|
||||||
vyos_command:
|
|
||||||
commands: show system commit
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- "'this is a test' in result.stdout_lines[0][1]"
|
|
||||||
|
|
||||||
- name: teardown
|
|
||||||
cli_config: *rm
|
|
||||||
|
|
||||||
- debug: msg="END cli_config/cli_comment.yaml on connection={{ ansible_connection }}"
|
|
@ -1 +0,0 @@
|
|||||||
shippable/vyos/group1
|
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
testcase: "*"
|
|
||||||
test_items: []
|
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
- name: collect all cli test cases
|
|
||||||
find:
|
|
||||||
paths: "{{ role_path }}/tests/cli"
|
|
||||||
patterns: "{{ testcase }}.yaml"
|
|
||||||
register: test_cases
|
|
||||||
delegate_to: localhost
|
|
||||||
|
|
||||||
- name: set test_items
|
|
||||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
|
||||||
|
|
||||||
- name: run test case (connection=network_cli)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=network_cli"
|
|
||||||
with_items: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
||||||
|
|
||||||
- name: run test case (connection=local)
|
|
||||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
|
||||||
with_first_found: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
@ -1,2 +0,0 @@
|
|||||||
---
|
|
||||||
- {include: cli.yaml, tags: ['cli']}
|
|
@ -1,46 +0,0 @@
|
|||||||
---
|
|
||||||
- name: get host name
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show host name
|
|
||||||
register: vyos_host
|
|
||||||
|
|
||||||
- name: get version info
|
|
||||||
vyos_command:
|
|
||||||
commands:
|
|
||||||
- show version
|
|
||||||
register: vyos_version
|
|
||||||
|
|
||||||
- name: collect all facts from the device
|
|
||||||
vyos_facts:
|
|
||||||
gather_subset: all
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: "check that hostname is present"
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
# hostname
|
|
||||||
- result.ansible_facts.ansible_net_hostname == vyos_host.stdout[0]
|
|
||||||
|
|
||||||
- name: "check that subsets are present"
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
# subsets
|
|
||||||
- "'neighbors' in result.ansible_facts.ansible_net_gather_subset"
|
|
||||||
- "'default' in result.ansible_facts.ansible_net_gather_subset"
|
|
||||||
- "'config' in result.ansible_facts.ansible_net_gather_subset"
|
|
||||||
|
|
||||||
- name: "check that version info is present"
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
# version info
|
|
||||||
- result.ansible_facts.ansible_net_version in vyos_version.stdout_lines[0][0]
|
|
||||||
- result.ansible_facts.ansible_net_model in vyos_version.stdout_lines[0][9]
|
|
||||||
- result.ansible_facts.ansible_net_serialnum in vyos_version.stdout_lines[0][10]
|
|
||||||
|
|
||||||
- name: "check that config info is present"
|
|
||||||
assert:
|
|
||||||
that:
|
|
||||||
# config info
|
|
||||||
- result.ansible_facts.ansible_net_commits is defined
|
|
||||||
- result.ansible_facts.ansible_net_config is defined
|
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
testcase: "[^_].*"
|
|
||||||
test_items: []
|
|
@ -1,2 +0,0 @@
|
|||||||
dependencies:
|
|
||||||
- prepare_vyos_tests
|
|
@ -1,19 +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 }}"
|
|
||||||
|
|
||||||
- name: Run test case (connection=network_cli)
|
|
||||||
include: "{{ test_case_to_run }}"
|
|
||||||
vars:
|
|
||||||
ansible_connection: network_cli
|
|
||||||
with_items: "{{ test_items }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: test_case_to_run
|
|
@ -1,2 +0,0 @@
|
|||||||
---
|
|
||||||
- {include: cli.yaml, tags: ['cli']}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue