diff options
| author | omnom62 <omnom62@outlook.com> | 2026-07-06 14:54:12 +1000 |
|---|---|---|
| committer | John Estabrook <jestabro@vyos.io> | 2026-08-21 14:02:19 -0500 |
| commit | 45dc95873fd906c582fbbd5e6ca3838caf867399 (patch) | |
| tree | 71b3e53e0080da60abf9fd2381705971dfe4d507 /plugins/modules/vyos_command.py | |
| parent | 7a6b5e4f3a7a021cfa75faa7bf833741dfc09cff (diff) | |
| download | rest.vyos-45dc95873fd906c582fbbd5e6ca3838caf867399.tar.gz rest.vyos-45dc95873fd906c582fbbd5e6ca3838caf867399.zip | |
T8989: wave4 vyos_command, dict_op refactorT8989_wave4
* T8989: vyos_command module
* T8989: vyos_command module UAT and SIT
* T8989: vyos_command changelog
* T8989: vyos_command linter
* T8989: vyos_config module
* T8989: vyos_config module changelog
* T8989: Wave 4 vyos_config module with integration and unit tests
* T8323: vyos_system module
* T8332: vyos_system SIT and UAT
* T8323: vyos_vlan module
* T8323: vyos_vlan module
* T8323: vyos_vlan module SIT and UAT
* T8323: vyos_system module
* T8989: Wave 4 vyos_vlan reworked with dict_op engine
* T8989: Fix dict_op single-value string list handling, add vyos_system integration tests
* T8989: logging_global refactor
* T8989: migrate ntp_global, logging_global, firewall_global to dict_op engine
* T8989: vyos_nat module for REST API collection
* T8989: vyos_nat module for REST API collection, linter fixes
* T8989: vyos_ha module for REST API collection
* T8989: vyos_ha module for REST API collection
* T8989: vyos_ha module sanity and linter fixes
* T8989: vyos_ha module sanity and linter fixes
* T8989: vyos_ha module linter fixes
* T8989: vyos.rest AI comment fixes
* T8323: vyos_nat AI comment fixes
* T8989 ai fixes
* T8989: vyos_bgp_address_family dict_op
* T8989: vyos_bgp_address_family vyos_bgp_global dict_op
* T8989: dict_op refactor for firewall_*, nat, user
* T8989: dict_op refactor for firewall_*, nat, user
* T8989: dict_op refactor for ntp_global, ha
* T8989: snmp_server dict_op refactor
* T8989: snmp_server dict_op refactor
* T8989: route_map dict_op refactor
Diffstat (limited to 'plugins/modules/vyos_command.py')
| -rw-r--r-- | plugins/modules/vyos_command.py | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/plugins/modules/vyos_command.py b/plugins/modules/vyos_command.py new file mode 100644 index 0000000..d688251 --- /dev/null +++ b/plugins/modules/vyos_command.py @@ -0,0 +1,228 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# GNU General Public License v3.0+ +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: vyos_command +short_description: Run show commands on VyOS devices using REST API +description: + - Sends show commands to VyOS devices via the REST API C(/show) endpoint + and returns the output. + - Equivalent to C(vyos_command) in the CLI collection but uses the REST API. + - Uses REST API (C(connection=httpapi)) instead of CLI. +version_added: "1.0.0" +author: + - VyOS Community (@vyos) +options: + commands: + description: + - List of show commands to run on the device. + - Each command is a list of path elements passed to the C(/show) endpoint. + - Commands may be specified as a string (space-separated) or a list. + type: list + elements: raw + required: true + wait_for: + description: + - Specifies what to evaluate from the output of the command and what + conditionals to apply. This argument will cause the task to wait for + a particular conditional to be true before moving forward. + type: list + elements: str + aliases: [waitfor] + match: + description: + - The C(match) argument is used in conjunction with the C(wait_for) + argument to specify the match policy. + type: str + choices: [any, all] + default: all + retries: + description: + - Specifies the number of retries a command should be run before it + is considered failed. + type: int + default: 10 + interval: + description: + - Configures the interval in seconds to wait between retries of the + command. + type: int + default: 1 +notes: + - Requires C(ansible_connection=httpapi) with the VyOS httpapi plugin. + - C(ansible_network_os) must be set to C(vyos.rest.vyos). + - Only C(show) commands are supported via the REST API. + - Commands are passed as path lists to the C(/show) endpoint. +""" + +EXAMPLES = r""" +- name: Run show version + vyos.rest.vyos_command: + commands: + - - version + register: result + +- name: Run multiple show commands + vyos.rest.vyos_command: + commands: + - - interfaces + - - ip + - route + - - system + - uptime + register: result + +- name: Run show commands as strings + vyos.rest.vyos_command: + commands: + - "interfaces" + - "ip route" + - "version" + register: result + +- name: Wait for BGP to establish + vyos.rest.vyos_command: + commands: + - - ip + - bgp + - summary + wait_for: + - result[0] contains Established + retries: 10 + interval: 5 +""" + +RETURN = r""" +stdout: + description: List of output from each command. + returned: always + type: list + sample: ["VyOS 1.5.0\n...", "Interface IP Address\n..."] +stdout_lines: + description: List of output split into lines for each command. + returned: always + type: list +failed_conditions: + description: List of conditions that failed. + returned: failed + type: list +""" + +import time + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.rest.plugins.module_utils.vyos import VyOSModule + + +def parse_command(cmd): + """Convert a command to a path list.""" + if isinstance(cmd, list): + return cmd + elif isinstance(cmd, str): + return cmd.split() + return list(cmd) + + +def run_commands(vyos, commands): + """Run show commands and return stdout list.""" + stdout = [] + for cmd in commands: + path = parse_command(cmd) + try: + result = vyos.show(path) + stdout.append(result if result else "") + except Exception as e: + stdout.append("ERROR: %s" % str(e)) + return stdout + + +def evaluate_conditions(stdout, wait_for, match): + """Evaluate wait_for conditions against stdout.""" + failed = [] + results = [] + + for condition in wait_for: + # Parse simple conditions: "result[N] contains STRING" + if " contains " in condition: + parts = condition.split(" contains ", 1) + ref = parts[0].strip() + value = parts[1].strip() + # Extract index from result[N] + if ref.startswith("result[") and ref.endswith("]"): + try: + idx = int(ref[7:-1]) + matched = value in stdout[idx] + results.append(matched) + if not matched: + failed.append(condition) + except (ValueError, IndexError): + failed.append(condition) + else: + failed.append(condition) + else: + # Unsupported condition format + failed.append(condition) + + if match == "any": + return not any(results), failed + return bool(failed), failed + + +def main(): + module = AnsibleModule( + argument_spec=dict( + commands=dict(type="list", elements="raw", required=True), + wait_for=dict(type="list", elements="str", aliases=["waitfor"]), + match=dict(type="str", default="all", choices=["any", "all"]), + retries=dict(type="int", default=10), + interval=dict(type="int", default=1), + ), + supports_check_mode=True, + ) + + vyos = VyOSModule(module) + commands = module.params["commands"] + wait_for = module.params["wait_for"] or [] + match = module.params["match"] + retries = module.params["retries"] + interval = module.params["interval"] + + stdout = [] + failed_conditions = [] + + for attempt in range(retries): + stdout = run_commands(vyos, commands) + + if not wait_for: + break + + failed_check, failed_conditions = evaluate_conditions(stdout, wait_for, match) + if not failed_check: + break + + if attempt < retries - 1: + time.sleep(interval) + else: + if failed_conditions: + module.fail_json( + msg="One or more conditional statements have not been satisfied", + failed_conditions=failed_conditions, + ) + + stdout_lines = [out.splitlines() for out in stdout] + + module.exit_json( + changed=False, + stdout=stdout, + stdout_lines=stdout_lines, + ) + + +if __name__ == "__main__": + main() |
