summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromnom62 <omnom62@outlook.com>2026-07-07 19:28:52 +1000
committeromnom62 <omnom62@outlook.com>2026-07-07 19:28:52 +1000
commitbe24a31fb8a4dcdd7c237eee28299cc4a20e4f9c (patch)
tree3ef69ebbfd8a15f09af454c590ac6a3260e0ecf3
parent7c57a114f8a6b63047b47817bd0ecc71ac3ad4cc (diff)
downloadrest.vyos-be24a31fb8a4dcdd7c237eee28299cc4a20e4f9c.tar.gz
rest.vyos-be24a31fb8a4dcdd7c237eee28299cc4a20e4f9c.zip
T8332: vyos_system SIT and UAT
-rw-r--r--docs/vyos.rest.vyos_system_module.rst266
-rw-r--r--tests/integration/targets/vyos_system/aliases1
-rw-r--r--tests/integration/targets/vyos_system/defaults/main.yaml3
-rw-r--r--tests/integration/targets/vyos_system/tasks/httpapi.yaml21
-rw-r--r--tests/integration/targets/vyos_system/tasks/main.yaml5
-rw-r--r--tests/integration/targets/vyos_system/tests/httpapi/_remove_config.yaml11
-rw-r--r--tests/integration/targets/vyos_system/tests/httpapi/absent.yaml37
-rw-r--r--tests/integration/targets/vyos_system/tests/httpapi/gathered.yaml14
-rw-r--r--tests/integration/targets/vyos_system/tests/httpapi/present.yaml47
-rw-r--r--tests/integration/targets/vyos_system/vars/main.yaml2
-rw-r--r--tests/unit/fixtures/system_running.json6
-rw-r--r--tests/unit/modules/test_vyos_system.py142
12 files changed, 555 insertions, 0 deletions
diff --git a/docs/vyos.rest.vyos_system_module.rst b/docs/vyos.rest.vyos_system_module.rst
new file mode 100644
index 0000000..9b97920
--- /dev/null
+++ b/docs/vyos.rest.vyos_system_module.rst
@@ -0,0 +1,266 @@
+.. _vyos.rest.vyos_system_module:
+
+
+*********************
+vyos.rest.vyos_system
+*********************
+
+**Manage system settings on VyOS devices using REST API**
+
+
+Version added: 1.0.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- Manages basic system settings on VyOS devices via the REST API.
+- Covers hostname, domain name, name servers, and domain search.
+- 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>domain_name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Device domain name.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>domain_search</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>List of domain search suffixes.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>host_name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Device hostname.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name_server</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>List of DNS name servers.</div>
+ <div style="font-size: small; color: darkgreen"><br/>aliases: name_servers</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>state</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>present</b>&nbsp;&larr;</div></li>
+ <li>absent</li>
+ </ul>
+ </td>
+ <td>
+ <div><code>present</code> applies the configuration.</div>
+ <div><code>absent</code> removes the configuration.</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``.
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ - name: Configure hostname and domain
+ vyos.rest.vyos_system:
+ host_name: router1
+ domain_name: example.com
+ state: present
+
+ - name: Configure name servers
+ vyos.rest.vyos_system:
+ name_server:
+ - 8.8.8.8
+ - 8.8.4.4
+ state: present
+
+ - name: Configure domain search
+ vyos.rest.vyos_system:
+ domain_search:
+ - sub1.example.com
+ - sub2.example.com
+ state: present
+
+ - name: Remove domain name and name servers
+ vyos.rest.vyos_system:
+ domain_name: example.com
+ name_server:
+ - 8.8.8.8
+ state: absent
+
+
+
+Return Values
+-------------
+Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="1">Key</th>
+ <th>Returned</th>
+ <th width="100%">Description</th>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>after</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>when changed</td>
+ <td>
+ <div>System configuration after this module ran.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>before</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>always</td>
+ <td>
+ <div>System configuration before this module ran.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>commands</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">list</span>
+ </div>
+ </td>
+ <td>always</td>
+ <td>
+ <div>List of API command tuples sent to the device.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>response</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>when changes are applied</td>
+ <td>
+ <div>Raw API response.</div>
+ <br/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>saved</b>
+ <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>when changes are applied</td>
+ <td>
+ <div>Whether the config was saved after changes.</div>
+ <br/>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- VyOS Community (@vyos)
diff --git a/tests/integration/targets/vyos_system/aliases b/tests/integration/targets/vyos_system/aliases
new file mode 100644
index 0000000..cc0afef
--- /dev/null
+++ b/tests/integration/targets/vyos_system/aliases
@@ -0,0 +1 @@
+network/vyos
diff --git a/tests/integration/targets/vyos_system/defaults/main.yaml b/tests/integration/targets/vyos_system/defaults/main.yaml
new file mode 100644
index 0000000..164afea
--- /dev/null
+++ b/tests/integration/targets/vyos_system/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: "[^_].*"
+test_items: []
diff --git a/tests/integration/targets/vyos_system/tasks/httpapi.yaml b/tests/integration/targets/vyos_system/tasks/httpapi.yaml
new file mode 100644
index 0000000..4147e6d
--- /dev/null
+++ b/tests/integration/targets/vyos_system/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_system/tasks/main.yaml b/tests/integration/targets/vyos_system/tasks/main.yaml
new file mode 100644
index 0000000..b1f6193
--- /dev/null
+++ b/tests/integration/targets/vyos_system/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_system/tests/httpapi/_remove_config.yaml b/tests/integration/targets/vyos_system/tests/httpapi/_remove_config.yaml
new file mode 100644
index 0000000..7c11046
--- /dev/null
+++ b/tests/integration/targets/vyos_system/tests/httpapi/_remove_config.yaml
@@ -0,0 +1,11 @@
+---
+- name: Remove test system configuration
+ vyos.rest.vyos_system:
+ domain_name: lab.example.com
+ name_server:
+ - 8.8.8.8
+ - 8.8.4.4
+ domain_search:
+ - sub1.example.com
+ state: absent
+ ignore_errors: true
diff --git a/tests/integration/targets/vyos_system/tests/httpapi/absent.yaml b/tests/integration/targets/vyos_system/tests/httpapi/absent.yaml
new file mode 100644
index 0000000..da0857e
--- /dev/null
+++ b/tests/integration/targets/vyos_system/tests/httpapi/absent.yaml
@@ -0,0 +1,37 @@
+---
+- debug:
+ msg: START vyos_system absent integration tests on connection={{ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+ - name: Setup system configuration
+ vyos.rest.vyos_system:
+ domain_name: lab.example.com
+ name_server:
+ - 8.8.8.8
+ state: present
+
+ - name: Remove domain name
+ register: result
+ vyos.rest.vyos_system:
+ domain_name: lab.example.com
+ state: absent
+
+ - assert:
+ that:
+ - result.changed == true
+
+ - name: Remove domain name (IDEMPOTENT)
+ register: result
+ vyos.rest.vyos_system:
+ domain_name: lab.example.com
+ state: absent
+
+ - assert:
+ that:
+ - result.changed == false
+ - result.commands == []
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_system/tests/httpapi/gathered.yaml b/tests/integration/targets/vyos_system/tests/httpapi/gathered.yaml
new file mode 100644
index 0000000..650ef1e
--- /dev/null
+++ b/tests/integration/targets/vyos_system/tests/httpapi/gathered.yaml
@@ -0,0 +1,14 @@
+---
+- debug:
+ msg: START vyos_system gathered integration tests on connection={{ ansible_connection }}
+
+- block:
+ - name: Gather system configuration
+ register: result
+ vyos.rest.vyos_system:
+ state: present
+
+ - assert:
+ that:
+ - result.before.host_name is defined
+ - result.before.host_name | length > 0
diff --git a/tests/integration/targets/vyos_system/tests/httpapi/present.yaml b/tests/integration/targets/vyos_system/tests/httpapi/present.yaml
new file mode 100644
index 0000000..aced4f4
--- /dev/null
+++ b/tests/integration/targets/vyos_system/tests/httpapi/present.yaml
@@ -0,0 +1,47 @@
+---
+- debug:
+ msg: START vyos_system present integration tests on connection={{ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+ - name: Configure domain and name servers
+ register: result
+ vyos.rest.vyos_system:
+ domain_name: lab.example.com
+ name_server:
+ - 8.8.8.8
+ - 8.8.4.4
+ state: present
+
+ - assert:
+ that:
+ - result.changed == true
+
+ - name: Configure domain and name servers (IDEMPOTENT)
+ register: result
+ vyos.rest.vyos_system:
+ domain_name: lab.example.com
+ name_server:
+ - 8.8.8.8
+ - 8.8.4.4
+ state: present
+
+ - assert:
+ that:
+ - result.changed == false
+ - result.commands == []
+
+ - name: Add domain search
+ register: result
+ vyos.rest.vyos_system:
+ domain_search:
+ - sub1.example.com
+ state: present
+
+ - assert:
+ that:
+ - result.changed == true
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_system/vars/main.yaml b/tests/integration/targets/vyos_system/vars/main.yaml
new file mode 100644
index 0000000..4303881
--- /dev/null
+++ b/tests/integration/targets/vyos_system/vars/main.yaml
@@ -0,0 +1,2 @@
+---
+# only common vars here
diff --git a/tests/unit/fixtures/system_running.json b/tests/unit/fixtures/system_running.json
new file mode 100644
index 0000000..b8a4969
--- /dev/null
+++ b/tests/unit/fixtures/system_running.json
@@ -0,0 +1,6 @@
+{
+ "host-name": "vyos150",
+ "domain-name": "lab.example.com",
+ "name-server": ["8.8.8.8", "8.8.4.4"],
+ "domain-search": "sub1.example.com"
+}
diff --git a/tests/unit/modules/test_vyos_system.py b/tests/unit/modules/test_vyos_system.py
new file mode 100644
index 0000000..1bd9cff
--- /dev/null
+++ b/tests/unit/modules/test_vyos_system.py
@@ -0,0 +1,142 @@
+# -*- 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_system import (
+ build_commands,
+ get_running_config,
+)
+
+
+_BASE = ["system"]
+
+
+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 TestVyOSSystemGetRunning(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_vyos = MagicMock()
+ self.fixture = load_fixture("system_running.json")
+ self.mock_vyos.get_config = MagicMock(return_value=self.fixture)
+
+ def test_parses_hostname(self):
+ result = get_running_config(self.mock_vyos)
+ self.assertEqual(result["host_name"], "vyos150")
+
+ def test_parses_domain_name(self):
+ result = get_running_config(self.mock_vyos)
+ self.assertEqual(result["domain_name"], "lab.example.com")
+
+ def test_parses_name_servers_list(self):
+ result = get_running_config(self.mock_vyos)
+ self.assertIn("8.8.8.8", result["name_server"])
+ self.assertIn("8.8.4.4", result["name_server"])
+
+ def test_parses_domain_search_string(self):
+ result = get_running_config(self.mock_vyos)
+ self.assertIn("sub1.example.com", result["domain_search"])
+
+ def test_empty_config(self):
+ self.mock_vyos.get_config = MagicMock(return_value={})
+ result = get_running_config(self.mock_vyos)
+ self.assertEqual(result, {})
+
+
+class TestVyOSSystemBuildCommands(unittest.TestCase):
+
+ def _have(self):
+ return {
+ "host_name": "vyos150",
+ "domain_name": "lab.example.com",
+ "name_server": ["8.8.8.8", "8.8.4.4"],
+ "domain_search": ["sub1.example.com"],
+ }
+
+ def test_present_idempotent(self):
+ config = {
+ "host_name": "vyos150",
+ "domain_name": "lab.example.com",
+ "name_server": ["8.8.8.8", "8.8.4.4"],
+ "domain_search": ["sub1.example.com"],
+ }
+ cmds = build_commands(config, self._have(), "present")
+ self.assertEqual(cmds, [])
+
+ def test_present_new_hostname(self):
+ config = {
+ "host_name": "router1",
+ "domain_name": None,
+ "name_server": None,
+ "domain_search": None,
+ }
+ cmds = build_commands(config, self._have(), "present")
+ self.assertIn(("set", _BASE + ["host-name", "router1"]), cmds)
+
+ def test_present_new_nameserver(self):
+ config = {
+ "host_name": None,
+ "domain_name": None,
+ "name_server": ["1.1.1.1"],
+ "domain_search": None,
+ }
+ cmds = build_commands(config, self._have(), "present")
+ self.assertIn(("set", _BASE + ["name-server", "1.1.1.1"]), cmds)
+
+ def test_present_existing_nameserver_skipped(self):
+ config = {
+ "host_name": None,
+ "domain_name": None,
+ "name_server": ["8.8.8.8"],
+ "domain_search": None,
+ }
+ cmds = build_commands(config, self._have(), "present")
+ self.assertEqual(cmds, [])
+
+ def test_absent_domain_name(self):
+ config = {
+ "host_name": None,
+ "domain_name": "lab.example.com",
+ "name_server": None,
+ "domain_search": None,
+ }
+ cmds = build_commands(config, self._have(), "absent")
+ self.assertIn(("delete", _BASE + ["domain-name"]), cmds)
+
+ def test_absent_nameserver(self):
+ config = {
+ "host_name": None,
+ "domain_name": None,
+ "name_server": ["8.8.8.8"],
+ "domain_search": None,
+ }
+ cmds = build_commands(config, self._have(), "absent")
+ self.assertIn(("delete", _BASE + ["name-server", "8.8.8.8"]), cmds)
+
+ def test_absent_nonexistent_skipped(self):
+ # domain_name not in have - nothing to delete
+ config = {
+ "host_name": None,
+ "domain_name": "other.com",
+ "name_server": None,
+ "domain_search": None,
+ }
+ have = {"host_name": "vyos150"} # no domain_name in have
+ cmds = build_commands(config, have, "absent")
+ self.assertEqual(cmds, [])
+
+
+if __name__ == "__main__":
+ unittest.main()