diff --git a/changelogs/fragments/T7011-lldp-integration-tests.yaml b/changelogs/fragments/T7011-lldp-integration-tests.yaml new file mode 100644 index 00000000..8735e7cf --- /dev/null +++ b/changelogs/fragments/T7011-lldp-integration-tests.yaml @@ -0,0 +1,8 @@ +--- +breaking_changes: + - if "address" is available, merge will cause it to be added, in contrast to + the previous behavior where it was replaced. When used in replace mode, it + will remove any existing addresses and replace them with the new one. + +minor_changes: + - lldp_global address is now addresses, with appropriate coercion diff --git a/docs/vyos.vyos.vyos_lldp_global_module.rst b/docs/vyos.vyos.vyos_lldp_global_module.rst index d9e002a9..9e8758ce 100644 --- a/docs/vyos.vyos.vyos_lldp_global_module.rst +++ b/docs/vyos.vyos.vyos_lldp_global_module.rst @@ -1,576 +1,607 @@ .. _vyos.vyos.vyos_lldp_global_module: ************************** vyos.vyos.vyos_lldp_global ************************** **LLDP global resource module** Version added: 1.0.0 .. contents:: :local: :depth: 1 Synopsis -------- - This module manages link layer discovery protocol (LLDP) attributes on VyOS devices. Parameters ---------- .. raw:: html + + + + + +
Parameter Choices/Defaults Comments
config
dictionary
The provided link layer discovery protocol (LLDP) configuration.
address
string
-
This argument defines management-address.
+
Exactly one management address (exclusive with addresses). Deprecated in favor of addresses. To be removed in 7.0.0.
+
+
+ addresses + +
+ list + / elements=string +
+
+ +
One or more management addresses. The management address is used to identify the management interface of the system. Only addresses connected to the system will be transmitted.
enable
boolean
    Choices:
  • no
  • yes
This argument is a boolean value to enable or disable LLDP.
legacy_protocols
list / elements=string
    Choices:
  • cdp
  • edp
  • fdp
  • sonmp
List of the supported legacy protocols.
snmp
string
This argument enable the SNMP queries to LLDP database.
running_config
string
This option is used only with state parsed.
The value of this option should be the output received from the VyOS device by executing the command show configuration commands | grep lldp.
The state parsed reads the configuration from running_config option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the parsed key within the result.
state
string
    Choices:
  • merged ←
  • replaced
  • deleted
  • gathered
  • rendered
  • parsed
The state of the configuration after module completion.

Notes ----- .. note:: - Tested against VyOS 1.3.8 - This module works with connection ``ansible.netcommon.network_cli``. See `the VyOS OS Platform Options <../network/user_guide/platform_vyos.html>`_. Examples -------- .. code-block:: yaml # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lldp_global: config: legacy_protocols: - fdp - cdp snmp: enable - address: 192.0.2.11 + addresses: + - 192.0.2.11 state: merged # # # ------------------------ # Module Execution Results # ------------------------ # # before": {} # # commands": [ # "set service lldp legacy-protocols fdp", # "set service lldp legacy-protocols cdp", # "set service lldp snmp enable", # "set service lldp management-address '192.0.2.11'" # ] # # after": { # "snmp": "enable" - # "address": "192.0.2.11" + # "addresses": [ + # "192.0.2.11" + # ] # "legacy_protocols": [ # "cdp", # "fdp" # ] # "enable": true # } # # After state: # ------------- # # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # - name: Replace device configurations with provided configurations vyos.vyos.vyos_lldp_global: config: legacy_protocols: - edp - sonmp - cdp - address: 192.0.2.14 + addresses: + - 192.0.2.14 state: replaced # # # ------------------------ # Module Execution Results # ------------------------ # # # "before": { # "snmp": "enable" - # "address": "192.0.2.11" + # "addresses": [ + # "192.0.2.11" + # ] # "legacy_protocols": [ # "cdp", # "fdp" # ] # "enable": true # } # # "commands": [ # "delete service lldp snmp", # "delete service lldp legacy-protocols fdp", + # "delete service lldp management-address '192.0.2.11'", # "set service lldp management-address '192.0.2.14'", # "set service lldp legacy-protocols edp", # "set service lldp legacy-protocols sonmp" # ] # # "after": { - # "address": "192.0.2.14" + # "addresses": [ + # "192.0.2.14" + # ] # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # "enable": true # } # # After state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # Using deleted # # Before state # ------------- # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # - name: Delete attributes of given lldp service (This won't delete the LLDP service itself) vyos.vyos.vyos_lldp_global: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { - # "address": "192.0.2.14" + # "addresses": [ + # "192.0.2.14" + # ] # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # "enable": true # } # # "commands": [ # "delete service lldp management-address", # "delete service lldp legacy-protocols" # ] # # "after": { # "enable": true # } # # After state # ------------ # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # - name: Gather lldp global config with provided configurations vyos.vyos.vyos_lldp_global: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lldp_global: config: - address: 192.0.2.17 + addresses: + - 192.0.2.17 enable: true legacy_protocols: - cdp state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set service lldp legacy-protocols 'cdp'", # "set service lldp", # "set service lldp management-address '192.0.2.17'" # ] # # Using parsed # # - name: Parse the provided commands to provide structured configuration vyos.vyos.vyos_lldp_global: running_config: "set service lldp legacy-protocols 'cdp' set service lldp legacy-protocols 'fdp' set service lldp management-address '192.0.2.11'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { - # "address": "192.0.2.11", + # "addresses": [ + # "192.0.2.11" + # ] # "enable": true, # "legacy_protocols": [ # "cdp", # "fdp" # ] # } Return Values ------------- Common return values are documented `here `_, the following are the fields unique to this module: .. raw:: html
Key Returned Description
after
dictionary
when changed
The resulting configuration after module invocation.

Sample:
The configuration returned will always be in the same format of the parameters above.
before
dictionary
always
The configuration prior to the module invocation.

Sample:
The configuration returned will always be in the same format of the parameters above.
commands
list
always
The set of commands pushed to the remote device.

Sample:
['set service lldp legacy-protocols sonmp', "set service lldp management-address '192.0.2.14'"]


Status ------ Authors ~~~~~~~ - Rohit Thakur (@rohitthakur2590) diff --git a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py index 5c33d674..d79de617 100644 --- a/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py @@ -1,73 +1,78 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The arg spec for the vyos_lldp_global module """ from __future__ import absolute_import, division, print_function __metaclass__ = type class Lldp_globalArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_lldp_global module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "options": { "address": {"type": "str"}, + "addresses": { + "elements": "str", + "type": "list", + }, "enable": {"type": "bool"}, "legacy_protocols": { "choices": [ "cdp", "edp", "fdp", "sonmp", ], "elements": "str", "type": "list", }, "snmp": {"type": "str"}, }, "type": "dict", + "mutually_exclusive": [["addresses", "address"]], }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py index 1afda782..1dfd25e4 100644 --- a/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py @@ -1,260 +1,307 @@ # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_lldp_global class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible.module_utils.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 ( dict_diff, 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 ( get_lst_diff_for_dicts, list_diff_have_only, ) class Lldp_global(ConfigBase): """ The vyos_lldp_global class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "lldp_global", ] - params = ["enable", "address", "snmp", "legacy_protocols"] + # address intentionally omitted since it's coerced to addresses + params = ["enable", "addresses", "snmp", "legacy_protocols"] def __init__(self, module): super(Lldp_global, self).__init__(module) def get_lldp_global_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data, ) lldp_global_facts = facts["ansible_network_resources"].get("lldp_global") if not lldp_global_facts: return [] return lldp_global_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() + # fix for new name/type + if self._module.params["config"]: + temp_have_address = self._module.params["config"].pop("address", None) + if temp_have_address: + self._module.params["config"]["addresses"] = [temp_have_address] + if self.state in self.ACTION_STATES: existing_lldp_global_facts = self.get_lldp_global_facts() else: existing_lldp_global_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lldp_global_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_lldp_global_facts = self.get_lldp_global_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed", ) result["parsed"] = self.get_lldp_global_facts(data=running_config) else: changed_lldp_global_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lldp_global_facts if result["changed"]: result["after"] = changed_lldp_global_facts elif self.state == "gathered": result["gathered"] = changed_lldp_global_facts result["warnings"] = warnings return result def set_config(self, existing_lldp_global_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_lldp_global_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if self.state in ("merged", "replaced", "rendered") and not want: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(self.state), ) if self.state == "deleted": commands.extend(self._state_deleted(want=None, have=have)) elif self.state in ("merged", "rendered"): commands.extend(self._state_merged(want=want, have=have)) elif self.state == "replaced": commands.extend(self._state_replaced(want=want, have=have)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] commands.extend(self._render_updates(want, have)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if want: for item in Lldp_global.params: if item == "legacy_protocols": commands.extend(self._update_lldp_protocols(want, have)) + if item == "addresses": + commands.extend(self._update_management_addresses(want, have)) elif have.get(item) and not want.get(item) and item != "enable": commands.append(Lldp_global.del_cmd + item) elif have: + if have.get("enable"): + commands.append(self._compute_command(remove=True)) + return commands for item in Lldp_global.params: if have.get(item): if item == "legacy_protocols": commands.append(self._compute_command("legacy-protocols", remove=True)) - elif item == "address": + elif item == "addresses": commands.append(self._compute_command("management-address", remove=True)) elif item == "snmp": commands.append(self._compute_command(item, remove=True)) return commands def _render_updates(self, want, have): commands = [] if have: temp_have_legacy_protos = have.pop("legacy_protocols", None) + temp_have_addreses = have.pop("addresses", None) + temp_have_address = have.pop("address", None) + if temp_have_address: + temp_have_addresses = [temp_have_address] else: have = {} temp_want_legacy_protos = want.pop("legacy_protocols", None) + temp_want_addreses = want.pop("addresses", None) + temp_want_address = want.pop("address", None) + if temp_want_address: + temp_want_addresses = [temp_want_address] updates = dict_diff(have, want) if have and temp_have_legacy_protos: have["legacy_protocols"] = temp_have_legacy_protos if not have and temp_want_legacy_protos: want["legacy_protocols"] = temp_want_legacy_protos + if have and temp_have_addreses: + have["addresses"] = temp_have_addreses + if not have and temp_want_addreses: + want["addresses"] = temp_want_addreses commands.extend(self._add_lldp_protocols(want, have)) + commands.extend(self._add_management_addresses(want, have)) if updates: for key, value in iteritems(updates): - if value: + if value is not None: if key == "enable": - commands.append(self._compute_command()) - elif key == "address": - commands.append(self._compute_command("management-address", str(value))) + if value is False: + commands.append(self._compute_command(remove=True)) + return commands + else: + commands.append(self._compute_command()) elif key == "snmp": if value == "disable": commands.append(self._compute_command(key, remove=True)) else: commands.append(self._compute_command(key, str(value))) return commands def _add_lldp_protocols(self, want, have): commands = [] diff_members = get_lst_diff_for_dicts(want, have, "legacy_protocols") - for key in diff_members: - commands.append(self._compute_command("legacy-protocols", key)) + if diff_members: + for key in diff_members: + commands.append(self._compute_command("legacy-protocols", key)) + return commands + + def _add_management_addresses(self, want, have): + commands = [] + diff_members = get_lst_diff_for_dicts(want, have, "addresses") + if diff_members: + for key in diff_members: + commands.append(self._compute_command("management-address", key)) return commands def _update_lldp_protocols(self, want_item, have_item): commands = [] want_protocols = want_item.get("legacy_protocols") or [] have_protocols = have_item.get("legacy_protocols") or [] members_diff = list_diff_have_only(want_protocols, have_protocols) if members_diff: for member in members_diff: commands.append(self._compute_command("legacy-protocols", member, remove=True)) return commands + def _update_management_addresses(self, want_item, have_item): + commands = [] + want_addresses = want_item.get("addresses") or [] + have_addresses = have_item.get("addresses") or [] + + members_diff = list_diff_have_only(want_addresses, have_addresses) + if members_diff: + for member in members_diff: + commands.append(self._compute_command("management-address", member, remove=True)) + return commands + def _compute_command(self, key=None, value=None, remove=False): if remove: cmd = "delete service lldp" else: cmd = "set service lldp" if key: cmd += " " + key if value: cmd += " '" + value + "'" return cmd diff --git a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py index b2afe590..7a6e9b8e 100644 --- a/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py +++ b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py @@ -1,111 +1,126 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos lldp_global fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type from 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.lldp_global.lldp_global import ( Lldp_globalArgs, ) class Lldp_globalFacts(object): """The vyos lldp_global fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Lldp_globalArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) + def get_config(self, connection): + return connection.get("show configuration commands | grep lldp") + def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for lldp_global :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: - data = connection.get_config() + data = self.get_config(connection) objs = {} lldp_output = findall(r"^set service lldp (\S+)", data, M) if lldp_output: for item in set(lldp_output): lldp_regex = r" %s .+$" % item cfg = findall(lldp_regex, data, M) obj = self.render_config(cfg) if obj: objs.update(obj) lldp_service = findall(r"^set service (lldp)?('lldp')", data, M) if lldp_service or lldp_output: lldp_obj = {} lldp_obj["enable"] = True objs.update(lldp_obj) facts = {} params = utils.validate_config(self.argument_spec, {"config": objs}) facts["lldp_global"] = utils.remove_empties(params["config"]) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def render_config(self, conf): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ protocol_conf = "\n".join(filter(lambda x: ("legacy-protocols" in x), conf)) - att_conf = "\n".join(filter(lambda x: ("legacy-protocols" not in x), conf)) - config = self.parse_attribs(["snmp", "address"], att_conf) + att_conf = "\n".join(filter(lambda x: ("snmp" in x), conf)) + addr_conf = "\n".join(filter(lambda x: ("management-address" in x), conf)) + config = self.parse_attribs(["snmp"], att_conf) config["legacy_protocols"] = self.parse_protocols(protocol_conf) + config["addresses"] = self.parse_addresses(addr_conf) return utils.remove_empties(config) def parse_protocols(self, conf): protocol_support = None if conf: protocols = findall(r"^.*legacy-protocols (.+)", conf, M) if protocols: protocol_support = [] for protocol in protocols: protocol_support.append(protocol.strip("'")) return protocol_support + def parse_addresses(self, conf): + management_addresses = None + if conf: + addresses = findall(r"^.*management-address (.+)", conf, M) + if addresses: + management_addresses = [] + for protocol in addresses: + management_addresses.append(protocol.strip("'")) + return management_addresses + def parse_attribs(self, attribs, conf): config = {} for item in attribs: value = utils.parse_conf_arg(conf, item) if value: config[item] = value.strip("'") else: config[item] = None return utils.remove_empties(config) diff --git a/plugins/modules/vyos_lldp_global.py b/plugins/modules/vyos_lldp_global.py index e2f7914e..190f4513 100644 --- a/plugins/modules/vyos_lldp_global.py +++ b/plugins/modules/vyos_lldp_global.py @@ -1,480 +1,502 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_lldp_global """ from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_lldp_global version_added: '1.0.0' short_description: LLDP global resource module description: This module manages link layer discovery protocol (LLDP) attributes on VyOS devices. notes: - Tested against VyOS 1.3.8 - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). author: - Rohit Thakur (@rohitthakur2590) options: config: description: The provided link layer discovery protocol (LLDP) configuration. type: dict suboptions: enable: description: - This argument is a boolean value to enable or disable LLDP. type: bool address: description: - - This argument defines management-address. + - Exactly one management address (exclusive with addresses). + Deprecated in favor of addresses. To be removed in 7.0.0. type: str + addresses: + description: + - One or more management addresses. The management address is used to identify + the management interface of the system. Only addresses connected to the + system will be transmitted. + type: list + elements: str snmp: description: - This argument enable the SNMP queries to LLDP database. type: str legacy_protocols: description: - List of the supported legacy protocols. type: list elements: str choices: - cdp - edp - fdp - sonmp running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep lldp). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state of the configuration after module completion. type: str choices: - merged - replaced - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using merged # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # - name: Merge provided configuration with device configuration vyos.vyos.vyos_lldp_global: config: legacy_protocols: - fdp - cdp snmp: enable - address: 192.0.2.11 + addresses: + - 192.0.2.11 state: merged # # # ------------------------ # Module Execution Results # ------------------------ # # before": {} # # commands": [ # "set service lldp legacy-protocols fdp", # "set service lldp legacy-protocols cdp", # "set service lldp snmp enable", # "set service lldp management-address '192.0.2.11'" # ] # # after": { # "snmp": "enable" -# "address": "192.0.2.11" +# "addresses": [ +# "192.0.2.11" +# ] # "legacy_protocols": [ # "cdp", # "fdp" # ] # "enable": true # } # # After state: # ------------- # # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols fdp # set service lldp management-address '192.0.2.11' # set service lldp snmp enable # - name: Replace device configurations with provided configurations vyos.vyos.vyos_lldp_global: config: legacy_protocols: - edp - sonmp - cdp - address: 192.0.2.14 + addresses: + - 192.0.2.14 state: replaced # # # ------------------------ # Module Execution Results # ------------------------ # # # "before": { # "snmp": "enable" -# "address": "192.0.2.11" +# "addresses": [ +# "192.0.2.11" +# ] # "legacy_protocols": [ # "cdp", # "fdp" # ] # "enable": true # } # # "commands": [ # "delete service lldp snmp", # "delete service lldp legacy-protocols fdp", +# "delete service lldp management-address '192.0.2.11'", # "set service lldp management-address '192.0.2.14'", # "set service lldp legacy-protocols edp", # "set service lldp legacy-protocols sonmp" # ] # # "after": { -# "address": "192.0.2.14" +# "addresses": [ +# "192.0.2.14" +# ] # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # "enable": true # } # # After state: # ------------- # # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # Using deleted # # Before state # ------------- # vyos@vyos:~$ show configuration commands|grep lldp # set service lldp legacy-protocols cdp # set service lldp legacy-protocols edp # set service lldp legacy-protocols sonmp # set service lldp management-address '192.0.2.14' # - name: Delete attributes of given lldp service (This won't delete the LLDP service itself) vyos.vyos.vyos_lldp_global: config: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": { -# "address": "192.0.2.14" +# "addresses": [ +# "192.0.2.14" +# ] # "legacy_protocols": [ # "cdp", # "edp", # "sonmp" # ] # "enable": true # } # # "commands": [ # "delete service lldp management-address", # "delete service lldp legacy-protocols" # ] # # "after": { # "enable": true # } # # After state # ------------ # vyos@vyos:~$ show configuration commands | grep lldp # set service lldp # Using gathered # # Before state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # - name: Gather lldp global config with provided configurations vyos.vyos.vyos_lldp_global: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": { # "config_trap": true, # "group": { # "address_group": [ # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.3.1" # }, # { # "address": "192.0.3.2" # } # ], # "name": "ENG-HOSTS" # }, # { # "description": "Sales office hosts address list", # "members": [ # { # "address": "192.0.2.1" # }, # { # "address": "192.0.2.2" # }, # { # "address": "192.0.2.3" # } # ], # "name": "SALES-HOSTS" # } # ], # "network_group": [ # { # "description": "This group has the Management network addresses", # "members": [ # { # "address": "192.0.1.0/24" # } # ], # "name": "MGMT" # } # ] # }, # "log_martians": true, # "ping": { # "all": true, # "broadcast": true # }, # "route_redirects": [ # { # "afi": "ipv4", # "icmp_redirects": { # "receive": false, # "send": true # }, # "ip_src_route": true # } # ], # "state_policy": [ # { # "action": "accept", # "connection_type": "established", # "log": true # }, # { # "action": "reject", # "connection_type": "invalid" # } # ], # "syn_cookies": true, # "twa_hazards_protection": true, # "validation": "strict" # } # # After state: # ------------- # # vyos@192# run show configuration commands | grep lldp # set service lldp legacy-protocols 'cdp' # set service lldp management-address '192.0.2.17' # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_lldp_global: config: - address: 192.0.2.17 + addresses: + - 192.0.2.17 enable: true legacy_protocols: - cdp state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set service lldp legacy-protocols 'cdp'", # "set service lldp", # "set service lldp management-address '192.0.2.17'" # ] # # Using parsed # # - name: Parse the provided commands to provide structured configuration vyos.vyos.vyos_lldp_global: running_config: "set service lldp legacy-protocols 'cdp' set service lldp legacy-protocols 'fdp' set service lldp management-address '192.0.2.11'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": { -# "address": "192.0.2.11", +# "addresses": [ +# "192.0.2.11" +# ] # "enable": true, # "legacy_protocols": [ # "cdp", # "fdp" # ] # } """ RETURN = """ before: description: The configuration prior to the module invocation. returned: always type: dict sample: > The configuration returned will always be in the same format of the parameters above. after: description: The resulting configuration after module invocation. returned: when changed type: dict sample: > The configuration returned will always be in the same format of the parameters above. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - set service lldp legacy-protocols sonmp - set service lldp management-address '192.0.2.14' """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import ( Lldp_globalArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_global.lldp_global import ( Lldp_global, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Lldp_globalArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Lldp_global(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_config.yaml index 9144919d..31f527f9 100644 --- a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_config.yaml +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_config.yaml @@ -1,11 +1,10 @@ --- - - name: ensure facts include_tasks: _get_version.yaml - name: Remove pre-existing firewall rules vyos.vyos.vyos_config: lines: "{{ remove_config }}" ignore_errors: true vars: ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_firewall_config.yaml b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_firewall_config.yaml index 1cd452b7..afada4ef 100644 --- a/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_firewall_config.yaml +++ b/tests/integration/targets/vyos_firewall_interfaces/tests/cli/_remove_firewall_config.yaml @@ -1,11 +1,10 @@ --- - - name: ensure facts include_tasks: _get_version.yaml - name: Remove pre-existing firewall rules vyos.vyos.vyos_config: lines: "{{ remove_firewall_config }}" ignore_errors: true vars: ansible_connection: ansible.netcommon.network_cli diff --git a/tests/integration/targets/vyos_lldp_global/tests/cli/deleted.yaml b/tests/integration/targets/vyos_lldp_global/tests/cli/deleted.yaml index 9cf87b86..66c792c9 100644 --- a/tests/integration/targets/vyos_lldp_global/tests/cli/deleted.yaml +++ b/tests/integration/targets/vyos_lldp_global/tests/cli/deleted.yaml @@ -1,43 +1,43 @@ --- - debug: msg: START vyos_lldp_global deleted integration tests ansible_connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Delete attributes of LLDP service. register: result vyos.vyos.vyos_lldp_global: &id001 config: state: deleted - name: Assert that the before dicts were correctly generated assert: that: - - "{{ populate == result['before']}}" + - populate == result['before'] - name: Assert that the correct set of commands were generated assert: that: - - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + - deleted['commands'] | symmetric_difference(result['commands']) |length == 0 - name: Assert that the after dicts were correctly generated assert: that: - - "{{ deleted['after'] == result['after']}}" + - deleted['after'] == result['after'] - name: Delete attributes of given interfaces (IDEMPOTENT) register: result vyos.vyos.vyos_lldp_global: *id001 - name: Assert that the previous task was idempotent assert: that: - result.changed == false - name: Assert that the before dicts were correctly generated assert: that: - - "{{ deleted['after'] == result['before'] }}" + - deleted['after'] == result['before'] always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_lldp_global/tests/cli/merged.yaml b/tests/integration/targets/vyos_lldp_global/tests/cli/merged.yaml index a2e8e6bf..915a2902 100644 --- a/tests/integration/targets/vyos_lldp_global/tests/cli/merged.yaml +++ b/tests/integration/targets/vyos_lldp_global/tests/cli/merged.yaml @@ -1,46 +1,46 @@ --- - debug: msg: START vyos_lldp_global merged integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Merge the provided configuration with the existing running configuration register: result vyos.vyos.vyos_lldp_global: &id001 config: legacy_protocols: - fdp - cdp address: 192.0.2.11 state: merged - name: Assert that before dicts were correctly generated assert: - that: "{{ merged['before'] == result['before'] }}" + that: merged['before'] == result['before'] - name: Assert that correct set of commands were generated assert: that: - - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + - merged['commands'] | symmetric_difference(result['commands']) |length == 0 - name: Assert that after dicts was correctly generated assert: that: - - "{{ merged['after'] == result['after'] }}" + - merged['after'] == result['after'] - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) register: result vyos.vyos.vyos_lldp_global: *id001 - name: Assert that the previous task was idempotent assert: that: - result['changed'] == false - name: Assert that before dicts were correctly generated assert: that: - - "{{ merged['after'] == result['before'] }}" + - merged['after'] == result['before'] always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_lldp_global/tests/cli/replaced.yaml b/tests/integration/targets/vyos_lldp_global/tests/cli/replaced.yaml index c74aa460..ad0f53ba 100644 --- a/tests/integration/targets/vyos_lldp_global/tests/cli/replaced.yaml +++ b/tests/integration/targets/vyos_lldp_global/tests/cli/replaced.yaml @@ -1,48 +1,48 @@ --- - debug: msg: START vyos_lldp_global replaced integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Replace device configurations of LLDP service with provided configurations register: result vyos.vyos.vyos_lldp_global: &id001 config: legacy_protocols: - edp - sonmp - cdp address: 192.0.2.14 state: replaced - name: Assert that correct set of commands were generated assert: that: - - "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + - replaced['commands'] | symmetric_difference(result['commands']) |length == 0 - name: Assert that before dicts are correctly generated assert: that: - - "{{ populate == result['before'] }}" + - populate == result['before'] - name: Assert that after dict is correctly generated assert: that: - - "{{ replaced['after'] == result['after'] }}" + - replaced['after'] == result['after'] - name: Replace device configurations of LLDP service with provided configurarions (IDEMPOTENT) register: result vyos.vyos.vyos_lldp_global: *id001 - name: Assert that task was idempotent assert: that: - result['changed'] == false - name: Assert that before dict is correctly generated assert: that: - - "{{ replaced['after'] == result['before'] }}" + - replaced['after'] == result['before'] always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_lldp_global/tests/cli/rtt.yaml b/tests/integration/targets/vyos_lldp_global/tests/cli/rtt.yaml index 10fe4547..d4ea595b 100644 --- a/tests/integration/targets/vyos_lldp_global/tests/cli/rtt.yaml +++ b/tests/integration/targets/vyos_lldp_global/tests/cli/rtt.yaml @@ -1,50 +1,50 @@ --- - debug: msg: START vyos_lldp_global round trip integration tests on connection={{ ansible_connection }} - include_tasks: _remove_config.yaml - block: - name: Apply the provided configuration (base config) register: base_config vyos.vyos.vyos_lldp_global: config: legacy_protocols: - fdp - cdp address: 192.0.2.11 state: merged - name: Gather vyos_lldp_global facts vyos.vyos.vyos_facts: gather_subset: - default gather_network_resources: - lldp_global - name: Apply the provided configuration (config to be reverted) register: result vyos.vyos.vyos_lldp_global: config: legacy_protocols: - edp - sonmp - cdp address: 192.0.2.14 state: merged - name: Assert that changes were applied assert: - that: "{{ round_trip['after'] == result['after'] }}" + that: round_trip['after'] == result['after'] - name: Revert back to base config using facts round trip register: revert vyos.vyos.vyos_lldp_global: config: "{{ ansible_facts['network_resources']['lldp_global'] }}" state: replaced - name: Assert that config was reverted assert: - that: "{{ base_config['after'] == revert['after'] }}" + that: base_config['after'] == revert['after'] always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_lldp_global/vars/main.yaml b/tests/integration/targets/vyos_lldp_global/vars/main.yaml index 622b8e4d..dbfe1f3f 100644 --- a/tests/integration/targets/vyos_lldp_global/vars/main.yaml +++ b/tests/integration/targets/vyos_lldp_global/vars/main.yaml @@ -1,60 +1,68 @@ --- merged: before: - address: 192.0.2.17 + addresses: + - 192.0.2.17 enable: true legacy_protocols: - cdp commands: - set service lldp management-address '192.0.2.11' - set service lldp legacy-protocols 'fdp' after: - address: 192.0.2.11 + addresses: + - 192.0.2.17 + - 192.0.2.11 enable: true legacy_protocols: - cdp - fdp populate: - address: 192.0.2.17 + addresses: + - 192.0.2.17 enable: true legacy_protocols: - cdp replaced: commands: - set service lldp legacy-protocols 'edp' - set service lldp legacy-protocols 'sonmp' - set service lldp management-address '192.0.2.14' + - delete service lldp management-address '192.0.2.17' after: - address: 192.0.2.14 + addresses: + - 192.0.2.14 enable: true legacy_protocols: - cdp - edp - sonmp parsed: after: - address: 192.0.2.17 + addresses: + - 192.0.2.17 enable: true legacy_protocols: - fdp - cdp rendered: commands: - set service lldp legacy-protocols 'cdp' - set service lldp - set service lldp management-address '192.0.2.17' deleted: commands: - - delete service lldp management-address - - delete service lldp legacy-protocols - after: - enable: true + - delete service lldp + after: [] + round_trip: after: - address: 192.0.2.14 + addresses: + - 192.0.2.11 + - 192.0.2.14 enable: true legacy_protocols: - cdp - edp - fdp - sonmp diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_lldp_global_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_lldp_global_config.cfg new file mode 100644 index 00000000..05b8fa7b --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_lldp_global_config.cfg @@ -0,0 +1,4 @@ +set service lldp management-address '192.0.2.14' +set service lldp management-address 'ff00::1' +set service lldp legacy-protocols 'cdp' +set service lldp legacy-protocols 'fdp' diff --git a/tests/unit/modules/network/vyos/test_vyos_lldp_global.py b/tests/unit/modules/network/vyos/test_vyos_lldp_global.py new file mode 100644 index 00000000..0dc1a8d7 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_lldp_global.py @@ -0,0 +1,139 @@ +# (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 . + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from unittest.mock import patch + +from ansible_collections.vyos.vyos.plugins.modules import vyos_lldp_global +from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args + +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosLLDPModule(TestVyosModule): + module = vyos_lldp_global + + def setUp(self): + super(TestVyosLLDPModule, self).setUp() + + 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_get_config = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global.Lldp_globalFacts.get_config", + ) + self.get_config = self.mock_get_config.start() + + def tearDown(self): + super(TestVyosLLDPModule, self).tearDown() + + self.mock_get_resource_connection_config.stop() + self.mock_get_resource_connection_facts.stop() + self.mock_get_config.stop() + + def load_fixtures(self, commands=None, filename=None): + self.get_config.return_value = load_fixture("vyos_lldp_global_config.cfg") + + def test_vyos_lldp_global_merge_enabled(self): + set_module_args(dict(config=dict(enable=True))) + self.execute_module(changed=False) + + def test_vyos_lldp_global_merge_disabled(self): + set_module_args(dict(config=dict(enable=False))) + self.execute_module(changed=True, commands=["delete service lldp"]) + + def test_vyos_lldp_global_merge_addresses(self): + set_module_args( + dict( + config=dict( + enable=True, + addresses=["192.0.0.1"], + ), + ), + ) + self.execute_module( + changed=True, + commands=["set service lldp management-address '192.0.0.1'"], + ) + + def test_vyos_lldp_global_replace_addresses(self): + set_module_args( + dict( + config=dict( + enable=True, + addresses=["192.0.0.1"], + legacy_protocols=["cdp", "fdp"], + ), + state="replaced", + ), + ) + commands = [ + "set service lldp management-address '192.0.0.1'", + "delete service lldp management-address '192.0.2.14'", + "delete service lldp management-address 'ff00::1'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_lldp_global_replace_protocols(self): + set_module_args( + dict( + config=dict( + enable=True, + addresses=["192.0.2.14", "ff00::1"], + legacy_protocols=["cdp"], + ), + state="replaced", + ), + ) + commands = [ + "delete service lldp legacy-protocols 'fdp'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_lldp_global_replace_address(self): + set_module_args( + dict( + config=dict( + enable=True, + address="192.0.0.1", + legacy_protocols=["cdp", "fdp"], + ), + state="replaced", + ), + ) + commands = [ + "set service lldp management-address '192.0.0.1'", + "delete service lldp management-address '192.0.2.14'", + "delete service lldp management-address 'ff00::1'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_lldp_global_delete_all(self): + set_module_args(dict(config=dict(), state="deleted")) + self.execute_module(changed=True, commands=["delete service lldp"])