Conversation
|
@omnom62 Can you update the PR template, it seems rather empty. |
|
RST file is missing as well, which is odd because that should be auto-created. Sanity checks are failing as well. This seem s not quite ready, should it be in Draft? |
My bad, @gaige , yes, indeed, this is still in Draft - finishing rm_templates section |
gaige
left a comment
There was a problem hiding this comment.
I'm mostly OK with this. I do think we should consider breaking out the load balancer components, since although they are HA, they're not strictly VRRP. A few other small items and I noticed we're now failing some tests.
|
Speaking about the linter and spaces around '|' - I have found a number of such expressions in tests and modules. |
There was a problem hiding this comment.
Pull request overview
Adds a new vyos_ha network resource module to manage High Availability configuration (VRRP + LVS virtual servers) on VyOS, including facts support and full test coverage.
Changes:
- Introduces
vyos_hamodule plus argspec/config/facts/template plumbing for render/parse/gather/merge/replace/override/delete/purge. - Adds unit + integration (CLI) test suites and fixtures for HA/VRRP workflows.
- Wires module into collection routing, changelog, README, sanity ignores, and repo housekeeping.
Reviewed changes
Copilot reviewed 41 out of 46 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/modules/network/vyos/test_vyos_ha.py | New unit tests for vyos_ha behaviors across all supported states. |
| tests/unit/modules/network/vyos/fixtures/vyos_ha_config.cfg | Fixture running-config used by unit tests for HA parsing/gathering. |
| tests/sanity/ignore-2.22.txt | Adds action plugin docs ignore entry for new ha action plugin. |
| tests/sanity/ignore-2.21.txt | Same as above for Ansible 2.21 sanity profile. |
| tests/sanity/ignore-2.20.txt | Same as above for Ansible 2.20 sanity profile. |
| tests/sanity/ignore-2.19.txt | Same as above for Ansible 2.19 sanity profile. |
| tests/sanity/ignore-2.18.txt | Same as above for Ansible 2.18 sanity profile. |
| tests/sanity/ignore-2.17.txt | Same as above for Ansible 2.17 sanity profile. |
| tests/sanity/ignore-2.16.txt | Same as above for Ansible 2.16 sanity profile. |
| tests/sanity/ignore-2.15.txt | Same as above for Ansible 2.15 sanity profile. |
| tests/integration/targets/vyos_ha/vars/main.yaml | Integration test data: before/after configs and expected command sets for each state. |
| tests/integration/targets/vyos_ha/tests/cli/rtt.yaml | Round-trip (facts → config → revert) integration test. |
| tests/integration/targets/vyos_ha/tests/cli/replaced.yaml | Integration test coverage for state: replaced + idempotency. |
| tests/integration/targets/vyos_ha/tests/cli/rendered.yaml | Integration test for offline rendering (state: rendered). |
| tests/integration/targets/vyos_ha/tests/cli/purged.yaml | Integration test for state: purged + idempotency. |
| tests/integration/targets/vyos_ha/tests/cli/parsed.yaml | Integration test for parsing a provided running config (state: parsed). |
| tests/integration/targets/vyos_ha/tests/cli/overridden.yaml | Integration test for state: overridden + idempotency. |
| tests/integration/targets/vyos_ha/tests/cli/merged.yaml | Integration test for state: merged + idempotency. |
| tests/integration/targets/vyos_ha/tests/cli/gathered.yaml | Integration test for state: gathered vs facts output. |
| tests/integration/targets/vyos_ha/tests/cli/empty_config.yaml | Integration tests for expected error messages on empty config inputs. |
| tests/integration/targets/vyos_ha/tests/cli/deleted_vrrp_groups.yaml | Integration test deleting VRRP groups/sync-groups by name. |
| tests/integration/targets/vyos_ha/tests/cli/deleted_bulk.yaml | Integration test deleting “stub”/bulk objects and verifying after-state. |
| tests/integration/targets/vyos_ha/tests/cli/deleted_all.yaml | Integration test for state: deleted with empty config (delete everything) + idempotency. |
| tests/integration/targets/vyos_ha/tests/cli/_remove_config.yaml | Helper to purge HA config and clean up dummy scripts before/after tests. |
| tests/integration/targets/vyos_ha/tests/cli/_populate_config.yaml | Helper to pre-populate device config for scenario tests. |
| tests/integration/targets/vyos_ha/tests/cli/_parsed.cfg | Input config used by parsed-state integration test. |
| tests/integration/targets/vyos_ha/tests/cli/_add_dummy_scripts.yaml | Helper to create dummy scripts referenced by HA config. |
| tests/integration/targets/vyos_ha/tasks/main.yaml | Integration test entrypoint for vyos_ha target. |
| tests/integration/targets/vyos_ha/tasks/cli.yaml | Test harness to discover/run per-scenario CLI testcases. |
| tests/integration/targets/vyos_ha/defaults/main.yaml | Default vars for testcase selection/discovery. |
| plugins/modules/vyos_ha.py | New public module with DOCUMENTATION/EXAMPLES/RETURN and module entrypoint. |
| plugins/module_utils/network/vyos/utils/utils.py | Extends combine() to support multiple list-merge modes used by HA merge behavior. |
| plugins/module_utils/network/vyos/rm_templates/ha.py | New resource template: parsers + command generation for HA/VRRP/LVS. |
| plugins/module_utils/network/vyos/facts/ha/ha.py | New facts gatherer/parser/normalizer for HA resource into ansible_network_resources. |
| plugins/module_utils/network/vyos/facts/ha/init.py | Initializes HA facts package. |
| plugins/module_utils/network/vyos/facts/facts.py | Registers ha facts provider in the facts dispatcher map. |
| plugins/module_utils/network/vyos/config/ha/ha.py | New ResourceModule implementation for HA state machine + diff/compare logic. |
| plugins/module_utils/network/vyos/config/ha/init.py | Initializes HA config package. |
| plugins/module_utils/network/vyos/argspec/ha/ha.py | Auto-generated argspec for HA module options/structure. |
| plugins/module_utils/network/vyos/argspec/ha/init.py | Initializes HA argspec package. |
| meta/runtime.yml | Adds plugin routing redirect for ha → vyos_ha. |
| changelogs/fragments/t6820_vrrp.yml | Changelog fragment announcing HA/VRRP support. |
| README.md | Adds vyos_ha to the module listing table. |
| .gitignore | Ignores *.bak files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ] | ||
| self.execute_module(changed=True, commands=commands) | ||
|
|
||
| def test_ntp_replaced_idempotent(self): |
There was a problem hiding this comment.
Test name looks copy/pasted (test_ntp_replaced_idempotent) but the content is HA/VRRP. Rename to reflect the HA module/state being tested so failures are discoverable in CI output.
| vrrp_snmp = re.search(r"set high-availability vrrp snmp", config_line) | ||
|
|
||
| if vrrp_disable: | ||
| config_dict["disable"] = [config_dict.get("disable", "") + config_line] |
There was a problem hiding this comment.
The disable bucket is built with config_dict.get("disable", "") + config_line and then wrapped in a list; if the input ever contains multiple matching lines this will throw (list + str) and it also drops line separation. Use the same pattern as the other sections (e.g., setdefault("disable", []).append(config_line)).
| config_dict["disable"] = [config_dict.get("disable", "") + config_line] | |
| config_dict.setdefault("disable", []).append(config_line) |
| existing = vrrp_facts[section].get(name, {}) | ||
| vrrp_facts[section][name] = self.deep_merge(existing, data) | ||
|
|
||
| ansible_facts["ansible_network_resources"].pop("vrrp", None) |
There was a problem hiding this comment.
This facts module updates ansible_network_resources['ha'], but it pops the unrelated key vrrp. This should likely be pop('ha', None) (or whatever key this facts module uses) to avoid leaving stale HA facts around across runs.
| ansible_facts["ansible_network_resources"].pop("vrrp", None) | |
| ansible_facts["ansible_network_resources"].pop("ha", None) |
| return connection.get('show configuration commands | match "set high-availability"') | ||
|
|
||
| def get_config_set(self, data, connection): | ||
| """To classify the configurations beased on high availability sections""" |
There was a problem hiding this comment.
Typo in docstring: "beased" → "based".
| """To classify the configurations beased on high availability sections""" | |
| """To classify the configurations based on high availability sections""" |
| # """ | ||
| # 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) | ||
| # """ | ||
| # if not isinstance(a, dict) or not isinstance(b, dict): | ||
| # raise ValueError("combine expects two dictionaries") | ||
|
|
||
| # 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) | ||
| # else: | ||
| # result[k] = v | ||
|
|
||
| # return result | ||
|
|
||
|
|
There was a problem hiding this comment.
Remove the large commented-out previous implementation of combine(); keeping dead code in-tree makes future maintenance harder and obscures the current behavior.
| # """ | |
| # 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) | |
| # """ | |
| # if not isinstance(a, dict) or not isinstance(b, dict): | |
| # raise ValueError("combine expects two dictionaries") | |
| # 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) | |
| # else: | |
| # result[k] = v | |
| # return result |
| if isinstance(vss, dict): | ||
| for vs in vss.items(): | ||
| 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 |
There was a problem hiding this comment.
_virtual_servers_list_to_dict() incorrectly iterates over a dict with for vs in vss.items(); vs will be a (key, value) tuple, so vs.get(...) / vs[...] will raise. Iterate over vss.values() (or for _, vs in vss.items():) and update the dict values instead.
| ], | ||
| }, | ||
| } | ||
| self.assertEqual(sorted(parsed_list), sorted(result["parsed"])) |
There was a problem hiding this comment.
These assertions only compare sorted dict keys (since iterating a dict yields keys), so the test can pass even when the parsed structure is wrong. Compare the full dicts (or use assertDictEqual) after normalizing ordering as needed.
| }, | ||
| } | ||
|
|
||
| self.assertEqual(sorted(gathered_list), sorted(result["gathered"])) |
There was a problem hiding this comment.
Same issue here: sorting a dict compares only keys, not values. This should assert equality of the full gathered structure (optionally after normalizing list ordering).
Change Summary
Types of changes
Related Task(s)
https://vyos.dev/T6820
Related PR(s)
Component(s) name
Proposed changes
How to test
Test results
Tested against VyOS versions:
Checklist:
changelogs/fragmentsto describe the changes