summaryrefslogtreecommitdiff
path: root/plugins/modules
diff options
context:
space:
mode:
authoromnom62 <omnom62@outlook.com>2026-03-02 21:20:30 +1000
committeromnom62 <omnom62@outlook.com>2026-03-02 21:20:30 +1000
commit7952a8645124ef160c2759a2dae9e9cb32eac1a9 (patch)
treea01436ff4f4af14d7386715e9e7c613f05d5ac2c /plugins/modules
downloadrest.vyos-7952a8645124ef160c2759a2dae9e9cb32eac1a9.tar.gz
rest.vyos-7952a8645124ef160c2759a2dae9e9cb32eac1a9.zip
first commit
Diffstat (limited to 'plugins/modules')
-rw-r--r--plugins/modules/vyos_ntp_global.py379
1 files changed, 379 insertions, 0 deletions
diff --git a/plugins/modules/vyos_ntp_global.py b/plugins/modules/vyos_ntp_global.py
new file mode 100644
index 0000000..746838c
--- /dev/null
+++ b/plugins/modules/vyos_ntp_global.py
@@ -0,0 +1,379 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.vyos.rest.plugins.module_utils.vyos import VyOSModule
+from ansible_collections.vyos.rest.plugins.module_utils.utils import normalize_to_list
+
+# ------------------------------------------------------------
+# Helpers
+# ------------------------------------------------------------
+
+def normalize_config(config):
+
+ result = {
+ "allow_clients": sorted(config.get("allow_clients", [])),
+ "listen_addresses": sorted(config.get("listen_addresses", [])),
+ "servers": {}
+ }
+
+ for s in config.get("servers", []):
+
+ name = s["server"]
+
+ result["servers"][name] = sorted(s.get("options", []))
+
+ return result
+
+def normalize_servers(value):
+ result = {}
+
+ if isinstance(value, dict):
+ for server, data in value.items():
+
+ if isinstance(data, dict):
+ result[server] = sorted(list(data.keys()))
+
+ elif isinstance(data, list):
+ result[server] = sorted(data)
+
+ elif isinstance(data, str):
+ result[server] = [data]
+
+ else:
+ result[server] = []
+
+ elif isinstance(value, list):
+ for server in value:
+ result[server] = []
+
+ elif isinstance(value, str):
+ result[value] = []
+
+ return result
+
+
+def get_running_config(vyos):
+
+ raw = vyos.get_config(["service", "ntp"])
+
+ result = {
+ "allow_clients": [],
+ "listen_addresses": [],
+ "servers": {}
+ }
+
+ if not raw:
+ return result
+
+ allow_raw = raw.get("allow-client", {}).get("address", [])
+ result["allow_clients"] = sorted(normalize_to_list(allow_raw))
+
+ listen_raw = raw.get("listen-address", [])
+ result["listen_addresses"] = sorted(normalize_to_list(listen_raw))
+
+ servers_raw = raw.get("server", {})
+ result["servers"] = normalize_servers(servers_raw)
+
+ return result
+
+def build_commands(desired, existing, state):
+
+ cmds = []
+
+ # overridden = delete all then merged
+ if state == "overridden":
+
+ if existing["allow_clients"]:
+ cmds.append(("delete", ["service", "ntp", "allow-clients"]))
+
+ if existing["listen_addresses"]:
+ cmds.append(("delete", ["service", "ntp", "listen-address"]))
+
+ if existing["servers"]:
+ cmds.append(("delete", ["service", "ntp", "server"]))
+
+ state = "merged"
+
+ cmds += diff_list(
+ "allow-client", "address", # singular
+ desired["allow_clients"],
+ existing["allow_clients"],
+ state
+ )
+
+ # listen_addresses
+ cmds += diff_list(
+ "listen-address", None,
+ desired["listen_addresses"],
+ existing["listen_addresses"],
+ state
+ )
+
+ # servers
+ cmds += diff_servers(
+ desired["servers"],
+ existing["servers"],
+ state
+ )
+
+ return cmds
+
+
+def diff_list(node, subnode, desired, existing, state):
+
+ cmds = []
+
+ desired = set(desired)
+ existing = set(existing)
+
+ if state in ["merged", "replaced"]:
+
+ for v in desired - existing:
+
+ path = ["service", "ntp", node]
+
+ if subnode:
+ path += [subnode, v]
+ else:
+ path += [v]
+
+ cmds.append(("set", path))
+
+ if state in ["replaced", "deleted"]:
+
+ for v in existing - desired:
+
+ path = ["service", "ntp", node]
+
+ if subnode:
+ path += [subnode, v]
+ else:
+ path += [v]
+
+ cmds.append(("delete", path))
+
+ return cmds
+
+
+def diff_servers(desired, existing, state):
+
+ cmds = []
+
+ desired_set = set(desired.keys())
+ existing_set = set(existing.keys())
+
+ # add/update
+ if state in ["merged", "replaced"]:
+
+ for server in desired_set:
+
+ desired_opts = set(desired[server])
+ existing_opts = set(existing.get(server, []))
+
+ # add server
+ if server not in existing_set:
+ cmds.append(("set", ["service", "ntp", "server", server]))
+
+ # add options
+ for opt in desired_opts - existing_opts:
+
+ cmds.append(
+ ("set", ["service", "ntp", "server", server, opt])
+ )
+
+ # remove options (replaced only)
+ if state == "replaced":
+
+ for opt in existing_opts - desired_opts:
+
+ cmds.append(
+ ("delete", ["service", "ntp", "server", server, opt])
+ )
+
+ # delete
+ if state in ["replaced", "deleted"]:
+
+ for server in existing_set - desired_set:
+
+ cmds.append(("delete", ["service", "ntp", "server", server]))
+
+ return cmds
+
+
+def parse_running_config(text):
+
+ result = {
+ "allow_clients": [],
+ "listen_addresses": [],
+ "servers": {}
+ }
+
+ for line in text.splitlines():
+
+ parts = line.strip().split()
+
+ if len(parts) < 4:
+ continue
+
+ if parts[3] == "allow-clients":
+ result["allow_clients"].append(parts[-1])
+
+ elif parts[3] == "listen-address":
+ result["listen_addresses"].append(parts[-1])
+
+ elif parts[3] == "server":
+
+ server = parts[4]
+
+ if server not in result["servers"]:
+ result["servers"][server] = []
+
+ if len(parts) > 5:
+ result["servers"][server].append(parts[5])
+
+ return result
+
+
+def render_commands(config):
+
+ cmds = []
+
+ for c in config["allow_clients"]:
+ cmds.append(f"set service ntp allow-clients address {c}")
+
+ for l in config["listen_addresses"]:
+ cmds.append(f"set service ntp listen-address {l}")
+
+ for server, opts in config["servers"].items():
+
+ if not opts:
+ cmds.append(f"set service ntp server {server}")
+
+ for opt in opts:
+ cmds.append(f"set service ntp server {server} {opt}")
+
+ return cmds
+
+# ------------------------------------------------------------
+# Main
+# ------------------------------------------------------------
+
+def main():
+
+ argument_spec = dict(
+
+ config=dict(
+ type="dict",
+ options=dict(
+ allow_clients=dict(type="list", elements="str"),
+ listen_addresses=dict(type="list", elements="str"),
+ servers=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ server=dict(type="str", required=True),
+ options=dict(type="list", elements="str")
+ )
+ )
+ )
+ ),
+
+ running_config=dict(type="str"),
+
+ state=dict(
+ default="merged",
+ choices=[
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "gathered",
+ "rendered",
+ "parsed"
+ ]
+ )
+ )
+
+ module = AnsibleModule(argument_spec, supports_check_mode=True)
+
+ vyos = VyOSModule(module)
+
+ state = module.params["state"]
+ config = module.params.get("config") or {}
+
+ result = {
+ "changed": False
+ }
+
+ # --------------------------------------------------------
+ # parsed
+ # --------------------------------------------------------
+
+ if state == "parsed":
+
+ parsed = parse_running_config(module.params["running_config"])
+
+ module.exit_json(parsed=parsed)
+
+ # --------------------------------------------------------
+ # rendered
+ # --------------------------------------------------------
+
+ desired = normalize_config(config)
+
+ if state == "rendered":
+
+ module.exit_json(rendered=render_commands(desired))
+
+ # --------------------------------------------------------
+ # gathered
+ # --------------------------------------------------------
+
+ existing = get_running_config(vyos)
+
+ if state == "gathered":
+
+ module.exit_json(gathered=existing)
+
+ # --------------------------------------------------------
+ # deleted
+ # --------------------------------------------------------
+
+ if state == "deleted":
+
+ desired = {
+ "allow_clients": [],
+ "listen_addresses": [],
+ "servers": {}
+ }
+
+ # --------------------------------------------------------
+ # diff engine
+ # --------------------------------------------------------
+
+ commands = build_commands(desired, existing, state)
+
+ result["before"] = existing
+
+ result["commands"] = commands
+
+ if commands:
+
+ result["changed"] = True
+
+ if not module.check_mode:
+
+ vyos.apply_commands(commands)
+
+ result["after"] = desired
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main() \ No newline at end of file