summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/httpapi/vyos.py8
-rw-r--r--plugins/module_utils/vyos.py113
-rw-r--r--plugins/module_utils/vyos_rest.py48
-rw-r--r--plugins/modules/vyos_ntp_global.py13
4 files changed, 93 insertions, 89 deletions
diff --git a/plugins/httpapi/vyos.py b/plugins/httpapi/vyos.py
index 1719254..6dd6042 100644
--- a/plugins/httpapi/vyos.py
+++ b/plugins/httpapi/vyos.py
@@ -105,11 +105,15 @@ class HttpApi(HttpApiBase):
Raises:
ConnectionError: on HTTP error or VyOS success=false response.
"""
+
try:
api_key = self._get_api_key()
- body = json.dumps(payload)
- form_data = urlencode({"data": body, "key": api_key})
+ if "_raw_list" in payload:
+ body = json.dumps(payload["_raw_list"])
+ else:
+ body = json.dumps(payload)
+ form_data = urlencode({"data": body, "key": api_key})
response, response_data = self.connection.send(
endpoint,
data=form_data,
diff --git a/plugins/module_utils/vyos.py b/plugins/module_utils/vyos.py
index 87fc37b..82670e7 100644
--- a/plugins/module_utils/vyos.py
+++ b/plugins/module_utils/vyos.py
@@ -62,58 +62,75 @@ class VyOSModule:
# Write
# ------------------------------------------------------------------
- def apply_commands(self, commands):
- """Execute a list of ``(op, path)`` command tuples against the device.
-
- Args:
- commands (list): Each item is a 2-tuple ``(op, path)`` where:
- - *op* is ``"set"`` or ``"delete"``
- - *path* is a list of config path tokens
-
- Optionally a 3-tuple ``(op, path, value)`` for leaf nodes
- that carry a value.
-
- Returns:
- list: Results from each command (dicts with ``op`` and ``path``).
-
- Raises:
- VyOSRestError: If a command fails and cannot be recovered.
-
- Notes:
- **Version-adaptive set:** if a ``set`` is rejected by the device,
- the method retries once with the last path segment removed.
- This handles schema simplifications between VyOS releases
- (e.g. ``allow-client address <prefix>`` -> ``allow-client <prefix>``
- in VyOS 1.5+).
-
- **Idempotent delete:** ``delete`` failures are silently ignored
- (the path is already absent).
- """
- results = []
+ # def apply_commands(self, commands):
+ # """Execute a list of ``(op, path)`` command tuples against the device.
+
+ # Args:
+ # commands (list): Each item is a 2-tuple ``(op, path)`` where:
+ # - *op* is ``"set"`` or ``"delete"``
+ # - *path* is a list of config path tokens
+
+ # Optionally a 3-tuple ``(op, path, value)`` for leaf nodes
+ # that carry a value.
+
+ # Returns:
+ # list: Results from each command (dicts with ``op`` and ``path``).
+
+ # Raises:
+ # VyOSRestError: If a command fails and cannot be recovered.
+
+ # Notes:
+ # **Version-adaptive set:** if a ``set`` is rejected by the device,
+ # the method retries once with the last path segment removed.
+ # This handles schema simplifications between VyOS releases
+ # (e.g. ``allow-client address <prefix>`` -> ``allow-client <prefix>``
+ # in VyOS 1.5+).
+
+ # **Idempotent delete:** ``delete`` failures are silently ignored
+ # (the path is already absent).
+ # """
+ # results = []
+
+ # for cmd in commands:
+ # # Support two command formats:
+ # # Tuple: ("set", ["path", "tokens"]) or ("set", ["path"], "value")
+ # # Dict: {"op": "set", "path": ["path", "tokens"]}
+ # if isinstance(cmd, dict):
+ # op = cmd["op"]
+ # path = list(cmd["path"])
+ # value = cmd.get("value")
+ # else:
+ # op = cmd[0]
+ # path = list(cmd[1])
+ # value = cmd[2] if len(cmd) > 2 else None
+
+ # if op == "set":
+ # results.append(self._apply_set(path, value))
+ # elif op == "delete":
+ # results.append(self._apply_delete(path))
+ # else:
+ # self._module.fail_json(
+ # msg="Unknown command op '{op}' in apply_commands".format(op=op),
+ # )
+
+ # return results
+ def apply_commands(self, commands):
+ if not commands:
+ return []
+ payload = []
for cmd in commands:
- # Support two command formats:
- # Tuple: ("set", ["path", "tokens"]) or ("set", ["path"], "value")
- # Dict: {"op": "set", "path": ["path", "tokens"]}
if isinstance(cmd, dict):
- op = cmd["op"]
- path = list(cmd["path"])
- value = cmd.get("value")
- else:
- op = cmd[0]
- path = list(cmd[1])
- value = cmd[2] if len(cmd) > 2 else None
-
- if op == "set":
- results.append(self._apply_set(path, value))
- elif op == "delete":
- results.append(self._apply_delete(path))
+ op, path = cmd["op"], list(cmd["path"])
else:
- self._module.fail_json(
- msg="Unknown command op '{op}' in apply_commands".format(op=op),
- )
-
- return results
+ op, path = cmd[0], list(cmd[1])
+ payload.append({"op": op, "path": path})
+ try:
+ return self._client.configure_batch(payload)
+ except VyOSRestError as exc:
+ self._module.fail_json(
+ msg="apply_commands failed: {e}".format(e=str(exc)),
+ )
def _apply_set(self, path, value=None):
"""Set a config path, retrying with a shortened path on failure."""
diff --git a/plugins/module_utils/vyos_rest.py b/plugins/module_utils/vyos_rest.py
index 95b3aac..aaf2b23 100644
--- a/plugins/module_utils/vyos_rest.py
+++ b/plugins/module_utils/vyos_rest.py
@@ -1,36 +1,5 @@
"""
VyOS REST API client utility — supports both connection modes.
-
-**httpapi mode (recommended)**::
-
- # inventory
- vyos15x ansible_host=192.168.122.194
- ansible_network_os=vyos.rest.vyos
- ansible_connection=ansible.netcommon.httpapi
- ansible_httpapi_use_ssl=true
- ansible_httpapi_port=443
- ansible_httpapi_validate_certs=false
- ansible_httpapi_api_key=vyos123
-
- # playbook — no connection params needed on any task
- - vyos.rest.vyos_banner:
- banner: pre-login
- text: "Authorised use only."
- state: present
-
-**local mode (alternative, no inventory network_os required)**::
-
- - vyos.rest.vyos_banner:
- hostname: 192.168.122.194
- api_key: vyos123
- verify_ssl: false
- banner: pre-login
- text: "Authorised use only."
- state: present
-
-Both modes call the same VyOS endpoints. The httpapi mode routes calls
-through the persistent connection daemon (better for large inventories);
-the local mode opens a new HTTPS connection per task.
"""
from __future__ import absolute_import, division, print_function
@@ -45,7 +14,7 @@ import os
try:
from urllib.parse import urlencode
except ImportError:
- from urllib import urlencode # Python 2
+ from urllib import urlencode
try:
import urllib3
@@ -218,6 +187,21 @@ class VyOSRestClient:
def image_delete(self, name):
return self._post("/image", {"op": "delete", "name": name})
+ def configure_batch(self, commands):
+ """Send all commands as a single atomic commit."""
+ if self._mode == "httpapi":
+ try:
+ return self._conn.send_request(
+ endpoint="/configure",
+ _raw_list=commands,
+ )
+ except ConnectionError as exc:
+ raise VyOSRestError(str(exc))
+ except Exception as exc:
+ raise VyOSRestError(
+ "configure_batch failed: {e}".format(e=str(exc)),
+ )
+
def config_file_save(self, path=None):
payload = {"op": "save"}
if path:
diff --git a/plugins/modules/vyos_ntp_global.py b/plugins/modules/vyos_ntp_global.py
index 185dc02..88073e1 100644
--- a/plugins/modules/vyos_ntp_global.py
+++ b/plugins/modules/vyos_ntp_global.py
@@ -218,13 +218,12 @@ def build_commands(desired, existing, state):
cmds = []
if state == "overridden":
- if existing["allow_clients"]:
- cmds.append(("delete", ["service", "ntp", "allow-client"]))
- if existing["listen_addresses"]:
- cmds.append(("delete", ["service", "ntp", "listen-address"]))
- if existing["servers"]:
- cmds.append(("delete", ["service", "ntp", "server"]))
- state = "merged"
+ state = "replaced"
+
+ if state == "deleted":
+ if existing["servers"] or existing["allow_clients"] or existing["listen_addresses"]:
+ cmds.append(("delete", ["service", "ntp"]))
+ return cmds
cmds += diff_list(
"allow-client",