summaryrefslogtreecommitdiff
path: root/plugins/modules/vyos_configure.py
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/modules/vyos_configure.py')
-rw-r--r--plugins/modules/vyos_configure.py276
1 files changed, 77 insertions, 199 deletions
diff --git a/plugins/modules/vyos_configure.py b/plugins/modules/vyos_configure.py
index 355a78c..02fc7ad 100644
--- a/plugins/modules/vyos_configure.py
+++ b/plugins/modules/vyos_configure.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# GNU General Public License v3.0+
from __future__ import absolute_import, division, print_function
@@ -10,249 +10,127 @@ __metaclass__ = type
DOCUMENTATION = r"""
---
module: vyos_configure
-short_description: Manage VyOS device configuration via the REST API.
+short_description: Send raw set/delete commands to a VyOS device via REST API.
description:
- - Sends one or more C(set) or C(delete) configuration commands to a VyOS
- device via its HTTPS REST API (C(/configure) endpoint).
- - Each command in I(commands) is executed and committed atomically.
- - Supports Ansible check-mode (C(check_mode=yes)) - no changes are applied.
+ - Sends one or more set/delete configuration commands to a VyOS device
+ via the HTTPS REST API as a single atomic batch commit.
+ - Useful for configuration not covered by dedicated resource modules,
+ or for test setup and teardown tasks.
+ - Commands are parsed from CLI-style strings (C(set ...) / C(delete ...)).
version_added: "1.0.0"
author:
- VyOS Community (@vyos)
options:
commands:
description:
- - List of VyOS configuration commands.
- - Each item may be either a plain string in VyOS CLI syntax
- (e.g. C(set interfaces ethernet eth0 description 'WAN'))
- or a dict with keys C(op), C(path), and optionally C(value).
+ - List of CLI-style configuration commands.
+ - Each command must start with C(set) or C(delete).
type: list
- elements: raw
+ elements: str
required: true
save:
description:
- - Whether to save the configuration to disk after a successful commit.
+ - Whether to save the configuration after applying commands.
type: bool
default: false
- hostname:
- description:
- - IP address or FQDN of the VyOS device.
- type: str
- required: true
- port:
- description:
- - HTTPS port for the REST API.
- type: int
- default: 443
- api_key:
- description:
- - API key configured on the device.
- type: str
- required: true
- no_log: true
- timeout:
- description:
- - Request timeout in seconds.
- type: int
- default: 30
- verify_ssl:
- description:
- - Validate the device's TLS certificate.
- type: bool
- default: false
-notes:
- - The VyOS HTTP API must be enabled on the device before using this module.
- - Enable with C(set service https api keys id <ID> key <KEY>) and
- C(set service https api rest), then C(commit && save).
- - Tested against VyOS 1.3 and 1.4.
-requirements:
- - VyOS 1.3+
seealso:
- - module: vyos.rest.vyos_retrieve
- - module: vyos.rest.vyos_show"""
-
-RETURN = r"""
-commands_sent:
- description: The list of commands dispatched to the API.
- returned: always
- type: list
- elements: dict
- sample:
- - op: set
- path: ["system", "host-name"]
- value: vyos-router
-changed:
- description: Whether the device configuration was modified.
- returned: always
- type: bool
-saved:
- description: Whether the configuration was saved to disk.
- returned: always
- type: bool
+ - module: vyos.vyos.vyos_config
"""
-import re
-
-
EXAMPLES = r"""
-- name: Set hostname via REST API
+- name: Add loopback address for testing
vyos.rest.vyos_configure:
- hostname: 192.168.1.1
- api_key: MY-KEY
commands:
- - "set system host-name vyos-router"
+ - set interfaces loopback lo address 20.1.1.1/32
+ save: false
-- name: Configure an interface description and address
+- name: Remove loopback address after testing
vyos.rest.vyos_configure:
- hostname: 192.168.1.1
- api_key: MY-KEY
commands:
- - "set interfaces ethernet eth1 description 'UPLINK'"
- - "set interfaces ethernet eth1 address 10.0.0.1/30"
- save: true
+ - delete interfaces loopback lo address 20.1.1.1/32
+ save: false
-- name: Delete a static route using dict syntax
+- name: Multiple commands in one atomic commit
vyos.rest.vyos_configure:
- hostname: 192.168.1.1
- api_key: MY-KEY
commands:
- - op: delete
- path: ["protocols", "static", "route", "192.0.2.0/24"]
+ - set system host-name vyos-test
+ - set system domain-name example.com
+ save: true
"""
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import (
- VYOS_REST_CONNECTION_ARGSPEC,
- VyOSRestClient,
- VyOSRestError,
-)
-
+RETURN = r"""
+commands:
+ description: Parsed command payloads sent to the device.
+ returned: always
+ type: list
+response:
+ description: Raw API response.
+ returned: when commands are applied
+ type: dict
+"""
-def _parse_cli_command(cmd_str):
- """Parse a VyOS CLI-syntax string into an API payload dict.
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.vyos.rest.plugins.module_utils.vyos import VyOSModule
- Supports:
- set <path> [<value>]
- delete <path>
- comment <path> <comment>
- Args:
- cmd_str (str): e.g. "set interfaces ethernet eth0 description 'WAN'"
+def _parse_command(line):
+ """Parse a CLI-style command string into (op, path) tuple.
- Returns:
- dict: e.g. {"op": "set", "path": [...], "value": "WAN"}
+ Examples:
+ "set interfaces loopback lo address 20.1.1.1/32"
+ -> ("set", ["interfaces", "loopback", "lo", "address", "20.1.1.1/32"])
+ "delete service snmp"
+ -> ("delete", ["service", "snmp"])
"""
- cmd_str = cmd_str.strip()
- op_match = re.match(r"^(set|delete|comment)\s+(.+)$", cmd_str, re.IGNORECASE)
- if not op_match:
- raise ValueError(
- "Cannot parse command '{cmd}'. Must start with set/delete/comment.".format(
- cmd=cmd_str,
- ),
- )
- op = op_match.group(1).lower()
- rest = op_match.group(2).strip()
-
- # Extract a quoted or bare trailing value for 'set'
- value = None
- if op == "set":
- # Look for a trailing quoted value: '...' or "..."
- quoted = re.search(r"""(['"])(.*?)\1\s*$""", rest)
- if quoted:
- value = quoted.group(2)
- rest = rest[: quoted.start()].strip()
- else:
- # Bare final token may be the value if the second-to-last token
- # is a known leaf keyword – use a simple heuristic: if the
- # last segment has no sub-tokens we treat it as value.
- tokens = rest.split()
- # We cannot know the schema here, so we pass the full path and
- # no value; the device will decide.
- path = tokens
- return {"op": op, "path": path}
-
- path = rest.split()
- payload = {"op": op, "path": path}
- if value is not None:
- payload["value"] = value
- return payload
-
-
-def _normalise_commands(raw_commands):
- """Normalise the mixed list of str/dict commands into API dicts."""
- result = []
- for item in raw_commands:
- if isinstance(item, dict):
- if "op" not in item or "path" not in item:
- raise ValueError(
- "Command dict must have 'op' and 'path' keys: {item}".format(
- item=item,
- ),
- )
- result.append(item)
- elif isinstance(item, str):
- result.append(_parse_cli_command(item))
- else:
- raise ValueError(
- "Command must be a string or dict, got: {t}".format(t=type(item)),
- )
- return result
+ line = line.strip()
+ if line.startswith("set "):
+ parts = line[4:].split()
+ return ("set", parts)
+ elif line.startswith("delete "):
+ parts = line[7:].split()
+ return ("delete", parts)
+ else:
+ return None
def main():
- argument_spec = dict(
- commands=dict(type="list", elements="raw", required=True),
- save=dict(type="bool", default=False),
- )
- argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC)
-
module = AnsibleModule(
- argument_spec=argument_spec,
+ argument_spec=dict(
+ commands=dict(type="list", elements="str", required=True),
+ save=dict(type="bool", default=False),
+ ),
supports_check_mode=True,
)
- try:
- commands = _normalise_commands(module.params["commands"])
- except ValueError as exc:
- module.fail_json(msg=str(exc))
+ vyos = VyOSModule(module)
+ commands_raw = module.params["commands"]
+ do_save = module.params["save"]
- if module.check_mode:
- module.exit_json(
- changed=True,
- commands_sent=commands,
- saved=False,
- msg="Check mode: no changes applied.",
- )
+ commands = []
+ for line in commands_raw:
+ parsed = _parse_command(line)
+ if parsed is None:
+ module.fail_json(
+ msg="Invalid command '{c}' — must start with 'set' or 'delete'".format(c=line),
+ )
+ commands.append(parsed)
- client = VyOSRestClient(module)
- changed = False
- saved = False
+ if not commands:
+ module.exit_json(changed=False, commands=[])
- try:
- for cmd in commands:
- op = cmd["op"].lower()
- path = cmd["path"]
- value = cmd.get("value")
- if op == "set":
- client.configure_set(path, value)
- elif op == "delete":
- client.configure_delete(path)
- elif op == "comment":
- client.configure_comment(path, value or "")
- else:
- module.fail_json(
- msg="Unsupported op '{op}'".format(op=op),
- )
- changed = True
+ if module.check_mode:
+ module.exit_json(changed=True, commands=commands)
- if module.params["save"]:
- client.config_file_save()
- saved = True
+ response = vyos.apply_commands(commands)
- except VyOSRestError as exc:
- module.fail_json(msg=str(exc), commands_sent=commands)
+ if do_save:
+ vyos.save_config()
- module.exit_json(changed=changed, commands_sent=commands, saved=saved)
+ module.exit_json(
+ changed=True,
+ commands=commands,
+ response=response,
+ )
if __name__ == "__main__":