diff options
| author | omnom62 <omnom62@outlook.com> | 2026-06-01 05:42:31 +1000 |
|---|---|---|
| committer | omnom62 <omnom62@outlook.com> | 2026-06-01 05:42:31 +1000 |
| commit | 967a8c581df46f3c01d642ed08ec0d7ec43d79f6 (patch) | |
| tree | dccfe21768a6629ac85de2fb70136cd1baf9be3f | |
| parent | 6a161ed8f263e152ebfd227e669a51773de5a028 (diff) | |
| download | rest.vyos-967a8c581df46f3c01d642ed08ec0d7ec43d79f6.tar.gz rest.vyos-967a8c581df46f3c01d642ed08ec0d7ec43d79f6.zip | |
removing previews
25 files changed, 0 insertions, 6711 deletions
diff --git a/plugins/modules/vyos_bgp_address_family.py.preview b/plugins/modules/vyos_bgp_address_family.py.preview deleted file mode 100644 index ff80053..0000000 --- a/plugins/modules/vyos_bgp_address_family.py.preview +++ /dev/null @@ -1,354 +0,0 @@ -#!/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_bgp_address_family -short_description: BGP Address Family resource module via REST API. -description: - - Manages BGP address-family configuration (networks, redistribute, - aggregate-address, per-neighbor AFI settings) via the VyOS HTTPS REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: BGP address-family configuration. - type: dict - suboptions: - as_number: - description: Local AS number. - type: int - required: true - address_family: - description: Global address-family parameters. - type: list - elements: dict - suboptions: - afi: - description: Address family (ipv4 or ipv6). - type: str - choices: [ipv4, ipv6] - aggregate_address: - description: List of BGP aggregate networks. - type: list - elements: dict - suboptions: - prefix: - type: str - as_set: - type: bool - summary_only: - type: bool - networks: - description: Networks to originate. - type: list - elements: dict - suboptions: - prefix: - type: str - path_limit: - type: int - backdoor: - type: bool - route_map: - type: str - redistribute: - description: Protocols to redistribute. - type: list - elements: dict - suboptions: - protocol: - type: str - choices: [connected, kernel, ospf, ospfv3, rip, ripng, static] - table: - type: str - route_map: - type: str - metric: - type: int - neighbors: - description: Per-neighbor address-family settings. - type: list - elements: dict - suboptions: - neighbor_address: - description: Neighbor IP. - type: str - address_family: - type: list - elements: dict - suboptions: - afi: - type: str - choices: [ipv4, ipv6] - allowas_in: - type: int - as_override: - type: bool - route_map: - description: Route-map settings. - type: dict - suboptions: - import_map: - type: str - export_map: - type: str - soft_reconfiguration: - type: bool - nexthop_self: - type: bool - remove_private_as: - type: bool - state: - description: Desired state. - type: str - choices: [merged, replaced, deleted, gathered] - default: merged - hostname: - description: Device IP or FQDN. - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - description: Config before module ran. - returned: always - type: dict -after: - description: Config after module ran. - returned: when changed - type: dict -commands: - description: Commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def _get_bgp(client): - try: - result = client.retrieve_show_config(["protocols", "bgp"]) - return result.get("data") or {} - except VyOSRestError: - return {} - - -def _afi_unicast_key(afi): - return "ipv4-unicast" if afi == "ipv4" else "ipv6-unicast" - - -def _apply(client, config, commands): - asn = str(config["as_number"]) - base = ["protocols", "bgp", asn] - - for af in config.get("address_family") or []: - afi = af["afi"] - af_base = base + ["address-family", _afi_unicast_key(afi)] - - for agg in af.get("aggregate_address") or []: - p = agg["prefix"] - agg_base = af_base + ["aggregate-address", p] - client.configure_set(agg_base) - commands.append("set {b}".format(b=" ".join(agg_base))) - if agg.get("as_set"): - client.configure_set(agg_base + ["as-set"]) - if agg.get("summary_only"): - client.configure_set(agg_base + ["summary-only"]) - - for net in af.get("networks") or []: - net_base = af_base + ["network", net["prefix"]] - client.configure_set(net_base) - commands.append("set {b}".format(b=" ".join(net_base))) - if net.get("route_map"): - client.configure_set(net_base + ["route-map"], net["route_map"]) - if net.get("backdoor"): - client.configure_set(net_base + ["backdoor"]) - - for redist in af.get("redistribute") or []: - r_base = af_base + ["redistribute", redist["protocol"]] - client.configure_set(r_base) - commands.append("set {b}".format(b=" ".join(r_base))) - if redist.get("metric"): - client.configure_set(r_base + ["metric"], str(redist["metric"])) - if redist.get("route_map"): - client.configure_set(r_base + ["route-map"], redist["route_map"]) - - for nbr in config.get("neighbors") or []: - n_base = base + ["neighbor", nbr["neighbor_address"]] - for af in nbr.get("address_family") or []: - afi = af["afi"] - n_af_base = n_base + ["address-family", _afi_unicast_key(afi)] - client.configure_set(n_af_base) - commands.append("set {b}".format(b=" ".join(n_af_base))) - if af.get("allowas_in"): - client.configure_set(n_af_base + ["allowas-in", "number"], str(af["allowas_in"])) - if af.get("as_override"): - client.configure_set(n_af_base + ["as-override"]) - if af.get("nexthop_self"): - client.configure_set(n_af_base + ["nexthop-self"]) - if af.get("remove_private_as"): - client.configure_set(n_af_base + ["remove-private-as"]) - if af.get("soft_reconfiguration"): - client.configure_set(n_af_base + ["soft-reconfiguration", "inbound"]) - rm = af.get("route_map") or {} - if rm.get("import_map"): - client.configure_set(n_af_base + ["route-map", "import"], rm["import_map"]) - if rm.get("export_map"): - client.configure_set(n_af_base + ["route-map", "export"], rm["export_map"]) - - -def main(): - argument_spec = dict( - config=dict( - type="dict", - options=dict( - as_number=dict(type="int", required=True), - address_family=dict( - type="list", - elements="dict", - options=dict( - afi=dict(type="str", choices=["ipv4", "ipv6"]), - aggregate_address=dict( - type="list", - elements="dict", - options=dict( - prefix=dict(type="str"), - as_set=dict(type="bool"), - summary_only=dict(type="bool"), - ), - ), - networks=dict( - type="list", - elements="dict", - options=dict( - prefix=dict(type="str"), - path_limit=dict(type="int"), - backdoor=dict(type="bool"), - route_map=dict(type="str"), - ), - ), - redistribute=dict( - type="list", - elements="dict", - options=dict( - protocol=dict( - type="str", - choices=[ - "connected", - "kernel", - "ospf", - "ospfv3", - "rip", - "ripng", - "static", - ], - ), - table=dict(type="str"), - route_map=dict(type="str"), - metric=dict(type="int"), - ), - ), - ), - ), - neighbors=dict( - type="list", - elements="dict", - options=dict( - neighbor_address=dict(type="str"), - address_family=dict( - type="list", - elements="dict", - options=dict( - afi=dict(type="str", choices=["ipv4", "ipv6"]), - allowas_in=dict(type="int"), - as_override=dict(type="bool"), - nexthop_self=dict(type="bool"), - remove_private_as=dict(type="bool"), - soft_reconfiguration=dict(type="bool"), - route_map=dict( - type="dict", - options=dict( - import_map=dict(type="str"), - export_map=dict(type="str"), - ), - ), - ), - ), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule( - argument_spec=argument_spec, - required_if=[("state", "merged", ["config"]), ("state", "replaced", ["config"])], - supports_check_mode=True, - ) - - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") - commands = [] - changed = False - before = _get_bgp(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - if before: - client.configure_delete(["protocols", "bgp"]) - commands.append("delete protocols bgp") - changed = True - elif state in ("merged", "replaced"): - if state == "replaced" and before: - client.configure_delete(["protocols", "bgp"]) - commands.append("delete protocols bgp") - _apply(client, config, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get_bgp(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_bgp_global.py.preview b/plugins/modules/vyos_bgp_global.py.preview deleted file mode 100644 index 6a08f4f..0000000 --- a/plugins/modules/vyos_bgp_global.py.preview +++ /dev/null @@ -1,365 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_bgp_global -short_description: Manage BGP global configuration on VyOS via the REST API. -description: - - Manages BGP global parameters on VyOS devices using the HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_bgp_global) but uses the HTTP API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: BGP global configuration. - type: dict - suboptions: - as_number: - description: Local BGP AS number. - type: int - required: true - router_id: - description: BGP router ID (IPv4 address). - type: str - neighbors: - description: List of BGP neighbor configurations. - type: list - elements: dict - suboptions: - neighbor: - description: Neighbor IP address. - type: str - required: true - remote_as: - description: Neighbor's AS number. - type: int - description: - description: Neighbor description. - type: str - password: - description: MD5 authentication password. - type: str - no_log: true - update_source: - description: Local interface or address for BGP session. - type: str - ebgp_multihop: - description: Maximum hops for eBGP sessions. - type: int - shutdown: - description: Whether to administratively shut down the neighbor. - type: bool - networks: - description: List of networks to originate. - type: list - elements: dict - suboptions: - prefix: - description: Network prefix to advertise. - type: str - required: true - route_map: - description: Route map to apply. - type: str - redistribute: - description: List of protocols to redistribute into BGP. - type: list - elements: dict - suboptions: - protocol: - description: Protocol to redistribute. - type: str - choices: [connected, kernel, ospf, ospfv3, rip, ripng, static] - required: true - metric: - description: Metric for redistributed routes. - type: int - route_map: - description: Route map to apply. - type: str - state: - description: - - C(merged): Merge BGP config with existing. - - C(replaced): Replace entire BGP config. - - C(deleted): Remove BGP configuration. - - C(gathered): Read BGP config from device. - type: str - choices: [merged, replaced, deleted, gathered] - default: merged - 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 -requirements: - - VyOS 1.3+ -seealso: - - module: vyos.vyos.vyos_bgp_global -examples: | - - name: Configure BGP - vyos.rest.vyos_bgp_global: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - as_number: 65001 - router_id: 192.168.1.1 - neighbors: - - neighbor: 192.168.2.1 - remote_as: 65002 - description: "Peer AS65002" - networks: - - prefix: 10.0.0.0/8 - redistribute: - - protocol: connected - state: merged - - - name: Remove BGP configuration - vyos.rest.vyos_bgp_global: - hostname: 192.168.1.1 - api_key: MY-KEY - state: deleted -""" - -RETURN = r""" -before: - description: BGP config before the module ran. - returned: always - type: dict -after: - description: BGP config after the module ran. - returned: when changed - type: dict -gathered: - description: BGP config read from device (state=gathered). - returned: when state is gathered - type: dict -commands: - description: set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_BGP_BASE = ["protocols", "bgp"] - - -def _get_bgp(client): - try: - result = client.retrieve_show_config(_BGP_BASE) - return result.get("data") or {} - except VyOSRestError: - return {} - - -def _apply_bgp(client, config, commands): - asn = str(config["as_number"]) - base = _BGP_BASE + [asn] - client.configure_set(base) - commands.append("set protocols bgp {a}".format(a=asn)) - - if config.get("router_id"): - client.configure_set(base + ["parameters", "router-id"], config["router_id"]) - commands.append( - "set protocols bgp {a} parameters router-id {r}".format( - a=asn, - r=config["router_id"], - ), - ) - - for nbr in config.get("neighbors") or []: - nbase = base + ["neighbor", nbr["neighbor"]] - client.configure_set(nbase) - commands.append("set protocols bgp {a} neighbor {n}".format(a=asn, n=nbr["neighbor"])) - if nbr.get("remote_as"): - client.configure_set(nbase + ["remote-as"], str(nbr["remote_as"])) - commands.append( - "set protocols bgp {a} neighbor {n} remote-as {r}".format( - a=asn, - n=nbr["neighbor"], - r=nbr["remote_as"], - ), - ) - if nbr.get("description"): - client.configure_set(nbase + ["description"], nbr["description"]) - if nbr.get("password"): - client.configure_set(nbase + ["password"], nbr["password"]) - if nbr.get("update_source"): - client.configure_set(nbase + ["update-source"], nbr["update_source"]) - if nbr.get("ebgp_multihop"): - client.configure_set(nbase + ["ebgp-multihop"], str(nbr["ebgp_multihop"])) - if nbr.get("shutdown"): - client.configure_set(nbase + ["shutdown"]) - commands.append( - "set protocols bgp {a} neighbor {n} shutdown".format( - a=asn, - n=nbr["neighbor"], - ), - ) - - for net in config.get("networks") or []: - nbase = base + ["address-family", "ipv4-unicast", "network", net["prefix"]] - client.configure_set(nbase) - commands.append( - "set protocols bgp {a} address-family ipv4-unicast network {p}".format( - a=asn, - p=net["prefix"], - ), - ) - if net.get("route_map"): - client.configure_set(nbase + ["route-map"], net["route_map"]) - - for redist in config.get("redistribute") or []: - rbase = base + [ - "address-family", - "ipv4-unicast", - "redistribute", - redist["protocol"], - ] - client.configure_set(rbase) - commands.append( - "set protocols bgp {a} address-family ipv4-unicast redistribute {p}".format( - a=asn, - p=redist["protocol"], - ), - ) - if redist.get("metric"): - client.configure_set(rbase + ["metric"], str(redist["metric"])) - if redist.get("route_map"): - client.configure_set(rbase + ["route-map"], redist["route_map"]) - - -def main(): - argument_spec = dict( - config=dict( - type="dict", - options=dict( - as_number=dict(type="int", required=True), - router_id=dict(type="str"), - neighbors=dict( - type="list", - elements="dict", - options=dict( - neighbor=dict(type="str", required=True), - remote_as=dict(type="int"), - description=dict(type="str"), - password=dict(type="str", no_log=True), - update_source=dict(type="str"), - ebgp_multihop=dict(type="int"), - shutdown=dict(type="bool"), - ), - ), - networks=dict( - type="list", - elements="dict", - options=dict( - prefix=dict(type="str", required=True), - route_map=dict(type="str"), - ), - ), - redistribute=dict( - type="list", - elements="dict", - options=dict( - protocol=dict( - type="str", - required=True, - choices=[ - "connected", - "kernel", - "ospf", - "ospfv3", - "rip", - "ripng", - "static", - ], - ), - metric=dict(type="int"), - route_map=dict(type="str"), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=[ - ("state", "merged", ["config"]), - ("state", "replaced", ["config"]), - ], - supports_check_mode=True, - ) - - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") - commands = [] - changed = False - - before = _get_bgp(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - if before: - client.configure_delete(_BGP_BASE) - commands.append("delete protocols bgp") - changed = True - - elif state in ("merged", "replaced"): - if state == "replaced" and before: - client.configure_delete(_BGP_BASE) - commands.append("delete protocols bgp") - _apply_bgp(client, config, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get_bgp(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_config_file.py.preview b/plugins/modules/vyos_config_file.py.preview deleted file mode 100644 index 9d5e48a..0000000 --- a/plugins/modules/vyos_config_file.py.preview +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_config_file -short_description: Save or load VyOS configuration files via the REST API. -description: - - Manages VyOS configuration persistence via the HTTPS REST API - (C(/config-file) endpoint). - - Use I(op=save) to write the running configuration to a file. - - Use I(op=load) to load a configuration from a file and apply it. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - op: - description: - - C(save) - Save the running configuration to I(file) or the default C(/config/config.boot). - - C(load) - Load and commit the configuration from I(file). - type: str - choices: [save, load] - default: save - file: - description: - - Path on the VyOS device for save/load target. - - If omitted for C(save), the device uses its default boot config path. - type: str - 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 -requirements: - - VyOS 1.3+""" - -RETURN = r""" -output: - description: Text output from the config-file operation. - returned: success - type: str -""" - -EXAMPLES = r""" -- name: Save running config (default location) - vyos.rest.vyos_config_file: - hostname: 192.168.1.1 - api_key: MY-KEY - op: save - -- name: Save running config to a custom file - vyos.rest.vyos_config_file: - hostname: 192.168.1.1 - api_key: MY-KEY - op: save - file: /config/backups/config.boot.bak - -- name: Load a configuration from file - vyos.rest.vyos_config_file: - hostname: 192.168.1.1 - api_key: MY-KEY - op: load - file: /config/backups/config.boot.bak -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def main(): - argument_spec = dict( - op=dict(type="str", default="save", choices=["save", "load"]), - file=dict(type="str"), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=[("op", "load", ["file"])], - supports_check_mode=False, - ) - - client = VyOSRestClient(module) - op = module.params["op"] - file_path = module.params.get("file") - - try: - if op == "save": - result = client.config_file_save(file_path) - else: - result = client.config_file_load(file_path) - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=True, output=result.get("data", "")) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_configure.py.preview b/plugins/modules/vyos_configure.py.preview deleted file mode 100644 index 355a78c..0000000 --- a/plugins/modules/vyos_configure.py.preview +++ /dev/null @@ -1,259 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_configure -short_description: Manage VyOS device configuration via the 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. -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). - type: list - elements: raw - required: true - save: - description: - - Whether to save the configuration to disk after a successful commit. - 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 -""" - -import re - - -EXAMPLES = r""" -- name: Set hostname via REST API - vyos.rest.vyos_configure: - hostname: 192.168.1.1 - api_key: MY-KEY - commands: - - "set system host-name vyos-router" - -- name: Configure an interface description and address - 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 - -- name: Delete a static route using dict syntax - 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"] -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def _parse_cli_command(cmd_str): - """Parse a VyOS CLI-syntax string into an API payload dict. - - Supports: - set <path> [<value>] - delete <path> - comment <path> <comment> - - Args: - cmd_str (str): e.g. "set interfaces ethernet eth0 description 'WAN'" - - Returns: - dict: e.g. {"op": "set", "path": [...], "value": "WAN"} - """ - 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 - - -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, - supports_check_mode=True, - ) - - try: - commands = _normalise_commands(module.params["commands"]) - except ValueError as exc: - module.fail_json(msg=str(exc)) - - if module.check_mode: - module.exit_json( - changed=True, - commands_sent=commands, - saved=False, - msg="Check mode: no changes applied.", - ) - - client = VyOSRestClient(module) - changed = False - saved = False - - 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.params["save"]: - client.config_file_save() - saved = True - - except VyOSRestError as exc: - module.fail_json(msg=str(exc), commands_sent=commands) - - module.exit_json(changed=changed, commands_sent=commands, saved=saved) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_facts.py.preview b/plugins/modules/vyos_facts.py.preview deleted file mode 100644 index 66e684b..0000000 --- a/plugins/modules/vyos_facts.py.preview +++ /dev/null @@ -1,161 +0,0 @@ -#!/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_facts -short_description: Get facts about VyOS devices via REST API. -description: - - Collects facts from VyOS devices using the HTTPS REST API. - - Returns system info and optionally per-resource configuration facts. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - gather_subset: - description: - - Restrict facts to a subset. Use C(all), C(default), C(config), or C(min). - type: list - elements: str - default: ["min"] - gather_network_resources: - description: - - Which network resource facts to gather. - type: list - elements: str - available_network_resources: - description: - - When true, return a list of available resource names. - type: bool - default: false - hostname: - description: - - Device IP or FQDN. - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -ansible_facts: - description: Dictionary of facts keyed by resource name. - returned: always - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_NETWORK_RESOURCES = [ - "interfaces", - "l3_interfaces", - "lag_interfaces", - "lldp_global", - "lldp_interfaces", - "static_routes", - "firewall_rules", - "firewall_global", - "firewall_interfaces", - "ospfv2", - "ospfv3", -] - -_RESOURCE_PATHS = { - "interfaces": ["interfaces"], - "l3_interfaces": ["interfaces"], - "lag_interfaces": ["interfaces", "bonding"], - "lldp_global": ["service", "lldp"], - "lldp_interfaces": ["service", "lldp"], - "static_routes": ["protocols", "static"], - "firewall_rules": ["firewall"], - "firewall_global": ["firewall"], - "firewall_interfaces": ["firewall"], - "ospfv2": ["protocols", "ospf"], - "ospfv3": ["protocols", "ospfv3"], -} - - -def _get_subset(client, resource): - path = _RESOURCE_PATHS.get(resource, []) - try: - result = client.retrieve_show_config(path) - return result.get("data") or {} - except VyOSRestError: - return {} - - -def main(): - argument_spec = dict( - gather_subset=dict(type="list", elements="str", default=["min"]), - gather_network_resources=dict(type="list", elements="str"), - available_network_resources=dict(type="bool", default=False), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - - client = VyOSRestClient(module) - facts = {} - - if module.params["available_network_resources"]: - facts["available_network_resources"] = _NETWORK_RESOURCES - - gather_subset = module.params["gather_subset"] - get_all = "all" in gather_subset - - # Always gather system info for "min" or "all" - if get_all or "min" in gather_subset or "default" in gather_subset: - try: - info = client.info() - facts["version"] = info.get("data", {}).get("version", "") - facts["hostname"] = info.get("data", {}).get("hostname", "") - except VyOSRestError: - pass - - # Config subset - if get_all or "config" in gather_subset: - try: - result = client.retrieve_show_config([]) - facts["config"] = result.get("data") or {} - except VyOSRestError: - facts["config"] = {} - - # Network resources - requested = module.params.get("gather_network_resources") or [] - if "all" in requested: - requested = _NETWORK_RESOURCES - - for resource in requested: - if resource.startswith("!"): - continue - if resource in _NETWORK_RESOURCES: - facts["network_resources"] = facts.get("network_resources", {}) - facts["network_resources"][resource] = _get_subset(client, resource) - - module.exit_json(changed=False, ansible_facts=facts) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_firewall_global.py.preview b/plugins/modules/vyos_firewall_global.py.preview deleted file mode 100644 index a3974fd..0000000 --- a/plugins/modules/vyos_firewall_global.py.preview +++ /dev/null @@ -1,360 +0,0 @@ -#!/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_firewall_global -short_description: Firewall global resource module via REST API. -description: - - Manages global VyOS firewall parameters (groups, ICMP redirect policy, - source validation, ping policy) via the HTTPS REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: Global firewall configuration. - type: dict - suboptions: - validation: - description: Source-validation policy (strict/loose/disable). - type: str - choices: [strict, loose, disable] - config_trap: - description: SNMP trap on firewall config changes. - type: bool - ping: - description: ICMP echo policy. - type: dict - suboptions: - all: - type: bool - broadcast: - type: bool - route_redirects: - description: ICMP redirect / source-route options per AFI. - type: list - elements: dict - suboptions: - afi: - type: str - choices: [ipv4, ipv6] - required: true - icmp_redirects: - type: dict - suboptions: - send: - type: bool - receive: - type: bool - ip_src_route: - type: bool - group: - description: Firewall object groups. - type: dict - suboptions: - address_group: - type: list - elements: dict - suboptions: - name: - type: str - required: true - description: - type: str - members: - description: List of IP addresses or ranges. - type: list - elements: dict - suboptions: - address: - type: str - afi: - type: str - default: ipv4 - choices: [ipv4, ipv6] - network_group: - type: list - elements: dict - suboptions: - name: - type: str - required: true - description: - type: str - members: - description: List of network prefixes. - type: list - elements: dict - suboptions: - address: - type: str - port_group: - type: list - elements: dict - suboptions: - name: - type: str - required: true - description: - type: str - members: - description: List of port numbers or ranges. - type: list - elements: dict - suboptions: - port: - type: str - state: - description: Desired state. - type: str - choices: [merged, replaced, deleted, gathered] - default: merged - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - returned: always - type: dict -after: - returned: when changed - type: dict -commands: - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_FW_BASE = ["firewall"] - - -def _get(client): - try: - r = client.retrieve_show_config(_FW_BASE) - return r.get("data") or {} - except VyOSRestError: - return {} - - -def _apply(client, config, commands): - if config.get("validation"): - path = ( - _FW_BASE + ["ip", "src-route"] - if config["validation"] == "disable" - else _FW_BASE + ["ip", "enable-default-log"] - ) - # Use the proper path - p = ( - ["firewall", "global-options", "source-validation"] - if client.retrieve_exists(["firewall", "global-options"]) - else ["firewall", "ip", "src-route"] - ) - client.configure_set(["firewall", "ip", "source-validation"], config["validation"]) - commands.append("set firewall ip source-validation {v}".format(v=config["validation"])) - - if config.get("config_trap") is not None: - val = "enable" if config["config_trap"] else "disable" - client.configure_set(["firewall", "config-trap"], val) - commands.append("set firewall config-trap {v}".format(v=val)) - - ping = config.get("ping") or {} - if ping.get("all") is not None: - action = "enable" if ping["all"] else "disable" - client.configure_set(["firewall", "ip", "ping", "all"], action) - commands.append("set firewall ip ping all {a}".format(a=action)) - if ping.get("broadcast") is not None: - action = "enable" if ping["broadcast"] else "disable" - client.configure_set(["firewall", "ip", "ping", "broadcast"], action) - commands.append("set firewall ip ping broadcast {a}".format(a=action)) - - for rr in config.get("route_redirects") or []: - afi = rr["afi"] - ip_key = "ip" if afi == "ipv4" else "ipv6" - icmp = rr.get("icmp_redirects") or {} - if icmp.get("send") is not None: - p = ["firewall", ip_key, "send-redirects"] - client.configure_set(p, "enable" if icmp["send"] else "disable") - commands.append( - "set {p} {v}".format(p=" ".join(p), v="enable" if icmp["send"] else "disable"), - ) - if icmp.get("receive") is not None: - p = ["firewall", ip_key, "disable-forwarding"] - client.configure_set(p, "enable" if not icmp["receive"] else "disable") - if rr.get("ip_src_route") is not None: - p = ["firewall", ip_key, "source-route"] - client.configure_set(p, "enable" if rr["ip_src_route"] else "disable") - - grp = config.get("group") or {} - for ag in grp.get("address_group") or []: - gpath = ["firewall", "group", "address-group", ag["name"]] - client.configure_set(gpath) - commands.append("set {p}".format(p=" ".join(gpath))) - if ag.get("description"): - client.configure_set(gpath + ["description"], ag["description"]) - for m in ag.get("members") or []: - client.configure_set(gpath + ["address"], m["address"]) - commands.append("set {p} address {a}".format(p=" ".join(gpath), a=m["address"])) - - for ng in grp.get("network_group") or []: - gpath = ["firewall", "group", "network-group", ng["name"]] - client.configure_set(gpath) - commands.append("set {p}".format(p=" ".join(gpath))) - if ng.get("description"): - client.configure_set(gpath + ["description"], ng["description"]) - for m in ng.get("members") or []: - client.configure_set(gpath + ["network"], m["address"]) - - for pg in grp.get("port_group") or []: - gpath = ["firewall", "group", "port-group", pg["name"]] - client.configure_set(gpath) - commands.append("set {p}".format(p=" ".join(gpath))) - if pg.get("description"): - client.configure_set(gpath + ["description"], pg["description"]) - for m in pg.get("members") or []: - client.configure_set(gpath + ["port"], str(m["port"])) - - -def main(): - argument_spec = dict( - config=dict( - type="dict", - options=dict( - validation=dict(type="str", choices=["strict", "loose", "disable"]), - config_trap=dict(type="bool"), - ping=dict( - type="dict", - options=dict(all=dict(type="bool"), broadcast=dict(type="bool")), - ), - route_redirects=dict( - type="list", - elements="dict", - options=dict( - afi=dict(type="str", required=True, choices=["ipv4", "ipv6"]), - icmp_redirects=dict( - type="dict", - options=dict(send=dict(type="bool"), receive=dict(type="bool")), - ), - ip_src_route=dict(type="bool"), - ), - ), - group=dict( - type="dict", - options=dict( - address_group=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - description=dict(type="str"), - afi=dict(type="str", default="ipv4"), - members=dict( - type="list", - elements="dict", - options=dict(address=dict(type="str")), - ), - ), - ), - network_group=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - description=dict(type="str"), - members=dict( - type="list", - elements="dict", - options=dict(address=dict(type="str")), - ), - ), - ), - port_group=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - description=dict(type="str"), - members=dict( - type="list", - elements="dict", - options=dict(port=dict(type="str")), - ), - ), - ), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule( - argument_spec=argument_spec, - required_if=[("state", "merged", ["config"]), ("state", "replaced", ["config"])], - supports_check_mode=True, - ) - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") - commands = [] - changed = False - before = _get(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - if before: - client.configure_delete(_FW_BASE) - commands.append("delete firewall") - changed = True - elif state in ("merged", "replaced"): - if state == "replaced" and before: - client.configure_delete(_FW_BASE) - commands.append("delete firewall") - _apply(client, config, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_firewall_interfaces.py.preview b/plugins/modules/vyos_firewall_interfaces.py.preview deleted file mode 100644 index d9085a6..0000000 --- a/plugins/modules/vyos_firewall_interfaces.py.preview +++ /dev/null @@ -1,244 +0,0 @@ -#!/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_firewall_interfaces -short_description: Firewall interfaces resource module via REST API. -description: - - Attaches or detaches named firewall rule sets to/from interface - directions (in/out/local) using the VyOS HTTPS REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of interface firewall attachment configurations. - type: list - elements: dict - suboptions: - name: - description: Interface name. - type: str - required: true - access_rules: - description: Firewall rule sets per AFI. - type: list - elements: dict - suboptions: - afi: - description: Address family. - type: str - choices: [ipv4, ipv6] - required: true - rules: - description: Rule sets and directions. - type: list - elements: dict - suboptions: - name: - description: Rule set name. - type: str - direction: - description: Traffic direction. - type: str - choices: [in, local, out] - required: true - state: - description: Desired state. - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - returned: always - type: list -after: - returned: when changed - type: list -commands: - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_IFACE_TYPES = { - "eth": "ethernet", - "bond": "bonding", - "vti": "vti", - "vxlan": "vxlan", - "lo": "loopback", - "dummy": "dummy", - "br": "bridge", - "wg": "wireguard", - "tun": "tunnel", -} - - -def _itype(name): - for p, t in _IFACE_TYPES.items(): - if name.startswith(p): - return t - return "ethernet" - - -def _fw_direction_key(afi, direction): - if afi == "ipv4": - return {"in": "in", "out": "out", "local": "local"}[direction] - return {"in": "in", "out": "out", "local": "local"}[direction] - - -def _apply(client, iface_cfg, commands): - name = iface_cfg["name"] - itype = _itype(name) - ibase = ["interfaces", itype, name, "firewall"] - - for ar in iface_cfg.get("access_rules") or []: - afi = ar["afi"] - fw_key = "firewall" if afi == "ipv4" else "ipv6" - for rule in ar.get("rules") or []: - direction = rule["direction"] - rs_name = rule.get("name") - if rs_name: - path = ibase + [direction, fw_key if afi == "ipv6" else "name"] - client.configure_set(path, rs_name) - commands.append( - "set interfaces {t} {n} firewall {d} {k} '{rs}'".format( - t=itype, - n=name, - d=direction, - k="ipv6-name" if afi == "ipv6" else "name", - rs=rs_name, - ), - ) - - -def _delete_iface_fw(client, name, commands): - itype = _itype(name) - path = ["interfaces", itype, name, "firewall"] - try: - client.configure_delete(path) - commands.append("delete interfaces {t} {n} firewall".format(t=itype, n=name)) - except VyOSRestError: - pass - - -def _get(client): - try: - result = client.retrieve_show_config(["interfaces"]) - raw = result.get("data") or {} - out = [] - for itype, itype_data in raw.items(): - if not isinstance(itype_data, dict): - continue - for iname, idata in itype_data.items(): - fw = idata.get("firewall") if isinstance(idata, dict) else None - if fw: - out.append({"name": iname, "firewall": fw}) - return out - except VyOSRestError: - return [] - - -def main(): - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - access_rules=dict( - type="list", - elements="dict", - options=dict( - afi=dict(type="str", required=True, choices=["ipv4", "ipv6"]), - rules=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str"), - direction=dict( - type="str", - required=True, - choices=["in", "local", "out"], - ), - ), - ), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - before = _get(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - targets = {i["name"] for i in config} if config else {i["name"] for i in before} - for iface in before: - if iface["name"] in targets: - _delete_iface_fw(client, iface["name"], commands) - changed = True - elif state in ("merged", "replaced", "overridden"): - if state in ("replaced", "overridden"): - for cfg in config: - _delete_iface_fw(client, cfg["name"], commands) - for cfg in config: - _apply(client, cfg, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_firewall_rules.py.preview b/plugins/modules/vyos_firewall_rules.py.preview deleted file mode 100644 index f535f3a..0000000 --- a/plugins/modules/vyos_firewall_rules.py.preview +++ /dev/null @@ -1,492 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_firewall_rules -short_description: Manage firewall rules on VyOS via the REST API. -description: - - Manages VyOS firewall rule sets and individual rules using the HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_firewall_rules) but uses the HTTP API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of firewall rule set configurations. - type: list - elements: dict - suboptions: - afi: - description: Address family. - type: str - choices: [ipv4, ipv6] - required: true - rule_sets: - description: List of named rule sets. - type: list - elements: dict - suboptions: - name: - description: Rule set name. - type: str - required: true - default_action: - description: Default action for unmatched traffic. - type: str - choices: [drop, reject, accept] - default: drop - description: - description: Rule set description. - type: str - rules: - description: List of firewall rules. - type: list - elements: dict - suboptions: - number: - description: Rule number (1-999999). - type: int - required: true - action: - description: Rule action. - type: str - choices: [drop, reject, accept, inspect] - description: - description: Rule description. - type: str - protocol: - description: Protocol (e.g. C(tcp), C(udp), C(icmp), C(all)). - type: str - source: - description: Source match criteria. - type: dict - suboptions: - address: - description: Source IP address or network. - type: str - port: - description: Source port or port range. - type: str - group: - description: Address or network group. - type: dict - suboptions: - address_group: - type: str - network_group: - type: str - destination: - description: Destination match criteria. - type: dict - suboptions: - address: - description: Destination IP address or network. - type: str - port: - description: Destination port or port range. - type: str - group: - description: Address or network group. - type: dict - suboptions: - address_group: - type: str - network_group: - type: str - state: - description: Connection state matching. - type: dict - suboptions: - established: - type: bool - new: - type: bool - related: - type: bool - invalid: - type: bool - enabled: - description: Whether the rule is active. - type: bool - default: true - state: - description: - - C(merged): Add/update listed rule sets and rules. - - C(replaced): Replace listed rule sets entirely. - - C(overridden): Replace the entire firewall rule config. - - C(deleted): Remove listed (or all) rule sets. - - C(gathered): Read firewall rule config from device. - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - 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 -requirements: - - VyOS 1.3+ -seealso: - - module: vyos.vyos.vyos_firewall_rules -examples: | - - name: Create an IPv4 firewall rule set - vyos.rest.vyos_firewall_rules: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - - afi: ipv4 - rule_sets: - - name: OUTSIDE-IN - default_action: drop - rules: - - number: 10 - action: accept - protocol: tcp - destination: - port: "80,443" - state: - established: true - related: true - state: merged - - - name: Delete all firewall rules - vyos.rest.vyos_firewall_rules: - hostname: 192.168.1.1 - api_key: MY-KEY - state: deleted -""" - -RETURN = r""" -before: - description: Firewall rule config before the module ran. - returned: always - type: list -after: - description: Firewall rule config after the module ran. - returned: when changed - type: list -gathered: - description: Firewall config read from device (state=gathered). - returned: when state is gathered - type: list -commands: - description: set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_FW_PATH = { - "ipv4": ["firewall", "name"], - "ipv6": ["firewall", "ipv6-name"], -} - - -def _get_fw_rules(client): - try: - result = client.retrieve_show_config(["firewall"]) - data = result.get("data") or {} - out = [] - for afi, key in [("ipv4", "name"), ("ipv6", "ipv6-name")]: - sets_data = data.get(key, {}) - if not isinstance(sets_data, dict): - continue - rule_sets = [] - for rs_name, rs_data in sets_data.items(): - if not isinstance(rs_data, dict): - continue - rs = { - "name": rs_name, - "default_action": rs_data.get("default-action", "drop"), - } - if "description" in rs_data: - rs["description"] = rs_data["description"] - rules = [] - for rnum, rdata in (rs_data.get("rule") or {}).items(): - if not isinstance(rdata, dict): - continue - rule = {"number": int(rnum)} - if "action" in rdata: - rule["action"] = rdata["action"] - if "description" in rdata: - rule["description"] = rdata["description"] - if "protocol" in rdata: - rule["protocol"] = rdata["protocol"] - rule["enabled"] = "disable" not in rdata - rules.append(rule) - if rules: - rs["rules"] = sorted(rules, key=lambda r: r["number"]) - rule_sets.append(rs) - if rule_sets: - out.append({"afi": afi, "rule_sets": rule_sets}) - return out - except VyOSRestError: - return [] - - -def _apply_rule_set(client, afi, rs, commands): - base = _FW_PATH[afi] + [rs["name"]] - client.configure_set(base) - commands.append("set {p}".format(p=" ".join(base))) - - if rs.get("default_action"): - client.configure_set(base + ["default-action"], rs["default_action"]) - commands.append( - "set {p} default-action {a}".format( - p=" ".join(base), - a=rs["default_action"], - ), - ) - if rs.get("description"): - client.configure_set(base + ["description"], rs["description"]) - commands.append( - "set {p} description '{d}'".format( - p=" ".join(base), - d=rs["description"], - ), - ) - - for rule in rs.get("rules") or []: - rbase = base + ["rule", str(rule["number"])] - client.configure_set(rbase) - commands.append("set {p}".format(p=" ".join(rbase))) - - if rule.get("action"): - client.configure_set(rbase + ["action"], rule["action"]) - commands.append( - "set {p} action {a}".format(p=" ".join(rbase), a=rule["action"]), - ) - if rule.get("description"): - client.configure_set(rbase + ["description"], rule["description"]) - if rule.get("protocol"): - client.configure_set(rbase + ["protocol"], rule["protocol"]) - commands.append( - "set {p} protocol {pr}".format( - p=" ".join(rbase), - pr=rule["protocol"], - ), - ) - for direction in ("source", "destination"): - match = rule.get(direction) - if not match: - continue - mbase = rbase + [direction] - if match.get("address"): - client.configure_set(mbase + ["address"], match["address"]) - commands.append( - "set {p} address '{a}'".format( - p=" ".join(mbase), - a=match["address"], - ), - ) - if match.get("port"): - client.configure_set(mbase + ["port"], match["port"]) - commands.append( - "set {p} port {pt}".format( - p=" ".join(mbase), - pt=match["port"], - ), - ) - - state_match = rule.get("state") - if state_match: - sbase = rbase + ["state"] - for st_name in ("established", "new", "related", "invalid"): - if state_match.get(st_name): - client.configure_set(sbase + [st_name], "enable") - commands.append( - "set {p} {s} enable".format( - p=" ".join(sbase), - s=st_name, - ), - ) - - if "enabled" in rule and not rule["enabled"]: - client.configure_set(rbase + ["disable"]) - commands.append("set {p} disable".format(p=" ".join(rbase))) - - -def main(): - rule_spec = dict( - number=dict(type="int", required=True), - action=dict(type="str", choices=["drop", "reject", "accept", "inspect"]), - description=dict(type="str"), - protocol=dict(type="str"), - source=dict( - type="dict", - options=dict( - address=dict(type="str"), - port=dict(type="str"), - group=dict( - type="dict", - options=dict( - address_group=dict(type="str"), - network_group=dict(type="str"), - ), - ), - ), - ), - destination=dict( - type="dict", - options=dict( - address=dict(type="str"), - port=dict(type="str"), - group=dict( - type="dict", - options=dict( - address_group=dict(type="str"), - network_group=dict(type="str"), - ), - ), - ), - ), - state=dict( - type="dict", - options=dict( - established=dict(type="bool"), - new=dict(type="bool"), - related=dict(type="bool"), - invalid=dict(type="bool"), - ), - ), - enabled=dict(type="bool", default=True), - ) - - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - afi=dict(type="str", required=True, choices=["ipv4", "ipv6"]), - rule_sets=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - default_action=dict( - type="str", - choices=["drop", "reject", "accept"], - default="drop", - ), - description=dict(type="str"), - rules=dict(type="list", elements="dict", options=rule_spec), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - - before = _get_fw_rules(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state in ("deleted", "overridden") and not config: - for afi_key in ("name", "ipv6-name"): - try: - client.configure_delete(["firewall", afi_key]) - commands.append( - "delete firewall {k}".format(k=afi_key), - ) - except VyOSRestError: - pass - changed = True - - elif state == "deleted" and config: - for entry in config: - afi = entry["afi"] - base_path = _FW_PATH[afi] - for rs in entry.get("rule_sets") or []: - try: - client.configure_delete(base_path + [rs["name"]]) - commands.append( - "delete {p} {n}".format( - p=" ".join(base_path), - n=rs["name"], - ), - ) - changed = True - except VyOSRestError: - pass - - elif state in ("merged", "replaced", "overridden"): - if state == "replaced": - for entry in config: - afi = entry["afi"] - base_path = _FW_PATH[afi] - for rs in entry.get("rule_sets") or []: - try: - client.configure_delete(base_path + [rs["name"]]) - commands.append( - "delete {p} {n}".format( - p=" ".join(base_path), - n=rs["name"], - ), - ) - except VyOSRestError: - pass - for entry in config: - afi = entry["afi"] - for rs in entry.get("rule_sets") or []: - _apply_rule_set(client, afi, rs, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get_fw_rules(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_generate.py.preview b/plugins/modules/vyos_generate.py.preview deleted file mode 100644 index 448575a..0000000 --- a/plugins/modules/vyos_generate.py.preview +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_generate -short_description: Execute generate commands on a VyOS device via REST API. -description: - - Sends a C(generate) operational command to a VyOS device via the HTTPS - REST API (C(/generate) endpoint). - - Commonly used to generate WireGuard keypairs, TLS certificates, etc. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - path: - description: - - Generate command tokens. - - E.g. C(["wireguard", "default-keypair"]). - type: list - elements: str - required: true - 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 -requirements: - - VyOS 1.3+ -examples: | - - name: Generate WireGuard default keypair - vyos.rest.vyos_generate: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["wireguard", "default-keypair"] - - - name: Generate a new SSH host key - vyos.rest.vyos_generate: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["ssh-server", "host-key", "rsa"] -""" - -RETURN = r""" -output: - description: Text output produced by the generate command (may be empty). - returned: success - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def main(): - argument_spec = dict( - path=dict(type="list", elements="str", required=True), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False, - ) - - client = VyOSRestClient(module) - try: - result = client.generate(module.params["path"]) - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=True, output=result.get("data", "")) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_image.py.preview b/plugins/modules/vyos_image.py.preview deleted file mode 100644 index be12705..0000000 --- a/plugins/modules/vyos_image.py.preview +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_image -short_description: Manage VyOS system images via the REST API. -description: - - Adds or removes VyOS system images using the HTTPS REST API - (C(/image) endpoint). - - Use I(state=present) to download and install an image from a URL. - - Use I(state=absent) to delete an installed image by name. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - state: - description: - - C(present): Download and install the image from I(url). - - C(absent): Delete the image specified by I(name). - type: str - choices: [present, absent] - required: true - url: - description: - - HTTP(S) URL of the VyOS C(.iso) file to install. - - Required when I(state=present). - type: str - name: - description: - - Version name of the image to remove (e.g. C(1.4-rolling-202401010000)). - - Required when I(state=absent). - type: str - 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. Image downloads can take several minutes; - increase this value accordingly. - type: int - default: 300 - verify_ssl: - description: Validate the device's TLS certificate. - type: bool - default: false -requirements: - - VyOS 1.3+ -examples: | - - name: Install the latest rolling release - vyos.rest.vyos_image: - hostname: 192.168.1.1 - api_key: MY-KEY - state: present - url: "https://downloads.vyos.io/rolling/current/amd64/vyos-rolling-latest.iso" - timeout: 600 - - - name: Remove an old image - vyos.rest.vyos_image: - hostname: 192.168.1.1 - api_key: MY-KEY - state: absent - name: "1.4-rolling-202312010000" -""" - -RETURN = r""" -output: - description: Text output from the image add/delete operation. - returned: success - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def main(): - argument_spec = dict( - state=dict(type="str", required=True, choices=["present", "absent"]), - url=dict(type="str"), - name=dict(type="str"), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - # Override default timeout to 300s for image downloads - argument_spec["timeout"]["default"] = 300 - - module = AnsibleModule( - argument_spec=argument_spec, - required_if=[ - ("state", "present", ["url"]), - ("state", "absent", ["name"]), - ], - supports_check_mode=False, - ) - - client = VyOSRestClient(module) - state = module.params["state"] - - try: - if state == "present": - result = client.image_add(module.params["url"]) - else: - result = client.image_delete(module.params["name"]) - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=True, output=result.get("data", "")) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_interfaces.py.preview b/plugins/modules/vyos_interfaces.py.preview deleted file mode 100644 index 9edc1da..0000000 --- a/plugins/modules/vyos_interfaces.py.preview +++ /dev/null @@ -1,401 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_interfaces -short_description: Manage interface attributes on a VyOS device via the REST API. -description: - - Manages interface base attributes (description, MTU, enabled state, VIFs, - duplex, speed) on VyOS devices using the HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_interfaces) but uses HTTP API. - - Supports Ethernet, Bonding, VXLAN, Loopback, and Virtual Tunnel interfaces. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of interface configurations. - type: list - elements: dict - suboptions: - name: - description: > - Full interface name (e.g. C(eth0), C(bond0), C(vti1), C(vxlan2)). - type: str - required: true - description: - description: Interface description. - type: str - enabled: - description: > - Administrative state. C(true) = up, C(false) = admin-down. - type: bool - default: true - mtu: - description: MTU in bytes. Applicable for Ethernet, Bonding, VXLAN, VTI. - type: int - duplex: - description: Duplex mode. Only for Ethernet interfaces. - type: str - choices: [auto, full, half] - speed: - description: Link speed. Only for Ethernet interfaces. - type: str - vifs: - description: List of VLAN sub-interface configurations. - type: list - elements: dict - suboptions: - vlan_id: - description: 802.1Q VLAN ID. - type: int - required: true - description: - description: VIF description. - type: str - enabled: - description: Administrative state of the VIF. - type: bool - default: true - state: - description: - - C(merged): Merge the provided config with existing interface config. - - C(replaced): Replace per-interface config with provided values. - - C(overridden): Override all interface config with provided values. - - C(deleted): Delete interface config for listed interfaces (or all). - - C(gathered): Read current interface config from device. - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - 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 -requirements: - - VyOS 1.3+ -seealso: - - module: vyos.vyos.vyos_interfaces - - module: vyos.rest.vyos_l3_interfaces -examples: | - - name: Set description and MTU on eth1 - vyos.rest.vyos_interfaces: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - - name: eth1 - description: "WAN uplink" - mtu: 1500 - enabled: true - state: merged - - - name: Disable eth2 - vyos.rest.vyos_interfaces: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - - name: eth2 - enabled: false - state: merged - - - name: Create a VLAN sub-interface - vyos.rest.vyos_interfaces: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - - name: eth1 - vifs: - - vlan_id: 100 - description: "VLAN 100" - state: merged -""" - -RETURN = r""" -before: - description: Interface configuration before the module ran. - returned: always - type: list -after: - description: Interface configuration after the module ran. - returned: when changed - type: list -gathered: - description: Interface config read from device (state=gathered). - returned: when state is gathered - type: list -commands: - description: REST API set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_IFACE_TYPES = { - "eth": "ethernet", - "bond": "bonding", - "vti": "vti", - "vxlan": "vxlan", - "lo": "loopback", - "dummy": "dummy", - "peth": "pseudo-ethernet", - "vtun": "openvpn", - "wg": "wireguard", - "br": "bridge", - "macsec": "macsec", - "tun": "tunnel", -} - - -def _iface_type(name): - """Infer the VyOS interface type from its name prefix.""" - for prefix, itype in _IFACE_TYPES.items(): - if name.startswith(prefix): - return itype - return "ethernet" - - -def _iface_base_path(name): - return ["interfaces", _iface_type(name), name] - - -def _get_interfaces(client): - try: - result = client.retrieve_show_config(["interfaces"]) - ifaces_raw = result.get("data") or {} - interfaces = [] - for itype, itype_data in ifaces_raw.items(): - if not isinstance(itype_data, dict): - continue - for iname, idata in itype_data.items(): - if not isinstance(idata, dict): - continue - entry = {"name": iname} - if "description" in idata: - entry["description"] = idata["description"] - if "mtu" in idata: - entry["mtu"] = int(idata["mtu"]) - entry["enabled"] = "disable" not in idata - if "duplex" in idata: - entry["duplex"] = idata["duplex"] - if "speed" in idata: - entry["speed"] = idata["speed"] - if "vif" in idata: - vifs = [] - for vid, vdata in idata["vif"].items(): - vif_entry = {"vlan_id": int(vid)} - if isinstance(vdata, dict): - if "description" in vdata: - vif_entry["description"] = vdata["description"] - vif_entry["enabled"] = "disable" not in vdata - vifs.append(vif_entry) - entry["vifs"] = vifs - interfaces.append(entry) - return interfaces - except VyOSRestError: - return [] - - -def _apply_interface(client, iface_cfg, commands): - name = iface_cfg["name"] - base = _iface_base_path(name) - - if iface_cfg.get("description") is not None: - client.configure_set(base + ["description"], iface_cfg["description"]) - commands.append( - "set interfaces {t} {n} description '{d}'".format( - t=_iface_type(name), - n=name, - d=iface_cfg["description"], - ), - ) - if iface_cfg.get("mtu") is not None: - client.configure_set(base + ["mtu"], str(iface_cfg["mtu"])) - commands.append( - "set interfaces {t} {n} mtu {m}".format( - t=_iface_type(name), - n=name, - m=iface_cfg["mtu"], - ), - ) - if "enabled" in iface_cfg: - if not iface_cfg["enabled"]: - client.configure_set(base + ["disable"]) - commands.append( - "set interfaces {t} {n} disable".format(t=_iface_type(name), n=name), - ) - else: - try: - client.configure_delete(base + ["disable"]) - commands.append( - "delete interfaces {t} {n} disable".format( - t=_iface_type(name), - n=name, - ), - ) - except VyOSRestError: - pass - if iface_cfg.get("duplex"): - client.configure_set(base + ["duplex"], iface_cfg["duplex"]) - commands.append( - "set interfaces {t} {n} duplex {d}".format( - t=_iface_type(name), - n=name, - d=iface_cfg["duplex"], - ), - ) - if iface_cfg.get("speed"): - client.configure_set(base + ["speed"], iface_cfg["speed"]) - commands.append( - "set interfaces {t} {n} speed {s}".format( - t=_iface_type(name), - n=name, - s=iface_cfg["speed"], - ), - ) - for vif in iface_cfg.get("vifs") or []: - vid = str(vif["vlan_id"]) - vif_base = base + ["vif", vid] - client.configure_set(vif_base) - commands.append( - "set interfaces {t} {n} vif {v}".format( - t=_iface_type(name), - n=name, - v=vid, - ), - ) - if vif.get("description"): - client.configure_set(vif_base + ["description"], vif["description"]) - commands.append( - "set interfaces {t} {n} vif {v} description '{d}'".format( - t=_iface_type(name), - n=name, - v=vid, - d=vif["description"], - ), - ) - if "enabled" in vif and not vif["enabled"]: - client.configure_set(vif_base + ["disable"]) - commands.append( - "set interfaces {t} {n} vif {v} disable".format( - t=_iface_type(name), - n=name, - v=vid, - ), - ) - - -def main(): - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - description=dict(type="str"), - enabled=dict(type="bool", default=True), - mtu=dict(type="int"), - duplex=dict(type="str", choices=["auto", "full", "half"]), - speed=dict(type="str"), - vifs=dict( - type="list", - elements="dict", - options=dict( - vlan_id=dict(type="int", required=True), - description=dict(type="str"), - enabled=dict(type="bool", default=True), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - - before = _get_interfaces(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - targets = {i["name"] for i in config} if config else {i["name"] for i in before} - for iface in before: - if iface["name"] in targets: - base = _iface_base_path(iface["name"]) - # Only delete mutable attributes, not the interface itself - for attr in ["description", "mtu", "duplex", "speed"]: - if attr in iface: - try: - client.configure_delete(base + [attr]) - commands.append( - "delete interfaces {t} {n} {a}".format( - t=_iface_type(iface["name"]), - n=iface["name"], - a=attr, - ), - ) - except VyOSRestError: - pass - changed = True - - elif state in ("merged", "replaced", "overridden"): - for iface_cfg in config: - _apply_interface(client, iface_cfg, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get_interfaces(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_l3_interfaces.py.preview b/plugins/modules/vyos_l3_interfaces.py.preview deleted file mode 100644 index f6d4e6c..0000000 --- a/plugins/modules/vyos_l3_interfaces.py.preview +++ /dev/null @@ -1,381 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_l3_interfaces -short_description: Manage L3 interface attributes on VyOS via the REST API. -description: - - Manages IPv4 and IPv6 addresses on VyOS interfaces via the HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_l3_interfaces) but uses the HTTP API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of L3 interface configurations. - type: list - elements: dict - suboptions: - name: - description: Full interface name (e.g. C(eth0), C(eth1)). - type: str - required: true - ipv4: - description: List of IPv4 address assignments. - type: list - elements: dict - suboptions: - address: - description: IPv4 address in CIDR notation or C(dhcp). - type: str - required: true - ipv6: - description: List of IPv6 address assignments. - type: list - elements: dict - suboptions: - address: - description: IPv6 address in CIDR notation or C(dhcpv6) or C(autoconf). - type: str - required: true - vifs: - description: VLAN sub-interface L3 settings. - type: list - elements: dict - suboptions: - vlan_id: - description: 802.1Q VLAN ID. - type: int - required: true - ipv4: - description: IPv4 addresses for this VIF. - type: list - elements: dict - suboptions: - address: - type: str - required: true - ipv6: - description: IPv6 addresses for this VIF. - type: list - elements: dict - suboptions: - address: - type: str - required: true - state: - description: - - C(merged): Add addresses to the interface (preserve existing). - - C(replaced): Replace all addresses on each listed interface. - - C(overridden): Replace all addresses on all interfaces. - - C(deleted): Remove all addresses from listed (or all) interfaces. - - C(gathered): Read L3 config from device without changes. - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - 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 -requirements: - - VyOS 1.3+ -seealso: - - module: vyos.vyos.vyos_l3_interfaces - - module: vyos.rest.vyos_interfaces -examples: | - - name: Assign addresses to eth1 and eth2 - vyos.rest.vyos_l3_interfaces: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - - name: eth1 - ipv4: - - address: 10.0.1.1/24 - ipv6: - - address: "2001:db8::1/64" - - name: eth2 - ipv4: - - address: dhcp - state: merged - - - name: Remove all addresses from eth1 - vyos.rest.vyos_l3_interfaces: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - - name: eth1 - state: deleted -""" - -RETURN = r""" -before: - description: L3 interface config before the module ran. - returned: always - type: list -after: - description: L3 interface config after the module ran. - returned: when changed - type: list -gathered: - description: L3 config read from device (state=gathered). - returned: when state is gathered - type: list -commands: - description: set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_IFACE_TYPES = { - "eth": "ethernet", - "bond": "bonding", - "vti": "vti", - "vxlan": "vxlan", - "lo": "loopback", - "dummy": "dummy", - "br": "bridge", - "wg": "wireguard", - "tun": "tunnel", -} - - -def _iface_type(name): - for prefix, t in _IFACE_TYPES.items(): - if name.startswith(prefix): - return t - return "ethernet" - - -def _base(name): - return ["interfaces", _iface_type(name), name] - - -def _get_l3_interfaces(client): - try: - result = client.retrieve_show_config(["interfaces"]) - raw = result.get("data") or {} - out = [] - for itype, itype_data in raw.items(): - if not isinstance(itype_data, dict): - continue - for iname, idata in itype_data.items(): - if not isinstance(idata, dict): - continue - entry = {"name": iname, "ipv4": [], "ipv6": []} - for addr in _listify(idata.get("address")): - if ":" in addr: - entry["ipv6"].append({"address": addr}) - elif addr == "dhcp": - entry["ipv4"].append({"address": addr}) - else: - entry["ipv4"].append({"address": addr}) - # VIFs - if "vif" in idata: - vifs = [] - for vid, vdata in idata["vif"].items(): - vif_e = {"vlan_id": int(vid), "ipv4": [], "ipv6": []} - if isinstance(vdata, dict): - for addr in _listify(vdata.get("address")): - if ":" in addr: - vif_e["ipv6"].append({"address": addr}) - else: - vif_e["ipv4"].append({"address": addr}) - vifs.append(vif_e) - entry["vifs"] = vifs - if entry["ipv4"] or entry["ipv6"] or entry.get("vifs"): - out.append(entry) - return out - except VyOSRestError: - return [] - - -def _listify(val): - """Return val as a list regardless of whether it's a str or list.""" - if val is None: - return [] - if isinstance(val, list): - return val - return [val] - - -def _set_addresses(client, name, ipv4, ipv6, commands): - base = _base(name) - itype = _iface_type(name) - for a in ipv4 or []: - client.configure_set(base + ["address"], a["address"]) - commands.append( - "set interfaces {t} {n} address '{a}'".format( - t=itype, - n=name, - a=a["address"], - ), - ) - for a in ipv6 or []: - client.configure_set(base + ["address"], a["address"]) - commands.append( - "set interfaces {t} {n} address '{a}'".format( - t=itype, - n=name, - a=a["address"], - ), - ) - - -def _delete_addresses(client, name, commands): - base = _base(name) - itype = _iface_type(name) - try: - client.configure_delete(base + ["address"]) - commands.append( - "delete interfaces {t} {n} address".format(t=itype, n=name), - ) - except VyOSRestError: - pass - - -def main(): - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - ipv4=dict( - type="list", - elements="dict", - options=dict(address=dict(type="str", required=True)), - ), - ipv6=dict( - type="list", - elements="dict", - options=dict(address=dict(type="str", required=True)), - ), - vifs=dict( - type="list", - elements="dict", - options=dict( - vlan_id=dict(type="int", required=True), - ipv4=dict( - type="list", - elements="dict", - options=dict(address=dict(type="str", required=True)), - ), - ipv6=dict( - type="list", - elements="dict", - options=dict(address=dict(type="str", required=True)), - ), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - - before = _get_l3_interfaces(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - targets = {i["name"] for i in config} if config else {i["name"] for i in before} - for iface in before: - if iface["name"] in targets: - _delete_addresses(client, iface["name"], commands) - changed = True - - elif state in ("merged", "replaced", "overridden"): - if state in ("replaced", "overridden"): - # First remove existing addresses on targeted interfaces - targets = {i["name"] for i in config} - if state == "overridden": - targets = {i["name"] for i in before} - for iface in before: - if iface["name"] in targets: - _delete_addresses(client, iface["name"], commands) - - for iface_cfg in config: - name = iface_cfg["name"] - _set_addresses( - client, - name, - iface_cfg.get("ipv4"), - iface_cfg.get("ipv6"), - commands, - ) - for vif in iface_cfg.get("vifs") or []: - vid = str(vif["vlan_id"]) - base = _base(name) + ["vif", vid] - itype = _iface_type(name) - for a in (vif.get("ipv4") or []) + (vif.get("ipv6") or []): - client.configure_set(base + ["address"], a["address"]) - commands.append( - "set interfaces {t} {n} vif {v} address '{a}'".format( - t=itype, - n=name, - v=vid, - a=a["address"], - ), - ) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get_l3_interfaces(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_lag_interfaces.py.preview b/plugins/modules/vyos_lag_interfaces.py.preview deleted file mode 100644 index 15fe1f3..0000000 --- a/plugins/modules/vyos_lag_interfaces.py.preview +++ /dev/null @@ -1,228 +0,0 @@ -#!/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_lag_interfaces -short_description: LAG/bonding interfaces resource module via REST API. -description: - - Manages Link Aggregation Group (LAG/bond) interfaces on VyOS via REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of LAG interface configurations. - type: list - elements: dict - suboptions: - name: - description: Bond interface name (e.g. bond0). - type: str - required: true - mode: - description: Bonding mode. - type: str - choices: [802.3ad, active-backup, broadcast, round-robin, - transmit-load-balance, adaptive-load-balance, xor-hash] - members: - description: Member interfaces. - type: list - elements: dict - suboptions: - member: - description: Interface name. - type: str - primary: - description: Primary interface name. - type: str - hash_policy: - description: Transmit hash policy. - type: str - choices: [layer2, layer2+3, layer3+4] - arp_monitor: - description: ARP monitoring settings. - type: dict - suboptions: - interval: - description: Monitoring interval in ms. - type: int - target: - description: Target IP addresses. - type: list - elements: str - state: - description: Desired state. - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - returned: always - type: list -after: - returned: when changed - type: list -commands: - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_MODE_MAP = { - "802.3ad": "802.3ad", - "active-backup": "active-backup", - "broadcast": "broadcast", - "round-robin": "round-robin", - "transmit-load-balance": "transmit-load-balance", - "adaptive-load-balance": "adaptive-load-balance", - "xor-hash": "xor-hash", -} - - -def _get(client): - try: - r = client.retrieve_show_config(["interfaces", "bonding"]) - data = r.get("data") or {} - out = [] - for bname, bdata in data.items(): - entry = {"name": bname} - if isinstance(bdata, dict): - if "mode" in bdata: - entry["mode"] = bdata["mode"] - if "primary" in bdata: - entry["primary"] = bdata["primary"] - if "hash-policy" in bdata: - entry["hash_policy"] = bdata["hash-policy"] - members = [] - for m in ( - bdata.get("member", {}).keys() if isinstance(bdata.get("member"), dict) else [] - ): - members.append({"member": m}) - if members: - entry["members"] = members - out.append(entry) - return out - except VyOSRestError: - return [] - - -def _apply(client, cfg, commands): - name = cfg["name"] - base = ["interfaces", "bonding", name] - client.configure_set(base) - commands.append("set interfaces bonding {n}".format(n=name)) - - if cfg.get("mode"): - client.configure_set(base + ["mode"], cfg["mode"]) - commands.append("set interfaces bonding {n} mode {m}".format(n=name, m=cfg["mode"])) - if cfg.get("primary"): - client.configure_set(base + ["primary"], cfg["primary"]) - if cfg.get("hash_policy"): - client.configure_set(base + ["hash-policy"], cfg["hash_policy"]) - for m in cfg.get("members") or []: - client.configure_set(["interfaces", "ethernet", m["member"], "bond-group"], name) - commands.append("set interfaces ethernet {m} bond-group {n}".format(m=m["member"], n=name)) - arp = cfg.get("arp_monitor") or {} - if arp.get("interval"): - client.configure_set(base + ["arp-monitor", "interval"], str(arp["interval"])) - for t in arp.get("target") or []: - client.configure_set(base + ["arp-monitor", "target"], t) - - -def main(): - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - mode=dict(type="str", choices=list(_MODE_MAP.keys())), - members=dict( - type="list", - elements="dict", - options=dict(member=dict(type="str")), - ), - primary=dict(type="str"), - hash_policy=dict(type="str", choices=["layer2", "layer2+3", "layer3+4"]), - arp_monitor=dict( - type="dict", - options=dict( - interval=dict(type="int"), - target=dict(type="list", elements="str"), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - before = _get(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - targets = {i["name"] for i in config} if config else {i["name"] for i in before} - for b in before: - if b["name"] in targets: - client.configure_delete(["interfaces", "bonding", b["name"]]) - commands.append("delete interfaces bonding {n}".format(n=b["name"])) - changed = True - elif state in ("merged", "replaced", "overridden"): - for cfg in config: - _apply(client, cfg, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_lldp_interfaces.py.preview b/plugins/modules/vyos_lldp_interfaces.py.preview deleted file mode 100644 index 4f012aa..0000000 --- a/plugins/modules/vyos_lldp_interfaces.py.preview +++ /dev/null @@ -1,227 +0,0 @@ -#!/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_lldp_interfaces -short_description: LLDP interfaces resource module via REST API. -description: - - Manages per-interface LLDP settings (enable/disable, location) on VyOS - via the HTTPS REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of LLDP interface configurations. - type: list - elements: dict - suboptions: - name: - description: Interface name. - type: str - required: true - enable: - description: Enable LLDP on this interface (default true). - type: bool - default: true - location: - description: LLDP-MED location data. - type: dict - suboptions: - coordinate_based: - type: dict - suboptions: - altitude: - type: int - datum: - type: str - choices: [WGS84, NAD83, MLLW] - latitude: - type: str - required: true - longitude: - type: str - required: true - elin: - description: Emergency ELIN number (10-25 digits). - type: str - state: - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - returned: always - type: list -after: - returned: when changed - type: list -commands: - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_IFACE_TYPES = { - "eth": "ethernet", - "bond": "bonding", - "lo": "loopback", - "dummy": "dummy", - "br": "bridge", -} - - -def _itype(name): - for p, t in _IFACE_TYPES.items(): - if name.startswith(p): - return t - return "ethernet" - - -def _get(client): - try: - r = client.retrieve_show_config(["service", "lldp", "interface"]) - data = r.get("data") or {} - out = [] - for iname, idata in data.items(): - entry = {"name": iname} - if isinstance(idata, dict): - entry["enable"] = "disable" not in idata - out.append(entry) - return out - except VyOSRestError: - return [] - - -def _apply(client, cfg, commands): - name = cfg["name"] - base = ["service", "lldp", "interface", name] - client.configure_set(base) - commands.append("set service lldp interface {n}".format(n=name)) - - if "enable" in cfg and not cfg["enable"]: - client.configure_set(base + ["disable"]) - commands.append("set service lldp interface {n} disable".format(n=name)) - - loc = cfg.get("location") or {} - coord = loc.get("coordinate_based") or {} - if coord: - lbase = base + ["location", "coordinate-based"] - if coord.get("latitude"): - client.configure_set(lbase + ["latitude"], coord["latitude"]) - if coord.get("longitude"): - client.configure_set(lbase + ["longitude"], coord["longitude"]) - if coord.get("altitude"): - client.configure_set(lbase + ["altitude"], str(coord["altitude"])) - if coord.get("datum"): - client.configure_set(lbase + ["datum"], coord["datum"]) - commands.append( - "set service lldp interface {n} location coordinate-based ...".format(n=name), - ) - if loc.get("elin"): - client.configure_set(base + ["location", "elin"], loc["elin"]) - commands.append( - "set service lldp interface {n} location elin {e}".format(n=name, e=loc["elin"]), - ) - - -def main(): - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - enable=dict(type="bool", default=True), - location=dict( - type="dict", - options=dict( - coordinate_based=dict( - type="dict", - options=dict( - altitude=dict(type="int"), - datum=dict(type="str", choices=["WGS84", "NAD83", "MLLW"]), - latitude=dict(type="str", required=True), - longitude=dict(type="str", required=True), - ), - ), - elin=dict(type="str"), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - before = _get(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - for cfg in config if config else before: - name = cfg["name"] if isinstance(cfg, dict) else cfg - try: - client.configure_delete(["service", "lldp", "interface", name]) - commands.append("delete service lldp interface {n}".format(n=name)) - changed = True - except VyOSRestError: - pass - elif state in ("merged", "replaced", "overridden"): - for cfg in config: - _apply(client, cfg, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_ospf_interfaces.py.preview b/plugins/modules/vyos_ospf_interfaces.py.preview deleted file mode 100644 index 37876d4..0000000 --- a/plugins/modules/vyos_ospf_interfaces.py.preview +++ /dev/null @@ -1,290 +0,0 @@ -#!/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_ospf_interfaces -short_description: OSPF Interfaces Resource Module via REST API. -description: - - Manages per-interface OSPF parameters (cost, hello/dead timers, authentication, - passive mode, etc.) for both OSPFv2 and OSPFv3 via the VyOS HTTPS REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of OSPF interface configurations. - type: list - elements: dict - suboptions: - name: - description: Interface name. - type: str - address_family: - description: OSPF settings per address family. - type: list - elements: dict - suboptions: - afi: - description: Address family (ipv4=OSPFv2, ipv6=OSPFv3). - type: str - choices: [ipv4, ipv6] - required: true - authentication: - type: dict - suboptions: - plaintext_password: - type: str - md5_key: - type: dict - suboptions: - key_id: - type: int - key: - type: str - bandwidth: - type: int - cost: - type: int - dead_interval: - type: int - hello_interval: - type: int - mtu_ignore: - type: bool - network: - type: str - priority: - type: int - retransmit_interval: - type: int - transmit_delay: - type: int - ifmtu: - type: int - instance: - type: str - passive: - type: bool - state: - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - returned: always - type: list -after: - returned: when changed - type: list -commands: - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_IFACE_TYPES = { - "eth": "ethernet", - "bond": "bonding", - "lo": "loopback", - "dummy": "dummy", - "br": "bridge", - "wg": "wireguard", - "tun": "tunnel", - "vxlan": "vxlan", -} - - -def _itype(name): - for p, t in _IFACE_TYPES.items(): - if name.startswith(p): - return t - return "ethernet" - - -def _ospf_iface_base(name, afi): - itype = _itype(name) - if afi == "ipv4": - return ["interfaces", itype, name, "ip", "ospf"] - return ["interfaces", itype, name, "ipv6", "ospfv3"] - - -def _apply(client, cfg, commands): - name = cfg["name"] - for af in cfg.get("address_family") or []: - afi = af["afi"] - base = _ospf_iface_base(name, afi) - - int_map = { - "cost": "cost", - "bandwidth": "bandwidth", - "dead_interval": "dead-interval", - "hello_interval": "hello-interval", - "priority": "priority", - "retransmit_interval": "retransmit-interval", - "transmit_delay": "transmit-delay", - "ifmtu": "ifmtu", - } - for param, key in int_map.items(): - if af.get(param) is not None: - client.configure_set(base + [key], str(af[param])) - commands.append("set {b} {k} {v}".format(b=" ".join(base), k=key, v=af[param])) - - if af.get("mtu_ignore"): - client.configure_set(base + ["mtu-ignore"]) - commands.append("set {b} mtu-ignore".format(b=" ".join(base))) - if af.get("passive"): - client.configure_set(base + ["passive"]) - commands.append("set {b} passive".format(b=" ".join(base))) - if af.get("network"): - client.configure_set(base + ["network"], af["network"]) - if af.get("instance"): - client.configure_set(base + ["instance-id"], af["instance"]) - - auth = af.get("authentication") or {} - if auth.get("plaintext_password"): - client.configure_set( - base + ["authentication", "plaintext-password"], - auth["plaintext_password"], - ) - commands.append( - "set {b} authentication plaintext-password ...".format(b=" ".join(base)), - ) - md5 = auth.get("md5_key") or {} - if md5.get("key_id") and md5.get("key"): - client.configure_set( - base + ["authentication", "md5", "key-id", str(md5["key_id"]), "md5-key"], - md5["key"], - ) - - -def _get(client): - try: - r = client.retrieve_show_config(["interfaces"]) - raw = r.get("data") or {} - out = [] - for itype, itype_data in raw.items(): - if not isinstance(itype_data, dict): - continue - for iname, idata in itype_data.items(): - if not isinstance(idata, dict): - continue - afs = [] - ip_ospf = (idata.get("ip") or {}).get("ospf") - if ip_ospf: - afs.append({"afi": "ipv4", "ospf_data": ip_ospf}) - ip6_ospfv3 = (idata.get("ipv6") or {}).get("ospfv3") - if ip6_ospfv3: - afs.append({"afi": "ipv6", "ospfv3_data": ip6_ospfv3}) - if afs: - out.append({"name": iname, "address_family": afs}) - return out - except VyOSRestError: - return [] - - -def main(): - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str"), - address_family=dict( - type="list", - elements="dict", - options=dict( - afi=dict(type="str", required=True, choices=["ipv4", "ipv6"]), - authentication=dict( - type="dict", - options=dict( - plaintext_password=dict(type="str", no_log=True), - md5_key=dict( - type="dict", - options=dict( - key_id=dict(type="int"), - key=dict(type="str", no_log=True), - ), - ), - ), - ), - bandwidth=dict(type="int"), - cost=dict(type="int"), - dead_interval=dict(type="int"), - hello_interval=dict(type="int"), - mtu_ignore=dict(type="bool"), - network=dict(type="str"), - priority=dict(type="int"), - retransmit_interval=dict(type="int"), - transmit_delay=dict(type="int"), - ifmtu=dict(type="int"), - instance=dict(type="str"), - passive=dict(type="bool"), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - before = _get(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - for cfg in config: - _apply(client, cfg, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_ospfv2.py.preview b/plugins/modules/vyos_ospfv2.py.preview deleted file mode 100644 index 063d0dc..0000000 --- a/plugins/modules/vyos_ospfv2.py.preview +++ /dev/null @@ -1,487 +0,0 @@ -#!/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_ospfv2 -short_description: OSPFv2 resource module via REST API. -description: - - Manages OSPFv2 (OSPF for IPv4) configuration on VyOS via HTTPS REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: OSPFv2 configuration. - type: dict - suboptions: - areas: - type: list - elements: dict - suboptions: - area_id: - type: str - area_type: - type: dict - suboptions: - normal: - type: bool - nssa: - type: dict - suboptions: - set: - type: bool - default_cost: - type: int - no_summary: - type: bool - translate: - type: str - choices: [always, candidate, never] - stub: - type: dict - suboptions: - set: - type: bool - default_cost: - type: int - no_summary: - type: bool - authentication: - type: str - choices: [plaintext-password, md5] - network: - type: list - elements: dict - suboptions: - address: - type: str - range: - type: list - elements: dict - suboptions: - address: - type: str - cost: - type: int - not_advertise: - type: bool - substitute: - type: str - shortcut: - type: str - choices: [default, disable, enable] - auto_cost: - type: dict - suboptions: - reference_bandwidth: - type: int - default_information: - type: dict - suboptions: - originate: - type: dict - suboptions: - always: - type: bool - metric: - type: int - metric_type: - type: int - route_map: - type: str - log_adjacency_changes: - type: str - choices: [detail] - max_metric: - type: dict - suboptions: - router_lsa: - type: dict - suboptions: - administrative: - type: bool - on_shutdown: - type: int - on_startup: - type: int - mpls_te: - type: dict - suboptions: - enabled: - type: bool - router_address: - type: str - neighbor: - type: list - elements: dict - suboptions: - neighbor_id: - type: str - poll_interval: - type: int - priority: - type: int - parameters: - type: dict - suboptions: - router_id: - type: str - opaque_lsa: - type: bool - rfc1583_compatibility: - type: bool - abr_type: - type: str - choices: [cisco, ibm, shortcut, standard] - passive_interface: - type: list - elements: str - redistribute: - type: list - elements: dict - suboptions: - route_type: - type: str - choices: [bgp, connected, kernel, rip, static] - metric: - type: int - metric_type: - type: int - route_map: - type: str - state: - type: str - choices: [merged, replaced, deleted, gathered] - default: merged - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - returned: always - type: dict -after: - returned: when changed - type: dict -commands: - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_BASE = ["protocols", "ospf"] - - -def _get(client): - try: - r = client.retrieve_show_config(_BASE) - return r.get("data") or {} - except VyOSRestError: - return {} - - -def _apply(client, config, commands): - params = config.get("parameters") or {} - if params.get("router_id"): - client.configure_set(_BASE + ["parameters", "router-id"], params["router_id"]) - commands.append("set protocols ospf parameters router-id {r}".format(r=params["router_id"])) - if params.get("opaque_lsa"): - client.configure_set(_BASE + ["parameters", "opaque-lsa"]) - if params.get("rfc1583_compatibility"): - client.configure_set(_BASE + ["parameters", "rfc1583-compatibility"]) - if params.get("abr_type"): - client.configure_set(_BASE + ["parameters", "abr-type"], params["abr_type"]) - - ac = config.get("auto_cost") or {} - if ac.get("reference_bandwidth"): - client.configure_set( - _BASE + ["auto-cost", "reference-bandwidth"], - str(ac["reference_bandwidth"]), - ) - commands.append( - "set protocols ospf auto-cost reference-bandwidth {b}".format( - b=ac["reference_bandwidth"], - ), - ) - - if config.get("log_adjacency_changes"): - client.configure_set(_BASE + ["log-adjacency-changes", config["log_adjacency_changes"]]) - - for nbr in config.get("neighbor") or []: - nb = _BASE + ["neighbor", nbr["neighbor_id"]] - client.configure_set(nb) - if nbr.get("poll_interval"): - client.configure_set(nb + ["poll-interval"], str(nbr["poll_interval"])) - if nbr.get("priority"): - client.configure_set(nb + ["priority"], str(nbr["priority"])) - - for pi in config.get("passive_interface") or []: - client.configure_set(_BASE + ["passive-interface"], pi) - commands.append("set protocols ospf passive-interface {i}".format(i=pi)) - - for redist in config.get("redistribute") or []: - rb = _BASE + ["redistribute", redist["route_type"]] - client.configure_set(rb) - commands.append("set protocols ospf redistribute {r}".format(r=redist["route_type"])) - if redist.get("metric"): - client.configure_set(rb + ["metric"], str(redist["metric"])) - if redist.get("metric_type"): - client.configure_set(rb + ["metric-type"], str(redist["metric_type"])) - if redist.get("route_map"): - client.configure_set(rb + ["route-map"], redist["route_map"]) - - di = (config.get("default_information") or {}).get("originate") or {} - if di: - dib = _BASE + ["default-information", "originate"] - client.configure_set(dib) - commands.append("set protocols ospf default-information originate") - if di.get("always"): - client.configure_set(dib + ["always"]) - if di.get("metric"): - client.configure_set(dib + ["metric"], str(di["metric"])) - if di.get("metric_type"): - client.configure_set(dib + ["metric-type"], str(di["metric_type"])) - if di.get("route_map"): - client.configure_set(dib + ["route-map"], di["route_map"]) - - for area in config.get("areas") or []: - aid = area["area_id"] - ab = _BASE + ["area", aid] - client.configure_set(ab) - commands.append("set protocols ospf area {a}".format(a=aid)) - - at = area.get("area_type") or {} - if at.get("normal"): - pass # default - nssa = at.get("nssa") or {} - if nssa.get("set"): - client.configure_set(ab + ["area-type", "nssa"]) - if nssa.get("default_cost"): - client.configure_set( - ab + ["area-type", "nssa", "default-cost"], - str(nssa["default_cost"]), - ) - if nssa.get("no_summary"): - client.configure_set(ab + ["area-type", "nssa", "no-summary"]) - stub = at.get("stub") or {} - if stub.get("set"): - client.configure_set(ab + ["area-type", "stub"]) - if stub.get("default_cost"): - client.configure_set( - ab + ["area-type", "stub", "default-cost"], - str(stub["default_cost"]), - ) - - if area.get("authentication"): - client.configure_set(ab + ["authentication"], area["authentication"]) - - for net in area.get("network") or []: - client.configure_set(ab + ["network"], net["address"]) - commands.append( - "set protocols ospf area {a} network {n}".format( - a=aid, - n=net["address"], - ), - ) - - for rng in area.get("range") or []: - rb2 = ab + ["range", rng["address"]] - client.configure_set(rb2) - if rng.get("cost"): - client.configure_set(rb2 + ["cost"], str(rng["cost"])) - if rng.get("not_advertise"): - client.configure_set(rb2 + ["not-advertise"]) - if rng.get("substitute"): - client.configure_set(rb2 + ["substitute"], rng["substitute"]) - - if area.get("shortcut"): - client.configure_set(ab + ["shortcut"], area["shortcut"]) - - -def main(): - argument_spec = dict( - config=dict( - type="dict", - options=dict( - areas=dict( - type="list", - elements="dict", - options=dict( - area_id=dict(type="str"), - area_type=dict( - type="dict", - options=dict( - normal=dict(type="bool"), - nssa=dict( - type="dict", - options=dict( - set=dict(type="bool"), - default_cost=dict(type="int"), - no_summary=dict(type="bool"), - translate=dict( - type="str", - choices=["always", "candidate", "never"], - ), - ), - ), - stub=dict( - type="dict", - options=dict( - set=dict(type="bool"), - default_cost=dict(type="int"), - no_summary=dict(type="bool"), - ), - ), - ), - ), - authentication=dict(type="str", choices=["plaintext-password", "md5"]), - network=dict( - type="list", - elements="dict", - options=dict(address=dict(type="str")), - ), - range=dict( - type="list", - elements="dict", - options=dict( - address=dict(type="str"), - cost=dict(type="int"), - not_advertise=dict(type="bool"), - substitute=dict(type="str"), - ), - ), - shortcut=dict(type="str", choices=["default", "disable", "enable"]), - ), - ), - auto_cost=dict(type="dict", options=dict(reference_bandwidth=dict(type="int"))), - default_information=dict( - type="dict", - options=dict( - originate=dict( - type="dict", - options=dict( - always=dict(type="bool"), - metric=dict(type="int"), - metric_type=dict(type="int"), - route_map=dict(type="str"), - ), - ), - ), - ), - log_adjacency_changes=dict(type="str", choices=["detail"]), - max_metric=dict(type="dict"), - mpls_te=dict( - type="dict", - options=dict( - enabled=dict(type="bool"), - router_address=dict(type="str"), - ), - ), - neighbor=dict( - type="list", - elements="dict", - options=dict( - neighbor_id=dict(type="str"), - poll_interval=dict(type="int"), - priority=dict(type="int"), - ), - ), - parameters=dict( - type="dict", - options=dict( - router_id=dict(type="str"), - opaque_lsa=dict(type="bool"), - rfc1583_compatibility=dict(type="bool"), - abr_type=dict(type="str", choices=["cisco", "ibm", "shortcut", "standard"]), - ), - ), - passive_interface=dict(type="list", elements="str"), - redistribute=dict( - type="list", - elements="dict", - options=dict( - route_type=dict( - type="str", - choices=["bgp", "connected", "kernel", "rip", "static"], - ), - metric=dict(type="int"), - metric_type=dict(type="int"), - route_map=dict(type="str"), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule( - argument_spec=argument_spec, - required_if=[("state", "merged", ["config"]), ("state", "replaced", ["config"])], - supports_check_mode=True, - ) - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") - commands = [] - changed = False - before = _get(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - if before: - client.configure_delete(_BASE) - commands.append("delete protocols ospf") - changed = True - elif state in ("merged", "replaced"): - if state == "replaced" and before: - client.configure_delete(_BASE) - commands.append("delete protocols ospf") - _apply(client, config, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_ospfv3.py.preview b/plugins/modules/vyos_ospfv3.py.preview deleted file mode 100644 index 0de49d7..0000000 --- a/plugins/modules/vyos_ospfv3.py.preview +++ /dev/null @@ -1,257 +0,0 @@ -#!/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_ospfv3 -short_description: OSPFv3 resource module via REST API. -description: - - Manages OSPFv3 (OSPF for IPv6) configuration on VyOS via HTTPS REST API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: OSPFv3 configuration. - type: dict - suboptions: - areas: - type: list - elements: dict - suboptions: - area_id: - type: str - export_list: - type: str - import_list: - type: str - interface: - description: Interfaces in this area. - type: list - elements: dict - suboptions: - name: - type: str - range: - type: list - elements: dict - suboptions: - address: - type: str - advertise: - type: bool - not_advertise: - type: bool - parameters: - type: dict - suboptions: - router_id: - type: str - redistribute: - type: list - elements: dict - suboptions: - route_type: - type: str - choices: [bgp, connected, kernel, ripng, static] - route_map: - type: str - state: - type: str - choices: [merged, replaced, deleted, gathered] - default: merged - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -before: - returned: always - type: dict -after: - returned: when changed - type: dict -commands: - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_BASE = ["protocols", "ospfv3"] - - -def _get(client): - try: - r = client.retrieve_show_config(_BASE) - return r.get("data") or {} - except VyOSRestError: - return {} - - -def _apply(client, config, commands): - params = config.get("parameters") or {} - if params.get("router_id"): - client.configure_set(_BASE + ["parameters", "router-id"], params["router_id"]) - commands.append( - "set protocols ospfv3 parameters router-id {r}".format(r=params["router_id"]), - ) - - for redist in config.get("redistribute") or []: - rb = _BASE + ["redistribute", redist["route_type"]] - client.configure_set(rb) - commands.append("set protocols ospfv3 redistribute {r}".format(r=redist["route_type"])) - if redist.get("route_map"): - client.configure_set(rb + ["route-map"], redist["route_map"]) - - for area in config.get("areas") or []: - aid = area["area_id"] - ab = _BASE + ["area", aid] - client.configure_set(ab) - commands.append("set protocols ospfv3 area '{a}'".format(a=aid)) - if area.get("export_list"): - client.configure_set(ab + ["export-list"], area["export_list"]) - commands.append( - "set protocols ospfv3 area {a} export-list {e}".format( - a=aid, - e=area["export_list"], - ), - ) - if area.get("import_list"): - client.configure_set(ab + ["import-list"], area["import_list"]) - for iface in area.get("interface") or []: - client.configure_set(ab + ["interface"], iface["name"]) - commands.append( - "set protocols ospfv3 area {a} interface {i}".format( - a=aid, - i=iface["name"], - ), - ) - for rng in area.get("range") or []: - rb2 = ab + ["range", rng["address"]] - client.configure_set(rb2) - commands.append( - "set protocols ospfv3 area {a} range {r}".format( - a=aid, - r=rng["address"], - ), - ) - if rng.get("advertise"): - client.configure_set(rb2 + ["advertise"]) - if rng.get("not_advertise"): - client.configure_set(rb2 + ["not-advertise"]) - - -def main(): - argument_spec = dict( - config=dict( - type="dict", - options=dict( - areas=dict( - type="list", - elements="dict", - options=dict( - area_id=dict(type="str"), - export_list=dict(type="str"), - import_list=dict(type="str"), - interface=dict( - type="list", - elements="dict", - options=dict(name=dict(type="str")), - ), - range=dict( - type="list", - elements="dict", - options=dict( - address=dict(type="str"), - advertise=dict(type="bool"), - not_advertise=dict(type="bool"), - ), - ), - ), - ), - parameters=dict(type="dict", options=dict(router_id=dict(type="str"))), - redistribute=dict( - type="list", - elements="dict", - options=dict( - route_type=dict( - type="str", - choices=["bgp", "connected", "kernel", "ripng", "static"], - ), - route_map=dict(type="str"), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule( - argument_spec=argument_spec, - required_if=[("state", "merged", ["config"]), ("state", "replaced", ["config"])], - supports_check_mode=True, - ) - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") - commands = [] - changed = False - before = _get(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state == "deleted": - if before: - client.configure_delete(_BASE) - commands.append("delete protocols ospfv3") - changed = True - elif state in ("merged", "replaced"): - if state == "replaced" and before: - client.configure_delete(_BASE) - commands.append("delete protocols ospfv3") - _apply(client, config, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_ping.py.preview b/plugins/modules/vyos_ping.py.preview deleted file mode 100644 index 45368d1..0000000 --- a/plugins/modules/vyos_ping.py.preview +++ /dev/null @@ -1,180 +0,0 @@ -#!/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_ping -short_description: Test reachability using ping via VyOS REST API. -description: - - Sends an ICMP ping via the VyOS HTTPS REST API (/show endpoint) and - checks reachability. Mirrors C(vyos.vyos.vyos_ping). -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - dest: - description: Destination IP address or hostname. - type: str - required: true - count: - description: Number of packets to send. - type: int - default: 5 - source: - description: Source interface or IP address. - type: str - ttl: - description: Time-to-live value. - type: int - size: - description: Packet size in bytes. - type: int - interval: - description: Interval between pings in seconds. - type: int - state: - description: C(present) = expect success; C(absent) = expect failure. - type: str - choices: [present, absent] - default: present - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -commands: - description: Ping command tokens sent to the API. - returned: always - type: list -packet_loss: - description: Packet loss percentage string. - returned: always - type: str -packets_rx: - description: Packets received. - returned: always - type: int -packets_tx: - description: Packets transmitted. - returned: always - type: int -rtt: - description: RTT statistics dict (min/avg/max/mdev). - returned: when available - type: dict -""" - -import re - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def _build_ping_path(params): - path = ["ping", params["dest"]] - if params.get("count"): - path += ["count", str(params["count"])] - if params.get("source"): - path += ["interface", params["source"]] - if params.get("ttl"): - path += ["ttl", str(params["ttl"])] - if params.get("size"): - path += ["size", str(params["size"])] - return path - - -def _parse_ping_output(output): - stats = {"packet_loss": "100%", "packets_rx": 0, "packets_tx": 0} - tx_match = re.search(r"(\d+) packets transmitted", output) - rx_match = re.search(r"(\d+) received", output) - loss_match = re.search(r"(\d+(?:\.\d+)?%)\s+packet loss", output) - if tx_match: - stats["packets_tx"] = int(tx_match.group(1)) - if rx_match: - stats["packets_rx"] = int(rx_match.group(1)) - if loss_match: - stats["packet_loss"] = loss_match.group(1) - rtt_match = re.search( - r"rtt min/avg/max/mdev = ([\d.]+)/([\d.]+)/([\d.]+)/([\d.]+)", - output, - ) - if rtt_match: - stats["rtt"] = { - "min": rtt_match.group(1), - "avg": rtt_match.group(2), - "max": rtt_match.group(3), - "mdev": rtt_match.group(4), - } - return stats - - -def main(): - argument_spec = dict( - dest=dict(type="str", required=True), - count=dict(type="int", default=5), - source=dict(type="str"), - ttl=dict(type="int"), - size=dict(type="int"), - interval=dict(type="int"), - state=dict(type="str", default="present", choices=["present", "absent"]), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - - if module.check_mode: - module.exit_json(changed=False, commands=[], packet_loss="0%", packets_rx=0, packets_tx=0) - - client = VyOSRestClient(module) - path = _build_ping_path(module.params) - - try: - result = client.show(path) - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - output = result.get("data", "") - stats = _parse_ping_output(output) - reachable = stats["packets_rx"] > 0 - - if module.params["state"] == "present" and not reachable: - module.fail_json( - msg="Ping to {dest} failed — no packets received.".format(dest=module.params["dest"]), - **stats, - ) - elif module.params["state"] == "absent" and reachable: - module.fail_json( - msg="Expected {dest} to be unreachable but ping succeeded.".format( - dest=module.params["dest"], - ), - **stats, - ) - - module.exit_json(changed=False, commands=path, **stats) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_prefix_lists.py.preview b/plugins/modules/vyos_prefix_lists.py.preview deleted file mode 100644 index 5334e7a..0000000 --- a/plugins/modules/vyos_prefix_lists.py.preview +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -from __future__ import absolute_import, division, print_function - - -__metaclass__ = type - -DOCUMENTATION = r""" ---- -module: vyos_prefix_lists -short_description: Manage prefix-list configuration on VyOS devices using REST API -description: - - Manages IPv4 and IPv6 prefix lists on VyOS via the REST API. - - Uses REST API (C(connection=httpapi)) instead of CLI. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) - -options: - config: - description: List of prefix-list configurations grouped by address family. - type: list - elements: dict - suboptions: - afi: - description: Address family identifier. - type: str - choices: [ipv4, ipv6] - required: true - prefix_lists: - description: Named prefix lists. - type: list - elements: dict - suboptions: - name: - description: Prefix list name. - type: str - required: true - description: - description: Prefix list description. - type: str - entries: - description: Prefix list rules. - type: list - elements: dict - suboptions: - sequence: - description: Rule sequence number. - type: int - required: true - description: - description: Rule description. - type: str - action: - description: Permit or deny. - type: str - choices: [permit, deny] - ge: - description: Minimum prefix length. - type: int - le: - description: Maximum prefix length. - type: int - prefix: - description: Network prefix to match. - type: str - - state: - description: - - Desired state of the prefix-list configuration. - - C(merged) adds or updates entries without removing existing ones. - - C(replaced) replaces each named prefix list mentioned in config. - - C(overridden) replaces all prefix lists for the given AFIs. - - C(deleted) removes prefix lists. Without config removes all. - - C(gathered) returns current configuration as structured data. - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - -notes: - - Requires C(ansible_connection=httpapi) with the VyOS httpapi plugin. - - C(ansible_network_os) must be set to C(vyos.rest.vyos). -""" - -EXAMPLES = r""" -- name: Merge prefix list configuration - vyos.rest.vyos_prefix_lists: - config: - - afi: ipv4 - prefix_lists: - - name: AnsibleIPv4PrefixList - description: PL configured by ansible - entries: - - sequence: 2 - action: permit - prefix: 92.168.10.0/26 - le: 32 - - sequence: 3 - action: deny - prefix: 72.168.2.0/24 - ge: 26 - - afi: ipv6 - prefix_lists: - - name: AllowIPv6Prefix - entries: - - sequence: 5 - action: permit - prefix: 2001:db8:8000::/35 - le: 37 - state: merged - -- name: Delete all prefix lists - vyos.rest.vyos_prefix_lists: - state: deleted - -- name: Gather current prefix list configuration - vyos.rest.vyos_prefix_lists: - state: gathered -""" - -RETURN = r""" -before: - description: Prefix list configuration before this module ran. - returned: always - type: list - -after: - description: Prefix list configuration after this module ran. - returned: when changed - type: list - -commands: - description: List of API command tuples sent to the device. - returned: always - type: list - -gathered: - description: Current prefix list configuration as structured data. - returned: when state is gathered - type: list - -saved: - description: Whether the config was saved after changes. - returned: when changes are applied - type: bool - -response: - description: Raw API response. - returned: when changes are applied - type: dict -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos import VyOSModule - - -# AFI → API key mapping -_PL_KEY = {"ipv4": "prefix-list", "ipv6": "prefix-list6"} - - -# ------------------------------------------------------------ -# Parsing: API response → argspec list -# ------------------------------------------------------------ - - -def get_running_config(vyos): - """ - Fetch current prefix list config and return in argspec list format: - [{"afi": "ipv4", "prefix_lists": [...]}, {"afi": "ipv6", ...}] - """ - raw = vyos.get_config(["policy"]) - if not raw or not isinstance(raw, dict): - return [] - - result = [] - for afi, api_key in [("ipv4", "prefix-list"), ("ipv6", "prefix-list6")]: - pl_data = raw.get(api_key) or {} - if not isinstance(pl_data, dict): - continue - - pls = [] - for pl_name, pl_info in sorted(pl_data.items()): - entry = {"name": pl_name} - pl_info = pl_info or {} - - if pl_info.get("description"): - entry["description"] = pl_info["description"] - - rules = [] - for seq, rdata in sorted( - (pl_info.get("rule") or {}).items(), - key=lambda x: int(x[0]), - ): - rdata = rdata or {} - rule = {"sequence": int(seq)} - if rdata.get("action"): - rule["action"] = rdata["action"] - if rdata.get("prefix"): - rule["prefix"] = rdata["prefix"] - if rdata.get("ge") is not None: - rule["ge"] = int(rdata["ge"]) - if rdata.get("le") is not None: - rule["le"] = int(rdata["le"]) - if rdata.get("description"): - rule["description"] = rdata["description"] - rules.append(rule) - - if rules: - entry["entries"] = rules - pls.append(entry) - - if pls: - result.append({"afi": afi, "prefix_lists": pls}) - - return result - - -# ------------------------------------------------------------ -# Internal normalization: argspec list → nested dicts for diffing -# ------------------------------------------------------------ - - -def _normalize(config): - """ - Convert argspec list to nested dict keyed by afi → pl_name → rule_seq. - { - "ipv4": { - "AnsibleIPv4PrefixList": { - "description": "...", - "rules": {2: {...}, 3: {...}} - } - }, - "ipv6": {...} - } - """ - result = {"ipv4": {}, "ipv6": {}} - for entry in config or []: - afi = entry.get("afi") - if afi not in result: - continue - for pl in entry.get("prefix_lists") or []: - name = pl["name"] - rules = {} - for r in pl.get("entries") or []: - seq = r["sequence"] - rules[seq] = {k: v for k, v in r.items() if k != "sequence" and v is not None} - result[afi][name] = { - "description": pl.get("description"), - "rules": rules, - } - return result - - -# ------------------------------------------------------------ -# Command builders -# ------------------------------------------------------------ - - -def _rule_cmds(base, seq, want_rule, have_rule, state): - """Build commands for a single rule entry.""" - cmds = [] - rbase = base + ["rule", str(seq)] - - if seq not in (have_rule or {}): - # New rule - for field, api_key in [ - ("action", "action"), - ("prefix", "prefix"), - ("description", "description"), - ]: - if want_rule.get(field): - cmds.append(("set", rbase + [api_key, want_rule[field]])) - if want_rule.get("ge") is not None: - cmds.append(("set", rbase + ["ge", str(want_rule["ge"])])) - if want_rule.get("le") is not None: - cmds.append(("set", rbase + ["le", str(want_rule["le"])])) - else: - # Existing rule — update changed fields - h = have_rule[seq] - for field, api_key in [ - ("action", "action"), - ("prefix", "prefix"), - ("description", "description"), - ]: - if want_rule.get(field) and want_rule[field] != h.get(field): - cmds.append(("set", rbase + [api_key, want_rule[field]])) - for field in ("ge", "le"): - if want_rule.get(field) is not None and want_rule[field] != h.get(field): - cmds.append(("set", rbase + [field, str(want_rule[field])])) - - return cmds - - -def build_commands(config, have_raw, state): - """ - Build command tuples to move from have → want. - """ - cmds = [] - - if state == "deleted": - if not config: - # Delete all prefix lists for both AFIs - for afi, api_key in _PL_KEY.items(): - # Only delete if something exists - if any(e.get("afi") == afi for e in have_raw): - cmds.append(("delete", ["policy", api_key])) - else: - # Delete only specified prefix lists - for entry in config: - afi = entry["afi"] - api_key = _PL_KEY[afi] - for pl in entry.get("prefix_lists") or []: - cmds.append(("delete", ["policy", api_key, pl["name"]])) - return cmds - - want = _normalize(config) - have = _normalize(have_raw) - - for afi, api_key in _PL_KEY.items(): - want_afi = want.get(afi, {}) - have_afi = have.get(afi, {}) - - if state == "overridden": - # Delete PLs present on device but not in want - for pl_name in set(have_afi) - set(want_afi): - cmds.append(("delete", ["policy", api_key, pl_name])) - - for pl_name, want_pl in want_afi.items(): - have_pl = have_afi.get(pl_name, {}) - base = ["policy", api_key, pl_name] - - if state == "replaced" and pl_name in have_afi: - # Full replacement — delete existing then re-set - cmds.append(("delete", ["policy", api_key, pl_name])) - have_pl = {} - - # Description - if want_pl.get("description") and want_pl["description"] != have_pl.get("description"): - cmds.append(("set", base + ["description", want_pl["description"]])) - - # Rules - want_rules = want_pl.get("rules", {}) - have_rules = have_pl.get("rules", {}) - - if state == "replaced": - # have_rules is now empty after delete above - have_rules = {} - - for seq, want_rule in want_rules.items(): - cmds += _rule_cmds(base, seq, want_rule, have_rules, state) - - # merged: leave extra have rules alone - # replaced: they were deleted with the parent node above - - return cmds - - -# ------------------------------------------------------------ -# Argument spec -# ------------------------------------------------------------ - -ARGUMENT_SPEC = dict( - config=dict(type="list", elements="dict"), - state=dict( - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), -) - - -# ------------------------------------------------------------ -# Main -# ------------------------------------------------------------ - - -def main(): - - module = AnsibleModule(ARGUMENT_SPEC, supports_check_mode=True) - - vyos = VyOSModule(module) - - state = module.params["state"] - config = module.params.get("config") or [] - - have = get_running_config(vyos) - - if state == "gathered": - module.exit_json(changed=False, gathered=have) - - commands = build_commands(config, have, state) - - if module.check_mode: - module.exit_json(changed=bool(commands), commands=commands, before=have) - - if commands: - response = vyos.apply_commands(commands) - saved = vyos.save_config() - module.exit_json( - changed=True, - before=have, - after=get_running_config(vyos), - commands=commands, - saved=saved, - response=response, - ) - - module.exit_json(changed=False, before=have, after=have, commands=[]) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_reset.py.preview b/plugins/modules/vyos_reset.py.preview deleted file mode 100644 index ca3e343..0000000 --- a/plugins/modules/vyos_reset.py.preview +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_reset -short_description: Execute reset commands on a VyOS device via REST API. -description: - - Sends a C(reset) operational command to a VyOS device via the HTTPS REST - API (C(/reset) endpoint). - - Useful for resetting BGP sessions, VPN tunnels, ARP caches, etc. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - path: - description: - - Reset command path tokens. - - E.g. C(["ip", "bgp", "192.0.2.1"]) to reset a BGP peer. - type: list - elements: str - required: true - 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 -requirements: - - VyOS 1.3+ -examples: | - - name: Reset a BGP peer - vyos.rest.vyos_reset: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["ip", "bgp", "192.0.2.11"] - - - name: Reset an IPsec VPN peer - vyos.rest.vyos_reset: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["vpn", "ipsec-peer", "203.0.113.5"] - - - name: Clear ARP cache - vyos.rest.vyos_reset: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["arp", "cache"] -""" - -RETURN = r""" -output: - description: Text output from the reset command (usually empty on success). - returned: success - type: str -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def main(): - argument_spec = dict( - path=dict(type="list", elements="str", required=True), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=False, - ) - - client = VyOSRestClient(module) - try: - result = client.reset(module.params["path"]) - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=True, output=result.get("data", "")) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_show.py.preview b/plugins/modules/vyos_show.py.preview deleted file mode 100644 index fbe6c1b..0000000 --- a/plugins/modules/vyos_show.py.preview +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_show -short_description: Execute op-mode show commands on a VyOS device via REST API. -description: - - Sends a C(show) operational-mode command to a VyOS device via the - HTTPS REST API (C(/show) endpoint) and returns the text output. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - path: - description: - - Operational-mode command tokens following C(show). - - E.g. C(["interfaces"]) maps to C(show interfaces). - type: list - elements: str - required: true - 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: - - This module never modifies device state. -requirements: - - VyOS 1.3+ -seealso: - - module: vyos.rest.vyos_configure - - module: vyos.rest.vyos_retrieve -examples: | - - name: Show interfaces - vyos.rest.vyos_show: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["interfaces"] - register: iface_output - - - name: Show version - vyos.rest.vyos_show: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["version"] - register: ver_output - - - name: Show installed images - vyos.rest.vyos_show: - hostname: 192.168.1.1 - api_key: MY-KEY - path: ["system", "image"] - register: images -""" - -RETURN = r""" -output: - description: Raw text output from the show command. - returned: success - type: str - sample: | - Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down - Interface IP Address S/L Description - eth0 10.0.0.1/24 u/u WAN -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def main(): - argument_spec = dict( - path=dict(type="list", elements="str", required=True), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - - client = VyOSRestClient(module) - try: - result = client.show(module.params["path"]) - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=False, output=result.get("data", "")) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_static_routes.py.preview b/plugins/modules/vyos_static_routes.py.preview deleted file mode 100644 index f203603..0000000 --- a/plugins/modules/vyos_static_routes.py.preview +++ /dev/null @@ -1,397 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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 - -DOCUMENTATION = r""" ---- -module: vyos_static_routes -short_description: Manage static routes on VyOS via the REST API. -description: - - Manages IPv4 and IPv6 static routes on VyOS devices using the HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_static_routes) but uses the HTTP API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - config: - description: List of static route address-family configurations. - type: list - elements: dict - suboptions: - address_families: - description: List of address family route groups. - type: list - elements: dict - suboptions: - afi: - description: Address family indicator. - type: str - choices: [ipv4, ipv6] - required: true - routes: - description: List of static route entries. - type: list - elements: dict - suboptions: - dest: - description: Destination prefix in CIDR notation. - type: str - required: true - blackhole_config: - description: Blackhole route configuration. - type: dict - suboptions: - distance: - description: Administrative distance (1-255). - type: int - type: - description: Blackhole type. - type: str - next_hops: - description: List of next-hop addresses. - type: list - elements: dict - suboptions: - forward_router_address: - description: Next-hop IP address. - type: str - required: true - admin_distance: - description: Administrative distance for this next-hop. - type: int - enabled: - description: Whether this next-hop is enabled. - type: bool - default: true - interface: - description: Outgoing interface name. - type: str - state: - description: - - C(merged): Add routes (preserve existing). - - C(replaced): Replace routes for listed destinations. - - C(overridden): Replace the entire static route table. - - C(deleted): Remove listed (or all) static routes. - - C(gathered): Read static routes from device. - type: str - choices: [merged, replaced, overridden, deleted, gathered] - default: merged - 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 -requirements: - - VyOS 1.3+ -seealso: - - module: vyos.vyos.vyos_static_routes -examples: | - - name: Add IPv4 static routes - vyos.rest.vyos_static_routes: - hostname: 192.168.1.1 - api_key: MY-KEY - config: - - address_families: - - afi: ipv4 - routes: - - dest: 192.0.2.0/24 - next_hops: - - forward_router_address: 10.0.0.1 - - dest: 203.0.113.0/24 - blackhole_config: - distance: 200 - state: merged - - - name: Delete all static routes - vyos.rest.vyos_static_routes: - hostname: 192.168.1.1 - api_key: MY-KEY - state: deleted -""" - -RETURN = r""" -before: - description: Static route config before the module ran. - returned: always - type: list -after: - description: Static route config after the module ran. - returned: when changed - type: list -gathered: - description: Static routes read from device (state=gathered). - returned: when state is gathered - type: list -commands: - description: set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_ROUTE_BASE = { - "ipv4": ["protocols", "static", "route"], - "ipv6": ["protocols", "static", "route6"], -} - - -def _get_static_routes(client): - try: - result = client.retrieve_show_config(["protocols", "static"]) - data = result.get("data") or {} - out = [] - for afi, route_key in [("ipv4", "route"), ("ipv6", "route6")]: - routes_data = data.get(route_key, {}) - if not isinstance(routes_data, dict): - continue - routes = [] - for dest, rdata in routes_data.items(): - route_entry = {"dest": dest} - if isinstance(rdata, dict): - if "blackhole" in rdata: - bh = rdata["blackhole"] - bc = {} - if isinstance(bh, dict) and "distance" in bh: - bc["distance"] = int(bh["distance"]) - route_entry["blackhole_config"] = bc - next_hops = [] - for nh_key in ("next-hop", "next_hop"): - nh_data = rdata.get(nh_key, {}) - if isinstance(nh_data, dict): - for nh_addr, nh_opts in nh_data.items(): - nh = {"forward_router_address": nh_addr} - if isinstance(nh_opts, dict): - if "distance" in nh_opts: - nh["admin_distance"] = int(nh_opts["distance"]) - nh["enabled"] = "disable" not in nh_opts - next_hops.append(nh) - if next_hops: - route_entry["next_hops"] = next_hops - routes.append(route_entry) - if routes: - out.append({"address_families": [{"afi": afi, "routes": routes}]}) - return out - except VyOSRestError: - return [] - - -def _apply_route(client, afi, route, commands): - base = _ROUTE_BASE[afi] - dest = route["dest"] - - if route.get("blackhole_config") is not None: - bh_path = base + [dest, "blackhole"] - client.configure_set(bh_path) - commands.append("set {p}".format(p=" ".join(bh_path))) - dist = route["blackhole_config"].get("distance") - if dist: - client.configure_set(bh_path + ["distance"], str(dist)) - commands.append( - "set {p} distance {d}".format(p=" ".join(bh_path), d=dist), - ) - - for nh in route.get("next_hops") or []: - nh_addr = nh["forward_router_address"] - nh_path = base + [dest, "next-hop", nh_addr] - client.configure_set(nh_path) - commands.append("set {p}".format(p=" ".join(nh_path))) - if nh.get("admin_distance"): - client.configure_set(nh_path + ["distance"], str(nh["admin_distance"])) - commands.append( - "set {p} distance {d}".format( - p=" ".join(nh_path), - d=nh["admin_distance"], - ), - ) - if "enabled" in nh and not nh["enabled"]: - client.configure_set(nh_path + ["disable"]) - commands.append("set {p} disable".format(p=" ".join(nh_path))) - if nh.get("interface"): - client.configure_set(nh_path + ["interface"], nh["interface"]) - commands.append( - "set {p} interface {i}".format( - p=" ".join(nh_path), - i=nh["interface"], - ), - ) - - -def main(): - argument_spec = dict( - config=dict( - type="list", - elements="dict", - options=dict( - address_families=dict( - type="list", - elements="dict", - options=dict( - afi=dict(type="str", required=True, choices=["ipv4", "ipv6"]), - routes=dict( - type="list", - elements="dict", - options=dict( - dest=dict(type="str", required=True), - blackhole_config=dict( - type="dict", - options=dict( - distance=dict(type="int"), - type=dict(type="str"), - ), - ), - next_hops=dict( - type="list", - elements="dict", - options=dict( - forward_router_address=dict( - type="str", - required=True, - ), - admin_distance=dict(type="int"), - enabled=dict(type="bool", default=True), - interface=dict(type="str"), - ), - ), - ), - ), - ), - ), - ), - ), - state=dict( - type="str", - default="merged", - choices=["merged", "replaced", "overridden", "deleted", "gathered"], - ), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - - client = VyOSRestClient(module) - state = module.params["state"] - config = module.params.get("config") or [] - commands = [] - changed = False - - before = _get_static_routes(client) - - if state == "gathered": - module.exit_json(changed=False, gathered=before, before=before, commands=[]) - - if module.check_mode: - module.exit_json(changed=True, before=before, commands=["(check mode)"]) - - try: - if state in ("overridden", "deleted") and not config: - # Delete all static routes - for afi_key in ("route", "route6"): - try: - client.configure_delete(["protocols", "static", afi_key]) - commands.append( - "delete protocols static {k}".format(k=afi_key), - ) - except VyOSRestError: - pass - changed = True - - elif state == "deleted" and config: - for entry in config: - for af in entry.get("address_families") or []: - afi = af["afi"] - base = _ROUTE_BASE[afi] - for route in af.get("routes") or []: - try: - client.configure_delete(base + [route["dest"]]) - commands.append( - "delete {p} {d}".format( - p=" ".join(base), - d=route["dest"], - ), - ) - except VyOSRestError: - pass - changed = True - - elif state in ("merged", "replaced", "overridden"): - if state in ("replaced", "overridden"): - # Remove existing routes for affected destinations - dests_by_afi = {} - for entry in config: - for af in entry.get("address_families") or []: - afi = af["afi"] - dests_by_afi.setdefault(afi, set()) - for route in af.get("routes") or []: - dests_by_afi[afi].add(route["dest"]) - if state == "overridden": - for afi_key in ("route", "route6"): - try: - client.configure_delete( - ["protocols", "static", afi_key], - ) - commands.append( - "delete protocols static {k}".format(k=afi_key), - ) - except VyOSRestError: - pass - else: - for afi, dests in dests_by_afi.items(): - base = _ROUTE_BASE[afi] - for dest in dests: - try: - client.configure_delete(base + [dest]) - commands.append( - "delete {p} {d}".format( - p=" ".join(base), - d=dest, - ), - ) - except VyOSRestError: - pass - - for entry in config: - for af in entry.get("address_families") or []: - afi = af["afi"] - for route in af.get("routes") or []: - _apply_route(client, afi, route, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - after = _get_static_routes(client) if changed else before - module.exit_json(changed=changed, before=before, after=after, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_system.py.preview b/plugins/modules/vyos_system.py.preview deleted file mode 100644 index d1d696e..0000000 --- a/plugins/modules/vyos_system.py.preview +++ /dev/null @@ -1,147 +0,0 @@ -#!/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_system -short_description: Manage system settings on VyOS via the REST API. -description: - - Manages system-level settings — hostname, domain name, name servers, - domain search list — using the VyOS HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_system) but uses the HTTP API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - host_name: - description: System hostname. - type: str - domain_name: - description: System domain name. - type: str - name_server: - description: List of DNS name servers. Mutually exclusive with domain_search. - type: list - elements: str - aliases: [name_servers] - domain_search: - description: List of domain search suffixes. Mutually exclusive with name_server. - type: list - elements: str - state: - description: C(present) to apply, C(absent) to remove. - type: str - choices: [present, absent] - default: present - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -commands: - description: set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def main(): - argument_spec = dict( - host_name=dict(type="str"), - domain_name=dict(type="str"), - name_server=dict(type="list", elements="str", aliases=["name_servers"]), - domain_search=dict(type="list", elements="str"), - state=dict(type="str", default="present", choices=["present", "absent"]), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule( - argument_spec=argument_spec, - mutually_exclusive=[["name_server", "domain_search"]], - supports_check_mode=True, - ) - - if module.check_mode: - module.exit_json(changed=True, commands=["(check mode)"]) - - client = VyOSRestClient(module) - state = module.params["state"] - commands = [] - changed = False - - try: - if state == "present": - if module.params.get("host_name"): - client.configure_set(["system", "host-name"], module.params["host_name"]) - commands.append("set system host-name '{h}'".format(h=module.params["host_name"])) - changed = True - if module.params.get("domain_name"): - client.configure_set(["system", "domain-name"], module.params["domain_name"]) - commands.append( - "set system domain-name '{d}'".format(d=module.params["domain_name"]), - ) - changed = True - for ns in module.params.get("name_server") or []: - client.configure_set(["system", "name-server"], ns) - commands.append("set system name-server {ns}".format(ns=ns)) - changed = True - for ds in module.params.get("domain_search") or []: - client.configure_set(["system", "domain-search", "domain"], ds) - commands.append("set system domain-search domain {ds}".format(ds=ds)) - changed = True - else: - if module.params.get("host_name"): - try: - client.configure_delete(["system", "host-name"]) - commands.append("delete system host-name") - changed = True - except VyOSRestError: - pass - if module.params.get("domain_name"): - try: - client.configure_delete(["system", "domain-name"]) - commands.append("delete system domain-name") - changed = True - except VyOSRestError: - pass - if module.params.get("name_server"): - try: - client.configure_delete(["system", "name-server"]) - commands.append("delete system name-server") - changed = True - except VyOSRestError: - pass - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=changed, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_user.py.preview b/plugins/modules/vyos_user.py.preview deleted file mode 100644 index 41e014d..0000000 --- a/plugins/modules/vyos_user.py.preview +++ /dev/null @@ -1,282 +0,0 @@ -#!/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_user -short_description: Manage local users on VyOS via the REST API. -description: - - Manages local user accounts on VyOS devices using the HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_user) but uses the HTTP API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - aggregate: - description: List of user definitions. Mutually exclusive with I(name). - type: list - elements: dict - aliases: [users, collection] - suboptions: - name: - description: Username. - type: str - required: true - full_name: - description: Full name. - type: str - configured_password: - description: Plaintext password (will be hashed on device). - type: str - no_log: true - encrypted_password: - description: Pre-hashed password string. - type: str - no_log: true - update_password: - description: When to update the password. - type: str - choices: [always, on_create] - level: - description: User privilege level. - type: str - choices: [admin, operator] - public_keys: - description: SSH public keys for authentication. - type: list - elements: dict - suboptions: - name: - description: Key identifier (e.g. user@host). - type: str - required: true - key: - description: Base64-encoded public key. - type: str - required: true - type: - description: Key type. - type: str - required: true - choices: [ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, - ecdsa-sha2-nistp384, ssh-ed25519, ecdsa-sha2-nistp521] - state: - description: Present or absent. - type: str - choices: [present, absent] - name: - description: Single username. Mutually exclusive with I(aggregate). - type: str - full_name: - description: Full name for the single user. - type: str - configured_password: - description: Plaintext password. - type: str - no_log: true - encrypted_password: - description: Pre-hashed password. - type: str - no_log: true - update_password: - type: str - choices: [always, on_create] - level: - type: str - choices: [admin, operator] - public_keys: - type: list - elements: dict - suboptions: - name: - type: str - required: true - key: - type: str - required: true - type: - type: str - required: true - state: - description: Whether to create (present) or remove (absent) the user. - type: str - choices: [present, absent] - default: present - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -commands: - description: set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -_KEY_TYPES = [ - "ssh-dss", - "ssh-rsa", - "ecdsa-sha2-nistp256", - "ecdsa-sha2-nistp384", - "ssh-ed25519", - "ecdsa-sha2-nistp521", -] - - -def _apply_user(client, user_def, commands, update_password="always"): - name = user_def["name"] - base = ["system", "login", "user", name] - client.configure_set(base) - commands.append("set system login user {n}".format(n=name)) - - if user_def.get("full_name"): - client.configure_set(base + ["full-name"], user_def["full_name"]) - if user_def.get("level"): - client.configure_set(base + ["level"], user_def["level"]) - if user_def.get("configured_password") and update_password in ("always",): - client.configure_set( - base + ["authentication", "plaintext-password"], - user_def["configured_password"], - ) - commands.append( - "set system login user {n} authentication plaintext-password".format(n=name), - ) - if user_def.get("encrypted_password"): - client.configure_set( - base + ["authentication", "encrypted-password"], - user_def["encrypted_password"], - ) - for pk in user_def.get("public_keys") or []: - kb = base + ["authentication", "public-keys", pk["name"]] - client.configure_set(kb + ["key"], pk["key"]) - client.configure_set(kb + ["type"], pk["type"]) - commands.append( - "set system login user {n} authentication public-keys {k}".format( - n=name, - k=pk["name"], - ), - ) - - -def _delete_user(client, name, commands): - try: - client.configure_delete(["system", "login", "user", name]) - commands.append("delete system login user {n}".format(n=name)) - except VyOSRestError: - pass - - -def main(): - pk_spec = dict( - type="list", - elements="dict", - options=dict( - name=dict(type="str", required=True), - key=dict(type="str", required=True), - type=dict(type="str", required=True, choices=_KEY_TYPES), - ), - ) - user_spec = dict( - name=dict(type="str", required=True), - full_name=dict(type="str"), - configured_password=dict(type="str", no_log=True), - encrypted_password=dict(type="str", no_log=True), - update_password=dict(type="str", choices=["always", "on_create"]), - level=dict(type="str", choices=["admin", "operator"]), - public_keys=pk_spec, - state=dict(type="str", choices=["present", "absent"]), - ) - argument_spec = dict( - aggregate=dict( - type="list", - elements="dict", - aliases=["users", "collection"], - options=user_spec, - ), - name=dict(type="str"), - full_name=dict(type="str"), - configured_password=dict(type="str", no_log=True), - encrypted_password=dict(type="str", no_log=True), - update_password=dict(type="str", choices=["always", "on_create"]), - level=dict(type="str", choices=["admin", "operator"]), - public_keys=pk_spec, - state=dict(type="str", default="present", choices=["present", "absent"]), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule( - argument_spec=argument_spec, - mutually_exclusive=[["aggregate", "name"]], - supports_check_mode=True, - ) - if module.check_mode: - module.exit_json(changed=True, commands=["(check mode)"]) - - client = VyOSRestClient(module) - commands = [] - changed = False - - # Build unified user list - users = module.params.get("aggregate") or [] - if module.params.get("name"): - users = [ - { - "name": module.params["name"], - "full_name": module.params.get("full_name"), - "configured_password": module.params.get("configured_password"), - "encrypted_password": module.params.get("encrypted_password"), - "update_password": module.params.get("update_password", "always"), - "level": module.params.get("level"), - "public_keys": module.params.get("public_keys"), - "state": module.params.get("state", "present"), - }, - ] - - try: - for u in users: - u_state = u.get("state") or module.params.get("state", "present") - if u_state == "absent": - _delete_user(client, u["name"], commands) - else: - _apply_user( - client, - u, - commands, - update_password=u.get("update_password") or "always", - ) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=changed, commands=commands) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/vyos_vlan.py.preview b/plugins/modules/vyos_vlan.py.preview deleted file mode 100644 index 18eca92..0000000 --- a/plugins/modules/vyos_vlan.py.preview +++ /dev/null @@ -1,197 +0,0 @@ -#!/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_vlan -short_description: Manage VLANs on VyOS network devices via REST API. -description: - - Manages 802.1Q VLAN sub-interfaces on VyOS Ethernet interfaces using - the HTTPS REST API. - - Mirrors C(vyos.vyos.vyos_vlan) but uses the HTTP API. -version_added: "1.0.0" -author: - - VyOS Community (@vyos) -options: - vlan_id: - description: VLAN ID (0-4094). - type: int - name: - description: VLAN name (used as description on the VIF). - type: str - address: - description: IP address for the VLAN interface. - type: str - interfaces: - description: List of physical interfaces to configure the VLAN on. - type: list - elements: str - aggregate: - description: List of VLAN definitions. - type: list - elements: dict - suboptions: - vlan_id: - type: int - required: true - name: - type: str - address: - type: str - interfaces: - type: list - elements: str - required: true - state: - type: str - choices: [present, absent] - state: - description: C(present) to configure, C(absent) to remove. - type: str - choices: [present, absent] - default: present - purge: - description: Remove VLANs not defined in aggregate. - type: bool - default: false - hostname: - type: str - required: true - port: - type: int - default: 443 - api_key: - type: str - required: true - no_log: true - timeout: - type: int - default: 30 - verify_ssl: - type: bool - default: false -""" - -RETURN = r""" -commands: - description: set/delete commands issued. - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.rest.plugins.module_utils.vyos_rest import ( - VYOS_REST_CONNECTION_ARGSPEC, - VyOSRestClient, - VyOSRestError, -) - - -def _apply_vlan(client, vlan, commands): - vid = str(vlan["vlan_id"]) - for iface in vlan.get("interfaces") or []: - # Detect interface type - if iface.startswith("bond"): - itype = "bonding" - else: - itype = "ethernet" - vif_base = ["interfaces", itype, iface, "vif", vid] - client.configure_set(vif_base) - commands.append("set interfaces {t} {i} vif {v}".format(t=itype, i=iface, v=vid)) - if vlan.get("name"): - client.configure_set(vif_base + ["description"], vlan["name"]) - commands.append( - "set interfaces {t} {i} vif {v} description '{n}'".format( - t=itype, - i=iface, - v=vid, - n=vlan["name"], - ), - ) - if vlan.get("address"): - client.configure_set(vif_base + ["address"], vlan["address"]) - commands.append( - "set interfaces {t} {i} vif {v} address {a}".format( - t=itype, - i=iface, - v=vid, - a=vlan["address"], - ), - ) - - -def _delete_vlan(client, vlan, commands): - vid = str(vlan["vlan_id"]) - for iface in vlan.get("interfaces") or []: - itype = "bonding" if iface.startswith("bond") else "ethernet" - try: - client.configure_delete(["interfaces", itype, iface, "vif", vid]) - commands.append("delete interfaces {t} {i} vif {v}".format(t=itype, i=iface, v=vid)) - except VyOSRestError: - pass - - -def main(): - vlan_spec = dict( - vlan_id=dict(type="int", required=True), - name=dict(type="str"), - address=dict(type="str"), - interfaces=dict(type="list", elements="str"), - state=dict(type="str", choices=["present", "absent"]), - ) - argument_spec = dict( - vlan_id=dict(type="int"), - name=dict(type="str"), - address=dict(type="str"), - interfaces=dict(type="list", elements="str"), - aggregate=dict(type="list", elements="dict", options=vlan_spec), - state=dict(type="str", default="present", choices=["present", "absent"]), - purge=dict(type="bool", default=False), - ) - argument_spec.update(VYOS_REST_CONNECTION_ARGSPEC) - module = AnsibleModule( - argument_spec=argument_spec, - mutually_exclusive=[["aggregate", "vlan_id"]], - supports_check_mode=True, - ) - if module.check_mode: - module.exit_json(changed=True, commands=["(check mode)"]) - - client = VyOSRestClient(module) - commands = [] - changed = False - - vlans = module.params.get("aggregate") or [] - if module.params.get("vlan_id"): - vlans = [ - { - "vlan_id": module.params["vlan_id"], - "name": module.params.get("name"), - "address": module.params.get("address"), - "interfaces": module.params.get("interfaces") or [], - "state": module.params.get("state", "present"), - }, - ] - - try: - for vlan in vlans: - v_state = vlan.get("state") or module.params.get("state", "present") - if v_state == "absent": - _delete_vlan(client, vlan, commands) - else: - _apply_vlan(client, vlan, commands) - changed = True - except VyOSRestError as exc: - module.fail_json(msg=str(exc)) - - module.exit_json(changed=changed, commands=commands) - - -if __name__ == "__main__": - main() |
