VyOS: firewall_interfaces module added (#67254)

* firewall_interfaces module added

Signed-off-by: rohitthakur2590 <rohitthakur2590@outlook.com>

* sanity fixes

Signed-off-by: rohitthakur2590 <rohitthakur2590@outlook.com>

* sanity fixes

Signed-off-by: rohitthakur2590 <rohitthakur2590@outlook.com>

* delete opr updated

Signed-off-by: rohitthakur2590 <rohitthakur2590@outlook.com>

* tests updated

Signed-off-by: rohitthakur2590 <rohitthakur2590@outlook.com>

* comments incorporated

Signed-off-by: rohitthakur2590 <rohitthakur2590@outlook.com>

* ci failure fix

Signed-off-by: rohitthakur2590 <rohitthakur2590@outlook.com>
pull/67895/head
Rohit 5 years ago committed by GitHub
parent 7765870421
commit 8f9f8ec594
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,85 @@
#
# -*- 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

@ -0,0 +1,364 @@
#
# -*- 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'

@ -8,6 +8,7 @@ calls the appropriate facts gathering function
""" """
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
from ansible.module_utils.network.common.facts.facts import FactsBase 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.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.l3_interfaces.l3_interfaces import L3_interfacesFacts
@ -17,6 +18,7 @@ from ansible.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces imp
from ansible.module_utils.network.vyos.facts.firewall_rules.firewall_rules import Firewall_rulesFacts 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.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_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 from ansible.module_utils.network.vyos.facts.legacy.base import Default, Neighbors, Config
@ -33,7 +35,8 @@ FACT_RESOURCE_SUBSETS = dict(
lldp_interfaces=Lldp_interfacesFacts, lldp_interfaces=Lldp_interfacesFacts,
static_routes=Static_routesFacts, static_routes=Static_routesFacts,
firewall_rules=Firewall_rulesFacts, firewall_rules=Firewall_rulesFacts,
firewall_global=Firewall_globalFacts firewall_global=Firewall_globalFacts,
firewall_interfaces=Firewall_interfacesFacts
) )

@ -0,0 +1,183 @@
#
# -*- 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

@ -11,6 +11,7 @@ from ansible.module_utils.compat import ipaddress
def search_obj_in_list(name, lst, key='name'): def search_obj_in_list(name, lst, key='name'):
if lst:
for item in lst: for item in lst:
if item[key] == name: if item[key] == name:
return item return item

@ -52,7 +52,7 @@ options:
can also be used with an initial C(M(!)) to specify that a can also be used with an initial C(M(!)) to specify that a
specific subset should not be collected. specific subset should not be collected.
Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces', Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces',
'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global'. 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global', 'firewall_interfaces'.
required: false required: false
version_added: "2.9" version_added: "2.9"
""" """

File diff suppressed because it is too large Load Diff

@ -0,0 +1,120 @@
---
merged:
before: []
commands:
- "set interfaces ethernet eth1 firewall in name 'INBOUND'"
- "set interfaces ethernet eth1 firewall out name 'OUTBOUND'"
- "set interfaces ethernet eth1 firewall local name 'LOCAL'"
- "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'"
- "set interfaces ethernet eth3 firewall in name 'INBOUND'"
- "set interfaces ethernet eth3 firewall out name 'OUTBOUND'"
- "set interfaces ethernet eth3 firewall local name 'LOCAL'"
- "set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'"
after:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'in'
- name: 'OUBOUND'
direction: 'out'
- afi: 'ipv6'
rules:
- name: 'V6-LOCAL'
direction: 'local'
populate:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'in'
- name: 'OUBOUND'
direction: 'out'
- afi: 'ipv6'
rules:
- name: 'LOCAL'
direction: 'local'
replaced:
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:
- name: 'eth2'
enable: false
location:
civic_based:
country_code: 'US'
ca_info:
- ca_type: 0
ca_value: 'ENGLISH'
- name: 'eth1'
enable: false
location:
coordinate_based:
altitude: 2200
datum: 'WGS84'
longitude: '222.267255W'
latitude: '33.524449N'
populate_intf:
- name: 'eth2'
enable: false
location:
civic_based:
country_code: 'US'
ca_info:
- ca_type: 0
ca_value: 'ENGLISH'
overridden:
commands:
- "delete service lldp interface eth2 location"
- "delete service lldp interface eth2 'disable'"
- "set service lldp interface eth2 location elin '0000000911'"
after:
- name: 'eth2'
location:
elin: 0000000911
deleted:
commands:
- "delete service lldp interface eth1"
- "delete service lldp interface eth2"
after: []
round_trip:
after:
- 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'

@ -0,0 +1,3 @@
---
dependencies:
- prepare_vyos_tests

@ -0,0 +1,19 @@
---
- 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

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

@ -0,0 +1,10 @@
set interfaces ethernet eth1 firewall in name 'INBOUND'
set interfaces ethernet eth1 firewall out name 'OUTBOUND'
set interfaces ethernet eth1 firewall local name 'LOCAL'
set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
set interfaces ethernet eth2 firewall in name 'INBOUND'
set interfaces ethernet eth2 firewall out name 'OUTBOUND'
set interfaces ethernet eth2 firewall local name 'LOCAL'
set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'
set interfaces ethernet eth0

@ -0,0 +1,15 @@
---
- name: Setup
cli_config:
config: "{{ lines }}"
vars:
lines: |
set interfaces ethernet eth1 firewall in name 'INBOUND'
set interfaces ethernet eth1 firewall out name 'OUTBOUND'
set interfaces ethernet eth1 firewall local name 'LOCAL'
set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'
set interfaces ethernet eth2 firewall in name 'INBOUND'
set interfaces ethernet eth2 firewall out name 'OUTBOUND'
set interfaces ethernet eth2 firewall local name 'LOCAL'
set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'

@ -0,0 +1,10 @@
---
- name: Setup
cli_config:
config: "{{ lines }}"
vars:
lines: |
set firewall name 'INBOUND'
set firewall name 'OUTBOUND'
set firewall name 'LOCAL'
set firewall ipv6-name 'V6-LOCAL'

@ -0,0 +1,8 @@
---
- name: Remove Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
delete interfaces ethernet eth1 firewall
delete interfaces ethernet eth2 firewall

@ -0,0 +1,10 @@
---
- name: Remove Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
delete firewall name INBOUND
delete firewall name OUTBOUND
delete firewall name LOCAL
delete firewall ipv6-name V6-LOCAL

@ -0,0 +1,50 @@
---
- debug:
msg: "Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Delete attributes of given firewall rules.
vyos_firewall_interfaces: &deleted
config:
- name: 'eth1'
- name: 'eth2'
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that the correct set of commands were generated
assert:
that:
- "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that the after dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete attributes of given interfaces (IDEMPOTENT)
vyos_firewall_interfaces: *deleted
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,56 @@
---
- debug:
msg: "Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Delete firewall interfaces based on IP address type provided.
vyos_firewall_interfaces: &deleted_afi
config:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
- afi: 'ipv6'
- name: 'eth2'
access_rules:
- afi: 'ipv4'
- afi: 'ipv6'
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that the correct set of commands were generated
assert:
that:
- "{{ deleted_afi['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that the after dicts were correctly generated
assert:
that:
- "{{ deleted_afi['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete attributes of given interfaces (IDEMPOTENT)
vyos_firewall_interfaces: *deleted_afi
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ deleted_afi['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,48 @@
---
- debug:
msg: "Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Delete all the firewall interfaces.
vyos_firewall_interfaces: &deleted_all
config:
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that the correct set of commands were generated
assert:
that:
- "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that the after dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete attributes of given interfaces (IDEMPOTENT)
vyos_firewall_interfaces: *deleted_all
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,54 @@
---
- debug:
msg: "Start vyos_firewall_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Delete firewall interface.
vyos_firewall_interfaces: &deleted_single
config:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- direction: 'in'
name: 'INBOUND'
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that the correct set of commands were generated
assert:
that:
- "{{ deleted_single['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that the after dicts were correctly generated
assert:
that:
- "{{ deleted_single['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete attributes of given interfaces (IDEMPOTENT)
vyos_firewall_interfaces: *deleted_single
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ deleted_single['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

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

@ -0,0 +1,37 @@
---
- debug:
msg: "START vyos_firewall_interfaces gathered integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
vyos_firewall_interfaces: &gathered
config:
state: gathered
register: result
- name: Assert that gathered dicts was correctly generated
assert:
that:
- "{{ populate | symmetric_difference(result['gathered']) |length == 0 }}"
- name: Gather the existing running configuration (IDEMPOTENT)
vyos_firewall_interfaces: *gathered
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,75 @@
---
- debug:
msg: "START vyos_firewall_interfaces merged integration tests on connection={{ ansible_connection }}"
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _remove_config.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
vyos_firewall_interfaces: &merged
config:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- direction: 'in'
name: 'INBOUND'
- direction: 'local'
name: 'LOCAL'
- direction: 'out'
name: 'OUTBOUND'
- afi: 'ipv6'
rules:
- direction: 'local'
name: 'V6-LOCAL'
- name: 'eth2'
access_rules:
- afi: 'ipv4'
rules:
- direction: 'in'
name: 'INBOUND'
- direction: 'local'
name: 'LOCAL'
- direction: 'out'
name: 'OUTBOUND'
- afi: 'ipv6'
rules:
- direction: 'local'
name: 'V6-LOCAL'
state: merged
register: result
- name: Assert that before dicts were correctly generated
assert:
that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that after dicts was correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
vyos_firewall_interfaces: *merged
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Assert that before dicts were correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,59 @@
---
- debug:
msg: "START vyos_firewall_interfaces merged integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
vyos_firewall_interfaces: &merged_edit
config:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- direction: 'in'
name: 'OUTBOUND'
- direction: 'out'
name: 'INBOUND'
state: merged
register: result
- name: Assert that before dicts were correctly generated
assert:
that: "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ merged_edit['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that after dicts was correctly generated
assert:
that:
- "{{ merged_edit['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
vyos_firewall_interfaces: *merged_edit
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Assert that before dicts were correctly generated
assert:
that:
- "{{ merged_edit['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,58 @@
---
- debug:
msg: "START vyos_firewall_interfaces overridden integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Overrides all device configuration with provided configuration
vyos_firewall_interfaces: &overridden
config:
- name: 'eth2'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'out'
state: overridden
register: result
- name: Assert that before dicts were correctly generated
assert:
that:
- "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that correct commands were generated
assert:
that:
- "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that after dicts were correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Overrides all device configuration with provided configurations (IDEMPOTENT)
vyos_firewall_interfaces: *overridden
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Assert that before dicts were correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,44 @@
---
- debug:
msg: "START vyos_firewall_interfaces parsed integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Gather firewall_interfaces facts
vyos_facts:
gather_subset:
- default
gather_network_resources:
- firewall_interfaces
register: firewall_interfaces_facts
- name: Provide the running configuration for parsing (config to be parsed)
vyos_firewall_interfaces: &parsed
running_config:
"{{ lookup('file', '_parsed_config.cfg') }}"
state: parsed
register: result
- name: Assert that correct parsing done
assert:
that: "{{ ansible_facts['network_resources']['firewall_interfaces'] | symmetric_difference(result['parsed']) |length == 0 }}"
- name: Gather the existing running configuration (IDEMPOTENT)
vyos_firewall_interfaces: *parsed
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,55 @@
---
- debug:
msg: "START vyos_firewall_interfaces rendered integration tests on connection={{ ansible_connection }}"
- block:
- name: Structure provided configuration into device specific commands
vyos_firewall_interfaces: &rendered
config:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'in'
- name: 'OUTBOUND'
direction: 'out'
- name: 'LOCAL'
direction: 'local'
- afi: 'ipv6'
rules:
- name: 'V6-LOCAL'
direction: 'local'
- name: 'eth2'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'in'
- name: 'OUTBOUND'
direction: 'out'
- name: 'LOCAL'
direction: 'local'
- afi: 'ipv6'
rules:
- name: 'V6-LOCAL'
direction: 'local'
state: rendered
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ rendered['commands'] | symmetric_difference(result['rendered']) |length == 0 }}"
- name: Structure provided configuration into device specific commands (IDEMPOTENT)
vyos_firewall_interfaces: *rendered
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml

@ -0,0 +1,68 @@
---
- debug:
msg: "START vyos_firewall_interfaces replaced integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml
- include_tasks: _populate_rule_sets.yaml
- include_tasks: _populate.yaml
- block:
- name: Replace device configurations of listed firewall rules with provided configurations
vyos_firewall_interfaces: &replaced
config:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- name: 'OUTBOUND'
direction: 'out'
- afi: 'ipv6'
rules:
- name: 'V6-LOCAL'
direction: 'local'
- name: 'eth2'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'in'
state: replaced
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ populate | symmetric_difference(result['before']) |length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Replace device configurations of listed firewall rules with provided configurarions (IDEMPOTENT)
vyos_firewall_interfaces: *replaced
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Assert that before dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,76 @@
---
- debug:
msg: "START vyos_firewall_interfaces round trip integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml
- include_tasks: _populate_rule_sets.yaml
- block:
- name: Apply the provided configuration (base config)
vyos_firewall_interfaces:
config:
- name: 'eth1'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'in'
- name: 'OUTBOUND'
direction: 'out'
- name: 'LOCAL'
direction: 'local'
- afi: 'ipv6'
rules:
- name: 'V6-LOCAL'
direction: 'local'
state: merged
register: base_config
- name: Gather firewall_interfaces facts
vyos_facts:
gather_subset:
- default
gather_network_resources:
- firewall_interfaces
- name: Apply the provided configuration (config to be reverted)
vyos_firewall_interfaces:
config:
- name: 'eth2'
access_rules:
- afi: 'ipv4'
rules:
- name: 'INBOUND'
direction: 'in'
- name: 'OUTBOUND'
direction: 'out'
- name: 'LOCAL'
direction: 'local'
- afi: 'ipv6'
rules:
- name: 'V6-LOCAL'
direction: 'local'
state: merged
register: result
- name: Assert that changes were applied
assert:
that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length == 0 }}"
- name: Revert back to base config using facts round trip
vyos_firewall_interfaces:
config: "{{ ansible_facts['network_resources']['firewall_interfaces'] }}"
state: overridden
register: revert
- name: Assert that config was reverted
assert:
that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- include_tasks: _remove_firewall_config.yaml

@ -0,0 +1,294 @@
---
merged:
before:
- name: eth0
- name: eth1
- name: eth2
commands:
- "set interfaces ethernet eth1 firewall in name 'INBOUND'"
- "set interfaces ethernet eth1 firewall out name 'OUTBOUND'"
- "set interfaces ethernet eth1 firewall local name 'LOCAL'"
- "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'"
- "set interfaces ethernet eth2 firewall in name 'INBOUND'"
- "set interfaces ethernet eth2 firewall out name 'OUTBOUND'"
- "set interfaces ethernet eth2 firewall local name 'LOCAL'"
- "set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'"
after:
- name: eth0
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth1
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth2
populate:
- name: eth0
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth1
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth2
merged_edit:
commands:
- "set interfaces ethernet eth1 firewall in name 'OUTBOUND'"
- "set interfaces ethernet eth1 firewall out name 'INBOUND'"
after:
- name: eth0
- access_rules:
- afi: ipv4
rules:
- direction: in
name: OUTBOUND
- direction: local
name: LOCAL
- direction: out
name: INBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth1
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth2
replaced:
commands:
- "delete interfaces ethernet eth2 firewall out name"
- "delete interfaces ethernet eth2 firewall local name"
- "delete interfaces ethernet eth2 firewall local ipv6-name"
- "delete interfaces ethernet eth1 firewall local name"
- "delete interfaces ethernet eth1 firewall in name"
after:
- name: eth0
- access_rules:
- afi: ipv4
rules:
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth1
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
name: eth2
overridden:
before:
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth1
- access_rules:
- afi: ipv4
rules:
- name: INBOUND
direction: in
- name: LOCAL
direction: local
- name: OUTBOUND
direction: out
- afi: ipv6
rules:
- name: V6-LOCAL
direction: local
name: eth2
commands:
- "delete interfaces ethernet eth1 firewall"
- "delete interfaces ethernet eth2 firewall in name"
- "delete interfaces ethernet eth2 firewall local name"
- "delete interfaces ethernet eth2 firewall local ipv6-name"
- "set interfaces ethernet eth2 firewall out name 'INBOUND'"
after:
- name: eth0
- name: eth1
- access_rules:
- afi: ipv4
rules:
- name: INBOUND
direction: out
name: eth2
deleted:
commands:
- "delete interfaces ethernet eth1 firewall"
- "delete interfaces ethernet eth2 firewall"
after:
- name: eth0
- name: eth1
- name: eth2
deleted_afi:
commands:
- "delete interfaces ethernet eth1 firewall in name"
- "delete interfaces ethernet eth1 firewall local name"
- "delete interfaces ethernet eth1 firewall out name"
- "delete interfaces ethernet eth1 firewall local ipv6-name"
- "delete interfaces ethernet eth2 firewall in name"
- "delete interfaces ethernet eth2 firewall local name"
- "delete interfaces ethernet eth2 firewall out name"
- "delete interfaces ethernet eth2 firewall local ipv6-name"
after:
- name: eth0
- access_rules:
- afi: ipv4
- afi: ipv6
name: eth1
- access_rules:
- afi: ipv4
- afi: ipv6
name: eth2
deleted_single:
commands:
- "delete interfaces ethernet eth1 firewall in name 'INBOUND'"
after:
- name: eth0
- access_rules:
- afi: ipv4
rules:
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth1
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth2
rendered:
commands:
- "set interfaces ethernet eth1 firewall in name 'INBOUND'"
- "set interfaces ethernet eth1 firewall out name 'OUTBOUND'"
- "set interfaces ethernet eth1 firewall local name 'LOCAL'"
- "set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'"
- "set interfaces ethernet eth2 firewall in name 'INBOUND'"
- "set interfaces ethernet eth2 firewall out name 'OUTBOUND'"
- "set interfaces ethernet eth2 firewall local name 'LOCAL'"
- "set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'"
round_trip:
after:
- name: eth0
- access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL
name: eth1
- name: eth2
access_rules:
- afi: ipv4
rules:
- direction: in
name: INBOUND
- direction: local
name: LOCAL
- direction: out
name: OUTBOUND
- afi: ipv6
rules:
- direction: local
name: V6-LOCAL

@ -0,0 +1,8 @@
set interfaces ethernet eth0 firewall in name 'INBOUND'
set interfaces ethernet eth0 firewall local ipv6-name 'V6-LOCAL'
set interfaces ethernet eth0 firewall local name 'LOCAL'
set interfaces ethernet eth0 firewall out name 'OUTBOUND'
set interfaces ethernet eth2 firewall in name 'INBOUND'
set interfaces ethernet eth2 firewall local ipv6-name 'V6-LOCAL'
set interfaces ethernet eth2 firewall local name 'LOCAL'
set interfaces ethernet eth2 firewall out name 'OUTBOUND'

@ -0,0 +1,305 @@
# (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/>.
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from units.compat.mock import patch, MagicMock
from ansible.modules.network.vyos import vyos_firewall_interfaces
from units.modules.utils import set_module_args
from .vyos_module import TestVyosModule, load_fixture
class TestVyosFirewallInterfacesModule(TestVyosModule):
module = vyos_firewall_interfaces
def setUp(self):
super(TestVyosFirewallInterfacesModule, self).setUp()
self.mock_get_config = patch(
'ansible.module_utils.network.common.network.Config.get_config')
self.get_config = self.mock_get_config.start()
self.mock_load_config = patch(
'ansible.module_utils.network.common.network.Config.load_config')
self.load_config = self.mock_load_config.start()
self.mock_get_resource_connection_config = patch(
'ansible.module_utils.network.common.cfg.base.get_resource_connection'
)
self.get_resource_connection_config = self.mock_get_resource_connection_config.start(
)
self.mock_get_resource_connection_facts = patch(
'ansible.module_utils.network.common.facts.facts.get_resource_connection'
)
self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start(
)
self.mock_execute_show_command = patch(
'ansible.module_utils.network.vyos.facts.firewall_interfaces.firewall_interfaces.Firewall_interfacesFacts.get_device_data'
)
self.execute_show_command = self.mock_execute_show_command.start()
def tearDown(self):
super(TestVyosFirewallInterfacesModule, self).tearDown()
self.mock_get_resource_connection_config.stop()
self.mock_get_resource_connection_facts.stop()
self.mock_get_config.stop()
self.mock_load_config.stop()
self.mock_execute_show_command.stop()
def load_fixtures(self, commands=None):
def load_from_file(*args, **kwargs):
return load_fixture('vyos_firewall_interfaces_config.cfg')
self.execute_show_command.side_effect = load_from_file
def test_vyos_firewall_rule_set_01_merged(self):
set_module_args(
dict(config=[
dict(name='eth1',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
]),
dict(name='eth3',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
])
],
state="merged"))
commands = [
"set interfaces ethernet eth1 firewall in name 'INBOUND'",
"set interfaces ethernet eth1 firewall out name 'OUTBOUND'",
"set interfaces ethernet eth1 firewall local name 'LOCAL'",
"set interfaces ethernet eth1 firewall local ipv6-name 'V6-LOCAL'",
"set interfaces ethernet eth3 firewall in name 'INBOUND'",
"set interfaces ethernet eth3 firewall out name 'OUTBOUND'",
"set interfaces ethernet eth3 firewall local name 'LOCAL'",
"set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_rule_set_02_merged_idem(self):
set_module_args(
dict(config=[
dict(name='eth0',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
]),
dict(name='eth2',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
])
],
state="merged"))
self.execute_module(changed=False, commands=[])
def test_vyos_firewall_rule_set_01_deleted_per_afi(self):
set_module_args(
dict(config=[
dict(name='eth0',
access_rules=[dict(afi='ipv4'),
dict(afi='ipv6')])
],
state="deleted"))
commands = [
"delete interfaces ethernet eth0 firewall in name",
"delete interfaces ethernet eth0 firewall local name",
"delete interfaces ethernet eth0 firewall out name",
"delete interfaces ethernet eth0 firewall local ipv6-name"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_rule_set_03_deleted_per_interface(self):
set_module_args(
dict(config=[dict(name='eth0'),
dict(name='eth2')],
state="deleted"))
commands = [
"delete interfaces ethernet eth0 firewall",
"delete interfaces ethernet eth2 firewall"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_rule_set_03_deleted_all(self):
set_module_args(dict(config=[], state="deleted"))
commands = [
"delete interfaces ethernet eth0 firewall",
"delete interfaces ethernet eth2 firewall"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_rule_set_03_deleted(self):
set_module_args(
dict(config=[dict(name='eth0'),
dict(name='eth2')],
state="deleted"))
commands = [
"delete interfaces ethernet eth0 firewall",
"delete interfaces ethernet eth2 firewall"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_rule_set_04_deleted_interface_idem(self):
set_module_args(
dict(config=[dict(name='eth1'),
dict(name='eth3')],
state="deleted"))
self.execute_module(changed=False, commands=[])
def test_vyos_firewall_rule_set_02_replaced_idem(self):
set_module_args(
dict(config=[
dict(name='eth0',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
]),
dict(name='eth2',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
])
],
state="replaced"))
self.execute_module(changed=False, commands=[])
def test_vyos_firewall_rule_set_01_replaced(self):
set_module_args(
dict(config=[
dict(name='eth0',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
]),
dict(name='eth2',
access_rules=[
dict(afi='ipv4',
rules=[dict(name='LOCAL', direction='local')]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
]),
dict(name='eth3',
access_rules=[
dict(afi='ipv4',
rules=[dict(name='LOCAL', direction='local')]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
])
],
state="replaced"))
commands = [
"delete interfaces ethernet eth0 firewall out name",
"delete interfaces ethernet eth0 firewall local name",
"delete interfaces ethernet eth2 firewall in name",
"delete interfaces ethernet eth2 firewall out name",
"set interfaces ethernet eth3 firewall local name 'LOCAL'",
"set interfaces ethernet eth3 firewall local ipv6-name 'V6-LOCAL'"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_rule_set_01_overridden(self):
set_module_args(
dict(config=[
dict(name='eth1',
access_rules=[
dict(afi='ipv4',
rules=[dict(name='INBOUND', direction='in')])
])
],
state="overridden"))
commands = [
"delete interfaces ethernet eth0 firewall",
"delete interfaces ethernet eth2 firewall",
"set interfaces ethernet eth1 firewall in name 'INBOUND'"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_rule_set_02_overridden_idem(self):
set_module_args(
dict(config=[
dict(name='eth0',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
]),
dict(name='eth2',
access_rules=[
dict(afi='ipv4',
rules=[
dict(name='INBOUND', direction='in'),
dict(name='OUTBOUND', direction='out'),
dict(name='LOCAL', direction='local')
]),
dict(afi='ipv6',
rules=[dict(name='V6-LOCAL', direction='local')])
])
],
state="overridden"))
self.execute_module(changed=False, commands=[])
Loading…
Cancel
Save