diff options
author | Bradley A. Thornton <bthornto@thethorntons.net> | 2019-08-19 07:56:36 -0700 |
---|---|---|
committer | Bradley A. Thornton <bthornto@thethorntons.net> | 2019-08-19 07:56:36 -0700 |
commit | 7d4127b40ce899b43180df68d84ec6adcda20c0e (patch) | |
tree | b5d5a7a85b6aa288ea4d183e129d00d2b9b8d527 /plugins/module_utils | |
parent | 3fabcd898a415a724048f445dfc35e29f895fe2e (diff) | |
download | vyos.vyos-7d4127b40ce899b43180df68d84ec6adcda20c0e.tar.gz vyos.vyos-7d4127b40ce899b43180df68d84ec6adcda20c0e.zip |
based on ansible/ansible 843a51628b49d7aaa1447616fe0fcdf6a4ec7b1a
Diffstat (limited to 'plugins/module_utils')
10 files changed, 457 insertions, 17 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/facts/facts.py b/plugins/module_utils/network/vyos/argspec/facts/facts.py index 624d2fdb..31b1aa9a 100644 --- a/plugins/module_utils/network/vyos/argspec/facts/facts.py +++ b/plugins/module_utils/network/vyos/argspec/facts/facts.py @@ -24,6 +24,8 @@ class FactsArgs(object): # pylint: disable=R0903 "!l3_interfaces", "lag_interfaces", "!lag_interfaces", + "lldp_global", + "!lldp_global", ] argument_spec = { diff --git a/plugins/module_utils/network/vyos/argspec/lldp_global/__init__.py b/plugins/module_utils/network/vyos/argspec/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/lldp_global/__init__.py 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 new file mode 100644 index 00000000..84bbc00c --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py @@ -0,0 +1,56 @@ +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the vyos_lldp_global module +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lldp_globalArgs(object): # pylint: disable=R0903 + """The arg spec for the vyos_lldp_global module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "options": { + "address": {"type": "str"}, + "enable": {"type": "bool"}, + "legacy_protocols": { + "choices": ["cdp", "edp", "fdp", "sonmp"], + "type": "list", + }, + "snmp": {"type": "str"}, + }, + "type": "dict", + }, + "state": { + "choices": ["merged", "replaced", "deleted"], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/lldp_global/__init__.py b/plugins/module_utils/network/vyos/config/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/config/lldp_global/__init__.py 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 new file mode 100644 index 00000000..54606fab --- /dev/null +++ b/plugins/module_utils/network/vyos/config/lldp_global/lldp_global.py @@ -0,0 +1,248 @@ +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_lldp_global class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from ansible.module_utils.network.common.cfg.base import ConfigBase +from ansible.module_utils.network.common.utils import to_list, dict_diff +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( + Facts, +) +from ansible.module_utils.six import iteritems +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"] + + def __init__(self, module): + super(Lldp_global, self).__init__(module) + + def get_lldp_global_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, self.gather_network_resources + ) + lldp_global_facts = facts["ansible_network_resources"].get( + "lldp_global" + ) + if not lldp_global_facts: + return {} + return lldp_global_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + commands = list() + warnings = list() + + existing_lldp_global_facts = self.get_lldp_global_facts() + commands.extend(self.set_config(existing_lldp_global_facts)) + if commands: + if not self._module.check_mode: + self._connection.edit_config(commands) + result["changed"] = True + result["commands"] = commands + + changed_lldp_global_facts = self.get_lldp_global_facts() + + result["before"] = existing_lldp_global_facts + if result["changed"]: + result["after"] = changed_lldp_global_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_lldp_global_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lldp_global_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + state = self._module.params["state"] + if state == "deleted": + commands.extend(self._state_deleted(want=None, have=have)) + elif state == "merged": + commands.extend(self._state_merged(want=want, have=have)) + elif state == "replaced": + commands.extend(self._state_replaced(want=want, have=have)) + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if have: + commands.extend(self._state_deleted(want, have)) + commands.extend(self._state_merged(want, have)) + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + commands.extend(self._render_updates(want, have)) + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + if want: + for item in Lldp_global.params: + if item == "legacy_protocols": + commands.extend(self._update_lldp_protocols(want, have)) + elif ( + have.get(item) and not want.get(item) and item != "enable" + ): + commands.append(Lldp_global.del_cmd + item) + elif have: + for item in Lldp_global.params: + if have.get(item): + if item == "legacy_protocols": + commands.append( + self._compute_command( + "legacy-protocols", remove=True + ) + ) + elif item == "address": + commands.append( + self._compute_command( + "management-address", remove=True + ) + ) + elif item == "snmp": + commands.append( + self._compute_command(item, remove=True) + ) + + return commands + + def _render_updates(self, want, have): + commands = [] + if have: + temp_have_legacy_protos = have.pop("legacy_protocols", None) + else: + have = {} + temp_want_legacy_protos = want.pop("legacy_protocols", None) + + updates = dict_diff(have, want) + + if have and temp_have_legacy_protos: + have["legacy_protocols"] = temp_have_legacy_protos + if not have and temp_want_legacy_protos: + want["legacy_protocols"] = temp_want_legacy_protos + + commands.extend(self._add_lldp_protocols(want, have)) + + if updates: + for key, value in iteritems(updates): + if value: + if key == "enable": + commands.append(self._compute_command()) + elif key == "address": + commands.append( + self._compute_command( + "management-address", str(value) + ) + ) + elif key == "snmp": + if value == "disable": + commands.append( + self._compute_command(key, remove=True) + ) + else: + commands.append( + self._compute_command(key, str(value)) + ) + return commands + + def _add_lldp_protocols(self, want, have): + commands = [] + diff_members = get_lst_diff_for_dicts(want, have, "legacy_protocols") + for key in diff_members: + commands.append(self._compute_command("legacy-protocols", key)) + return commands + + def _update_lldp_protocols(self, want_item, have_item): + commands = [] + want_protocols = want_item.get("legacy_protocols") or [] + have_protocols = have_item.get("legacy_protocols") or [] + + members_diff = list_diff_have_only(want_protocols, have_protocols) + if members_diff: + for member in members_diff: + commands.append( + self._compute_command( + "legacy-protocols", member, remove=True + ) + ) + return commands + + def _compute_command(self, key=None, value=None, remove=False): + if remove: + cmd = "delete service lldp" + else: + cmd = "set service lldp" + if key: + cmd += " " + key + + if value: + cmd += " '" + value + "'" + return cmd diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index 9c389c91..fb05f2ac 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -6,12 +6,9 @@ The facts class for vyos this file validates each subset of facts and selectively calls the appropriate facts gathering function """ - from __future__ import absolute_import, division, print_function __metaclass__ = type - - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import ( FactsArgs, ) @@ -25,6 +22,9 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_in from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import ( Lag_interfacesFacts, ) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global import ( + Lldp_globalFacts, +) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( Default, Neighbors, @@ -37,6 +37,7 @@ FACT_RESOURCE_SUBSETS = dict( interfaces=InterfacesFacts, l3_interfaces=L3_interfacesFacts, lag_interfaces=Lag_interfacesFacts, + lldp_global=Lldp_globalFacts, ) @@ -71,7 +72,6 @@ class Facts(FactsBase): resource_facts_type, data, ) - if self.VALID_LEGACY_GATHER_SUBSETS: self.get_network_legacy_facts( FACT_LEGACY_SUBSETS, legacy_facts_type diff --git a/plugins/module_utils/network/vyos/facts/legacy/base.py b/plugins/module_utils/network/vyos/facts/legacy/base.py index 34992b1b..dce93aae 100644 --- a/plugins/module_utils/network/vyos/facts/legacy/base.py +++ b/plugins/module_utils/network/vyos/facts/legacy/base.py @@ -12,8 +12,6 @@ based on the configuration. from __future__ import absolute_import, division, print_function __metaclass__ = type - - import platform import re from ansible.module_utils.network.vyos.vyos import ( diff --git a/plugins/module_utils/network/vyos/facts/lldp_global/__init__.py b/plugins/module_utils/network/vyos/facts/lldp_global/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/lldp_global/__init__.py 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 new file mode 100644 index 00000000..89543932 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py @@ -0,0 +1,114 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos lldp_global fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from re import findall, M +from copy import deepcopy + +from ansible.module_utils.network.common import utils +from ansible_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 populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for lldp_global + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = connection.get_config() + + objs = {} + lldp_output = findall(r"^set service lldp (\S+)", data, M) + if lldp_output: + for item in set(lldp_output): + lldp_regex = r" %s .+$" % item + cfg = findall(lldp_regex, data, M) + obj = self.render_config(cfg) + if obj: + objs.update(obj) + lldp_service = findall(r"^set service (lldp)?('lldp')", data, M) + if lldp_service or lldp_output: + lldp_obj = {} + lldp_obj["enable"] = True + objs.update(lldp_obj) + + facts = {} + params = utils.validate_config(self.argument_spec, {"config": objs}) + facts["lldp_global"] = utils.remove_empties(params["config"]) + + ansible_facts["ansible_network_resources"].update(facts) + + return ansible_facts + + def render_config(self, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + protocol_conf = "\n".join( + filter(lambda x: ("legacy-protocols" in x), conf) + ) + att_conf = "\n".join( + filter(lambda x: ("legacy-protocols" not in x), conf) + ) + config = self.parse_attribs(["snmp", "address"], att_conf) + config["legacy_protocols"] = self.parse_protocols(protocol_conf) + return utils.remove_empties(config) + + def parse_protocols(self, conf): + protocol_support = None + if conf: + protocols = findall(r"^.*legacy-protocols (.+)", conf, M) + if protocols: + protocol_support = [] + for protocol in protocols: + protocol_support.append(protocol.strip("'")) + return protocol_support + + def parse_attribs(self, attribs, conf): + config = {} + for item in attribs: + value = utils.parse_conf_arg(conf, item) + if value: + config[item] = value.strip("'") + else: + config[item] = None + return utils.remove_empties(config) diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index d6c11bdc..1968cccd 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -67,7 +67,33 @@ def diff_list_of_dicts(want, have): return diff +def get_lst_diff_for_dicts(want, have, lst): + """ + This function generates a list containing values + that are only in want and not in list in have dict + :param want: dict object to want + :param have: dict object to have + :param lst: list the diff on + :return: new list object with values which are only in want. + """ + if not have: + diff = want.get(lst) or [] + + else: + want_elements = want.get(lst) or {} + have_elements = have.get(lst) or {} + diff = list_diff_want_only(want_elements, have_elements) + return diff + + def list_diff_have_only(want_list, have_list): + """ + This function generated the list containing values + that are only in have list. + :param want_list: + :param have_list: + :return: new list with values which are only in have list + """ if have_list and not want_list: diff = have_list elif not have_list: @@ -82,6 +108,13 @@ def list_diff_have_only(want_list, have_list): def list_diff_want_only(want_list, have_list): + """ + This function generated the list containing values + that are only in want list. + :param want_list: + :param have_list: + :return: new list with values which are only in want list + """ if have_list and not want_list: diff = None elif not have_list: @@ -93,14 +126,3 @@ def list_diff_want_only(want_list, have_list): if i in want_list and i not in have_list ] return diff - - -def get_lst_diff_for_dicts(want, have, lst): - if not have: - diff = want.get(lst) or [] - - else: - want_elements = want.get(lst) or {} - have_elements = have.get(lst) or {} - diff = list_diff_want_only(want_elements, have_elements) - return diff |