summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromnom62 <75066712+omnom62@users.noreply.github.com>2026-08-19 22:21:24 +1000
committerGitHub <noreply@github.com>2026-08-19 15:21:24 +0300
commita6adced609472d88cb525f2ef1a4054b52692f76 (patch)
treede206d2bbc56c970c2db3162cdee3d78b341c811
parentb6b58bc747c4ea59371253c5541cde92b922c173 (diff)
downloadvyos.vyos-a6adced609472d88cb525f2ef1a4054b52692f76.tar.gz
vyos.vyos-a6adced609472d88cb525f2ef1a4054b52692f76.zip
T6820: HA/VRRP module (#438)
* Init VRRP * RM Templates * rm_templates for global_parameters * virtual servers rm_templates * update * sync-group rm_tempalte * VRRP GP GARP template rework * RM template and new facts logic * vrrp argspec * Suppressing backup versions of files * Facts for vrrp * VRRP facts updates * Facts almost done * Argspec vrrp * Argspec completed * Argspec and facts for VRRP finished * Documentation for VRRP module * Sanity tests fixes and documentation * VRRP config started * Doc fixes * Doc fixes * Doc fixes * Configuration for VRRP * wip * Updates to facts, config, templates in VRRP * VRRP groups templates, facts and config * VRRP works - templates for groups and virtual_servers * Mid-result * VRRP VS prototype * Groups processor update * Reworked vrrp.group and vrrp.group.garp processors * VRRP grp recursive * VRRP groups and vserver contd * Update to VRRP want processing * Updates to VRRP funcs * VRRP VS RS config * Sanity test fixes midway * VRRP Real server fixes * VRRP sync_group * Latest fixes to etire VRRP config parsing * Midway compare function * update to vrrp group processing * Mid results for merge parsers / compare * Working merge for vrrp and virtserv * Final merge / compare * WIP replaced / Delete * Updates to state processing * Mid-result for virtual_servers * Mid result commit * Testing high-availability vrrp processing * Retesting vrrp stanza * Mid results * Real server parsing * SNMP vrrp fixed * Sanity fixes * Purged, rendered, gathered, parsed action states for VRRP * VRRP integration and unit tests; code fixes midway results * Sanity check fixes * Merged integration test case for VRRP * VRRP rendered integration test case * Sanity tests fixes * VRRP unit tests - merged and purged * Mid-way replaced, deleted and overriden work. Description string fix * Updates to integration and unit tests for VRRP * Mid-result for deleted state VRRP * Mid-results for VRRP deleted * Deleted vrrp midway results * Deleted state function for VRRP * Deleted state VRRP, sanity checks fix * Deleted integration tests * Deleted_all unit and integration tests * Deleted_all unit and integration tests * Sanity check fixes * Mid-results overridden VRRP * WIP vrrp overridden sit and uat * VRRP overridden sanity test * UAT overriden vrrp * UAT deleted for vrrp * VRRP replaced UAT and SIT * VRRP RTT SIT * VRRP SIT suite ready * Finals for VRRP module - clean-up and fixed UAT * VRRP module doc examples updated * VRRP sanity tests fixes * VRRP disbale default fix * Misc lint fixes * Misc Lint fixes * remove old files * Rename vyos_vrrp -> vyos_ha * Rename vyos_vrrp to vyos_ha * doc fixes * Changes to HA module * HA module fixes * Sanity test fixes * changelog * ignore for HA/VRRP and 2.22 * AI-generated comments and fixes * Fixes after review * Misc fixes * Misc fixes * Misc fixes * Misc fixes * Sanity fixes * Sanity fixes * Misc changes * T6820: code refactor to address valid AI comments * T6820: Optimising facts, config and rm_templates * T6820: UAT fixes * T6820: docstring for utils/combine() * T6820 Misc fixes to changelog --------- Co-authored-by: Gaige B Paulsen <gaige@cluetrust.com>
-rw-r--r--.gitignore1
-rw-r--r--README.md1
-rw-r--r--changelogs/fragments/t6820_vrrp.yml3
-rw-r--r--docs/vyos.vyos.vyos_ha_module.rst2523
-rw-r--r--meta/runtime.yml2
l---------plugins/action/ha.py1
-rw-r--r--plugins/module_utils/network/vyos/argspec/ha/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/argspec/ha/ha.py196
-rw-r--r--plugins/module_utils/network/vyos/config/ha/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/config/ha/ha.py705
-rw-r--r--plugins/module_utils/network/vyos/facts/facts.py4
-rw-r--r--plugins/module_utils/network/vyos/facts/ha/__init__.py0
-rw-r--r--plugins/module_utils/network/vyos/facts/ha/ha.py174
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/ha.py1011
-rw-r--r--plugins/module_utils/network/vyos/utils/utils.py54
-rw-r--r--plugins/modules/vyos_ha.py1327
-rw-r--r--tests/integration/targets/vyos_ha/defaults/main.yaml3
-rw-r--r--tests/integration/targets/vyos_ha/tasks/cli.yaml20
-rw-r--r--tests/integration/targets/vyos_ha/tasks/main.yaml5
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/_add_dummy_scripts.yaml6
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/_parsed.cfg69
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/_populate_config.yaml8
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/_remove_config.yaml15
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/deleted_all.yaml44
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/deleted_bulk.yaml50
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/deleted_vrrp_groups.yaml45
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/empty_config.yaml58
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/gathered.yaml25
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/merged.yaml42
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/overridden.yaml35
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/parsed.yaml15
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/purged.yaml44
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/rendered.yaml15
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/replaced.yaml37
-rw-r--r--tests/integration/targets/vyos_ha/tests/cli/rtt.yaml39
-rw-r--r--tests/integration/targets/vyos_ha/vars/main.yaml787
-rw-r--r--tests/sanity/ignore-2.15.txt1
-rw-r--r--tests/sanity/ignore-2.16.txt1
-rw-r--r--tests/sanity/ignore-2.17.txt1
-rw-r--r--tests/sanity/ignore-2.18.txt1
-rw-r--r--tests/sanity/ignore-2.19.txt1
-rw-r--r--tests/sanity/ignore-2.20.txt1
-rw-r--r--tests/sanity/ignore-2.21.txt1
-rw-r--r--tests/sanity/ignore-2.22.txt1
-rw-r--r--tests/unit/modules/network/vyos/fixtures/vyos_ha_config.cfg64
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_ha.py1405
46 files changed, 8833 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore
index 1dbe8b0e..2f5df4fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,7 @@ venv.bak/
*.code-workspace
.vscode/
.DS_Store
+*.bak
changelogs/.plugin-cache.yaml
# inventory for testing
diff --git a/README.md b/README.md
index 0a4b8f48..3e54f27c 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ Name | Description
[vyos.vyos.vyos_firewall_global](https://github.com/vyos/vyos.vyos/blob/main/docs/vyos.vyos.vyos_firewall_global_module.rst)|Firewall global resource module
[vyos.vyos.vyos_firewall_interfaces](https://github.com/vyos/vyos.vyos/blob/main/docs/vyos.vyos.vyos_firewall_interfaces_module.rst)|Firewall interfaces resource module
[vyos.vyos.vyos_firewall_rules](https://github.com/vyos/vyos.vyos/blob/main/docs/vyos.vyos.vyos_firewall_rules_module.rst)|Firewall rules resource module
+[vyos.vyos.vyos_ha](https://github.com/vyos/vyos.vyos/blob/main/docs/vyos.vyos.vyos_ha_module.rst)|Manage VRRP and load balancer configuration on VyOS
[vyos.vyos.vyos_hostname](https://github.com/vyos/vyos.vyos/blob/main/docs/vyos.vyos.vyos_hostname_module.rst)|Manages hostname resource module
[vyos.vyos.vyos_interfaces](https://github.com/vyos/vyos.vyos/blob/main/docs/vyos.vyos.vyos_interfaces_module.rst)|Manages interface attributes of VyOS network devices.
[vyos.vyos.vyos_l3_interfaces](https://github.com/vyos/vyos.vyos/blob/main/docs/vyos.vyos.vyos_l3_interfaces_module.rst)|Layer 3 interfaces resource module.
diff --git a/changelogs/fragments/t6820_vrrp.yml b/changelogs/fragments/t6820_vrrp.yml
new file mode 100644
index 00000000..94145121
--- /dev/null
+++ b/changelogs/fragments/t6820_vrrp.yml
@@ -0,0 +1,3 @@
+---
+minor_changes:
+ - vyos_ha - Add VRRP (High Availability) support.
diff --git a/docs/vyos.vyos.vyos_ha_module.rst b/docs/vyos.vyos.vyos_ha_module.rst
new file mode 100644
index 00000000..5e03661c
--- /dev/null
+++ b/docs/vyos.vyos.vyos_ha_module.rst
@@ -0,0 +1,2523 @@
+.. _vyos.vyos.vyos_ha_module:
+
+
+*****************
+vyos.vyos.vyos_ha
+*****************
+
+**Manage VRRP and load balancer configuration on VyOS**
+
+
+Version added: 1.0.0
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+- This module configures VRRP groups, global VRRP parameters, VRRP sync groups, and LVS-style virtual servers on VyOS 1.4+.
+- Supports creation, modification, deletion, replacement, rendering, and parsing of VRRP-related configuration.
+
+
+
+
+Parameters
+----------
+
+.. raw:: html
+
+ <table border=0 cellpadding=0 class="documentation-table">
+ <tr>
+ <th colspan="5">Parameter</th>
+ <th>Choices/<font color="blue">Defaults</font></th>
+ <th width="100%">Comments</th>
+ </tr>
+ <tr>
+ <td colspan="5">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>config</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Full VRRP and virtual server configuration.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>disable</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Disable all VRRP and L4-LB configuration under this module.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>virtual_servers</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=dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>List of load balancer virtual server (LVS) definitions.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>address</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>Virtual IP address for the server.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>algorithm</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>Load balancing algorithm used for dispatching connections.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>delay_loop</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Delay loop interval in seconds.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>forward_method</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>direct</li>
+ <li>nat</li>
+ </ul>
+ </td>
+ <td>
+ <div>Forwarding method used by LVS.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>fwmark</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Firewall mark for LVS traffic classification.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Unique identifier for the virtual server.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>persistence_timeout</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Client persistence timeout in seconds.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>port</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>TCP/UDP port provided by the virtual service.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>protocol</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>tcp</li>
+ <li>udp</li>
+ </ul>
+ </td>
+ <td>
+ <div>Transport protocol for the virtual server.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>real_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=dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Backend real servers behind the virtual service.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>address</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Real server IP address.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>connection_timeout</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Backend server connection timeout.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>health_check_script</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>Path to health check script used for backend validation.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>port</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Backend server port.</div>
+ </td>
+ </tr>
+
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td colspan="4">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>vrrp</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP configuration including groups, global parameters, SNMP settings, and sync-groups.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>global_parameters</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Global VRRP tuning parameters.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>garp</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Gratuitous ARP related configuration.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>interval</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>GARP interval in seconds.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_delay</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Delay before sending GARP as master.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_refresh</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Refresh interval for master GARP announcements.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_refresh_repeat</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Number of times to repeat refresh announcements.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_repeat</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Number of GARP repeats when transitioning to master.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>startup_delay</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Delay before VRRP starts after boot.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>version</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>VRRP protocol version.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>groups</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=dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP instance configuration groups.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>address</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>Virtual router IP addresses.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>advertise_interval</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP advertisement interval.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>authentication</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP group authentication options.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>password</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>Authentication password.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>type</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>Authentication type.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>description</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>Text description for the VRRP group.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>disable</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Disable this VRRP group.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>excluded_address</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>IP address excluded from source checks.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>garp</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>GARP-specific settings for this group.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>interval</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>GARP interval.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_delay</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>GARP master delay.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_refresh</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>GARP master refresh interval.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_refresh_repeat</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Repeated refresh sends.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master_repeat</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>GARP repeat count.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>health_check</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP group health check options.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>failure_count</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Allowed number of failed checks.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>interval</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Health check interval.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ping</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>Host to ping for checks.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>script</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>Script to execute for health checking.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>hello_source_address</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>Source address for VRRP hello packets.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>interface</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>Interface used by the VRRP group.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP group name.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>no_preempt</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Disable preemption.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>peer_address</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>Peer VRRP router address.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>preempt_delay</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Delay before taking master role.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>priority</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP priority (higher = preferred master).</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>rfc3768_compatibility</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Enable or disable RFC3768 compatibility mode.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>track</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Track interface and VRRP behaviour.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>exclude_vrrp_interface</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">boolean</span>
+ </div>
+ </td>
+ <td>
+ <ul style="margin: 0; padding: 0"><b>Choices:</b>
+ <li>no</li>
+ <li>yes</li>
+ </ul>
+ </td>
+ <td>
+ <div>Exclude VRRP interface from tracking.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>interface</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>Interface to track.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>transition_script</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Scripts executed during VRRP state transitions.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>backup</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>Path to backup script.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>fault</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>Path to fault script.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master</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>Path to master script.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>stop</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>Path to stop script.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>vrid</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP Virtual Router ID.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>snmp</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>enabled</li>
+ <li>disabled</li>
+ </ul>
+ </td>
+ <td>
+ <div>Enable SNMP support for VRRP.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="3">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>sync_groups</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=dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>VRRP sync-groups for coordinated failover.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>health_check</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Health check options for sync group.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>failure_count</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Allowed number of failures.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>interval</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">integer</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Health check interval.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>ping</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>Host to ping.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>script</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>Script to run for health checking.</div>
+ </td>
+ </tr>
+
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>member</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 VRRP groups participating in this sync group.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>name</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">string</span>
+ / <span style="color: red">required</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Sync-group name.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="2">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>transition_script</b>
+ <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
+ <div style="font-size: small">
+ <span style="color: purple">dictionary</span>
+ </div>
+ </td>
+ <td>
+ </td>
+ <td>
+ <div>Transition scripts for sync group events.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>backup</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>Backup state script.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>fault</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>Fault state script.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>master</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>Master state script.</div>
+ </td>
+ </tr>
+ <tr>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td class="elbow-placeholder"></td>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>stop</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>Stop state script.</div>
+ </td>
+ </tr>
+
+
+
+
+ <tr>
+ <td colspan="5">
+ <div class="ansibleOptionAnchor" id="parameter-"></div>
+ <b>running_config</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>Used only when <code>state=parsed</code>. Must contain the output of <code>show configuration commands | grep high-availability</code>.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="5">
+ <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>deleted</li>
+ <li><div style="color: blue"><b>merged</b>&nbsp;&larr;</div></li>
+ <li>purged</li>
+ <li>replaced</li>
+ <li>gathered</li>
+ <li>rendered</li>
+ <li>parsed</li>
+ <li>overridden</li>
+ </ul>
+ </td>
+ <td>
+ <div>Desired end state of the VRRP configuration.</div>
+ </td>
+ </tr>
+ </table>
+ <br/>
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml
+
+ # Using merged
+ # Before state
+
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # vyos@vyos:~$
+
+ - name: Merge provided configuration with device configuration
+ vyos.vyos.vyos_ha:
+ config:
+ disable: true
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.5
+ algorithm: round-robin
+ real_server:
+ - address: 10.10.50.2
+ port: 8443
+ - name: s2
+ address: 10.10.10.2
+ persistence_timeout: 30
+ port: 81
+ protocol: tcp
+ - name: s3
+ address: 10.10.10.3
+ port: 88
+ protocol: udp
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 30
+ garp:
+ master_repeat: 6
+ groups:
+ - name: "g1"
+ peer_address: 192.168.1.3
+ priority: 100
+ disable: false
+ no_preempt: false
+ vrid: 20
+ sync_groups:
+ - name: "sg1"
+ health_check:
+ failure_count: 5
+ state: merged
+
+ # After State
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # set high-availability disable
+ # set high-availability virtual-server s1 address '10.10.10.5'
+ # set high-availability virtual-server s1 algorithm 'round-robin'
+ # set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+ # set high-availability virtual-server s2 address '10.10.10.2'
+ # set high-availability virtual-server s2 persistence-timeout '30'
+ # set high-availability virtual-server s2 port '81'
+ # set high-availability virtual-server s2 protocol 'tcp'
+ # set high-availability virtual-server s3 address '10.10.10.3'
+ # set high-availability virtual-server s3 port '88'
+ # set high-availability virtual-server s3 protocol 'udp'
+ # set high-availability vrrp global-parameters garp master-repeat '6'
+ # set high-availability vrrp global-parameters startup-delay '30'
+ # set high-availability vrrp group g1 peer-address '192.168.1.3'
+ # set high-availability vrrp group g1 priority '100'
+ # set high-availability vrrp group g1 vrid '20'
+ # set high-availability vrrp snmp
+ # set high-availability vrrp sync-group sg1 health-check failure-count '5'
+ # vyos@vyos:~$
+ #
+ # # Module Execution:
+ #
+ # "after": {
+ # "disable": true,
+ # "virtual_servers": [
+ # {
+ # "address": "10.10.10.5",
+ # "algorithm": "round-robin",
+ # "name": "s1",
+ # "real_server": [
+ # {
+ # "address": "10.10.50.2",
+ # "port": 8443
+ # }
+ # ]
+ # },
+ # {
+ # "address": "10.10.10.2",
+ # "name": "s2",
+ # "persistence_timeout": 30,
+ # "port": 81,
+ # "protocol": "tcp"
+ # },
+ # {
+ # "address": "10.10.10.3",
+ # "name": "s3",
+ # "port": 88,
+ # "protocol": "udp"
+ # }
+ # ],
+ # "vrrp": {
+ # "global_parameters": {
+ # "garp": {
+ # "master_repeat": 6
+ # },
+ # "startup_delay": 30
+ # },
+ # "groups": [
+ # {
+ # "disable": false,
+ # "name": "g1",
+ # "no_preempt": false,
+ # "peer_address": "192.168.1.3",
+ # "priority": 100,
+ # "rfc3768_compatibility": false,
+ # "vrid": 20
+ # }
+ # ],
+ # "snmp": "enabled",
+ # "sync_groups": [
+ # {
+ # "health_check": {
+ # "failure_count": 5
+ # },
+ # "name": "sg1"
+ # }
+ # ]
+ # }
+ # },
+ # "before": {
+ # "disable": false
+ # },
+ # "changed": true,
+ # "commands": [
+ # "set high-availability disable",
+ # "set high-availability virtual-server s1 address 10.10.10.5",
+ # "set high-availability virtual-server s1 algorithm round-robin",
+ # "set high-availability virtual-server s1 real-server 10.10.50.2 port 8443",
+ # "set high-availability virtual-server s2 address 10.10.10.2",
+ # "set high-availability virtual-server s2 persistence-timeout 30",
+ # "set high-availability virtual-server s2 port 81",
+ # "set high-availability virtual-server s2 protocol tcp",
+ # "set high-availability virtual-server s3 address 10.10.10.3",
+ # "set high-availability virtual-server s3 port 88",
+ # "set high-availability virtual-server s3 protocol udp",
+ # "set high-availability vrrp global-parameters garp master-repeat 6",
+ # "set high-availability vrrp global-parameters startup-delay 30",
+ # "set high-availability vrrp group g1 peer-address 192.168.1.3",
+ # "set high-availability vrrp group g1 priority 100",
+ # "set high-availability vrrp group g1 vrid 20",
+ # "set high-availability vrrp snmp",
+ # "set high-availability vrrp sync-group sg1 health-check failure-count 5"
+ # ],
+
+ # Using replaced:
+ # --------------
+
+ # Before state:
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # set high-availability disable
+ # set high-availability virtual-server s1 address '10.10.10.5'
+ # set high-availability virtual-server s1 algorithm 'round-robin'
+ # set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+ # set high-availability virtual-server s2 address '10.10.10.2'
+ # set high-availability virtual-server s2 persistence-timeout '30'
+ # set high-availability virtual-server s2 port '81'
+ # set high-availability virtual-server s2 protocol 'tcp'
+ # set high-availability virtual-server s3 address '10.10.10.3'
+ # set high-availability virtual-server s3 port '88'
+ # set high-availability virtual-server s3 protocol 'udp'
+ # set high-availability vrrp global-parameters garp master-repeat '6'
+ # set high-availability vrrp global-parameters startup-delay '30'
+ # set high-availability vrrp group g1 peer-address '192.168.1.3'
+ # set high-availability vrrp group g1 priority '100'
+ # set high-availability vrrp group g1 vrid '20'
+ # set high-availability vrrp snmp
+ # set high-availability vrrp sync-group sg1 health-check failure-count '5'
+ # vyos@vyos:~$
+
+ - name: Replace
+ vyos.vyos.vyos_ha:
+ config:
+ disable: false
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.3
+ algorithm: round-robin
+ port: 8443
+ real_server:
+ - address: 10.10.50.3
+ port: 8443
+ - name: s2
+ address: 10.10.10.2
+ persistence_timeout: 300
+ port: 81
+ protocol: tcp
+ real_server:
+ - address: 10.10.50.30
+ port: 8443
+ - name: s3
+ address: 10.10.10.3
+ port: 88
+ protocol: udp
+ real_server:
+ - address: 10.10.50.6
+ port: 8443
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 30
+ garp:
+ master_repeat: 6
+ groups:
+ - name: "g1"
+ peer_address: 192.168.1.13
+ priority: 100
+ disable: false
+ no_preempt: true
+ interface: eth1
+ address: 192.168.51.13
+ vrid: 20
+ sync_groups:
+ - name: "sg1"
+ health_check:
+ failure_count: 3
+ state: replaced
+
+ # After state:
+
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # set high-availability virtual-server s1 address '10.10.10.3'
+ # set high-availability virtual-server s1 algorithm 'round-robin'
+ # set high-availability virtual-server s1 port '8443'
+ # set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+ # set high-availability virtual-server s1 real-server 10.10.50.3 port '8443'
+ # set high-availability virtual-server s2 address '10.10.10.2'
+ # set high-availability virtual-server s2 persistence-timeout '300'
+ # set high-availability virtual-server s2 port '81'
+ # set high-availability virtual-server s2 protocol 'tcp'
+ # set high-availability virtual-server s2 real-server 10.10.50.3 port '8443'
+ # set high-availability virtual-server s3 address '10.10.10.3'
+ # set high-availability virtual-server s3 port '88'
+ # set high-availability virtual-server s3 protocol 'udp'
+ # set high-availability virtual-server s3 real-server 10.10.50.6 port '8443'
+ # set high-availability vrrp global-parameters garp master-repeat '6'
+ # set high-availability vrrp global-parameters startup-delay '30'
+ # set high-availability vrrp group g1 address 192.168.51.13
+ # set high-availability vrrp group g1 interface 'eth1'
+ # set high-availability vrrp group g1 no-preempt
+ # set high-availability vrrp group g1 peer-address '192.168.1.3'
+ # set high-availability vrrp group g1 peer-address '192.168.1.13'
+ # set high-availability vrrp group g1 priority '100'
+ # set high-availability vrrp group g1 vrid '20'
+ # set high-availability vrrp snmp
+ # set high-availability vrrp sync-group sg1 health-check failure-count '3'
+ # vyos@vyos:~$
+ #
+ #
+ # Module Execution:
+ #
+ # "after": {
+ # "disable": false,
+ # "virtual_servers": [
+ # {
+ # "address": "10.10.10.3",
+ # "algorithm": "round-robin",
+ # "name": "s1",
+ # "port": 8443,
+ # "real_server": [
+ # {
+ # "address": "10.10.50.2",
+ # "port": 8443
+ # },
+ # {
+ # "address": "10.10.50.3",
+ # "port": 8443
+ # }
+ # ]
+ # },
+ # {
+ # "address": "10.10.10.2",
+ # "name": "s2",
+ # "persistence_timeout": 300,
+ # "port": 81,
+ # "protocol": "tcp",
+ # "real_server": [
+ # {
+ # "address": "10.10.50.3",
+ # "port": 8443
+ # }
+ # ]
+ # },
+ # {
+ # "address": "10.10.10.3",
+ # "name": "s3",
+ # "port": 88,
+ # "protocol": "udp",
+ # "real_server": [
+ # {
+ # "address": "10.10.50.6",
+ # "port": 8443
+ # }
+ # ]
+ # }
+ # ],
+ # "vrrp": {
+ # "global_parameters": {
+ # "garp": {
+ # "master_repeat": 6
+ # },
+ # "startup_delay": 30
+ # },
+ # "groups": [
+ # {
+ # "address": "192.168.51.13",
+ # "disable": false,
+ # "interface": "eth1",
+ # "name": "g1",
+ # "no_preempt": true,
+ # "peer_address": "192.168.1.13",
+ # "priority": 100,
+ # "rfc3768_compatibility": false,
+ # "vrid": 20
+ # }
+ # ],
+ # "snmp": "enabled",
+ # "sync_groups": [
+ # {
+ # "health_check": {
+ # "failure_count": 3
+ # },
+ # "name": "sg1"
+ # }
+ # ]
+ # }
+ # },
+ # "before": {
+ # "disable": true,
+ # "virtual_servers": [
+ # {
+ # "address": "10.10.10.5",
+ # "algorithm": "round-robin",
+ # "name": "s1",
+ # "real_server": [
+ # {
+ # "address": "10.10.50.2",
+ # "port": 8443
+ # }
+ # ]
+ # },
+ # {
+ # "address": "10.10.10.2",
+ # "name": "s2",
+ # "persistence_timeout": 30,
+ # "port": 81,
+ # "protocol": "tcp"
+ # },
+ # {
+ # "address": "10.10.10.3",
+ # "name": "s3",
+ # "port": 88,
+ # "protocol": "udp"
+ # }
+ # ],
+ # "vrrp": {
+ # "global_parameters": {
+ # "garp": {
+ # "master_repeat": 6
+ # },
+ # "startup_delay": 30
+ # },
+ # "groups": [
+ # {
+ # "disable": false,
+ # "name": "g1",
+ # "no_preempt": false,
+ # "peer_address": "192.168.1.3",
+ # "priority": 100,
+ # "rfc3768_compatibility": false,
+ # "vrid": 20
+ # }
+ # ],
+ # "snmp": "enabled",
+ # "sync_groups": [
+ # {
+ # "health_check": {
+ # "failure_count": 5
+ # },
+ # "name": "sg1"
+ # }
+ # ]
+ # }
+ # },
+ # "changed": true,
+ # "commands": [
+ # "delete high-availability disable",
+ # "set high-availability virtual-server s1 address 10.10.10.3",
+ # "set high-availability virtual-server s1 port 8443",
+ # "set high-availability virtual-server s1 real-server 10.10.50.3 port 8443",
+ # "set high-availability virtual-server s2 persistence-timeout 300",
+ # "set high-availability virtual-server s2 real-server 10.10.50.3 port 8443",
+ # "set high-availability virtual-server s3 real-server 10.10.50.6 port 8443",
+ # "set high-availability vrrp group g1 address 192.168.51.13",
+ # "set high-availability vrrp group g1 interface eth1",
+ # "set high-availability vrrp group g1 no-preempt",
+ # "set high-availability vrrp group g1 peer-address 192.168.1.13",
+ # "set high-availability vrrp sync-group sg1 health-check failure-count 3"
+ # ],
+
+ # Using deleted:
+ # -------------
+
+ # Before state:
+
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # set high-availability disable
+ # set high-availability virtual-server s1 address '10.10.10.5'
+ # set high-availability virtual-server s1 algorithm 'round-robin'
+ # set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+ # set high-availability virtual-server s2 address '10.10.10.2'
+ # set high-availability virtual-server s2 persistence-timeout '30'
+ # set high-availability virtual-server s2 port '81'
+ # set high-availability virtual-server s2 protocol 'tcp'
+ # set high-availability virtual-server s3 address '10.10.10.3'
+ # set high-availability virtual-server s3 port '88'
+ # set high-availability virtual-server s3 protocol 'udp'
+ # set high-availability vrrp global-parameters garp master-repeat '6'
+ # set high-availability vrrp global-parameters startup-delay '30'
+ # set high-availability vrrp group g1 peer-address '192.168.1.3'
+ # set high-availability vrrp group g1 priority '100'
+ # set high-availability vrrp group g1 vrid '20'
+ # set high-availability vrrp snmp
+ # set high-availability vrrp sync-group sg1 health-check failure-count '5'
+ # vyos@vyos:~$
+
+ - name: Delete configuration
+ vyos.vyos.vyos_ha:
+ config:
+ disable: false
+ vrrp:
+ snmp: disabled
+ global_parameters:
+ startup_delay: 32
+ version: 3
+ virtual_servers:
+ - name: 's1'
+ address: '10.10.10.1'
+ algorithm: 'round-robin'
+ delay_loop: 60
+ forward_method: 'direct'
+ persistence_timeout: 30
+ port: 443
+ protocol: 'tcp'
+ real_server:
+ - address: '10.10.10.1'
+ connection_timeout: 61
+ port: 443
+ state: deleted
+
+ # After state:
+
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # set high-availability disable
+ # set high-availability virtual-server s2 address '10.10.10.2'
+ # set high-availability virtual-server s2 persistence-timeout '30'
+ # set high-availability virtual-server s2 port '81'
+ # set high-availability virtual-server s2 protocol 'tcp'
+ # set high-availability virtual-server s3 address '10.10.10.3'
+ # set high-availability virtual-server s3 port '88'
+ # set high-availability virtual-server s3 protocol 'udp'
+ # set high-availability vrrp global-parameters garp master-repeat '6'
+ # set high-availability vrrp group g1 peer-address '192.168.1.3'
+ # set high-availability vrrp group g1 priority '100'
+ # set high-availability vrrp group g1 vrid '20'
+ # set high-availability vrrp snmp
+ # set high-availability vrrp sync-group sg1 health-check failure-count '5'
+
+ # vyos@vyos:~$
+ #
+ #
+ # Module Execution:
+ #
+ # "after": {
+ # "disable": true,
+ # "virtual_servers": [
+ # {
+ # "address": "10.10.10.2",
+ # "name": "s2",
+ # "persistence_timeout": 30,
+ # "port": 81,
+ # "protocol": "tcp"
+ # },
+ # {
+ # "address": "10.10.10.3",
+ # "name": "s3",
+ # "port": 88,
+ # "protocol": "udp"
+ # }
+ # ],
+ # "vrrp": {
+ # "global_parameters": {
+ # "garp": {
+ # "master_repeat": 6
+ # }
+ # },
+ # "groups": [
+ # {
+ # "disable": false,
+ # "name": "g1",
+ # "no_preempt": false,
+ # "peer_address": "192.168.1.3",
+ # "priority": 100,
+ # "rfc3768_compatibility": false,
+ # "vrid": 20
+ # }
+ # ],
+ # "snmp": "enabled",
+ # "sync_groups": [
+ # {
+ # "health_check": {
+ # "failure_count": 5
+ # },
+ # "name": "sg1"
+ # }
+ # ]
+ # }
+ # },
+ # "before": {
+ # "disable": true,
+ # "virtual_servers": [
+ # {
+ # "address": "10.10.10.5",
+ # "algorithm": "round-robin",
+ # "name": "s1",
+ # "real_server": [
+ # {
+ # "address": "10.10.50.2",
+ # "port": 8443
+ # }
+ # ]
+ # },
+ # {
+ # "address": "10.10.10.2",
+ # "name": "s2",
+ # "persistence_timeout": 30,
+ # "port": 81,
+ # "protocol": "tcp"
+ # },
+ # {
+ # "address": "10.10.10.3",
+ # "name": "s3",
+ # "port": 88,
+ # "protocol": "udp"
+ # }
+ # ],
+ # "vrrp": {
+ # "global_parameters": {
+ # "garp": {
+ # "master_repeat": 6
+ # },
+ # "startup_delay": 30
+ # },
+ # "groups": [
+ # {
+ # "disable": false,
+ # "name": "g1",
+ # "no_preempt": false,
+ # "peer_address": "192.168.1.3",
+ # "priority": 100,
+ # "rfc3768_compatibility": false,
+ # "vrid": 20
+ # }
+ # ],
+ # "snmp": "enabled",
+ # "sync_groups": [
+ # {
+ # "health_check": {
+ # "failure_count": 5
+ # },
+ # "name": "sg1"
+ # }
+ # ]
+ # }
+ # },
+ # "changed": true,
+ # "commands": [
+ # "delete high-availability virtual-server s1",
+ # "delete high-availability vrrp global-parameters startup-delay"
+ # ],
+
+ # Using purged:
+
+ # Before state:
+
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # set high-availability disable
+ # set high-availability virtual-server s2 address '10.10.10.2'
+ # set high-availability virtual-server s2 persistence-timeout '30'
+ # set high-availability virtual-server s2 port '81'
+ # set high-availability virtual-server s2 protocol 'tcp'
+ # set high-availability virtual-server s3 address '10.10.10.3'
+ # set high-availability virtual-server s3 port '88'
+ # set high-availability virtual-server s3 protocol 'udp'
+ # set high-availability vrrp global-parameters garp master-repeat '6'
+ # set high-availability vrrp group g1 peer-address '192.168.1.3'
+ # set high-availability vrrp group g1 priority '100'
+ # set high-availability vrrp group g1 vrid '20'
+ # set high-availability vrrp snmp
+ # set high-availability vrrp sync-group sg1 health-check failure-count '5'
+ # vyos@vyos:~$
+
+
+ - name: Purge configuration
+ vyos.vyos.vyos_ha:
+ config:
+ state: purged
+
+ # After state:
+
+ # vyos@vyos:~$ show configuration commands | match "set high-availability"
+ # vyos@vyos:~$
+ #
+ # Module Execution:
+ #
+ # "after": {
+ # "disable": false
+ # },
+ # "before": {
+ # "disable": true,
+ # "virtual_servers": [
+ # {
+ # "address": "10.10.10.2",
+ # "name": "s2",
+ # "persistence_timeout": 30,
+ # "port": 81,
+ # "protocol": "tcp"
+ # },
+ # {
+ # "address": "10.10.10.3",
+ # "name": "s3",
+ # "port": 88,
+ # "protocol": "udp"
+ # }
+ # ],
+ # "vrrp": {
+ # "global_parameters": {
+ # "garp": {
+ # "master_repeat": 6
+ # }
+ # },
+ # "groups": [
+ # {
+ # "disable": false,
+ # "name": "g1",
+ # "no_preempt": false,
+ # "peer_address": "192.168.1.3",
+ # "priority": 100,
+ # "rfc3768_compatibility": false,
+ # "vrid": 20
+ # }
+ # ],
+ # "snmp": "enabled",
+ # "sync_groups": [
+ # {
+ # "health_check": {
+ # "failure_count": 5
+ # },
+ # "name": "sg1"
+ # }
+ # ]
+ # }
+ # },
+ # "changed": true,
+ # "commands": [
+ # "delete high-availability"
+ # ],
+
+
+ # using gathered:
+ # --------------
+
+ # Before state:
+ # vyos@vyos:~$
+ # show configuration commands | match "set high-availability"
+ # set high-availability disable
+ # set high-availability virtual-server s1 address '10.10.10.5'
+ # set high-availability virtual-server s1 algorithm 'round-robin'
+ # set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+ # set high-availability virtual-server s2 address '10.10.10.2'
+ # set high-availability virtual-server s2 persistence-timeout '30'
+ # set high-availability virtual-server s2 port '81'
+ # set high-availability virtual-server s2 protocol 'tcp'
+ # set high-availability virtual-server s3 address '10.10.10.3'
+ # set high-availability virtual-server s3 port '88'
+ # set high-availability virtual-server s3 protocol 'udp'
+ # set high-availability vrrp global-parameters garp master-repeat '6'
+ # set high-availability vrrp global-parameters startup-delay '30'
+ # set high-availability vrrp group g1 peer-address '192.168.1.3'
+ # set high-availability vrrp group g1 priority '100'
+ # set high-availability vrrp group g1 vrid '20'
+ # set high-availability vrrp snmp
+ # set high-availability vrrp sync-group sg1 health-check failure-count '5'
+ # vyos@vyos:~$
+
+ - name: gather configs
+ vyos.vyos.vyos_ha:
+ state: gathered
+
+ # Module Execution:
+ # "changed": false,
+ # "gathered": {
+ # "disable": true,
+ # "virtual_servers": [
+ # {
+ # "address": "10.10.10.5",
+ # "algorithm": "round-robin",
+ # "name": "s1",
+ # "real_server": [
+ # {
+ # "address": "10.10.50.2",
+ # "port": 8443
+ # }
+ # ]
+ # },
+ # {
+ # "address": "10.10.10.2",
+ # "name": "s2",
+ # "persistence_timeout": 30,
+ # "port": 81,
+ # "protocol": "tcp"
+ # },
+ # {
+ # "address": "10.10.10.3",
+ # "name": "s3",
+ # "port": 88,
+ # "protocol": "udp"
+ # }
+ # ],
+ # "vrrp": {
+ # "global_parameters": {
+ # "garp": {
+ # "master_repeat": 6
+ # },
+ # "startup_delay": 30
+ # },
+ # "groups": [
+ # {
+ # "disable": false,
+ # "name": "g1",
+ # "no_preempt": false,
+ # "peer_address": "192.168.1.3",
+ # "priority": 100,
+ # "rfc3768_compatibility": false,
+ # "vrid": 20
+ # }
+ # ],
+ # "snmp": "enabled",
+ # "sync_groups": [
+ # {
+ # "health_check": {
+ # "failure_count": 5
+ # },
+ # "name": "sg1"
+ # }
+ # ]
+ # }
+ # },
+ #
+
+ # Using parsed:
+ # ------------
+
+ # parsed.cfg
+ # set high-availability vrrp group g1 interface eth2
+ # set high-availability vrrp group g1 address 1.1.1.1
+ # set high-availability vrrp group g1 disable
+ # set high-availability vrrp group g1 no-preempt
+ # set high-availability vrrp group g1 advertise-interval 10
+ # set high-availability vrrp group g1 peer-address 2.2.2.2
+ # set high-availability vrrp group g1 rfc3768-compatibility
+ # set high-availability vrrp group g1 vrid 20
+
+ - name: parse configs
+ vyos.vyos.vyos_ha:
+ running_config: "{{ lookup('file', './parsed.cfg') }}"
+ state: parsed
+
+ # Module execution:
+ # "parsed": {
+ # "disable": false,
+ # "vrrp": {
+ # "groups": [
+ # {
+ # "address": "1.1.1.1",
+ # "advertise_interval": 10,
+ # "disable": true,
+ # "interface": "eth2",
+ # "name": "g1",
+ # "no_preempt": true,
+ # "peer_address": "2.2.2.2",
+ # "rfc3768_compatibility": true,
+ # "vrid": 20
+ # }
+ # ]
+ # }
+ # }
+ #
+
+ # Using rendered:
+ # --------------
+
+ - name: Render
+ vyos.vyos.vyos_ha:
+ config:
+ disable: true
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 32
+ version: 3
+ garp:
+ interval: 30
+ master_delay: 11
+ master_refresh: 100
+ master_refresh_repeat: 200
+ master_repeat: 5
+ state: rendered
+
+ # Module Execution:
+ # "rendered": [
+ # "set high-availability disable",
+ # "set high-availability vrrp global-parameters garp interval 30",
+ # "set high-availability vrrp global-parameters garp master-delay 11",
+ # "set high-availability vrrp global-parameters garp master-refresh 100",
+ # "set high-availability vrrp global-parameters garp master-refresh-repeat 200",
+ # "set high-availability vrrp global-parameters garp master-repeat 5",
+ # "set high-availability vrrp global-parameters startup-delay 32",
+ # "set high-availability vrrp global-parameters version 3",
+ # "set high-availability vrrp snmp"
+ # ]
+
+
+
+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>The resulting configuration after module execution.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">This output will always be in the same format as the module argspec.</div>
+ </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>when <em>state</em> is <code>merged</code>, <code>replaced</code>, <code>overridden</code>, <code>deleted</code> or <code>purged</code></td>
+ <td>
+ <div>The configuration prior to the module execution.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">This output will always be in the same format as the module argspec.</div>
+ </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>when <em>state</em> is <code>merged</code>, <code>replaced</code>, <code>overridden</code>, <code>deleted</code> or <code>purged</code></td>
+ <td>
+ <div>The set of commands pushed to the remote device.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[&quot;set high-availability vrrp group g1 address &#x27;1.1.1.1&#x27;&quot;, &quot;set high-availability vrrp group g1 advertise-interval &#x27;10&#x27;&quot;, &quot;set high-availability vrrp group g1 description &#x27;Group 1&#x27;&quot;]</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>gathered</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>when <em>state</em> is <code>gathered</code></td>
+ <td>
+ <div>Facts about the network resource gathered from the remote device as structured data.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">This output will always be in the same format as the module argspec.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>parsed</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>when <em>state</em> is <code>parsed</code></td>
+ <td>
+ <div>The device native config provided in <em>running_config</em> option parsed into structured data as per module argspec.</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">This output will always be in the same format as the module argspec.</div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="1">
+ <div class="ansibleOptionAnchor" id="return-"></div>
+ <b>rendered</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>when <em>state</em> is <code>rendered</code></td>
+ <td>
+ <div>The provided configuration in the task rendered in device-native format (offline).</div>
+ <br/>
+ <div style="font-size: smaller"><b>Sample:</b></div>
+ <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[&quot;set high-availability vrrp global-parameters garp master-delay &#x27;10&#x27;&quot;, &quot;set high-availability vrrp global-parameters garp master-refresh &#x27;100&#x27;&quot;, &quot;set high-availability vrrp global-parameters garp master-refresh-repeat &#x27;200&#x27;&quot;]</div>
+ </td>
+ </tr>
+ </table>
+ <br/><br/>
+
+
+Status
+------
+
+
+Authors
+~~~~~~~
+
+- Evgeny Molotkov (@omnom62)
diff --git a/meta/runtime.yml b/meta/runtime.yml
index fe819fdf..9bee831f 100644
--- a/meta/runtime.yml
+++ b/meta/runtime.yml
@@ -68,3 +68,5 @@ plugin_routing:
redirect: vyos.vyos.vyos_vlan
vrf:
redirect: vyos.vyos.vyos_vrf
+ ha:
+ redirect: vyos.vyos.vyos_ha
diff --git a/plugins/action/ha.py b/plugins/action/ha.py
new file mode 120000
index 00000000..331a791f
--- /dev/null
+++ b/plugins/action/ha.py
@@ -0,0 +1 @@
+vyos.py \ No newline at end of file
diff --git a/plugins/module_utils/network/vyos/argspec/ha/__init__.py b/plugins/module_utils/network/vyos/argspec/ha/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/ha/__init__.py
diff --git a/plugins/module_utils/network/vyos/argspec/ha/ha.py b/plugins/module_utils/network/vyos/argspec/ha/ha.py
new file mode 100644
index 00000000..4fe431cd
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/ha/ha.py
@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+# Copyright 2024 Red Hat
+# 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
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the
+# cli_rm_builder.
+#
+# Manually editing this file is not advised.
+#
+# To update the argspec make the desired changes
+# in the module docstring and re-run
+# cli_rm_builder.
+#
+#############################################
+
+"""
+The arg spec for the vyos_ha module
+"""
+
+
+class HaArgs(object): # pylint: disable=R0903
+ """The arg spec for the vyos_ha module"""
+
+ argument_spec = {
+ "config": {
+ "type": "dict",
+ "required": False,
+ "options": {
+ "disable": {"type": "bool", "default": False},
+ "virtual_servers": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "name": {"type": "str", "required": True},
+ "address": {"type": "str"},
+ "algorithm": {"type": "str"},
+ "delay_loop": {"type": "int"},
+ "forward_method": {"type": "str", "choices": ["direct", "nat"]},
+ "fwmark": {"type": "int"},
+ "persistence_timeout": {"type": "int"},
+ "port": {"type": "int"},
+ "protocol": {"type": "str", "choices": ["tcp", "udp"]},
+ "real_server": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "address": {"type": "str", "required": True},
+ "port": {"type": "int"},
+ "connection_timeout": {"type": "int"},
+ "health_check_script": {"type": "str"},
+ },
+ },
+ },
+ },
+ "vrrp": {
+ "type": "dict",
+ "options": {
+ "global_parameters": {
+ "type": "dict",
+ "options": {
+ "garp": {
+ "type": "dict",
+ "options": {
+ "interval": {"type": "int"},
+ "master_delay": {"type": "int"},
+ "master_refresh": {"type": "int"},
+ "master_refresh_repeat": {"type": "int"},
+ "master_repeat": {"type": "int"},
+ },
+ },
+ "startup_delay": {"type": "int"},
+ "version": {"type": "str"},
+ },
+ },
+ "groups": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "name": {"type": "str", "required": True},
+ "address": {"type": "list", "elements": "str"},
+ "advertise_interval": {"type": "int"},
+ "authentication": {
+ "type": "dict",
+ "options": {
+ "password": {"type": "str", "no_log": True},
+ "type": {"type": "str"},
+ },
+ },
+ "description": {"type": "str"},
+ "disable": {"type": "bool", "default": False},
+ "excluded_address": {"type": "list", "elements": "str"},
+ "garp": {
+ "type": "dict",
+ "options": {
+ "interval": {"type": "int"},
+ "master_delay": {"type": "int"},
+ "master_refresh": {"type": "int"},
+ "master_refresh_repeat": {"type": "int"},
+ "master_repeat": {"type": "int"},
+ },
+ },
+ "health_check": {
+ "type": "dict",
+ "options": {
+ "failure_count": {"type": "int"},
+ "interval": {"type": "int"},
+ "ping": {"type": "str"},
+ "script": {"type": "str"},
+ },
+ },
+ "hello_source_address": {"type": "str"},
+ "interface": {"type": "str"},
+ "no_preempt": {"type": "bool", "default": False},
+ "peer_address": {"type": "str"},
+ "preempt_delay": {"type": "int"},
+ "priority": {"type": "int"},
+ "rfc3768_compatibility": {"type": "bool", "default": False},
+ "track": {
+ "type": "dict",
+ "options": {
+ "exclude_vrrp_interface": {"type": "bool"},
+ "interface": {"type": "list", "elements": "str"},
+ },
+ },
+ "transition_script": {
+ "type": "dict",
+ "options": {
+ "backup": {"type": "str"},
+ "fault": {"type": "str"},
+ "master": {"type": "str"},
+ "stop": {"type": "str"},
+ },
+ },
+ "vrid": {"type": "int", "required": False},
+ },
+ },
+ "snmp": {
+ "type": "str",
+ "choices": ["disabled", "enabled"],
+ },
+ "sync_groups": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "name": {"type": "str", "required": True},
+ "health_check": {
+ "type": "dict",
+ "options": {
+ "failure_count": {"type": "int"},
+ "interval": {"type": "int"},
+ "ping": {"type": "str"},
+ "script": {"type": "str"},
+ },
+ },
+ "member": {"type": "list", "elements": "str"},
+ "transition_script": {
+ "type": "dict",
+ "options": {
+ "backup": {"type": "str"},
+ "fault": {"type": "str"},
+ "master": {"type": "str"},
+ "stop": {"type": "str"},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "state": {
+ "type": "str",
+ "choices": [
+ "deleted",
+ "merged",
+ "purged",
+ "replaced",
+ "gathered",
+ "rendered",
+ "parsed",
+ "overridden",
+ ],
+ "default": "merged",
+ },
+ "running_config": {"type": "str"},
+ } # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/config/ha/__init__.py b/plugins/module_utils/network/vyos/config/ha/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/ha/__init__.py
diff --git a/plugins/module_utils/network/vyos/config/ha/ha.py b/plugins/module_utils/network/vyos/config/ha/ha.py
new file mode 100644
index 00000000..7fc96a35
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/ha/ha.py
@@ -0,0 +1,705 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The vyos_ha config file.
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to its desired end-state is
+created.
+"""
+
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import (
+ ResourceModule,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_empties,
+)
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ha import (
+ HaTemplate,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import combine
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import (
+ LooseVersion,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
+
+
+class Ha(ResourceModule):
+ """
+ The vyos_ha config class
+ """
+
+ def __init__(self, module):
+ super(Ha, self).__init__(
+ empty_fact_val={},
+ facts_module=Facts(module),
+ module=module,
+ resource="ha",
+ tmplt=HaTemplate(),
+ )
+ self.parsers = [
+ "disable",
+ ]
+
+ self._validate_template()
+
+ def _validate_template(self):
+ version = get_os_version(self._module)
+ if LooseVersion(version) >= LooseVersion("1.4"):
+ self._tmplt = HaTemplate()
+ else:
+ self._module.fail_json(msg="High Availability is not supported in this version of VyOS")
+
+ def execute_module(self):
+ """Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+
+ if self.state not in ["parsed", "gathered", "purged"]:
+ self.generate_commands()
+ self.run_commands()
+
+ if self.state == "purged":
+ wantd = {"disable": False}
+ haved = deepcopy(self.have)
+ if wantd != haved:
+ self.commands = ["delete high-availability"]
+ self.run_commands()
+ if "before" in self.result:
+ self._normalize_lists(self.result["before"])
+ if "after" in self.result:
+ self._normalize_lists(self.result["after"])
+ if "parsed" in self.result:
+ self._normalize_lists(self.result["parsed"])
+ return self.result
+
+ def generate_commands(self):
+ """Generate configuration commands to send based on
+ want, have and desired state.
+ """
+ wantd = deepcopy(self.want)
+ haved = deepcopy(self.have)
+ for entry in wantd, haved:
+ self._list_to_named_dict(entry)
+ self._normalize_lists(entry)
+
+ if self.state in ["deleted"]:
+ wantd, haved, p = self._prune_stubs(self._module.params.get("config", {}), haved)
+
+ if self.state in ["overridden"]:
+ wo = deepcopy(wantd)
+ self._diff_w_h(wo, haved)
+
+ haved_disable = haved.get("disable")
+
+ for k1, v1 in wo.items():
+
+ if not isinstance(v1, dict):
+ continue
+
+ for name, obj in v1.items():
+ if isinstance(obj, dict) and not obj:
+ wi, hi, pi = self._prune_stubs({k1: {name: {}}}, haved)
+ haved = hi
+
+ for k2, v2 in v1.items():
+ if not isinstance(v2, dict):
+ continue
+
+ for name, obj in v2.items():
+ if isinstance(obj, dict) and not obj:
+ wi, hi, pi = self._prune_stubs({k1: {k2: {name: {}}}}, haved)
+ haved = hi
+
+ if haved_disable is not None:
+ haved["disable"] = haved_disable
+
+ keys = set(wantd) | set(haved)
+
+ for k in keys:
+
+ want = wantd.get(k, {})
+ have = haved.get(k, {})
+
+ if k == "vrrp":
+ if self.state in ["merged"]:
+ want = combine(have, want, recursive=True, list_merge="append_rp")
+ self._compare_vrrp(want, have)
+
+ if k == "virtual_servers":
+ if self.state in ["merged"]:
+ want = combine(have, want, recursive=True)
+ self._compare_vsrvs(want, have)
+
+ if self.state in ["deleted"] and k == "disable":
+ want = have
+ if self.state in ["overridden"] and k == "disable" and not want:
+ want = False
+ if self.state in ["rendered"]:
+ have = None
+
+ self.compare(
+ parsers=self.parsers,
+ want={k: want},
+ have={k: have},
+ )
+
+ self.commands = list(dict.fromkeys(self.commands))
+
+ def _compare_vsrvs(self, want, have):
+ """Compare virtual servers.
+
+ Pre-index both want and have by (name, attribute) signature so that
+ each lookup is O(1) instead of O(n). Groups that are identical
+ between want and have are skipped entirely via an equality
+ short-circuit before leaf decomposition.
+ """
+ vs_parsers = [
+ "virtual_servers.address",
+ "virtual_servers.algorithm",
+ "virtual_servers.delay_loop",
+ "virtual_servers.forward_method",
+ "virtual_servers.persistence_timeout",
+ "virtual_servers.fwmark",
+ "virtual_servers.port",
+ "virtual_servers.protocol",
+ "virtual_servers.real_server.port",
+ "virtual_servers.real_server.health_check_script",
+ "virtual_servers.real_server.connection_timeout",
+ ]
+
+ want_index = (
+ {vs["name"]: vs for vs in want.values() if isinstance(vs, dict) and vs.get("name")}
+ if isinstance(want, dict)
+ else {}
+ )
+ have_index = (
+ {vs["name"]: vs for vs in have.values() if isinstance(vs, dict) and vs.get("name")}
+ if isinstance(have, dict)
+ else {}
+ )
+
+ all_names = set(want_index) | set(have_index)
+
+ for name in all_names:
+ w = want_index.get(name, {})
+ h = have_index.get(name, {})
+
+ if w == h and self.state not in ["rendered"]:
+ continue
+
+ wlist = self._extract_named_leafs(w) if w else []
+ hlist = self._extract_named_leafs(h) if h else []
+
+ if self.state == "rendered":
+ hlist = []
+
+ def _vsrv_sig(item):
+ if not isinstance(item, dict):
+ return None
+ iname = item.get("name")
+ if not iname:
+ return None
+ if "real_server" in item:
+ rs = item["real_server"]
+ if not isinstance(rs, dict) or "address" not in rs:
+ return None
+ addr = rs["address"]
+ for k in rs:
+ if k != "address":
+ return ("real_server", iname, addr, k)
+ return ("real_server", iname, addr, None)
+ for k in item:
+ if k != "name":
+ return ("attr", iname, k)
+ return None
+
+ have_leaf_index = {}
+ for hdict in hlist:
+ sig = _vsrv_sig(hdict)
+ if sig is not None:
+ have_leaf_index[sig] = hdict
+
+ want_leaf_index = {}
+ for wdict in wlist:
+ sig = _vsrv_sig(wdict)
+ if sig is not None:
+ want_leaf_index[sig] = wdict
+
+ if self.state in ["replaced", "deleted"]:
+ for sig, hdict in have_leaf_index.items():
+ wdict = want_leaf_index.get(sig, {})
+ if self.state == "deleted" and wdict:
+ wdict = {}
+ elif not wdict:
+ hdict = {}
+ self.compare(
+ parsers=vs_parsers,
+ want={"virtual_servers": wdict},
+ have={"virtual_servers": hdict},
+ )
+
+ if self.state in ["merged", "replaced", "rendered", "overridden"]:
+ for sig, wdict in want_leaf_index.items():
+ hdict = have_leaf_index.get(sig, {})
+ self.compare(
+ parsers=vs_parsers,
+ want={"virtual_servers": wdict},
+ have={"virtual_servers": hdict},
+ )
+
+ def _compare_vrrp(self, want, have):
+ """Compare VRRP groups and sync-groups.
+
+ Pre-index groups by name so matching is O(1). Groups that are
+ identical between want and have are skipped via equality
+ short-circuit before any leaf decomposition occurs — this is the
+ dominant performance win for large idempotent runs.
+ """
+ vrrp_parsers = [
+ "vrrp.snmp",
+ "vrrp.global_parameters",
+ "vrrp.global_parameters.garp",
+ "vrrp.groups",
+ "vrrp.groups.disable",
+ "vrrp.groups.no_preempt",
+ "vrrp.groups.rfc3768_compatibility",
+ "vrrp.groups.address",
+ "vrrp.groups.excluded_address",
+ "vrrp.groups.garp",
+ "vrrp.groups.authentication",
+ "vrrp.groups.transition_script",
+ "vrrp.groups.health_check",
+ "vrrp.groups.track.interface",
+ "vrrp.groups.track.exclude_vrrp_interface",
+ "vrrp.sync_groups.member",
+ "vrrp.sync_groups.transition_script",
+ "vrrp.sync_groups.health_check",
+ ]
+
+ if (
+ have.get("snmp") == "enabled"
+ and want.get("snmp") != "enabled"
+ and self.state not in ["deleted", "overridden"]
+ and (self.state != "merged" or "snmp" in want)
+ ):
+ self.commands.append("delete high-availability vrrp snmp")
+
+ non_named = {k: v for k, v in (want or {}).items() if k not in ("groups", "sync_groups")}
+ non_named_have = {
+ k: v for k, v in (have or {}).items() if k not in ("groups", "sync_groups")
+ }
+
+ hlist_non = self._extract_leaf_items(non_named_have)
+ wlist_non = self._extract_leaf_items(non_named)
+
+ if self.state == "rendered":
+ hlist_non = []
+
+ have_non_index = {}
+ for hdict in hlist_non:
+ sig = self._vrrp_leaf_sig(hdict)
+ have_non_index[sig] = hdict
+
+ want_non_index = {}
+ for wdict in wlist_non:
+ sig = self._vrrp_leaf_sig(wdict)
+ want_non_index[sig] = wdict
+
+ if self.state in ["replaced", "deleted"]:
+ for sig, hdict in have_non_index.items():
+ wdict = want_non_index.get(sig, {})
+ if self.state == "deleted" and wdict:
+ wdict = {}
+ if self.state == "replaced" and wdict and wdict != hdict:
+ wdict = {}
+ elif not wdict:
+ hdict = {}
+ self.compare(parsers=vrrp_parsers, want={"vrrp": wdict}, have={"vrrp": hdict})
+
+ if self.state in ["merged", "replaced", "rendered", "overridden"]:
+ for sig, wdict in want_non_index.items():
+ hdict = have_non_index.get(sig, {})
+ self.compare(parsers=vrrp_parsers, want={"vrrp": wdict}, have={"vrrp": hdict})
+
+ for section in ("groups", "sync_groups"):
+ want_objs = (want or {}).get(section, {})
+ have_objs = (have or {}).get(section, {})
+
+ if not isinstance(want_objs, dict):
+ want_objs = {}
+ if not isinstance(have_objs, dict):
+ have_objs = {}
+
+ all_names = set(want_objs) | set(have_objs)
+
+ for name in all_names:
+ w = want_objs.get(name, {})
+ h = have_objs.get(name, {})
+
+ if w == h and self.state not in ["rendered"]:
+ continue
+
+ wlist = self._extract_leaf_items({section: {name: w}}) if w else []
+ hlist = self._extract_leaf_items({section: {name: h}}) if h else []
+
+ if self.state == "rendered":
+ hlist = []
+
+ have_leaf_index = {}
+ for hdict in hlist:
+ sig = self._vrrp_leaf_sig(hdict)
+ have_leaf_index[sig] = hdict
+
+ want_leaf_index = {}
+ for wdict in wlist:
+ sig = self._vrrp_leaf_sig(wdict)
+ want_leaf_index[sig] = wdict
+
+ if self.state in ["replaced", "deleted"]:
+ for sig, hdict in have_leaf_index.items():
+ wdict = want_leaf_index.get(sig, {})
+ if self.state == "deleted" and wdict:
+ wdict = {}
+ if self.state == "replaced" and wdict and wdict != hdict:
+ wdict = {}
+ elif not wdict:
+ hdict = {}
+ self.compare(
+ parsers=vrrp_parsers,
+ want={"vrrp": wdict},
+ have={"vrrp": hdict},
+ )
+
+ if self.state in ["merged", "replaced", "rendered", "overridden"]:
+ for sig, wdict in want_leaf_index.items():
+ hdict = have_leaf_index.get(sig, {})
+ self.compare(
+ parsers=vrrp_parsers,
+ want={"vrrp": wdict},
+ have={"vrrp": hdict},
+ )
+
+ def _vrrp_leaf_sig(self, item):
+ """Build a hashable signature for a VRRP leaf dict for O(1) indexing."""
+ if not isinstance(item, dict) or not item:
+ return ()
+
+ container = next(iter(item))
+ inner = item[container]
+
+ sig = [container]
+
+ if isinstance(inner, dict) and "name" in inner:
+ sig.append(("name", inner["name"]))
+
+ if isinstance(inner, dict):
+ for k, v in inner.items():
+ if k == "name":
+ continue
+ if not isinstance(v, dict):
+ sig.append(k)
+ break
+ sig.append(k)
+ for leaf in v:
+ sig.append(leaf)
+ break
+ break
+
+ return tuple(sig)
+
+ def _list_to_named_dict(self, data):
+ """Convert all named-object lists to name-keyed dicts in-place.
+
+ Replaces the three separate _vrrp_groups_list_to_dict,
+ _vrrp_sync_groups_list_to_dict, and _virtual_servers_list_to_dict
+ methods with a single helper. Also normalises real_server lists
+ inside virtual servers.
+ """
+ # VRRP groups and sync_groups
+ vrrp = data.get("vrrp", {})
+ for key in ("groups", "sync_groups"):
+ items = vrrp.get(key)
+ if isinstance(items, list):
+ vrrp[key] = {
+ item["name"]: item
+ for item in items
+ if isinstance(item, dict) and item.get("name")
+ }
+
+ # Virtual servers
+ vss = data.get("virtual_servers")
+ if isinstance(vss, list):
+ new_vss = {}
+ for vs in vss:
+ if not isinstance(vs, dict):
+ continue
+ name = vs.get("name")
+ if not name:
+ continue
+ rs = vs.get("real_server")
+ if isinstance(rs, list):
+ vs["real_server"] = {
+ item["address"]: item
+ for item in rs
+ if isinstance(item, dict) and item.get("address")
+ }
+ new_vss[name] = vs
+ data["virtual_servers"] = new_vss
+ elif isinstance(vss, dict):
+ for vs in vss.values():
+ if not isinstance(vs, dict):
+ continue
+ rs = vs.get("real_server")
+ if isinstance(rs, list):
+ vs["real_server"] = {
+ item["address"]: item
+ for item in rs
+ if isinstance(item, dict) and item.get("address")
+ }
+
+ return data
+
+ def _extract_leaf_items(self, data, path=None, parent_name=None):
+ path = path or []
+ results = []
+
+ if isinstance(data, dict):
+ current_name = data.get("name", parent_name)
+
+ for k, v in data.items():
+ if k == "name" or (k == "snmp" and v == "disabled"):
+ continue
+ results.extend(self._extract_leaf_items(v, path + [k], current_name))
+ return results
+
+ leaf_key = path[-1]
+ top_key = path[0]
+
+ if top_key in ["groups", "sync_groups"]:
+ subkeys = path[2:]
+ else:
+ subkeys = path[1:]
+
+ nested = {leaf_key: data}
+
+ for p in reversed(subkeys[:-1]):
+ nested = {p: nested}
+ if parent_name:
+ out = {top_key: {"name": parent_name}}
+ out[top_key].update(nested)
+ else:
+ out = {top_key: nested}
+
+ results.append(out)
+ return results
+
+ def _normalize_lists(self, node):
+ """
+ Recursively normalize all lists inside a dict or list.
+ All lists are sorted to ensure consistent ordering for comparison.
+ """
+ if isinstance(node, dict):
+ for k, v in node.items():
+ if isinstance(v, list):
+ if all(not isinstance(i, (dict, list)) for i in v):
+ node[k] = sorted(v)
+ else:
+ for item in v:
+ self._normalize_lists(item)
+ elif isinstance(v, dict):
+ self._normalize_lists(v)
+ elif isinstance(node, list):
+ for item in node:
+ self._normalize_lists(item)
+
+ def _extract_named_leafs(self, data, parent_name=None, prefix_key=None):
+ results = []
+
+ if prefix_key == "real_server" and isinstance(data, dict):
+ for d, server_data in data.items():
+ if not isinstance(server_data, dict):
+ continue
+
+ address = server_data.get("address")
+ if not address:
+ continue
+
+ for k, v in server_data.items():
+ if k == "address":
+ continue
+
+ results.append(
+ {
+ "name": parent_name,
+ "real_server": {
+ "address": address,
+ k: v,
+ },
+ },
+ )
+ return results
+
+ if isinstance(data, dict):
+ current_name = data.get("name", parent_name)
+
+ for k, v in data.items():
+ if k == "name":
+ continue
+
+ results.extend(
+ self._extract_named_leafs(v, current_name, k),
+ )
+
+ return results
+
+ return [
+ {
+ "name": parent_name,
+ prefix_key: data,
+ },
+ ]
+
+ def _prune_stubs(self, w, h, path=""):
+ wc = {}
+ hc = self._remove_defaults(h)
+
+ if not self._remove_defaults(w) and remove_empties(hc):
+ self.commands = ["delete high-availability"]
+ return {}, {}, path
+
+ for k, wg in (self._remove_defaults(w) or {}).items():
+ next_path = f"{path} {k}".strip()
+ stub = self._cli_path(next_path)
+ hg = remove_empties(hc).get(k)
+
+ if hg is None:
+ continue
+
+ if not isinstance(wg, (dict, list)):
+ self.commands.append(f"delete high-availability {stub}")
+ hc.pop(k, None)
+ wc.pop(k, None)
+ continue
+
+ if not wg:
+ self.commands.append(f"delete high-availability {stub}")
+ hc.pop(k, None)
+ wc.pop(k, None)
+ continue
+
+ if isinstance(wg, list) and isinstance(hg, dict):
+ for item in wg:
+ name = item.get("name")
+ if not name:
+ continue
+
+ if name in hg:
+ self.commands.append(
+ f"delete high-availability {stub} {name}",
+ )
+
+ hg.pop(name, None)
+
+ if hg:
+ hc[k] = hg
+ else:
+ hc.pop(k, None)
+
+ if self._remove_defaults(wg):
+ wc[k] = wg
+ else:
+ wc.pop(k, None)
+
+ continue
+
+ if isinstance(wg, dict) and isinstance(hg, dict):
+ wi, hi, p = self._prune_stubs(wg, hg, next_path)
+
+ if wi:
+ wc[k] = wi
+
+ if hi:
+ hc[k] = hi
+ else:
+ hc.pop(k, None)
+
+ return wc, hc, path
+
+ def _remove_defaults(self, data):
+ """Strip None and False from config dicts, but preserve "disabled".
+
+ False is the argspec default for boolean flags (disable, no_preempt,
+ rfc3768_compatibility) and carries no config intent — stripping it
+ prevents spurious `delete` commands for fields already at their
+ default state.
+
+ "disabled" is an explicit user choice for snmp and must be preserved
+ so that _prune_stubs can act on it. The original code stripped it,
+ which made `snmp: disabled` invisible to the deleted-state logic.
+ """
+ if isinstance(data, dict):
+ cleaned = {}
+ for k, v in data.items():
+ if v is None or v is False:
+ continue
+ v = self._remove_defaults(v)
+ cleaned[k] = v
+ return cleaned
+ return data
+
+ def _cli_path(self, path):
+ token_map = {
+ "groups": "group",
+ "sync_groups": "sync-group",
+ "virtual_servers": "virtual-server",
+ }
+
+ parts = []
+ for p in path.split():
+ p = token_map.get(p, p)
+ parts.append(p.replace("_", "-"))
+
+ return " ".join(parts)
+
+ def _diff_w_h(self, w, h):
+
+ NAMED_OBJECT_KEYS = {
+ "groups",
+ "sync_groups",
+ "virtual_servers",
+ "global_parameters",
+ }
+
+ if not isinstance(w, dict) or not isinstance(h, dict):
+ return w
+
+ for key in w.keys() & h.keys():
+ wv = w[key]
+ hv = h[key]
+
+ if key in NAMED_OBJECT_KEYS and isinstance(wv, dict) and isinstance(hv, dict):
+ for name in wv.keys() & hv.keys():
+ if wv[name] != hv[name] and isinstance(wv[name], (dict, list)):
+ wv[name] = {}
+ elif wv[name] != hv[name]:
+ wv[name] = None
+ continue
+ self._diff_w_h(wv, hv)
+ return w
diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py
index 24c444dd..7f7bad8a 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -30,6 +30,9 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firew
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import (
Firewall_rulesFacts,
)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ha.ha import (
+ HaFacts,
+)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.hostname.hostname import (
HostnameFacts,
)
@@ -94,6 +97,7 @@ FACT_RESOURCE_SUBSETS = dict(
firewall_rules=Firewall_rulesFacts,
firewall_global=Firewall_globalFacts,
firewall_interfaces=Firewall_interfacesFacts,
+ ha=HaFacts,
ospfv3=Ospfv3Facts,
ospfv2=Ospfv2Facts,
ospf_interfaces=Ospf_interfacesFacts,
diff --git a/plugins/module_utils/network/vyos/facts/ha/__init__.py b/plugins/module_utils/network/vyos/facts/ha/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/ha/__init__.py
diff --git a/plugins/module_utils/network/vyos/facts/ha/ha.py b/plugins/module_utils/network/vyos/facts/ha/ha.py
new file mode 100644
index 00000000..fc559783
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/ha/ha.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The vyos_ha fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ha.ha import (
+ HaArgs,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ha import (
+ HaTemplate,
+)
+
+
+class HaFacts(object):
+ """The vyos_ha facts class"""
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = HaArgs.argument_spec
+
+ def get_config(self, connection):
+ return connection.get('show configuration commands | match "set high-availability"')
+
+ def get_config_set(self, data, connection):
+ """Classify config lines into per-object buckets for isolated parsing.
+
+ Each bucket is parsed by a single HaTemplate instance so that facts
+ from different objects (groups, sync-groups, virtual-servers) never
+ bleed into each other.
+
+ Keys are namespaced to avoid collisions between a VRRP group and a
+ sync-group that share the same name (e.g. both named "g1").
+ An elif chain ensures each line lands in exactly one bucket.
+ """
+ config_dict = {}
+ for config_line in data.splitlines():
+ vrrp_disable = re.search(r"set high-availability disable", config_line)
+ vrrp_snmp = re.search(r"set high-availability vrrp snmp", config_line)
+ vrrp_gp = re.search(
+ r"set high-availability vrrp global-parameters (\S+).*",
+ config_line,
+ )
+ vrrp_grp = re.search(r"set high-availability vrrp group (\S+).*", config_line)
+ vrrp_sg = re.search(r"set high-availability vrrp sync-group (\S+).*", config_line)
+ vrrp_vsrv = re.search(r"set high-availability virtual-server (\S+).*", config_line)
+
+ if vrrp_disable:
+ config_dict.setdefault("disable", []).append(config_line)
+ elif vrrp_snmp:
+ config_dict.setdefault("vrrp", []).append(config_line)
+ elif vrrp_gp:
+ config_dict.setdefault("global_parameters", []).append(config_line)
+ elif vrrp_grp:
+ key = "vrrp_group_{0}".format(vrrp_grp.group(1))
+ config_dict.setdefault(key, []).append(config_line)
+ elif vrrp_sg:
+ key = "vrrp_sg_{0}".format(vrrp_sg.group(1))
+ config_dict.setdefault(key, []).append(config_line)
+ elif vrrp_vsrv:
+ config_dict.setdefault(vrrp_vsrv.group(1), []).append(config_line)
+
+ return list(config_dict.values())
+
+ def deep_merge(self, dest, src):
+ for key, value in src.items():
+ if key in dest and isinstance(dest[key], dict) and isinstance(value, dict):
+ self.deep_merge(dest[key], value)
+ else:
+ dest[key] = value
+ return dest
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """Populate the facts for vrrp network resource
+
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+
+ :rtype: dictionary
+ :returns: facts
+ """
+ facts = {}
+ objs = {}
+
+ if not data:
+ data = self.get_config(connection)
+ resources = self.get_config_set(data, connection)
+ vrrp_facts = {"disable": False, "virtual_servers": {}, "vrrp": {}}
+ for resource in resources:
+ vrrp_parser = HaTemplate(
+ lines=resource,
+ module=self._module,
+ )
+ objs = vrrp_parser.parse()
+ if "disable" in objs:
+ vrrp_facts["disable"] = objs["disable"]
+
+ for section in ("virtual_servers", "vrrp"):
+ if section in objs:
+ for name, data in objs[section].items():
+ if not isinstance(data, dict):
+ vrrp_facts[section][name] = data
+ continue
+ existing = vrrp_facts[section].get(name, {})
+ vrrp_facts[section][name] = self.deep_merge(existing, data)
+
+ ansible_facts["ansible_network_resources"].pop("ha", None)
+
+ vrrp_facts = self.normalize_config(vrrp_facts)
+
+ validate_parser = HaTemplate(lines=[], module=self._module)
+ params = utils.remove_empties(
+ validate_parser.validate_config(
+ self.argument_spec,
+ {"config": vrrp_facts},
+ redact=True,
+ ),
+ )
+
+ facts["ha"] = params.get("config", {})
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def normalize_config(self, config):
+ if not config:
+ return config
+
+ if isinstance(config.get("virtual_servers"), dict):
+ config["virtual_servers"] = list(config["virtual_servers"].values())
+
+ vrrp = config.get("vrrp", {})
+
+ if isinstance(vrrp.get("groups"), dict):
+ vrrp["groups"] = list(vrrp["groups"].values())
+
+ if isinstance(vrrp.get("sync_groups"), dict):
+ vrrp["sync_groups"] = list(vrrp["sync_groups"].values())
+
+ # Normalize real_server inside each virtual_server
+ for vs in config.get("virtual_servers", []):
+ if isinstance(vs.get("real_server"), dict):
+ vs["real_server"] = list(vs["real_server"].values())
+
+ for group in vrrp.get("groups", []):
+ if isinstance(group.get("address"), list):
+ group["address"] = sorted(group["address"])
+
+ if isinstance(group.get("excluded_address"), list):
+ group["excluded_address"] = sorted(group["excluded_address"])
+
+ if isinstance(group.get("track", {}).get("interface"), list):
+ group["track"]["interface"] = sorted(group["track"]["interface"])
+
+ for sg in vrrp.get("sync_groups", []):
+ if isinstance(sg.get("member"), list):
+ sg["member"] = sorted(sg["member"])
+
+ return config
diff --git a/plugins/module_utils/network/vyos/rm_templates/ha.py b/plugins/module_utils/network/vyos/rm_templates/ha.py
new file mode 100644
index 00000000..300b14a7
--- /dev/null
+++ b/plugins/module_utils/network/vyos/rm_templates/ha.py
@@ -0,0 +1,1011 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The Ha parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import (
+ NetworkTemplate,
+)
+
+
+def _tmplt_vsrvs(config_data):
+ config_data = config_data["virtual_servers"]
+ command = []
+
+ cmd = "high-availability virtual-server {name}".format(**config_data)
+ for key, value in config_data.items():
+ if key == "name" or isinstance(value, dict) or value is None:
+ continue
+ else:
+ command.append(f"{cmd} {key.replace('_', '-')} {value}")
+
+ return command
+
+
+def _tmplt_vsrvs_rsrv(config_data):
+ config_data = config_data["virtual_servers"]
+ command = []
+ cmd = "high-availability virtual-server {name}".format(**config_data)
+ config_data = config_data["real_server"]
+ address = config_data["address"]
+ for key, value in config_data.items():
+ if key == "address" or value is None:
+ continue
+ if value is not None and key == "health_check_script":
+ command.append(cmd + " real-server " + address + " health-check script " + value)
+ else:
+ command.append(cmd + " real-server " + f"{address} {key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_sgroup_hc(config_data):
+ config_data = config_data["vrrp"]["sync_groups"]
+ command = []
+ cmd = "high-availability vrrp sync-group {name}".format(**config_data)
+ config_data = config_data["health_check"]
+ for key, value in config_data.items():
+ if value is not None:
+ command.append(cmd + " health-check " + f"{key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_sgroup_ts(config_data):
+ config_data = config_data["vrrp"]["sync_groups"]
+ command = []
+ cmd = "high-availability vrrp sync-group {name}".format(**config_data)
+ config_data = config_data["transition_script"]
+ for key, value in config_data.items():
+ if value is not None:
+ command.append(cmd + " transition-script " + f"{key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_gp(config_data):
+ config_data = config_data["vrrp"]["global_parameters"]
+ command = []
+
+ cmd = "high-availability vrrp global-parameters".format(**config_data)
+ for key, value in config_data.items():
+ if isinstance(value, dict) or value is None:
+ continue
+ else:
+ command.append(f"{cmd} {key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_gp_garp(config_data):
+ config_data = config_data["vrrp"]["global_parameters"]["garp"]
+ command = []
+ cmd = "high-availability vrrp global-parameters garp"
+
+ for key, value in config_data.items():
+ if value is None:
+ continue
+ command.append(f"{cmd} {key.replace('_', '-')} {value}")
+
+ return command
+
+
+def _tmplt_vrrp_group(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+
+ for key, value in config_data.items():
+ if (
+ key == "name"
+ or isinstance(value, dict)
+ or isinstance(value, list)
+ or isinstance(value, bool)
+ or value is None
+ ):
+ continue
+ else:
+ if key == "description":
+ value = f"'{value}'"
+ command.append(f"{cmd} {key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_group_bool(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+
+ for key, value in config_data.items():
+ if key != "name" and value is not None:
+ command.append(f"{cmd} {key.replace('_', '-')}")
+ return command
+
+
+def _tmplt_vrrp_group_garp(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+ config_data = config_data["garp"]
+ for key, value in config_data.items():
+ if value is not None:
+ command.append(cmd + " garp " + f"{key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_group_auth(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+ config_data = config_data["authentication"]
+ for key, value in config_data.items():
+ if value is not None:
+ command.append(cmd + " authentication " + f"{key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_group_ts(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+ config_data = config_data["transition_script"]
+ for key, value in config_data.items():
+ if value is not None:
+ command.append(cmd + " transition-script " + f"{key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_sgroup_member(config_data):
+ sgroup = config_data["vrrp"]["sync_groups"]
+ command = []
+ cmd = "high-availability vrrp sync-group {name}".format(**sgroup)
+ members = sgroup.get("member", [])
+ for member in members:
+ if member is None:
+ continue
+ command.append(f"{cmd} member {member}")
+ return command
+
+
+def _tmplt_vrrp_group_exaddress(config_data):
+ group = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**group)
+ exaddresses = group.get("excluded_address", [])
+ for exaddress in exaddresses:
+ if exaddress is None:
+ continue
+ command.append(f"{cmd} excluded-address {exaddress}")
+ return command
+
+
+def _tmplt_vrrp_group_address(config_data):
+ group = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**group)
+ addresses = group.get("address", [])
+ for address in addresses:
+ if address is None:
+ continue
+ command.append(f"{cmd} address {address}")
+ return command
+
+
+def _tmplt_vrrp_group_hc(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+ config_data = config_data["health_check"]
+ for key, value in config_data.items():
+ if value is not None:
+ command.append(cmd + " health-check " + f"{key.replace('_', '-')} {value}")
+ return command
+
+
+def _tmplt_vrrp_group_track_list(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+ config_data = config_data["track"]
+ for key, value in config_data.items():
+ if isinstance(value, list) and value is not None and key != "name":
+ for item in value:
+ command.append(cmd + " track " + f"{key.replace('_', '-')} {item}")
+ return command
+
+
+def _tmplt_vrrp_group_track_bool(config_data):
+ config_data = config_data["vrrp"]["groups"]
+ command = []
+ cmd = "high-availability vrrp group {name}".format(**config_data)
+ config_data = config_data["track"]
+ for key, value in config_data.items():
+ if key != "name" and value is not None:
+ command.append(cmd + " track " + f"{key.replace('_', '-')}")
+ return command
+
+
+class HaTemplate(NetworkTemplate):
+ def __init__(self, lines=None, module=None):
+ prefix = {"set": "set", "remove": "delete"}
+ super(HaTemplate, self).__init__(
+ lines=lines,
+ tmplt=self,
+ prefix=prefix,
+ module=module,
+ )
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "disable",
+ "getval": re.compile(
+ r"""
+ ^set
+ \shigh-availability
+ \s(?P<disable>disable)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "high-availability disable",
+ "result": {
+ "disable": "{{ True if disable is defined else False }}",
+ },
+ },
+ {
+ "name": "virtual_servers.address",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+address\s+(?P<address>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "address": "{{ address if address is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.algorithm",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+algorithm\s+(?P<algorithm>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "algorithm": "{{ algorithm if algorithm is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.delay_loop",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+delay-loop\s+(?P<delay_loop>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "delay_loop": "{{ delay_loop if delay_loop is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.forward_method",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+forward-method\s+(?P<forward_method>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "forward_method": "{{ forward_method if forward_method is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.fwmark",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+fwmark\s+(?P<fwmark>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "fwmark": "{{ fwmark if fwmark is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.persistence_timeout",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+persistence-timeout\s+(?P<persistence_timeout>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "persistence_timeout": "{{ persistence_timeout if persistence_timeout is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.port",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+port\s+(?P<port>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "port": "{{ port if port is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.protocol",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ (?:\s+protocol\s+(?P<protocol>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "protocol": "{{ protocol if protocol is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.real_server.port",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ \sreal-server
+ \s+(?P<address>\S+)
+ (?:\s+port\s+(?P<port>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs_rsrv,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "real_server": {
+ "{{ address }}": {
+ "address": "{{ address }}",
+ "port": "{{ port if port is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.real_server.health_check_script",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ \sreal-server
+ \s+(?P<address>\S+)
+ (?:\s+health-check\sscript\s+(?P<hcscript>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs_rsrv,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "real_server": {
+ "{{ address }}": {
+ "address": "{{ address }}",
+ "health_check_script": "{{ hcscript if hcscript is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "virtual_servers.real_server.connection_timeout",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svirtual-server
+ \s+(?P<name>\S+)
+ \sreal-server
+ \s+(?P<address>\S+)
+ (?:\s+connection-timeout\s+(?P<cont>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vsrvs_rsrv,
+ "result": {
+ "virtual_servers": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "real_server": {
+ "{{ address }}": {
+ "address": "{{ address }}",
+ "connection_timeout": "{{ cont if cont is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.sync_groups.member",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\ssync-group
+ \s+(?P<sgname>\S+)
+ \smember
+ \s+(?P<member>\S+)
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_sgroup_member,
+ "result": {
+ "vrrp": {
+ "sync_groups": {
+ "{{ sgname }}": {
+ "name": "{{ sgname }}",
+ "member": [
+ "{{ member }}",
+ ],
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.sync_groups.health_check",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\ssync-group
+ \s+(?P<sgname>\S+)
+ \shealth-check
+ (?:\s+failure-count\s+(?P<failure_count>\S+))?
+ (?:\s+interval\s+(?P<int>\S+))?
+ (?:\s+ping\s+(?P<ping>\S+))?
+ (?:\s+script\s+(?P<script>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_sgroup_hc,
+ "result": {
+ "vrrp": {
+ "sync_groups": {
+ "{{ sgname }}": {
+ "name": "{{ sgname }}",
+ "health_check": {
+ "failure_count": "{{ failure_count if failure_count is defined else None }}",
+ "interval": "{{ int if int is defined else None }}",
+ "ping": "{{ ping if ping is defined else None }}",
+ "script": "{{ script if script is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.sync_groups.transition_script",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\ssync-group
+ \s+(?P<sgname>\S+)
+ \stransition-script
+ (?:\s+backup\s+(?P<backup>\S+))?
+ (?:\s+fault\s+(?P<fault>\S+))?
+ (?:\s+master\s+(?P<master>\S+))?
+ (?:\s+stop\s+(?P<stop>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_sgroup_ts,
+ "result": {
+ "vrrp": {
+ "sync_groups": {
+ "{{ sgname }}": {
+ "name": "{{ sgname }}",
+ "transition_script": {
+ "backup": "{{ backup if backup is defined else None }}",
+ "fault": "{{ fault if fault is defined else None }}",
+ "master": "{{ master if master is defined else None }}",
+ "stop": "{{ stop if stop is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.global_parameters.garp",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sglobal-parameters
+ \s+garp
+ (?:\s+interval\s+(?P<interval>\S+))?
+ (?:\s+master-delay\s+(?P<master_delay>\S+))?
+ (?:\s+master-refresh\s+(?P<master_refresh>\S+))?
+ (?:\s+master-refresh-repeat\s+(?P<master_refresh_repeat>\S+))?
+ (?:\s+master-repeat\s+(?P<master_repeat>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_gp_garp,
+ "result": {
+ "vrrp": {
+ "global_parameters": {
+ "garp": {
+ "interval": "{{ interval if interval is defined else None }}",
+ "master_delay": "{{ master_delay if master_delay is defined else None }}",
+ "master_refresh": "{{ master_refresh if master_refresh is defined else None }}",
+ "master_refresh_repeat": "{{ master_refresh_repeat if master_refresh_repeat is defined else None }}",
+ "master_repeat": "{{ master_repeat if master_repeat is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.global_parameters",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sglobal-parameters
+ (?=\s+(?:startup-delay|version)\s)
+ (?:\s+startup-delay\s+(?P<startup_delay>\S+))?
+ (?:\s+version\s+(?P<version>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_gp,
+ "result": {
+ "vrrp": {
+ "global_parameters": {
+ "startup_delay": "{{ startup_delay if startup_delay is defined else None }}",
+ "version": "{{ version if version is defined else None }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ (?:\s+description\s+(?P<description>'.+?'|\S+))?
+ (?:\s+advertise-interval\s+(?P<advertise_interval>\S+))?
+ (?:\s+hello-source-address\s+(?P<hello_source>\S+))?
+ (?:\s+interface\s+(?P<interface>\S+))?
+ (?:\s+peer-address\s+(?P<peer_address>\S+))?
+ (?:\s+preempt-delay\s+(?P<preempt_delay>\S+))?
+ (?:\s+priority\s+(?P<priority>\S+))?
+ (?:\s+vrid\s+(?P<vrid>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "description": "{{ description | replace(\"'\", \"\") if description is defined else None }}",
+ "advertise_interval": "{{ advertise_interval if advertise_interval is defined else None }}",
+ "hello_source_address": "{{ hello_source if hello_source is defined else None }}",
+ "interface": "{{ interface if interface is defined else None }}",
+ "peer_address": "{{ peer_address if peer_address is defined else None }}",
+ "preempt_delay": "{{ preempt_delay if preempt_delay is defined else None }}",
+ "priority": "{{ priority if priority is defined else None }}",
+ "vrid": "{{ vrid if vrid is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.excluded_address",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \sexcluded-address
+ \s+(?P<excluded_address>.*)
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_exaddress,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "excluded_address": [
+ "{{ excluded_address | replace(\"'\", \"\") }}",
+ ],
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.address",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \saddress
+ \s+(?P<address>.*)
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_address,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "address": [
+ "{{ address | replace(\"'\", \"\") }}",
+ ],
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.garp",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \s+garp
+ (?:\s+interval\s+(?P<interval>\S+))?
+ (?:\s+master-delay\s+(?P<master_delay>\S+))?
+ (?:\s+master-refresh\s+(?P<master_refresh>\S+))?
+ (?:\s+master-refresh-repeat\s+(?P<master_refresh_repeat>\S+))?
+ (?:\s+master-repeat\s+(?P<master_repeat>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_garp,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "garp": {
+ "interval": "{{ interval if interval is defined else None }}",
+ "master_delay": "{{ master_delay if master_delay is defined else None }}",
+ "master_refresh": "{{ master_refresh if master_refresh is defined else None }}",
+ "master_refresh_repeat": "{{ master_refresh_repeat if master_refresh_repeat is defined else None }}",
+ "master_repeat": "{{ master_repeat if master_repeat is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.authentication",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \s+authentication
+ (?:\s+password\s+(?P<password>\S+))?
+ (?:\s+type\s+(?P<type>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_auth,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "authentication": {
+ "password": "{{ password if password is defined else None }}",
+ "type": "{{ type if type is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.transition_script",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \stransition-script
+ (?:\s+backup\s+(?P<backup>\S+))?
+ (?:\s+fault\s+(?P<fault>\S+))?
+ (?:\s+master\s+(?P<master>\S+))?
+ (?:\s+stop\s+(?P<stop>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_ts,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "transition_script": {
+ "backup": "{{ backup if backup is defined else None }}",
+ "fault": "{{ fault if fault is defined else None }}",
+ "master": "{{ master if master is defined else None }}",
+ "stop": "{{ stop if stop is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.health_check",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \shealth-check
+ (?:\s+failure-count\s+(?P<failure_count>\S+))?
+ (?:\s+interval\s+(?P<int>\S+))?
+ (?:\s+ping\s+(?P<ping>\S+))?
+ (?:\s+script\s+(?P<script>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_hc,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "health_check": {
+ "failure_count": "{{ failure_count if failure_count is defined else None }}",
+ "interval": "{{ int if int is defined else None }}",
+ "ping": "{{ ping if ping is defined else None }}",
+ "script": "{{ script if script is defined else None }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.track.interface",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \strack
+ (?:\s+interface\s+(?P<interface>\S+))?
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_track_list,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "track": {
+ "interface": "{{ [interface.strip(\"'\")] if interface is defined else [] }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.snmp",
+ "getval": re.compile(
+ r"""
+ ^set
+ \shigh-availability
+ \svrrp
+ \s(?P<snmp>snmp)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "high-availability vrrp snmp",
+ "result": {
+ "vrrp": {
+ "snmp": "{{ 'enabled' if snmp is defined else 'disabled' }}",
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.disable",
+ "getval": re.compile(
+ r"""
+ ^set
+ \shigh-availability\svrrp\sgroup
+ \s(?P<gname>\S+)
+ \s(?P<disable>disable)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_bool,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "disable": "{{ True if disable is defined else False }}",
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.no_preempt",
+ "getval": re.compile(
+ r"""
+ ^set
+ \shigh-availability\svrrp\sgroup
+ \s(?P<gname>\S+)
+ \s(?P<no_preempt>no-preempt)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_bool,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "no_preempt": "{{ True if no_preempt is defined else False }}",
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.rfc3768_compatibility",
+ "getval": re.compile(
+ r"""
+ ^set
+ \shigh-availability\svrrp\sgroup
+ \s(?P<gname>\S+)
+ \s(?P<rfc3768_compatibility>rfc3768-compatibility)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_bool,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "rfc3768_compatibility": "{{ True if rfc3768_compatibility is defined else False }}",
+ },
+ },
+ },
+ },
+ },
+ {
+ "name": "vrrp.groups.track.exclude_vrrp_interface",
+ "getval": re.compile(
+ r"""
+ ^set\shigh-availability\svrrp\sgroup
+ \s+(?P<gname>\S+)
+ \strack
+ \s(?P<exclude_vrrp_inter>exclude-vrrp-interface)
+ $
+ """,
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_vrrp_group_track_bool,
+ "result": {
+ "vrrp": {
+ "groups": {
+ "{{ gname }}": {
+ "name": "{{ gname }}",
+ "track": {
+ "exclude_vrrp_interface": "{{ True if exclude_vrrp_inter is defined else False }}",
+ },
+ },
+ },
+ },
+ },
+ },
+ ]
+ # fmt: on
diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py
index 78445c02..df0a72ca 100644
--- a/plugins/module_utils/network/vyos/utils/utils.py
+++ b/plugins/module_utils/network/vyos/utils/utils.py
@@ -288,12 +288,29 @@ def in_target_not_none(h, key):
def combine(a, b, recursive=False, list_merge="replace"):
- """
- Merge two dictionaries (shallow or deep).
- :param a: dict
- :param b: dict
- :param recursive: bool, deep merge
- :param list_merge: str, only 'replace' is supported (default Ansible behavior)
+ """Merge dict ``b`` into dict ``a``, returning a new dict.
+
+ :param a: Base dictionary.
+ :param b: Dictionary whose values take precedence over ``a``.
+ :param recursive: When True, nested dicts are merged recursively rather
+ than replaced wholesale.
+ :param list_merge: Controls how list values are combined when the same key
+ exists in both dicts. Supported modes:
+
+ - ``"replace"`` *(default)* — ``b``'s list replaces ``a``'s list.
+ - ``"append"`` — ``b``'s list is appended to ``a``'s list (duplicates
+ kept).
+ - ``"prepend"`` — ``b``'s list is prepended to ``a``'s list
+ (duplicates kept).
+ - ``"append_rp"`` — like ``"append"`` but duplicates are removed,
+ preserving the first occurrence (rp = remove-preserve).
+ - ``"prepend_rp"`` — like ``"prepend"`` but duplicates are removed,
+ preserving the first occurrence.
+
+ Passing any other value raises ``ValueError``.
+ :returns: New merged dict.
+ :raises ValueError: If either argument is not a dict, or if an
+ unsupported ``list_merge`` mode is given.
"""
if not isinstance(a, dict) or not isinstance(b, dict):
raise ValueError("combine expects two dictionaries")
@@ -301,8 +318,29 @@ def combine(a, b, recursive=False, list_merge="replace"):
result = a.copy()
for k, v in b.items():
- if recursive and k in result and isinstance(result[k], dict) and isinstance(v, dict):
- result[k] = combine(result[k], v, recursive=True, list_merge=list_merge)
+ if k in result:
+ # dict merge
+ if recursive and isinstance(result[k], dict) and isinstance(v, dict):
+ result[k] = combine(result[k], v, recursive=True, list_merge=list_merge)
+
+ # list merge
+ elif isinstance(result[k], list) and isinstance(v, list):
+ if list_merge == "replace":
+ result[k] = v
+ elif list_merge == "append":
+ result[k] = result[k] + v
+ elif list_merge == "prepend":
+ result[k] = v + result[k]
+ elif list_merge == "append_rp":
+ result[k] = list(dict.fromkeys(result[k] + v))
+ elif list_merge == "prepend_rp":
+ result[k] = list(dict.fromkeys(v + result[k]))
+ else:
+ raise ValueError(f"Unsupported list_merge mode: {list_merge}")
+
+ # everything else
+ else:
+ result[k] = v
else:
result[k] = v
diff --git a/plugins/modules/vyos_ha.py b/plugins/modules/vyos_ha.py
new file mode 100644
index 00000000..ddd02b48
--- /dev/null
+++ b/plugins/modules/vyos_ha.py
@@ -0,0 +1,1327 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2024 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""
+The module file for vyos_ha module, which manages VRRP and load balancer configuration on VyOS
+"""
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: vyos_ha
+author: Evgeny Molotkov (@omnom62)
+short_description: Manage VRRP and load balancer configuration on VyOS
+version_added: "1.0.0"
+description:
+ - This module configures VRRP groups, global VRRP parameters, VRRP sync groups,
+ and LVS-style virtual servers on VyOS 1.4+.
+ - Supports creation, modification, deletion, replacement, rendering, and parsing
+ of VRRP-related configuration.
+
+options:
+ config:
+ description:
+ - Full VRRP and virtual server configuration.
+ type: dict
+ suboptions:
+ disable:
+ description:
+ - Disable all VRRP and L4-LB configuration under this module.
+ type: bool
+ default: false
+ virtual_servers:
+ description:
+ - List of load balancer virtual server (LVS) definitions.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Unique identifier for the virtual server.
+ type: str
+ required: true
+ address:
+ description:
+ - Virtual IP address for the server.
+ type: str
+ algorithm:
+ description:
+ - Load balancing algorithm used for dispatching connections.
+ type: str
+ delay_loop:
+ description:
+ - Delay loop interval in seconds.
+ type: int
+ forward_method:
+ description:
+ - Forwarding method used by LVS.
+ type: str
+ choices: [direct, nat]
+ fwmark:
+ description:
+ - Firewall mark for LVS traffic classification.
+ type: int
+ persistence_timeout:
+ description:
+ - Client persistence timeout in seconds.
+ type: int
+ port:
+ description:
+ - TCP/UDP port provided by the virtual service.
+ type: int
+ protocol:
+ description:
+ - Transport protocol for the virtual server.
+ type: str
+ choices: [tcp, udp]
+
+ real_server:
+ description:
+ - Backend real servers behind the virtual service.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description:
+ - Real server IP address.
+ type: str
+ required: true
+ port:
+ description:
+ - Backend server port.
+ type: int
+ connection_timeout:
+ description:
+ - Backend server connection timeout.
+ type: int
+ health_check_script:
+ description:
+ - Path to health check script used for backend validation.
+ type: str
+
+ vrrp:
+ description:
+ - VRRP configuration including groups, global parameters, SNMP settings,
+ and sync-groups.
+ type: dict
+ suboptions:
+
+ global_parameters:
+ description:
+ - Global VRRP tuning parameters.
+ type: dict
+ suboptions:
+ garp:
+ description:
+ - Gratuitous ARP related configuration.
+ type: dict
+ suboptions:
+ interval:
+ description:
+ - GARP interval in seconds.
+ type: int
+ master_delay:
+ description:
+ - Delay before sending GARP as master.
+ type: int
+ master_refresh:
+ description:
+ - Refresh interval for master GARP announcements.
+ type: int
+ master_refresh_repeat:
+ description:
+ - Number of times to repeat refresh announcements.
+ type: int
+ master_repeat:
+ description:
+ - Number of GARP repeats when transitioning to master.
+ type: int
+
+ startup_delay:
+ description:
+ - Delay before VRRP starts after boot.
+ type: int
+
+ version:
+ description:
+ - VRRP protocol version.
+ type: str
+
+ groups:
+ description:
+ - VRRP instance configuration groups.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - VRRP group name.
+ type: str
+ required: true
+ address:
+ description:
+ - Virtual router IP addresses.
+ type: list
+ elements: str
+
+ advertise_interval:
+ description:
+ - VRRP advertisement interval.
+ type: int
+
+ authentication:
+ description:
+ - VRRP group authentication options.
+ type: dict
+ suboptions:
+ password:
+ description:
+ - Authentication password.
+ type: str
+ type:
+ description:
+ - Authentication type.
+ type: str
+
+ description:
+ description:
+ - Text description for the VRRP group.
+ type: str
+
+ disable:
+ description:
+ - Disable this VRRP group.
+ type: bool
+ default: false
+
+ excluded_address:
+ description:
+ - IP address excluded from source checks.
+ type: list
+ elements: str
+
+ garp:
+ description:
+ - GARP-specific settings for this group.
+ type: dict
+ suboptions:
+ interval:
+ description: GARP interval.
+ type: int
+ master_delay:
+ description: GARP master delay.
+ type: int
+ master_refresh:
+ description: GARP master refresh interval.
+ type: int
+ master_refresh_repeat:
+ description: Repeated refresh sends.
+ type: int
+ master_repeat:
+ description: GARP repeat count.
+ type: int
+
+ health_check:
+ description:
+ - VRRP group health check options.
+ type: dict
+ suboptions:
+ failure_count:
+ description: Allowed number of failed checks.
+ type: int
+ interval:
+ description: Health check interval.
+ type: int
+ ping:
+ description: Host to ping for checks.
+ type: str
+ script:
+ description: Script to execute for health checking.
+ type: str
+
+ hello_source_address:
+ description:
+ - Source address for VRRP hello packets.
+ type: str
+
+ interface:
+ description:
+ - Interface used by the VRRP group.
+ type: str
+
+ no_preempt:
+ description:
+ - Disable preemption.
+ type: bool
+ default: false
+
+ peer_address:
+ description:
+ - Peer VRRP router address.
+ type: str
+
+ preempt_delay:
+ description:
+ - Delay before taking master role.
+ type: int
+
+ priority:
+ description:
+ - VRRP priority (higher = preferred master).
+ type: int
+
+ rfc3768_compatibility:
+ description:
+ - Enable or disable RFC3768 compatibility mode.
+ type: bool
+ default: false
+
+ track:
+ description:
+ - Track interface and VRRP behaviour.
+ type: dict
+ suboptions:
+ exclude_vrrp_interface:
+ description:
+ - Exclude VRRP interface from tracking.
+ type: bool
+ interface:
+ description:
+ - Interface to track.
+ type: list
+ elements: str
+
+ transition_script:
+ description:
+ - Scripts executed during VRRP state transitions.
+ type: dict
+ suboptions:
+ backup:
+ description: Path to backup script.
+ type: str
+ fault:
+ description: Path to fault script.
+ type: str
+ master:
+ description: Path to master script.
+ type: str
+ stop:
+ description: Path to stop script.
+ type: str
+
+ vrid:
+ description:
+ - VRRP Virtual Router ID.
+ type: int
+
+ snmp:
+ description:
+ - Enable SNMP support for VRRP.
+ type: str
+ choices: ['enabled', 'disabled']
+
+ sync_groups:
+ description:
+ - VRRP sync-groups for coordinated failover.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Sync-group name.
+ type: str
+ required: true
+
+ health_check:
+ description:
+ - Health check options for sync group.
+ type: dict
+ suboptions:
+ failure_count:
+ description: Allowed number of failures.
+ type: int
+ interval:
+ description: Health check interval.
+ type: int
+ ping:
+ description: Host to ping.
+ type: str
+ script:
+ description: Script to run for health checking.
+ type: str
+
+ member:
+ description:
+ - List of VRRP groups participating in this sync group.
+ type: list
+ elements: str
+
+ transition_script:
+ description:
+ - Transition scripts for sync group events.
+ type: dict
+ suboptions:
+ backup:
+ description: Backup state script.
+ type: str
+ fault:
+ description: Fault state script.
+ type: str
+ master:
+ description: Master state script.
+ type: str
+ stop:
+ description: Stop state script.
+ type: str
+
+ state:
+ description:
+ - Desired end state of the VRRP configuration.
+ type: str
+ choices:
+ - deleted
+ - merged
+ - purged
+ - replaced
+ - gathered
+ - rendered
+ - parsed
+ - overridden
+ default: merged
+
+ running_config:
+ description:
+ - Used only when C(state=parsed). Must contain the output of
+ C(show configuration commands | grep high-availability).
+ type: str
+"""
+
+EXAMPLES = """
+# Using merged
+# Before state
+
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# vyos@vyos:~$
+
+- name: Merge provided configuration with device configuration
+ vyos.vyos.vyos_ha:
+ config:
+ disable: true
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.5
+ algorithm: round-robin
+ real_server:
+ - address: 10.10.50.2
+ port: 8443
+ - name: s2
+ address: 10.10.10.2
+ persistence_timeout: 30
+ port: 81
+ protocol: tcp
+ - name: s3
+ address: 10.10.10.3
+ port: 88
+ protocol: udp
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 30
+ garp:
+ master_repeat: 6
+ groups:
+ - name: "g1"
+ peer_address: 192.168.1.3
+ priority: 100
+ disable: false
+ no_preempt: false
+ vrid: 20
+ sync_groups:
+ - name: "sg1"
+ health_check:
+ failure_count: 5
+ state: merged
+
+# After State
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# set high-availability disable
+# set high-availability virtual-server s1 address '10.10.10.5'
+# set high-availability virtual-server s1 algorithm 'round-robin'
+# set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+# set high-availability virtual-server s2 address '10.10.10.2'
+# set high-availability virtual-server s2 persistence-timeout '30'
+# set high-availability virtual-server s2 port '81'
+# set high-availability virtual-server s2 protocol 'tcp'
+# set high-availability virtual-server s3 address '10.10.10.3'
+# set high-availability virtual-server s3 port '88'
+# set high-availability virtual-server s3 protocol 'udp'
+# set high-availability vrrp global-parameters garp master-repeat '6'
+# set high-availability vrrp global-parameters startup-delay '30'
+# set high-availability vrrp group g1 peer-address '192.168.1.3'
+# set high-availability vrrp group g1 priority '100'
+# set high-availability vrrp group g1 vrid '20'
+# set high-availability vrrp snmp
+# set high-availability vrrp sync-group sg1 health-check failure-count '5'
+# vyos@vyos:~$
+#
+# # Module Execution:
+#
+# "after": {
+# "disable": true,
+# "virtual_servers": [
+# {
+# "address": "10.10.10.5",
+# "algorithm": "round-robin",
+# "name": "s1",
+# "real_server": [
+# {
+# "address": "10.10.50.2",
+# "port": 8443
+# }
+# ]
+# },
+# {
+# "address": "10.10.10.2",
+# "name": "s2",
+# "persistence_timeout": 30,
+# "port": 81,
+# "protocol": "tcp"
+# },
+# {
+# "address": "10.10.10.3",
+# "name": "s3",
+# "port": 88,
+# "protocol": "udp"
+# }
+# ],
+# "vrrp": {
+# "global_parameters": {
+# "garp": {
+# "master_repeat": 6
+# },
+# "startup_delay": 30
+# },
+# "groups": [
+# {
+# "disable": false,
+# "name": "g1",
+# "no_preempt": false,
+# "peer_address": "192.168.1.3",
+# "priority": 100,
+# "rfc3768_compatibility": false,
+# "vrid": 20
+# }
+# ],
+# "snmp": "enabled",
+# "sync_groups": [
+# {
+# "health_check": {
+# "failure_count": 5
+# },
+# "name": "sg1"
+# }
+# ]
+# }
+# },
+# "before": {
+# "disable": false
+# },
+# "changed": true,
+# "commands": [
+# "set high-availability disable",
+# "set high-availability virtual-server s1 address 10.10.10.5",
+# "set high-availability virtual-server s1 algorithm round-robin",
+# "set high-availability virtual-server s1 real-server 10.10.50.2 port 8443",
+# "set high-availability virtual-server s2 address 10.10.10.2",
+# "set high-availability virtual-server s2 persistence-timeout 30",
+# "set high-availability virtual-server s2 port 81",
+# "set high-availability virtual-server s2 protocol tcp",
+# "set high-availability virtual-server s3 address 10.10.10.3",
+# "set high-availability virtual-server s3 port 88",
+# "set high-availability virtual-server s3 protocol udp",
+# "set high-availability vrrp global-parameters garp master-repeat 6",
+# "set high-availability vrrp global-parameters startup-delay 30",
+# "set high-availability vrrp group g1 peer-address 192.168.1.3",
+# "set high-availability vrrp group g1 priority 100",
+# "set high-availability vrrp group g1 vrid 20",
+# "set high-availability vrrp snmp",
+# "set high-availability vrrp sync-group sg1 health-check failure-count 5"
+# ],
+
+# Using replaced:
+# --------------
+
+# Before state:
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# set high-availability disable
+# set high-availability virtual-server s1 address '10.10.10.5'
+# set high-availability virtual-server s1 algorithm 'round-robin'
+# set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+# set high-availability virtual-server s2 address '10.10.10.2'
+# set high-availability virtual-server s2 persistence-timeout '30'
+# set high-availability virtual-server s2 port '81'
+# set high-availability virtual-server s2 protocol 'tcp'
+# set high-availability virtual-server s3 address '10.10.10.3'
+# set high-availability virtual-server s3 port '88'
+# set high-availability virtual-server s3 protocol 'udp'
+# set high-availability vrrp global-parameters garp master-repeat '6'
+# set high-availability vrrp global-parameters startup-delay '30'
+# set high-availability vrrp group g1 peer-address '192.168.1.3'
+# set high-availability vrrp group g1 priority '100'
+# set high-availability vrrp group g1 vrid '20'
+# set high-availability vrrp snmp
+# set high-availability vrrp sync-group sg1 health-check failure-count '5'
+# vyos@vyos:~$
+
+- name: Replace
+ vyos.vyos.vyos_ha:
+ config:
+ disable: false
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.3
+ algorithm: round-robin
+ port: 8443
+ real_server:
+ - address: 10.10.50.3
+ port: 8443
+ - name: s2
+ address: 10.10.10.2
+ persistence_timeout: 300
+ port: 81
+ protocol: tcp
+ real_server:
+ - address: 10.10.50.30
+ port: 8443
+ - name: s3
+ address: 10.10.10.3
+ port: 88
+ protocol: udp
+ real_server:
+ - address: 10.10.50.6
+ port: 8443
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 30
+ garp:
+ master_repeat: 6
+ groups:
+ - name: "g1"
+ peer_address: 192.168.1.13
+ priority: 100
+ disable: false
+ no_preempt: true
+ interface: eth1
+ address: 192.168.51.13
+ vrid: 20
+ sync_groups:
+ - name: "sg1"
+ health_check:
+ failure_count: 3
+ state: replaced
+
+# After state:
+
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# set high-availability virtual-server s1 address '10.10.10.3'
+# set high-availability virtual-server s1 algorithm 'round-robin'
+# set high-availability virtual-server s1 port '8443'
+# set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+# set high-availability virtual-server s1 real-server 10.10.50.3 port '8443'
+# set high-availability virtual-server s2 address '10.10.10.2'
+# set high-availability virtual-server s2 persistence-timeout '300'
+# set high-availability virtual-server s2 port '81'
+# set high-availability virtual-server s2 protocol 'tcp'
+# set high-availability virtual-server s2 real-server 10.10.50.3 port '8443'
+# set high-availability virtual-server s3 address '10.10.10.3'
+# set high-availability virtual-server s3 port '88'
+# set high-availability virtual-server s3 protocol 'udp'
+# set high-availability virtual-server s3 real-server 10.10.50.6 port '8443'
+# set high-availability vrrp global-parameters garp master-repeat '6'
+# set high-availability vrrp global-parameters startup-delay '30'
+# set high-availability vrrp group g1 address 192.168.51.13
+# set high-availability vrrp group g1 interface 'eth1'
+# set high-availability vrrp group g1 no-preempt
+# set high-availability vrrp group g1 peer-address '192.168.1.3'
+# set high-availability vrrp group g1 peer-address '192.168.1.13'
+# set high-availability vrrp group g1 priority '100'
+# set high-availability vrrp group g1 vrid '20'
+# set high-availability vrrp snmp
+# set high-availability vrrp sync-group sg1 health-check failure-count '3'
+# vyos@vyos:~$
+#
+#
+# Module Execution:
+#
+# "after": {
+# "disable": false,
+# "virtual_servers": [
+# {
+# "address": "10.10.10.3",
+# "algorithm": "round-robin",
+# "name": "s1",
+# "port": 8443,
+# "real_server": [
+# {
+# "address": "10.10.50.2",
+# "port": 8443
+# },
+# {
+# "address": "10.10.50.3",
+# "port": 8443
+# }
+# ]
+# },
+# {
+# "address": "10.10.10.2",
+# "name": "s2",
+# "persistence_timeout": 300,
+# "port": 81,
+# "protocol": "tcp",
+# "real_server": [
+# {
+# "address": "10.10.50.3",
+# "port": 8443
+# }
+# ]
+# },
+# {
+# "address": "10.10.10.3",
+# "name": "s3",
+# "port": 88,
+# "protocol": "udp",
+# "real_server": [
+# {
+# "address": "10.10.50.6",
+# "port": 8443
+# }
+# ]
+# }
+# ],
+# "vrrp": {
+# "global_parameters": {
+# "garp": {
+# "master_repeat": 6
+# },
+# "startup_delay": 30
+# },
+# "groups": [
+# {
+# "address": "192.168.51.13",
+# "disable": false,
+# "interface": "eth1",
+# "name": "g1",
+# "no_preempt": true,
+# "peer_address": "192.168.1.13",
+# "priority": 100,
+# "rfc3768_compatibility": false,
+# "vrid": 20
+# }
+# ],
+# "snmp": "enabled",
+# "sync_groups": [
+# {
+# "health_check": {
+# "failure_count": 3
+# },
+# "name": "sg1"
+# }
+# ]
+# }
+# },
+# "before": {
+# "disable": true,
+# "virtual_servers": [
+# {
+# "address": "10.10.10.5",
+# "algorithm": "round-robin",
+# "name": "s1",
+# "real_server": [
+# {
+# "address": "10.10.50.2",
+# "port": 8443
+# }
+# ]
+# },
+# {
+# "address": "10.10.10.2",
+# "name": "s2",
+# "persistence_timeout": 30,
+# "port": 81,
+# "protocol": "tcp"
+# },
+# {
+# "address": "10.10.10.3",
+# "name": "s3",
+# "port": 88,
+# "protocol": "udp"
+# }
+# ],
+# "vrrp": {
+# "global_parameters": {
+# "garp": {
+# "master_repeat": 6
+# },
+# "startup_delay": 30
+# },
+# "groups": [
+# {
+# "disable": false,
+# "name": "g1",
+# "no_preempt": false,
+# "peer_address": "192.168.1.3",
+# "priority": 100,
+# "rfc3768_compatibility": false,
+# "vrid": 20
+# }
+# ],
+# "snmp": "enabled",
+# "sync_groups": [
+# {
+# "health_check": {
+# "failure_count": 5
+# },
+# "name": "sg1"
+# }
+# ]
+# }
+# },
+# "changed": true,
+# "commands": [
+# "delete high-availability disable",
+# "set high-availability virtual-server s1 address 10.10.10.3",
+# "set high-availability virtual-server s1 port 8443",
+# "set high-availability virtual-server s1 real-server 10.10.50.3 port 8443",
+# "set high-availability virtual-server s2 persistence-timeout 300",
+# "set high-availability virtual-server s2 real-server 10.10.50.3 port 8443",
+# "set high-availability virtual-server s3 real-server 10.10.50.6 port 8443",
+# "set high-availability vrrp group g1 address 192.168.51.13",
+# "set high-availability vrrp group g1 interface eth1",
+# "set high-availability vrrp group g1 no-preempt",
+# "set high-availability vrrp group g1 peer-address 192.168.1.13",
+# "set high-availability vrrp sync-group sg1 health-check failure-count 3"
+# ],
+
+# Using deleted:
+# -------------
+
+# Before state:
+
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# set high-availability disable
+# set high-availability virtual-server s1 address '10.10.10.5'
+# set high-availability virtual-server s1 algorithm 'round-robin'
+# set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+# set high-availability virtual-server s2 address '10.10.10.2'
+# set high-availability virtual-server s2 persistence-timeout '30'
+# set high-availability virtual-server s2 port '81'
+# set high-availability virtual-server s2 protocol 'tcp'
+# set high-availability virtual-server s3 address '10.10.10.3'
+# set high-availability virtual-server s3 port '88'
+# set high-availability virtual-server s3 protocol 'udp'
+# set high-availability vrrp global-parameters garp master-repeat '6'
+# set high-availability vrrp global-parameters startup-delay '30'
+# set high-availability vrrp group g1 peer-address '192.168.1.3'
+# set high-availability vrrp group g1 priority '100'
+# set high-availability vrrp group g1 vrid '20'
+# set high-availability vrrp snmp
+# set high-availability vrrp sync-group sg1 health-check failure-count '5'
+# vyos@vyos:~$
+
+- name: Delete configuration
+ vyos.vyos.vyos_ha:
+ config:
+ disable: false
+ vrrp:
+ snmp: disabled
+ global_parameters:
+ startup_delay: 32
+ version: 3
+ virtual_servers:
+ - name: 's1'
+ address: '10.10.10.1'
+ algorithm: 'round-robin'
+ delay_loop: 60
+ forward_method: 'direct'
+ persistence_timeout: 30
+ port: 443
+ protocol: 'tcp'
+ real_server:
+ - address: '10.10.10.1'
+ connection_timeout: 61
+ port: 443
+ state: deleted
+
+# After state:
+
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# set high-availability disable
+# set high-availability virtual-server s2 address '10.10.10.2'
+# set high-availability virtual-server s2 persistence-timeout '30'
+# set high-availability virtual-server s2 port '81'
+# set high-availability virtual-server s2 protocol 'tcp'
+# set high-availability virtual-server s3 address '10.10.10.3'
+# set high-availability virtual-server s3 port '88'
+# set high-availability virtual-server s3 protocol 'udp'
+# set high-availability vrrp global-parameters garp master-repeat '6'
+# set high-availability vrrp group g1 peer-address '192.168.1.3'
+# set high-availability vrrp group g1 priority '100'
+# set high-availability vrrp group g1 vrid '20'
+# set high-availability vrrp snmp
+# set high-availability vrrp sync-group sg1 health-check failure-count '5'
+
+# vyos@vyos:~$
+#
+#
+# Module Execution:
+#
+# "after": {
+# "disable": true,
+# "virtual_servers": [
+# {
+# "address": "10.10.10.2",
+# "name": "s2",
+# "persistence_timeout": 30,
+# "port": 81,
+# "protocol": "tcp"
+# },
+# {
+# "address": "10.10.10.3",
+# "name": "s3",
+# "port": 88,
+# "protocol": "udp"
+# }
+# ],
+# "vrrp": {
+# "global_parameters": {
+# "garp": {
+# "master_repeat": 6
+# }
+# },
+# "groups": [
+# {
+# "disable": false,
+# "name": "g1",
+# "no_preempt": false,
+# "peer_address": "192.168.1.3",
+# "priority": 100,
+# "rfc3768_compatibility": false,
+# "vrid": 20
+# }
+# ],
+# "snmp": "enabled",
+# "sync_groups": [
+# {
+# "health_check": {
+# "failure_count": 5
+# },
+# "name": "sg1"
+# }
+# ]
+# }
+# },
+# "before": {
+# "disable": true,
+# "virtual_servers": [
+# {
+# "address": "10.10.10.5",
+# "algorithm": "round-robin",
+# "name": "s1",
+# "real_server": [
+# {
+# "address": "10.10.50.2",
+# "port": 8443
+# }
+# ]
+# },
+# {
+# "address": "10.10.10.2",
+# "name": "s2",
+# "persistence_timeout": 30,
+# "port": 81,
+# "protocol": "tcp"
+# },
+# {
+# "address": "10.10.10.3",
+# "name": "s3",
+# "port": 88,
+# "protocol": "udp"
+# }
+# ],
+# "vrrp": {
+# "global_parameters": {
+# "garp": {
+# "master_repeat": 6
+# },
+# "startup_delay": 30
+# },
+# "groups": [
+# {
+# "disable": false,
+# "name": "g1",
+# "no_preempt": false,
+# "peer_address": "192.168.1.3",
+# "priority": 100,
+# "rfc3768_compatibility": false,
+# "vrid": 20
+# }
+# ],
+# "snmp": "enabled",
+# "sync_groups": [
+# {
+# "health_check": {
+# "failure_count": 5
+# },
+# "name": "sg1"
+# }
+# ]
+# }
+# },
+# "changed": true,
+# "commands": [
+# "delete high-availability virtual-server s1",
+# "delete high-availability vrrp global-parameters startup-delay"
+# ],
+
+# Using purged:
+
+# Before state:
+
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# set high-availability disable
+# set high-availability virtual-server s2 address '10.10.10.2'
+# set high-availability virtual-server s2 persistence-timeout '30'
+# set high-availability virtual-server s2 port '81'
+# set high-availability virtual-server s2 protocol 'tcp'
+# set high-availability virtual-server s3 address '10.10.10.3'
+# set high-availability virtual-server s3 port '88'
+# set high-availability virtual-server s3 protocol 'udp'
+# set high-availability vrrp global-parameters garp master-repeat '6'
+# set high-availability vrrp group g1 peer-address '192.168.1.3'
+# set high-availability vrrp group g1 priority '100'
+# set high-availability vrrp group g1 vrid '20'
+# set high-availability vrrp snmp
+# set high-availability vrrp sync-group sg1 health-check failure-count '5'
+# vyos@vyos:~$
+
+
+- name: Purge configuration
+ vyos.vyos.vyos_ha:
+ config:
+ state: purged
+
+# After state:
+
+# vyos@vyos:~$ show configuration commands | match "set high-availability"
+# vyos@vyos:~$
+#
+# Module Execution:
+#
+# "after": {
+# "disable": false
+# },
+# "before": {
+# "disable": true,
+# "virtual_servers": [
+# {
+# "address": "10.10.10.2",
+# "name": "s2",
+# "persistence_timeout": 30,
+# "port": 81,
+# "protocol": "tcp"
+# },
+# {
+# "address": "10.10.10.3",
+# "name": "s3",
+# "port": 88,
+# "protocol": "udp"
+# }
+# ],
+# "vrrp": {
+# "global_parameters": {
+# "garp": {
+# "master_repeat": 6
+# }
+# },
+# "groups": [
+# {
+# "disable": false,
+# "name": "g1",
+# "no_preempt": false,
+# "peer_address": "192.168.1.3",
+# "priority": 100,
+# "rfc3768_compatibility": false,
+# "vrid": 20
+# }
+# ],
+# "snmp": "enabled",
+# "sync_groups": [
+# {
+# "health_check": {
+# "failure_count": 5
+# },
+# "name": "sg1"
+# }
+# ]
+# }
+# },
+# "changed": true,
+# "commands": [
+# "delete high-availability"
+# ],
+
+
+# using gathered:
+# --------------
+
+# Before state:
+# vyos@vyos:~$
+# show configuration commands | match "set high-availability"
+# set high-availability disable
+# set high-availability virtual-server s1 address '10.10.10.5'
+# set high-availability virtual-server s1 algorithm 'round-robin'
+# set high-availability virtual-server s1 real-server 10.10.50.2 port '8443'
+# set high-availability virtual-server s2 address '10.10.10.2'
+# set high-availability virtual-server s2 persistence-timeout '30'
+# set high-availability virtual-server s2 port '81'
+# set high-availability virtual-server s2 protocol 'tcp'
+# set high-availability virtual-server s3 address '10.10.10.3'
+# set high-availability virtual-server s3 port '88'
+# set high-availability virtual-server s3 protocol 'udp'
+# set high-availability vrrp global-parameters garp master-repeat '6'
+# set high-availability vrrp global-parameters startup-delay '30'
+# set high-availability vrrp group g1 peer-address '192.168.1.3'
+# set high-availability vrrp group g1 priority '100'
+# set high-availability vrrp group g1 vrid '20'
+# set high-availability vrrp snmp
+# set high-availability vrrp sync-group sg1 health-check failure-count '5'
+# vyos@vyos:~$
+
+- name: gather configs
+ vyos.vyos.vyos_ha:
+ state: gathered
+
+# Module Execution:
+# "changed": false,
+# "gathered": {
+# "disable": true,
+# "virtual_servers": [
+# {
+# "address": "10.10.10.5",
+# "algorithm": "round-robin",
+# "name": "s1",
+# "real_server": [
+# {
+# "address": "10.10.50.2",
+# "port": 8443
+# }
+# ]
+# },
+# {
+# "address": "10.10.10.2",
+# "name": "s2",
+# "persistence_timeout": 30,
+# "port": 81,
+# "protocol": "tcp"
+# },
+# {
+# "address": "10.10.10.3",
+# "name": "s3",
+# "port": 88,
+# "protocol": "udp"
+# }
+# ],
+# "vrrp": {
+# "global_parameters": {
+# "garp": {
+# "master_repeat": 6
+# },
+# "startup_delay": 30
+# },
+# "groups": [
+# {
+# "disable": false,
+# "name": "g1",
+# "no_preempt": false,
+# "peer_address": "192.168.1.3",
+# "priority": 100,
+# "rfc3768_compatibility": false,
+# "vrid": 20
+# }
+# ],
+# "snmp": "enabled",
+# "sync_groups": [
+# {
+# "health_check": {
+# "failure_count": 5
+# },
+# "name": "sg1"
+# }
+# ]
+# }
+# },
+#
+
+# Using parsed:
+# ------------
+
+# parsed.cfg
+# set high-availability vrrp group g1 interface eth2
+# set high-availability vrrp group g1 address 1.1.1.1
+# set high-availability vrrp group g1 disable
+# set high-availability vrrp group g1 no-preempt
+# set high-availability vrrp group g1 advertise-interval 10
+# set high-availability vrrp group g1 peer-address 2.2.2.2
+# set high-availability vrrp group g1 rfc3768-compatibility
+# set high-availability vrrp group g1 vrid 20
+
+- name: parse configs
+ vyos.vyos.vyos_ha:
+ running_config: "{{ lookup('file', './parsed.cfg') }}"
+ state: parsed
+
+# Module execution:
+# "parsed": {
+# "disable": false,
+# "vrrp": {
+# "groups": [
+# {
+# "address": "1.1.1.1",
+# "advertise_interval": 10,
+# "disable": true,
+# "interface": "eth2",
+# "name": "g1",
+# "no_preempt": true,
+# "peer_address": "2.2.2.2",
+# "rfc3768_compatibility": true,
+# "vrid": 20
+# }
+# ]
+# }
+# }
+#
+
+# Using rendered:
+# --------------
+
+- name: Render
+ vyos.vyos.vyos_ha:
+ config:
+ disable: true
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 32
+ version: 3
+ garp:
+ interval: 30
+ master_delay: 11
+ master_refresh: 100
+ master_refresh_repeat: 200
+ master_repeat: 5
+ state: rendered
+
+# Module Execution:
+# "rendered": [
+# "set high-availability disable",
+# "set high-availability vrrp global-parameters garp interval 30",
+# "set high-availability vrrp global-parameters garp master-delay 11",
+# "set high-availability vrrp global-parameters garp master-refresh 100",
+# "set high-availability vrrp global-parameters garp master-refresh-repeat 200",
+# "set high-availability vrrp global-parameters garp master-repeat 5",
+# "set high-availability vrrp global-parameters startup-delay 32",
+# "set high-availability vrrp global-parameters version 3",
+# "set high-availability vrrp snmp"
+# ]
+"""
+
+RETURN = """
+before:
+ description: The configuration prior to the module execution.
+ returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged)
+ type: dict
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+after:
+ description: The resulting configuration after module execution.
+ returned: when changed
+ type: dict
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged)
+ type: list
+ sample:
+ - set high-availability vrrp group g1 address '1.1.1.1'
+ - set high-availability vrrp group g1 advertise-interval '10'
+ - set high-availability vrrp group g1 description 'Group 1'
+rendered:
+ description: The provided configuration in the task rendered in device-native format (offline).
+ returned: when I(state) is C(rendered)
+ type: list
+ sample:
+ - set high-availability vrrp global-parameters garp master-delay '10'
+ - set high-availability vrrp global-parameters garp master-refresh '100'
+ - set high-availability vrrp global-parameters garp master-refresh-repeat '200'
+gathered:
+ description: Facts about the network resource gathered from the remote device as structured data.
+ returned: when I(state) is C(gathered)
+ type: list
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+parsed:
+ description: The device native config provided in I(running_config) option parsed into structured data as per module argspec.
+ returned: when I(state) is C(parsed)
+ type: list
+ sample: >
+ This output will always be in the same format as the
+ module argspec.
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.ha.ha import (
+ HaArgs,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ha.ha import (
+ Ha,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=HaArgs.argument_spec,
+ mutually_exclusive=[["config", "running_config"]],
+ required_if=[
+ ["state", "merged", ["config"]],
+ ["state", "replaced", ["config"]],
+ ["state", "overridden", ["config"]],
+ ["state", "rendered", ["config"]],
+ ["state", "parsed", ["running_config"]],
+ ],
+ supports_check_mode=True,
+ )
+
+ result = Ha(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/integration/targets/vyos_ha/defaults/main.yaml b/tests/integration/targets/vyos_ha/defaults/main.yaml
new file mode 100644
index 00000000..164afead
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: "[^_].*"
+test_items: []
diff --git a/tests/integration/targets/vyos_ha/tasks/cli.yaml b/tests/integration/targets/vyos_ha/tasks/cli.yaml
new file mode 100644
index 00000000..daccf720
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tasks/cli.yaml
@@ -0,0 +1,20 @@
+---
+- name: Collect all cli test cases
+ ansible.builtin.find:
+ paths: "{{ role_path }}/tests/cli"
+ 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=ansible.netcommon.network_cli)
+ ansible.builtin.include_tasks: "{{ test_case_to_run }}"
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
+ with_items: "{{ test_items }}"
+ loop_control:
+ loop_var: test_case_to_run
diff --git a/tests/integration/targets/vyos_ha/tasks/main.yaml b/tests/integration/targets/vyos_ha/tasks/main.yaml
new file mode 100644
index 00000000..e6378581
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tasks/main.yaml
@@ -0,0 +1,5 @@
+---
+- name: Run CLI tests
+ ansible.builtin.include_tasks: cli.yaml
+ tags:
+ - network_cli
diff --git a/tests/integration/targets/vyos_ha/tests/cli/_add_dummy_scripts.yaml b/tests/integration/targets/vyos_ha/tests/cli/_add_dummy_scripts.yaml
new file mode 100644
index 00000000..60a44758
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/_add_dummy_scripts.yaml
@@ -0,0 +1,6 @@
+---
+- name: Create dummy scripts for HA tests
+ vyos.vyos.vyos_command:
+ commands: touch /var/tmp/script.sh && chmod +x /var/tmp/script.sh
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
diff --git a/tests/integration/targets/vyos_ha/tests/cli/_parsed.cfg b/tests/integration/targets/vyos_ha/tests/cli/_parsed.cfg
new file mode 100644
index 00000000..d003d106
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/_parsed.cfg
@@ -0,0 +1,69 @@
+set high-availability virtual-server s1 address 10.10.10.1
+set high-availability virtual-server s1 algorithm round-robin
+set high-availability virtual-server s1 delay-loop 60
+set high-availability virtual-server s1 forward-method direct
+set high-availability virtual-server s1 fwmark 10
+set high-availability virtual-server s1 persistence-timeout 30
+set high-availability virtual-server s1 protocol tcp
+set high-availability virtual-server s1 real-server 10.10.50.1 health-check script /var/tmp/script.sh
+set high-availability virtual-server s1 real-server 10.10.50.1 port 443
+set high-availability virtual-server s2 address 10.10.10.2
+set high-availability virtual-server s2 port 81
+set high-availability virtual-server s2 real-server real1 connection-timeout 5
+set high-availability virtual-server s2 real-server real1 port 8081
+set high-availability virtual-server s2 real-server real2 port 8080
+set high-availability vrrp global-parameters startup-delay 31
+set high-availability vrrp global-parameters version 3
+set high-availability vrrp global-parameters garp interval 30
+set high-availability vrrp global-parameters garp master-delay 10
+set high-availability vrrp global-parameters garp master-refresh 100
+set high-availability vrrp global-parameters garp master-refresh-repeat 200
+set high-availability vrrp global-parameters garp master-repeat 5
+set high-availability vrrp group g1 address 1.1.1.1
+set high-availability vrrp group g1 address 5.5.5.5
+set high-availability vrrp group g1 address 8.8.8.8 interface eth2
+set high-availability vrrp group g1 advertise-interval 10
+set high-availability vrrp group g1 description 'Group_1'
+set high-availability vrrp group g1 disable
+set high-availability vrrp group g1 interface eth2
+set high-availability vrrp group g1 no-preempt
+set high-availability vrrp group g1 peer-address 192.168.1.3
+set high-availability vrrp group g1 preempt-delay 15
+set high-availability vrrp group g1 priority 100
+set high-availability vrrp group g1 rfc3768-compatibility
+set high-availability vrrp group g1 vrid 20
+set high-availability vrrp group g1 authentication password testpass
+set high-availability vrrp group g1 authentication type plaintext-password
+set high-availability vrrp group g1 excluded-address 192.168.1.7 interface eth3
+set high-availability vrrp group g1 excluded-address 192.168.1.8
+set high-availability vrrp group g1 garp interval 20
+set high-availability vrrp group g1 garp master-delay 5
+set high-availability vrrp group g1 garp master-refresh 50
+set high-availability vrrp group g1 garp master-refresh-repeat 100
+set high-availability vrrp group g1 garp master-repeat 3
+set high-availability vrrp group g1 track exclude-vrrp-interface
+set high-availability vrrp group g1 track interface eth0
+set high-availability vrrp group g1 track interface eth1
+set high-availability vrrp group g1 transition-script backup /var/tmp/script.sh
+set high-availability vrrp group g1 transition-script fault /var/tmp/script.sh
+set high-availability vrrp group g1 transition-script master /var/tmp/script.sh
+set high-availability vrrp group g1 transition-script stop /var/tmp/script.sh
+set high-availability vrrp group g2 address 2.2.2.2
+set high-availability vrrp group g2 description 'Group_2'
+set high-availability vrrp group g2 interface eth1
+set high-availability vrrp group g2 vrid 11
+set high-availability vrrp group g2 health-check failure-count 5
+set high-availability vrrp group g2 health-check interval 15
+set high-availability vrrp group g2 health-check ping 192.168.1.100
+set high-availability vrrp group g2 health-check script /var/tmp/script.sh
+set high-availability vrrp group g2 hello-source-address 2.2.2.2
+set high-availability vrrp sync-group sg1 health-check failure-count 3
+set high-availability vrrp sync-group sg1 health-check interval 10
+set high-availability vrrp sync-group sg1 health-check ping 192.168.2.100
+set high-availability vrrp sync-group sg1 health-check script /var/tmp/script.sh
+set high-availability vrrp sync-group sg1 member g1
+set high-availability vrrp sync-group sg1 member g2
+set high-availability vrrp sync-group sg1 transition-script backup /var/tmp/script.sh
+set high-availability vrrp sync-group sg1 transition-script fault /var/tmp/script.sh
+set high-availability vrrp sync-group sg1 transition-script master /var/tmp/script.sh
+set high-availability vrrp sync-group sg1 transition-script stop /var/tmp/script.sh
diff --git a/tests/integration/targets/vyos_ha/tests/cli/_populate_config.yaml b/tests/integration/targets/vyos_ha/tests/cli/_populate_config.yaml
new file mode 100644
index 00000000..95e81752
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/_populate_config.yaml
@@ -0,0 +1,8 @@
+---
+- ansible.builtin.include_tasks: _add_dummy_scripts.yaml
+
+- name: Setup initial HA configuration
+ vyos.vyos.vyos_config:
+ lines: "{{ populate_commands}}"
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
diff --git a/tests/integration/targets/vyos_ha/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_ha/tests/cli/_remove_config.yaml
new file mode 100644
index 00000000..416e5c4d
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/_remove_config.yaml
@@ -0,0 +1,15 @@
+---
+- name: Remove pre-existing HA config
+ vyos.vyos.vyos_ha:
+ config:
+ state: purged
+ ignore_errors: true
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
+
+- name: Remove dummy scripts for HA tests
+ vyos.vyos.vyos_command:
+ commands:
+ - rm -f /var/tmp/script.sh
+ vars:
+ ansible_connection: ansible.netcommon.network_cli
diff --git a/tests/integration/targets/vyos_ha/tests/cli/deleted_all.yaml b/tests/integration/targets/vyos_ha/tests/cli/deleted_all.yaml
new file mode 100644
index 00000000..f97e8802
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/deleted_all.yaml
@@ -0,0 +1,44 @@
+---
+- debug:
+ msg: Start vyos_ha purged / deleted_all integration tests ansible_connection={{ ansible_connection }}
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Purge all the HA configuration.
+ register: result
+ vyos.vyos.vyos_ha: &id001
+ config:
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "merged.before == result['before']"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ purged.commands | symmetric_difference(result['commands']) | length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "purged.after == result['after']"
+
+ - name: Purge all the HA configuration. (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ha: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands | length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "purged.after == result['before']"
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/deleted_bulk.yaml b/tests/integration/targets/vyos_ha/tests/cli/deleted_bulk.yaml
new file mode 100644
index 00000000..52a6477b
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/deleted_bulk.yaml
@@ -0,0 +1,50 @@
+---
+- debug:
+ msg: Start vyos_ha deleted integration tests ansible_connection={{ ansible_connection }}
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Delete stub objects.
+ register: result
+ vyos.vyos.vyos_ha: &id001
+ config:
+ disable: false
+ vrrp:
+ global_parameters:
+ startup_delay: 32
+ garp: {}
+ groups:
+ - name: g1
+ sync_groups: []
+ virtual_servers: []
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ # - "{{ merged.before | symmetric_difference(result['before']) | length == 0 }}"
+ - "merged.before == result['before']"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_bulk['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "{{ deleted_bulk['after'] | symmetric_difference(result['after']) | length == 0 }}"
+
+ - name: Delete stub objects (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ha: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands | length == 0
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/deleted_vrrp_groups.yaml b/tests/integration/targets/vyos_ha/tests/cli/deleted_vrrp_groups.yaml
new file mode 100644
index 00000000..b84b6905
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/deleted_vrrp_groups.yaml
@@ -0,0 +1,45 @@
+---
+- debug:
+ msg: Start vyos_ha deleted integration tests ansible_connection={{ ansible_connection }}
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Delete a vrrp group and sync_group based on its name.
+ register: result
+ vyos.vyos.vyos_ha: &id001
+ config:
+ vrrp:
+ groups:
+ - name: "g1"
+ sync_groups:
+ - name: "sg1"
+ state: deleted
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "merged.before == result['before']"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ deleted_vrrp_groups['commands'] | symmetric_difference(result['commands']) |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "deleted_vrrp_groups['after'] == result['after']"
+
+ - name: Delete attributes of group (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ha: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands|length == 0
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_ha/tests/cli/empty_config.yaml
new file mode 100644
index 00000000..676d2c66
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/empty_config.yaml
@@ -0,0 +1,58 @@
+---
+- debug:
+ msg: START vyos_ha empty_config integration tests on connection={{ ansible_connection }}
+
+- name: Merged with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ha:
+ config:
+ state: merged
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state merged'
+
+- name: Replaced with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ha:
+ config:
+ state: replaced
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state replaced'
+
+- name: Overridden with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ha:
+ config:
+ state: overridden
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state overridden'
+
+- name: Parsed with empty running_config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ha:
+ running_config:
+ state: parsed
+
+- assert:
+ that:
+ - result.msg == 'value of running_config parameter must not be empty for state parsed'
+
+- name: Rendered with empty config should give appropriate error message
+ register: result
+ ignore_errors: true
+ vyos.vyos.vyos_ha:
+ config:
+ state: rendered
+
+- assert:
+ that:
+ - result.msg == 'value of config parameter must not be empty for state rendered'
diff --git a/tests/integration/targets/vyos_ha/tests/cli/gathered.yaml b/tests/integration/targets/vyos_ha/tests/cli/gathered.yaml
new file mode 100644
index 00000000..da39727a
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/gathered.yaml
@@ -0,0 +1,25 @@
+---
+- debug:
+ msg: START vyos_ha gathered integration tests on connection={{ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Gather config from the device in structured format.
+ register: result
+ vyos.vyos.vyos_ha:
+ state: gathered
+
+ - vyos.vyos.vyos_facts:
+ gather_network_resources: ha
+
+ - name: Assert that facts are correctly generated
+ assert:
+ that:
+ - result.changed == false
+ - "result.gathered == ansible_facts['network_resources']['ha']"
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/merged.yaml b/tests/integration/targets/vyos_ha/tests/cli/merged.yaml
new file mode 100644
index 00000000..cb870d01
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/merged.yaml
@@ -0,0 +1,42 @@
+---
+- debug:
+ msg: START vyos_ha merged integration tests on connection={{ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Merge the provided configuration with the existing running configuration
+ register: result
+ vyos.vyos.vyos_ha: &id001
+ config: "{{ merged.config }}"
+ state: merged
+
+ - vyos.vyos.vyos_facts:
+ gather_network_resources: ha
+
+ - assert:
+ that:
+ - result.changed == true
+ - result.commands | symmetric_difference(merged.commands) == []
+ - "result.after == ansible_facts['network_resources']['ha']"
+
+ - name: Assert that before dicts were correctly generated
+ assert:
+ that:
+ - "merged.before == result.before"
+
+ - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ha: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ - result['commands'] == []
+ - "result['before'] == ansible_facts['network_resources']['ha']"
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/overridden.yaml b/tests/integration/targets/vyos_ha/tests/cli/overridden.yaml
new file mode 100644
index 00000000..44a85b76
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/overridden.yaml
@@ -0,0 +1,35 @@
+---
+- debug:
+ msg: START vyos_ha overridden integration tests on connection={{ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Override the existing configuration with the provided running configuration
+ register: result
+ vyos.vyos.vyos_ha: &id001
+ config: "{{ overridden.config }}"
+ state: overridden
+
+ - vyos.vyos.vyos_facts:
+ gather_network_resources: ha
+
+ - name: Verify that the configuration was correctly overridden
+ assert:
+ that:
+ - result.changed == true
+ - result.after == ansible_facts['network_resources']['ha']
+
+ - name: Override the existing configuration with the provided running configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ha: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/parsed.yaml b/tests/integration/targets/vyos_ha/tests/cli/parsed.yaml
new file mode 100644
index 00000000..c7134d6c
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/parsed.yaml
@@ -0,0 +1,15 @@
+---
+- debug:
+ msg: START vyos_ha parsed integration tests on connection={{ ansible_connection }}
+
+- name: Provide the running configuration for parsing (config to be parsed)
+ register: result
+ vyos.vyos.vyos_ha:
+ running_config: "{{ lookup('file', '_parsed.cfg') }}"
+ state: parsed
+
+- name: Assert that config was correctly parsed
+ assert:
+ that:
+ - result.changed == false
+ - "result.parsed == merged.before"
diff --git a/tests/integration/targets/vyos_ha/tests/cli/purged.yaml b/tests/integration/targets/vyos_ha/tests/cli/purged.yaml
new file mode 100644
index 00000000..ffc00d64
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/purged.yaml
@@ -0,0 +1,44 @@
+---
+- debug:
+ msg: Start vyos_ha purged / deleted_all integration tests ansible_connection={{ ansible_connection }}
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Purge all the HA configuration.
+ register: result
+ vyos.vyos.vyos_ha: &id001
+ config:
+ state: purged
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "merged.before == result['before']"
+
+ - name: Assert that the correct set of commands were generated
+ assert:
+ that:
+ - "{{ purged.commands | symmetric_difference(result['commands']) |length == 0 }}"
+
+ - name: Assert that the after dicts were correctly generated
+ assert:
+ that:
+ - "purged.after == result['after']"
+
+ - name: Purge all the HA configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ha: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result.changed == false
+ - result.commands | length == 0
+
+ - name: Assert that the before dicts were correctly generated
+ assert:
+ that:
+ - "purged.after == result['before']"
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/rendered.yaml b/tests/integration/targets/vyos_ha/tests/cli/rendered.yaml
new file mode 100644
index 00000000..efece2c6
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/rendered.yaml
@@ -0,0 +1,15 @@
+---
+- debug:
+ msg: START vyos_ha rendered integration tests on connection={{ ansible_connection }}
+
+- block:
+ - name: Structure provided configuration into device specific commands
+ register: result
+ vyos.vyos.vyos_ha:
+ config: "{{ rendered.config }}"
+ state: rendered
+
+ - assert:
+ that:
+ - result.changed == false
+ - "result.rendered | sort == rendered.commands | sort"
diff --git a/tests/integration/targets/vyos_ha/tests/cli/replaced.yaml b/tests/integration/targets/vyos_ha/tests/cli/replaced.yaml
new file mode 100644
index 00000000..d35f3345
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/replaced.yaml
@@ -0,0 +1,37 @@
+---
+- debug:
+ msg: START vyos_ha replaced integration tests on connection={{ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate_config.yaml
+
+- block:
+ - name: Replace the provided configuration with the existing running configuration
+ register: result
+ vyos.vyos.vyos_ha: &id001
+ config: "{{ merged.config }}"
+ state: replaced
+
+ - vyos.vyos.vyos_facts:
+ gather_network_resources: ha
+
+ - assert:
+ that:
+ - result.changed == true
+ - result.commands | symmetric_difference(replaced.commands) == []
+ - "result.after == ansible_facts['network_resources']['ha']"
+
+ - name: Replace the provided configuration with the existing running configuration (IDEMPOTENT)
+ register: result
+ vyos.vyos.vyos_ha: *id001
+
+ - name: Assert that the previous task was idempotent
+ assert:
+ that:
+ - result['changed'] == false
+ - result['commands'] == []
+ - "result['before'] == ansible_facts['network_resources']['ha']"
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/tests/cli/rtt.yaml b/tests/integration/targets/vyos_ha/tests/cli/rtt.yaml
new file mode 100644
index 00000000..06e017be
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/tests/cli/rtt.yaml
@@ -0,0 +1,39 @@
+---
+- debug:
+ msg: START vyos_ha rtt integration tests on connection={{ ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- block:
+ - name: RTT Merge the provided configuration with the existing running configuration
+ register: baseconfig
+ vyos.vyos.vyos_ha:
+ config: "{{ merged.config }}"
+ state: merged
+
+ - vyos.vyos.vyos_facts:
+ gather_network_resources: ha
+
+ - assert:
+ that:
+ - baseconfig.changed == true
+ - baseconfig.commands|symmetric_difference(rtt.commands) == []
+ - "baseconfig.after == ansible_facts['network_resources']['ha']"
+
+ - name: RTT Merge the existing configuration with the provided running configuration
+ register: result
+ vyos.vyos.vyos_ha:
+ config: "{{ rtt.config }}"
+
+ - name: RTT Revert back to base config using facts round trip
+ register: revert
+ vyos.vyos.vyos_ha:
+ config: "{{ ansible_facts['network_resources']['ha'] }}"
+ state: overridden
+
+ - name: RTT Assert that config was reverted
+ assert:
+ that: baseconfig.after == revert.after
+
+ always:
+ - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_ha/vars/main.yaml b/tests/integration/targets/vyos_ha/vars/main.yaml
new file mode 100644
index 00000000..6cf70f2e
--- /dev/null
+++ b/tests/integration/targets/vyos_ha/vars/main.yaml
@@ -0,0 +1,787 @@
+---
+merged:
+ before:
+ disable: false
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.1
+ algorithm: "round-robin"
+ delay_loop: 60
+ forward_method: direct
+ fwmark: 10
+ persistence_timeout: 30
+ protocol: tcp
+ real_server:
+ - address: 10.10.50.1
+ health_check_script: "/var/tmp/script.sh"
+ port: 443
+ - name: s2
+ address: 10.10.10.2
+ port: 81
+ real_server:
+ - address: real1
+ connection_timeout: 5
+ port: 8081
+ - address: real2
+ port: 8080
+ vrrp:
+ global_parameters:
+ startup_delay: 31
+ version: "3"
+ garp:
+ interval: 30
+ master_delay: 10
+ master_refresh: 100
+ master_refresh_repeat: 200
+ master_repeat: 5
+ groups:
+ - name: "g1"
+ description: "Group_1"
+ interface: "eth2"
+ address:
+ - "1.1.1.1"
+ - "5.5.5.5"
+ - "8.8.8.8 interface eth2"
+ advertise_interval: 10
+ peer_address: 192.168.1.3
+ preempt_delay: 15
+ priority: 100
+ disable: true
+ no_preempt: true
+ rfc3768_compatibility: true
+ vrid: 20
+ excluded_address:
+ - "192.168.1.7 interface eth3"
+ - "192.168.1.8"
+ garp:
+ interval: 20
+ master_delay: 5
+ master_refresh: 50
+ master_refresh_repeat: 100
+ master_repeat: 3
+ authentication:
+ type: "plaintext-password"
+ password: "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER"
+ transition_script:
+ master: "/var/tmp/script.sh"
+ backup: "/var/tmp/script.sh"
+ fault: "/var/tmp/script.sh"
+ stop: "/var/tmp/script.sh"
+ track:
+ exclude_vrrp_interface: true
+ interface:
+ - eth0
+ - eth1
+ - name: "g2"
+ description: "Group_2"
+ interface: "eth1"
+ address:
+ - "2.2.2.2"
+ disable: false
+ no_preempt: false
+ rfc3768_compatibility: false
+ vrid: 11
+ health_check:
+ failure_count: 5
+ interval: 15
+ ping: "192.168.1.100"
+ script: "/var/tmp/script.sh"
+ hello_source_address: "2.2.2.2"
+ sync_groups:
+ - name: "sg1"
+ member:
+ - "g1"
+ - "g2"
+ transition_script:
+ master: "/var/tmp/script.sh"
+ backup: "/var/tmp/script.sh"
+ fault: "/var/tmp/script.sh"
+ stop: "/var/tmp/script.sh"
+ health_check:
+ failure_count: 3
+ interval: 10
+ ping: "192.168.2.100"
+ script: "/var/tmp/script.sh"
+ config:
+ disable: true
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.5
+ algorithm: round-robin
+ real_server:
+ - address: 10.10.50.2
+ port: 8443
+ - name: s2
+ address: 10.10.10.2
+ persistence_timeout: 30
+ port: 81
+ protocol: tcp
+ - name: s3
+ address: 10.10.10.3
+ port: 88
+ protocol: udp
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 30
+ garp:
+ master_repeat: 6
+ groups:
+ - name: "g1"
+ peer_address: 192.168.1.3
+ priority: 100
+ disable: false
+ no_preempt: false
+ vrid: 20
+ - name: "g2"
+ address:
+ - "4.4.4.4"
+ - "6.6.6.6 interface eth3"
+ sync_groups:
+ - name: "sg1"
+ health_check:
+ failure_count: 5
+ commands:
+ - delete high-availability vrrp group g1 disable
+ - delete high-availability vrrp group g1 no-preempt
+ - delete high-availability vrrp group g1 rfc3768-compatibility
+ - set high-availability disable
+ - set high-availability virtual-server s1 address 10.10.10.5
+ - set high-availability virtual-server s1 real-server 10.10.50.2 port 8443
+ - set high-availability virtual-server s2 persistence-timeout 30
+ - set high-availability virtual-server s2 protocol tcp
+ - set high-availability virtual-server s3 address 10.10.10.3
+ - set high-availability virtual-server s3 port 88
+ - set high-availability virtual-server s3 protocol udp
+ - set high-availability vrrp global-parameters garp master-repeat 6
+ - set high-availability vrrp global-parameters startup-delay 30
+ - set high-availability vrrp group g2 address 2.2.2.2
+ - set high-availability vrrp group g2 address 4.4.4.4
+ - set high-availability vrrp group g2 address 6.6.6.6 interface eth3
+ - set high-availability vrrp snmp
+ - set high-availability vrrp sync-group sg1 health-check failure-count 5
+
+populate_commands:
+ - set high-availability virtual-server s1 address 10.10.10.1
+ - set high-availability virtual-server s1 algorithm round-robin
+ - set high-availability virtual-server s1 delay-loop 60
+ - set high-availability virtual-server s1 forward-method direct
+ - set high-availability virtual-server s1 fwmark 10
+ - set high-availability virtual-server s1 persistence-timeout 30
+ - set high-availability virtual-server s1 protocol tcp
+ - set high-availability virtual-server s1 real-server 10.10.50.1 health-check script /var/tmp/script.sh
+ - set high-availability virtual-server s1 real-server 10.10.50.1 port 443
+ - set high-availability virtual-server s2 address 10.10.10.2
+ - set high-availability virtual-server s2 port 81
+ - set high-availability virtual-server s2 real-server real1 connection-timeout 5
+ - set high-availability virtual-server s2 real-server real1 port 8081
+ - set high-availability virtual-server s2 real-server real2 port 8080
+ - set high-availability vrrp global-parameters startup-delay 31
+ - set high-availability vrrp global-parameters version 3
+ - set high-availability vrrp global-parameters garp interval 30
+ - set high-availability vrrp global-parameters garp master-delay 10
+ - set high-availability vrrp global-parameters garp master-refresh 100
+ - set high-availability vrrp global-parameters garp master-refresh-repeat 200
+ - set high-availability vrrp global-parameters garp master-repeat 5
+ - set high-availability vrrp group g1 address 1.1.1.1
+ - set high-availability vrrp group g1 address 5.5.5.5
+ - set high-availability vrrp group g1 address 8.8.8.8 interface eth2
+ - set high-availability vrrp group g1 advertise-interval 10
+ - set high-availability vrrp group g1 description 'Group_1'
+ - set high-availability vrrp group g1 disable
+ - set high-availability vrrp group g1 interface eth2
+ - set high-availability vrrp group g1 no-preempt
+ - set high-availability vrrp group g1 peer-address 192.168.1.3
+ - set high-availability vrrp group g1 preempt-delay 15
+ - set high-availability vrrp group g1 priority 100
+ - set high-availability vrrp group g1 rfc3768-compatibility
+ - set high-availability vrrp group g1 vrid 20
+ - set high-availability vrrp group g1 authentication password testpass
+ - set high-availability vrrp group g1 authentication type plaintext-password
+ - set high-availability vrrp group g1 excluded-address 192.168.1.7 interface eth3
+ - set high-availability vrrp group g1 excluded-address 192.168.1.8
+ - set high-availability vrrp group g1 garp interval 20
+ - set high-availability vrrp group g1 garp master-delay 5
+ - set high-availability vrrp group g1 garp master-refresh 50
+ - set high-availability vrrp group g1 garp master-refresh-repeat 100
+ - set high-availability vrrp group g1 garp master-repeat 3
+ - set high-availability vrrp group g1 track exclude-vrrp-interface
+ - set high-availability vrrp group g1 track interface eth0
+ - set high-availability vrrp group g1 track interface eth1
+ - set high-availability vrrp group g1 transition-script backup /var/tmp/script.sh
+ - set high-availability vrrp group g1 transition-script fault /var/tmp/script.sh
+ - set high-availability vrrp group g1 transition-script master /var/tmp/script.sh
+ - set high-availability vrrp group g1 transition-script stop /var/tmp/script.sh
+ - set high-availability vrrp group g2 address 2.2.2.2
+ - set high-availability vrrp group g2 description 'Group_2'
+ - set high-availability vrrp group g2 interface eth1
+ - set high-availability vrrp group g2 vrid 11
+ - set high-availability vrrp group g2 health-check failure-count 5
+ - set high-availability vrrp group g2 health-check interval 15
+ - set high-availability vrrp group g2 health-check ping 192.168.1.100
+ - set high-availability vrrp group g2 health-check script /var/tmp/script.sh
+ - set high-availability vrrp group g2 hello-source-address 2.2.2.2
+ - set high-availability vrrp sync-group sg1 health-check failure-count 3
+ - set high-availability vrrp sync-group sg1 health-check interval 10
+ - set high-availability vrrp sync-group sg1 health-check ping 192.168.2.100
+ - set high-availability vrrp sync-group sg1 health-check script /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 member g1
+ - set high-availability vrrp sync-group sg1 member g2
+ - set high-availability vrrp sync-group sg1 transition-script backup /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 transition-script fault /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 transition-script master /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 transition-script stop /var/tmp/script.sh
+
+rendered:
+ config:
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.1
+ algorithm: round-robin
+ delay_loop: 60
+ forward_method: direct
+ fwmark: 10
+ persistence_timeout: 30
+ protocol: tcp
+ real_server:
+ - address: 10.10.50.1
+ health_check_script: /var/tmp/script.sh
+ port: 443
+ - name: s2
+ address: 10.10.10.2
+ port: 81
+ real_server:
+ - address: real1
+ connection_timeout: 5
+ port: 8081
+ - address: real2
+ port: 8080
+ vrrp:
+ snmp: enabled
+ global_parameters:
+ startup_delay: 31
+ version: "3"
+ garp:
+ interval: 30
+ master_delay: 10
+ master_refresh: 100
+ master_refresh_repeat: 200
+ master_repeat: 5
+ groups:
+ - name: "g1"
+ description: "Group_1"
+ interface: "eth2"
+ address:
+ - "1.1.1.1"
+ - "2.2.2.2"
+ - "3.3.3.3 interface eth2"
+ advertise_interval: 10
+ peer_address: 192.168.1.3
+ preempt_delay: 15
+ priority: 100
+ disable: true
+ no_preempt: true
+ rfc3768_compatibility: true
+ vrid: 20
+ excluded_address:
+ - "192.168.1.7 interface eth3"
+ garp:
+ interval: 20
+ master_delay: 5
+ master_refresh: 50
+ master_refresh_repeat: 100
+ master_repeat: 3
+ authentication:
+ type: "plaintext-password"
+ password: "test123"
+ transition_script:
+ master: "/var/tmp/script.sh"
+ backup: "/var/tmp/script.sh"
+ fault: "/var/tmp/script.sh"
+ stop: "/var/tmp/script.sh"
+ track:
+ exclude_vrrp_interface: true
+ interface:
+ - eth0
+ - name: "g2"
+ description: "Group_2"
+ interface: "eth1"
+ address: "2.2.2.2"
+ disable: false
+ no_preempt: false
+ rfc3768_compatibility: false
+ vrid: 11
+ health_check:
+ failure_count: 5
+ interval: 15
+ ping: "192.168.1.100"
+ script: "/var/tmp/script.sh"
+ hello_source_address: "2.2.2.2"
+ sync_groups:
+ - name: "sg1"
+ member:
+ - "g1"
+ transition_script:
+ master: "/var/tmp/script.sh"
+ backup: "/var/tmp/script.sh"
+ fault: "/var/tmp/script.sh"
+ stop: "/var/tmp/script.sh"
+ health_check:
+ failure_count: 3
+ interval: 10
+ ping: "192.168.2.100"
+ script: "/var/tmp/script.sh"
+ commands:
+ - set high-availability virtual-server s1 address 10.10.10.1
+ - set high-availability virtual-server s1 algorithm round-robin
+ - set high-availability virtual-server s1 delay-loop 60
+ - set high-availability virtual-server s1 forward-method direct
+ - set high-availability virtual-server s1 fwmark 10
+ - set high-availability virtual-server s1 persistence-timeout 30
+ - set high-availability virtual-server s1 protocol tcp
+ - set high-availability virtual-server s1 real-server 10.10.50.1 health-check script /var/tmp/script.sh
+ - set high-availability virtual-server s1 real-server 10.10.50.1 port 443
+ - set high-availability virtual-server s2 address 10.10.10.2
+ - set high-availability virtual-server s2 port 81
+ - set high-availability virtual-server s2 real-server real1 connection-timeout 5
+ - set high-availability virtual-server s2 real-server real1 port 8081
+ - set high-availability virtual-server s2 real-server real2 port 8080
+ - set high-availability vrrp global-parameters garp interval 30
+ - set high-availability vrrp global-parameters garp master-delay 10
+ - set high-availability vrrp global-parameters garp master-refresh 100
+ - set high-availability vrrp global-parameters garp master-refresh-repeat 200
+ - set high-availability vrrp global-parameters garp master-repeat 5
+ - set high-availability vrrp global-parameters startup-delay 31
+ - set high-availability vrrp global-parameters version 3
+ - set high-availability vrrp group g1 address 1.1.1.1
+ - set high-availability vrrp group g1 address 2.2.2.2
+ - set high-availability vrrp group g1 address 3.3.3.3 interface eth2
+ - set high-availability vrrp group g1 advertise-interval 10
+ - set high-availability vrrp group g1 authentication password ********
+ - set high-availability vrrp group g1 authentication type plaintext-password
+ - set high-availability vrrp group g1 description 'Group_1'
+ - set high-availability vrrp group g1 disable
+ - set high-availability vrrp group g1 excluded-address 192.168.1.7 interface eth3
+ - set high-availability vrrp group g1 garp interval 20
+ - set high-availability vrrp group g1 garp master-delay 5
+ - set high-availability vrrp group g1 garp master-refresh 50
+ - set high-availability vrrp group g1 garp master-refresh-repeat 100
+ - set high-availability vrrp group g1 garp master-repeat 3
+ - set high-availability vrrp group g1 interface eth2
+ - set high-availability vrrp group g1 no-preempt
+ - set high-availability vrrp group g1 peer-address 192.168.1.3
+ - set high-availability vrrp group g1 preempt-delay 15
+ - set high-availability vrrp group g1 priority 100
+ - set high-availability vrrp group g1 rfc3768-compatibility
+ - set high-availability vrrp group g1 track exclude-vrrp-interface
+ - set high-availability vrrp group g1 track interface eth0
+ - set high-availability vrrp group g1 transition-script backup /var/tmp/script.sh
+ - set high-availability vrrp group g1 transition-script fault /var/tmp/script.sh
+ - set high-availability vrrp group g1 transition-script master /var/tmp/script.sh
+ - set high-availability vrrp group g1 transition-script stop /var/tmp/script.sh
+ - set high-availability vrrp group g1 vrid 20
+ - set high-availability vrrp group g2 address 2.2.2.2
+ - set high-availability vrrp group g2 description 'Group_2'
+ - set high-availability vrrp group g2 health-check failure-count 5
+ - set high-availability vrrp group g2 health-check interval 15
+ - set high-availability vrrp group g2 health-check ping 192.168.1.100
+ - set high-availability vrrp group g2 health-check script /var/tmp/script.sh
+ - set high-availability vrrp group g2 hello-source-address 2.2.2.2
+ - set high-availability vrrp group g2 interface eth1
+ - set high-availability vrrp group g2 vrid 11
+ - set high-availability vrrp snmp
+ - set high-availability vrrp sync-group sg1 health-check failure-count 3
+ - set high-availability vrrp sync-group sg1 health-check interval 10
+ - set high-availability vrrp sync-group sg1 health-check ping 192.168.2.100
+ - set high-availability vrrp sync-group sg1 health-check script /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 member g1
+ - set high-availability vrrp sync-group sg1 transition-script backup /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 transition-script fault /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 transition-script master /var/tmp/script.sh
+ - set high-availability vrrp sync-group sg1 transition-script stop /var/tmp/script.sh
+
+purged:
+ commands:
+ - delete high-availability
+ after:
+ disable: false
+
+deleted_vrrp_groups:
+ commands:
+ - delete high-availability vrrp group g1
+ - delete high-availability vrrp sync-group sg1
+ after:
+ disable: false
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.1
+ algorithm: round-robin
+ delay_loop: 60
+ forward_method: direct
+ fwmark: 10
+ persistence_timeout: 30
+ protocol: tcp
+ real_server:
+ - address: 10.10.50.1
+ port: 443
+ health_check_script: /var/tmp/script.sh
+ - name: s2
+ address: 10.10.10.2
+ port: 81
+ real_server:
+ - address: real1
+ port: 8081
+ connection_timeout: 5
+ - address: real2
+ port: 8080
+ vrrp:
+ global_parameters:
+ startup_delay: 31
+ version: "3"
+ garp:
+ interval: 30
+ master_delay: 10
+ master_refresh: 100
+ master_refresh_repeat: 200
+ master_repeat: 5
+ groups:
+ - name: g2
+ address:
+ - "2.2.2.2"
+ description: Group_2
+ disable: false
+ interface: eth1
+ vrid: 11
+ hello_source_address: 2.2.2.2
+ no_preempt: false
+ rfc3768_compatibility: false
+ health_check:
+ failure_count: 5
+ interval: 15
+ ping: 192.168.1.100
+ script: /var/tmp/script.sh
+
+deleted_bulk:
+ commands:
+ - delete high-availability virtual-server
+ - delete high-availability vrrp global-parameters garp
+ - delete high-availability vrrp global-parameters startup-delay
+ - delete high-availability vrrp group g1
+ - delete high-availability vrrp sync-group
+ after:
+ disable: false
+ vrrp:
+ global_parameters:
+ version: "3"
+ groups:
+ - name: g2
+ address:
+ - "2.2.2.2"
+ description: Group_2
+ disable: false
+ hello_source_address: 2.2.2.2
+ interface: eth1
+ vrid: 11
+ no_preempt: false
+ rfc3768_compatibility: false
+ health_check:
+ failure_count: 5
+ interval: 15
+ ping: 192.168.1.100
+ script: /var/tmp/script.sh
+
+overridden:
+ config:
+ disable: false
+ vrrp:
+ snmp: disabled
+ global_parameters:
+ startup_delay: 32
+ version: 3
+ garp:
+ interval: 30
+ master_delay: 11
+ master_refresh: 100
+ master_refresh_repeat: 200
+ master_repeat: 6
+ groups:
+ - name: "g1"
+ description: "Group_1"
+ interface: "eth2"
+ address:
+ - "1.1.1.1"
+ - "3.3.3.3 interface eth3"
+ disable: true
+ no_preempt: true
+ rfc3768_compatibility: true
+ vrid: 20
+ peer_address: "192.168.1.3"
+ advertise_interval: 10
+ priority: 100
+ garp:
+ interval: 20
+ master_delay: 5
+ master_refresh: 50
+ master_refresh_repeat: 100
+ master_repeat: 3
+ authentication:
+ type: "plaintext-password"
+ password: "test123"
+ transition_script:
+ master: "/var/tmp/script.sh"
+ backup: "/var/tmp/script.sh"
+ fault: "/var/tmp/script.sh"
+ stop: "/var/tmp/script.sh"
+ track:
+ exclude_vrrp_interface: true
+ sync_groups:
+ - name: "sg1"
+ member:
+ - "g1"
+ - "g2"
+ transition_script:
+ master: "/var/tmp/script.sh"
+ backup: "/var/tmp/script.sh"
+ fault: "/var/tmp/script.sh"
+ stop: "/var/tmp/script.sh"
+ health_check:
+ failure_count: 4
+ interval: 10
+ ping: "192.168.1.100"
+ script: "/var/tmp/script.sh"
+ virtual_servers:
+ - name: "s1"
+ address: "10.10.10.15"
+ algorithm: "round-robin"
+ delay_loop: 60
+ fwmark: 12
+ forward_method: "direct"
+ persistence_timeout: 30
+ protocol: "tcp"
+ real_server:
+ - address: "10.10.10.1"
+ connection_timeout: 61
+ port: 443
+ after:
+ disable: false
+ virtual_servers:
+ - address: "10.10.10.15"
+ algorithm: round-robin
+ delay_loop: 60
+ forward_method: direct
+ fwmark: 12
+ name: s1
+ persistence_timeout: 30
+ protocol: tcp
+ real_server:
+ - address: "10.10.10.1"
+ connection_timeout: 61
+ port: 443
+ - address: "10.10.10.2"
+ name: s2
+ port: 81
+ real_server:
+ - address: real1
+ connection_timeout: 5
+ port: 8081
+ - address: real2
+ port: 8080
+ vrrp:
+ global_parameters:
+ garp:
+ interval: 30
+ master_delay: 11
+ master_refresh: 100
+ master_refresh_repeat: 200
+ master_repeat: 6
+ startup_delay: 32
+ version: "3"
+ groups:
+ - address: "1.1.1.1"
+ advertise_interval: 10
+ authentication:
+ password: VALUE_SPECIFIED_IN_NO_LOG_PARAMETER
+ type: plaintext-password
+ description: Group_1
+ disable: true
+ garp:
+ interval: 20
+ master_delay: 5
+ master_refresh: 50
+ master_refresh_repeat: 100
+ master_repeat: 3
+ interface: eth2
+ name: g1
+ no_preempt: true
+ peer_address: "192.168.1.3"
+ priority: 100
+ rfc3768_compatibility: true
+ track:
+ exclude_vrrp_interface: true
+ transition_script:
+ backup: /var/tmp/script.sh
+ fault: /var/tmp/script.sh
+ master: /var/tmp/script.sh
+ stop: /var/tmp/script.sh
+ vrid: 20
+ - address: "2.2.2.2"
+ description: Group_2
+ disable: false
+ health_check:
+ failure_count: 5
+ interval: 15
+ ping: "192.168.1.100"
+ script: /var/tmp/script.sh
+ hello_source_address: "2.2.2.2"
+ interface: eth1
+ name: g2
+ no_preempt: false
+ rfc3768_compatibility: false
+ vrid: 11
+ sync_groups:
+ - health_check:
+ failure_count: 4
+ interval: 10
+ ping: "192.168.1.100"
+ script: /var/tmp/script.sh
+ member:
+ - g1
+ - g2
+ name: sg1
+ transition_script:
+ backup: /var/tmp/script.sh
+ fault: /var/tmp/script.sh
+ master: /var/tmp/script.sh
+ stop: /var/tmp/script.sh
+
+replaced:
+ commands:
+ - delete high-availability vrrp global-parameters garp master-repeat 5
+ - delete high-availability vrrp global-parameters startup-delay 31
+ - delete high-availability vrrp group g1 disable
+ - delete high-availability vrrp group g1 no-preempt
+ - delete high-availability vrrp group g1 rfc3768-compatibility
+ - delete high-availability vrrp group g2 address 2.2.2.2
+ - delete high-availability vrrp sync-group sg1 health-check failure-count 3
+ - set high-availability disable
+ - set high-availability virtual-server s1 address 10.10.10.5
+ - set high-availability virtual-server s1 real-server 10.10.50.2 port 8443
+ - set high-availability virtual-server s2 persistence-timeout 30
+ - set high-availability virtual-server s2 protocol tcp
+ - set high-availability virtual-server s3 address 10.10.10.3
+ - set high-availability virtual-server s3 port 88
+ - set high-availability virtual-server s3 protocol udp
+ - set high-availability vrrp global-parameters garp master-repeat 6
+ - set high-availability vrrp global-parameters startup-delay 30
+ - set high-availability vrrp group g2 address 4.4.4.4
+ - set high-availability vrrp group g2 address 6.6.6.6 interface eth3
+ - set high-availability vrrp snmp
+ - set high-availability vrrp sync-group sg1 health-check failure-count 5
+
+rtt:
+ commands:
+ - set high-availability disable
+ - set high-availability virtual-server s1 address 10.10.10.5
+ - set high-availability virtual-server s1 algorithm round-robin
+ - set high-availability virtual-server s1 real-server 10.10.50.2 port 8443
+ - set high-availability virtual-server s2 address 10.10.10.2
+ - set high-availability virtual-server s2 persistence-timeout 30
+ - set high-availability virtual-server s2 port 81
+ - set high-availability virtual-server s2 protocol tcp
+ - set high-availability virtual-server s3 address 10.10.10.3
+ - set high-availability virtual-server s3 port 88
+ - set high-availability virtual-server s3 protocol udp
+ - set high-availability vrrp global-parameters garp master-repeat 6
+ - set high-availability vrrp global-parameters startup-delay 30
+ - set high-availability vrrp group g1 peer-address 192.168.1.3
+ - set high-availability vrrp group g1 priority 100
+ - set high-availability vrrp group g1 vrid 20
+ - set high-availability vrrp group g2 address 4.4.4.4
+ - set high-availability vrrp group g2 address 6.6.6.6 interface eth3
+ - set high-availability vrrp snmp
+ - set high-availability vrrp sync-group sg1 health-check failure-count 5
+ config:
+ disable: false
+ virtual_servers:
+ - name: s1
+ address: 10.10.10.5
+ fwmark: 10
+ algorithm: least-connection
+ real_server:
+ - address: 10.10.50.2
+ port: 8443
+ - name: s2
+ address: 10.10.10.2
+ persistence_timeout: 36
+ port: 443
+ protocol: tcp
+ real_server:
+ - address: 10.10.50.3
+ port: 8443
+ - name: s3
+ address: 10.10.10.3
+ port: 80
+ protocol: udp
+ real_server:
+ - address: 10.10.50.4
+ port: 8443
+ vrrp:
+ snmp: disabled
+ global_parameters:
+ startup_delay: 36
+ garp:
+ master_repeat: 6
+ groups:
+ - name: "g1"
+ peer_address: 192.168.1.3
+ address: "1.1.1.1"
+ priority: 100
+ disable: true
+ no_preempt: false
+ interface: eth2
+ vrid: 20
+ - name: "g2"
+ address:
+ - "2.2.2.2"
+ interface: eth1
+ vrid: 11
+ sync_groups:
+ - name: "sg1"
+ health_check:
+ failure_count: 10
+ after:
+ disable: true
+ virtual_servers:
+ - address: "10.10.10.5"
+ algorithm: "round-robin"
+ name: "s1"
+ real_server:
+ - address: "10.10.50.2"
+ port: 8443
+ - address: "10.10.10.2"
+ name: "s2"
+ persistence_timeout: 30
+ port: 81
+ protocol: "tcp"
+ - address: "10.10.10.3"
+ name: "s3"
+ port: 88
+ protocol: "udp"
+ vrrp:
+ global_parameters:
+ garp:
+ master_repeat: 6
+ startup_delay: 30
+ groups:
+ - disable: false
+ name: "g1"
+ no_preempt: false
+ peer_address: "192.168.1.3"
+ priority: 100
+ rfc3768_compatibility: false
+ vrid: 20
+ snmp: "enabled"
+ sync_groups:
+ - health_check:
+ failure_count: 5
+ name: "sg1"
diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.15.txt
+++ b/tests/sanity/ignore-2.15.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.16.txt
+++ b/tests/sanity/ignore-2.16.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.17.txt
+++ b/tests/sanity/ignore-2.17.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/sanity/ignore-2.18.txt b/tests/sanity/ignore-2.18.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.18.txt
+++ b/tests/sanity/ignore-2.18.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.19.txt
+++ b/tests/sanity/ignore-2.19.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/sanity/ignore-2.20.txt b/tests/sanity/ignore-2.20.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.20.txt
+++ b/tests/sanity/ignore-2.20.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/sanity/ignore-2.21.txt b/tests/sanity/ignore-2.21.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.21.txt
+++ b/tests/sanity/ignore-2.21.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/sanity/ignore-2.22.txt b/tests/sanity/ignore-2.22.txt
index bae35e38..255b0a87 100644
--- a/tests/sanity/ignore-2.22.txt
+++ b/tests/sanity/ignore-2.22.txt
@@ -8,6 +8,7 @@ plugins/action/facts.py action-plugin-docs
plugins/action/firewall_global.py action-plugin-docs
plugins/action/firewall_interfaces.py action-plugin-docs
plugins/action/firewall_rules.py action-plugin-docs
+plugins/action/ha.py action-plugin-docs
plugins/action/hostname.py action-plugin-docs
plugins/action/interface.py action-plugin-docs
plugins/action/interfaces.py action-plugin-docs
diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_ha_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_ha_config.cfg
new file mode 100644
index 00000000..ee69c65d
--- /dev/null
+++ b/tests/unit/modules/network/vyos/fixtures/vyos_ha_config.cfg
@@ -0,0 +1,64 @@
+set high-availability disable
+set high-availability virtual-server s1 address 10.10.10.1
+set high-availability virtual-server s1 algorithm round-robin
+set high-availability virtual-server s1 delay-loop 60
+set high-availability virtual-server s1 forward-method direct
+set high-availability virtual-server s1 fwmark 10
+set high-availability virtual-server s1 persistence-timeout 30
+set high-availability virtual-server s1 protocol tcp
+set high-availability virtual-server s1 real-server 10.10.50.1 health-check script '/var/tmp/script.sh'
+set high-availability virtual-server s1 real-server 10.10.50.1 port 443
+set high-availability virtual-server s2 address 10.10.10.2
+set high-availability virtual-server s2 port 81
+set high-availability virtual-server s2 real-server real1 connection-timeout 5
+set high-availability virtual-server s2 real-server real1 port 8081
+set high-availability virtual-server s2 real-server real2 port 8080
+set high-availability vrrp global-parameters garp interval 30
+set high-availability vrrp global-parameters garp master-delay 10
+set high-availability vrrp global-parameters garp master-refresh 100
+set high-availability vrrp global-parameters garp master-refresh-repeat 200
+set high-availability vrrp global-parameters garp master-repeat 5
+set high-availability vrrp global-parameters startup-delay 30
+set high-availability vrrp global-parameters version 3
+set high-availability vrrp group g1 address '1.1.1.1'
+set high-availability vrrp group g1 address '3.3.3.3'
+set high-availability vrrp group g1 address '5.5.5.5' interface 'eth2'
+set high-availability vrrp group g1 advertise-interval 10
+set high-availability vrrp group g1 authentication password 'testpass'
+set high-availability vrrp group g1 authentication type 'plaintext-password'
+set high-availability vrrp group g1 description 'Group_1'
+set high-availability vrrp group g1 disable
+set high-availability vrrp group g1 excluded-address '192.168.1.7' interface 'eth3'
+set high-availability vrrp group g1 excluded-address '192.168.1.8'
+set high-availability vrrp group g1 garp interval 20
+set high-availability vrrp group g1 garp master-delay 5
+set high-availability vrrp group g1 garp master-refresh 50
+set high-availability vrrp group g1 garp master-refresh-repeat 100
+set high-availability vrrp group g1 garp master-repeat 3
+set high-availability vrrp group g1 health-check failure-count 3
+set high-availability vrrp group g1 health-check interval 10
+set high-availability vrrp group g1 health-check ping '192.168.1.5'
+set high-availability vrrp group g1 health-check script 'script.sh'
+set high-availability vrrp group g1 hello-source-address '192.168.1.2'
+set high-availability vrrp group g1 interface 'eth2'
+set high-availability vrrp group g1 no-preempt
+set high-availability vrrp group g1 peer-address '192.168.1.3'
+set high-availability vrrp group g1 priority 100
+set high-availability vrrp group g1 rfc3768-compatibility
+set high-availability vrrp group g1 track exclude-vrrp-interface
+set high-availability vrrp group g1 track interface 'eth1'
+set high-availability vrrp group g1 transition-script backup '/var/tmp/script.sh'
+set high-availability vrrp group g1 transition-script fault '/var/tmp/script.sh'
+set high-availability vrrp group g1 transition-script master '/var/tmp/script.sh'
+set high-availability vrrp group g1 transition-script stop '/var/tmp/script.sh'
+set high-availability vrrp group g1 vrid 20
+set high-availability vrrp snmp
+set high-availability vrrp sync-group sg1 health-check failure-count 3
+set high-availability vrrp sync-group sg1 health-check interval 10
+set high-availability vrrp sync-group sg1 health-check ping '192.168.1.1'
+set high-availability vrrp sync-group sg1 health-check script '/var/tmp/script.sh'
+set high-availability vrrp sync-group sg1 member 'g1'
+set high-availability vrrp sync-group sg1 transition-script backup '/var/tmp/script.sh'
+set high-availability vrrp sync-group sg1 transition-script fault '/var/tmp/script.sh'
+set high-availability vrrp sync-group sg1 transition-script master '/var/tmp/script.sh'
+set high-availability vrrp sync-group sg1 transition-script stop '/var/tmp/script.sh'
diff --git a/tests/unit/modules/network/vyos/test_vyos_ha.py b/tests/unit/modules/network/vyos/test_vyos_ha.py
new file mode 100644
index 00000000..54833a36
--- /dev/null
+++ b/tests/unit/modules/network/vyos/test_vyos_ha.py
@@ -0,0 +1,1405 @@
+# (c) 2021 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+from unittest.mock import patch
+
+from ansible_collections.vyos.vyos.plugins.modules import vyos_ha
+from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args
+
+from .vyos_module import TestVyosModule, load_fixture
+
+
+class TestVyosHaModule(TestVyosModule):
+ module = vyos_ha
+
+ def setUp(self):
+ super(TestVyosHaModule, self).setUp()
+
+ self.mock_get_resource_connection_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection",
+ )
+ self.get_resource_connection_config = self.mock_get_resource_connection_config.start()
+
+ self.mock_get_resource_connection_facts = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection",
+ )
+ self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start()
+
+ self.mock_execute_show_command = patch(
+ "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ha.ha.HaFacts.get_config",
+ )
+
+ self.execute_show_command = self.mock_execute_show_command.start()
+
+ self.mock_get_os_version = patch(
+ "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ha.ha.get_os_version",
+ )
+ self.get_os_version = self.mock_get_os_version.start()
+ self.get_os_version.return_value = "1.5"
+ self.maxDiff = None
+
+ def tearDown(self):
+ super(TestVyosHaModule, self).tearDown()
+ self.mock_get_resource_connection_config.stop()
+ self.mock_get_resource_connection_facts.stop()
+ self.mock_execute_show_command.stop()
+ self.mock_get_os_version.stop()
+
+ def load_fixtures(self, commands=None, filename=None):
+ if filename is None:
+ filename = "vyos_ha_config.cfg"
+
+ def load_from_file(*args, **kwargs):
+ output = load_fixture(filename)
+ return output
+
+ self.execute_show_command.side_effect = load_from_file
+
+ def test_vrrp_merged_idempotent(self):
+ set_module_args(
+ dict(
+ config=dict(
+ disable=True,
+ virtual_servers=[
+ dict(
+ address="10.10.10.1",
+ algorithm="round-robin",
+ delay_loop=60,
+ forward_method="direct",
+ fwmark=10,
+ name="s1",
+ persistence_timeout="30",
+ protocol="tcp",
+ real_server=[
+ dict(
+ address="10.10.50.1",
+ port=443,
+ health_check_script="/var/tmp/script.sh",
+ ),
+ ],
+ ),
+ dict(
+ address="10.10.10.2",
+ name="s2",
+ port=81,
+ real_server=[
+ dict(
+ address="real1",
+ port=8081,
+ connection_timeout=5,
+ ),
+ dict(
+ address="real2",
+ port=8080,
+ ),
+ ],
+ ),
+ ],
+ vrrp=dict(
+ global_parameters=dict(
+ garp=dict(
+ master_refresh=100,
+ ),
+ version="3",
+ ),
+ groups=[
+ dict(
+ authentication=dict(
+ password="testpass",
+ type="plaintext-password",
+ ),
+ address=[
+ "1.1.1.1",
+ "3.3.3.3",
+ "5.5.5.5 interface eth2",
+ ],
+ advertise_interval=10,
+ description="Group_1",
+ disable=True,
+ excluded_address=[
+ "192.168.1.8",
+ "192.168.1.7 interface eth3",
+ ],
+ garp=dict(
+ interval=20,
+ master_delay=5,
+ master_refresh_repeat=100,
+ master_repeat=3,
+ ),
+ health_check=dict(
+ failure_count=3,
+ interval=10,
+ ping="192.168.1.5",
+ script="script.sh",
+ ),
+ hello_source_address="192.168.1.2",
+ interface="eth2",
+ name="g1",
+ no_preempt=True,
+ peer_address="192.168.1.3",
+ priority=100,
+ rfc3768_compatibility=True,
+ track=dict(
+ exclude_vrrp_interface=True,
+ interface=["eth1"],
+ ),
+ transition_script=dict(
+ backup="/var/tmp/script.sh",
+ master="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ vrid=20,
+ ),
+ ],
+ snmp="enabled",
+ sync_groups=[
+ dict(
+ health_check=dict(
+ failure_count=3,
+ interval=10,
+ ),
+ member=["g1"],
+ name="sg1",
+ transition_script=dict(
+ backup="/var/tmp/script.sh",
+ master="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ ),
+ ],
+ ),
+ ),
+ state="merged",
+ ),
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vrrp_merged(self):
+ set_module_args(
+ dict(
+ config=dict(
+ disable=True,
+ virtual_servers=[
+ dict(
+ name="s1",
+ address="10.10.10.5",
+ algorithm="round-robin",
+ real_server=[
+ dict(
+ address="10.10.50.2",
+ port=8443,
+ ),
+ ],
+ ),
+ dict(
+ name="s2",
+ address="10.10.10.2",
+ persistence_timeout=30,
+ port=81,
+ protocol="tcp",
+ ),
+ dict(
+ name="s3",
+ address="10.10.10.3",
+ port=88,
+ protocol="udp",
+ ),
+ ],
+ vrrp=dict(
+ snmp="disabled",
+ global_parameters=dict(
+ startup_delay=35,
+ garp=dict(
+ master_repeat=6,
+ ),
+ ),
+ groups=[
+ dict(
+ name="g1",
+ peer_address="192.168.1.3",
+ priority=100,
+ disable=False,
+ no_preempt=False,
+ ),
+ dict(
+ name="g2",
+ address=[
+ "192.168.3.3",
+ "192.168.4.4 interface eth3",
+ ],
+ preempt_delay=15,
+ ),
+ ],
+ sync_groups=[
+ dict(
+ name="sg1",
+ health_check=dict(
+ failure_count=5,
+ ),
+ ),
+ ],
+ ),
+ ),
+ state="merged",
+ ),
+ )
+ commands = [
+ "delete high-availability vrrp group g1 no-preempt",
+ "delete high-availability vrrp group g1 disable",
+ "delete high-availability vrrp group g1 rfc3768-compatibility",
+ "set high-availability vrrp group g2 address 192.168.3.3",
+ "set high-availability vrrp group g2 address 192.168.4.4 interface eth3",
+ "set high-availability vrrp group g2 preempt-delay 15",
+ "delete high-availability vrrp snmp",
+ "set high-availability virtual-server s1 address 10.10.10.5",
+ "set high-availability virtual-server s1 real-server 10.10.50.2 port 8443",
+ "set high-availability virtual-server s2 persistence-timeout 30",
+ "set high-availability virtual-server s2 protocol tcp",
+ "set high-availability virtual-server s3 address 10.10.10.3",
+ "set high-availability virtual-server s3 port 88",
+ "set high-availability virtual-server s3 protocol udp",
+ "set high-availability vrrp global-parameters garp master-repeat 6",
+ "set high-availability vrrp global-parameters startup-delay 35",
+ "set high-availability vrrp sync-group sg1 health-check failure-count 5",
+ ]
+
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vrrp_replaced(self):
+ set_module_args(
+ dict(
+ config=dict(
+ disable=False,
+ virtual_servers=[
+ dict(
+ name="s1",
+ address="10.10.10.5",
+ algorithm="round-robin",
+ real_server=[
+ dict(
+ address="10.10.50.2",
+ port=8443,
+ ),
+ ],
+ ),
+ dict(
+ name="s2",
+ address="10.10.10.2",
+ persistence_timeout=30,
+ port=81,
+ protocol="tcp",
+ ),
+ dict(
+ name="s3",
+ address="10.10.10.3",
+ port=88,
+ protocol="udp",
+ ),
+ ],
+ vrrp=dict(
+ snmp="disabled",
+ global_parameters=dict(
+ startup_delay=32,
+ garp=dict(
+ master_repeat=6,
+ ),
+ ),
+ groups=[
+ dict(
+ name="g1",
+ peer_address="192.168.1.3",
+ priority=100,
+ disable=False,
+ no_preempt=False,
+ vrid=20,
+ ),
+ dict(
+ name="g2",
+ address=[
+ "192.168.3.3",
+ "192.168.4.4 interface eth3",
+ ],
+ ),
+ ],
+ sync_groups=[
+ dict(
+ name="sg1",
+ health_check=dict(
+ failure_count=5,
+ ),
+ ),
+ ],
+ ),
+ ),
+ state="replaced",
+ ),
+ )
+ commands = [
+ "delete high-availability vrrp global-parameters garp master-repeat 5",
+ "delete high-availability vrrp global-parameters startup-delay 30",
+ "delete high-availability vrrp group g1 disable",
+ "delete high-availability vrrp group g1 no-preempt",
+ "delete high-availability vrrp group g1 rfc3768-compatibility",
+ "delete high-availability vrrp sync-group sg1 health-check failure-count 3",
+ "set high-availability vrrp group g2 address 192.168.3.3",
+ "set high-availability vrrp group g2 address 192.168.4.4 interface eth3",
+ "delete high-availability disable",
+ "delete high-availability vrrp snmp",
+ "set high-availability virtual-server s1 address 10.10.10.5",
+ "set high-availability virtual-server s1 real-server 10.10.50.2 port 8443",
+ "set high-availability virtual-server s2 persistence-timeout 30",
+ "set high-availability virtual-server s2 protocol tcp",
+ "set high-availability virtual-server s3 address 10.10.10.3",
+ "set high-availability virtual-server s3 port 88",
+ "set high-availability virtual-server s3 protocol udp",
+ "set high-availability vrrp global-parameters garp master-repeat 6",
+ "set high-availability vrrp global-parameters startup-delay 32",
+ "set high-availability vrrp sync-group sg1 health-check failure-count 5",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vrrp_replaced_idempotent(self):
+ set_module_args(
+ dict(
+ config=dict(
+ disable=True,
+ virtual_servers=[
+ dict(
+ address="10.10.10.1",
+ algorithm="round-robin",
+ delay_loop=60,
+ forward_method="direct",
+ fwmark=10,
+ name="s1",
+ persistence_timeout="30",
+ protocol="tcp",
+ real_server=[
+ dict(
+ address="10.10.50.1",
+ port=443,
+ health_check_script="/var/tmp/script.sh",
+ ),
+ ],
+ ),
+ dict(
+ address="10.10.10.2",
+ name="s2",
+ port=81,
+ real_server=[
+ dict(
+ address="real1",
+ port=8081,
+ connection_timeout=5,
+ ),
+ dict(
+ address="real2",
+ port=8080,
+ ),
+ ],
+ ),
+ ],
+ vrrp=dict(
+ global_parameters=dict(
+ garp=dict(
+ interval=30,
+ master_delay=10,
+ master_refresh=100,
+ master_refresh_repeat=200,
+ master_repeat=5,
+ ),
+ version="3",
+ startup_delay=30,
+ ),
+ groups=[
+ dict(
+ authentication=dict(
+ password="testpass",
+ type="plaintext-password",
+ ),
+ address=[
+ "1.1.1.1",
+ "3.3.3.3",
+ "5.5.5.5 interface eth2",
+ ],
+ advertise_interval=10,
+ description="Group_1",
+ disable=True,
+ excluded_address=[
+ "192.168.1.8",
+ "192.168.1.7 interface eth3",
+ ],
+ garp=dict(
+ interval=20,
+ master_delay=5,
+ master_refresh=50,
+ master_refresh_repeat=100,
+ master_repeat=3,
+ ),
+ health_check=dict(
+ failure_count=3,
+ interval=10,
+ ping="192.168.1.5",
+ script="script.sh",
+ ),
+ hello_source_address="192.168.1.2",
+ interface="eth2",
+ name="g1",
+ no_preempt=True,
+ peer_address="192.168.1.3",
+ priority=100,
+ rfc3768_compatibility=True,
+ track=dict(
+ exclude_vrrp_interface=True,
+ interface=["eth1"],
+ ),
+ transition_script=dict(
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ master="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ vrid=20,
+ ),
+ ],
+ snmp="enabled",
+ sync_groups=[
+ dict(
+ health_check=dict(
+ failure_count=3,
+ interval=10,
+ ping="192.168.1.1",
+ script="/var/tmp/script.sh",
+ ),
+ member=["g1"],
+ name="sg1",
+ transition_script=dict(
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ master="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ ),
+ ],
+ ),
+ ),
+ state="replaced",
+ ),
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vrrp_overridden(self):
+ set_module_args(
+ dict(
+ config=dict(
+ disable=False,
+ vrrp=dict(
+ snmp="disabled",
+ global_parameters=dict(
+ startup_delay=32,
+ version=3,
+ garp=dict(
+ interval=30,
+ master_delay=11,
+ master_refresh=100,
+ master_refresh_repeat=200,
+ master_repeat=6,
+ ),
+ ),
+ groups=[
+ dict(
+ name="g1",
+ description="Group_1",
+ interface="eth2",
+ address="1.1.1.1",
+ disable=True,
+ no_preempt=True,
+ rfc3768_compatibility=True,
+ vrid=20,
+ peer_address="192.168.1.3",
+ advertise_interval=10,
+ priority=100,
+ garp=dict(
+ interval=20,
+ master_delay=5,
+ master_refresh=50,
+ master_refresh_repeat=100,
+ master_repeat=3,
+ ),
+ authentication=dict(
+ type="plaintext-password",
+ password="testpass",
+ ),
+ transition_script=dict(
+ master="/var/tmp/script.sh",
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ track=dict(
+ exclude_vrrp_interface=True,
+ ),
+ ),
+ dict(
+ name="g2",
+ address=[
+ "192.168.3.3",
+ "192.168.4.4 interface eth3",
+ ],
+ ),
+ ],
+ sync_groups=[
+ dict(
+ name="sg1",
+ member=["g1", "g2"],
+ transition_script=dict(
+ master="/var/tmp/script.sh",
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ health_check=dict(
+ failure_count=4,
+ interval=10,
+ ping="192.168.1.100",
+ script="/var/tmp/script.sh",
+ ),
+ ),
+ ],
+ ),
+ virtual_servers=[
+ dict(
+ name="s1",
+ address="10.10.10.15",
+ algorithm="round-robin",
+ delay_loop=60,
+ fwmark=12,
+ forward_method="direct",
+ persistence_timeout=30,
+ protocol="tcp",
+ real_server=[
+ dict(
+ address="10.10.10.1",
+ connection_timeout=61,
+ port=443,
+ ),
+ ],
+ ),
+ ],
+ ),
+ state="overridden",
+ ),
+ )
+ commands = [
+ "delete high-availability disable",
+ "delete high-availability virtual-server s1",
+ "delete high-availability vrrp global-parameters garp",
+ "delete high-availability vrrp group g1",
+ "delete high-availability vrrp sync-group sg1",
+ "set high-availability virtual-server s1 address 10.10.10.15",
+ "set high-availability virtual-server s1 algorithm round-robin",
+ "set high-availability virtual-server s1 delay-loop 60",
+ "set high-availability virtual-server s1 forward-method direct",
+ "set high-availability virtual-server s1 fwmark 12",
+ "set high-availability virtual-server s1 persistence-timeout 30",
+ "set high-availability virtual-server s1 protocol tcp",
+ "set high-availability virtual-server s1 real-server 10.10.10.1 connection-timeout 61",
+ "set high-availability virtual-server s1 real-server 10.10.10.1 port 443",
+ "set high-availability vrrp global-parameters garp interval 30",
+ "set high-availability vrrp global-parameters garp master-delay 11",
+ "set high-availability vrrp global-parameters garp master-refresh 100",
+ "set high-availability vrrp global-parameters garp master-refresh-repeat 200",
+ "set high-availability vrrp global-parameters garp master-repeat 6",
+ "set high-availability vrrp global-parameters startup-delay 32",
+ "set high-availability vrrp group g1 address 1.1.1.1",
+ "set high-availability vrrp group g1 advertise-interval 10",
+ "set high-availability vrrp group g1 authentication password testpass",
+ "set high-availability vrrp group g1 authentication type plaintext-password",
+ "set high-availability vrrp group g1 description 'Group_1'",
+ "set high-availability vrrp group g1 disable",
+ "set high-availability vrrp group g1 garp interval 20",
+ "set high-availability vrrp group g1 garp master-delay 5",
+ "set high-availability vrrp group g1 garp master-refresh 50",
+ "set high-availability vrrp group g1 garp master-refresh-repeat 100",
+ "set high-availability vrrp group g1 garp master-repeat 3",
+ "set high-availability vrrp group g1 interface eth2",
+ "set high-availability vrrp group g1 no-preempt",
+ "set high-availability vrrp group g1 peer-address 192.168.1.3",
+ "set high-availability vrrp group g1 priority 100",
+ "set high-availability vrrp group g1 rfc3768-compatibility",
+ "set high-availability vrrp group g1 track exclude-vrrp-interface",
+ "set high-availability vrrp group g1 transition-script backup /var/tmp/script.sh",
+ "set high-availability vrrp group g1 transition-script fault /var/tmp/script.sh",
+ "set high-availability vrrp group g1 transition-script master /var/tmp/script.sh",
+ "set high-availability vrrp group g1 transition-script stop /var/tmp/script.sh",
+ "set high-availability vrrp group g1 vrid 20",
+ "set high-availability vrrp group g2 address 192.168.3.3",
+ "set high-availability vrrp group g2 address 192.168.4.4 interface eth3",
+ "set high-availability vrrp sync-group sg1 health-check failure-count 4",
+ "set high-availability vrrp sync-group sg1 health-check interval 10",
+ "set high-availability vrrp sync-group sg1 health-check ping 192.168.1.100",
+ "set high-availability vrrp sync-group sg1 health-check script /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 member g1",
+ "set high-availability vrrp sync-group sg1 member g2",
+ "set high-availability vrrp sync-group sg1 transition-script backup /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 transition-script fault /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 transition-script master /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 transition-script stop /var/tmp/script.sh",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vrrp_overridden_idempotent(self):
+ set_module_args(
+ dict(
+ config=dict(
+ disable=True,
+ virtual_servers=[
+ dict(
+ address="10.10.10.1",
+ algorithm="round-robin",
+ delay_loop=60,
+ forward_method="direct",
+ fwmark=10,
+ name="s1",
+ persistence_timeout="30",
+ protocol="tcp",
+ real_server=[
+ dict(
+ address="10.10.50.1",
+ port=443,
+ health_check_script="/var/tmp/script.sh",
+ ),
+ ],
+ ),
+ dict(
+ address="10.10.10.2",
+ name="s2",
+ port=81,
+ real_server=[
+ dict(
+ address="real1",
+ port=8081,
+ connection_timeout=5,
+ ),
+ dict(
+ address="real2",
+ port=8080,
+ ),
+ ],
+ ),
+ ],
+ vrrp=dict(
+ global_parameters=dict(
+ garp=dict(
+ interval=30,
+ master_delay=10,
+ master_refresh=100,
+ master_refresh_repeat=200,
+ master_repeat=5,
+ ),
+ version="3",
+ startup_delay=30,
+ ),
+ groups=[
+ dict(
+ authentication=dict(
+ password="testpass",
+ type="plaintext-password",
+ ),
+ address=[
+ "1.1.1.1",
+ "3.3.3.3",
+ "5.5.5.5 interface eth2",
+ ],
+ advertise_interval=10,
+ description="Group_1",
+ disable=True,
+ excluded_address=[
+ "192.168.1.8",
+ "192.168.1.7 interface eth3",
+ ],
+ garp=dict(
+ interval=20,
+ master_delay=5,
+ master_refresh=50,
+ master_refresh_repeat=100,
+ master_repeat=3,
+ ),
+ health_check=dict(
+ failure_count=3,
+ interval=10,
+ ping="192.168.1.5",
+ script="script.sh",
+ ),
+ hello_source_address="192.168.1.2",
+ interface="eth2",
+ name="g1",
+ no_preempt=True,
+ peer_address="192.168.1.3",
+ priority=100,
+ rfc3768_compatibility=True,
+ track=dict(
+ exclude_vrrp_interface=True,
+ interface=["eth1"],
+ ),
+ transition_script=dict(
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ master="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ vrid=20,
+ ),
+ ],
+ snmp="enabled",
+ sync_groups=[
+ dict(
+ health_check=dict(
+ failure_count=3,
+ interval=10,
+ ping="192.168.1.1",
+ script="/var/tmp/script.sh",
+ ),
+ member=["g1"],
+ name="sg1",
+ transition_script=dict(
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ master="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ ),
+ ],
+ ),
+ ),
+ state="overridden",
+ ),
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vrrp_rendered(self):
+ set_module_args(
+ dict(
+ config=dict(
+ virtual_servers=[
+ dict(
+ name="s1",
+ address="10.10.10.1",
+ algorithm="round-robin",
+ delay_loop=60,
+ forward_method="direct",
+ fwmark=10,
+ persistence_timeout=30,
+ protocol="tcp",
+ real_server=[
+ dict(
+ address="10.10.50.1",
+ health_check_script="/var/tmp/script.sh",
+ port=443,
+ ),
+ ],
+ ),
+ dict(
+ name="s2",
+ address="10.10.10.2",
+ port=81,
+ real_server=[
+ dict(
+ address="real1",
+ connection_timeout=5,
+ port=8081,
+ ),
+ dict(
+ address="real2",
+ port=8080,
+ ),
+ ],
+ ),
+ ],
+ vrrp=dict(
+ snmp="enabled",
+ global_parameters=dict(
+ startup_delay=31,
+ version="3",
+ garp=dict(
+ interval=30,
+ master_delay=10,
+ master_refresh=100,
+ master_refresh_repeat=200,
+ master_repeat=5,
+ ),
+ ),
+ groups=[
+ dict(
+ name="g1",
+ description="Group_1",
+ interface="eth2",
+ address="1.1.1.1",
+ advertise_interval=10,
+ peer_address="192.168.1.3",
+ priority=100,
+ disable=True,
+ no_preempt=True,
+ rfc3768_compatibility=True,
+ vrid=20,
+ excluded_address=[
+ "192.168.1.7 interface eth3",
+ ],
+ garp=dict(
+ interval=20,
+ master_delay=5,
+ master_refresh=50,
+ master_refresh_repeat=100,
+ master_repeat=3,
+ ),
+ authentication=dict(
+ type="plaintext-password",
+ password="testpass",
+ ),
+ transition_script=dict(
+ master="/var/tmp/script.sh",
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ track=dict(
+ exclude_vrrp_interface=True,
+ interface=[
+ "eth0",
+ ],
+ ),
+ ),
+ dict(
+ name="g2",
+ description="Group_2",
+ interface="eth1",
+ address=[
+ "2.2.2.2",
+ "2.2.2.3 interface eth3",
+ ],
+ disable=False,
+ no_preempt=False,
+ rfc3768_compatibility=False,
+ vrid=11,
+ health_check=dict(
+ failure_count=5,
+ interval=15,
+ ping="192.168.1.100",
+ script="/var/tmp/script.sh",
+ ),
+ hello_source_address="2.2.2.2",
+ ),
+ ],
+ sync_groups=[
+ dict(
+ name="sg1",
+ member=[
+ "g1",
+ ],
+ transition_script=dict(
+ master="/var/tmp/script.sh",
+ backup="/var/tmp/script.sh",
+ fault="/var/tmp/script.sh",
+ stop="/var/tmp/script.sh",
+ ),
+ health_check=dict(
+ failure_count=3,
+ interval=10,
+ ping="192.168.2.100",
+ script="/var/tmp/script.sh",
+ ),
+ ),
+ ],
+ ),
+ ),
+ state="rendered",
+ ),
+ )
+ rendered_commands = [
+ "set high-availability virtual-server s1 address 10.10.10.1",
+ "set high-availability virtual-server s1 algorithm round-robin",
+ "set high-availability virtual-server s1 delay-loop 60",
+ "set high-availability virtual-server s1 forward-method direct",
+ "set high-availability virtual-server s1 fwmark 10",
+ "set high-availability virtual-server s1 persistence-timeout 30",
+ "set high-availability virtual-server s1 protocol tcp",
+ "set high-availability virtual-server s1 real-server 10.10.50.1 health-check script /var/tmp/script.sh",
+ "set high-availability virtual-server s1 real-server 10.10.50.1 port 443",
+ "set high-availability virtual-server s2 address 10.10.10.2",
+ "set high-availability virtual-server s2 port 81",
+ "set high-availability virtual-server s2 real-server real1 connection-timeout 5",
+ "set high-availability virtual-server s2 real-server real1 port 8081",
+ "set high-availability virtual-server s2 real-server real2 port 8080",
+ "set high-availability vrrp global-parameters garp interval 30",
+ "set high-availability vrrp global-parameters garp master-delay 10",
+ "set high-availability vrrp global-parameters garp master-refresh 100",
+ "set high-availability vrrp global-parameters garp master-refresh-repeat 200",
+ "set high-availability vrrp global-parameters garp master-repeat 5",
+ "set high-availability vrrp global-parameters startup-delay 31",
+ "set high-availability vrrp global-parameters version 3",
+ "set high-availability vrrp group g1 address 1.1.1.1",
+ "set high-availability vrrp group g1 advertise-interval 10",
+ "set high-availability vrrp group g1 authentication password testpass",
+ "set high-availability vrrp group g1 authentication type plaintext-password",
+ "set high-availability vrrp group g1 description 'Group_1'",
+ "set high-availability vrrp group g1 disable",
+ "set high-availability vrrp group g1 excluded-address 192.168.1.7 interface eth3",
+ "set high-availability vrrp group g1 garp interval 20",
+ "set high-availability vrrp group g1 garp master-delay 5",
+ "set high-availability vrrp group g1 garp master-refresh 50",
+ "set high-availability vrrp group g1 garp master-refresh-repeat 100",
+ "set high-availability vrrp group g1 garp master-repeat 3",
+ "set high-availability vrrp group g1 interface eth2",
+ "set high-availability vrrp group g1 no-preempt",
+ "set high-availability vrrp group g1 peer-address 192.168.1.3",
+ "set high-availability vrrp group g1 priority 100",
+ "set high-availability vrrp group g1 rfc3768-compatibility",
+ "set high-availability vrrp group g1 track exclude-vrrp-interface",
+ "set high-availability vrrp group g1 track interface eth0",
+ "set high-availability vrrp group g1 transition-script backup /var/tmp/script.sh",
+ "set high-availability vrrp group g1 transition-script fault /var/tmp/script.sh",
+ "set high-availability vrrp group g1 transition-script master /var/tmp/script.sh",
+ "set high-availability vrrp group g1 transition-script stop /var/tmp/script.sh",
+ "set high-availability vrrp group g1 vrid 20",
+ "set high-availability vrrp group g2 address 2.2.2.2",
+ "set high-availability vrrp group g2 address 2.2.2.3 interface eth3",
+ "set high-availability vrrp group g2 description 'Group_2'",
+ "set high-availability vrrp group g2 health-check failure-count 5",
+ "set high-availability vrrp group g2 health-check interval 15",
+ "set high-availability vrrp group g2 health-check ping 192.168.1.100",
+ "set high-availability vrrp group g2 health-check script /var/tmp/script.sh",
+ "set high-availability vrrp group g2 hello-source-address 2.2.2.2",
+ "set high-availability vrrp group g2 interface eth1",
+ "set high-availability vrrp group g2 vrid 11",
+ "set high-availability vrrp snmp",
+ "set high-availability vrrp sync-group sg1 health-check failure-count 3",
+ "set high-availability vrrp sync-group sg1 health-check interval 10",
+ "set high-availability vrrp sync-group sg1 health-check ping 192.168.2.100",
+ "set high-availability vrrp sync-group sg1 health-check script /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 member g1",
+ "set high-availability vrrp sync-group sg1 transition-script backup /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 transition-script fault /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 transition-script master /var/tmp/script.sh",
+ "set high-availability vrrp sync-group sg1 transition-script stop /var/tmp/script.sh",
+ ]
+ result = self.execute_module(changed=False)
+ # rendered output order is not significant — use sorted comparison
+ self.assertEqual(
+ sorted(result["rendered"]),
+ sorted(rendered_commands),
+ result["rendered"],
+ )
+
+ def test_vrrp_parsed(self):
+ commands = (
+ "set high-availability disable",
+ "set high-availability virtual-server s1 address 10.10.10.1",
+ "set high-availability virtual-server s1 algorithm round-robin",
+ "set high-availability virtual-server s1 delay-loop 60",
+ "set high-availability virtual-server s1 forward-method direct",
+ "set high-availability virtual-server s1 fwmark 10",
+ "set high-availability virtual-server s1 persistence-timeout 30",
+ "set high-availability virtual-server s1 protocol tcp",
+ "set high-availability virtual-server s1 real-server 10.10.50.1 health-check script '/var/tmp/script.sh'",
+ "set high-availability virtual-server s1 real-server 10.10.50.1 port 443",
+ "set high-availability virtual-server s2 address 10.10.10.2",
+ "set high-availability virtual-server s2 port 81",
+ "set high-availability virtual-server s2 real-server real1 connection-timeout 5",
+ "set high-availability virtual-server s2 real-server real1 port 8081",
+ "set high-availability virtual-server s2 real-server real2 port 8080",
+ "set high-availability vrrp global-parameters garp interval 30",
+ "set high-availability vrrp global-parameters garp master-delay 10",
+ "set high-availability vrrp global-parameters garp master-refresh 100",
+ "set high-availability vrrp global-parameters garp master-refresh-repeat 200",
+ "set high-availability vrrp global-parameters garp master-repeat 5",
+ "set high-availability vrrp global-parameters startup-delay 30",
+ "set high-availability vrrp global-parameters version 3",
+ "set high-availability vrrp group g1 address '1.1.1.1'",
+ "set high-availability vrrp group g1 address '3.3.3.3'",
+ "set high-availability vrrp group g1 address '5.5.5.5' interface 'eth2'",
+ "set high-availability vrrp group g1 advertise-interval 10",
+ "set high-availability vrrp group g1 authentication password 'testpass'",
+ "set high-availability vrrp group g1 authentication type 'plaintext-password'",
+ "set high-availability vrrp group g1 description 'Group_1'",
+ "set high-availability vrrp group g1 disable",
+ "set high-availability vrrp group g1 excluded-address '192.168.1.7' interface 'eth3'",
+ "set high-availability vrrp group g1 excluded-address '192.168.1.8'",
+ "set high-availability vrrp group g1 garp interval 20",
+ "set high-availability vrrp group g1 garp master-delay 5",
+ "set high-availability vrrp group g1 garp master-refresh 50",
+ "set high-availability vrrp group g1 garp master-refresh-repeat 100",
+ "set high-availability vrrp group g1 garp master-repeat 3",
+ "set high-availability vrrp group g1 health-check failure-count 3",
+ "set high-availability vrrp group g1 health-check interval 10",
+ "set high-availability vrrp group g1 health-check ping '192.168.1.5'",
+ "set high-availability vrrp group g1 health-check script 'script.sh'",
+ "set high-availability vrrp group g1 hello-source-address '192.168.1.2'",
+ "set high-availability vrrp group g1 interface 'eth2'",
+ "set high-availability vrrp group g1 no-preempt",
+ "set high-availability vrrp group g1 peer-address '192.168.1.3'",
+ "set high-availability vrrp group g1 priority 100",
+ "set high-availability vrrp group g1 rfc3768-compatibility",
+ "set high-availability vrrp group g1 track exclude-vrrp-interface",
+ "set high-availability vrrp group g1 track interface 'eth1'",
+ "set high-availability vrrp group g1 transition-script backup '/var/tmp/script.sh'",
+ "set high-availability vrrp group g1 transition-script fault '/var/tmp/script.sh'",
+ "set high-availability vrrp group g1 transition-script master '/var/tmp/script.sh'",
+ "set high-availability vrrp group g1 transition-script stop '/var/tmp/script.sh'",
+ "set high-availability vrrp group g1 vrid 20",
+ "set high-availability vrrp group g2 address '192.168.3.3'",
+ "set high-availability vrrp group g2 address '192.168.4.4' interface 'eth3'",
+ "set high-availability vrrp snmp",
+ "set high-availability vrrp sync-group sg1 health-check failure-count 3",
+ "set high-availability vrrp sync-group sg1 health-check interval 10",
+ "set high-availability vrrp sync-group sg1 health-check ping '192.168.1.1'",
+ "set high-availability vrrp sync-group sg1 health-check script '/var/tmp/script.sh'",
+ "set high-availability vrrp sync-group sg1 member 'g1'",
+ "set high-availability vrrp sync-group sg1 transition-script backup '/var/tmp/script.sh'",
+ "set high-availability vrrp sync-group sg1 transition-script fault '/var/tmp/script.sh'",
+ "set high-availability vrrp sync-group sg1 transition-script master '/var/tmp/script.sh'",
+ "set high-availability vrrp sync-group sg1 transition-script stop '/var/tmp/script.sh'",
+ )
+ parsed_str = "\n".join(commands)
+ set_module_args(dict(running_config=parsed_str, state="parsed"))
+ result = self.execute_module(changed=False)
+ parsed_list = {
+ "disable": True,
+ "virtual_servers": [
+ {
+ "name": "s1",
+ "address": "10.10.10.1",
+ "algorithm": "round-robin",
+ "delay_loop": 60,
+ "forward_method": "direct",
+ "fwmark": 10,
+ "persistence_timeout": 30,
+ "protocol": "tcp",
+ "real_server": [
+ {
+ "address": "10.10.50.1",
+ "health_check_script": "/var/tmp/script.sh",
+ "port": 443,
+ },
+ ],
+ },
+ {
+ "name": "s2",
+ "address": "10.10.10.2",
+ "port": 81,
+ "real_server": [
+ {
+ "address": "real1",
+ "connection_timeout": 5,
+ "port": 8081,
+ },
+ {
+ "address": "real2",
+ "port": 8080,
+ },
+ ],
+ },
+ ],
+ "vrrp": {
+ "snmp": "enabled",
+ "global_parameters": {
+ "startup_delay": 30,
+ "version": "3",
+ "garp": {
+ "interval": 30,
+ "master_delay": 10,
+ "master_refresh": 100,
+ "master_refresh_repeat": 200,
+ "master_repeat": 5,
+ },
+ },
+ "groups": [
+ {
+ "name": "g1",
+ "description": "Group_1",
+ "disable": True,
+ "no_preempt": True,
+ "rfc3768_compatibility": True,
+ "interface": "eth2",
+ "address": ["1.1.1.1", "3.3.3.3", "5.5.5.5 interface eth2"],
+ "advertise_interval": 10,
+ "peer_address": "192.168.1.3",
+ "priority": 100,
+ "vrid": 20,
+ "garp": {
+ "interval": 20,
+ "master_delay": 5,
+ "master_refresh": 50,
+ "master_refresh_repeat": 100,
+ "master_repeat": 3,
+ },
+ "authentication": {
+ "type": "plaintext-password",
+ "password": "testpass",
+ },
+ "transition_script": {
+ "master": "/var/tmp/script.sh",
+ "backup": "/var/tmp/script.sh",
+ "fault": "/var/tmp/script.sh",
+ "stop": "/var/tmp/script.sh",
+ },
+ "health_check": {
+ "failure_count": 3,
+ "interval": 10,
+ "ping": "192.168.1.5",
+ "script": "script.sh",
+ },
+ "track": {
+ "exclude_vrrp_interface": True,
+ "interface": ["eth1"],
+ },
+ "excluded_address": [
+ "192.168.1.7 interface eth3",
+ "192.168.1.8",
+ ],
+ "hello_source_address": "192.168.1.2",
+ },
+ {
+ "name": "g2",
+ "address": ["192.168.3.3", "192.168.4.4 interface eth3"],
+ "disable": False,
+ "no_preempt": False,
+ "rfc3768_compatibility": False,
+ },
+ ],
+ "sync_groups": [
+ {
+ "name": "sg1",
+ "member": ["g1"],
+ "transition_script": {
+ "master": "/var/tmp/script.sh",
+ "backup": "/var/tmp/script.sh",
+ "fault": "/var/tmp/script.sh",
+ "stop": "/var/tmp/script.sh",
+ },
+ "health_check": {
+ "failure_count": 3,
+ "interval": 10,
+ "ping": "192.168.1.1",
+ "script": "/var/tmp/script.sh",
+ },
+ },
+ ],
+ },
+ }
+ self.assertEqual(parsed_list, result["parsed"])
+
+ def test_vrrp_gathered(self):
+ set_module_args(dict(state="gathered"))
+ result = self.execute_module(changed=False)
+ gathered_list = {
+ "disable": True,
+ "virtual_servers": [
+ {
+ "name": "s1",
+ "address": "10.10.10.1",
+ "algorithm": "round-robin",
+ "delay_loop": 60,
+ "forward_method": "direct",
+ "fwmark": 10,
+ "persistence_timeout": 30,
+ "protocol": "tcp",
+ "real_server": [
+ {
+ "address": "10.10.50.1",
+ "health_check_script": "/var/tmp/script.sh",
+ "port": 443,
+ },
+ ],
+ },
+ {
+ "name": "s2",
+ "address": "10.10.10.2",
+ "port": 81,
+ "real_server": [
+ {
+ "address": "real1",
+ "connection_timeout": 5,
+ "port": 8081,
+ },
+ {
+ "address": "real2",
+ "port": 8080,
+ },
+ ],
+ },
+ ],
+ "vrrp": {
+ "snmp": "enabled",
+ "global_parameters": {
+ "startup_delay": 30,
+ "version": "3",
+ "garp": {
+ "interval": 30,
+ "master_delay": 10,
+ "master_refresh": 100,
+ "master_refresh_repeat": 200,
+ "master_repeat": 5,
+ },
+ },
+ "groups": [
+ {
+ "name": "g1",
+ "description": "Group_1",
+ "disable": True,
+ "no_preempt": True,
+ "rfc3768_compatibility": True,
+ "interface": "eth2",
+ "address": ["1.1.1.1", "3.3.3.3", "5.5.5.5 interface eth2"],
+ "advertise_interval": 10,
+ "peer_address": "192.168.1.3",
+ "priority": 100,
+ "vrid": 20,
+ "garp": {
+ "interval": 20,
+ "master_delay": 5,
+ "master_refresh": 50,
+ "master_refresh_repeat": 100,
+ "master_repeat": 3,
+ },
+ "authentication": {
+ "type": "plaintext-password",
+ "password": "testpass",
+ },
+ "transition_script": {
+ "master": "/var/tmp/script.sh",
+ "backup": "/var/tmp/script.sh",
+ "fault": "/var/tmp/script.sh",
+ "stop": "/var/tmp/script.sh",
+ },
+ "health_check": {
+ "failure_count": 3,
+ "interval": 10,
+ "ping": "192.168.1.5",
+ "script": "script.sh",
+ },
+ "track": {
+ "exclude_vrrp_interface": True,
+ "interface": ["eth1"],
+ },
+ "excluded_address": [
+ "192.168.1.7 interface eth3",
+ "192.168.1.8",
+ ],
+ "hello_source_address": "192.168.1.2",
+ },
+ ],
+ "sync_groups": [
+ {
+ "name": "sg1",
+ "member": ["g1"],
+ "transition_script": {
+ "master": "/var/tmp/script.sh",
+ "backup": "/var/tmp/script.sh",
+ "fault": "/var/tmp/script.sh",
+ "stop": "/var/tmp/script.sh",
+ },
+ "health_check": {
+ "failure_count": 3,
+ "interval": 10,
+ "ping": "192.168.1.1",
+ "script": "/var/tmp/script.sh",
+ },
+ },
+ ],
+ },
+ }
+ self.assertEqual(gathered_list, result["gathered"])
+
+ def test_vrrp_groups_deleted(self):
+ set_module_args(
+ dict(
+ config=dict(
+ vrrp=dict(
+ groups=[
+ dict(name="g1"),
+ ],
+ sync_groups=[
+ dict(name="sg1"),
+ ],
+ ),
+ ),
+ state="deleted",
+ ),
+ )
+ commands = [
+ "delete high-availability vrrp group g1",
+ "delete high-availability vrrp sync-group sg1",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vrrp_objs_deleted(self):
+ set_module_args(
+ dict(
+ config=dict(
+ disable=False,
+ vrrp=dict(
+ global_parameters=dict(
+ startup_delay=32,
+ garp=dict(),
+ ),
+ groups=[
+ dict(name="g1"),
+ ],
+ sync_groups=[],
+ ),
+ virtual_servers=[],
+ ),
+ state="deleted",
+ ),
+ )
+ commands = [
+ "delete high-availability virtual-server",
+ "delete high-availability vrrp global-parameters garp",
+ "delete high-availability vrrp global-parameters startup-delay",
+ "delete high-availability vrrp group g1",
+ "delete high-availability vrrp sync-group",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vrrp_deleted_all(self):
+ set_module_args(
+ dict(
+ config=dict(),
+ state="deleted",
+ ),
+ )
+ commands = [
+ "delete high-availability",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vrrp_purged(self):
+ set_module_args(
+ dict(
+ config=dict(),
+ state="purged",
+ ),
+ )
+ commands = [
+ "delete high-availability",
+ ]
+ self.execute_module(changed=True, commands=commands)