diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b635fa6..9737666 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,35 +1,35 @@ --- repos: - repo: https://github.com/ansible-network/collection_prep rev: 1.1.0 hooks: - id: autoversion - id: update-docs - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-merge-conflict - id: debug-statements - id: end-of-file-fixer - id: no-commit-to-branch - id: trailing-whitespace - repo: https://github.com/pre-commit/mirrors-prettier rev: "v3.0.0-alpha.4" hooks: - id: prettier additional_dependencies: - prettier - prettier-plugin-toml - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort args: ["--filter-files"] - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black diff --git a/plugins/doc_fragments/vyos.py b/plugins/doc_fragments/vyos.py index c6abc21..698d0f5 100644 --- a/plugins/doc_fragments/vyos.py +++ b/plugins/doc_fragments/vyos.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function __metaclass__ = type # Copyright: (c) 2015, Peter Sprygada # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) class ModuleDocFragment(object): - # Standard files documentation fragment DOCUMENTATION = r"""options: {} notes: - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide ` """ diff --git a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py index 96c640f..55eca49 100644 --- a/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py +++ b/plugins/module_utils/network/vyos/config/bgp_global/bgp_global.py @@ -1,407 +1,406 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_bgp_global config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ import re from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global import ( Bgp_globalTemplate, ) class Bgp_global(ResourceModule): """ The vyos_bgp_global config class """ def __init__(self, module): super(Bgp_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="bgp_global", tmplt=Bgp_globalTemplate(), ) self.parsers = [] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = {} haved = {} if self.want.get("as_number") == self.have.get("as_number") or not self.have: if self.want: wantd = {self.want["as_number"]: self.want} if self.have: haved = {self.have["as_number"]: self.have} else: self._module.fail_json(msg="Only one bgp instance is allowed per device") # turn all lists of dicts into dicts prior to merge for entry in wantd, haved: self._bgp_global_list_to_dict(entry) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "purged": h_del = {} for k, v in iteritems(haved): if k in wantd or not wantd: h_del.update({k: v}) for num, entry in iteritems(h_del): self.commands.append(self._tmplt.render({"as_number": num}, "router", True)) wantd = {} if self.state == "deleted": self._compare(want={}, have=self.have) wantd = {} for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Bgp_global network resource. """ parsers = ["maximum_paths", "timers"] self._compare_neighbor(want, have) self._compare_lists(want, have) self._compare_bgp_params(want, have) for name, entry in iteritems(want): if name != "as_number": self.compare( parsers=parsers, want={"as_number": want["as_number"], name: entry}, have={ "as_number": want["as_number"], name: have.pop(name, {}), }, ) for name, entry in iteritems(have): if name != "as_number": self.compare( parsers=parsers, want={}, have={"as_number": have["as_number"], name: entry}, ) # Do the negation first command_set = [] for cmd in self.commands: if cmd not in command_set: if "delete" in cmd: command_set.insert(0, cmd) else: command_set.append(cmd) self.commands = command_set def _compare_neighbor(self, want, have): - parsers = [ "neighbor.advertisement_interval", "neighbor.allowas_in", "neighbor.as_override", "neighbor.attribute_unchanged.as_path", "neighbor.attribute_unchanged.med", "neighbor.attribute_unchanged.next_hop", "neighbor.capability_dynamic", "neighbor.capability_orf", "neighbor.default_originate", "neighbor.description", "neighbor.disable_capability_negotiation", "neighbor.disable_connected_check", "neighbor.disable_send_community", "neighbor.distribute_list", "neighbor.ebgp_multihop", "neighbor.filter_list", "neighbor.local_as", "neighbor.maximum_prefix", "neighbor.nexthop_self", "neighbor.override_capability", "neighbor.passive", "neighbor.password", "neighbor.peer_group_name", "neighbor.port", "neighbor.prefix_list", "neighbor.remote_as", "neighbor.remove_private_as", "neighbor.route_map", "neighbor.route_reflector_client", "neighbor.route_server_client", "neighbor.shutdown", "neighbor.soft_reconfiguration", "neighbor.strict_capability_match", "neighbor.unsuppress_map", "neighbor.update_source", "neighbor.weight", "neighbor.ttl_security", "neighbor.timers", "network.backdoor", "network.route_map", ] wneigh = want.pop("neighbor", {}) hneigh = have.pop("neighbor", {}) self._compare_neigh_lists(wneigh, hneigh) for name, entry in iteritems(wneigh): for k, v in entry.items(): if k == "address": continue if hneigh.get(name): h = {"address": name, k: hneigh[name].pop(k, {})} else: h = {} self.compare( parsers=parsers, want={ "as_number": want["as_number"], "neighbor": {"address": name, k: v}, }, have={"as_number": want["as_number"], "neighbor": h}, ) for name, entry in iteritems(hneigh): if name not in wneigh.keys(): if self._check_af(name): msg = "Use the _bgp_address_family module to delete the address_family under neighbor {0}, before replacing/deleting the neighbor.".format( name ) self._module.fail_json(msg=msg) else: self.commands.append( "delete protocols bgp " + str(have["as_number"]) + " neighbor " + name ) continue for k, v in entry.items(): self.compare( parsers=parsers, want={}, have={ "as_number": have["as_number"], "neighbor": {"address": name, k: v}, }, ) def _compare_bgp_params(self, want, have): parsers = [ "bgp_params.always_compare_med", "bgp_params.bestpath.as_path", "bgp_params.bestpath.compare_routerid", "bgp_params.bestpath.med", "bgp_params.cluster_id", "bgp_params.confederation", "bgp_params.dampening_half_life", "bgp_params.dampening_max_suppress_time", "bgp_params.dampening_re_use", "bgp_params.dampening_start_suppress_time", "bgp_params.default", "bgp_params.deterministic_med", "bgp_params.disbale_network_import_check", "bgp_params.enforce_first_as", "bgp_params.graceful_restart", "bgp_params.log_neighbor_changes", "bgp_params.no_client_to_client_reflection", "bgp_params.no_fast_external_failover", "bgp_params.routerid", "bgp_params.scan_time", ] wbgp = want.pop("bgp_params", {}) hbgp = have.pop("bgp_params", {}) for name, entry in iteritems(wbgp): if name == "confederation": if entry != hbgp.pop(name, {}): self.addcmd( { "as_number": want["as_number"], "bgp_params": {name: entry}, }, "bgp_params.confederation", False, ) elif name == "distance": if entry != hbgp.pop(name, {}): distance_parsers = [ "bgp_params.distance.global", "bgp_params.distance.prefix", ] for distance_type in entry: self.compare( parsers=distance_parsers, want={ "as_number": want["as_number"], "bgp_params": {name: distance_type}, }, have={ "as_number": want["as_number"], "bgp_params": {name: hbgp.pop(name, {})}, }, ) else: self.compare( parsers=parsers, want={ "as_number": want["as_number"], "bgp_params": {name: entry}, }, have={ "as_number": want["as_number"], "bgp_params": {name: hbgp.pop(name, {})}, }, ) if not wbgp and hbgp: self.commands.append("delete protocols bgp " + str(have["as_number"]) + " parameters") hbgp = {} for name, entry in iteritems(hbgp): if name == "confederation": self.commands.append( "delete protocols bgp " + str(have["as_number"]) + " parameters confederation" ) elif name == "distance": distance_parsers = [ "bgp_params.distance.global", "bgp_params.distance.prefix", ] self.compare( parsers=distance_parsers, want={}, have={ "as_number": have["as_number"], "bgp_params": {name: entry[0]}, }, ) else: self.compare( parsers=parsers, want={}, have={ "as_number": have["as_number"], "bgp_params": {name: entry}, }, ) def _compare_lists(self, want, have): parsers = [ "network.backdoor", "network.route_map", "redistribute.metric", "redistribute.route_map", "aggregate_address", ] for attrib in ["redistribute", "network", "aggregate_address"]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) for key, entry in iteritems(wdict): if entry != hdict.get(key, {}): self.compare( parsers=parsers, want={"as_number": want["as_number"], attrib: entry}, have=hdict.pop(key, {}), ) hdict.pop(key, {}) # remove remaining items in have for replaced if not wdict and hdict: attrib = re.sub("_", "-", attrib) self.commands.append( "delete protocols bgp " + str(have["as_number"]) + " " + attrib ) hdict = {} for key, entry in iteritems(hdict): self.compare( parsers=parsers, want={}, have={"as_number": have["as_number"], attrib: entry}, ) def _compare_neigh_lists(self, want, have): for attrib in [ "distribute_list", "filter_list", "prefix_list", "route_map", ]: wdict = want.pop(attrib, {}) hdict = have.pop(attrib, {}) for key, entry in iteritems(wdict): if entry != hdict.pop(key, {}): self.addcmd(entry, "neighbor.{0}".format(attrib), False) # remove remaining items in have for replaced for entry in hdict.values(): self.addcmd(entry, "neighbor.{0}".format(attrib), True) def _bgp_global_list_to_dict(self, entry): for name, proc in iteritems(entry): if "neighbor" in proc: neigh_dict = {} for entry in proc.get("neighbor", []): neigh_dict.update({entry["address"]: entry}) proc["neighbor"] = neigh_dict if "network" in proc: network_dict = {} for entry in proc.get("network", []): network_dict.update({entry["address"]: entry}) proc["network"] = network_dict if "aggregate_address" in proc: agg_dict = {} for entry in proc.get("aggregate_address", []): agg_dict.update({entry["prefix"]: entry}) proc["aggregate_address"] = agg_dict if "redistribute" in proc: redis_dict = {} for entry in proc.get("redistribute", []): redis_dict.update({entry["protocol"]: entry}) proc["redistribute"] = redis_dict def _check_af(self, neighbor): af_present = False if self._connection: config_lines = self._get_config(self._connection).splitlines() for line in config_lines: if "address-family" in line: af_present = True return af_present def _get_config(self, connection): return connection.get( 'show configuration commands | match "set protocols bgp .* neighbor"' ) diff --git a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py index cd677d4..3ce6ec2 100644 --- a/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py +++ b/plugins/module_utils/network/vyos/config/ntp_global/ntp_global.py @@ -1,188 +1,187 @@ # # -*- coding: utf-8 -*- # Copyright 2021 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type """ The vyos_ntp config file. It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to its desired end-state is created. """ from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import ( ResourceModule, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_merge, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ntp_global import ( NtpTemplate, ) class Ntp_global(ResourceModule): """ The vyos_ntp config class """ def __init__(self, module): super(Ntp_global, self).__init__( empty_fact_val={}, facts_module=Facts(module), module=module, resource="ntp_global", tmplt=NtpTemplate(), ) self.parsers = [ "allow_clients", "listen_addresses", "server", "options", "allow_clients_delete", "listen_addresses_delete", ] def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ if self.state not in ["parsed", "gathered"]: self.generate_commands() self.run_commands() return self.result def generate_commands(self): """Generate configuration commands to send based on want, have and desired state. """ wantd = self._ntp_list_to_dict(self.want) haved = self._ntp_list_to_dict(self.have) # if state is merged, merge want onto have and then compare if self.state == "merged": wantd = dict_merge(haved, wantd) # if state is deleted, empty out wantd and set haved to wantd if self.state == "deleted": haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd} wantd = {} commandlist = self._commandlist(haved) servernames = self._servernames(haved) # removing the servername and commandlist from the list after deleting it from haved for k, have in iteritems(haved): if k not in wantd: for hk, hval in iteritems(have): if hk == "allow_clients" and hk in commandlist: self.commands.append( self._tmplt.render({"": hk}, "allow_clients_delete", True) ) commandlist.remove(hk) elif hk == "listen_addresses" and hk in commandlist: self.commands.append( self._tmplt.render({"": hk}, "listen_addresses_delete", True) ) commandlist.remove(hk) elif hk == "server" and have["server"] in servernames: self._compareoverride(want={}, have=have) servernames.remove(have["server"]) # remove existing config for overridden,replaced and deleted # Getting the list of the server names from haved # to avoid the duplication of overridding/replacing the servers if self.state in ["overridden", "replaced"]: - commandlist = self._commandlist(haved) servernames = self._servernames(haved) for k, have in iteritems(haved): if k not in wantd and "server" not in have: self._compareoverride(want={}, have=have) # removing the servername from the list after deleting it from haved elif k not in wantd and have["server"] in servernames: self._compareoverride(want={}, have=have) servernames.remove(have["server"]) for k, want in iteritems(wantd): self._compare(want=want, have=haved.pop(k, {})) def _compare(self, want, have): """Leverages the base class `compare()` method and populates the list of commands to be run by comparing the `want` and `have` data with the `parsers` defined for the Ntp network resource. """ if "options" in want: self.compare(parsers="options", want=want, have=have) else: self.compare(parsers=self.parsers, want=want, have=have) def _compareoverride(self, want, have): # do not delete configuration with options level for i, val in iteritems(have): if i == "options": pass else: self.compare(parsers=i, want={}, have=have) def _ntp_list_to_dict(self, entry): servers_dict = {} for k, data in iteritems(entry): if k == "servers": for value in data: if "options" in value: result = self._serveroptions_list_to_dict(value) for res, resvalue in iteritems(result): servers_dict.update({res: resvalue}) else: servers_dict.update({value["server"]: value}) else: for value in data: servers_dict.update({"ip_" + value: {k: value}}) return servers_dict def _serveroptions_list_to_dict(self, entry): serveroptions_dict = {} for Opk, Op in iteritems(entry): if Opk == "options": for val in Op: dict = {} dict.update({"server": entry["server"]}) dict.update({Opk: val}) serveroptions_dict.update({entry["server"] + "_" + val: dict}) return serveroptions_dict def _commandlist(self, haved): commandlist = [] for k, have in iteritems(haved): for ck, cval in iteritems(have): if ck != "options" and ck not in commandlist: commandlist.append(ck) return commandlist def _servernames(self, haved): servernames = [] for k, have in iteritems(haved): for sk, sval in iteritems(have): if sk == "server" and sval not in [ "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", ]: if sval not in servernames: servernames.append(sval) return servernames diff --git a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py index 9287fbc..dedc241 100644 --- a/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/config/ospfv2/ospfv2.py @@ -1,810 +1,809 @@ # # -*- 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_ospfv2 class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( _bool_to_str, _in_target, _is_w_same, list_diff_want_only, ) class Ospfv2(ConfigBase): """ The vyos_ospfv2 class """ gather_subset = ["!all", "!min"] gather_network_resources = ["ospfv2"] def __init__(self, module): super(Ospfv2, self).__init__(module) def get_ospfv2_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 ) ospfv2_facts = facts["ansible_network_resources"].get("ospfv2", {}) return ospfv2_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_ospfv2_facts = self.get_ospfv2_facts() else: existing_ospfv2_facts = {} if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_ospfv2_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_ospfv2_facts = self.get_ospfv2_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_ospfv2_facts(data=running_config) else: changed_ospfv2_facts = {} if self.state in self.ACTION_STATES: result["before"] = existing_ospfv2_facts if result["changed"]: result["after"] = changed_ospfv2_facts elif self.state == "gathered": result["gathered"] = changed_ospfv2_facts result["warnings"] = warnings return result def set_config(self, existing_ospfv2_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_ospfv2_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 == "deleted": commands.extend(self._state_deleted(h)) elif self.state in ("merged", "rendered"): commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def search_obj_in_have(self, have, w_name, key): """ This function returns the rule-set/rule if it is present in target config. :param have: target config. :param w_name: rule-set name. :param type: rule_sets/rule/r_list. :return: rule-set/rule. """ if have: for item in have: if item[key] == w_name[key]: return item return None def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._render_ospf_param(have, want, opr=False)) commands.extend(self._render_ospf_param(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] commands.extend(self._render_ospf_param(want, have)) return commands def _state_deleted(self, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if have: commands.append("delete protocols ospf") return commands def _render_ospf_param(self, want, have, opr=True): """ This function forms the set/delete commands for ospf leaf attributes and triggers the process for other child attributes. for firewall_global attributes. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] w = deepcopy(remove_empties(want)) leaf = ("default_metric", "log_adjacency_changes") if w: - for (key, val) in iteritems(w): + for key, val in iteritems(w): if opr and key in leaf and not _is_w_same(w, have, key): commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) elif not opr and key in leaf and not _in_target(have, key): commands.append(self._form_attr_cmd(attr=key, val=_bool_to_str(val), opr=opr)) else: commands.extend(self._render_child_param(w, have, key, opr)) return commands def _render_child_param(self, w, h, key, opr=True): """ 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 in ("neighbor", "redistribute"): commands.extend(self._render_list_dict_param(key, w, h, opr=opr)) elif key in ("default_information", "max_metric"): commands.extend(self._render_nested_dict_param(key, w, h, opr=opr)) elif key in ("mpls_te", "auto_cost", "parameters", "auto_cost"): commands.extend(self._render_dict_param(key, w, h, opr=opr)) elif key in ( "route_map", "passive_interface", "passive_interface_exclude", ): commands.extend(self._render_list_param(key, w, h, opr=opr)) elif key == "areas": commands.extend(self._render_areas(key, w, h, opr=opr)) elif key == "timers": commands.extend(self._render_timers(key, w, h, opr=opr)) elif key == "distance": commands.extend(self._render_distance(key, w, h, opr=opr)) return commands def _render_dict_param(self, attr, want, have, opr=True): """ This function generate the commands for dictionary elements. :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 = {} if have: h = have.get(attr) or {} if not opr and not h: commands.append(self._form_attr_cmd(attr=attr, opr=opr)) elif want[attr]: leaf_dict = { "auto_cost": "reference_bandwidth", "mpls_te": ("enabled", "router_address"), "parameters": ( "router_id", "abr_type", "opaque_lsa", "rfc1583_compatibility", ), } leaf = leaf_dict[attr] - for (item, value) in iteritems(want[attr]): + for item, value in iteritems(want[attr]): if opr and item in leaf and not _is_w_same(want[attr], h, item): if item == "enabled": item = "enable" if item in ( "opaque_lsa", "enable", "rfc1583_compatibility", ): commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) else: commands.append( self._form_attr_cmd(key=attr, attr=item, val=value, opr=opr) ) elif not opr and item in leaf and not _in_target(h, item): if item == "enabled": commands.append(self._form_attr_cmd(key=attr, attr="enable", opr=opr)) else: commands.append(self._form_attr_cmd(key=attr, attr=item, opr=opr)) return commands def _render_list_param(self, attr, want, have, cmd=None, opr=True): """ This function forms the commands for passed target list attributes'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated list of commands. """ commands = [] h = [] if want: w = want.get(attr) or [] if have: h = have.get(attr) or [] if not cmd: cmd = self._compute_command(opr=opr) if w: if opr: members = list_diff_want_only(w, h) for member in members: command = cmd + attr.replace("_", "-") + " " if attr == "network": command += member["address"] else: command += member commands.append(command) elif not opr: if h: for member in w: if attr == "network": if not self.search_obj_in_have(h, member, "address"): commands.append( cmd + attr.replace("_", "-") + " " + member["address"] ) elif member not in h: commands.append(cmd + attr.replace("_", "-") + " " + member) else: commands.append(cmd + " " + attr.replace("_", "-")) return commands def _render_vlink(self, attr, want, have, cmd=None, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h = [] name = {"virtual_link": "address"} leaf_dict = { "virtual_link": ( "address", "dead_interval", "transmit_delay", "hello_interval", "retransmit_interval", ) } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: h = have.get(attr) or [] if not opr and not h: commands.append(cmd + attr.replace("_", "-")) elif w: for w_item in w: - for (key, val) in iteritems(w_item): + for key, val in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) if opr and key in leaf and not _is_w_same(w_item, h_item, key): if key in "address": commands.append(cmd + attr.replace("_", "-") + " " + str(val)) else: commands.append( cmd + attr.replace("_", "-") + " " + w_item[name[attr]] + " " + key.replace("_", "-") + " " + str(val) ) elif not opr and key in leaf and not _in_target(h_item, key): if key in "address": commands.append(cmd + attr.replace("_", "-") + " " + str(val)) else: commands.append( cmd + attr.replace("_", "-") + " " + w_item[name[attr]] + " " + key ) elif key == "authentication": commands.extend( self._render_vlink_auth( attr, key, w_item, h_item, w_item["address"], cmd, opr, ) ) return commands def _render_vlink_auth(self, attr, key, want, have, address, cmd=None, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h = [] w = want.get(key) or {} if have: h = have.get(key) or {} cmd += attr.replace("_", "-") + " " + address + " " + key + " " commands.extend(self._render_list_dict_param("md5", w, h, cmd, opr)) return commands def _render_list_dict_param(self, attr, want, have, cmd=None, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired list of dictionary. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h = [] name = { "redistribute": "route_type", "neighbor": "neighbor_id", "range": "address", "md5": "key_id", "vlink": "address", } leaf_dict = { "md5": "md5_key", "redistribute": ( "metric", "route_map", "route_type", "metric_type", ), "neighbor": ("priority", "poll_interval", "neighbor_id"), "range": ("cost", "address", "substitute", "not_advertise"), "vlink": ( "address", "dead_interval", "transmit_delay", "hello_interval", "retransmit_interval", ), } leaf = leaf_dict[attr] w = want.get(attr) or [] if have: h = have.get(attr) or [] if not opr and not h: commands.append(self._compute_command(attr=attr, opr=opr)) elif w: for w_item in w: - for (key, val) in iteritems(w_item): + for key, val in iteritems(w_item): if not cmd: cmd = self._compute_command(opr=opr) h_item = self.search_obj_in_have(h, w_item, name[attr]) if opr and key in leaf and not _is_w_same(w_item, h_item, key): if key in ( "route_type", "neighbor_id", "address", "key_id", ): commands.append(cmd + attr + " " + str(val)) elif key == "cost": commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key + " " + str(val) ) elif key == "not_advertise": commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") ) elif key == "md5_key": commands.append( cmd + attr + " " + "key-id" + " " + str(w_item[name[attr]]) + " " + key.replace("_", "-") + " " + w_item[key] ) else: commands.append( cmd + attr + " " + w_item[name[attr]] + " " + key.replace("_", "-") + " " + str(val) ) elif not opr and key in leaf and not _in_target(h_item, key): if key in ( "route_type", "neighbor_id", "address", "key_id", ): commands.append(cmd + attr + " " + str(val)) else: commands.append(cmd + attr + " " + w_item[name[attr]] + " " + key) return commands def _render_nested_dict_param(self, attr, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for attributes with in desired nested dicts. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param opr: True/False. :return: generated commands list. """ commands = [] attr_dict = { "default_information": "originate", "max_metric": "router_lsa", } leaf_dict = { "default_information": ( "always", "metric", "metric_type", "route_map", ), "max_metric": ("administrative", "on_startup", "on_shutdown"), } h = {} w = want.get(attr) or {} if have: h = have.get(attr) or {} if not opr and not h: commands.append(self._form_attr_cmd(attr=attr, opr=opr)) elif w: key = attr_dict[attr] w_attrib = want[attr].get(key) or {} cmd = self._compute_command(opr=opr) h_attrib = {} if w_attrib: leaf = leaf_dict[attr] if h and key in h.keys(): h_attrib = h.get(key) or {} - for (item, val) in iteritems(w[key]): + for item, val in iteritems(w[key]): if opr and item in leaf and not _is_w_same(w[key], h_attrib, item): if item in ("administrative", "always") and val: commands.append( cmd + attr.replace("_", "-") + " " + key.replace("_", "-") + " " + item.replace("_", "-") ) elif item not in ("administrative", "always"): commands.append( cmd + attr.replace("_", "-") + " " + key.replace("_", "-") + " " + item.replace("_", "-") + " " + str(val) ) elif not opr and item in leaf and not _in_target(h_attrib, item): - commands.append(cmd + attr + " " + item) return commands def _render_areas(self, attr, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for ospf area attributes. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] h_lst = {} w_lst = want.get(attr) or [] l_set = ("area_id", "shortcut", "authentication") if have: h_lst = have.get(attr) or [] if not opr and not h_lst: commands.append(self._form_attr_cmd(attr="area", opr=opr)) elif w_lst: for w_area in w_lst: cmd = ( self._compute_command( key="area", attr=_bool_to_str(w_area["area_id"]), opr=opr, ) + " " ) h_area = self.search_obj_in_have(h_lst, w_area, "area_id") if not opr and not h_area: commands.append( self._form_attr_cmd(key="area", attr=w_area["area_id"], opr=opr) ) else: - for (key, val) in iteritems(w_area): + for key, val in iteritems(w_area): if opr and key in l_set and not _is_w_same(w_area, h_area, key): if key == "area_id": commands.append( self._form_attr_cmd( attr="area", val=_bool_to_str(val), opr=opr, ) ) else: commands.append( cmd + key + " " + _bool_to_str(val).replace("_", "-") ) elif not opr and key in l_set: if key == "area_id" and not _in_target(h_area, key): commands.append(cmd) continue if key != "area_id" and not _in_target(h_area, key): commands.append(cmd + val + " " + key) elif key == "area_type": commands.extend(self._render_area_type(w_area, h_area, key, cmd, opr)) elif key == "network": commands.extend(self._render_list_param(key, w_area, h_area, cmd, opr)) elif key == "range": commands.extend( self._render_list_dict_param(key, w_area, h_area, cmd, opr) ) elif key == "virtual_link": commands.extend(self._render_vlink(key, w_area, h_area, cmd, opr)) return commands def _render_area_type(self, want, have, attr, cmd, opr=True): """ This function forms the set/delete commands based on the 'opr' type for area_types attributes. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: command to prepend. :param opr: True/False. :return: generated commands list. """ commands = [] h_type = {} w_type = want.get(attr) or [] if have: h_type = have.get(attr) or {} if not opr and not h_type: commands.append(cmd + attr.replace("_", "-")) elif w_type: key = "normal" if opr and key in w_type.keys() and not _is_w_same(w_type, h_type, key): if not w_type[key] and h_type and h_type[key]: commands.append( cmd.replace("set", "delete") + attr.replace("_", "-") + " " + key ) elif w_type[key]: commands.append(cmd + attr.replace("_", "-") + " " + key) elif not opr and key in w_type.keys() and not (h_type and key in h_type.keys()): commands.append(cmd + want["area"] + " " + attr.replace("_", "-")) a_type = { "nssa": ("set", "default_cost", "no_summary", "translate"), "stub": ("set", "default_cost", "no_summary"), } for key in a_type: w_area = want[attr].get(key) or {} h_area = {} if w_area: if h_type and key in h_type.keys(): h_area = h_type.get(key) or {} - for (item, val) in iteritems(w_type[key]): + for item, val in iteritems(w_type[key]): if ( opr and item in a_type[key] and not _is_w_same(w_type[key], h_area, item) ): if item == "set" and val: commands.append(cmd + attr.replace("_", "-") + " " + key) elif not val and h_area and h_area[item]: commands.append( cmd.replace("set", "delete") + attr.replace("_", "-") + " " + key ) elif item != "set": commands.append( cmd + attr.replace("_", "-") + " " + key + " " + item.replace("_", "-") + " " + str(val) ) elif not opr and item in a_type[key] and not (h_type and key in h_type): if item == "set": commands.append(cmd + attr.replace("_", "-") + " " + key) else: commands.append( cmd + want["area"] + " " + attr.replace("_", "-") + " " + key + " " + item.replace("_", "-") ) return commands 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. """ return self._compute_command(key, attr=self._map_attrib(attr), val=val, opr=opr) 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 opr: True/False. :return: generated command. """ if remove or not opr: cmd = "delete protocols ospf " else: cmd = "set protocols ospf " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val: cmd += " '" + str(val) + "'" return cmd def _map_attrib(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return "disable" if attrib == "disabled" else attrib.replace("_", "-") diff --git a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py index c9479db..9fd00c1 100644 --- a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py @@ -1,134 +1,133 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import ( InterfacesArgs, ) class InterfacesFacts(object): """The vyos interfaces fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = InterfacesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): - data = connection.get_config(flags=["| grep interfaces"]) return data def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for interfaces :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_device_data(connection) objs = [] interface_names = findall( r"^set interfaces (?:ethernet|bonding|vti|loopback|vxlan|openvpn|wireguard) (?:\'*)(\S+)(?:\'*)", data, M, ) if interface_names: for interface in set(interface_names): intf_regex = r" %s .+$" % interface.strip("'") cfg = findall(intf_regex, data, M) obj = self.render_config(cfg) obj["name"] = interface.strip("'") if obj: objs.append(obj) facts = {} if objs: facts["interfaces"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["interfaces"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf)) eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf)) config = self.parse_attribs(["description", "speed", "mtu", "duplex"], eth_conf) config["vifs"] = self.parse_vifs(vif_conf) return utils.remove_empties(config) def parse_vifs(self, conf): vif_names = findall(r"vif (?:\'*)(\d+)(?:\'*)", conf, M) vifs_list = None if vif_names: vifs_list = [] for vif in set(vif_names): vif_regex = r" %s .+$" % vif cfg = "\n".join(findall(vif_regex, conf, M)) obj = self.parse_attribs(["description", "mtu"], cfg) obj["vlan_id"] = int(vif) if obj: vifs_list.append(obj) vifs_list = sorted(vifs_list, key=lambda i: i["vlan_id"]) return vifs_list def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value and item == "mtu": config[item] = int(value.strip("'")) elif value: config[item] = value.strip("'") else: config[item] = None if "disable" in conf: config["enabled"] = False else: config["enabled"] = True return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/facts/legacy/base.py b/plugins/module_utils/network/vyos/facts/legacy/base.py index 719df3f..59666e1 100644 --- a/plugins/module_utils/network/vyos/facts/legacy/base.py +++ b/plugins/module_utils/network/vyos/facts/legacy/base.py @@ -1,163 +1,159 @@ # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The VyOS interfaces fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type import platform import re from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_capabilities, run_commands, ) class LegacyFactsBase(object): - COMMANDS = frozenset() def __init__(self, module): self.module = module self.facts = dict() self.warnings = list() self.responses = None def populate(self): self.responses = run_commands(self.module, list(self.COMMANDS)) class Default(LegacyFactsBase): - COMMANDS = [ "show version", ] def populate(self): super(Default, self).populate() data = self.responses[0] self.facts["serialnum"] = self.parse_serialnum(data) self.facts.update(self.platform_facts()) def parse_serialnum(self, data): match = re.search(r"(?:HW|Hardware) S/N:\s+(\S+)", data) if match: return match.group(1) def platform_facts(self): platform_facts = {} resp = get_capabilities(self.module) device_info = resp["device_info"] platform_facts["system"] = device_info["network_os"] for item in ("model", "image", "version", "platform", "hostname"): val = device_info.get("network_os_%s" % item) if val: platform_facts[item] = val platform_facts["api"] = resp["network_api"] platform_facts["python_version"] = platform.python_version() return platform_facts class Config(LegacyFactsBase): - COMMANDS = [ "show configuration commands", "show system commit", ] def populate(self): super(Config, self).populate() self.facts["config"] = self.responses commits = self.responses[1] entries = list() entry = None for line in commits.split("\n"): match = re.match(r"(\d+)\s+(.+)by(.+)via(.+)", line) if match: if entry: entries.append(entry) entry = dict( revision=match.group(1), datetime=match.group(2), by=str(match.group(3)).strip(), via=str(match.group(4)).strip(), comment=None, ) else: entry["comment"] = line.strip() self.facts["commits"] = entries class Neighbors(LegacyFactsBase): - COMMANDS = [ "show lldp neighbors", "show lldp neighbors detail", ] def populate(self): super(Neighbors, self).populate() all_neighbors = self.responses[0] if "LLDP not configured" not in all_neighbors: neighbors = self.parse(self.responses[1]) self.facts["neighbors"] = self.parse_neighbors(neighbors) def parse(self, data): parsed = list() values = None for line in data.split("\n"): if not line: continue if line[0] == " ": values += "\n%s" % line elif line.startswith("Interface"): if values: parsed.append(values) values = line if values: parsed.append(values) return parsed def parse_neighbors(self, data): facts = dict() for item in data: interface = self.parse_interface(item) host = self.parse_host(item) port = self.parse_port(item) if interface not in facts: facts[interface] = list() facts[interface].append(dict(host=host, port=port)) return facts def parse_interface(self, data): match = re.search(r"^Interface:\s+(\S+),", data) return match.group(1) def parse_host(self, data): match = re.search(r"SysName:\s+(.+)$", data, re.M) if match: return match.group(1) def parse_port(self, data): match = re.search(r"PortDescr:\s+(.+)$", data, re.M) if match: return match.group(1) diff --git a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py index 4dcd546..a5c4636 100644 --- a/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py +++ b/plugins/module_utils/network/vyos/facts/ospfv2/ospfv2.py @@ -1,482 +1,481 @@ # # -*- 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 ospfv2 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 M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ospfv2.ospfv2 import ( Ospfv2Args, ) class Ospfv2Facts(object): """The vyos ospfv2 fact class""" def __init__( self, module, subspec="config", options="options", ): - self._module = module self.argument_spec = Ospfv2Args.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 ospfv2 :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: data = self.get_device_data(connection) # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead objs = {} ospfv2 = findall(r"^set protocols ospf (.+)", data, M) if ospfv2: objs = self.render_config(ospfv2) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["ospfv2"] = 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 :param conf: The configuration :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) a_lst = ["default_metric", "log_adjacency_changes"] config = self.parse_attr(conf, a_lst) if not config: config = {} config["timers"] = self.parse_timers(conf) config["auto_cost"] = self.parse_auto_cost(conf) config["distance"] = self.parse_distance(conf) config["max_metric"] = self.parse_max_metric(conf) config["default_information"] = self.parse_def_info(conf) config["route_map"] = self.parse_leaf_list(conf, "route-map") config["mpls_te"] = self.parse_attrib(conf, "mpls_te", "mpls-te") config["areas"] = self.parse_attrib_list(conf, "area", "area_id") config["parameters"] = self.parse_attrib(conf, "parameters", "parameters") config["neighbor"] = self.parse_attrib_list(conf, "neighbor", "neighbor_id") config["passive_interface"] = self.parse_leaf_list(conf, "passive-interface") config["redistribute"] = self.parse_attrib_list(conf, "redistribute", "route_type") config["passive_interface_exclude"] = self.parse_leaf_list( conf, "passive-interface-exclude" ) return config def parse_timers(self, conf): """ This function triggers the parsing of 'timers' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} cfg_dict["refresh"] = self.parse_refresh(conf, "refresh") cfg_dict["throttle"] = self.parse_throttle(conf, "spf") return cfg_dict def parse_throttle(self, conf, attrib=None): """ This function triggers the parsing of 'throttle' attributes :param conf: configuration :param attrib: 'spf' :return: generated config dictionary """ cfg_dict = {} cfg_dict[attrib] = self.parse_attrib(conf, attrib, match=attrib) return cfg_dict def parse_refresh(self, conf, attrib=None): """ This function triggers the parsing of 'refresh' attributes :param conf: configuration :param attrib: 'refresh' :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["timers"], match=attrib) return cfg_dict def parse_leaf_list(self, conf, attrib): """ This function forms the regex to fetch the listed attributes from the configuration data :param conf: configuration data :param attrib: attribute name :return: generated rule list configuration """ lst = [] items = findall(r"^" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: for i in set(items): lst.append(i.strip("'")) lst.sort() return lst def parse_distance(self, conf, attrib=None): """ This function triggers the parsing of 'distance' attributes :param conf: configuration :param attrib: attribute name :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["global"], match=attrib) cfg_dict["ospf"] = self.parse_ospf(conf, "ospf") return cfg_dict def parse_ospf(self, conf, attrib=None): """ This function triggers the parsing of 'distance ospf' attributes :param conf: configuration :param attrib: 'ospf' :return: generated config dictionary """ cfg_dict = self.parse_attrib(conf, "ospf", match=attrib) return cfg_dict def parse_max_metric(self, conf): """ This function triggers the parsing of 'max_metric' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} cfg_dict["router_lsa"] = self.parse_attrib(conf, "router_lsa", match="router-lsa") return cfg_dict def parse_auto_cost(self, conf, attrib=None): """ This function triggers the parsing of 'auto_cost' attributes :param conf: configuration :param attrib: attribute name :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["reference_bandwidth"], match=attrib) return cfg_dict def parse_def_info(self, conf): """ This function triggers the parsing of 'default_information' attributes :param conf: configuration :return: generated config dictionary """ cfg_dict = {} cfg_dict["originate"] = self.parse_attrib(conf, "originate", "originate") return cfg_dict def parse_area(self, conf, area_id): """ This function triggers the parsing of 'area' attributes. :param conf: configuration data :param area_id: area identity :return: generated rule configuration dictionary. """ rule = self.parse_attrib(conf, "area_id", match=area_id) r_sub = { "area_type": self.parse_area_type(conf, "area-type"), "network": self.parse_network(conf), "range": self.parse_attrib_list(conf, "range", "address"), "virtual_link": self.parse_attrib_list(conf, "virtual-link", "address"), } rule.update(r_sub) return rule def parse_key(self, conf, key_id): """ This function triggers the parsing of 'area' attributes. :param conf: configuration data :param area_id: area identity :return: generated rule configuration dictionary. """ rule = self.parse_attrib(conf, "key_id", match=key_id) return rule def parse_area_type(self, conf, attrib=None): """ This function triggers the parsing of 'area_type' attributes :param conf: configuration :param attrib: 'area-type' :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["normal"], match=attrib) cfg_dict["nssa"] = self.parse_attrib(conf, "nssa", match="nssa") cfg_dict["stub"] = self.parse_attrib(conf, "stub", match="stub") return cfg_dict def parse_network(self, conf): """ This function forms the regex to fetch the 'network' :param conf: configuration data :return: generated rule list configuration """ a_lst = [] applications = findall(r"network (.+)", conf, M) if applications: app_lst = [] for r in set(applications): obj = {"address": r.strip("'")} app_lst.append(obj) a_lst = sorted(app_lst, key=lambda i: i["address"]) return a_lst def parse_vlink(self, conf): """ This function triggers the parsing of 'virtual_link' attributes :param conf: configuration data :return: generated rule configuration dictionary """ rule = self.parse_attrib(conf, "vlink") r_sub = {"authentication": self.parse_authentication(conf, "authentication")} rule.update(r_sub) return rule def parse_authentication(self, conf, attrib=None): """ This function triggers the parsing of 'authentication' attributes. :param conf: configuration :param attrib: 'authentication' :return: generated config dictionary """ cfg_dict = self.parse_attr(conf, ["plaintext_password"], match=attrib) cfg_dict["md5"] = self.parse_attrib_list(conf, "key-id", "key_id") return cfg_dict def parse_attrib_list(self, conf, attrib, param): """ This function forms the regex to fetch the listed attributes from config :param conf: configuration data :param attrib: attribute name :param param: parameter data :return: generated rule list configuration """ r_lst = [] if attrib == "area": items = findall( r"^" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)", conf, M, ) elif attrib == "key-id": items = findall( r"^.*" + attrib.replace("_", "-") + " (?:'*)(\\S+)(?:'*)", conf, M, ) else: items = findall(r"" + attrib + " (?:'*)(\\S+)(?:'*)", conf, M) if items: a_lst = [] for item in set(items): i_regex = r" %s .+$" % item cfg = "\n".join(findall(i_regex, conf, M)) if attrib == "area": obj = self.parse_area(cfg, item) elif attrib == "virtual-link": obj = self.parse_vlink(cfg) elif attrib == "key-id": obj = self.parse_key(cfg, item) else: obj = self.parse_attrib(cfg, attrib) obj[param] = item.strip("'") if obj: a_lst.append(obj) r_lst = sorted(a_lst, key=lambda i: i[param]) return r_lst def parse_attrib(self, conf, param, match=None): """ This function triggers the parsing of 'ospf' attributes :param conf: configuration data :return: generated configuration dictionary """ param_lst = { "key_id": ["md5_key"], "mpls_te": ["enabled", "router_address"], "area_id": ["shortcut", "authentication"], "neighbor": ["priority", "poll_interval"], "stub": ["set", "default_cost", "no_summary"], "range": ["cost", "substitute", "not_advertise"], "ospf": ["external", "inter_area", "intra_area"], "spf": ["delay", "max_holdtime", "initial_holdtime"], "redistribute": ["metric", "metric_type", "route_map"], "nssa": ["set", "translate", "default_cost", "no_summary"], "config_routes": ["default_metric", "log_adjacency_changes"], "originate": ["always", "metric", "metric_type", "route_map"], "router_lsa": ["administrative", "on_shutdown", "on_startup"], "parameters": [ "abr_type", "opaque_lsa", "router_id", "rfc1583_compatibility", ], "vlink": [ "dead_interval", "hello_interval", "transmit_delay", "retransmit_interval", ], } cfg_dict = self.parse_attr(conf, param_lst[param], match) return cfg_dict def parse_attr(self, conf, attr_list, match=None): """ This function peforms the following: - Form the regex to fetch the required attribute config. - Type cast the output in desired format. :param conf: configuration. :param attr_list: list of attributes. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} for attrib in attr_list: regex = self.map_regex(attrib) if match: regex = match.replace("_", "-") + " " + regex if conf: if self.is_bool(attrib): out = conf.find(attrib.replace("_", "-")) dis = conf.find(attrib.replace("_", "-") + " 'disable'") if match: if attrib == "set" and conf.find(match) >= 1: config[attrib] = True en = conf.find(match + " 'enable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True elif match and en >= 1: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) config[attrib] = val return config def map_regex(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ return ( "disable" if attrib == "disabled" else ( "enable" if attrib == "enabled" else ("area" if attrib == "area_id" else attrib.replace("_", "-")) ) ) def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ( "set", "always", "normal", "enabled", "opaque_lsa", "not_advertise", "administrative", "rfc1583_compatibility", ) return True if attrib in bool_set else False def is_num(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ num_set = ( "ospf", "delay", "metric", "inter_area", "intra_area", "on_startup", "metric_type", "on_shutdown", "max_holdtime", "poll_interval", "default_metric", "initial_holdtime", "key_id", ) return True if attrib in num_set else False diff --git a/plugins/modules/vyos_vlan.py b/plugins/modules/vyos_vlan.py index de20d87..f3e3dc8 100644 --- a/plugins/modules/vyos_vlan.py +++ b/plugins/modules/vyos_vlan.py @@ -1,394 +1,393 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright: (c) 2017, Ansible by Red Hat, inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = """ module: vyos_vlan author: Trishna Guha (@trishnaguha) short_description: Manage VLANs on VyOS network devices description: - This module provides declarative management of VLANs on VyOS network devices. version_added: 1.0.0 notes: - Tested against VyOS 1.1.8 (helium). - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: name: description: - Name of the VLAN. type: str address: description: - Configure Virtual interface address. type: str vlan_id: description: - ID of the VLAN. Range 0-4094. type: int interfaces: description: - List of interfaces that should be associated to the VLAN. type: list elements: str associated_interfaces: description: - This is a intent option and checks the operational state of the for given vlan C(name) for associated interfaces. If the value in the C(associated_interfaces) does not match with the operational state of vlan on device it will result in failure. type: list elements: str delay: description: - Delay the play should wait to check for declarative intent params values. default: 10 type: int aggregate: description: List of VLANs definitions. type: list elements: dict suboptions: name: description: - Name of the VLAN. type: str address: description: - Configure Virtual interface address. type: str vlan_id: description: - ID of the VLAN. Range 0-4094. type: int required: true interfaces: description: - List of interfaces that should be associated to the VLAN. type: list elements: str required: true associated_interfaces: description: - This is a intent option and checks the operational state of the for given vlan C(name) for associated interfaces. If the value in the C(associated_interfaces) does not match with the operational state of vlan on device it will result in failure. type: list elements: str delay: description: - Delay the play should wait to check for declarative intent params values. type: int state: description: - State of the VLAN configuration. type: str choices: - present - absent purge: description: - Purge VLANs not defined in the I(aggregate) parameter. default: false type: bool state: description: - State of the VLAN configuration. default: present type: str choices: - present - absent extends_documentation_fragment: - vyos.vyos.vyos """ EXAMPLES = """ - name: Create vlan vyos.vyos.vyos_vlan: vlan_id: 100 name: vlan-100 interfaces: eth1 state: present - name: Add interfaces to VLAN vyos.vyos.vyos_vlan: vlan_id: 100 interfaces: - eth1 - eth2 - name: Configure virtual interface address vyos.vyos.vyos_vlan: vlan_id: 100 interfaces: eth1 address: 172.26.100.37/24 - name: vlan interface config + intent vyos.vyos.vyos_vlan: vlan_id: 100 interfaces: eth0 associated_interfaces: - eth0 - name: vlan intent check vyos.vyos.vyos_vlan: vlan_id: 100 associated_interfaces: - eth3 - eth4 - name: Delete vlan vyos.vyos.vyos_vlan: vlan_id: 100 interfaces: eth1 state: absent """ RETURN = """ commands: description: The list of configuration mode commands to send to the device returned: always type: list sample: - set interfaces ethernet eth1 vif 100 description VLAN 100 - set interfaces ethernet eth1 vif 100 address 172.26.100.37/24 - delete interfaces ethernet eth1 vif 100 """ import re import time from copy import deepcopy from ansible.module_utils._text import to_text from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.validation import check_required_one_of from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( remove_default_spec, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( load_config, run_commands, ) def search_obj_in_list(vlan_id, lst): obj = list() for o in lst: if o["vlan_id"] == vlan_id: obj.append(o) return obj def map_obj_to_commands(updates, module): commands = list() want, have = updates purge = module.params["purge"] for w in want: vlan_id = w["vlan_id"] name = w["name"] address = w["address"] state = w["state"] obj_in_have = search_obj_in_list(vlan_id, have) if state == "absent": if obj_in_have: for obj in obj_in_have: for i in obj["interfaces"]: commands.append("delete interfaces ethernet {0} vif {1}".format(i, vlan_id)) elif state == "present": if not obj_in_have: if w["interfaces"] and w["vlan_id"]: for i in w["interfaces"]: cmd = "set interfaces ethernet {0} vif {1}".format(i, vlan_id) if w["name"]: commands.append(cmd + " description {0}".format(name)) elif w["address"]: commands.append(cmd + " address {0}".format(address)) else: commands.append(cmd) if purge: for h in have: obj_in_want = search_obj_in_list(h["vlan_id"], want) if not obj_in_want: for i in h["interfaces"]: commands.append( "delete interfaces ethernet {0} vif {1}".format(i, h["vlan_id"]) ) return commands def map_params_to_obj(module): obj = [] aggregate = module.params.get("aggregate") if aggregate: for item in aggregate: for key in item: if item.get(key) is None: item[key] = module.params[key] d = item.copy() if not d["vlan_id"]: module.fail_json(msg="vlan_id is required") d["vlan_id"] = str(d["vlan_id"]) try: check_required_one_of(module.required_one_of, item) except TypeError as exc: module.fail_json(to_text(exc)) obj.append(d) else: obj.append( { "vlan_id": str(module.params["vlan_id"]), "name": module.params["name"], "address": module.params["address"], "state": module.params["state"], "interfaces": module.params["interfaces"], "associated_interfaces": module.params["associated_interfaces"], } ) return obj def map_config_to_obj(module): objs = [] output = run_commands(module, "show interfaces") lines = output[0].strip().splitlines()[3:] for line in lines: splitted_line = re.split(r"\s{2,}", line.strip()) obj = {} eth = splitted_line[0].strip("'") if eth.startswith("eth"): obj["interfaces"] = [] if "." in eth: interface = eth.split(".")[0] obj["interfaces"].append(interface) obj["vlan_id"] = eth.split(".")[-1] else: obj["interfaces"].append(eth) obj["vlan_id"] = None if splitted_line[1].strip("'") != "-": obj["address"] = splitted_line[1].strip("'") if len(splitted_line) > 3: obj["name"] = splitted_line[3].strip("'") obj["state"] = "present" objs.append(obj) return objs def check_declarative_intent_params(want, module, result): - have = None obj_interface = list() is_delay = False for w in want: if w.get("associated_interfaces") is None: continue if result["changed"] and not is_delay: time.sleep(module.params["delay"]) is_delay = True if have is None: have = map_config_to_obj(module) obj_in_have = search_obj_in_list(w["vlan_id"], have) if obj_in_have: for obj in obj_in_have: obj_interface.extend(obj["interfaces"]) for w in want: if w.get("associated_interfaces") is None: continue for i in w["associated_interfaces"]: if (set(obj_interface) - set(w["associated_interfaces"])) != set([]): module.fail_json( msg="Interface {0} not configured on vlan {1}".format(i, w["vlan_id"]) ) def main(): """main entry point for module execution""" element_spec = dict( vlan_id=dict(type="int"), name=dict(), address=dict(), interfaces=dict(type="list", elements="str"), associated_interfaces=dict(type="list", elements="str"), delay=dict(default=10, type="int"), state=dict(default="present", choices=["present", "absent"]), ) aggregate_spec = deepcopy(element_spec) aggregate_spec["vlan_id"].update(required=True) aggregate_spec["interfaces"].update(required=True) # remove default in aggregate spec, to handle common arguments remove_default_spec(aggregate_spec) argument_spec = dict( aggregate=dict(type="list", elements="dict", options=aggregate_spec), purge=dict(default=False, type="bool"), ) argument_spec.update(element_spec) required_one_of = [ ["vlan_id", "aggregate"], ["aggregate", "interfaces", "associated_interfaces"], ] mutually_exclusive = [["vlan_id", "aggregate"]] module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=required_one_of, mutually_exclusive=mutually_exclusive, ) warnings = list() result = {"changed": False} if warnings: result["warnings"] = warnings want = map_params_to_obj(module) have = map_config_to_obj(module) commands = map_obj_to_commands((want, have), module) result["commands"] = commands if commands: commit = not module.check_mode load_config(module, commands, commit=commit) result["changed"] = True check_declarative_intent_params(want, module, result) module.exit_json(**result) if __name__ == "__main__": main() diff --git a/plugins/terminal/vyos.py b/plugins/terminal/vyos.py index b9e84d7..d5d57e7 100644 --- a/plugins/terminal/vyos.py +++ b/plugins/terminal/vyos.py @@ -1,74 +1,73 @@ # # (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 . # from __future__ import absolute_import, division, print_function __metaclass__ = type import os import re from ansible.errors import AnsibleConnectionFailure from ansible_collections.ansible.netcommon.plugins.plugin_utils.terminal_base import TerminalBase class TerminalModule(TerminalBase): - terminal_stdout_re = [ re.compile(rb"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), re.compile(rb"\@[\w\-\.]+:\S+?[>#\$] ?$"), ] terminal_stderr_re = [ re.compile(rb"\n\s*Invalid command:"), re.compile(rb"\nCommit failed"), re.compile(rb"\n\s+Set failed"), re.compile(rb"\n\s+Delete failed"), ] ansi_re = TerminalBase.ansi_re + [ # Color codes re.compile(rb"\x1b\[(\d+(;\d+)*)?m"), # Clear line (CSI K) re.compile(rb"\x1b\[K"), # Xterm change cursor mode (CSI ? 1 [h|l]) re.compile(rb"\x1b\[\?1(h|l)"), # Xterm change keypad (ESC [=|>]) re.compile(rb"\x1b(=|>)"), # Xterm window title string (OSC BEL) re.compile(rb"\x1b]0;[^\x07]*\x07"), ] terminal_config_prompt = re.compile(r"^.+#$") try: terminal_length = os.getenv("ANSIBLE_VYOS_TERMINAL_LENGTH", 10000) terminal_length = int(terminal_length) except ValueError: raise AnsibleConnectionFailure( "Invalid value set for vyos terminal length '%s', value should be a valid integer string" % terminal_length ) def on_open_shell(self): try: for cmd in (b"set terminal length 0", b"set terminal width 512"): self._exec_cli_command(cmd) self._exec_cli_command(b"set terminal length %d" % self.terminal_length) except AnsibleConnectionFailure: raise AnsibleConnectionFailure("unable to set terminal parameters") diff --git a/tests/unit/compat/mock.py b/tests/unit/compat/mock.py index 4bef4c2..2c62c9e 100644 --- a/tests/unit/compat/mock.py +++ b/tests/unit/compat/mock.py @@ -1,126 +1,125 @@ # (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. # Make coding more python3-ish from __future__ import absolute_import, division, print_function __metaclass__ = type """ Compat module for Python3.x's unittest.mock module """ import sys import _io # Python 2.7 # Note: Could use the pypi mock library on python3.x as well as python2.x. It # is the same as the python3 stdlib mock library try: # Allow wildcard import because we really do want to import all of mock's # symbols into this compat shim # pylint: disable=wildcard-import,unused-wildcard-import from unittest.mock import * except ImportError: # Python 2 # pylint: disable=wildcard-import,unused-wildcard-import try: from mock import * except ImportError: print("You need the mock library installed on python2.x to run tests") # Prior to 3.4.4, mock_open cannot handle binary read_data if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): file_spec = None def _iterate_read_data(read_data): # Helper for mock_open: # Retrieve lines from read_data via a generator so that separate calls to # readline, read, and readlines are properly interleaved sep = b"\n" if isinstance(read_data, bytes) else "\n" data_as_list = [l + sep for l in read_data.split(sep)] if data_as_list[-1] == sep: # If the last line ended in a newline, the list comprehension will have an # extra entry that's just a newline. Remove this. data_as_list = data_as_list[:-1] else: # If there wasn't an extra newline by itself, then the file being # emulated doesn't have a newline to end the last line remove the # newline that our naive format() added data_as_list[-1] = data_as_list[-1][:-1] for line in data_as_list: yield line def mock_open(mock=None, read_data=""): """ A helper function to create a mock to replace the use of `open`. It works for `open` called directly or used as a context manager. The `mock` argument is the mock object to configure. If `None` (the default) then a `MagicMock` will be created for you, with the API limited to methods or attributes available on standard file handles. `read_data` is a string for the `read` methoddline`, and `readlines` of the file handle to return. This is an empty string by default. """ def _readlines_side_effect(*args, **kwargs): if handle.readlines.return_value is not None: return handle.readlines.return_value return list(_data) def _read_side_effect(*args, **kwargs): if handle.read.return_value is not None: return handle.read.return_value return type(read_data)().join(_data) def _readline_side_effect(): if handle.readline.return_value is not None: while True: yield handle.readline.return_value for line in _data: yield line global file_spec if file_spec is None: - file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) if mock is None: mock = MagicMock(name="open", spec=open) handle = MagicMock(spec=file_spec) handle.__enter__.return_value = handle _data = _iterate_read_data(read_data) handle.write.return_value = None handle.read.return_value = None handle.readline.return_value = None handle.readlines.return_value = None handle.read.side_effect = _read_side_effect handle.readline.side_effect = _readline_side_effect() handle.readlines.side_effect = _readlines_side_effect mock.return_value = handle return mock diff --git a/tests/unit/modules/network/vyos/test_vyos_banner.py b/tests/unit/modules/network/vyos/test_vyos_banner.py index 08d4424..1d6738c 100644 --- a/tests/unit/modules/network/vyos/test_vyos_banner.py +++ b/tests/unit/modules/network/vyos/test_vyos_banner.py @@ -1,60 +1,59 @@ # 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 ansible_collections.vyos.vyos.plugins.modules import vyos_banner from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule class TestVyosBannerModule(TestVyosModule): - module = vyos_banner def setUp(self): super(TestVyosBannerModule, self).setUp() self.mock_get_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_banner.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_banner.load_config" ) self.load_config = self.mock_load_config.start() def tearDown(self): super(TestVyosBannerModule, self).tearDown() self.mock_get_config.stop() self.mock_load_config.stop() def load_fixtures(self, commands=None, filename=None): self.load_config.return_value = dict(diff=None, session="session") def test_vyos_banner_create(self): set_module_args(dict(banner="pre-login", text="test\nbanner\nstring")) commands = ["set system login banner pre-login 'test\\nbanner\\nstring'"] self.execute_module(changed=True, commands=commands) def test_vyos_banner_remove(self): set_module_args(dict(banner="pre-login", state="absent")) self.execute_module(changed=False, commands=[]) diff --git a/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py index a9f0ef1..0bbf74a 100644 --- a/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py +++ b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py @@ -1,601 +1,600 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_bgp_address_family from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosBgpafModule(TestVyosModule): - module = vyos_bgp_address_family def setUp(self): super(TestVyosBgpafModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_execute_show_command = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts." + "bgp_address_family.bgp_address_family.Bgp_address_familyFacts.get_device_data" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosBgpafModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_execute_show_command.stop() def load_fixtures(self, commands=None, filename=None): if filename is None: filename = "vyos_bgp_address_family_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def test_vyos_bgp_address_family_merged_idempotent(self): set_module_args( dict( config=dict( as_number=65536, address_family=[ dict( afi="ipv4", aggregate_address=[dict(prefix="192.0.2.0/24", as_set=True)], networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), dict(prefix="192.2.13.0/24", backdoor=True), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ripng", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.0.2.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="export", route_map="map01")], soft_reconfiguration=True, ), ], ), dict( neighbor_address="203.0.113.5", address_family=[ dict( afi="ipv6", attribute_unchanged=dict(next_hop=True), ) ], ), ], ) ) ) self.execute_module(changed=False, commands=[]) def test_vyos_bgp_address_family_merged(self): set_module_args( dict( config=dict( as_number=65536, address_family=[ dict( afi="ipv4", aggregate_address=[dict(prefix="192.0.2.0/24", summary_only=True)], networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ospfv3", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.10.21.25", address_family=[ dict( afi="ipv6", distribute_list=[dict(action="export", acl=10)], route_server_client=True, ), ], ), dict( neighbor_address="203.0.113.5", address_family=[ dict( afi="ipv4", filter_list=[ dict(action="export", path_list="list01"), ], capability=dict(orf="send"), ) ], ), ], ) ) ) commands = [ "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-setipv4-unicast aggregate-address 192.0.2.0/24 summary-only", "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20", "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast filter-list export list01", "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast capability prefix-list send", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client", ] self.execute_module(changed=True, commands=commands) def test_vyos_bgp_address_family_replaced_idempotent(self): set_module_args( dict( state="replaced", config=dict( as_number=65536, address_family=[ dict( afi="ipv4", aggregate_address=[dict(prefix="192.0.2.0/24", as_set=True)], networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), dict(prefix="192.2.13.0/24", backdoor=True), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ripng", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.0.2.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="export", route_map="map01")], soft_reconfiguration=True, ), ], ), dict( neighbor_address="203.0.113.5", address_family=[ dict( afi="ipv6", attribute_unchanged=dict(next_hop=True), ) ], ), ], ), ) ) self.execute_module(changed=False, commands=[]) def test_vyos_bgp_address_family_replaced(self): set_module_args( dict( state="replaced", config=dict( as_number=65536, address_family=[ dict( afi="ipv4", aggregate_address=[dict(prefix="192.0.2.0/24", summary_only=True)], networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ospfv3", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.10.21.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="import", route_map="map01")], ), dict( afi="ipv6", distribute_list=[dict(action="export", acl=10)], route_server_client=True, ), ], ), dict( neighbor_address="192.0.2.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="export", route_map="map01")], ), ], ), dict( neighbor_address="203.0.113.5", address_family=[ dict( afi="ipv4", filter_list=[ dict(action="export", path_list="list01"), ], capability=dict(orf="send"), ) ], ), ], ), ) ) commands = [ "delete protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged", "delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration", "delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng", "delete protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24", "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 summary-only", "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv4-unicast route-map import map01", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client", "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast filter-list export list01", "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast capability prefix-list send", ] self.execute_module(changed=True, commands=commands) def test_vyos_bgp_address_family_overridden_idempotent(self): set_module_args( dict( state="overridden", config=dict( as_number=65536, address_family=[ dict( afi="ipv4", aggregate_address=[dict(prefix="192.0.2.0/24", as_set=True)], networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), dict(prefix="192.2.13.0/24", backdoor=True), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ripng", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.0.2.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="export", route_map="map01")], soft_reconfiguration=True, ), ], ), dict( neighbor_address="203.0.113.5", address_family=[ dict( afi="ipv6", attribute_unchanged=dict(next_hop=True), ) ], ), ], ), ) ) self.execute_module(changed=False, commands=[]) def test_vyos_bgp_address_family_overridden(self): set_module_args( dict( state="overridden", config=dict( as_number=65536, address_family=[ dict( afi="ipv4", networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ospfv3", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.10.21.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="import", route_map="map01")], ), dict( afi="ipv6", distribute_list=[dict(action="export", acl=10)], route_server_client=True, ), ], ), ], ), ) ) commands = [ "delete protocols bgp 65536 neighbor 203.0.113.5 address-family", "delete protocols bgp 65536 neighbor 192.0.2.25 address-family", "delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng", "delete protocols bgp 65536 address-family ipv4 aggregate-address", "delete protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24", "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv4-unicast route-map import map01", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10", "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client", ] self.execute_module(changed=True, commands=commands) def test_vyos_bgp_address_family_deleted(self): set_module_args( dict( state="deleted", config=dict( as_number=65536, address_family=[ dict( afi="ipv4", ), ], neighbors=[ dict( neighbor_address="192.0.2.25", address_family=[ dict( afi="ipv4", ), ], ), dict( neighbor_address="203.0.113.5", ), ], ), ) ) commands = [ "delete protocols bgp 65536 address-family ipv4-unicast", "delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast", "delete protocols bgp 65536 neighbor 203.0.113.5 address-family", ] self.execute_module(changed=True, commands=commands) def test_vyos_bgp_address_family_incorrect_instance(self): set_module_args( dict( state="overridden", config=dict( as_number=100, address_family=[ dict( afi="ipv4", networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ospfv3", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.10.21.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="import", route_map="map01")], ), dict( afi="ipv6", distribute_list=[dict(action="export", acl=10)], route_server_client=True, ), ], ), ], ), ) ) result = self.execute_module(failed=True) self.assertIn("Only one bgp instance is allowed per device", result["msg"]) def test_vyos_bgp_address_family_rendered(self): set_module_args( dict( state="rendered", config=dict( as_number=65536, address_family=[ dict( afi="ipv4", aggregate_address=[dict(prefix="192.0.2.0/24", as_set=True)], networks=[ dict(prefix="192.1.13.0/24", route_map="map01"), dict(prefix="192.2.13.0/24", backdoor=True), ], ), dict( afi="ipv6", redistribute=[dict(protocol="ripng", metric=20)], ), ], neighbors=[ dict( neighbor_address="192.0.2.25", address_family=[ dict( afi="ipv4", route_map=[dict(action="export", route_map="map01")], soft_reconfiguration=True, ), ], ), dict( neighbor_address="203.0.113.5", address_family=[ dict( afi="ipv6", attribute_unchanged=dict(next_hop=True), ) ], ), ], ), ) ) rendered_cmds = [ "set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map map01", "set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor", "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set", "set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric 20", "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export map01", "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound", "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop", ] result = self.execute_module(changed=False) self.assertEqual( sorted(result["rendered"]), sorted(rendered_cmds), result["rendered"], ) def test_vyos_bgp_address_family_parsed(self): commands = [ "set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map map01", "set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor", "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set", "set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric 20", "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export map01", "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound", "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop", ] parsed_str = "\n".join(commands) set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = { "as_number": 65536, "address_family": [ { "afi": "ipv4", "networks": [ {"prefix": "192.1.13.0/24", "route_map": "map01"}, {"prefix": "192.2.13.0/24", "backdoor": True}, ], "aggregate_address": [{"prefix": "192.0.2.0/24", "as_set": True}], }, { "afi": "ipv6", "redistribute": [{"protocol": "ripng", "metric": 20}], }, ], "neighbors": [ { "neighbor_address": "192.0.2.25", "address_family": [ {"afi": "ipv4", "soft_reconfiguration": True}, ], }, { "neighbor_address": "203.0.113.5", "address_family": [ { "afi": "ipv6", "attribute_unchanged": {"next_hop": True}, } ], }, ], } self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) def test_vyos_bgp_address_family_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False) gather_list = { "as_number": 65536, "address_family": [ { "afi": "ipv4", "networks": [ {"prefix": "192.1.13.0/24", "route_map": "map01"}, {"prefix": "192.2.13.0/24", "backdoor": True}, ], "aggregate_address": [{"prefix": "192.0.2.0/24", "as_set": True}], }, { "afi": "ipv6", "redistribute": [{"protocol": "ripng", "metric": 20}], }, ], "neighbors": [ { "neighbor_address": "192.0.2.25", "address_family": [ {"afi": "ipv4", "soft_reconfiguration": True}, ], }, { "neighbor_address": "203.0.113.5", "address_family": [ { "afi": "ipv6", "attribute_unchanged": {"next_hop": True}, } ], }, ], } self.assertEqual(sorted(gather_list), sorted(result["gathered"])) diff --git a/tests/unit/modules/network/vyos/test_vyos_bgp_global.py b/tests/unit/modules/network/vyos/test_vyos_bgp_global.py index 23f2e6a..d0167de 100644 --- a/tests/unit/modules/network/vyos/test_vyos_bgp_global.py +++ b/tests/unit/modules/network/vyos/test_vyos_bgp_global.py @@ -1,551 +1,549 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_bgp_global from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosBgpglobalModule(TestVyosModule): - module = vyos_bgp_global def setUp(self): super(TestVyosBgpglobalModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_execute_show_command_config = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global.Bgp_global._get_config" ) self.execute_show_command_config = self.mock_execute_show_command_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global.Bgp_globalFacts.get_device_data" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosBgpglobalModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_execute_show_command.stop() self.mock_execute_show_command_config.stop() def load_fixtures(self, commands=None, filename=None): if filename is None: filename = "vyos_bgp_global_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file self.execute_show_command_config.side_effect = load_from_file def test_vyos_bgp_global_merged_idempotent(self): set_module_args( dict( config=dict( as_number="65536", neighbor=[ dict( address="10.0.0.4", disable_connected_check=True, timers=dict(holdtime=30, keepalive=10), capability=dict(orf="receive"), ), dict( address="192.168.0.2", attribute_unchanged=dict(as_path=True, med=True), ebgp_multihop=2, remote_as="65535", soft_reconfiguration=True, update_source="192.168.0.1", ), dict( address="2001:db8::2", ebgp_multihop=2, remote_as="65535", maximum_prefix=34, update_source="2001:db8::1", ), ], network=[ dict(address="172.16.42.32/27", backdoor=True), dict(address="172.16.42.251/32", route_map="map01"), ], bgp_params=dict( bestpath=dict(as_path="confed", compare_routerid=True), default=dict(no_ipv4_unicast=True), router_id="10.1.1.1", ), redistribute=[ dict(protocol="kernel", route_map="map01"), dict(protocol="static", metric=20), dict(protocol="static", route_map="map01"), ], ), state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_bgp_global_merged(self): set_module_args( dict( config=dict( as_number="65536", maximum_paths=[ dict(path="ebgp", count=20), dict(path="ibgp", count=45), ], neighbor=[ dict( address="2001:db8::2", ebgp_multihop=2, remote_as="65535", maximum_prefix=34, update_source="2001:db8::1", distribute_list=[ dict(action="export", acl=31), dict(action="import", acl=9), ], ) ], bgp_params=dict( confederation=[dict(peers=20), dict(identifier=66)], router_id="10.1.1.1", ), ), state="merged", ) ) commands = [ "set protocols bgp 65536 neighbor 2001:db8::2 distribute-list export 31", "set protocols bgp 65536 neighbor 2001:db8::2 distribute-list import 9", "set protocols bgp 65536 parameters confederation peers 20", "set protocols bgp 65536 parameters confederation identifier 66", "set protocols bgp 65536 maximum-paths ebgp 20", "set protocols bgp 65536 maximum-paths ibgp 45", ] self.execute_module(changed=True, commands=commands) def test_vyos_bgp_global_replaced_idempotent(self): set_module_args( dict( config=dict( as_number="65536", neighbor=[ dict( address="10.0.0.4", disable_connected_check=True, timers=dict(holdtime=30, keepalive=10), capability=dict(orf="receive"), ), dict( address="192.168.0.2", attribute_unchanged=dict(as_path=True, med=True), ebgp_multihop=2, remote_as="65535", soft_reconfiguration=True, update_source="192.168.0.1", ), dict( address="2001:db8::2", ebgp_multihop=2, remote_as="65535", maximum_prefix=34, update_source="2001:db8::1", ), ], network=[ dict(address="172.16.42.32/27", backdoor=True), dict(address="172.16.42.251/32", route_map="map01"), ], bgp_params=dict( bestpath=dict(as_path="confed", compare_routerid=True), default=dict(no_ipv4_unicast=True), router_id="10.1.1.1", ), redistribute=[ dict(protocol="kernel", route_map="map01"), dict(protocol="static", metric=20), dict(protocol="static", route_map="map01"), ], ), state="replaced", ) ) self.execute_module(changed=False, commands=[]) # def test_vyos_bgp_global_replaced(self): set_module_args( dict( config=dict( as_number="65536", timers=dict(holdtime=30, keepalive=10), neighbor=[ dict( address="200.11.155.3", prefix_list=[ dict(action="export", prefix_list=10), ], allowas_in=10, ), dict( address="2001:db8::2", remote_as="65535", as_override=True, default_originate="map01", route_map=[ dict(action="export", route_map="map01"), ], ), ], bgp_params=dict( log_neighbor_changes=True, no_client_to_client_reflection=True, confederation=[dict(peers=20), dict(identifier=66)], router_id="10.1.1.1", ), ), state="replaced", ) ) commands = [ "delete protocols bgp 65536 parameters default", "delete protocols bgp 65536 parameters bestpath compare-routerid", "delete protocols bgp 65536 parameters bestpath as-path confed", "delete protocols bgp 65536 network", "delete protocols bgp 65536 redistribute", "delete protocols bgp 65536 neighbor 2001:db8::2 update-source 2001:db8::1", "delete protocols bgp 65536 neighbor 2001:db8::2 maximum-prefix 34", "delete protocols bgp 65536 neighbor 2001:db8::2 ebgp-multihop 2", "delete protocols bgp 65536 neighbor 192.168.0.2", "delete protocols bgp 65536 neighbor 10.0.0.4", "set protocols bgp 65536 neighbor 200.11.155.3 prefix-list export 10", "set protocols bgp 65536 neighbor 200.11.155.3 allowas-in number 10", "set protocols bgp 65536 neighbor 2001:db8::2 as-override", "set protocols bgp 65536 neighbor 2001:db8::2 route-map export map01", "set protocols bgp 65536 parameters log-neighbor-changes", "set protocols bgp 65536 parameters no-client-to-client-reflection", "set protocols bgp 65536 parameters confederation peers 20", "set protocols bgp 65536 parameters confederation identifier 66", "set protocols bgp 65536 timers holdtime 30", "set protocols bgp 65536 timers keepalive 10", ] self.execute_module(changed=True, commands=commands) # def test_vyos_bgp_global_purged(self): set_module_args(dict(config=dict(as_number="65536"), state="purged")) # commands = ["delete protocols bgp 65536"] self.execute_module(changed=True, commands=commands) # def test_vyos_bgp_global_incorrect_instance(self): set_module_args( dict( config=dict( as_number="100", timers=dict(holdtime=30, keepalive=10), neighbor=[ dict( address="200.11.155.3", prefix_list=[ dict(action="export", prefix_list=10), ], allowas_in=10, ), dict( address="2001:db8::2", remote_as="65535", as_override=True, default_originate="map01", route_map=[ dict(action="export", route_map="map01"), ], ), ], bgp_params=dict( log_neighbor_changes=True, no_client_to_client_reflection=True, confederation=[dict(peers=20), dict(identifier=66)], router_id="10.1.1.1", ), ), state="replaced", ) ) result = self.execute_module(failed=True) self.assertIn("Only one bgp instance is allowed per device", result["msg"]) def test_vyos_bgp_global_replaced_af(self): set_module_args( dict( config=dict( as_number="65536", timers=dict(holdtime=30, keepalive=10), neighbor=[ dict( address="200.11.155.3", prefix_list=[ dict(action="export", prefix_list=10), ], allowas_in=10, ), dict( address="2001:db8::2", remote_as="65535", as_override=True, default_originate="map01", route_map=[ dict(action="export", route_map="map01"), ], ), ], bgp_params=dict( log_neighbor_changes=True, no_client_to_client_reflection=True, confederation=[dict(peers=20), dict(identifier=66)], router_id="10.1.1.1", ), ), state="replaced", ) ) result = self.execute_module(failed=True, filename="vyos_bgp_global_af_config.cfg") self.assertIn( "Use the _bgp_address_family module to delete the address_family under neighbor 5001::64, before replacing/deleting the neighbor.", result["msg"], ) def test_vyos_bgp_global_rendered(self): set_module_args( dict( config=dict( as_number="65536", neighbor=[ dict( address="10.0.0.4", disable_connected_check=True, timers=dict(holdtime=30, keepalive=10), capability=dict(orf="receive"), ), dict( address="192.168.0.2", attribute_unchanged=dict(as_path=True, med=True), ebgp_multihop=2, remote_as="65535", soft_reconfiguration=True, update_source="192.168.0.1", ), dict( address="2001:db8::2", ebgp_multihop=2, remote_as="65535", maximum_prefix=34, update_source="2001:db8::1", ), ], network=[ dict(address="172.16.42.32/27", backdoor=True), dict(address="172.16.42.251/32", route_map="map01"), ], bgp_params=dict( bestpath=dict(as_path="confed", compare_routerid=True), default=dict(no_ipv4_unicast=True), router_id="10.1.1.1", ), redistribute=[ dict(protocol="kernel", route_map="map01"), dict(protocol="static", metric=20), dict(protocol="static", route_map="map01"), ], ), state="rendered", ) ) rendered_cmds = [ "set protocols bgp 65536 neighbor 10.0.0.4 disable-connected-check", "set protocols bgp 65536 neighbor 10.0.0.4 timers holdtime 30", "set protocols bgp 65536 neighbor 10.0.0.4 timers keepalive 10", "set protocols bgp 65536 neighbor 10.0.0.4 capability orf prefix-list receive", "set protocols bgp 65536 neighbor 192.168.0.2 attribute-unchanged as-path", "set protocols bgp 65536 neighbor 192.168.0.2 attribute-unchanged med", "set protocols bgp 65536 neighbor 192.168.0.2 attribute-unchanged next-hop", "set protocols bgp 65536 neighbor 192.168.0.2 ebgp-multihop 2", "set protocols bgp 65536 neighbor 192.168.0.2 remote-as 65535", "set protocols bgp 65536 neighbor 192.168.0.2 soft-reconfiguration", "set protocols bgp 65536 neighbor 192.168.0.2 update-source 192.168.0.1", "set protocols bgp 65536 neighbor 2001:db8::2 ebgp-multihop 2", "set protocols bgp 65536 neighbor 2001:db8::2 remote-as 65535", "set protocols bgp 65536 neighbor 2001:db8::2 maximum-prefix 34", "set protocols bgp 65536 neighbor 2001:db8::2 update-source 2001:db8::1", "set protocols bgp 65536 redistribute kernel route-map map01", "set protocols bgp 65536 redistribute static route-map map01", "set protocols bgp 65536 network 172.16.42.32/27 backdoor", "set protocols bgp 65536 network 172.16.42.251/32 route-map map01", "set protocols bgp 65536 parameters bestpath as-path confed", "set protocols bgp 65536 parameters bestpath compare-routerid", "set protocols bgp 65536 parameters default no-ipv4-unicast", "set protocols bgp 65536 parameters router-id 10.1.1.1", ] result = self.execute_module(changed=False) self.assertEqual( sorted(result["rendered"]), sorted(rendered_cmds), result["rendered"], ) def test_vyos_bgp_global_parsed(self): - commands = [ "set protocols bgp 65536 neighbor 10.0.0.4 disable-connected-check", "set protocols bgp 65536 neighbor 10.0.0.4 timers holdtime 30", "set protocols bgp 65536 neighbor 10.0.0.4 timers keepalive 10", "set protocols bgp 65536 neighbor 10.0.0.4 capability orf prefix-list receive", "set protocols bgp 65536 neighbor 192.168.0.2 attribute-unchanged as-path", "set protocols bgp 65536 neighbor 192.168.0.2 attribute-unchanged med", "set protocols bgp 65536 neighbor 192.168.0.2 attribute-unchanged next-hop", "set protocols bgp 65536 neighbor 192.168.0.2 ebgp-multihop 2", "set protocols bgp 65536 neighbor 192.168.0.2 remote-as 65535", "set protocols bgp 65536 neighbor 192.168.0.2 soft-reconfiguration", "set protocols bgp 65536 neighbor 192.168.0.2 update-source 192.168.0.1", "set protocols bgp 65536 neighbor 2001:db8::2 ebgp-multihop 2", "set protocols bgp 65536 neighbor 2001:db8::2 remote-as 65535", "set protocols bgp 65536 neighbor 2001:db8::2 maximum-prefix 34", "set protocols bgp 65536 neighbor 2001:db8::2 update-source 2001:db8::1", "set protocols bgp 65536 redistribute kernel route-map map01", "set protocols bgp 65536 redistribute static route-map map01", "set protocols bgp 65536 network 172.16.42.32/27 backdoor", "set protocols bgp 65536 network 172.16.42.251/32 route-map map01", "set protocols bgp 65536 parameters bestpath as-path confed", "set protocols bgp 65536 parameters bestpath compare-routerid", "set protocols bgp 65536 parameters default no-ipv4-unicast", "set protocols bgp 65536 parameters router-id 10.1.1.1", ] parsed_str = "\n".join(commands) set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = { "as_number": 65536, "bgp_params": { "bestpath": {"as_path": "confed", "compare_routerid": True}, "default": {"no_ipv4_unicast": True}, "router_id": "10.1.1.1", }, "neighbor": [ { "address": "10.0.0.4", "capability": {"orf": "receive"}, "disable_connected_check": True, "timers": {"holdtime": 30, "keepalive": 10}, }, { "address": "192.168.0.2", "attribute_unchanged": { "as_path": True, "med": True, "next_hop": True, }, "ebgp_multihop": 2, "remote_as": 65535, "update_source": "192.168.0.1", }, { "address": "2001:db8::2", "ebgp_multihop": 2, "maximum_prefix": 34, "remote_as": 65535, "update_source": "2001:db8::1", }, ], "network": [ {"address": "172.16.42.32/27", "backdoor": True}, {"address": "172.16.42.251/32", "route_map": "map01"}, ], "redistribute": [ {"protocol": "kernel", "route_map": "map01"}, {"protocol": "static", "route_map": "map01"}, ], } self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) def test_vyos_bgp_global_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False) gather_list = { "as_number": 65536, "bgp_params": { "bestpath": {"as_path": "confed", "compare_routerid": True}, "default": {"no_ipv4_unicast": True}, "router_id": "10.1.1.1", }, "neighbor": [ { "address": "10.0.0.4", "capability": {"orf": "receive"}, "disable_connected_check": True, "timers": {"holdtime": 30, "keepalive": 10}, }, { "address": "192.168.0.2", "attribute_unchanged": {"as_path": True, "med": True}, "ebgp_multihop": 2, "remote_as": 65535, "soft_reconfiguration": True, "update_source": "192.168.0.1", }, { "address": "2001:db8::2", "ebgp_multihop": 2, "maximum_prefix": 34, "remote_as": 65535, "update_source": "2001:db8::1", }, ], "network": [ {"address": "172.16.42.32/27", "backdoor": True}, {"address": "172.16.42.251/32", "route_map": "map01"}, ], "redistribute": [ {"protocol": "kernel", "route_map": "map01"}, {"metric": 20, "protocol": "static"}, {"protocol": "static", "route_map": "map01"}, ], } self.assertEqual(sorted(gather_list), sorted(result["gathered"])) diff --git a/tests/unit/modules/network/vyos/test_vyos_command.py b/tests/unit/modules/network/vyos/test_vyos_command.py index e03ea7d..5479bbb 100644 --- a/tests/unit/modules/network/vyos/test_vyos_command.py +++ b/tests/unit/modules/network/vyos/test_vyos_command.py @@ -1,119 +1,118 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_command from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosCommandModule(TestVyosModule): - module = vyos_command def setUp(self): super(TestVyosCommandModule, self).setUp() self.mock_run_commands = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_command.run_commands" ) self.run_commands = self.mock_run_commands.start() def tearDown(self): super(TestVyosCommandModule, self).tearDown() self.mock_run_commands.stop() def load_fixtures(self, commands=None, filename=None): def load_from_file(*args, **kwargs): module, commands = args output = list() for item in commands: try: command = item["command"] except ValueError: command = item filename = str(command).replace(" ", "_") output.append(load_fixture(filename)) return output self.run_commands.side_effect = load_from_file def test_vyos_command_simple(self): set_module_args(dict(commands=["show version"])) result = self.execute_module() self.assertEqual(len(result["stdout"]), 1) self.assertTrue(result["stdout"][0].startswith("Version: VyOS")) def test_vyos_command_multiple(self): set_module_args(dict(commands=["show version", "show version"])) result = self.execute_module() self.assertEqual(len(result["stdout"]), 2) self.assertTrue(result["stdout"][0].startswith("Version: VyOS")) def test_vyos_command_wait_for(self): wait_for = 'result[0] contains "VyOS maintainers"' set_module_args(dict(commands=["show version"], wait_for=wait_for)) self.execute_module() def test_vyos_command_wait_for_fails(self): wait_for = 'result[0] contains "test string"' set_module_args(dict(commands=["show version"], wait_for=wait_for)) self.execute_module(failed=True) self.assertEqual(self.run_commands.call_count, 10) def test_vyos_command_retries(self): wait_for = 'result[0] contains "test string"' set_module_args(dict(commands=["show version"], wait_for=wait_for, retries=2)) self.execute_module(failed=True) self.assertEqual(self.run_commands.call_count, 3) def test_vyos_command_no_retries(self): wait_for = 'result[0] contains "test string"' set_module_args(dict(commands=["show version"], wait_for=wait_for, retries=0)) self.execute_module(failed=True) self.assertEqual(self.run_commands.call_count, 1) def test_vyos_command_match_any(self): wait_for = [ 'result[0] contains "VyOS maintainers"', 'result[0] contains "test string"', ] set_module_args(dict(commands=["show version"], wait_for=wait_for, match="any")) self.execute_module() def test_vyos_command_match_all(self): wait_for = [ 'result[0] contains "VyOS maintainers"', 'result[0] contains "maintainers@vyos.net"', ] set_module_args(dict(commands=["show version"], wait_for=wait_for, match="all")) self.execute_module() def test_vyos_command_match_all_failure(self): wait_for = [ 'result[0] contains "VyOS maintainers"', 'result[0] contains "test string"', ] commands = ["show version", "show version"] set_module_args(dict(commands=commands, wait_for=wait_for, match="all")) self.execute_module(failed=True) diff --git a/tests/unit/modules/network/vyos/test_vyos_config.py b/tests/unit/modules/network/vyos/test_vyos_config.py index def567a..ca949c6 100644 --- a/tests/unit/modules/network/vyos/test_vyos_config.py +++ b/tests/unit/modules/network/vyos/test_vyos_config.py @@ -1,141 +1,140 @@ # # (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 ansible_collections.vyos.vyos.plugins.cliconf.vyos import Cliconf from ansible_collections.vyos.vyos.plugins.modules import vyos_config from ansible_collections.vyos.vyos.tests.unit.compat.mock import MagicMock, patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosConfigModule(TestVyosModule): - module = vyos_config def setUp(self): super(TestVyosConfigModule, self).setUp() self.mock_get_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_run_commands = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_config.run_commands" ) self.run_commands = self.mock_run_commands.start() self.mock_get_connection = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_config.get_connection" ) self.get_connection = self.mock_get_connection.start() self.cliconf_obj = Cliconf(MagicMock()) self.running_config = load_fixture("vyos_config_config.cfg") self.conn = self.get_connection() self.conn.edit_config = MagicMock() self.running_config = load_fixture("vyos_config_config.cfg") def tearDown(self): super(TestVyosConfigModule, self).tearDown() self.mock_get_config.stop() self.mock_load_config.stop() self.mock_run_commands.stop() self.mock_get_connection.stop() def load_fixtures(self, commands=None, filename=None): config_file = "vyos_config_config.cfg" self.get_config.return_value = load_fixture(config_file) self.load_config.return_value = None def test_vyos_config_unchanged(self): src = load_fixture("vyos_config_config.cfg") self.conn.get_diff = MagicMock(return_value=self.cliconf_obj.get_diff(src, src)) set_module_args(dict(src=src)) self.execute_module() def test_vyos_config_src(self): src = load_fixture("vyos_config_src.cfg") set_module_args(dict(src=src)) candidate = "\n".join(self.module.format_commands(src.splitlines())) commands = [ "set system host-name foo", "delete interfaces ethernet eth0 address", ] self.conn.get_diff = MagicMock( return_value=self.cliconf_obj.get_diff(candidate, self.running_config) ) self.execute_module(changed=True, commands=commands) def test_vyos_config_src_brackets(self): src = load_fixture("vyos_config_src_brackets.cfg") set_module_args(dict(src=src)) commands = [ "set interfaces ethernet eth0 address 10.10.10.10/24", "set policy route testroute rule 1 set table 10", "set system host-name foo", ] self.conn.get_diff = MagicMock(side_effect=self.cliconf_obj.get_diff) self.execute_module(changed=True, commands=commands) def test_vyos_config_backup(self): set_module_args(dict(backup=True)) result = self.execute_module() self.assertIn("__backup__", result) def test_vyos_config_lines(self): commands = ["set system host-name foo"] set_module_args(dict(lines=commands)) candidate = "\n".join(commands) self.conn.get_diff = MagicMock( return_value=self.cliconf_obj.get_diff(candidate, self.running_config) ) self.execute_module(changed=True, commands=commands) def test_vyos_config_config(self): config = "set system host-name localhost" new_config = ["set system host-name router"] set_module_args(dict(lines=new_config, config=config)) candidate = "\n".join(new_config) self.conn.get_diff = MagicMock(return_value=self.cliconf_obj.get_diff(candidate, config)) self.execute_module(changed=True, commands=new_config) def test_vyos_config_match_none(self): lines = [ "set system interfaces ethernet eth0 address 1.2.3.4/24", "set system interfaces ethernet eth0 description test string", ] set_module_args(dict(lines=lines, match="none")) candidate = "\n".join(lines) self.conn.get_diff = MagicMock( return_value=self.cliconf_obj.get_diff(candidate, None, diff_match="none") ) self.execute_module(changed=True, commands=lines, sort=False) diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_global.py b/tests/unit/modules/network/vyos/test_vyos_firewall_global.py index 367da22..8164768 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_global.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_global.py @@ -1,361 +1,360 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_firewall_global from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.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_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.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, filename=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( afi="ipv4", 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"), ], ), dict( afi="ipv6", name="GOOGLE-DNS-v6", members=[ dict(address="2001:4860:4860::8888"), dict(address="2001:4860:4860::8844"), ], ), ], network_group=[ dict( afi="ipv4", name="MGMT", description="This group has the Management network addresses", members=[dict(address="192.0.1.0/24")], ), dict( afi="ipv6", name="DOCUMENTATION-v6", description="IPv6 Addresses reserved for documentation per RFC 3849", members=[ dict(address="2001:0DB8::/32"), dict(address="3FFF:FFFF::/32"), ], ), ], port_group=[ dict( name="TELNET", description="This group has the telnet ports", members=[dict(port="23")], ) ], ), ), 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 ipv6-address-group GOOGLE-DNS-v6 address 2001:4860:4860::8888", "set firewall group ipv6-address-group GOOGLE-DNS-v6 address 2001:4860:4860::8844", "set firewall group ipv6-address-group GOOGLE-DNS-v6", "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 group ipv6-network-group DOCUMENTATION-v6 network 2001:0DB8::/32", "set firewall group ipv6-network-group DOCUMENTATION-v6 network 3FFF:FFFF::/32", "set firewall group ipv6-network-group DOCUMENTATION-v6 description 'IPv6 Addresses reserved for documentation per RFC 3849'", "set firewall group ipv6-network-group DOCUMENTATION-v6", "set firewall group port-group TELNET port 23", "set firewall group port-group TELNET description 'This group has the telnet ports'", "set firewall group port-group TELNET", "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( afi="ipv4", 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"), ], ), dict( afi="ipv6", name="LOCAL-v6", description="This group has the hosts address lists of this machine", members=[ dict(address="::1"), dict(address="fdec:2503:89d6:59b3::1"), ], ), ], network_group=[ dict( afi="ipv4", name="RND", description="This group has the Management network addresses", members=[dict(address="192.0.2.0/24")], ), dict( afi="ipv6", name="UNIQUE-LOCAL-v6", description="This group encompasses the ULA address space in IPv6", members=[dict(address="fc00::/7")], ), ], port_group=[ dict( name="SSH", description="This group has the ssh ports", members=[dict(port="22")], ) ], ) ), 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( afi="ipv4", 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"), ], ), dict( afi="ipv6", name="LOCAL-v6", description="This group has the hosts address lists of this machine", members=[ dict(address="::1"), dict(address="fdec:2503:89d6:59b3::2"), ], ), ], network_group=[ dict( afi="ipv4", name="RND", description="This group has the Management network addresses", members=[dict(address="192.0.2.0/24")], ), dict( afi="ipv6", name="UNIQUE-LOCAL-v6", description="This group encompasses the ULA address space in IPv6", members=[dict(address="fc00::/7")], ), ], port_group=[ dict( name="SSH", description="This group has the ssh ports", members=[dict(port="2222")], ) ], ) ), 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", "delete firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::1", "set firewall group ipv6-address-group LOCAL-v6 address fdec:2503:89d6:59b3::2", "delete firewall group port-group SSH port 22", "set firewall group port-group SSH port 2222", ] 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( afi="ipv4", 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"), ], ), dict( afi="ipv6", name="LOCAL-v6", description="This group has the hosts address lists of this machine", members=[ dict(address="::1"), dict(address="fdec:2503:89d6:59b3::1"), ], ), ], network_group=[ dict( afi="ipv4", name="RND", description="This group has the Management network addresses", members=[dict(address="192.0.2.0/24")], ), dict( afi="ipv6", name="UNIQUE-LOCAL-v6", description="This group encompasses the ULA address space in IPv6", members=[dict(address="fc00::/7")], ), ], port_group=[ dict( name="SSH", description="This group has the ssh ports", members=[dict(port="22")], ) ], ) ), 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) diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_interfaces.py b/tests/unit/modules/network/vyos/test_vyos_firewall_interfaces.py index fe64659..acb9894 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_interfaces.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_interfaces.py @@ -1,389 +1,388 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_firewall_interfaces from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.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_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.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, filename=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=[]) diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_rules.py b/tests/unit/modules/network/vyos/test_vyos_firewall_rules.py index 623ab1e..7952b47 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_rules.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_rules.py @@ -1,1183 +1,1182 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_firewall_rules from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosFirewallRulesModule(TestVyosModule): - module = vyos_firewall_rules def setUp(self): super(TestVyosFirewallRulesModule, self).setUp() self.mock_get_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data" ) self.mock_execute_show_command = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules.Firewall_rulesFacts.get_device_data" ) self.execute_show_command = self.mock_execute_show_command.start() self.mock_get_os_version = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules.Firewall_rules._get_os_version" ) self.get_os_version = self.mock_get_os_version.start() self.get_os_version.return_value = "Vyos 1.2" 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() self.mock_get_os_version.stop() def load_fixtures(self, commands=None, filename=None): def load_from_file(*args, **kwargs): return load_fixture("vyos_firewall_rules_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( afi="ipv6", rule_sets=[ dict( name="V6-INBOUND", description="This is IPv6 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V6-OUTBOUND", description="This is IPv6 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), dict( afi="ipv4", rule_sets=[ dict( name="V4-INBOUND", description="This is IPv4 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V4-OUTBOUND", description="This is IPv4 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), ], state="merged", ) ) commands = [ "set firewall ipv6-name V6-INBOUND default-action 'reject'", "set firewall ipv6-name V6-INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6-name V6-INBOUND enable-default-log", "set firewall ipv6-name V6-OUTBOUND default-action 'accept'", "set firewall ipv6-name V6-OUTBOUND description 'This is IPv6 OUTBOUND rule set'", "set firewall name V4-INBOUND default-action 'reject'", "set firewall name V4-INBOUND description 'This is IPv4 INBOUND rule set'", "set firewall name V4-INBOUND enable-default-log", "set firewall name V4-OUTBOUND default-action 'accept'", "set firewall name V4-OUTBOUND description 'This is IPv4 OUTBOUND rule set'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_rule_set_02_merged(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="V6-INBOUND", description="This is IPv6 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V6-OUTBOUND", description="This is IPv6 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), dict( afi="ipv4", rule_sets=[ dict( name="V4-INBOUND", description="This is IPv4 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V4-OUTBOUND", description="This is IPv4 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), ], state="merged", ) ) commands = [ "set firewall ipv6-name V6-INBOUND default-action 'reject'", "set firewall ipv6-name V6-INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6-name V6-INBOUND enable-default-log", "set firewall ipv6-name V6-OUTBOUND default-action 'accept'", "set firewall ipv6-name V6-OUTBOUND description 'This is IPv6 OUTBOUND rule set'", "set firewall name V4-INBOUND default-action 'reject'", "set firewall name V4-INBOUND description 'This is IPv4 INBOUND rule set'", "set firewall name V4-INBOUND enable-default-log", "set firewall name V4-OUTBOUND default-action 'accept'", "set firewall name V4-OUTBOUND description 'This is IPv4 OUTBOUND rule set'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_01(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", description="This is IPv4 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", log="disable", protocol="icmp", fragment="match-frag", disable=True, ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall name INBOUND default-action 'accept'", "set firewall name INBOUND description 'This is IPv4 INBOUND rule set'", "set firewall name INBOUND enable-default-log", "set firewall name INBOUND rule 101 protocol 'icmp'", "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall name INBOUND rule 101 fragment 'match-frag'", "set firewall name INBOUND rule 101", "set firewall name INBOUND rule 101 disable", "set firewall name INBOUND rule 101 action 'accept'", "set firewall name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall name INBOUND rule 101 log 'disable'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_02(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="tcp", source=dict( address="192.0.2.0", mac_address="38:00:25:19:76:0c", port=2127, ), destination=dict(address="192.0.1.0", port=2124), limit=dict( burst=10, rate=dict(number=20, unit="second"), ), recent=dict(count=10, time=20), state=dict( established=True, related=True, invalid=True, new=True, ), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall name INBOUND rule 101 protocol 'tcp'", "set firewall name INBOUND rule 101 destination address 192.0.1.0", "set firewall name INBOUND rule 101 destination port 2124", "set firewall name INBOUND rule 101", "set firewall name INBOUND rule 101 source address 192.0.2.0", "set firewall name INBOUND rule 101 source mac-address 38:00:25:19:76:0c", "set firewall name INBOUND rule 101 source port 2127", "set firewall name INBOUND rule 101 state new enable", "set firewall name INBOUND rule 101 state invalid enable", "set firewall name INBOUND rule 101 state related enable", "set firewall name INBOUND rule 101 state established enable", "set firewall name INBOUND rule 101 limit burst 10", "set firewall name INBOUND rule 101 limit rate 20/second", "set firewall name INBOUND rule 101 recent count 10", "set firewall name INBOUND rule 101 recent time 20", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_03(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", destination=dict( group=dict( address_group="OUT-ADDR-GROUP", network_group="OUT-NET-GROUP", port_group="OUT-PORT-GROUP", ) ), source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ) ), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall name INBOUND rule 101 source group address-group IN-ADDR-GROUP", "set firewall name INBOUND rule 101 source group network-group IN-NET-GROUP", "set firewall name INBOUND rule 101 source group port-group IN-PORT-GROUP", "set firewall name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP", "set firewall name INBOUND rule 101 destination group network-group OUT-NET-GROUP", "set firewall name INBOUND rule 101 destination group port-group OUT-PORT-GROUP", "set firewall name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_04(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", time=dict( monthdays="2", startdate="2020-01-24", starttime="13:20:00", stopdate="2020-01-28", stoptime="13:30:00", weekdays="!Sat,Sun", utc=True, ), tcp=dict(flags="ALL"), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall name INBOUND rule 101", "set firewall name INBOUND rule 101 tcp flags ALL", "set firewall name INBOUND rule 101 time utc", "set firewall name INBOUND rule 101 time monthdays 2", "set firewall name INBOUND rule 101 time startdate 2020-01-24", "set firewall name INBOUND rule 101 time stopdate 2020-01-28", "set firewall name INBOUND rule 101 time weekdays !Sat,Sun", "set firewall name INBOUND rule 101 time stoptime 13:30:00", "set firewall name INBOUND rule 101 time starttime 13:20:00", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_01(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", disabled=True, icmp=dict(type_name="echo-request"), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", "set firewall ipv6-name INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6-name INBOUND enable-default-log", "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6-name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 disable", "set firewall ipv6-name INBOUND rule 101 action 'accept'", "set firewall ipv6-name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6-name INBOUND rule 101 icmpv6 type echo-request", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_02(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="tcp", source=dict( address="2001:db8::12", mac_address="38:00:25:19:76:0c", port=2127, ), destination=dict(address="2001:db8::11", port=2124), limit=dict( burst=10, rate=dict(number=20, unit="second"), ), recent=dict(count=10, time=20), state=dict( established=True, related=True, invalid=True, new=True, ), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall ipv6-name INBOUND rule 101 protocol 'tcp'", "set firewall ipv6-name INBOUND rule 101 destination address 2001:db8::11", "set firewall ipv6-name INBOUND rule 101 destination port 2124", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 source address 2001:db8::12", "set firewall ipv6-name INBOUND rule 101 source mac-address 38:00:25:19:76:0c", "set firewall ipv6-name INBOUND rule 101 source port 2127", "set firewall ipv6-name INBOUND rule 101 state new enable", "set firewall ipv6-name INBOUND rule 101 state invalid enable", "set firewall ipv6-name INBOUND rule 101 state related enable", "set firewall ipv6-name INBOUND rule 101 state established enable", "set firewall ipv6-name INBOUND rule 101 limit burst 10", "set firewall ipv6-name INBOUND rule 101 recent count 10", "set firewall ipv6-name INBOUND rule 101 recent time 20", "set firewall ipv6-name INBOUND rule 101 limit rate 20/second", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_03(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", destination=dict( group=dict( address_group="OUT-ADDR-GROUP", network_group="OUT-NET-GROUP", port_group="OUT-PORT-GROUP", ) ), source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ) ), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall ipv6-name INBOUND rule 101 source group address-group IN-ADDR-GROUP", "set firewall ipv6-name INBOUND rule 101 source group network-group IN-NET-GROUP", "set firewall ipv6-name INBOUND rule 101 source group port-group IN-PORT-GROUP", "set firewall ipv6-name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP", "set firewall ipv6-name INBOUND rule 101 destination group network-group OUT-NET-GROUP", "set firewall ipv6-name INBOUND rule 101 destination group port-group OUT-PORT-GROUP", "set firewall ipv6-name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_04(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", time=dict( monthdays="2", startdate="2020-01-24", starttime="13:20:00", stopdate="2020-01-28", stoptime="13:30:00", weekdays="!Sat,Sun", utc=True, ), tcp=dict(flags="ALL"), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 tcp flags ALL", "set firewall ipv6-name INBOUND rule 101 time utc", "set firewall ipv6-name INBOUND rule 101 time monthdays 2", "set firewall ipv6-name INBOUND rule 101 time startdate 2020-01-24", "set firewall ipv6-name INBOUND rule 101 time stopdate 2020-01-28", "set firewall ipv6-name INBOUND rule 101 time weekdays !Sat,Sun", "set firewall ipv6-name INBOUND rule 101 time stoptime 13:30:00", "set firewall ipv6-name INBOUND rule 101 time starttime 13:20:00", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_icmp_01(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type_name="port-unreachable"), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall ipv6-name INBOUND rule 101 icmpv6 type port-unreachable", "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6-name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_01(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type=1, code=1), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall name INBOUND rule 101 icmp type 1", "set firewall name INBOUND rule 101 icmp code 1", "set firewall name INBOUND rule 101 protocol 'icmp'", "set firewall name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_02(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type_name="echo-request"), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall name INBOUND rule 101 icmp type-name echo-request", "set firewall name INBOUND rule 101 protocol 'icmp'", "set firewall name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_del_01(self): set_module_args( dict( config=[dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")])], state="deleted", ) ) commands = ["delete firewall name V4-INGRESS"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_02(self): set_module_args( dict( config=[ dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")]), dict(afi="ipv6", rule_sets=[dict(name="V6-INGRESS")]), ], state="deleted", ) ) commands = [ "delete firewall name V4-INGRESS", "delete firewall ipv6-name V6-INGRESS", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_03(self): set_module_args(dict(config=[], state="deleted")) commands = ["delete firewall name", "delete firewall ipv6-name"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_04(self): set_module_args( dict( config=[ dict(afi="ipv4", rule_sets=[dict(name="V4-ING")]), dict(afi="ipv6", rule_sets=[dict(name="V6-ING")]), ], state="deleted", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_rep_01(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="reject", description="Rule 101 is configured by Ansible RM", ipsec="match-ipsec", protocol="tcp", fragment="match-frag", disabled=False, ), dict( number="102", action="accept", description="Rule 102 is configured by Ansible RM", protocol="icmp", disabled=True, ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", description="This rule-set is configured by Ansible RM", ), dict( name="EGRESS", default_action="reject", description="This rule-set is configured by Ansible RM", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ) ], ), ], ), ], state="replaced", ) ) commands = [ "delete firewall name V4-INGRESS rule 101 disable", "set firewall name V4-INGRESS description 'This is IPv4 INGRESS rule set'", "set firewall name V4-INGRESS rule 101 protocol 'tcp'", "set firewall name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible RM'", "set firewall name V4-INGRESS rule 101 action 'reject'", "set firewall name V4-INGRESS rule 102 disable", "set firewall name V4-INGRESS rule 102 action 'accept'", "set firewall name V4-INGRESS rule 102 protocol 'icmp'", "set firewall name V4-INGRESS rule 102 description 'Rule 102 is configured by Ansible RM'", "set firewall name V4-INGRESS rule 102", "set firewall ipv6-name V6-INGRESS description 'This rule-set is configured by Ansible RM'", "set firewall ipv6-name EGRESS description 'This rule-set is configured by Ansible RM'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_02(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=False, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disabled=True, ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ) ], ), ], ), ], state="replaced", ) ) commands = [ "delete firewall name V4-INGRESS enable-default-log", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_01(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disabled=True, ) ], ), dict( name="EGRESS", default_action="reject", ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ) ], ), ], ), ], state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_02(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disabled=True, ), ], ), ], ), ], state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_01(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disabled=True, ) ], ), dict( name="EGRESS", default_action="reject", ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ) ], ), ], ), ], state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_01(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-IN", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="1", action="reject", description="Rule 1 is configured by Ansible RM", ipsec="match-ipsec", log="enable", protocol="tcp", fragment="match-frag", disabled=False, source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ) ), ), dict( number="2", action="accept", description="Rule 102 is configured by Ansible RM", protocol="icmp", disabled=True, ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-IN", default_action="accept", description="This rule-set is configured by Ansible RM", ), dict( name="V6-EG", default_action="reject", description="This rule-set is configured by Ansible RM", ), ], ), ], state="overridden", ) ) commands = [ "delete firewall ipv6-name V6-INGRESS", "delete firewall ipv6-name EGRESS", "delete firewall name V4-INGRESS", "delete firewall name EGRESS", "set firewall name V4-IN default-action 'accept'", "set firewall name V4-IN description 'This is IPv4 INGRESS rule set'", "set firewall name V4-IN enable-default-log", "set firewall name V4-IN rule 1 protocol 'tcp'", "set firewall name V4-IN rule 1 log 'enable'", "set firewall name V4-IN rule 1 description 'Rule 1 is configured by Ansible RM'", "set firewall name V4-IN rule 1 fragment 'match-frag'", "set firewall name V4-IN rule 1 source group address-group IN-ADDR-GROUP", "set firewall name V4-IN rule 1 source group network-group IN-NET-GROUP", "set firewall name V4-IN rule 1 source group port-group IN-PORT-GROUP", "set firewall name V4-IN rule 1", "set firewall name V4-IN rule 1 action 'reject'", "set firewall name V4-IN rule 1 ipsec 'match-ipsec'", "set firewall name V4-IN rule 2 disable", "set firewall name V4-IN rule 2 action 'accept'", "set firewall name V4-IN rule 2 protocol 'icmp'", "set firewall name V4-IN rule 2 description 'Rule 102 is configured by Ansible RM'", "set firewall name V4-IN rule 2", "set firewall ipv6-name V6-IN default-action 'accept'", "set firewall ipv6-name V6-IN description 'This rule-set is configured by Ansible RM'", "set firewall ipv6-name V6-EG default-action 'reject'", "set firewall ipv6-name V6-EG description 'This rule-set is configured by Ansible RM'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_idem_01(self): set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disabled=True, ) ], ), dict( name="EGRESS", default_action="reject", ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ) ], ), ], ), ], state="overridden", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v6_rule_sets_rule_merged_01_version(self): self.get_os_version.return_value = "VyOS 1.4-rolling-202007010117" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", disabled=True, icmp=dict(type_name="echo-request"), ) ], ), ], ) ], state="merged", ) ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", "set firewall ipv6-name INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6-name INBOUND enable-default-log", "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6-name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 disable", "set firewall ipv6-name INBOUND rule 101 action 'accept'", "set firewall ipv6-name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6-name INBOUND rule 101 icmpv6 type-name echo-request", ] self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_hostname.py b/tests/unit/modules/network/vyos/test_vyos_hostname.py index 13d4e32..3df9a17 100644 --- a/tests/unit/modules/network/vyos/test_vyos_hostname.py +++ b/tests/unit/modules/network/vyos/test_vyos_hostname.py @@ -1,113 +1,112 @@ # (c) 2021 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 ansible_collections.vyos.vyos.plugins.modules import vyos_hostname from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosHostnameModule(TestVyosModule): - module = vyos_hostname def setUp(self): super(TestVyosHostnameModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.hostname.hostname.HostnameFacts.get_config" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosHostnameModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_execute_show_command.stop() def load_fixtures(self, commands=None, filename=None): if filename is None: filename = "vyos_hostname_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def test_vyos_hostname_merged_idempotent(self): set_module_args(dict(config=dict(hostname="vyos_test"))) self.execute_module(changed=False, commands=[]) def test_vyos_hostname_replaced_idempotent(self): set_module_args(dict(config=dict(hostname="vyos_test"), state="replaced")) self.execute_module(changed=False, commands=[]) def test_vyos_hostname_overridden_idempotent(self): set_module_args(dict(config=dict(hostname="vyos_test"), state="overridden")) self.execute_module(changed=False, commands=[]) def test_vyos_hostname_merged(self): set_module_args(dict(config=dict(hostname="vyos"))) self.execute_module(changed=True, commands=["set system host-name vyos"]) def test_vyos_hostname_replaced(self): set_module_args(dict(config=dict(hostname="vyos"), state="replaced")) self.execute_module(changed=True, commands=["set system host-name vyos"]) def test_vyos_hostname_overridden(self): set_module_args(dict(config=dict(hostname="vyos"), state="overridden")) def test_vyos_hostname_deleted(self): set_module_args(dict(state="deleted")) self.execute_module(changed=True, commands=["delete system host-name vyos_test"]) def test_vyos_hostname_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False, filename="vyos_hostname_config.cfg") gathered_list = {"hostname": "vyos_test"} self.assertEqual(sorted(gathered_list), sorted(result["gathered"])) def test_vyos_hostname_parsed(self): parsed_str = "set system host-name vyos_test" set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = {"hostname": "vyos_test"} self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) def test_vyos_hostname_rendered(self): set_module_args(dict(state="rendered", config=dict(hostname="vyos_test"))) commands = ["set system host-name vyos_test"] result = self.execute_module(changed=False) self.assertEqual(sorted(result["rendered"]), sorted(commands), result["rendered"]) diff --git a/tests/unit/modules/network/vyos/test_vyos_interfaces.py b/tests/unit/modules/network/vyos/test_vyos_interfaces.py index 1595040..ea6dbf5 100644 --- a/tests/unit/modules/network/vyos/test_vyos_interfaces.py +++ b/tests/unit/modules/network/vyos/test_vyos_interfaces.py @@ -1,186 +1,185 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_interfaces from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosFirewallInterfacesModule(TestVyosModule): - module = vyos_interfaces def setUp(self): super(TestVyosFirewallInterfacesModule, self).setUp() self.mock_get_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos." "facts.interfaces.interfaces.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, filename=None): def load_from_file(*args, **kwargs): return load_fixture("vyos_interfaces_config.cfg") self.execute_show_command.side_effect = load_from_file def test_vyos_interfaces_merged(self): set_module_args( dict( config=[ dict(name="bond1", description="Bond - 1", enabled=True), dict(name="vtun1", description="vtun - 1", enabled=True), dict(name="wg01", description="wg - 1", enabled=True), ], state="merged", ) ) commands = [ "set interfaces bonding bond1 description 'Bond - 1'", "set interfaces openvpn vtun1 description 'vtun - 1'", "set interfaces wireguard wg01 description 'wg - 1'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_merged_idempotent(self): set_module_args( dict( config=[ dict( name="wg02", description="wire guard int 2", enabled=True, ), ], state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_interfaces_merged_newinterface(self): set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="merged", ) ) commands = [ "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_replaced_newinterface(self): set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="replaced", ) ) commands = [ "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_overridden_newinterface(self): set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="overridden", ) ) commands = [ "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", "delete interfaces wireguard wg02 description", "delete interfaces ethernet eth3 description", ] self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_logging_global.py b/tests/unit/modules/network/vyos/test_vyos_logging_global.py index 12da571..f1ab00d 100644 --- a/tests/unit/modules/network/vyos/test_vyos_logging_global.py +++ b/tests/unit/modules/network/vyos/test_vyos_logging_global.py @@ -1,452 +1,451 @@ # # (c) 2021, Ansible by Red Hat, inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # from __future__ import absolute_import, division, print_function __metaclass__ = type from textwrap import dedent from ansible_collections.vyos.vyos.plugins.modules import vyos_logging_global from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule class TestVyosLoggingGlobalModule(TestVyosModule): - module = vyos_logging_global def setUp(self): super(TestVyosLoggingGlobalModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.logging_global.logging_global.Logging_globalFacts.get_logging_data" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosLoggingGlobalModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_execute_show_command.stop() def test_vyos_logging_global_merged_idempotent(self): self.execute_show_command.return_value = dedent( """\ set system syslog console facility all set system syslog console facility local7 level 'err' set system syslog console facility news level 'debug' set system syslog file xyz set system syslog file abc archive size '125' set system syslog file def archive file '2' set system syslog file def facility local6 level 'emerg' set system syslog file def facility local7 level 'emerg' set system syslog global archive file '2' set system syslog global archive size '111' set system syslog global facility cron level 'debug' set system syslog global facility local7 level 'debug' set system syslog global marker interval '111' set system syslog global preserve-fqdn set system syslog host 10.0.2.12 facility all protocol 'udp' set system syslog host 10.0.2.15 facility all level 'all' set system syslog host 10.0.2.15 facility all protocol 'udp' set system syslog host 10.0.2.15 port '122' set system syslog user paul facility local7 level 'err' set system syslog user vyos facility local6 level 'alert' set system syslog user vyos facility local7 level 'debug' """ ) playbook = dict( config=dict( console=dict( facilities=[ dict(facility="all"), dict(facility="local7", severity="err"), dict(facility="news", severity="debug"), ] ), files=[ dict(path="xyz"), dict(path="abc", archive=dict(size=125)), dict( path="def", archive=dict(file_num=2), facilities=[ dict(facility="local6", severity="emerg"), dict(facility="local7", severity="emerg"), ], ), ], hosts=[ dict( hostname="10.0.2.15", port=122, facilities=[ dict(facility="all", severity="all"), dict(facility="all", protocol="udp"), ], ), dict( hostname="10.0.2.12", facilities=[dict(facility="all", protocol="udp")], ), ], users=[ dict( username="vyos", facilities=[ dict(facility="local7", severity="debug"), dict(facility="local6", severity="alert"), ], ), dict( username="paul", facilities=[dict(facility="local7", severity="err")], ), ], global_params=dict( archive=dict(size=111, file_num=2), marker_interval=111, preserve_fqdn="True", facilities=[ dict(facility="cron", severity="debug"), dict(facility="local7", severity="debug"), ], ), ) ) compare_cmds = [] playbook["state"] = "merged" set_module_args(playbook) result = self.execute_module() self.maxDiff = None self.assertEqual(sorted(result["commands"]), sorted(compare_cmds)) def test_vyos_logging_global_merged(self): self.execute_show_command.return_value = dedent( """\ """ ) playbook = dict( config=dict( console=dict( facilities=[ dict(facility="all"), dict(facility="local7", severity="err"), dict(facility="news", severity="debug"), ] ), files=[ dict(path="xyz"), dict(path="abc", archive=dict(size=125)), dict( path="def", archive=dict(file_num=2), facilities=[ dict(facility="local6", severity="emerg"), dict(facility="local7", severity="emerg"), ], ), ], hosts=[ dict( hostname="10.0.2.15", port=122, facilities=[ dict(facility="all", severity="all"), dict(facility="all", protocol="udp"), ], ), dict( hostname="10.0.2.12", facilities=[dict(facility="all", protocol="udp")], ), ], users=[ dict( username="vyos", facilities=[ dict(facility="local7", severity="debug"), dict(facility="local6", severity="alert"), ], ), dict( username="paul", facilities=[dict(facility="local7", severity="err")], ), ], global_params=dict( archive=dict(size=111, file_num=2), marker_interval=111, preserve_fqdn="True", facilities=[ dict(facility="cron", severity="debug"), dict(facility="local7", severity="debug"), ], ), ) ) compare_cmds = [ "set system syslog user paul facility local7 level err", "set system syslog host 10.0.2.15 facility all protocol udp", "set system syslog global marker interval 111", "set system syslog file def archive file 2", "set system syslog file abc archive size 125", "set system syslog console facility local7 level err", "set system syslog host 10.0.2.12 facility all protocol udp", "set system syslog global facility local7 level debug", "set system syslog host 10.0.2.15 facility all level all", "set system syslog global preserve-fqdn", "set system syslog global archive file 2", "set system syslog console facility all", "set system syslog file xyz", "set system syslog file def facility local7 level emerg", "set system syslog console facility news level debug", "set system syslog user vyos facility local6 level alert", "set system syslog global facility cron level debug", "set system syslog file def facility local6 level emerg", "set system syslog global archive size 111", "set system syslog host 10.0.2.15 port 122", "set system syslog user vyos facility local7 level debug", ] playbook["state"] = "merged" set_module_args(playbook) result = self.execute_module(changed=True) self.maxDiff = None self.assertEqual(sorted(result["commands"]), sorted(compare_cmds)) def test_vyos_logging_global_deleted(self): self.execute_show_command.return_value = dedent( """\ set system syslog console facility all set system syslog console facility local7 level 'err' set system syslog console facility news level 'debug' set system syslog file xyz set system syslog file abc archive size '125' set system syslog file def archive file '2' set system syslog file def facility local6 level 'emerg' set system syslog file def facility local7 level 'emerg' set system syslog global archive file '2' set system syslog global archive size '111' set system syslog global facility cron level 'debug' set system syslog global facility local7 level 'debug' set system syslog global marker interval '111' set system syslog global preserve-fqdn set system syslog host 10.0.2.12 facility all protocol 'udp' set system syslog host 10.0.2.15 facility all level 'all' set system syslog host 10.0.2.15 facility all protocol 'udp' set system syslog host 10.0.2.15 port '122' set system syslog user paul facility local7 level 'err' set system syslog user vyos facility local6 level 'alert' set system syslog user vyos facility local7 level 'debug' """ ) playbook = dict(config=dict()) compare_cmds = ["delete system syslog"] playbook["state"] = "deleted" set_module_args(playbook) result = self.execute_module(changed=True) self.maxDiff = None self.assertEqual(sorted(result["commands"]), sorted(compare_cmds)) def test_vyos_logging_global_replaced(self): """ passing all commands as have and expecting [] commands """ self.execute_show_command.return_value = dedent( """\ set system syslog console facility all set system syslog console facility local7 level 'err' set system syslog console facility news level 'debug' set system syslog file xyz set system syslog file abc archive size '125' set system syslog file def archive file '2' set system syslog file def facility local6 level 'emerg' set system syslog file def facility local7 level 'emerg' set system syslog global archive file '2' set system syslog global archive size '111' set system syslog global facility cron level 'debug' set system syslog global facility local7 level 'debug' set system syslog global marker interval '111' set system syslog global preserve-fqdn set system syslog host 10.0.2.12 facility all protocol 'udp' set system syslog host 10.0.2.15 facility all level 'all' set system syslog host 10.0.2.15 facility all protocol 'udp' set system syslog host 10.0.2.15 port '122' set system syslog user paul facility local7 level 'err' set system syslog user vyos facility local6 level 'alert' set system syslog user vyos facility local7 level 'debug' """ ) playbook = dict( config=dict( console=dict(facilities=[dict(facility="local7", severity="emerg")]), files=[ dict( path="abc", archive=dict(file_num=2), facilities=[ dict(facility="local6", severity="err"), dict(facility="local7", severity="emerg"), ], ) ], ) ) compare_cmds = [ "delete system syslog console facility all", "delete system syslog console facility local7", "delete system syslog console facility news", "delete system syslog file def", "delete system syslog file xyz", "delete system syslog global facility cron", "delete system syslog global facility local7", "delete system syslog global archive file 2", "delete system syslog global archive size 111", "delete system syslog global marker", "delete system syslog global preserve-fqdn", "delete system syslog host 10.0.2.12", "delete system syslog host 10.0.2.15", "delete system syslog user paul", "delete system syslog user vyos", "set system syslog console facility local7 level emerg", "set system syslog file abc facility local6 level err", "set system syslog file abc facility local7 level emerg", "delete system syslog file abc archive size 125", "set system syslog file abc archive file 2", ] playbook["state"] = "replaced" set_module_args(playbook) result = self.execute_module(changed=True) self.maxDiff = None self.assertEqual(sorted(result["commands"]), sorted(compare_cmds)) def test_vyos_logging_global_replaced_idempotent(self): """ passing all commands as have and expecting [] commands """ self.execute_show_command.return_value = dedent( """\ set system syslog console facility local6 """ ) playbook = dict(config=dict(console=dict(facilities=[dict(facility="local6")]))) compare_cmds = [] playbook["state"] = "replaced" set_module_args(playbook) result = self.execute_module(changed=False) self.maxDiff = None self.assertEqual(sorted(result["commands"]), sorted(compare_cmds)) def test_vyos_logging_global_overridden(self): self.execute_show_command.return_value = dedent( """\ set system syslog console set system syslog global """ ) playbook = dict( config=dict( console=dict(facilities=[dict(facility="local7", severity="emerg")]), files=[ dict( path="abc", archive=dict(file_num=2), facilities=[ dict(facility="local6", severity="err"), dict(facility="local7", severity="emerg"), ], ) ], ) ) compare_cmds = [ "set system syslog console facility local7 level emerg", "set system syslog file abc facility local6 level err", "set system syslog file abc facility local7 level emerg", "set system syslog file abc archive file 2", ] playbook["state"] = "overridden" set_module_args(playbook) result = self.execute_module(changed=True) print(result["commands"]) self.maxDiff = None self.assertEqual(sorted(result["commands"]), sorted(compare_cmds)) def test_vyos_logging_global_rendered(self): playbook = dict( config=dict( console=dict(facilities=[dict(facility="all")]), hosts=[ dict( hostname="10.0.2.16", facilities=[dict(facility="local6")], ) ], users=[ dict(username="vyos"), dict(username="paul", facilities=[dict(facility="local7")]), ], ) ) compare_cmds = [ "set system syslog console facility all", "set system syslog host 10.0.2.16 facility local6", "set system syslog user vyos", "set system syslog user paul facility local7", ] playbook["state"] = "rendered" set_module_args(playbook) result = self.execute_module() self.maxDiff = None self.assertEqual(sorted(result["rendered"]), sorted(compare_cmds)) def test_vyos_logging_global_parsed(self): set_module_args( dict( running_config=dedent( """\ set system syslog console facility all set system syslog file xyz """ ), state="parsed", ) ) parsed = dict( console=dict(facilities=[dict(facility="all")]), files=[dict(path="xyz")], ) result = self.execute_module(changed=False) self.maxDiff = None self.assertEqual(sorted(result["parsed"]), sorted(parsed)) def test_vyos_logging_global_gathered(self): self.execute_show_command.return_value = dedent( """\ set system syslog console facility all """ ) set_module_args(dict(state="gathered")) gathered = dict(console=dict(facilities=[dict(facility="all")])) result = self.execute_module(changed=False) self.maxDiff = None self.assertEqual(sorted(result["gathered"]), sorted(gathered)) diff --git a/tests/unit/modules/network/vyos/test_vyos_ntp_global.py b/tests/unit/modules/network/vyos/test_vyos_ntp_global.py index 0a6119b..5da1056 100644 --- a/tests/unit/modules/network/vyos/test_vyos_ntp_global.py +++ b/tests/unit/modules/network/vyos/test_vyos_ntp_global.py @@ -1,349 +1,347 @@ # (c) 2021 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 ansible_collections.vyos.vyos.plugins.modules import vyos_ntp_global from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosNTPModule(TestVyosModule): - module = vyos_ntp_global def setUp(self): super(TestVyosNTPModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ntp_global.ntp_global.Ntp_globalFacts.get_config" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosNTPModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_execute_show_command.stop() def load_fixtures(self, commands=None, filename=None): if filename is None: filename = "vyos_ntp_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def test_ntp_merged_idempotent(self): set_module_args( dict( config=dict( allow_clients=["10.1.1.0/24", "10.1.2.0/24"], listen_addresses=["10.2.3.1", "10.4.3.1"], servers=[ dict(server="server1"), dict(server="server3", options=["noselect", "dynamic"]), dict(server="time1.vyos.net"), dict(server="time2.vyos.net"), dict(server="time3.vyos.net"), ], ), state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_ntp_merged(self): set_module_args( dict( config=dict( allow_clients=["10.2.2.0/24", "10.3.3.0/24"], listen_addresses=["10.3.4.1", "10.4.5.1"], servers=[ dict(server="server4", options=["dynamic", "preempt"]), dict( server="server5", options=[ "noselect", "pool", "preempt", "prefer", ], ), ], ), state="merged", ) ) commands = [ "set system ntp allow-clients address 10.2.2.0/24", "set system ntp allow-clients address 10.3.3.0/24", "set system ntp listen-address 10.3.4.1", "set system ntp listen-address 10.4.5.1", "set system ntp server server4 dynamic", "set system ntp server server4 preempt", "set system ntp server server5 pool", "set system ntp server server5 noselect", "set system ntp server server5 preempt", "set system ntp server server5 prefer", ] self.execute_module(changed=True, commands=commands) def test_ntp_replaced(self): set_module_args( dict( config=dict( allow_clients=["10.3.4.0/24", "10.4.5.0/24"], listen_addresses=["10.3.3.1", "10.4.4.1"], servers=[ dict(server="server4", options=["noselect", "prefer"]), dict( server="server6", options=[ "noselect", "dynamic", "prefer", "preempt", ], ), dict(server="time1.vyos.net"), dict(server="time2.vyos.net"), dict(server="time3.vyos.net"), ], ), state="replaced", ) ) commands = [ "delete system ntp allow-clients address 10.1.1.0/24", "delete system ntp allow-clients address 10.1.2.0/24", "delete system ntp listen-address 10.2.3.1", "delete system ntp listen-address 10.4.3.1", "delete system ntp server server1", "delete system ntp server server3", "set system ntp allow-clients address 10.3.4.0/24", "set system ntp allow-clients address 10.4.5.0/24", "set system ntp listen-address 10.3.3.1", "set system ntp listen-address 10.4.4.1", "set system ntp server server4 noselect", "set system ntp server server4 prefer", "set system ntp server server6 noselect", "set system ntp server server6 dynamic", "set system ntp server server6 prefer", "set system ntp server server6 preempt", ] self.execute_module(changed=True, commands=commands) def test_ntp_replaced_idempotent(self): set_module_args( dict( config=dict( allow_clients=["10.1.1.0/24", "10.1.2.0/24"], listen_addresses=["10.2.3.1", "10.4.3.1"], servers=[ dict(server="server1"), dict(server="server3", options=["noselect", "dynamic"]), dict(server="time1.vyos.net"), dict(server="time2.vyos.net"), dict(server="time3.vyos.net"), ], ), state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_ntp_overridden(self): set_module_args( dict( config=dict( allow_clients=["10.9.9.0/24"], listen_addresses=["10.9.9.1"], servers=[ dict(server="server9"), dict(server="server6", options=["noselect", "dynamic"]), dict(server="time1.vyos.net"), dict(server="time2.vyos.net"), dict(server="time3.vyos.net"), ], ), state="overridden", ) ) commands = [ "delete system ntp allow-clients address 10.1.1.0/24", "delete system ntp allow-clients address 10.1.2.0/24", "delete system ntp listen-address 10.2.3.1", "delete system ntp listen-address 10.4.3.1", "delete system ntp server server1", "delete system ntp server server3", "set system ntp allow-clients address 10.9.9.0/24", "set system ntp listen-address 10.9.9.1", "set system ntp server server9", "set system ntp server server6 noselect", "set system ntp server server6 dynamic", ] self.execute_module(changed=True, commands=commands) def test_ntp_overridden_idempotent(self): set_module_args( dict( config=dict( allow_clients=["10.1.1.0/24", "10.1.2.0/24"], listen_addresses=["10.2.3.1", "10.4.3.1"], servers=[ dict(server="server1"), dict(server="server3", options=["noselect", "dynamic"]), dict(server="time1.vyos.net"), dict(server="time2.vyos.net"), dict(server="time3.vyos.net"), ], ), state="overridden", ) ) self.execute_module(changed=False, commands=[]) def test_ntp_rendered(self): set_module_args( dict( config=dict( allow_clients=["10.7.7.0/24", "10.8.8.0/24"], listen_addresses=["10.7.9.1"], servers=[ dict(server="server79"), dict(server="server46", options=["noselect", "dynamic"]), dict(server="time1.vyos.net"), dict(server="time2.vyos.net"), dict(server="time3.vyos.net"), ], ), state="rendered", ) ) rendered_commands = [ "set system ntp allow-clients address 10.7.7.0/24", "set system ntp allow-clients address 10.8.8.0/24", "set system ntp listen-address 10.7.9.1", "set system ntp server server79", "set system ntp server server46 noselect", "set system ntp server server46 dynamic", "set system ntp server time1.vyos.net", "set system ntp server time2.vyos.net", "set system ntp server time3.vyos.net", ] result = self.execute_module(changed=False) self.assertEqual( sorted(result["rendered"]), sorted(rendered_commands), result["rendered"], ) def test_ntp_parsed(self): - commands = ( "set system ntp allow-clients address 10.7.7.0/24", "set system ntp allow-clients address 10.6.7.0/24", "set system ntp listen-address 10.7.9.1", "set system ntp listen-address 10.7.7.1", "set system ntp server check", "set system ntp server server46 noselect", "set system ntp server server46 prefer", "set system ntp server time1.vyos.net", "set system ntp server time2.vyos.net", "set system ntp server time3.vyos.net", ) parsed_str = "\n".join(commands) set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = { "allow_clients": ["10.6.7.0/24", "10.7.7.0/24"], "listen_addresses": ["10.7.7.1", "10.7.9.1"], "servers": [ {"server": "check"}, {"server": "server46", "options": ["noselect", "prefer"]}, {"server": "time1.vyos.net"}, {"server": "time2.vyos.net"}, {"server": "time3.vyos.net"}, ], } self.assertEqual(parsed_list, result["parsed"]) def test_ntp_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False) gathered_list = { "allow_clients": ["10.1.1.0/24", "10.1.2.0/24"], "listen_addresses": ["10.2.3.1", "10.4.3.1"], "servers": [ {"server": "server1"}, {"server": "server3", "options": ["dynamic", "noselect"]}, {"server": "time1.vyos.net"}, {"server": "time2.vyos.net"}, {"server": "time3.vyos.net"}, ], } self.assertEqual(gathered_list, result["gathered"]) def test_ntp_deleted(self): set_module_args( dict( config=dict( allow_clients=["10.1.1.0/24"], listen_addresses=["10.2.3.1"], servers=[ dict(server="server1"), dict(server="server3", options=["noselect"]), dict(server="time1.vyos.net"), dict(server="time2.vyos.net"), dict(server="time3.vyos.net"), ], ), state="deleted", ) ) commands = [ "delete system ntp allow-clients", "delete system ntp listen-address", "delete system ntp server server1", "delete system ntp server server3", "delete system ntp server time1.vyos.net", "delete system ntp server time2.vyos.net", "delete system ntp server time3.vyos.net", ] self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_ospf_interfaces.py b/tests/unit/modules/network/vyos/test_vyos_ospf_interfaces.py index e8c3241..74eaf7c 100644 --- a/tests/unit/modules/network/vyos/test_vyos_ospf_interfaces.py +++ b/tests/unit/modules/network/vyos/test_vyos_ospf_interfaces.py @@ -1,444 +1,443 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_ospf_interfaces from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosOspfInterfacesModule(TestVyosModule): - module = vyos_ospf_interfaces def setUp(self): super(TestVyosOspfInterfacesModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_execute_show_command = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospf_interfaces.ospf_interfaces.Ospf_interfacesFacts.get_device_data" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosOspfInterfacesModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_execute_show_command.stop() def load_fixtures(self, commands=None, filename=None): if filename is None: filename = "vyos_ospf_interfaces_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def sort_address_family(self, entry_list): for entry in entry_list: if entry.get("address_family"): entry["address_family"].sort(key=lambda i: i.get("afi")) def test_vyos_ospf_interfaces_merged_new_config(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict( afi="ipv4", cost=100, authentication=dict(plaintext_password="abcdefg!"), priority=55, ), dict(afi="ipv6", mtu_ignore=True, instance=20), ], ), dict( name="bond2", address_family=[ dict( afi="ipv4", transmit_delay=9, ), dict(afi="ipv6", passive=True), ], ), ], state="merged", ) ) commands = [ "set interfaces bonding bond2 ip ospf transmit-delay 9", "set interfaces bonding bond2 ipv6 ospfv3 passive", "set interfaces ethernet eth0 ip ospf cost 100", "set interfaces ethernet eth0 ip ospf priority 55", "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", "set interfaces ethernet eth0 ipv6 ospfv3 instance-id 20", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospf_interfaces_merged_idempotent(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict(afi="ipv6", mtu_ignore=True, instance=33), ], ), dict( name="eth1", address_family=[ dict( afi="ipv4", cost=100, ), dict(afi="ipv6", ifmtu=33), ], ), ], ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospf_interfaces_existing_config_merged(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict(afi="ipv6", cost=500), ], ), dict( name="eth1", address_family=[ dict( afi="ipv4", priority=100, ), dict(afi="ipv6", ifmtu=25), ], ), ], ) ) commands = [ "set interfaces ethernet eth0 ipv6 ospfv3 cost 500", "set interfaces ethernet eth1 ip ospf priority 100", "set interfaces ethernet eth1 ipv6 ospfv3 ifmtu 25", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospf_interfaces_replaced(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict( afi="ipv4", cost=100, authentication=dict(plaintext_password="abcdefg!"), priority=55, ), ], ), dict( name="bond2", address_family=[ dict( afi="ipv4", transmit_delay=9, ), dict(afi="ipv6", passive=True), ], ), ], state="replaced", ) ) commands = [ "set interfaces bonding bond2 ip ospf transmit-delay 9", "set interfaces bonding bond2 ipv6 ospfv3 passive", "set interfaces ethernet eth0 ip ospf cost 100", "set interfaces ethernet eth0 ip ospf priority 55", "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", "delete interfaces ethernet eth0 ipv6 ospfv3 instance-id 33", "delete interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospf_interfaces_replaced_idempotent(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict(afi="ipv6", mtu_ignore=True, instance=33), ], ), dict( name="eth1", address_family=[ dict( afi="ipv4", cost=100, ), dict(afi="ipv6", ifmtu=33), ], ), ], state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospf_interfaces_overridden(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict( afi="ipv4", cost=100, authentication=dict(plaintext_password="abcdefg!"), priority=55, ), ], ), dict( name="bond2", address_family=[ dict( afi="ipv4", transmit_delay=9, ), dict(afi="ipv6", passive=True), ], ), ], state="overridden", ) ) commands = [ "set interfaces bonding bond2 ip ospf transmit-delay 9", "set interfaces bonding bond2 ipv6 ospfv3 passive", "set interfaces ethernet eth0 ip ospf cost 100", "set interfaces ethernet eth0 ip ospf priority 55", "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", "delete interfaces ethernet eth1 ip ospf", "delete interfaces ethernet eth1 ipv6 ospfv3", "delete interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore", "delete interfaces ethernet eth0 ipv6 ospfv3 instance-id 33", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospf_interfaces_overridden_idempotent(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict(afi="ipv6", mtu_ignore=True, instance=33), ], ), dict( name="eth1", address_family=[ dict( afi="ipv4", cost=100, ), dict(afi="ipv6", ifmtu=33), ], ), ], state="overridden", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospf_interfaces_deleted(self): set_module_args( dict( config=[ dict( name="eth0", ), ], state="deleted", ) ) commands = ["delete interfaces ethernet eth0 ipv6 ospfv3"] self.execute_module(changed=True, commands=commands) def test_vyos_ospf_interfaces_notpresent_deleted(self): set_module_args( dict( config=[ dict( name="eth3", ), ], state="deleted", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospf_interfaces_rendered(self): set_module_args( dict( config=[ dict( name="eth0", address_family=[ dict( afi="ipv4", cost=100, authentication=dict(plaintext_password="abcdefg!"), priority=55, ), dict(afi="ipv6", mtu_ignore=True, instance=20), ], ), dict( name="bond2", address_family=[ dict( afi="ipv4", transmit_delay=9, ), dict(afi="ipv6", passive=True), ], ), ], state="rendered", ) ) commands = [ "set interfaces ethernet eth0 ip ospf cost 100", "set interfaces ethernet eth0 ip ospf authentication plaintext-password abcdefg!", "set interfaces ethernet eth0 ip ospf priority 55", "set interfaces ethernet eth0 ipv6 ospfv3 mtu-ignore", "set interfaces ethernet eth0 ipv6 ospfv3 instance-id 20", "set interfaces bonding bond2 ip ospf transmit-delay 9", "set interfaces bonding bond2 ipv6 ospfv3 passive", ] result = self.execute_module(changed=False) self.assertEqual(sorted(result["rendered"]), sorted(commands), result["rendered"]) def test_vyos_ospf_interfaces_parsed(self): commands = [ "set interfaces bonding bond2 ip ospf authentication md5 key-id 10 md5-key '1111111111232345'", "set interfaces bonding bond2 ip ospf bandwidth '70'", "set interfaces bonding bond2 ip ospf transmit-delay '45'", "set interfaces bonding bond2 ipv6 ospfv3 'passive'", "set interfaces ethernet eth0 ip ospf cost '50'", "set interfaces ethernet eth0 ip ospf priority '26'", "set interfaces ethernet eth0 ipv6 ospfv3 instance-id '33'", "set interfaces ethernet eth0 ipv6 ospfv3 'mtu-ignore'", "set interfaces ethernet eth1 ip ospf network 'point-to-point'", "set interfaces ethernet eth1 ip ospf priority '26'", "set interfaces ethernet eth1 ip ospf transmit-delay '50'", "set interfaces ethernet eth1 ipv6 ospfv3 dead-interval '39'", ] parsed_str = "\n".join(commands) set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = [ { "address_family": [ { "afi": "ipv4", "authentication": { "md5_key": { "key": "1111111111232345", "key_id": 10, } }, "bandwidth": 70, "transmit_delay": 45, }, {"afi": "ipv6", "passive": True}, ], "name": "bond2", }, { "address_family": [ {"afi": "ipv4", "cost": 50, "priority": 26}, {"afi": "ipv6", "instance": "33", "mtu_ignore": True}, ], "name": "eth0", }, { "address_family": [ { "afi": "ipv4", "network": "point-to-point", "priority": 26, "transmit_delay": 50, }, {"afi": "ipv6", "dead_interval": 39}, ], "name": "eth1", }, ] result_list = self.sort_address_family(result["parsed"]) given_list = self.sort_address_family(parsed_list) self.assertEqual(result_list, given_list) def test_vyos_ospf_interfaces_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False, filename="vyos_ospf_interfaces_config.cfg") gathered_list = [ { "address_family": [{"afi": "ipv6", "instance": "33", "mtu_ignore": True}], "name": "eth0", }, { "address_family": [ {"afi": "ipv4", "cost": 100}, {"afi": "ipv6", "ifmtu": 33}, ], "name": "eth1", }, ] result_list = self.sort_address_family(result["gathered"]) given_list = self.sort_address_family(gathered_list) self.assertEqual(result_list, given_list) diff --git a/tests/unit/modules/network/vyos/test_vyos_ospfv2.py b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py index e036f68..84b882d 100644 --- a/tests/unit/modules/network/vyos/test_vyos_ospfv2.py +++ b/tests/unit/modules/network/vyos/test_vyos_ospfv2.py @@ -1,424 +1,423 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_ospfv2 from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosOspfv2Module(TestVyosModule): - module = vyos_ospfv2 def setUp(self): super(TestVyosOspfv2Module, self).setUp() self.mock_get_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv2.ospfv2.Ospfv2Facts.get_device_data" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosOspfv2Module, 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, filename=None): if filename is None: filename = "vyos_ospfv2_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def test_vyos_ospfv2_merged_new_config(self): set_module_args( dict( config=dict( log_adjacency_changes="detail", mpls_te=dict(enabled=True, router_address="192.0.11.11"), auto_cost=dict(reference_bandwidth=2), areas=[ dict( area_id="2", area_type=dict(normal=True), authentication="plaintext-password", shortcut="enable", ), dict( area_id="4", area_type=dict(stub=dict(default_cost=10)), network=[dict(address="192.0.2.0/24")], range=[ dict(address="192.0.3.0/24", cost=10), dict(address="192.0.4.0/24", cost=12), ], ), ], ), state="merged", ) ) commands = [ "set protocols ospf mpls-te enable", "set protocols ospf mpls-te router-address '192.0.11.11'", "set protocols ospf auto-cost reference-bandwidth '2'", "set protocols ospf log-adjacency-changes 'detail'", "set protocols ospf area '2'", "set protocols ospf area 2 authentication plaintext-password", "set protocols ospf area 2 shortcut enable", "set protocols ospf area 2 area-type normal", "set protocols ospf area 4 range 192.0.3.0/24 cost 10", "set protocols ospf area 4 range 192.0.3.0/24", "set protocols ospf area 4 range 192.0.4.0/24 cost 12", "set protocols ospf area 4 range 192.0.4.0/24", "set protocols ospf area 4 area-type stub default-cost 10", "set protocols ospf area '4'", "set protocols ospf area 4 network 192.0.2.0/24", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv2_merged_idem(self): set_module_args( dict( config=dict( areas=[ dict( area_id="12", area_type=dict(normal=True), authentication="plaintext-password", shortcut="enable", ), dict( area_id="14", area_type=dict(stub=dict(default_cost=20)), network=[dict(address="192.0.12.0/24")], range=[ dict(address="192.0.13.0/24", cost=10), dict(address="192.0.14.0/24", cost=12), ], ), ], ), state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospfv2_merged_update_existing(self): set_module_args( dict( config=dict( areas=[ dict( area_id="12", area_type=dict(normal=True), authentication="plaintext-password", shortcut="enable", ), dict( area_id="14", area_type=dict(stub=dict(set=False)), network=[ dict(address="192.0.12.0/24"), dict(address="192.0.22.0/24"), ], range=[ dict(address="192.0.13.0/24", cost=10), dict(address="192.0.14.0/24", cost=12), ], ), ], ), state="merged", ) ) commands = [ "delete protocols ospf area 14 area-type stub", "set protocols ospf area 14 network 192.0.22.0/24", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv2_replaced(self): set_module_args( dict( config=dict( log_adjacency_changes="detail", mpls_te=dict(enabled=True, router_address="192.0.11.11"), auto_cost=dict(reference_bandwidth=2), areas=[ dict( area_id="12", area_type=dict(normal=True), authentication="plaintext-password", shortcut="enable", ), dict( area_id="15", area_type=dict(stub=dict(default_cost=10)), network=[dict(address="192.0.12.0/24")], range=[ dict(address="192.0.13.0/24", cost=10), dict(address="192.0.14.0/24", cost=12), dict(address="192.0.15.0/24", cost=14), ], ), ], ), state="replaced", ) ) commands = [ "set protocols ospf mpls-te enable", "set protocols ospf mpls-te router-address '192.0.11.11'", "set protocols ospf auto-cost reference-bandwidth '2'", "set protocols ospf log-adjacency-changes 'detail'", "delete protocols ospf area 14", "set protocols ospf area 15 range 192.0.13.0/24 cost 10", "set protocols ospf area 15 range 192.0.13.0/24", "set protocols ospf area 15 range 192.0.14.0/24 cost 12", "set protocols ospf area 15 range 192.0.14.0/24", "set protocols ospf area 15 range 192.0.15.0/24 cost 14", "set protocols ospf area 15 range 192.0.15.0/24", "set protocols ospf area 15 area-type stub default-cost 10", "set protocols ospf area '15'", "set protocols ospf area 15 network 192.0.12.0/24", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv2_replaced_idem(self): set_module_args( dict( config=dict( areas=[ dict( area_id="12", area_type=dict(normal=True), authentication="plaintext-password", shortcut="enable", ), dict( area_id="14", area_type=dict(stub=dict(default_cost=20)), network=[dict(address="192.0.12.0/24")], range=[ dict(address="192.0.13.0/24", cost=10), dict(address="192.0.14.0/24", cost=12), ], ), ], ), state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospfv2_deleted_no_config(self): set_module_args(dict(config=None, state="deleted")) commands = ["delete protocols ospf"] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv2_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False, filename="vyos_ospfv2_config.cfg") gather_dict = { "areas": [ { "area_id": "2", "area_type": {"normal": True}, "authentication": "plaintext-password", "shortcut": "enable", }, { "area_id": "14", "area_type": {"stub": {"default_cost": 20, "set": True}}, "network": [{"address": "192.0.12.0/24"}], "range": [ {"address": "192.0.13.0/24", "cost": 10}, {"address": "192.0.14.0/24", "cost": 12}, ], }, ], } self.assertEqual(sorted(gather_dict), sorted(result["gathered"])) def test_vyos_ospfv2_parsed(self): parsed_str = """set protocols ospf area 2 area-type 'normal' set protocols ospf area 2 authentication 'plaintext-password' set protocols ospf area 2 shortcut 'enable' set protocols ospf area 3 area-type 'nssa' set protocols ospf area 4 area-type stub default-cost '20' set protocols ospf area 4 network '192.0.2.0/24' set protocols ospf area 4 range 192.0.3.0/24 cost '10' set protocols ospf area 4 range 192.0.4.0/24 cost '12' set protocols ospf default-information originate 'always' set protocols ospf default-information originate metric '10' set protocols ospf default-information originate metric-type '2' set protocols ospf auto-cost reference-bandwidth '2' set protocols ospf default-information originate route-map 'ingress' set protocols ospf log-adjacency-changes 'detail' set protocols ospf max-metric router-lsa 'administrative' set protocols ospf max-metric router-lsa on-shutdown '10' set protocols ospf max-metric router-lsa on-startup '10' set protocols ospf mpls-te 'enable' set protocols ospf mpls-te router-address '192.0.11.11' set protocols ospf neighbor 192.0.11.12 poll-interval '10' set protocols ospf neighbor 192.0.11.12 priority '2' set protocols ospf parameters abr-type 'cisco' set protocols ospf parameters 'opaque-lsa' set protocols ospf parameters 'rfc1583-compatibility' set protocols ospf parameters router-id '192.0.1.1' set protocols ospf passive-interface 'eth1' set protocols ospf passive-interface 'eth2' set protocols ospf redistribute bgp metric '10' set protocols ospf redistribute bgp metric-type '2'""" set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = { "areas": [ { "area_id": "2", "area_type": {"normal": True}, "authentication": "plaintext-password", "shortcut": "enable", }, {"area_id": "3", "area_type": {"nssa": {"set": True}}}, { "area_id": "4", "area_type": {"stub": {"default_cost": 20, "set": True}}, "network": [{"address": "192.0.2.0/24"}], "range": [ {"address": "192.0.3.0/24", "cost": 10}, {"address": "192.0.4.0/24", "cost": 12}, ], }, ], "auto_cost": {"reference_bandwidth": 2}, "default_information": { "originate": { "always": True, "metric": 10, "metric_type": 2, "route_map": "ingress", } }, "log_adjacency_changes": "detail", "max_metric": { "router_lsa": { "administrative": True, "on_shutdown": 10, "on_startup": 10, } }, "mpls_te": {"enabled": True, "router_address": "192.0.11.11"}, "neighbor": [ { "neighbor_id": "192.0.11.12", "poll_interval": 10, "priority": 2, } ], "parameters": { "abr_type": "cisco", "opaque_lsa": True, "rfc1583_compatibility": True, "router_id": "192.0.1.1", }, "passive_interface": ["eth2", "eth1"], "redistribute": [{"metric": 10, "metric_type": 2, "route_type": "bgp"}], } self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) def test_vyos_ospfv2_rendered(self): set_module_args( dict( config=dict( log_adjacency_changes="detail", mpls_te=dict(enabled=True, router_address="192.0.11.11"), auto_cost=dict(reference_bandwidth=2), areas=[ dict( area_id="2", area_type=dict(normal=True), authentication="plaintext-password", shortcut="enable", ), dict( area_id="4", area_type=dict(stub=dict(default_cost=10)), network=[dict(address="192.0.2.0/24")], range=[ dict(address="192.0.3.0/24", cost=10), dict(address="192.0.4.0/24", cost=12), ], ), ], ), state="rendered", ) ) commands = [ "set protocols ospf mpls-te enable", "set protocols ospf mpls-te router-address '192.0.11.11'", "set protocols ospf auto-cost reference-bandwidth '2'", "set protocols ospf log-adjacency-changes 'detail'", "set protocols ospf area '2'", "set protocols ospf area 2 authentication plaintext-password", "set protocols ospf area 2 shortcut enable", "set protocols ospf area 2 area-type normal", "set protocols ospf area 4 range 192.0.3.0/24 cost 10", "set protocols ospf area 4 range 192.0.3.0/24", "set protocols ospf area 4 range 192.0.4.0/24 cost 12", "set protocols ospf area 4 range 192.0.4.0/24", "set protocols ospf area 4 area-type stub default-cost 10", "set protocols ospf area '4'", "set protocols ospf area 4 network 192.0.2.0/24", ] result = self.execute_module(changed=False) self.assertEqual(sorted(result["rendered"]), sorted(commands), result["rendered"]) diff --git a/tests/unit/modules/network/vyos/test_vyos_ospfv3.py b/tests/unit/modules/network/vyos/test_vyos_ospfv3.py index d04b7f7..2231c0b 100644 --- a/tests/unit/modules/network/vyos/test_vyos_ospfv3.py +++ b/tests/unit/modules/network/vyos/test_vyos_ospfv3.py @@ -1,339 +1,338 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_ospfv3 from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosOspfv3Module(TestVyosModule): - module = vyos_ospfv3 def setUp(self): super(TestVyosOspfv3Module, self).setUp() self.mock_get_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospfv3.ospfv3.Ospfv3Facts.get_device_data" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosOspfv3Module, 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, filename=None): if filename is None: filename = "vyos_ospfv3_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def test_vyos_ospfv3_merged_new_config(self): set_module_args( dict( config=dict( redistribute=[dict(route_type="bgp")], parameters=dict(router_id="192.0.2.10"), areas=[ dict( area_id="2", export_list="export1", import_list="import1", range=[ dict(address="2001:db10::/32"), dict(address="2001:db20::/32"), dict(address="2001:db30::/32"), ], ), dict( area_id="3", range=[dict(address="2001:db40::/32")], ), ], ), state="merged", ) ) commands = [ "set protocols ospfv3 redistribute bgp", "set protocols ospfv3 parameters router-id '192.0.2.10'", "set protocols ospfv3 area 2 range 2001:db10::/32", "set protocols ospfv3 area 2 range 2001:db20::/32", "set protocols ospfv3 area 2 range 2001:db30::/32", "set protocols ospfv3 area '2'", "set protocols ospfv3 area 2 export-list export1", "set protocols ospfv3 area 2 import-list import1", "set protocols ospfv3 area '3'", "set protocols ospfv3 area 3 range 2001:db40::/32", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv3_merged_idem(self): set_module_args( dict( config=dict( areas=[ dict( area_id="12", export_list="export1", import_list="import1", range=[ dict(address="2001:db11::/32"), dict(address="2001:db22::/32"), dict(address="2001:db33::/32"), ], ), dict( area_id="13", range=[dict(address="2001:db44::/32")], ), ], ), state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospfv3_merged_update_existing(self): set_module_args( dict( config=dict( redistribute=[dict(route_type="bgp")], parameters=dict(router_id="192.0.2.10"), areas=[ dict( area_id="12", export_list="export1", import_list="import1", range=[ dict(address="2001:db11::/32"), dict(address="2001:db22::/32"), dict(address="2001:db33::/32"), ], ), dict( area_id="13", range=[ dict(address="2001:db44::/32"), dict(address="2001:db55::/32"), ], ), ], ), state="merged", ) ) commands = [ "set protocols ospfv3 redistribute bgp", "set protocols ospfv3 parameters router-id '192.0.2.10'", "set protocols ospfv3 area 13 range 2001:db55::/32", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv3_replaced(self): set_module_args( dict( config=dict( redistribute=[dict(route_type="bgp")], parameters=dict(router_id="192.0.2.10"), areas=[ dict( area_id="12", export_list="export1", import_list="import1", range=[ dict(address="2001:db10::/32"), dict(address="2001:db22::/32"), dict(address="2001:db33::/32"), ], ), dict( area_id="14", range=[dict(address="2001:db40::/32")], ), ], ), state="replaced", ) ) commands = [ "set protocols ospfv3 redistribute bgp", "set protocols ospfv3 parameters router-id '192.0.2.10'", "delete protocols ospfv3 area 12 range 2001:db11::/32", "set protocols ospfv3 area 12 range 2001:db10::/32", "delete protocols ospfv3 area 13", "set protocols ospfv3 area '14'", "set protocols ospfv3 area 14 range 2001:db40::/32", ] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv3_replaced_idem(self): set_module_args( dict( config=dict( areas=[ dict( area_id="12", export_list="export1", import_list="import1", range=[ dict(address="2001:db11::/32"), dict(address="2001:db22::/32"), dict(address="2001:db33::/32"), ], ), dict( area_id="13", range=[dict(address="2001:db44::/32")], ), ], ), state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_ospfv3_deleted_no_config(self): set_module_args(dict(config=None, state="deleted")) commands = ["delete protocols ospfv3"] self.execute_module(changed=True, commands=commands) def test_vyos_ospfv3_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False, filename="vyos_ospfv3_config.cfg") gather_dict = { "areas": [ { "area_id": "12", "export_list": "export1", "import_list": "import1", "range": [ {"address": "2001:db11::/32"}, {"address": "2001:db22::/32"}, {"address": "2001:db33::/32"}, ], }, {"area_id": "13", "range": [{"address": "2001:db44::/32"}]}, ], } self.assertEqual(sorted(gather_dict), sorted(result["gathered"])) def test_vyos_ospfv3_parsed(self): parsed_str = """set protocols ospfv3 area 2 export-list 'export1' set protocols ospfv3 area 2 import-list 'import1' set protocols ospfv3 area 2 range '2001:db10::/32' set protocols ospfv3 area 2 range '2001:db20::/32' set protocols ospfv3 area 2 range '2001:db30::/32' set protocols ospfv3 area 3 range '2001:db40::/32' set protocols ospfv3 parameters router-id '192.0.2.10' set protocols ospfv3 redistribute 'bgp'""" set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_dict = { "areas": [ { "area_id": "2", "export_list": "export1", "import_list": "import1", "range": [ {"address": "2001:db10::/32"}, {"address": "2001:db20::/32"}, {"address": "2001:db30::/32"}, ], }, {"area_id": "3", "range": [{"address": "2001:db40::/32"}]}, ], "parameters": {"router_id": "192.0.2.10"}, "redistribute": [{"route_type": "bgp"}], } self.assertEqual(sorted(parsed_dict), sorted(result["parsed"])) def test_vyos_ospfv3_rendered(self): set_module_args( dict( config=dict( redistribute=[dict(route_type="bgp")], parameters=dict(router_id="192.0.2.10"), areas=[ dict( area_id="2", export_list="export1", import_list="import1", range=[ dict(address="2001:db10::/32"), dict(address="2001:db20::/32"), dict(address="2001:db30::/32"), ], ), dict( area_id="3", range=[dict(address="2001:db40::/32")], ), ], ), state="rendered", ) ) commands = [ "set protocols ospfv3 redistribute bgp", "set protocols ospfv3 parameters router-id '192.0.2.10'", "set protocols ospfv3 area 2 range 2001:db10::/32", "set protocols ospfv3 area 2 range 2001:db20::/32", "set protocols ospfv3 area 2 range 2001:db30::/32", "set protocols ospfv3 area '2'", "set protocols ospfv3 area 2 export-list export1", "set protocols ospfv3 area 2 import-list import1", "set protocols ospfv3 area '3'", "set protocols ospfv3 area 3 range 2001:db40::/32", ] result = self.execute_module(changed=False) self.assertEqual(sorted(result["rendered"]), sorted(commands), result["rendered"]) diff --git a/tests/unit/modules/network/vyos/test_vyos_ping.py b/tests/unit/modules/network/vyos/test_vyos_ping.py index ea8d259..612e50c 100644 --- a/tests/unit/modules/network/vyos/test_vyos_ping.py +++ b/tests/unit/modules/network/vyos/test_vyos_ping.py @@ -1,106 +1,105 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_ping from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosPingModule(TestVyosModule): - module = vyos_ping def setUp(self): super(TestVyosPingModule, self).setUp() self.mock_run_commands = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_ping.run_commands" ) self.run_commands = self.mock_run_commands.start() def tearDown(self): super(TestVyosPingModule, self).tearDown() self.mock_run_commands.stop() def load_fixtures(self, commands=None, filename=None): def load_from_file(*args, **kwargs): commands = kwargs["commands"] output = list() for command in commands: filename = str(command).split(" | ", 1)[0].replace(" ", "_") output.append(load_fixture("vyos_ping_%s" % filename)) return output self.run_commands.side_effect = load_from_file def test_vyos_ping_expected_success(self): """Test for successful pings when destination should be reachable""" set_module_args(dict(count=2, dest="10.10.10.10")) self.execute_module() def test_vyos_ping_expected_failure(self): """Test for unsuccessful pings when destination should not be reachable""" set_module_args(dict(count=4, dest="10.10.10.20", state="absent")) self.execute_module() def test_vyos_ping_unexpected_success(self): """Test for successful pings when destination should not be reachable - FAIL.""" set_module_args(dict(count=2, dest="10.10.10.10", state="absent")) self.execute_module(failed=True) def test_vyos_ping_unexpected_failure(self): """Test for unsuccessful pings when destination should be reachable - FAIL.""" set_module_args(dict(count=4, dest="10.10.10.20")) self.execute_module(failed=True) def test_vyos_ping_failure_stats(self): """Test for asserting stats when ping fails""" set_module_args(dict(count=4, dest="10.10.10.20")) result = self.execute_module(failed=True) self.assertEqual(result["packet_loss"], "100%") self.assertEqual(result["packets_rx"], 0) self.assertEqual(result["packets_tx"], 4) def test_vyos_ping_success_stats(self): """Test for asserting stats when ping passes""" set_module_args(dict(count=2, dest="10.10.10.10")) result = self.execute_module() self.assertEqual(result["packet_loss"], "0%") self.assertEqual(result["packets_rx"], 2) self.assertEqual(result["packets_tx"], 2) self.assertEqual(result["rtt"]["min"], 12) self.assertEqual(result["rtt"]["avg"], 17) self.assertEqual(result["rtt"]["max"], 22) self.assertEqual(result["rtt"]["mdev"], 10) def test_vyos_ping_success_stats_with_options(self): set_module_args(dict(count=10, ttl=128, size=512, dest="10.10.10.11")) result = self.execute_module() self.assertEqual(result["packet_loss"], "0%") self.assertEqual(result["packets_rx"], 10) self.assertEqual(result["packets_tx"], 10) self.assertEqual(result["rtt"]["min"], 1) self.assertEqual(result["rtt"]["avg"], 3) self.assertEqual(result["rtt"]["max"], 21) self.assertEqual(result["rtt"]["mdev"], 5) diff --git a/tests/unit/modules/network/vyos/test_vyos_prefix_lists.py b/tests/unit/modules/network/vyos/test_vyos_prefix_lists.py index e8d044d..dafba2a 100644 --- a/tests/unit/modules/network/vyos/test_vyos_prefix_lists.py +++ b/tests/unit/modules/network/vyos/test_vyos_prefix_lists.py @@ -1,1240 +1,1239 @@ # (c) 2021 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 textwrap import dedent from ansible_collections.vyos.vyos.plugins.modules import vyos_prefix_lists from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule class TestVyosPrefixListsModule(TestVyosModule): - # Testing strategy # ------------------ # (a) The unit tests cover `merged` and `replaced` for every attribute. # Since `overridden` is essentially `replaced` but at a larger # scale, these indirectly cover `overridden` as well. # (b) For linear attributes replaced is not valid and hence, those tests # delete the attributes from the config subsection. # (c) The argspec for VRFs is same as the top-level spec and the config logic # is re-used. Hence, those attributes are not explictly covered. However, a # combination of VRF + top-level spec + AF is tested. module = vyos_prefix_lists def setUp(self): super(TestVyosPrefixListsModule, self).setUp() self.mock_get_resource_connection = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection = self.mock_get_resource_connection.start() self.mock_get_config = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.prefix_lists.prefix_lists.Prefix_listsFacts.get_config" ) self.get_config = self.mock_get_config.start() def tearDown(self): super(TestVyosPrefixListsModule, self).tearDown() self.get_resource_connection.stop() self.get_config.stop() # test merged for linear attributes def test_vyos_prefix_lists_linear_merged(self): self.get_config.return_value = dedent( """\ """ ) set_module_args( dict( config=[ dict( afi="ipv4", prefix_lists=[ dict( name="plist1", description="Test plist1", entries=[ dict( sequence=10, action="permit", description="Test rule 10", prefix="92.168.10.0/26", ), dict( sequence=20, action="deny", description="Test rule 20", prefix="72.168.2.0/24", ), ], ), dict( name="plist2", entries=[ dict( sequence=20, action="permit", prefix="82.168.10.0/26", le=32, ), dict( sequence=30, action="deny", prefix="62.168.2.0/24", ge=25, ), ], ), ], ), dict( afi="ipv6", prefix_lists=[ dict( name="plist3", description="Test plist3", entries=[ dict( sequence=10, action="deny", description="Test rule 10", prefix="2001:db8:1000::/36", le=36, ), dict( sequence=20, action="permit", description="Test rule 20", prefix="2001:db8:2000::/36", ), ], ), dict( name="plist4", entries=[ dict( sequence=20, action="permit", prefix="2001:db8:3000::/36", ), dict( sequence=50, action="deny", prefix="2001:db8:4000::/36", ), ], ), ], ), ], state="merged", ) ) commands = [ "set policy prefix-list plist1", "set policy prefix-list plist1 description 'Test plist1'", "set policy prefix-list plist1 rule 10", "set policy prefix-list plist1 rule 10 action 'permit'", "set policy prefix-list plist1 rule 10 description 'Test rule 10'", "set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26'", "set policy prefix-list plist1 rule 20", "set policy prefix-list plist1 rule 20 action 'deny'", "set policy prefix-list plist1 rule 20 description 'Test rule 20'", "set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24'", "set policy prefix-list plist2", "set policy prefix-list plist2 rule 20", "set policy prefix-list plist2 rule 20 action 'permit'", "set policy prefix-list plist2 rule 20 le '32'", "set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26'", "set policy prefix-list plist2 rule 30", "set policy prefix-list plist2 rule 30 action 'deny'", "set policy prefix-list plist2 rule 30 ge '25'", "set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24'", "set policy prefix-list6 plist3", "set policy prefix-list6 plist3 description 'Test plist3'", "set policy prefix-list6 plist3 rule 10", "set policy prefix-list6 plist3 rule 10 action 'deny'", "set policy prefix-list6 plist3 rule 10 description 'Test rule 10'", "set policy prefix-list6 plist3 rule 10 le '36'", "set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36'", "set policy prefix-list6 plist3 rule 20", "set policy prefix-list6 plist3 rule 20 action 'permit'", "set policy prefix-list6 plist3 rule 20 description 'Test rule 20'", "set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36'", "set policy prefix-list6 plist4", "set policy prefix-list6 plist4 rule 20", "set policy prefix-list6 plist4 rule 20 action 'permit'", "set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36'", "set policy prefix-list6 plist4 rule 50", "set policy prefix-list6 plist4 rule 50 action 'deny'", "set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36'", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test merged for linear attributes (idempotent) def test_vyos_prefix_lists_linear_merged_idempotent(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args( dict( config=[ dict( afi="ipv4", prefix_lists=[ dict( name="plist1", description="Test plist1", entries=[ dict( sequence=10, action="permit", description="Test rule 10", prefix="92.168.10.0/26", ), dict( sequence=20, action="deny", description="Test rule 20", prefix="72.168.2.0/24", ), ], ), dict( name="plist2", entries=[ dict( sequence=20, action="permit", prefix="82.168.10.0/26", le=32, ), dict( sequence=30, action="deny", prefix="62.168.2.0/24", ge=25, ), ], ), ], ), dict( afi="ipv6", prefix_lists=[ dict( name="plist3", description="Test plist3", entries=[ dict( sequence=10, action="deny", description="Test rule 10", prefix="2001:db8:1000::/36", le=36, ), dict( sequence=20, action="permit", description="Test rule 20", prefix="2001:db8:2000::/36", ), ], ), dict( name="plist4", entries=[ dict( sequence=20, action="permit", prefix="2001:db8:3000::/36", ), dict( sequence=50, action="deny", prefix="2001:db8:4000::/36", ), ], ), ], ), ], state="merged", ) ) result = self.execute_module(changed=False) self.assertEqual(result["commands"], []) # test existing rule with replaced def test_vyos_prefix_lists_replaced_update(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args( dict( config=[ dict( afi="ipv4", prefix_lists=[ dict( name="plist1", description="Test plist1", entries=[ dict( sequence=10, action="permit", prefix="82.168.10.0/26", ), dict( sequence=20, action="deny", description="Test rule 20", prefix="72.168.2.0/24", ), ], ) ], ) ], state="replaced", ) ) commands = [ "delete policy prefix-list plist1 rule 10 description 'Test rule 10'", "set policy prefix-list plist1 rule 10 prefix '82.168.10.0/26'", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test replaced def test_vyos_prefix_lists_replaced(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args( dict( config=[ dict( afi="ipv4", prefix_lists=[ dict( name="plist1", entries=[ dict( sequence=10, action="permit", prefix="82.168.10.0/26", ) ], ) ], ) ], state="replaced", ) ) commands = [ "delete policy prefix-list plist1 description 'Test plist1'", "set policy prefix-list plist1 rule 10 prefix '82.168.10.0/26'", "delete policy prefix-list plist1 rule 20", "delete policy prefix-list plist1 rule 10 description 'Test rule 10'", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test update with overridden def test_vyos_prefix_lists_overridden_update(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args( dict( config=[ dict( afi="ipv4", prefix_lists=[ dict( name="plist1", entries=[ dict( sequence=10, action="deny", prefix="102.168.10.0/26", ) ], ) ], ) ], state="overridden", ) ) commands = [ "delete policy prefix-list plist1 description 'Test plist1'", "delete policy prefix-list6 plist4", "delete policy prefix-list plist1 rule 10 description 'Test rule 10'", "set policy prefix-list plist1 rule 10 prefix '102.168.10.0/26'", "delete policy prefix-list6 plist3", "delete policy prefix-list plist1 rule 20", "set policy prefix-list plist1 rule 10 action 'deny'", "delete policy prefix-list plist2", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test overridden def test_vyos_prefix_lists_overridden(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args( dict( config=[ dict( afi="ipv4", prefix_lists=[ dict( name="plist5", entries=[ dict( sequence=50, action="permit", prefix="102.168.10.0/26", ) ], ) ], ) ], state="overridden", ) ) commands = [ "set policy prefix-list plist5", "set policy prefix-list plist5 rule 50", "set policy prefix-list plist5 rule 50 action 'permit'", "set policy prefix-list plist5 rule 50 prefix '102.168.10.0/26'", "delete policy prefix-list plist1", "delete policy prefix-list plist2", "delete policy prefix-list6 plist3", "delete policy prefix-list6 plist4", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test deleted (all) def test_vyos_prefix_lists_deleted_all(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args(dict(state="deleted")) commands = [ "delete policy prefix-list plist1", "delete policy prefix-list plist2", "delete policy prefix-list6 plist3", "delete policy prefix-list6 plist4", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test deleted (AFI) def test_vyos_prefix_lists_deleted_afi(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args(dict(config=[dict(afi="ipv4")], state="deleted")) commands = [ "delete policy prefix-list plist1", "delete policy prefix-list plist2", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test deleted (one prefix-list) def test_vyos_prefix_lists_deleted_one(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args( dict( config=[dict(afi="ipv6", prefix_lists=[dict(name="plist3")])], state="deleted", ) ) commands = ["delete policy prefix-list6 plist3"] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test deleted (one prefix-list from each AFI) def test_vyos_prefix_lists_deleted_one_from_each_afi(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args( dict( config=[ dict(afi="ipv4", prefix_lists=[dict(name="plist2")]), dict(afi="ipv6", prefix_lists=[dict(name="plist3")]), ], state="deleted", ) ) commands = [ "delete policy prefix-list plist2", "delete policy prefix-list6 plist3", ] result = self.execute_module(changed=True) self.assertEqual(set(result["commands"]), set(commands)) # test parsed def test_vyos_prefix_lists_parsed(self): cfg = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args(dict(running_config=cfg, state="parsed")) parsed = [ { "afi": "ipv4", "prefix_lists": [ { "description": "Test plist1", "name": "plist1", "entries": [ { "action": "permit", "description": "Test rule 10", "sequence": 10, "prefix": "92.168.10.0/26", }, { "action": "deny", "description": "Test rule 20", "sequence": 20, "prefix": "72.168.2.0/24", }, ], }, { "name": "plist2", "entries": [ { "action": "permit", "sequence": 20, "le": 32, "prefix": "82.168.10.0/26", }, { "action": "deny", "ge": 25, "sequence": 30, "prefix": "62.168.2.0/24", }, ], }, ], }, { "afi": "ipv6", "prefix_lists": [ { "description": "Test plist3", "name": "plist3", "entries": [ { "action": "deny", "description": "Test rule 10", "sequence": 10, "le": 36, "prefix": "2001:db8:1000::/36", }, { "action": "permit", "description": "Test rule 20", "sequence": 20, "prefix": "2001:db8:2000::/36", }, ], }, { "name": "plist4", "entries": [ { "action": "permit", "sequence": 20, "prefix": "2001:db8:3000::/36", }, { "action": "deny", "sequence": 50, "prefix": "2001:db8:4000::/36", }, ], }, ], }, ] result = self.execute_module(changed=False) self.assertEqual(result["parsed"], parsed) # test rendered def test_vyos_prefix_lists_rendered(self): set_module_args( dict( config=[ dict( afi="ipv4", prefix_lists=[ dict( name="plist1", description="Test plist1", entries=[ dict( sequence=10, action="permit", description="Test rule 10", prefix="92.168.10.0/26", ), dict( sequence=20, action="deny", description="Test rule 20", prefix="72.168.2.0/24", ), ], ), dict( name="plist2", entries=[ dict( sequence=20, action="permit", prefix="82.168.10.0/26", le=32, ), dict( sequence=30, action="deny", prefix="62.168.2.0/24", ge=25, ), ], ), ], ), dict( afi="ipv6", prefix_lists=[ dict( name="plist3", description="Test plist3", entries=[ dict( sequence=10, action="deny", description="Test rule 10", prefix="2001:db8:1000::/36", le=36, ), dict( sequence=20, action="permit", description="Test rule 20", prefix="2001:db8:2000::/36", ), ], ), dict( name="plist4", entries=[ dict( sequence=20, action="permit", prefix="2001:db8:3000::/36", ), dict( sequence=50, action="deny", prefix="2001:db8:4000::/36", ), ], ), ], ), ], state="rendered", ) ) rendered = [ "set policy prefix-list plist1", "set policy prefix-list plist1 description 'Test plist1'", "set policy prefix-list plist1 rule 10", "set policy prefix-list plist1 rule 10 action 'permit'", "set policy prefix-list plist1 rule 10 description 'Test rule 10'", "set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26'", "set policy prefix-list plist1 rule 20", "set policy prefix-list plist1 rule 20 action 'deny'", "set policy prefix-list plist1 rule 20 description 'Test rule 20'", "set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24'", "set policy prefix-list plist2", "set policy prefix-list plist2 rule 20", "set policy prefix-list plist2 rule 20 action 'permit'", "set policy prefix-list plist2 rule 20 le '32'", "set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26'", "set policy prefix-list plist2 rule 30", "set policy prefix-list plist2 rule 30 action 'deny'", "set policy prefix-list plist2 rule 30 ge '25'", "set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24'", "set policy prefix-list6 plist3", "set policy prefix-list6 plist3 description 'Test plist3'", "set policy prefix-list6 plist3 rule 10", "set policy prefix-list6 plist3 rule 10 action 'deny'", "set policy prefix-list6 plist3 rule 10 description 'Test rule 10'", "set policy prefix-list6 plist3 rule 10 le '36'", "set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36'", "set policy prefix-list6 plist3 rule 20", "set policy prefix-list6 plist3 rule 20 action 'permit'", "set policy prefix-list6 plist3 rule 20 description 'Test rule 20'", "set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36'", "set policy prefix-list6 plist4", "set policy prefix-list6 plist4 rule 20", "set policy prefix-list6 plist4 rule 20 action 'permit'", "set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36'", "set policy prefix-list6 plist4 rule 50", "set policy prefix-list6 plist4 rule 50 action 'deny'", "set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36'", ] result = self.execute_module(changed=False) self.assertEqual(set(result["rendered"]), set(rendered)) # test gathered def test_vyos_prefix_lists_gathered(self): self.get_config.return_value = dedent( """\ set policy prefix-list plist1 set policy prefix-list plist1 description 'Test plist1' set policy prefix-list plist1 rule 10 set policy prefix-list plist1 rule 10 action 'permit' set policy prefix-list plist1 rule 10 description 'Test rule 10' set policy prefix-list plist1 rule 10 prefix '92.168.10.0/26' set policy prefix-list plist1 rule 20 set policy prefix-list plist1 rule 20 action 'deny' set policy prefix-list plist1 rule 20 description 'Test rule 20' set policy prefix-list plist1 rule 20 prefix '72.168.2.0/24' set policy prefix-list plist2 set policy prefix-list plist2 rule 20 set policy prefix-list plist2 rule 20 action 'permit' set policy prefix-list plist2 rule 20 le '32' set policy prefix-list plist2 rule 20 prefix '82.168.10.0/26' set policy prefix-list plist2 rule 30 set policy prefix-list plist2 rule 30 action 'deny' set policy prefix-list plist2 rule 30 ge '25' set policy prefix-list plist2 rule 30 prefix '62.168.2.0/24' set policy prefix-list6 plist3 set policy prefix-list6 plist3 description 'Test plist3' set policy prefix-list6 plist3 rule 10 set policy prefix-list6 plist3 rule 10 action 'deny' set policy prefix-list6 plist3 rule 10 description 'Test rule 10' set policy prefix-list6 plist3 rule 10 le '36' set policy prefix-list6 plist3 rule 10 prefix '2001:db8:1000::/36' set policy prefix-list6 plist3 rule 20 set policy prefix-list6 plist3 rule 20 action 'permit' set policy prefix-list6 plist3 rule 20 description 'Test rule 20' set policy prefix-list6 plist3 rule 20 prefix '2001:db8:2000::/36' set policy prefix-list6 plist4 set policy prefix-list6 plist4 rule 20 set policy prefix-list6 plist4 rule 20 action 'permit' set policy prefix-list6 plist4 rule 20 prefix '2001:db8:3000::/36' set policy prefix-list6 plist4 rule 50 set policy prefix-list6 plist4 rule 50 action 'deny' set policy prefix-list6 plist4 rule 50 prefix '2001:db8:4000::/36' """ ) set_module_args(dict(state="gathered")) gathered = [ { "afi": "ipv4", "prefix_lists": [ { "description": "Test plist1", "name": "plist1", "entries": [ { "action": "permit", "description": "Test rule 10", "sequence": 10, "prefix": "92.168.10.0/26", }, { "action": "deny", "description": "Test rule 20", "sequence": 20, "prefix": "72.168.2.0/24", }, ], }, { "name": "plist2", "entries": [ { "action": "permit", "sequence": 20, "le": 32, "prefix": "82.168.10.0/26", }, { "action": "deny", "ge": 25, "sequence": 30, "prefix": "62.168.2.0/24", }, ], }, ], }, { "afi": "ipv6", "prefix_lists": [ { "description": "Test plist3", "name": "plist3", "entries": [ { "action": "deny", "description": "Test rule 10", "sequence": 10, "le": 36, "prefix": "2001:db8:1000::/36", }, { "action": "permit", "description": "Test rule 20", "sequence": 20, "prefix": "2001:db8:2000::/36", }, ], }, { "name": "plist4", "entries": [ { "action": "permit", "sequence": 20, "prefix": "2001:db8:3000::/36", }, { "action": "deny", "sequence": 50, "prefix": "2001:db8:4000::/36", }, ], }, ], }, ] result = self.execute_module(changed=False) self.assertEqual(result["gathered"], gathered) diff --git a/tests/unit/modules/network/vyos/test_vyos_route_maps.py b/tests/unit/modules/network/vyos/test_vyos_route_maps.py index 6b50ad5..9d3c71a 100644 --- a/tests/unit/modules/network/vyos/test_vyos_route_maps.py +++ b/tests/unit/modules/network/vyos/test_vyos_route_maps.py @@ -1,584 +1,582 @@ # (c) 2021 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 ansible_collections.vyos.vyos.plugins.modules import vyos_route_maps from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosRouteMapsModule(TestVyosModule): - module = vyos_route_maps def setUp(self): super(TestVyosRouteMapsModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.route_maps.route_maps.Route_mapsFacts.get_config" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosRouteMapsModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_execute_show_command.stop() def load_fixtures(self, commands=None, filename=None): if filename is None: filename = "vyos_route_maps_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def test_vyos_route_maps_merged_idempotent(self): set_module_args( dict( config=[ dict( route_map="test3", entries=[ dict( sequence=1, action="permit", match=dict( rpki="invalid", interface="eth2", metric=1, peer="1.1.1.2", ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), ), set=dict( ipv6_next_hop=dict( ip_type="global", value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.20", local_preference=4, metric=5, metric_type="type-1", origin="egp", originator_id="10.0.2.3", src="10.0.2.15", tag=5, weight=4, ), ) ], ) ], state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_route_maps_merged(self): set_module_args( dict( config=[ dict( route_map="test2", entries=[ dict( sequence=1, action="permit", match=dict( rpki="invalid", interface="eth2", metric=1, peer="1.1.1.3", ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), ), set=dict( ipv6_next_hop=dict( ip_type="global", value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.22", large_community="10:20:21", local_preference=4, metric=5, metric_type="type-2", origin="egp", originator_id="10.0.2.2", src="10.0.2.15", tag=4, weight=4, ), ) ], ) ], state="merged", ) ) commands = [ "set policy route-map test2 rule 1 action permit", "set policy route-map test2 rule 1 set bgp-extcommunity-rt 22:11", "set policy route-map test2 rule 1 set ip-next-hop 10.20.10.22", "set policy route-map test2 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", "set policy route-map test2 rule 1 set large-community 10:20:21", "set policy route-map test2 rule 1 set local-preference 4", "set policy route-map test2 rule 1 set metric 5", "set policy route-map test2 rule 1 set metric-type type-2", "set policy route-map test2 rule 1 set origin egp", "set policy route-map test2 rule 1 set originator-id 10.0.2.2", "set policy route-map test2 rule 1 set src 10.0.2.15", "set policy route-map test2 rule 1 set tag 4", "set policy route-map test2 rule 1 set weight 4", "set policy route-map test2 rule 1 set community internet", "set policy route-map test2 rule 1 match interface eth2", "set policy route-map test2 rule 1 match metric 1", "set policy route-map test2 rule 1 match peer 1.1.1.3", "set policy route-map test2 rule 1 match ipv6 nexthop fdda:5cc1:23:4::1f", "set policy route-map test2 rule 1 match rpki invalid", ] self.execute_module(changed=True, commands=commands) def test_route_maps_replaced(self): set_module_args( dict( config=[ dict( route_map="test3", entries=[ dict( sequence=1, action="permit", match=dict( rpki="invalid", metric=1, peer="1.1.1.3", ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), ), set=dict( ipv6_next_hop=dict( ip_type="global", value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.22", large_community="10:20:21", local_preference=4, metric=5, metric_type="type-2", origin="egp", originator_id="10.0.2.2", src="10.0.2.15", tag=4, weight=4, ), ) ], ), ], state="replaced", ) ) commands = [ "delete policy route-map test3 rule 1 match interface eth2", "set policy route-map test3 rule 1 set ip-next-hop 10.20.10.22", "set policy route-map test3 rule 1 set large-community 10:20:21", "set policy route-map test3 rule 1 set metric-type type-2", "set policy route-map test3 rule 1 set originator-id 10.0.2.2", "set policy route-map test3 rule 1 set tag 4", "set policy route-map test3 rule 1 match peer 1.1.1.3", ] self.execute_module(changed=True, commands=commands) def test_vyos_route_maps_replaced_idempotent(self): set_module_args( dict( config=[ dict( route_map="test3", entries=[ dict( sequence=1, action="permit", match=dict( rpki="invalid", interface="eth2", metric=1, peer="1.1.1.2", ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), ), set=dict( ipv6_next_hop=dict( ip_type="global", value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.20", local_preference=4, metric=5, metric_type="type-1", origin="egp", originator_id="10.0.2.3", src="10.0.2.15", tag=5, weight=4, ), ) ], ), ], state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_route_maps_overridden(self): set_module_args( dict( config=[ dict( route_map="test2", entries=[ dict( sequence=1, action="permit", match=dict(rpki="invalid", peer="1.1.1.3"), set=dict( ipv6_next_hop=dict( ip_type="global", value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.22", large_community="10:20:21", local_preference=4, metric=5, metric_type="type-2", origin="egp", originator_id="10.0.2.2", src="10.0.2.15", tag=4, weight=4, ), ) ], ) ], state="overridden", ) ) commands = [ "delete policy route-map test3", "set policy route-map test2 rule 1 action permit", "set policy route-map test2 rule 1 set bgp-extcommunity-rt 22:11", "set policy route-map test2 rule 1 set ip-next-hop 10.20.10.22", "set policy route-map test2 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", "set policy route-map test2 rule 1 set large-community 10:20:21", "set policy route-map test2 rule 1 set local-preference 4", "set policy route-map test2 rule 1 set metric 5", "set policy route-map test2 rule 1 set metric-type type-2", "set policy route-map test2 rule 1 set origin egp", "set policy route-map test2 rule 1 set originator-id 10.0.2.2", "set policy route-map test2 rule 1 set src 10.0.2.15", "set policy route-map test2 rule 1 set tag 4", "set policy route-map test2 rule 1 set weight 4", "set policy route-map test2 rule 1 set community internet", "set policy route-map test2 rule 1 match peer 1.1.1.3", "set policy route-map test2 rule 1 match rpki invalid", ] self.execute_module(changed=True, commands=commands) def test_vyos_route_maps_overridden_idempotent(self): set_module_args( dict( config=[ dict( route_map="test3", entries=[ dict( sequence=1, action="permit", match=dict( rpki="invalid", interface="eth2", metric=1, peer="1.1.1.2", ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), ), set=dict( ipv6_next_hop=dict( ip_type="global", value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.20", local_preference=4, metric=5, metric_type="type-1", origin="egp", originator_id="10.0.2.3", src="10.0.2.15", tag=5, weight=4, ), ) ], ), ], state="overridden", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_route_maps_rendered(self): set_module_args( dict( config=[ dict( route_map="test3", entries=[ dict( sequence=1, action="permit", match=dict( rpki="invalid", interface="eth2", metric=1, peer="1.1.1.2", ipv6=dict(next_hop="fdda:5cc1:23:4::1f"), ), set=dict( ipv6_next_hop=dict( ip_type="global", value="fdda:5cc1:23:4::1f", ), community=dict(value="internet"), bgp_extcommunity_rt="22:11", ip_next_hop="10.20.10.20", local_preference=4, metric=5, metric_type="type-1", origin="egp", originator_id="10.0.2.3", src="10.0.2.15", tag=5, weight=4, ), ) ], ), dict( route_map="test1", entries=[ dict( sequence=1, action="permit", description="test", on_match=dict(next=True), ), dict( sequence=2, action="permit", on_match=dict(goto=4), ), ], ), ], state="rendered", ) ) rendered_cmds = [ "set policy route-map test3 rule 1 action permit", "set policy route-map test3 rule 1 set bgp-extcommunity-rt 22:11", "set policy route-map test3 rule 1 set ip-next-hop 10.20.10.20", "set policy route-map test3 rule 1 set ipv6-next-hop global fdda:5cc1:23:4::1f", "set policy route-map test3 rule 1 set local-preference 4", "set policy route-map test3 rule 1 set metric 5", "set policy route-map test3 rule 1 set metric-type type-1", "set policy route-map test3 rule 1 set origin egp", "set policy route-map test3 rule 1 set originator-id 10.0.2.3", "set policy route-map test3 rule 1 set src 10.0.2.15", "set policy route-map test3 rule 1 set tag 5", "set policy route-map test3 rule 1 set weight 4", "set policy route-map test3 rule 1 set community internet", "set policy route-map test3 rule 1 match interface eth2", "set policy route-map test3 rule 1 match metric 1", "set policy route-map test3 rule 1 match peer 1.1.1.2", "set policy route-map test3 rule 1 match ipv6 nexthop fdda:5cc1:23:4::1f", "set policy route-map test3 rule 1 match rpki invalid", "set policy route-map test1 rule 1 description test", "set policy route-map test1 rule 1 action permit", "set policy route-map test1 rule 1 on-match next", "set policy route-map test1 rule 2 action permit", "set policy route-map test1 rule 2 on-match goto 4", ] result = self.execute_module(changed=False) self.assertEqual( sorted(result["rendered"]), sorted(rendered_cmds), result["rendered"], ) def test_yos_route_maps_parsed(self): - parsed_str = ( "set policy route-map test3 rule 1 action 'permit'" "\nset policy route-map test3 rule 1 match interface 'eth2'\nset policy route-map test3 rule 1 match ipv6 nexthop" " 'fdda:5cc1:23:4::1f'\nset policy route-map test3 rule 1 match metric '1'\nset policy route-map test3 rule 1 match peer " "'1.1.1.2'\nset policy route-map test3 rule 1 match rpki 'invalid'\nset policy route-map test3 rule 1 set bgp-extcommunity-rt " "'22:11'\nset policy route-map test3 rule 1 set community 'internet'\nset policy route-map test3 rule 1 set ipv6-next-hop global" " 'fdda:5cc1:23:4::1f'\nset policy route-map test3 rule 1 set ip-next-hop '10.20.10.20'\nset policy route-map " "test3 rule 1 set local-preference '4'\nset policy route-map test3 rule 1 set metric '5'\nset policy route-map test3 " "rule 1 set metric-type 'type-1'\nset policy route-map test3 rule 1 set origin 'egp'\nset policy route-map test3 rule 1 set originator-id " "'10.0.2.3'\nset policy route-map test3 rule 1 set src '10.0.2.15'" "\nset policy route-map test3 rule 1 set tag '5'\nset policy route-map test3 rule 1 set weight '4'" ) set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = [ { "entries": [ { "action": "permit", "match": { "interface": "eth2", "ipv6": {"next_hop": "fdda:5cc1:23:4::1f"}, "metric": 1, "peer": "1.1.1.2", "rpki": "invalid", }, "sequence": 1, "set": { "bgp_extcommunity_rt": "22:11", "community": {"value": "internet"}, "ip_next_hop": "10.20.10.20", "ipv6_next_hop": { "ip_type": "global", "value": "fdda:5cc1:23:4::1f", }, "local_preference": "4", "metric": "5", "metric_type": "type-1", "origin": "egp", "originator_id": "10.0.2.3", "src": "10.0.2.15", "tag": "5", "weight": "4", }, } ], "route_map": "test3", }, ] self.assertEqual(parsed_list, result["parsed"]) def test_vyos_route_maps_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False) gathered_list = [ { "entries": [ { "action": "permit", "match": { "interface": "eth2", "ipv6": {"next_hop": "fdda:5cc1:23:4::1f"}, "metric": 1, "peer": "1.1.1.2", "rpki": "invalid", }, "sequence": 1, "set": { "bgp_extcommunity_rt": "22:11", "community": {"value": "internet"}, "ip_next_hop": "10.20.10.20", "ipv6_next_hop": { "ip_type": "global", "value": "fdda:5cc1:23:4::1f", }, "local_preference": "4", "metric": "5", "metric_type": "type-1", "origin": "egp", "originator_id": "10.0.2.3", "src": "10.0.2.15", "tag": "5", "weight": "4", }, } ], "route_map": "test3", }, ] self.assertEqual(gathered_list, result["gathered"]) def test_vyos_route_maps_deleted(self): set_module_args( dict( config=[ dict( route_map="test3", entries=[ dict( sequence=1, action="permit", match=dict( rpki="invalid", interface="eth2", ), set=dict( origin="egp", originator_id="10.0.2.3", src="10.0.2.15", tag=5, weight=4, ), ) ], ), ], state="deleted", ) ) commands = ["delete policy route-map test3"] self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_snmp_server.py b/tests/unit/modules/network/vyos/test_vyos_snmp_server.py index 60a0746..73e29f6 100644 --- a/tests/unit/modules/network/vyos/test_vyos_snmp_server.py +++ b/tests/unit/modules/network/vyos/test_vyos_snmp_server.py @@ -1,518 +1,517 @@ # (c) 2021 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 ansible_collections.vyos.vyos.plugins.modules import vyos_snmp_server from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosSnmpServerModule(TestVyosModule): - module = vyos_snmp_server def setUp(self): super(TestVyosSnmpServerModule, self).setUp() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection" ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.snmp_server.snmp_server.Snmp_serverFacts.get_config" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosSnmpServerModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_execute_show_command.stop() def load_fixtures(self, commands=None, filename=None): if filename is None: filename = "vyos_snmp_server_config.cfg" def load_from_file(*args, **kwargs): output = load_fixture(filename) return output self.execute_show_command.side_effect = load_from_file def test_snmp_server_merged_idempotent(self): set_module_args( dict( config=dict( communities=[ dict( name="bridges", networks=["12.1.1.0/24", "1.1.1.0/24"], ) ], listen_addresses=[ dict(address="100.1.2.1", port=33), ], location="RDU, NC", snmp_v3=dict( users=[ dict( user="admin_user", authentication=dict(type="sha", plaintext_key="abc1234567"), privacy=dict(type="aes", plaintext_key="abc1234567"), ), dict( user="guest_user", authentication=dict(type="sha", plaintext_key="opq1234567"), privacy=dict(type="aes", plaintext_key="opq1234567"), ), ] ), ), state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_snmp_server_replaced_idempotent(self): set_module_args( dict( config=dict( communities=[ dict( name="bridges", networks=["12.1.1.0/24", "1.1.1.0/24"], ) ], listen_addresses=[ dict(address="100.1.2.1", port=33), ], location="RDU, NC", snmp_v3=dict( users=[ dict( user="admin_user", authentication=dict(type="sha", plaintext_key="abc1234567"), privacy=dict(type="aes", plaintext_key="abc1234567"), ), dict( user="guest_user", authentication=dict(type="sha", plaintext_key="opq1234567"), privacy=dict(type="aes", plaintext_key="opq1234567"), ), ] ), ), state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_snmp_server_overridden_idempotent(self): set_module_args( dict( config=dict( communities=[ dict( name="bridges", networks=["12.1.1.0/24", "1.1.1.0/24"], ) ], listen_addresses=[ dict(address="100.1.2.1", port=33), ], location="RDU, NC", snmp_v3=dict( users=[ dict( user="admin_user", authentication=dict(type="sha", plaintext_key="abc1234567"), privacy=dict(type="aes", plaintext_key="abc1234567"), ), dict( user="guest_user", authentication=dict(type="sha", plaintext_key="opq1234567"), privacy=dict(type="aes", plaintext_key="opq1234567"), ), ] ), ), state="overridden", ) ) self.execute_module(changed=False, commands=[]) def test_snmp_server_merged(self): set_module_args( dict( config=dict( communities=[ dict( name="routers", clients=["12.1.1.0/24", "1.1.1.0/24"], authorization_type="rw", ), dict(name="switches", authorization_type="ro"), ], contact="admin@example.com", description="snmp_config", smux_peer="peer1", trap_source="1.1.1.1", trap_target=dict(address="10.10.1.1", community="switches", port="80"), snmp_v3=dict( engine_id="34", groups=[ dict( group="default", mode="rw", seclevel="priv", view="view1", ) ], trap_targets=[ dict( address="20.12.1.1", authentication=dict(type="sha", plaintext_key="abc1234567"), privacy=dict(type="aes", plaintext_key="abc1234567"), ), ], ), ), state="merged", ) ) commands = [ "set service snmp community routers client 1.1.1.0/24", "set service snmp community routers client 12.1.1.0/24", "set service snmp community routers authorization rw", "set service snmp community switches authorization ro", "set service snmp v3 group default mode rw", "set service snmp v3 group default seclevel priv", "set service snmp v3 group default view view1", "set service snmp v3 engineid 34", "set service snmp contact admin@example.com", "set service snmp description snmp_config", "set service snmp smux-peer peer1", "set service snmp trap-source 1.1.1.1", "set service snmp trap-target 10.10.1.1", ] self.execute_module(changed=True, commands=commands) def test_snmp_server_replaced(self): set_module_args( dict( config=dict( communities=[ dict( name="routers", clients=["12.1.1.0/24", "1.1.1.0/24"], authorization_type="rw", ), dict(name="switches", authorization_type="ro"), ], contact="admin@example.com", description="snmp_config", smux_peer="peer1", trap_source="1.1.1.1", trap_target=dict(address="10.10.1.1", community="switches", port="80"), snmp_v3=dict( engine_id="34", groups=[ dict( group="default", mode="rw", seclevel="priv", view="view1", ) ], trap_targets=[ dict( address="20.12.1.1", authentication=dict(type="sha", plaintext_key="abc1234567"), privacy=dict(type="aes", plaintext_key="abc1234567"), ), ], ), ), state="replaced", ) ) commands = [ "set service snmp community routers client 1.1.1.0/24", "set service snmp community routers client 12.1.1.0/24", "set service snmp community routers authorization rw", "set service snmp community switches authorization ro", "delete service snmp community bridges network 1.1.1.0/24", "delete service snmp community bridges network 12.1.1.0/24", "delete service snmp listen-address 100.1.2.1 port 33", "set service snmp v3 group default mode rw", "set service snmp v3 group default seclevel priv", "set service snmp v3 group default view view1", "delete service snmp v3 user admin_user auth type sha", "delete service snmp v3 user admin_user auth plaintext-key abc1234567", "delete service snmp v3 user admin_user privacy type aes", "delete service snmp v3 user admin_user privacy plaintext-key abc1234567", "delete service snmp v3 user guest_user auth type sha", "delete service snmp v3 user guest_user auth plaintext-key opq1234567", "delete service snmp v3 user guest_user privacy type aes", "delete service snmp v3 user guest_user privacy plaintext-key opq1234567", "set service snmp v3 engineid 34", "set service snmp contact admin@example.com", "set service snmp description snmp_config", "set service snmp smux-peer peer1", "set service snmp trap-source 1.1.1.1", "set service snmp trap-target 10.10.1.1", "delete service snmp location 'RDU, NC'", ] self.execute_module(changed=True, commands=commands) def test_snmp_server_overridden(self): set_module_args( dict( config=dict( communities=[ dict( name="routers", clients=["12.1.1.0/24", "1.1.1.0/24"], authorization_type="rw", ), dict(name="switches", authorization_type="ro"), ], contact="admin@example.com", description="snmp_config", smux_peer="peer1", trap_source="1.1.1.1", trap_target=dict(address="10.10.1.1", community="switches", port="80"), snmp_v3=dict( engine_id="34", groups=[ dict( group="default", mode="rw", seclevel="priv", view="view1", ) ], trap_targets=[ dict( address="20.12.1.1", authentication=dict(type="sha", plaintext_key="abc1234567"), privacy=dict(type="aes", plaintext_key="abc1234567"), ), ], ), ), state="overridden", ) ) commands = [ "set service snmp community routers client 1.1.1.0/24", "set service snmp community routers client 12.1.1.0/24", "set service snmp community routers authorization rw", "set service snmp community switches authorization ro", "delete service snmp community bridges network 1.1.1.0/24", "delete service snmp community bridges network 12.1.1.0/24", "delete service snmp listen-address 100.1.2.1 port 33", "set service snmp v3 group default mode rw", "set service snmp v3 group default seclevel priv", "set service snmp v3 group default view view1", "delete service snmp v3 user admin_user auth type sha", "delete service snmp v3 user admin_user auth plaintext-key abc1234567", "delete service snmp v3 user admin_user privacy type aes", "delete service snmp v3 user admin_user privacy plaintext-key abc1234567", "delete service snmp v3 user guest_user auth type sha", "delete service snmp v3 user guest_user auth plaintext-key opq1234567", "delete service snmp v3 user guest_user privacy type aes", "delete service snmp v3 user guest_user privacy plaintext-key opq1234567", "set service snmp v3 engineid 34", "set service snmp contact admin@example.com", "set service snmp description snmp_config", "set service snmp smux-peer peer1", "set service snmp trap-source 1.1.1.1", "set service snmp trap-target 10.10.1.1", "delete service snmp location 'RDU, NC'", ] self.execute_module(changed=True, commands=commands) def test_snmp_server_deleted(self): set_module_args( dict( state="deleted", ) ) commands = ["delete service snmp"] self.execute_module(changed=True, commands=commands) def test_snmp_server_rendered(self): set_module_args( dict( config=dict( communities=[ dict( name="routers", clients=["12.1.1.0/24", "1.1.1.0/24"], authorization_type="rw", ), dict(name="switches", authorization_type="ro"), ], contact="admin@example.com", description="snmp_config", smux_peer="peer1", trap_source="1.1.1.1", trap_target=dict(address="10.10.1.1", community="switches", port="80"), snmp_v3=dict( engine_id="34", groups=[ dict( group="default", mode="rw", seclevel="priv", view="view1", ) ], trap_targets=[ dict( address="20.12.1.1", authentication=dict(type="sha", plaintext_key="abc1234567"), privacy=dict(type="aes", plaintext_key="abc1234567"), ), ], ), ), state="rendered", ) ) commands = [ "set service snmp community routers client 1.1.1.0/24", "set service snmp community routers client 12.1.1.0/24", "set service snmp community routers authorization rw", "set service snmp community switches authorization ro", "set service snmp v3 group default mode rw", "set service snmp v3 group default seclevel priv", "set service snmp v3 group default view view1", "set service snmp v3 engineid 34", "set service snmp contact admin@example.com", "set service snmp description snmp_config", "set service snmp smux-peer peer1", "set service snmp trap-source 1.1.1.1", "set service snmp trap-target 10.10.1.1", ] result = self.execute_module(changed=False) self.assertEqual( sorted(result["rendered"]), sorted(commands), result["rendered"], ) def test_snmp_server_parsed(self): commands = [ "set service snmp community routers client 1.1.1.0/24", "set service snmp community routers client 12.1.1.0/24", "set service snmp community routers authorization rw", "set service snmp community switches authorization ro", "set service snmp v3 group default mode rw", "set service snmp v3 group default seclevel priv", "set service snmp v3 group default view view1", "set service snmp v3 engineid 34", "set service snmp contact admin@example.com", "set service snmp description snmp_config", "set service snmp smux-peer peer1", "set service snmp trap-source 1.1.1.1", "set service snmp trap-target 10.10.1.1", ] parsed_str = "\n".join(commands) set_module_args(dict(running_config=parsed_str, state="parsed")) result = self.execute_module(changed=False) parsed_list = { "communities": [ { "authorization_type": "rw", "clients": ["1.1.1.0/24", "12.1.1.0/24"], "name": "routers", }, {"authorization_type": "ro", "name": "switches"}, ], "contact": "admin@example.com", "description": "snmp_config", "smux_peer": "peer1", "snmp_v3": { "engine_id": "34", "groups": [ { "group": "default", "mode": "rw", "seclevel": "priv", "view": "view1", } ], }, "trap_source": "1.1.1.1", "trap_target": {"address": "10.10.1.1"}, } self.assertEqual(parsed_list, result["parsed"]) def test_snmp_server_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False) gathered_list = { "communities": [ {"name": "bridges", "networks": ["1.1.1.0/24", "12.1.1.0/24"]}, ], "listen_addresses": [{"address": "100.1.2.1", "port": 33}], "location": "RDU, NC", "snmp_v3": { "users": [ { "authentication": { "plaintext_key": "abc1234567", "type": "sha", }, "privacy": { "plaintext_key": "abc1234567", "type": "aes", }, "user": "admin_user", }, { "authentication": { "plaintext_key": "opq1234567", "type": "sha", }, "privacy": { "plaintext_key": "opq1234567", "type": "aes", }, "user": "guest_user", }, ] }, } self.assertEqual(gathered_list, result["gathered"]) diff --git a/tests/unit/modules/network/vyos/test_vyos_static_routes.py b/tests/unit/modules/network/vyos/test_vyos_static_routes.py index df25992..3479009 100644 --- a/tests/unit/modules/network/vyos/test_vyos_static_routes.py +++ b/tests/unit/modules/network/vyos/test_vyos_static_routes.py @@ -1,272 +1,271 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_static_routes from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosStaticRoutesModule(TestVyosModule): - module = vyos_static_routes def setUp(self): super(TestVyosStaticRoutesModule, self).setUp() self.mock_get_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.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_collections.ansible.netcommon.plugins.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_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data" ) self.execute_show_command = self.mock_execute_show_command.start() def tearDown(self): super(TestVyosStaticRoutesModule, 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, filename=None): def load_from_file(*args, **kwargs): return load_fixture("vyos_static_routes_config.cfg") self.execute_show_command.side_effect = load_from_file def test_vyos_static_routes_merged(self): set_module_args( dict( config=[ dict( address_families=[ dict( afi="ipv4", routes=[ dict( dest="192.0.2.48/28", next_hops=[ dict( forward_router_address="192.0.2.9", admin_distance=10, ), dict( forward_router_address="192.0.2.10", interface="eth0", ), ], ) ], ) ] ) ], state="merged", ) ) commands = [ "set protocols static route 192.0.2.48/28", "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'", "set protocols static route 192.0.2.48/28 next-hop 192.0.2.9 distance '10'", "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'", "set protocols static route 192.0.2.48/28 next-hop 192.0.2.10 next-hop-interface 'eth0'", ] self.execute_module(changed=True, commands=commands) def test_vyos_static_routes_merged_idempotent(self): set_module_args( dict( config=[ dict( address_families=[ dict( afi="ipv4", routes=[ dict( dest="192.0.2.32/28", next_hops=[ dict(forward_router_address="192.0.2.9"), dict(forward_router_address="192.0.2.10"), ], ) ], ) ] ) ], state="merged", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_static_routes_replaced(self): set_module_args( dict( config=[ dict( address_families=[ dict( afi="ipv4", routes=[ dict( dest="192.0.2.48/28", next_hops=[ dict( forward_router_address="192.0.2.9", interface="eth0", ), dict( forward_router_address="192.0.2.10", admin_distance=10, ), ], ) ], ) ] ) ], state="replaced", ) ) commands = [ "set protocols static route 192.0.2.48/28", "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'", "set protocols static route 192.0.2.48/28 next-hop 192.0.2.9 next-hop-interface 'eth0'", "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'", "set protocols static route 192.0.2.48/28 next-hop 192.0.2.10 distance '10'", ] self.execute_module(changed=True, commands=commands) def test_vyos_static_routes_replaced_idempotent(self): set_module_args( dict( config=[ dict( address_families=[ dict( afi="ipv4", routes=[ dict( dest="192.0.2.32/28", next_hops=[ dict(forward_router_address="192.0.2.9"), dict(forward_router_address="192.0.2.10"), ], ) ], ) ] ) ], state="replaced", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_static_routes_overridden(self): set_module_args( dict( config=[ dict( address_families=[ dict( afi="ipv4", routes=[ dict( dest="192.0.2.48/28", next_hops=[ dict(forward_router_address="192.0.2.9"), dict(forward_router_address="192.0.2.10"), ], ) ], ) ] ) ], state="overridden", ) ) commands = [ "delete protocols static route 192.0.2.32/28", "set protocols static route 192.0.2.48/28", "set protocols static route 192.0.2.48/28 next-hop '192.0.2.9'", "set protocols static route 192.0.2.48/28 next-hop '192.0.2.10'", ] self.execute_module(changed=True, commands=commands) def test_vyos_static_routes_overridden_idempotent(self): set_module_args( dict( config=[ dict( address_families=[ dict( afi="ipv4", routes=[ dict( dest="192.0.2.32/28", next_hops=[ dict(forward_router_address="192.0.2.9"), dict(forward_router_address="192.0.2.10"), ], ) ], ) ] ) ], state="overridden", ) ) self.execute_module(changed=False, commands=[]) def test_vyos_static_routes_deleted(self): set_module_args( dict( config=[dict(address_families=[dict(afi="ipv4")])], state="deleted", ) ) commands = ["delete protocols static route"] self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_system.py b/tests/unit/modules/network/vyos/test_vyos_system.py index 3665720..03ef3df 100644 --- a/tests/unit/modules/network/vyos/test_vyos_system.py +++ b/tests/unit/modules/network/vyos/test_vyos_system.py @@ -1,113 +1,112 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_system from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosSystemModule(TestVyosModule): - module = vyos_system def setUp(self): super(TestVyosSystemModule, self).setUp() self.mock_get_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_system.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_system.load_config" ) self.load_config = self.mock_load_config.start() def tearDown(self): super(TestVyosSystemModule, self).tearDown() self.mock_get_config.stop() self.mock_load_config.stop() def load_fixtures(self, commands=None, filename=None): self.get_config.return_value = load_fixture("vyos_config_config.cfg") def test_vyos_system_hostname(self): set_module_args(dict(host_name="foo")) commands = ["set system host-name 'foo'"] self.execute_module(changed=True, commands=commands) def test_vyos_system_clear_hostname(self): set_module_args(dict(host_name="foo", state="absent")) commands = ["delete system host-name"] self.execute_module(changed=True, commands=commands) def test_vyos_remove_single_name_server(self): set_module_args(dict(name_server=["8.8.4.4"], state="absent")) commands = ["delete system name-server '8.8.4.4'"] self.execute_module(changed=True, commands=commands) def test_vyos_system_domain_name(self): set_module_args(dict(domain_name="example2.com")) commands = ["set system domain-name 'example2.com'"] self.execute_module(changed=True, commands=commands) def test_vyos_system_clear_domain_name(self): set_module_args(dict(domain_name="example.com", state="absent")) commands = ["delete system domain-name"] self.execute_module(changed=True, commands=commands) def test_vyos_system_domain_search(self): set_module_args(dict(domain_search=["foo.example.com", "bar.example.com"])) commands = [ "set system domain-search domain 'foo.example.com'", "set system domain-search domain 'bar.example.com'", ] self.execute_module(changed=True, commands=commands) def test_vyos_system_clear_domain_search(self): set_module_args(dict(domain_search=[])) commands = ["delete system domain-search domain"] self.execute_module(changed=True, commands=commands) def test_vyos_system_no_change(self): set_module_args( dict( host_name="router", domain_name="example.com", name_server=["8.8.8.8", "8.8.4.4"], ) ) result = self.execute_module() self.assertEqual([], result["commands"]) def test_vyos_system_clear_all(self): set_module_args(dict(state="absent")) commands = [ "delete system host-name", "delete system domain-search domain", "delete system domain-name", "delete system name-server", ] self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_user.py b/tests/unit/modules/network/vyos/test_vyos_user.py index 5a1f69f..57639d1 100644 --- a/tests/unit/modules/network/vyos/test_vyos_user.py +++ b/tests/unit/modules/network/vyos/test_vyos_user.py @@ -1,130 +1,129 @@ # (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 ansible_collections.vyos.vyos.plugins.modules import vyos_user from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosUserModule(TestVyosModule): - module = vyos_user def setUp(self): super(TestVyosUserModule, self).setUp() self.mock_get_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_user.get_config" ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.vyos.vyos.plugins.modules.vyos_user.load_config" ) self.load_config = self.mock_load_config.start() def tearDown(self): super(TestVyosUserModule, self).tearDown() self.mock_get_config.stop() self.mock_load_config.stop() def load_fixtures(self, commands=None, filename=None): self.get_config.return_value = load_fixture("vyos_user_config.cfg") self.load_config.return_value = dict(diff=None, session="session") def test_vyos_user_password(self): set_module_args(dict(name="ansible", configured_password="test")) result = self.execute_module(changed=True) self.assertEqual( result["commands"], ["set system login user ansible authentication plaintext-password test"], ) def test_vyos_user_delete(self): set_module_args(dict(name="ansible", state="absent")) result = self.execute_module(changed=True) self.assertEqual(result["commands"], ["delete system login user ansible"]) def test_vyos_user_level(self): set_module_args(dict(name="ansible", level="operator")) result = self.execute_module(changed=True) self.assertEqual( result["commands"], ["set system login user ansible level operator"], ) def test_vyos_user_level_invalid(self): set_module_args(dict(name="ansible", level="sysadmin")) self.execute_module(failed=True) def test_vyos_user_purge(self): set_module_args(dict(purge=True)) result = self.execute_module(changed=True) self.assertEqual( sorted(result["commands"]), sorted( [ "delete system login user ansible", "delete system login user admin", ] ), ) def test_vyos_user_update_password_changed(self): set_module_args( dict( name="test", configured_password="test", update_password="on_create", ) ) result = self.execute_module(changed=True) self.assertEqual( result["commands"], ["set system login user test authentication plaintext-password test"], ) def test_vyos_user_update_password_on_create_ok(self): set_module_args( dict( name="ansible", configured_password="test", update_password="on_create", ) ) self.execute_module() def test_vyos_user_update_password_always(self): set_module_args( dict( name="ansible", configured_password="test", update_password="always", ) ) result = self.execute_module(changed=True) self.assertEqual( result["commands"], ["set system login user ansible authentication plaintext-password test"], ) diff --git a/tests/unit/modules/network/vyos/vyos_module.py b/tests/unit/modules/network/vyos/vyos_module.py index 98a422d..b1bdc2e 100644 --- a/tests/unit/modules/network/vyos/vyos_module.py +++ b/tests/unit/modules/network/vyos/vyos_module.py @@ -1,106 +1,105 @@ # (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 import json import os from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( AnsibleExitJson, AnsibleFailJson, ModuleTestCase, ) fixture_path = os.path.join(os.path.dirname(__file__), "fixtures") fixture_data = {} def load_fixture(name): path = os.path.join(fixture_path, name) if path in fixture_data: return fixture_data[path] with open(path) as f: data = f.read() try: data = json.loads(data) except Exception: pass fixture_data[path] = data return data class TestVyosModule(ModuleTestCase): def execute_module( self, failed=False, changed=False, commands=None, sort=True, defaults=False, filename=None, ): - if filename is None: self.load_fixtures(commands) else: self.load_fixtures(commands, filename=filename) if failed: result = self.failed() self.assertTrue(result["failed"], result) else: result = self.changed(changed) self.assertEqual(result["changed"], changed, result) if commands is not None: if sort: self.assertEqual( sorted(commands), sorted(result["commands"]), result["commands"], ) else: self.assertEqual(commands, result["commands"], result["commands"]) return result def failed(self): with self.assertRaises(AnsibleFailJson) as exc: self.module.main() result = exc.exception.args[0] self.assertTrue(result["failed"], result) return result def changed(self, changed=False): with self.assertRaises(AnsibleExitJson) as exc: self.module.main() result = exc.exception.args[0] self.assertEqual(result["changed"], changed, result) return result def load_fixtures(self, commands=None, filename=None): pass