diff --git a/plugins/module_utils/network/vyos/argspec/vrrp/vrrp.py b/plugins/module_utils/network/vyos/argspec/vrrp/vrrp.py index 86afeedc..707c8498 100644 --- a/plugins/module_utils/network/vyos/argspec/vrrp/vrrp.py +++ b/plugins/module_utils/network/vyos/argspec/vrrp/vrrp.py @@ -1,63 +1,192 @@ # -*- coding: utf-8 -*- # Copyright 2024 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 ############################################# # WARNING # ############################################# # # This file is auto generated by the # cli_rm_builder. # # Manually editing this file is not advised. # # To update the argspec make the desired changes # in the module docstring and re-run # cli_rm_builder. # ############################################# """ The arg spec for the vyos_vrrp module """ class VrrpArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_vrrp module""" argument_spec = { "config": { "type": "dict", + "required": False, "options": { - "disable": {"aliases": ["disabled"], "type": "bool", "default": False}, - "virtual_server": { + "disable": {"type": "bool"}, + "virtual_servers": { "type": "list", "elements": "dict", "options": { - "name": {"required": True, "type": "str"}, - "address": {"type": "str"}, + "name": {"type": "str", "required": True}, + "address": {"type": "str", "required": True}, + "algorithm": { + "type": "str", + "choices": ["round_robin", "weighted_round_robin", "least_connection"], + }, + "delay_loop": {"type": "str"}, + "forward_method": {"type": "str", "choices": ["direct", "nat"]}, + "fwmark": {"type": "str"}, + "persistence_timeout": {"type": "str"}, + "port": {"type": "str"}, + "protocol": {"type": "str", "choices": ["tcp", "udp"]}, + "real_server": { + "type": "list", + "elements": "dict", + "options": { + "address": {"type": "str", "required": True}, + "port": {"type": "str"}, + "health_check_script": {"type": "str"}, + }, + }, + }, + }, + "vrrp": { + "type": "dict", + "options": { + "global_parameters": { + "type": "dict", + "options": { + "garp": { + "type": "dict", + "options": { + "interval": {"type": "int"}, + "master_delay": {"type": "int"}, + "master_refresh": {"type": "int"}, + "master_refresh_repeat": {"type": "int"}, + "master_repeat": {"type": "int"}, + }, + }, + }, + }, + "groups": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "address": {"type": "str"}, + "advertise_interval": {"type": "int"}, + "authentication": { + "type": "dict", + "options": { + "password": {"type": "str", "no_log": True}, + "type": {"type": "str", "choices": ["plaintext_password"]}, + }, + }, + "description": {"type": "str"}, + "disable": {"type": "bool"}, + "excluded_address": {"type": "str"}, + "garp": { + "type": "dict", + "options": { + "interval": {"type": "int"}, + "master_delay": {"type": "int"}, + "master_refresh": {"type": "int"}, + "master_refresh_repeat": {"type": "int"}, + "master_repeat": {"type": "int"}, + }, + }, + "health_check": { + "type": "dict", + "options": { + "failure_count": {"type": "int"}, + "interval": {"type": "int"}, + "ping": {"type": "str"}, + "script": {"type": "str"}, + }, + }, + "hello_source_address": {"type": "str"}, + "interface": {"type": "str"}, + "no_preempt": {"type": "bool"}, + "peer_address": {"type": "str"}, + "preempt_delay": {"type": "int"}, + "priority": {"type": "int"}, + "rfc3768_compatibility": {"type": "bool"}, + "track": { + "type": "dict", + "options": { + "exclude_vrrp_interface": {"type": "bool"}, + "interface": {"type": "str"}, + }, + }, + "transition_script": { + "type": "dict", + "options": { + "backup": {"type": "str"}, + "fault": {"type": "str"}, + "master": {"type": "str"}, + "stop": {"type": "str"}, + }, + }, + "vrid": {"type": "int", "required": True}, + }, + }, + "snmp": {"type": "bool"}, + "sync_groups": { + "type": "list", + "elements": "dict", + "options": { + "name": {"type": "str", "required": True}, + "health_check": { + "type": "dict", + "options": { + "failure_count": {"type": "int"}, + "interval": {"type": "int"}, + "ping": {"type": "str"}, + "script": {"type": "str"}, + }, + }, + "member": {"type": "list", "elements": "str"}, + "transition_script": { + "type": "dict", + "options": { + "backup": {"type": "str"}, + "fault": {"type": "str"}, + "master": {"type": "str"}, + "stop": {"type": "str"}, + }, + }, + }, + }, }, }, }, }, "state": { "type": "str", "choices": [ "deleted", "merged", "purged", "replaced", "gathered", "rendered", "parsed", ], "default": "merged", }, "running_config": {"type": "str"}, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/facts/vrrp/vrrp.py b/plugins/module_utils/network/vyos/facts/vrrp/vrrp.py index 83eca11a..efb4a979 100644 --- a/plugins/module_utils/network/vyos/facts/vrrp/vrrp.py +++ b/plugins/module_utils/network/vyos/facts/vrrp/vrrp.py @@ -1,125 +1,124 @@ # -*- 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 vrrp 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. """ import re from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.vrrp.vrrp import ( VrrpArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.vrrp import ( VrrpTemplate, ) class VrrpFacts(object): """The vyos vrrp facts class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = VrrpArgs.argument_spec def get_device_data(self, connection): return connection.get('show configuration commands | match "set high-availability"') def get_config_set(self, data, connection): """To classify the configurations beased on vrrp""" config_dict = {} for config_line in data.splitlines(): vrrp_grp = re.search(r"set high-availability vrrp group (\S+).*", config_line) vrrp_snmp = re.search(r"set high-availability vrrp snmp", config_line) vrrp_gp = re.search( r"set high-availability vrrp global-parameters (\S+).*", config_line, ) vrrp_sg = re.search(r"set high-availability vrrp sync-group (\S+).*", config_line) vrrp_vsrv = re.search(r"set high-availability virtual-server (\S+).*", config_line) vrrp_disable = re.search(r"set high-availability disable", config_line) if vrrp_disable: config_dict["disable"] = config_dict.get("disable", "") + config_line + "\n" if vrrp_gp: config_dict["global_parameters"] = ( config_dict.get(vrrp_gp.group(1), "") + config_line + "\n" ) if vrrp_vsrv: config_dict[vrrp_vsrv.group(1)] = ( config_dict.get(vrrp_vsrv.group(1), "") + config_line + "\n" ) if vrrp_sg: config_dict[vrrp_sg.group(1)] = ( config_dict.get(vrrp_sg.group(1), "") + config_line + "\n" ) if vrrp_grp: config_dict[vrrp_grp.group(1)] = ( config_dict.get(vrrp_grp.group(1), "") + config_line + "\n" ) return list(config_dict.values()) def deep_merge(self, dest, src): for key, value in src.items(): if key in dest and isinstance(dest[key], dict) and isinstance(value, dict): self.deep_merge(dest[key], value) else: dest[key] = value return dest def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for vrrp network resource :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ facts = {} objs = {} config_lines = [] if not data: data = self.get_device_data(connection) vrrp_facts = {"virtual_servers": {}, "sync_groups": {}, "vrrp": {}} - groups = [] resources = self.get_config_set(data, connection) for resource in data.splitlines(): vrrp_parser = VrrpTemplate( lines=resource.split("\n"), module=self._module, ) objs = vrrp_parser.parse() if "disable" in objs: vrrp_facts["disable"] = objs["disable"] - if "vrrp" in objs: - groups.append(objs) - for section in ("virtual_servers", "sync_groups", "vrrp"): + # if "vrrp" in objs: + # groups.append(objs) + for section in ("virtual_servers", "vrrp"): if section in objs: for name, data in objs[section].items(): existing = vrrp_facts[section].get(name, {}) vrrp_facts[section][name] = self.deep_merge(existing, data) self._module.fail_json(msg=str(vrrp_facts)) ansible_facts["ansible_network_resources"].pop("vrrp", None) params = utils.remove_empties( vrrp_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), ) facts["vrrp"] = params.get("config", []) ansible_facts["ansible_network_resources"].update(facts) self._module.fail_json(msg="STOP") return ansible_facts diff --git a/plugins/module_utils/network/vyos/rm_templates/vrrp.py b/plugins/module_utils/network/vyos/rm_templates/vrrp.py index a57505b7..d481daff 100644 --- a/plugins/module_utils/network/vyos/rm_templates/vrrp.py +++ b/plugins/module_utils/network/vyos/rm_templates/vrrp.py @@ -1,659 +1,665 @@ # -*- 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 Bgp_global parser templates file. This contains a list of parser definitions and associated functions that facilitates both facts gathering and native command generation for the given network resource. """ import re # from ansible.module_utils.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import ( NetworkTemplate, ) class VrrpTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): prefix = {"set": "set", "remove": "delete"} super(VrrpTemplate, self).__init__( lines=lines, tmplt=self, prefix=prefix, module=module, ) def _tmplt_vsrvs(config_data): config_data = config_data["virtual-server"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vsrvs_rsrv(config_data): config_data = config_data["virtual-server"]["real_servers"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_sgroup_hc(config_data): config_data = config_data["sync-group"]["health-check"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_sgroup_ts(config_data): config_data = config_data["sync-group"]["transition-script"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_gp(config_data): config_data = config_data["vrrp"]["global-parameters"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_gp_garp(config_data): config_data = config_data["vrrp"]["global-parameters"]["garp"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_group(config_data): config_data = config_data["vrrp"]["group"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_group_track(config_data): config_data = config_data["vrrp"]["group"]["track"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_group_hc(config_data): config_data = config_data["vrrp"]["group"]["health-check"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_group_ts(config_data): config_data = config_data["vrrp"]["group"]["transcription-script"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_group_garp(config_data): config_data = config_data["vrrp"]["group"]["garp"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command def _tmplt_vrrp_group_auth(config_data): config_data = config_data["vrrp"]["group"]["authentication"] command = [] # cmd = "service snmp v3 group {group}".format(**config_data) # if "mode" in config_data: # mode_cmd = cmd + " mode {mode}".format(**config_data) # command.append(mode_cmd) # if "seclevel" in config_data: # sec_cmd = cmd + " seclevel {seclevel}".format(**config_data) # command.append(sec_cmd) # if "view" in config_data: # view_cmd = cmd + " view {view}".format(**config_data) # command.append(view_cmd) return command # fmt: off PARSERS = [ { "name": "disable", "getval": re.compile( r""" ^set \shigh-availability \s(?Pdisable) $""", re.VERBOSE, ), "setval": "high-availability disable", "result": { "disable": "{{ True if disable is defined }}", }, }, { "name": "virtual_servers", "getval": re.compile( r""" ^set\shigh-availability\svirtual-server \s+(?P\S+) (?:\s+address\s+(?P
\S+))? (?:\s+algorithm\s+(?P\S+))? (?:\s+delay-loop\s+(?P\S+))? (?:\s+forward-method\s+(?P\S+))? (?:\s+fwmark\s+(?P\S+))? (?:\s+persistence-timeout\s+(?P\S+))? (?:\s+port\s+(?P\S+))? (?:\s+protocol\s+(?P\S+))? $ """, re.VERBOSE, ), "setval": _tmplt_vsrvs, "result": { "virtual_servers": { "{{ alias }}": { "alias": "{{ alias }}", "address": "{{ address if address is defined else None }}", "algorithm": "{{ algorithm if algorithm is defined else None }}", "delay_loop": "{{ delay_loop if delay_loop is defined else None }}", "forward_method": "{{ forward_method if forward_method is defined else None }}", "fwmark": "{{ fwmark if fwmark is defined else None }}", "persistence_timeout": "{{ persistence_timeout if persistence_timeout is defined else None }}", "port": "{{ port if port is defined else None }}", "protocol": "{{ protocol if protocol is defined else None }}", }, }, }, }, { "name": "virtual_servers.real_servers", "getval": re.compile( r""" ^set\shigh-availability\svirtual-server \s+(?P\S+) \sreal-server \s+(?P\S+) (?:\s+port\s+(?P\S+))? (?:\s+health-check\sscript\s+(?P\S+))? (?:\s+connection-timeout\s+(?P\S+))? $ """, re.VERBOSE, ), "setval": _tmplt_vsrvs_rsrv, # "compval": "global_parameters.garp.master_refersh_repeat", "result": { "virtual_servers": { "{{ alias }}": { "alias": "{{ alias }}", "real_servers": { "{{ name }}": { "name": "{{ name }}", "port": "{{ port if port is defined else None }}", "health_check_script": "{{ hcscript if hcscript is defined else None }}", "connection_timeout": "{{ cont if cont is defined else None }}", }, }, }, }, }, }, { "name": "sync_group.member", "getval": re.compile( r""" ^set\shigh-availability\svrrp\ssync-group \s+(?P\S+) \smember \s+(?P\S+) $ """, re.VERBOSE, ), "setval": "set high-availability vrrp sync-group {{sgname}} member {{member}}", "compval": "sync_groups.member", "result": { - "sync_groups": { - "{{ sgname }}": { - "name": "{{ sgname }}", - "member": "{{ member }}", + "vrrp": { + "sync_groups": { + "{{ sgname }}": { + "name": "{{ sgname }}", + "member": "{{ member }}", + }, }, }, }, }, { "name": "sync_group.health_check", "getval": re.compile( r""" ^set\shigh-availability\svrrp\ssync-group \s+(?P\S+) \shealth-check (?:\s+failure-count\s+(?P\S+)) ?(?:\s+interval\s+(?P\S+)) ?(?:\s+ping\s+(?P\S+)) ?(?:\s+script\s+(?P