summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromnom62 <omnom62@outlook.com>2026-07-02 16:48:48 +1000
committerJohn Estabrook <jestabro@vyos.io>2026-08-21 10:18:35 -0500
commit2d0804cc31aa2b0aa24bbd553f80ea84c9830fa7 (patch)
tree95b106b1f46fae397f5921f972464ac16cecbf5c
parent2282a8361d8f0b0a3f22bfd403fdabf498316b80 (diff)
downloadrest.vyos-2d0804cc31aa2b0aa24bbd553f80ea84c9830fa7.tar.gz
rest.vyos-2d0804cc31aa2b0aa24bbd553f80ea84c9830fa7.zip
T8989: Add vyos_facts integration and unit tests with fixtures
-rw-r--r--docs/vyos.rest.vyos_facts_module.rst296
-rw-r--r--tests/integration/targets/vyos_facts/aliases1
-rw-r--r--tests/integration/targets/vyos_facts/defaults/main.yaml3
-rw-r--r--tests/integration/targets/vyos_facts/tasks/httpapi.yaml21
-rw-r--r--tests/integration/targets/vyos_facts/tasks/main.yaml5
-rw-r--r--tests/integration/targets/vyos_facts/tests/httpapi/all.yaml16
-rw-r--r--tests/integration/targets/vyos_facts/tests/httpapi/default.yaml13
-rw-r--r--tests/integration/targets/vyos_facts/tests/httpapi/subsets.yaml33
-rw-r--r--tests/integration/targets/vyos_facts/vars/main.yaml2
-rw-r--r--tests/unit/fixtures/facts_interfaces.json11
-rw-r--r--tests/unit/fixtures/facts_system.json28
-rw-r--r--tests/unit/modules/test_vyos_facts.py97
12 files changed, 526 insertions, 0 deletions
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
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>gather_network_resources</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>When supplied, this argument will restrict the facts collected to a given subset. Possible values include the resource module names.</div>
+ <div>This argument is not currently used.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>gather_subset</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ / <span style="color: purple">elements=string</span>
+ </div>
+ </td>
+ <td>
+ <b>Default:</b><br/><div style="color: blue">["default"]</div>
+ </td>
+ <td>
+ <div>When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument include <code>all</code>, <code>default</code>, <code>config</code>, <code>interfaces</code>, <code>hostname</code>, <code>users</code>, <code>bgp</code>, <code>ospf</code>, <code>ntp</code>, <code>snmp</code> and <code>logging</code>.</div>
+ <div>Specify a list of values to include a larger subset. Use the exclamation mark (<code>!</code>) before a value to exclude it. Values <code>all</code> and <code>default</code> cannot be combined with each other or with negation.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+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
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Fact</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_bgp</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>BGP configuration.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_config</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Full device configuration as structured data.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_hostname</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Device hostname.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_interfaces</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Interface configuration.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_logging</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>Logging configuration.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_ntp</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>NTP configuration.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_ospf</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>OSPFv2 configuration.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_ospfv3</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>OSPFv3 configuration.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_snmp</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>SNMP configuration.
+ </div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1" colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>vyos_users</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td></td>
+ <td>
+ <div>User accounts (without passwords).
+ </div>
+ <br/>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+
+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()