VyOS: firewall global module added (#66800)

* firewall global module

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

* sanity fixed

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

* test updated

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

* sanity fixes

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

* group member handling updated

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

* comments incorporated

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

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

@ -0,0 +1,611 @@
#
# -*- 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, 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 list_diff_want_only, list_diff_have_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

@ -16,6 +16,7 @@ from ansible.module_utils.network.vyos.facts.lldp_global.lldp_global import Lldp
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.legacy.base import Default, Neighbors, Config
@ -31,7 +32,8 @@ FACT_RESOURCE_SUBSETS = dict(
lldp_global=Lldp_globalFacts,
lldp_interfaces=Lldp_interfacesFacts,
static_routes=Static_routesFacts,
firewall_rules=Firewall_rulesFacts
firewall_rules=Firewall_rulesFacts,
firewall_global=Firewall_globalFacts
)

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

@ -52,7 +52,7 @@ options:
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'.
'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules', 'firewall_global'.
required: false
version_added: "2.9"
"""

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,2 @@
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,19 @@
set firewall all-ping 'enable'
set firewall broadcast-ping 'enable'
set firewall config-trap 'enable'
set firewall group address-group MGMT-HOSTS address '192.0.1.1'
set firewall group address-group MGMT-HOSTS address '192.0.1.3'
set firewall group address-group MGMT-HOSTS address '192.0.1.5'
set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'
set firewall group network-group MGMT description 'This group has the Management network addresses'
set firewall group network-group MGMT network '192.0.1.0/24'
set firewall ip-src-route 'enable'
set firewall log-martians 'enable'
set firewall receive-redirects 'disable'
set firewall send-redirects 'enable'
set firewall source-validation 'strict'
set firewall state-policy established action 'accept'
set firewall state-policy established log 'enable'
set firewall state-policy invalid action 'reject'
set firewall syn-cookies 'enable'
set firewall twa-hazards-protection 'enable'

@ -0,0 +1,25 @@
---
- name: Setup
cli_config:
config: "{{ lines }}"
vars:
lines: |
set firewall all-ping 'enable'
set firewall broadcast-ping 'enable'
set firewall config-trap 'enable'
set firewall group address-group MGMT-HOSTS address '192.0.1.1'
set firewall group address-group MGMT-HOSTS address '192.0.1.3'
set firewall group address-group MGMT-HOSTS address '192.0.1.5'
set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'
set firewall group network-group MGMT description 'This group has the Management network addresses'
set firewall group network-group MGMT network '192.0.1.0/24'
set firewall ip-src-route 'enable'
set firewall log-martians 'enable'
set firewall receive-redirects 'disable'
set firewall send-redirects 'enable'
set firewall source-validation 'strict'
set firewall state-policy established action 'accept'
set firewall state-policy established log 'enable'
set firewall state-policy invalid action 'reject'
set firewall syn-cookies 'enable'
set firewall twa-hazards-protection 'enable'

@ -0,0 +1,7 @@
---
- name: Remove Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
delete firewall

@ -0,0 +1,44 @@
---
- debug:
msg: "Start vyos_firewall_global deleted integration tests ansible_connection={{ ansible_connection }}"
- include_tasks: _populate.yaml
- block:
- name: Delete attributes of firewall.
vyos_firewall_global: &deleted
config:
state: deleted
register: result
- name: Assert that the before dicts were correctly generated
assert:
that:
- "{{ populate == result['before'] }}"
- 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'] == result['after'] }}"
- name: Delete attributes of given interfaces (IDEMPOTENT)
vyos_firewall_global: *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'] == result['before'] }}"
always:
- include_tasks: _remove_config.yaml

@ -0,0 +1,47 @@
---
- debug:
msg: "START vyos_firewall_global empty_config integration tests on connection={{ ansible_connection }}"
- name: Merged with empty config should give appropriate error message
vyos_firewall_global:
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_global:
config:
state: replaced
register: result
ignore_errors: true
- assert:
that:
- result.msg == 'value of config parameter must not be empty for state replaced'
- name: Parsed with empty running_config should give appropriate error message
vyos_firewall_global:
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_global:
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,31 @@
---
- debug:
msg: "START vyos_firewall_global gathered integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
vyos_firewall_global: &gathered
config:
state: gathered
register: result
- name: Assert that gathered dicts was correctly generated
assert:
that:
- "{{ populate == result['gathered'] }}"
- name: Gather the existing running configuration (IDEMPOTENT)
vyos_firewall_global: *gathered
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml

@ -0,0 +1,76 @@
---
- debug:
msg: "START vyos_firewall_global merged integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Merge the provided configuration with the exisiting running configuration
vyos_firewall_global: &merged
config:
validation: strict
config_trap: True
log_martians: True
syn_cookies: True
twa_hazards_protection: True
ping:
all: True
broadcast: True
state_policy:
- connection_type: 'established'
action: 'accept'
log: True
- connection_type: 'invalid'
action: 'reject'
route_redirects:
- afi: 'ipv4'
ip_src_route: True
icmp_redirects:
send: True
receive: False
group:
address_group:
- name: 'MGMT-HOSTS'
description: 'This group has the Management hosts address list'
members:
- address: 192.0.1.1
- address: 192.0.1.3
- address: 192.0.1.5
network_group:
- name: 'MGMT'
description: 'This group has the Management network addresses'
members:
- address: 192.0.1.0/24
state: merged
register: result
- name: Assert that before dicts were correctly generated
assert:
that: "{{ merged['before'] == result['before'] }}"
- 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'] == result['after'] }}"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
vyos_firewall_global: *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'] == result['before'] }}"
always:
- include_tasks: _remove_config.yaml

@ -0,0 +1,39 @@
---
- debug:
msg: "START vyos_firewall_global parsed integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Gather firewall_global facts
vyos_facts:
gather_subset:
- default
gather_network_resources:
- firewall_global
register: firewall_global_facts
- name: Provide the running configuration for parsing (config to be parsed)
vyos_firewall_global: &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_global'] == result['parsed'] }}"
- name: Gather the existing running configuration (IDEMPOTENT)
vyos_firewall_global: *parsed
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml

@ -0,0 +1,69 @@
---
- debug:
msg: "START vyos_firewall_global rendered integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Structure provided configuration into device specific commands
vyos_firewall_global: &rendered
config:
validation: strict
config_trap: True
log_martians: True
syn_cookies: True
twa_hazards_protection: True
ping:
all: True
broadcast: True
state_policy:
- connection_type: 'established'
action: 'accept'
log: True
- connection_type: 'invalid'
action: 'reject'
route_redirects:
- afi: 'ipv4'
ip_src_route: True
icmp_redirects:
send: True
receive: False
group:
address_group:
- name: 'SALES-HOSTS'
description: 'Sales office hosts address list'
members:
- address: 192.0.2.1
- address: 192.0.2.2
- address: 192.0.2.3
- name: 'ENG-HOSTS'
description: 'Sales office hosts address list'
members:
- address: 192.0.3.1
- address: 192.0.3.2
network_group:
- name: 'MGMT'
description: 'This group has the Management network addresses'
members:
- address: 192.0.1.0/24
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_global: *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,84 @@
---
- debug:
msg: "START vyos_firewall_global replaced integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate.yaml
- block:
- name: Replace device configurations of listed firewall with provided configurations
vyos_firewall_global: &replaced
config:
validation: strict
config_trap: True
log_martians: True
syn_cookies: True
twa_hazards_protection: True
ping:
all: True
broadcast: True
state_policy:
- connection_type: 'established'
action: 'accept'
log: True
- connection_type: 'invalid'
action: 'reject'
route_redirects:
- afi: 'ipv4'
ip_src_route: True
icmp_redirects:
send: True
receive: False
group:
address_group:
- name: 'SALES-HOSTS'
description: 'Sales office hosts address list'
members:
- address: 192.0.2.1
- address: 192.0.2.2
- address: 192.0.2.3
- name: 'ENG-HOSTS'
description: 'Sales office hosts address list'
members:
- address: 192.0.3.1
- address: 192.0.3.2
network_group:
- name: 'MGMT'
description: 'This group has the Management network addresses'
members:
- address: 192.0.1.0/24
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 == result['before'] }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ replaced['after'] == result['after'] }}"
- name: Replace device configurations of listed firewall with provided configurarions (IDEMPOTENT)
vyos_firewall_global: *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'] == result['before'] }}"
always:
- include_tasks: _remove_config.yaml

@ -0,0 +1,83 @@
---
- debug:
msg: "START vyos_firewall_global round trip integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Apply the provided configuration (base config)
vyos_firewall_global:
config:
validation: strict
config_trap: True
log_martians: True
syn_cookies: True
twa_hazards_protection: True
ping:
all: True
broadcast: True
state_policy:
- connection_type: 'established'
action: 'accept'
log: True
- connection_type: 'invalid'
action: 'reject'
route_redirects:
- afi: 'ipv4'
ip_src_route: True
icmp_redirects:
send: True
receive: False
group:
address_group:
- name: 'MGMT-HOSTS'
description: 'This group has the Management hosts address list'
members:
- address: 192.0.1.1
- address: 192.0.1.3
- address: 192.0.1.5
network_group:
- name: 'MGMT'
description: 'This group has the Management network addresses'
members:
- address: 192.0.1.0/24
state: merged
register: base_config
- name: Gather firewall_global facts
vyos_facts:
gather_subset:
- default
gather_network_resources:
- firewall_global
- name: Apply the provided configuration (config to be reverted)
vyos_firewall_global:
config:
validation: strict
config_trap: False
log_martians: False
syn_cookies: False
twa_hazards_protection: False
ping:
all: False
broadcast: False
state: merged
register: result
- name: Assert that changes were applied
assert:
that: "{{ round_trip['after'] == result['after'] }}"
- name: Revert back to base config using facts round trip
vyos_firewall_global:
config: "{{ ansible_facts['network_resources']['firewall_global'] }}"
state: replaced
register: revert
- name: Assert that config was reverted
assert:
that: "{{ base_config['after'] == revert['after']}}"
always:
- include_tasks: _remove_config.yaml

@ -0,0 +1,218 @@
---
merged:
before: []
commands:
- "set firewall group address-group MGMT-HOSTS address 192.0.1.1"
- "set firewall group address-group MGMT-HOSTS address 192.0.1.3"
- "set firewall group address-group MGMT-HOSTS address 192.0.1.5"
- "set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list'"
- "set firewall group address-group MGMT-HOSTS"
- "set firewall group network-group MGMT network 192.0.1.0/24"
- "set firewall group network-group MGMT description 'This group has the Management network addresses'"
- "set firewall group network-group MGMT"
- "set firewall ip-src-route 'enable'"
- "set firewall receive-redirects 'disable'"
- "set firewall send-redirects 'enable'"
- "set firewall config-trap 'enable'"
- "set firewall state-policy established action 'accept'"
- "set firewall state-policy established log 'enable'"
- "set firewall state-policy invalid action 'reject'"
- "set firewall broadcast-ping 'enable'"
- "set firewall all-ping 'enable'"
- "set firewall log-martians 'enable'"
- "set firewall twa-hazards-protection 'enable'"
- "set firewall syn-cookies 'enable'"
- "set firewall source-validation 'strict'"
after:
config_trap: true
group:
address_group:
- members:
- address: 192.0.1.1
- address: 192.0.1.3
- address: 192.0.1.5
description: This group has the Management hosts address list
name: MGMT-HOSTS
network_group:
- members:
- address: 192.0.1.0/24
description: This group has the Management network addresses
name: MGMT
log_martians: true
ping:
all: true
broadcast: true
route_redirects:
- afi: ipv4
icmp_redirects:
receive: false
send: true
ip_src_route: true
syn_cookies: true
state_policy:
- action: accept
connection_type: established
log: true
- action: reject
connection_type: invalid
twa_hazards_protection: true
validation: strict
populate:
validation: strict
config_trap: True
log_martians: True
syn_cookies: True
twa_hazards_protection: True
ping:
all: True
broadcast: True
state_policy:
- connection_type: 'established'
action: 'accept'
log: True
- connection_type: 'invalid'
action: 'reject'
route_redirects:
- afi: 'ipv4'
ip_src_route: True
icmp_redirects:
send: True
receive: False
group:
address_group:
- name: 'MGMT-HOSTS'
description: 'This group has the Management hosts address list'
members:
- address: 192.0.1.1
- address: 192.0.1.3
- address: 192.0.1.5
network_group:
- name: 'MGMT'
description: 'This group has the Management network addresses'
members:
- address: 192.0.1.0/24
replaced:
commands:
- "delete firewall group address-group MGMT-HOSTS"
- "set firewall group address-group SALES-HOSTS address 192.0.2.1"
- "set firewall group address-group SALES-HOSTS address 192.0.2.2"
- "set firewall group address-group SALES-HOSTS address 192.0.2.3"
- "set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'"
- "set firewall group address-group SALES-HOSTS"
- "set firewall group address-group ENG-HOSTS address 192.0.3.1"
- "set firewall group address-group ENG-HOSTS address 192.0.3.2"
- "set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'"
- "set firewall group address-group ENG-HOSTS"
after:
config_trap: true
group:
address_group:
- members:
- address: 192.0.3.1
- address: 192.0.3.2
description: 'Sales office hosts address list'
name: 'ENG-HOSTS'
- members:
- address: 192.0.2.1
- address: 192.0.2.2
- address: 192.0.2.3
description: 'Sales office hosts address list'
name: 'SALES-HOSTS'
network_group:
- members:
- address: 192.0.1.0/24
description: 'This group has the Management network addresses'
name: 'MGMT'
log_martians: true
ping:
all: true
broadcast: true
route_redirects:
- afi: 'ipv4'
icmp_redirects:
receive: false
send: true
ip_src_route: true
state_policy:
- action: 'accept'
connection_type: 'established'
log: true
- action: 'reject'
connection_type: 'invalid'
syn_cookies: true
twa_hazards_protection: true
validation: strict
rendered:
commands:
- set firewall group address-group SALES-HOSTS address 192.0.2.1
- set firewall group address-group SALES-HOSTS address 192.0.2.2
- set firewall group address-group SALES-HOSTS address 192.0.2.3
- set firewall group address-group SALES-HOSTS description 'Sales office hosts address list'
- set firewall group address-group SALES-HOSTS
- set firewall group address-group ENG-HOSTS address 192.0.3.1
- set firewall group address-group ENG-HOSTS address 192.0.3.2
- set firewall group address-group ENG-HOSTS description 'Sales office hosts address list'
- set firewall group address-group ENG-HOSTS
- set firewall group network-group MGMT network 192.0.1.0/24
- set firewall group network-group MGMT description 'This group has the Management network addresses'
- set firewall group network-group MGMT
- set firewall ip-src-route 'enable'
- set firewall receive-redirects 'disable'
- set firewall send-redirects 'enable'
- set firewall config-trap 'enable'
- set firewall state-policy established action 'accept'
- set firewall state-policy established log 'enable'
- set firewall state-policy invalid action 'reject'
- set firewall broadcast-ping 'enable'
- set firewall all-ping 'enable'
- set firewall log-martians 'enable'
- set firewall twa-hazards-protection 'enable'
- set firewall syn-cookies 'enable'
- set firewall source-validation 'strict'
deleted:
commands:
- "delete firewall "
after: []
round_trip:
after:
validation: strict
config_trap: False
log_martians: False
syn_cookies: False
twa_hazards_protection: False
ping:
all: False
broadcast: False
state_policy:
- connection_type: 'established'
action: 'accept'
log: True
- connection_type: 'invalid'
action: 'reject'
route_redirects:
- afi: 'ipv4'
ip_src_route: True
icmp_redirects:
send: True
receive: False
group:
address_group:
- name: 'MGMT-HOSTS'
description: 'This group has the Management hosts address list'
members:
- address: 192.0.1.1
- address: 192.0.1.3
- address: 192.0.1.5
network_group:
- name: 'MGMT'
description: 'This group has the Management network addresses'
members:
- address: 192.0.1.0/24

@ -0,0 +1,6 @@
set firewall group address-group RND-HOSTS address 192.0.2.1
set firewall group address-group RND-HOSTS address 192.0.2.3
set firewall group address-group RND-HOSTS address 192.0.2.5
set firewall group address-group RND-HOSTS description 'This group has the Management hosts address lists'
set firewall group network-group RND network 192.0.2.0/24
set firewall group network-group RND description 'This group has the Management network addresses'

@ -0,0 +1,207 @@
# (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_global
from units.modules.utils import set_module_args
from .vyos_module import TestVyosModule, load_fixture
class TestVyosFirewallRulesModule(TestVyosModule):
module = vyos_firewall_global
def setUp(self):
super(TestVyosFirewallRulesModule, 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_global.firewall_global.Firewall_globalFacts.get_device_data'
)
self.execute_show_command = self.mock_execute_show_command.start()
def tearDown(self):
super(TestVyosFirewallRulesModule, 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_global_config.cfg')
self.execute_show_command.side_effect = load_from_file
def test_vyos_firewall_global_set_01_merged(self):
set_module_args(
dict(config=dict(
validation='strict',
config_trap=True,
log_martians=True,
syn_cookies=True,
twa_hazards_protection=True,
ping=dict(all=True, broadcast=True),
state_policy=[
dict(
connection_type='established',
action='accept',
log=True,
),
dict(connection_type='invalid', action='reject')
],
route_redirects=[
dict(afi='ipv4',
ip_src_route=True,
icmp_redirects=dict(send=True, receive=False))
],
group=dict(
address_group=[
dict(
name='MGMT-HOSTS',
description='This group has the Management hosts address lists',
members=[
dict(address='192.0.1.1'),
dict(address='192.0.1.3'),
dict(address='192.0.1.5')
])
],
network_group=[
dict(name='MGMT',
description='This group has the Management network addresses',
members=[dict(address='192.0.1.0/24')])
])),
state="merged"))
commands = [
"set firewall group address-group MGMT-HOSTS address 192.0.1.1",
"set firewall group address-group MGMT-HOSTS address 192.0.1.3",
"set firewall group address-group MGMT-HOSTS address 192.0.1.5",
"set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address lists'",
"set firewall group address-group MGMT-HOSTS",
"set firewall group network-group MGMT network 192.0.1.0/24",
"set firewall group network-group MGMT description 'This group has the Management network addresses'",
"set firewall group network-group MGMT",
"set firewall ip-src-route 'enable'",
"set firewall receive-redirects 'disable'",
"set firewall send-redirects 'enable'",
"set firewall config-trap 'enable'",
"set firewall state-policy established action 'accept'",
"set firewall state-policy established log 'enable'",
"set firewall state-policy invalid action 'reject'",
"set firewall broadcast-ping 'enable'",
"set firewall all-ping 'enable'",
"set firewall log-martians 'enable'",
"set firewall twa-hazards-protection 'enable'",
"set firewall syn-cookies 'enable'",
"set firewall source-validation 'strict'"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_global_set_01_merged_idem(self):
set_module_args(
dict(config=dict(group=dict(
address_group=[
dict(name='RND-HOSTS',
description='This group has the Management hosts address lists',
members=[
dict(address='192.0.2.1'),
dict(address='192.0.2.3'),
dict(address='192.0.2.5')
])
],
network_group=[
dict(name='RND',
description='This group has the Management network addresses',
members=[dict(address='192.0.2.0/24')])
])),
state="merged"))
self.execute_module(changed=False, commands=[])
def test_vyos_firewall_global_set_01_replaced(self):
set_module_args(
dict(config=dict(group=dict(
address_group=[
dict(name='RND-HOSTS',
description='This group has the Management hosts address lists',
members=[
dict(address='192.0.2.1'),
dict(address='192.0.2.7'),
dict(address='192.0.2.9')
])
],
network_group=[
dict(name='RND',
description='This group has the Management network addresses',
members=[dict(address='192.0.2.0/24')])
])),
state="replaced"))
commands = [
"delete firewall group address-group RND-HOSTS address 192.0.2.3",
"delete firewall group address-group RND-HOSTS address 192.0.2.5",
"set firewall group address-group RND-HOSTS address 192.0.2.7",
"set firewall group address-group RND-HOSTS address 192.0.2.9"
]
self.execute_module(changed=True, commands=commands)
def test_vyos_firewall_global_set_01_replaced_idem(self):
set_module_args(
dict(config=dict(group=dict(
address_group=[
dict(name='RND-HOSTS',
description='This group has the Management hosts address lists',
members=[
dict(address='192.0.2.1'),
dict(address='192.0.2.3'),
dict(address='192.0.2.5')
])
],
network_group=[
dict(name='RND',
description='This group has the Management network addresses',
members=[dict(address='192.0.2.0/24')])
])),
state="replaced"))
self.execute_module(changed=False, commands=[])
def test_vyos_firewall_global_set_01_deleted(self):
set_module_args(dict(config=dict(), state="deleted"))
commands = ["delete firewall "]
self.execute_module(changed=True, commands=commands)
Loading…
Cancel
Save