From 2d0804cc31aa2b0aa24bbd553f80ea84c9830fa7 Mon Sep 17 00:00:00 2001 From: omnom62 Date: Thu, 2 Jul 2026 16:48:48 +1000 Subject: T8989: Add vyos_facts integration and unit tests with fixtures --- docs/vyos.rest.vyos_facts_module.rst | 296 +++++++++++++++++++++ tests/integration/targets/vyos_facts/aliases | 1 + .../targets/vyos_facts/defaults/main.yaml | 3 + .../targets/vyos_facts/tasks/httpapi.yaml | 21 ++ .../integration/targets/vyos_facts/tasks/main.yaml | 5 + .../targets/vyos_facts/tests/httpapi/all.yaml | 16 ++ .../targets/vyos_facts/tests/httpapi/default.yaml | 13 + .../targets/vyos_facts/tests/httpapi/subsets.yaml | 33 +++ .../integration/targets/vyos_facts/vars/main.yaml | 2 + tests/unit/fixtures/facts_interfaces.json | 11 + tests/unit/fixtures/facts_system.json | 28 ++ tests/unit/modules/test_vyos_facts.py | 97 +++++++ 12 files changed, 526 insertions(+) create mode 100644 docs/vyos.rest.vyos_facts_module.rst create mode 100644 tests/integration/targets/vyos_facts/aliases create mode 100644 tests/integration/targets/vyos_facts/defaults/main.yaml create mode 100644 tests/integration/targets/vyos_facts/tasks/httpapi.yaml create mode 100644 tests/integration/targets/vyos_facts/tasks/main.yaml create mode 100644 tests/integration/targets/vyos_facts/tests/httpapi/all.yaml create mode 100644 tests/integration/targets/vyos_facts/tests/httpapi/default.yaml create mode 100644 tests/integration/targets/vyos_facts/tests/httpapi/subsets.yaml create mode 100644 tests/integration/targets/vyos_facts/vars/main.yaml create mode 100644 tests/unit/fixtures/facts_interfaces.json create mode 100644 tests/unit/fixtures/facts_system.json create mode 100644 tests/unit/modules/test_vyos_facts.py diff --git a/docs/vyos.rest.vyos_facts_module.rst b/docs/vyos.rest.vyos_facts_module.rst new file mode 100644 index 0000000..d5c6a89 --- /dev/null +++ b/docs/vyos.rest.vyos_facts_module.rst @@ -0,0 +1,296 @@ +.. _vyos.rest.vyos_facts_module: + + +******************** +vyos.rest.vyos_facts +******************** + +**Get facts about VyOS devices using REST API** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Collects facts from VyOS devices via the REST API. +- Returns structured facts under the ``ansible_facts`` key. +- Uses REST API (``connection=httpapi``) instead of CLI. + + + + +Parameters +---------- + +.. raw:: html + + + + + + + + + + + + + + + + + +
ParameterChoices/DefaultsComments
+
+ gather_network_resources + +
+ list + / elements=string +
+
+ +
When supplied, this argument will restrict the facts collected to a given subset. Possible values include the resource module names.
+
This argument is not currently used.
+
+
+ gather_subset + +
+ list + / elements=string +
+
+ Default:
["default"]
+
+
When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include all, default, config, interfaces, hostname, users, bgp, ospf, ntp, snmp and logging.
+
Specify a list of values to include a larger subset. Use the exclamation mark (!) before a value to exclude it. Values all and default cannot be combined with each other or with negation.
+
+
+ + +Notes +----- + +.. note:: + - Requires ``ansible_connection=httpapi`` with the VyOS httpapi plugin. + - ``ansible_network_os`` must be set to ``vyos.rest.vyos``. + - Only configuration facts are available via the REST API. Operational state (interface counters, BGP neighbors) is not supported. + + + +Examples +-------- + +.. code-block:: yaml + + - name: Gather all facts + vyos.rest.vyos_facts: + gather_subset: all + + - name: Gather default facts + vyos.rest.vyos_facts: + + - name: Gather interface and hostname facts only + vyos.rest.vyos_facts: + gather_subset: + - interfaces + - hostname + + - name: Gather all except config + vyos.rest.vyos_facts: + gather_subset: + - all + - '!config' + + +Returned Facts +-------------- +Facts returned by this module are added/updated in the ``hostvars`` host facts and can be referenced by name just like any other host fact. They do not need to be registered in order to use them. + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FactReturnedDescription
+
+ vyos_bgp + +
+ dictionary +
+
+
BGP configuration. +
+
+
+
+ vyos_config + +
+ dictionary +
+
+
Full device configuration as structured data. +
+
+
+
+ vyos_hostname + +
+ string +
+
+
Device hostname. +
+
+
+
+ vyos_interfaces + +
+ dictionary +
+
+
Interface configuration. +
+
+
+
+ vyos_logging + +
+ dictionary +
+
+
Logging configuration. +
+
+
+
+ vyos_ntp + +
+ dictionary +
+
+
NTP configuration. +
+
+
+
+ vyos_ospf + +
+ dictionary +
+
+
OSPFv2 configuration. +
+
+
+
+ vyos_ospfv3 + +
+ dictionary +
+
+
OSPFv3 configuration. +
+
+
+
+ vyos_snmp + +
+ dictionary +
+
+
SNMP configuration. +
+
+
+
+ vyos_users + +
+ list +
+
+
User accounts (without passwords). +
+
+
+

+ + + +Status +------ + + +Authors +~~~~~~~ + +- VyOS Community (@vyos) diff --git a/tests/integration/targets/vyos_facts/aliases b/tests/integration/targets/vyos_facts/aliases new file mode 100644 index 0000000..cc0afef --- /dev/null +++ b/tests/integration/targets/vyos_facts/aliases @@ -0,0 +1 @@ +network/vyos diff --git a/tests/integration/targets/vyos_facts/defaults/main.yaml b/tests/integration/targets/vyos_facts/defaults/main.yaml new file mode 100644 index 0000000..164afea --- /dev/null +++ b/tests/integration/targets/vyos_facts/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "[^_].*" +test_items: [] diff --git a/tests/integration/targets/vyos_facts/tasks/httpapi.yaml b/tests/integration/targets/vyos_facts/tasks/httpapi.yaml new file mode 100644 index 0000000..4147e6d --- /dev/null +++ b/tests/integration/targets/vyos_facts/tasks/httpapi.yaml @@ -0,0 +1,21 @@ +--- +- name: Collect all httpapi test cases + ansible.builtin.find: + paths: "{{ role_path }}/tests/httpapi" + patterns: "{{ testcase }}.yaml" + use_regex: true + register: test_cases + delegate_to: localhost + +- name: Set test_items + ansible.builtin.set_fact: + test_items: "{{ test_cases.files | map(attribute='path') | list }}" + +- name: Run test case (connection=httpapi) + ansible.builtin.include_tasks: "{{ test_case_to_run }}" + vars: + ansible_connection: ansible.netcommon.httpapi + ansible_network_os: vyos.rest.vyos + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/tests/integration/targets/vyos_facts/tasks/main.yaml b/tests/integration/targets/vyos_facts/tasks/main.yaml new file mode 100644 index 0000000..b1f6193 --- /dev/null +++ b/tests/integration/targets/vyos_facts/tasks/main.yaml @@ -0,0 +1,5 @@ +--- +- name: Run httpapi tests + ansible.builtin.include_tasks: httpapi.yaml + tags: + - httpapi diff --git a/tests/integration/targets/vyos_facts/tests/httpapi/all.yaml b/tests/integration/targets/vyos_facts/tests/httpapi/all.yaml new file mode 100644 index 0000000..419634f --- /dev/null +++ b/tests/integration/targets/vyos_facts/tests/httpapi/all.yaml @@ -0,0 +1,16 @@ +--- +- debug: + msg: START vyos_facts all integration tests on connection={{ ansible_connection }} + +- block: + - name: Gather all facts + vyos.rest.vyos_facts: + gather_subset: all + + - assert: + that: + - ansible_facts.vyos_hostname is defined + - ansible_facts.vyos_interfaces is defined + - ansible_facts.vyos_users is defined + - ansible_facts.vyos_config is defined + - ansible_facts.vyos_logging is defined diff --git a/tests/integration/targets/vyos_facts/tests/httpapi/default.yaml b/tests/integration/targets/vyos_facts/tests/httpapi/default.yaml new file mode 100644 index 0000000..ff1caee --- /dev/null +++ b/tests/integration/targets/vyos_facts/tests/httpapi/default.yaml @@ -0,0 +1,13 @@ +--- +- debug: + msg: START vyos_facts default integration tests on connection={{ ansible_connection }} + +- block: + - name: Gather default facts + vyos.rest.vyos_facts: + + - assert: + that: + - ansible_facts.vyos_hostname is defined + - ansible_facts.vyos_interfaces is defined + - ansible_facts.vyos_hostname | length > 0 diff --git a/tests/integration/targets/vyos_facts/tests/httpapi/subsets.yaml b/tests/integration/targets/vyos_facts/tests/httpapi/subsets.yaml new file mode 100644 index 0000000..eb4ca4f --- /dev/null +++ b/tests/integration/targets/vyos_facts/tests/httpapi/subsets.yaml @@ -0,0 +1,33 @@ +--- +- debug: + msg: START vyos_facts subsets integration tests on connection={{ ansible_connection }} + +- block: + - name: Gather hostname only + vyos.rest.vyos_facts: + gather_subset: + - hostname + + - assert: + that: + - ansible_facts.vyos_hostname is defined + + - name: Gather users + vyos.rest.vyos_facts: + gather_subset: + - users + + - assert: + that: + - ansible_facts.vyos_users is defined + - ansible_facts.vyos_users | selectattr('name', 'eq', 'vyos') | list | length == 1 + + - name: Gather config + vyos.rest.vyos_facts: + gather_subset: + - config + + - assert: + that: + - ansible_facts.vyos_config is defined + - ansible_facts.vyos_config.system is defined diff --git a/tests/integration/targets/vyos_facts/vars/main.yaml b/tests/integration/targets/vyos_facts/vars/main.yaml new file mode 100644 index 0000000..4303881 --- /dev/null +++ b/tests/integration/targets/vyos_facts/vars/main.yaml @@ -0,0 +1,2 @@ +--- +# only common vars here diff --git a/tests/unit/fixtures/facts_interfaces.json b/tests/unit/fixtures/facts_interfaces.json new file mode 100644 index 0000000..b58e17f --- /dev/null +++ b/tests/unit/fixtures/facts_interfaces.json @@ -0,0 +1,11 @@ +{ + "ethernet": { + "eth0": { "address": "dhcp", "hw-id": "52:54:00:65:5a:24" }, + "eth1": { + "description": "uplink", + "hw-id": "52:54:00:96:42:93", + "mtu": "1500" + }, + "eth2": { "hw-id": "52:54:00:75:31:fa" } + } +} diff --git a/tests/unit/fixtures/facts_system.json b/tests/unit/fixtures/facts_system.json new file mode 100644 index 0000000..68091ed --- /dev/null +++ b/tests/unit/fixtures/facts_system.json @@ -0,0 +1,28 @@ +{ + "host-name": "vyos-test", + "login": { + "user": { + "vyos": { + "authentication": { + "encrypted-password": "$6$abc123" + } + }, + "alice": { + "full-name": "Alice Smith", + "authentication": { + "encrypted-password": "$6$def456", + "public-keys": { + "alice-key": { + "key": "AAAAB3...", + "type": "ssh-rsa" + } + } + } + } + } + }, + "syslog": { + "local": {}, + "console": {} + } +} diff --git a/tests/unit/modules/test_vyos_facts.py b/tests/unit/modules/test_vyos_facts.py new file mode 100644 index 0000000..3fcf82e --- /dev/null +++ b/tests/unit/modules/test_vyos_facts.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import json +import os +import unittest + +from unittest.mock import MagicMock + +from ansible_collections.vyos.rest.plugins.modules.vyos_facts import ( + gather_bgp, + gather_hostname, + gather_interfaces, + gather_logging, + gather_users, +) + + +def load_fixture(filename): + fixtures_dir = os.path.join(os.path.dirname(__file__), "..", "fixtures") + with open(os.path.join(fixtures_dir, filename)) as f: + return json.load(f) + + +class TestVyOSFactsGather(unittest.TestCase): + + def setUp(self): + self.mock_vyos = MagicMock() + self.system_fixture = load_fixture("facts_system.json") + self.interfaces_fixture = load_fixture("facts_interfaces.json") + + def test_gather_hostname(self): + self.mock_vyos.get_config = MagicMock(return_value=self.system_fixture) + result = gather_hostname(self.mock_vyos) + self.assertEqual(result, "vyos-test") + + def test_gather_hostname_empty(self): + self.mock_vyos.get_config = MagicMock(return_value={}) + result = gather_hostname(self.mock_vyos) + self.assertEqual(result, "") + + def test_gather_interfaces(self): + self.mock_vyos.get_config = MagicMock(return_value=self.interfaces_fixture) + result = gather_interfaces(self.mock_vyos) + self.assertIn("ethernet", result) + self.assertIn("eth0", result["ethernet"]) + self.assertIn("eth1", result["ethernet"]) + self.assertEqual(result["ethernet"]["eth1"]["description"], "uplink") + + def test_gather_users(self): + self.mock_vyos.get_config = MagicMock( + return_value=self.system_fixture["login"], + ) + result = gather_users(self.mock_vyos) + names = [u["name"] for u in result] + self.assertIn("vyos", names) + self.assertIn("alice", names) + alice = next(u for u in result if u["name"] == "alice") + self.assertEqual(alice["full_name"], "Alice Smith") + self.assertIn("alice-key", alice["public_keys"]) + + def test_gather_users_empty(self): + self.mock_vyos.get_config = MagicMock(return_value={}) + result = gather_users(self.mock_vyos) + self.assertEqual(result, []) + + def test_gather_users_none(self): + self.mock_vyos.get_config = MagicMock(return_value=None) + result = gather_users(self.mock_vyos) + self.assertEqual(result, []) + + def test_gather_bgp(self): + data = {"system-as": "65000", "parameters": {"router-id": "192.0.1.1"}} + self.mock_vyos.get_config = MagicMock(return_value=data) + result = gather_bgp(self.mock_vyos) + self.assertEqual(result["system-as"], "65000") + self.assertEqual(result["parameters"]["router-id"], "192.0.1.1") + + def test_gather_bgp_empty(self): + self.mock_vyos.get_config = MagicMock(return_value={}) + result = gather_bgp(self.mock_vyos) + self.assertEqual(result, {}) + + def test_gather_logging(self): + self.mock_vyos.get_config = MagicMock( + return_value=self.system_fixture["syslog"], + ) + result = gather_logging(self.mock_vyos) + self.assertIn("local", result) + self.assertIn("console", result) + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3