summaryrefslogtreecommitdiff
path: root/plugins/modules
diff options
context:
space:
mode:
authoromnom62 <omnom62@outlook.com>2026-07-07 20:51:54 +1000
committeromnom62 <omnom62@outlook.com>2026-07-07 20:51:54 +1000
commita05655aee624df89232023cd3ec467ca46359eb2 (patch)
tree0600d9161895c065c06d720446b7ebdbe1c4bf8d /plugins/modules
parent121088ec0b11a8a36259e5afa3be33fc131b5ef2 (diff)
downloadrest.vyos-a05655aee624df89232023cd3ec467ca46359eb2.tar.gz
rest.vyos-a05655aee624df89232023cd3ec467ca46359eb2.zip
T8323: vyos_vlan module
Diffstat (limited to 'plugins/modules')
-rw-r--r--plugins/modules/vyos_vlan.py239
1 files changed, 239 insertions, 0 deletions
diff --git a/plugins/modules/vyos_vlan.py b/plugins/modules/vyos_vlan.py
new file mode 100644
index 0000000..863ec34
--- /dev/null
+++ b/plugins/modules/vyos_vlan.py
@@ -0,0 +1,239 @@
+#!/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 VLAN (vif) configuration on VyOS devices using REST API
+description:
+ - Manages VLAN sub-interface configuration on VyOS Ethernet interfaces
+ 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 VLAN configurations.
+ type: list
+ elements: dict
+ suboptions:
+ vlan_id:
+ description: VLAN ID (0-4094).
+ type: int
+ required: true
+ description:
+ description: VLAN description.
+ type: str
+ address:
+ description: IP address for the VLAN interface.
+ type: str
+ interfaces:
+ description: List of Ethernet interfaces to configure this VLAN on.
+ type: list
+ elements: str
+ required: true
+ state:
+ description:
+ - C(present) creates or updates VLANs.
+ - C(absent) removes VLANs.
+ - C(gathered) returns current VLAN configuration.
+ type: str
+ choices: [present, absent, gathered]
+ default: present
+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: Configure VLANs
+ vyos.rest.vyos_vlan:
+ config:
+ - vlan_id: 10
+ description: VLAN10
+ address: 192.168.10.1/24
+ interfaces:
+ - eth1
+ - vlan_id: 20
+ description: VLAN20
+ interfaces:
+ - eth1
+ - eth2
+ state: present
+
+- name: Remove a VLAN
+ vyos.rest.vyos_vlan:
+ config:
+ - vlan_id: 10
+ interfaces:
+ - eth1
+ state: absent
+
+- name: Gather VLAN configuration
+ vyos.rest.vyos_vlan:
+ state: gathered
+"""
+
+RETURN = r"""
+before:
+ description: VLAN configuration before this module ran.
+ returned: always
+ type: list
+after:
+ description: VLAN 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 VLAN 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,
+ diff_configs,
+ normalize,
+)
+
+
+_BASE = ["interfaces", "ethernet"]
+
+
+def get_running_config(vyos):
+ raw = vyos.get_config(_BASE)
+ if not raw or not isinstance(raw, dict):
+ return []
+
+ eth_data = raw.get("ethernet", raw)
+ if not isinstance(eth_data, dict):
+ return []
+
+ vlan_map = {}
+ for iface_name, iface_data in sorted(eth_data.items()):
+ iface_data = iface_data or {}
+ vif_data = iface_data.get("vif", {}) or {}
+ norm_vif = normalize(vif_data)
+ for vlan_id_str, vif_cfg in sorted(
+ norm_vif.items(),
+ key=lambda x: int(x[0]),
+ ):
+ vif_cfg = vif_cfg or {}
+ vlan_id = int(vlan_id_str)
+ if vlan_id not in vlan_map:
+ vlan_map[vlan_id] = {"vlan_id": vlan_id, "interfaces": []}
+ vlan_map[vlan_id]["interfaces"].append(iface_name)
+ for k, v in vif_cfg.items():
+ if k not in ("interfaces",):
+ if isinstance(v, list):
+ vlan_map[vlan_id][k] = v[0] if len(v) == 1 else v
+ else:
+ vlan_map[vlan_id][k] = v
+
+ return sorted(vlan_map.values(), key=lambda x: x["vlan_id"])
+
+
+_STRUCTURAL_KEYS = {"vlan_id", "interfaces"}
+
+
+def build_commands(config, have_list, state):
+ cmds = []
+
+ have_map = {(e["vlan_id"], iface): e for e in have_list for iface in e.get("interfaces", [])}
+
+ for want in config or []:
+ vlan_id = str(want["vlan_id"])
+ for iface in want.get("interfaces") or []:
+ vif_base = _BASE + [iface, "vif", vlan_id]
+ have_entry = have_map.get((want["vlan_id"], iface), {})
+
+ if state == "absent":
+ if have_entry:
+ cmds.append(("delete", vif_base))
+ continue
+
+ want_vif = {
+ k: v for k, v in want.items() if k not in _STRUCTURAL_KEYS and v is not None
+ }
+ have_vif = {k: v for k, v in have_entry.items() if k not in _STRUCTURAL_KEYS}
+
+ new_cmds = diff_configs(want_vif, have_vif, vif_base)
+ if not new_cmds and not have_entry:
+ cmds.append(("set", vif_base))
+ else:
+ cmds += new_cmds
+
+ return cmds
+
+
+ARGUMENT_SPEC = dict(
+ config=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ vlan_id=dict(type="int", required=True),
+ description=dict(type="str"),
+ address=dict(type="str"),
+ interfaces=dict(type="list", elements="str", required=True),
+ ),
+ ),
+ state=dict(
+ type="str",
+ default="present",
+ choices=["present", "absent", "gathered"],
+ ),
+)
+
+
+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()