summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromnom62 <75066712+omnom62@users.noreply.github.com>2026-07-28 05:09:47 +1000
committerGitHub <noreply@github.com>2026-07-28 05:09:47 +1000
commitb0243027a73583eed7014ae924fbc79bb55577b5 (patch)
tree822de31d5246309612f50c6a1c8b6f5717dba8eb
parent2e2a0f2da3df1a3197bef3c19d3e7deecf869759 (diff)
parent17041b6f6953b993762854a67953fb35176b46e7 (diff)
downloadvyos.vyos-b0243027a73583eed7014ae924fbc79bb55577b5.tar.gz
vyos.vyos-b0243027a73583eed7014ae924fbc79bb55577b5.zip
Merge branch 'main' into fix/black-formatting
-rw-r--r--.coderabbit.yaml205
-rw-r--r--.git-blame-ignore-revs4
l---------.github/copilot-instructions.md1
-rw-r--r--.github/instructions/modules.instructions.md37
-rw-r--r--.github/instructions/tests.instructions.md114
-rw-r--r--.github/mergify.yml34
-rw-r--r--.github/workflows/ah_token_refresh.yml2
-rw-r--r--.github/workflows/cla-check.yml2
-rw-r--r--.github/workflows/codecoverage.yml2
-rw-r--r--.github/workflows/codeql.yml30
-rw-r--r--.gitignore6
-rw-r--r--AGENTS.md39
-rw-r--r--changelogs/fragments/T8512-isort-fix.yml3
-rw-r--r--changelogs/fragments/T8518-vlan-unit-tests.yml3
-rw-r--r--changelogs/fragments/T8595_add_agents_md.yml2
-rw-r--r--changelogs/fragments/T8609_rm_templates_regex_backtracking.yml12
-rw-r--r--changelogs/fragments/T9082-codeql-workflow.yml2
-rw-r--r--changelogs/fragments/coderabbit-config.yml3
-rw-r--r--changelogs/fragments/copilot-instructions.yml4
-rw-r--r--changelogs/fragments/deprecated-cleanup.yml3
-rw-r--r--changelogs/fragments/fix-icmp-parse-attr.yml5
-rw-r--r--changelogs/fragments/fix-snmp-server-redirect.yml3
-rw-r--r--changelogs/fragments/fix-vlan-purge.yml3
-rw-r--r--changelogs/fragments/formatting-compliance.yml3
-rw-r--r--changelogs/fragments/missing-unit-tests.yml3
-rw-r--r--changelogs/fragments/t6818_password_filtering.yml2
-rw-r--r--changelogs/fragments/t8851-coderabbit-centralized.yml6
-rw-r--r--changelogs/fragments/vyos-user-quote-plaintext-password.yml3
-rw-r--r--meta/runtime.yml2
-rw-r--r--plugins/module_utils/network/vyos/config/vrf/vrf.old313
-rw-r--r--plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py4
-rw-r--r--plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py5
-rw-r--r--plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py8
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py49
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py49
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/bgp_global.py95
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py95
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py9
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py13
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/route_maps.py145
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/route_maps_14.py123
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/snmp_server.py78
-rw-r--r--plugins/module_utils/network/vyos/utils/version.py1
-rw-r--r--plugins/modules/vyos_bgp_global.py139
-rw-r--r--plugins/modules/vyos_config.py47
-rw-r--r--plugins/modules/vyos_user.py4
-rw-r--r--plugins/modules/vyos_vlan.py12
-rw-r--r--tests/integration/targets/vyos_config/tests/cli/check_config.yaml34
-rw-r--r--tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces.cfg10
-rw-r--r--tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces_empty.cfg7
-rw-r--r--tests/unit/modules/network/vyos/test_rm_templates_perf.py163
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py14
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_user.py30
-rw-r--r--tests/unit/modules/network/vyos/test_vyos_vlan.py158
54 files changed, 1233 insertions, 910 deletions
diff --git a/.coderabbit.yaml b/.coderabbit.yaml
new file mode 100644
index 00000000..c14e52eb
--- /dev/null
+++ b/.coderabbit.yaml
@@ -0,0 +1,205 @@
+# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
+#
+# Per-repo CodeRabbit override for vyos/vyos.vyos (Ansible network collection).
+#
+# Most behavior is inherited from the org-level central baseline at
+# https://github.com/vyos/coderabbit/blob/production/.coderabbit.yaml.
+# This file keeps only what's distinct to this repo:
+# - Ansible-collection-specific `path_instructions` (15 entries).
+# - Two `path_filters` entries that aren't in the central list.
+# - `base_branches: [main]` — central uses VyOS release-train names; this
+# repo is an Ansible collection that lives on `main`.
+# - `knowledge_base.jira` scoped to VD.
+#
+# Migrated from standalone (591-line rich config from T8584, sha
+# 38eae56fb991b63e0c89245e4ef5fc130d3f5c8b) to centralized inheritance
+# mode under T8851 on 2026-05-24. The standalone config preceded
+# `vyos/coderabbit` central-config introduction (2026-05-12).
+
+inheritance: true
+
+reviews:
+ auto_review:
+ base_branches:
+ - main
+
+ path_filters:
+ # Repo-specific filters that aren't in the central baseline. The
+ # central already filters `!**/__pycache__/**`, `!**/*.pyc`,
+ # `!**/*.egg-info/**`, `!**/.venv/**`, `!**/.worktrees/**` — so they
+ # are not repeated here.
+ - '!changelogs/changelog.yaml'
+ - '!.collections/**'
+
+ path_instructions:
+ # ── Global PR hygiene ──────────────────────────────────────────────
+ - path: '**'
+ instructions: |
+ This is the vyos.vyos Ansible network collection (namespace=vyos, name=vyos,
+ version=6.0.0). PR titles must follow the format `T{id}: description` referencing
+ a Phorge task at vyos.dev. Every PR must include a changelog fragment in
+ changelogs/fragments/ (YAML, valid keys: major_changes, minor_changes,
+ breaking_changes, deprecated_features, removed_features, security_fixes,
+ bugfixes, known_issues, doc_changes, trivial; plus release_summary as a
+ prelude section). Style: black line-length=100, isort
+ profile=black line_length=100, flake8 max-line-length=120. Do not suggest
+ 88-char wrapping.
+
+ # ── Module entry points ────────────────────────────────────────────
+ - path: 'plugins/modules/vyos_*.py'
+ instructions: |
+ Module entry points. Each file must contain three YAML triple-string blocks:
+ DOCUMENTATION, EXAMPLES, and RETURN — this is Ansible's documentation contract,
+ not Python docstrings. Verify:
+ - DOCUMENTATION includes: module, author, short_description, description,
+ version_added, extends_documentation_fragment (vyos.vyos.vyos), options with
+ types and descriptions, and a notes section listing tested VyOS versions.
+ - EXAMPLES has at least one working task per supported state.
+ - RETURN documents all return keys with description, returned, type, and sample.
+ - The module wires argspec, config, and facts classes correctly.
+ - State choices include the full set where applicable: merged, replaced,
+ overridden, deleted, gathered, parsed, rendered.
+ Do not add Python-style docstrings (def-level) to these files — the YAML blocks
+ are the canonical documentation.
+
+ # ── Argspec (auto-generated) ───────────────────────────────────────
+ - path: 'plugins/module_utils/network/vyos/argspec/**'
+ instructions: |
+ Auto-generated by the Ansible resource module builder. These files carry a
+ "DO NOT EDIT" warning header. Do not suggest modifications to auto-generated
+ argspec files — changes will be overwritten. If the schema needs updating,
+ the resource module builder must regenerate it. Only flag issues if the
+ argument_spec dict has obvious type mismatches or missing required fields
+ that would cause runtime failures.
+
+ # ── Config classes ─────────────────────────────────────────────────
+ - path: 'plugins/module_utils/network/vyos/config/**'
+ instructions: |
+ Config builders extending ansible.netcommon ConfigBase or ResourceModule.
+ These generate VyOS CLI commands from desired state. Verify:
+ - execute_module() handles all declared states correctly.
+ - set_config() and _set_config() process gathered facts and desired config
+ without data loss.
+ - Command generation produces valid VyOS CLI syntax (set/delete prefixes,
+ proper quoting of values with spaces).
+ - No silent swallowing of unknown keys — unknown config should raise or warn.
+ - Methods that compare current vs desired state handle empty/None gracefully.
+ Some older config files have auto-generated headers — do not restructure those.
+
+ # ── Facts classes ──────────────────────────────────────────────────
+ - path: 'plugins/module_utils/network/vyos/facts/**'
+ instructions: |
+ Facts classes parse raw VyOS CLI output into structured dicts. Verify:
+ - Regex patterns handle edge cases (missing fields, empty values, quoted strings).
+ - populate() returns a clean dict even when device output is incomplete.
+ - get_device_data() uses the correct show command for the resource.
+ - facts/facts.py FACT_RESOURCE_SUBSETS and FACT_LEGACY_SUBSETS stay in sync
+ with available fact classes.
+ - Legacy facts (facts/legacy/) use run_commands(); resource facts use
+ get_resource_connection().
+
+ # ── RM Templates ───────────────────────────────────────────────────
+ - path: 'plugins/module_utils/network/vyos/rm_templates/*.py'
+ instructions: |
+ Parser templates mapping structured data to VyOS CLI commands and vice versa.
+ Files with a `_14` suffix target VyOS 1.4+ behavior — do not suggest merging
+ them with the base version. Verify:
+ - _tmplt_* helper functions produce syntactically valid VyOS commands.
+ - Regex patterns in PARSERS list correctly capture all variations of the CLI
+ output (quoted values, optional fields, nested hierarchies).
+ - New templates include both set and delete command generation.
+ - compval/getval paths match the argspec structure.
+
+ # ── Cliconf plugin ─────────────────────────────────────────────────
+ - path: 'plugins/cliconf/vyos.py'
+ instructions: |
+ Low-level CLI abstraction for VyOS. Handles configure mode, commit, diff,
+ command execution. Changes here affect all modules. Verify:
+ - edit_config() enters configure mode and commits correctly.
+ - get_diff() returns accurate before/after config diffs.
+ - Error handling catches VyOS-specific error patterns (commit failures,
+ invalid commands).
+ - __rpc__ list matches actually implemented methods.
+
+ # ── Terminal plugin ────────────────────────────────────────────────
+ - path: 'plugins/terminal/vyos.py'
+ instructions: |
+ Terminal prompt detection and initialization. Changes affect connection
+ reliability. Verify regex patterns against actual VyOS prompt formats
+ (configure mode, operational mode, different shell variants). Do not
+ remove existing patterns without testing against all supported VyOS versions.
+
+ # ── Action plugin ──────────────────────────────────────────────────
+ - path: 'plugins/action/vyos.py'
+ instructions: |
+ Auto-proxies all modules to the device. Must validate network_cli connection
+ type. Symlinks from each module name point here. Keep minimal — logic belongs
+ in config classes, not the action plugin.
+
+ # ── Changelog fragments ────────────────────────────────────────────
+ - path: 'changelogs/fragments/*.{yaml,yml}'
+ instructions: |
+ Changelog fragments for ansible-changelog. Valid top-level keys:
+ major_changes, minor_changes, breaking_changes, deprecated_features,
+ removed_features, security_fixes, bugfixes, known_issues, doc_changes,
+ trivial. release_summary is a prelude section (one per release).
+ Fragment filename should be descriptive (e.g., fix-bgp-neighbor-timers.yml).
+ Use `trivial` for tooling/housekeeping. Entries should be complete sentences.
+
+ # ── CI workflows ───────────────────────────────────────────────────
+ - path: '.github/workflows/**'
+ instructions: |
+ CI pipeline: tests.yml (main CI with changelog, build, lint, sanity, unit jobs),
+ codecoverage.yml, release.yml (Galaxy + Automation Hub publish), check_label.yaml,
+ cla-check.yml. Changes to release.yml or ah_token_refresh.yml affect publishing
+ credentials — review with extra care. Do not remove the `all_green` aggregation
+ job from tests.yml.
+
+ # ── Unit tests ─────────────────────────────────────────────────────
+ - path: 'tests/unit/**'
+ instructions: |
+ Unit tests use pytest + unittest.TestCase via TestVyosModule base class.
+ Key patterns:
+ - All test classes inherit TestVyosModule (from vyos_module.py).
+ - setUp() creates and starts mock patches; tearDown() stops them.
+ - execute_module(failed, changed, commands, sort) is the primary assertion method.
+ - load_fixtures() is overridden per test class to wire mock return values.
+ - Fixture files (.cfg) go in tests/unit/modules/network/vyos/fixtures/.
+ - Use load_fixture(name) to read fixtures — never inline raw config strings.
+ - set_module_args(dict(...)) configures module input before execution.
+ Style: black line-length=100, assertions via self.assertEqual / self.assertIn /
+ execute_module kwargs. pytest-xdist runs tests in parallel (-n 2).
+
+ # ── Test fixtures ──────────────────────────────────────────────────
+ - path: 'tests/unit/modules/network/vyos/fixtures/**'
+ instructions: |
+ Raw VyOS CLI output files (.cfg). These are loaded by load_fixture() and
+ cached in memory. Format is VyOS `set ...` configuration syntax or show
+ command output. Fixture filenames follow the pattern:
+ vyos_{module}_config.cfg (base) or vyos_{module}_config_v14.cfg (VyOS 1.4+).
+ New fixtures must be syntactically valid VyOS config. Do not add JSON fixtures
+ unless the test explicitly requires JSON parsing.
+
+ # ── Collection metadata ────────────────────────────────────────────
+ - path: 'galaxy.yml'
+ instructions: |
+ Collection metadata. namespace=vyos, name=vyos. Version bumps must be
+ coordinated with release process. Dependency on ansible.netcommon>=2.5.1
+ is required. Do not add unnecessary dependencies.
+
+ - path: 'meta/runtime.yml'
+ instructions: |
+ Module redirects and tombstones. Adding a new module requires a redirect
+ entry (short name → FQCN). Tombstoned modules (logging, vyos_logging) must
+ not be un-tombstoned. requires_ansible must stay >=2.15.0 unless explicitly
+ bumping minimum version.
+
+
+knowledge_base:
+ jira:
+ # `auto` activates Jira context lookups when this repo lives on an
+ # org with an Atlassian OAuth grant attached (VyOS-Networks); on the
+ # public vyos source it self-disables.
+ usage: auto
+ project_keys:
+ - VD
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 00000000..dedbdc55
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,4 @@
+# Formatting commits - ignore in git blame
+# style: apply black formatting across entire codebase
+# NOTE: Update this hash after the formatting PR is merged
+221ac461e4b3dcf37629f4cce89e45a59ace873c
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 120000
index 00000000..be77ac83
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1 @@
+../AGENTS.md \ No newline at end of file
diff --git a/.github/instructions/modules.instructions.md b/.github/instructions/modules.instructions.md
new file mode 100644
index 00000000..d7782e1e
--- /dev/null
+++ b/.github/instructions/modules.instructions.md
@@ -0,0 +1,37 @@
+---
+applyTo: "plugins/**,meta/runtime.yml"
+---
+
+# Plugin conventions
+
+## Module option descriptions
+
+- Must be complete English sentences ending with a period.
+- No grammar errors. Common mistake: "the number hops" should be "the number of hops".
+- Deprecated options must include `removed_in_version` and `removed_from_collection` fields.
+- Do not add new options with `deprecated: true` — remove deprecated options entirely.
+
+## rm_templates files
+
+Files in `plugins/module_utils/network/vyos/rm_templates/` define regex parsers and Jinja2 generators for a resource module. Each entry has:
+- `name` — unique identifier
+- `getval` — compiled regex with named groups
+- `setval` — Jinja2 template or callable producing a VyOS CLI command
+- `result` — dict mapping regex groups to facts structure
+- `shared` (optional) — bool, whether the template applies to a shared config block
+
+## meta/runtime.yml redirects
+
+Short-name redirects must point to the actual module name with the `vyos_` prefix. For example:
+```yaml
+snmp_server:
+ redirect: vyos.vyos.vyos_snmp_server # correct
+ # NOT: vyos.vyos.vyos_snmp_servers # wrong (pluralized)
+```
+
+Verify any changed redirect target exists as a real module file under `plugins/modules/`.
+
+## cliconf / terminal plugins
+
+`plugins/cliconf/vyos.py` — do not modify without understanding edit-mode and commit semantics.
+`plugins/terminal/vyos.py` — handles prompt detection; regex changes require testing against all supported VyOS versions listed in README.md.
diff --git a/.github/instructions/tests.instructions.md b/.github/instructions/tests.instructions.md
new file mode 100644
index 00000000..150db41e
--- /dev/null
+++ b/.github/instructions/tests.instructions.md
@@ -0,0 +1,114 @@
+---
+applyTo: "tests/unit/**"
+---
+
+# Unit test conventions
+
+## Base class and structure
+
+All test classes inherit from `TestVyosModule` in `tests/unit/modules/network/vyos/vyos_module.py`.
+
+```python
+class TestVyosFooModule(TestVyosModule):
+ module = vyos_foo
+
+ def setUp(self): ...
+ def tearDown(self): ...
+ def load_fixtures(self, commands=None, filename=None): ...
+ def test_...(self): ...
+```
+
+## Mocking: resource modules
+
+Resource module tests require two framework-level patches in `setUp` with corresponding cleanup in `tearDown`:
+
+```python
+# in setUp:
+self.mock_get_resource_connection_config = patch(
+ "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.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()
+
+# in tearDown:
+self.mock_get_resource_connection_config.stop()
+self.mock_get_resource_connection_facts.stop()
+```
+
+Most resource module tests also patch the facts class's `get_device_data` method directly and use it in `load_fixtures`:
+
+```python
+# in setUp:
+self.mock_execute_show_command = patch(
+ "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos."
+ "facts.{resource}.{resource}.{Resource}Facts.get_device_data"
+)
+self.execute_show_command = self.mock_execute_show_command.start()
+
+# in load_fixtures:
+def load_from_file(*args, **kwargs):
+ return load_fixture("vyos_{resource}_config.cfg")
+self.execute_show_command.side_effect = load_from_file
+```
+
+An alternative pattern sets `self.get_resource_connection_facts.return_value.get_config.return_value = fixture_data` in `load_fixtures` instead — both approaches work, but the `get_device_data` pattern is used by most existing tests.
+
+## Mocking: legacy modules
+
+Legacy modules (`vyos_vlan`, `vyos_config`, `vyos_command`, etc.) patch the specific helper the module calls, directly in the module under test. Which helper depends on the module:
+
+```python
+# vyos_command, vyos_facts, vyos_ping — patch run_commands
+self.mock_run_commands = patch("ansible_collections.vyos.vyos.plugins.modules.vyos_foo.run_commands")
+# vyos_config — patch get_config / load_config
+self.mock_load_config = patch("ansible_collections.vyos.vyos.plugins.modules.vyos_foo.load_config")
+```
+
+In `load_fixtures`, configure the mock using `side_effect` (for dynamic fixture loading) or `return_value` (for a fixed response):
+
+```python
+# side_effect — used by most existing legacy tests
+def load_from_file(*args, **kwargs):
+ return load_fixture("vyos_foo_config.cfg")
+self.run_commands.side_effect = load_from_file
+
+# return_value — acceptable for simple fixed responses
+self.run_commands.return_value = [SHOW_OUTPUT]
+```
+
+## load_fixtures and filename
+
+`execute_module()` always calls `load_fixtures()`. Never set mock return values inside a test method — they will be overwritten by the next `execute_module` call. Use the `filename` parameter to vary the fixture:
+
+```python
+def load_fixtures(self, commands=None, filename=None):
+ if filename == "empty":
+ self.run_commands.return_value = [EMPTY_OUTPUT]
+ else:
+ self.run_commands.return_value = [DEFAULT_OUTPUT]
+```
+
+Then call: `self.execute_module(changed=True, commands=commands, filename="empty")`
+
+## Fixture files
+
+Raw device CLI output lives in `tests/unit/modules/network/vyos/fixtures/` as `.cfg` files.
+Load with `load_fixture("vyos_foo_config.cfg")`.
+
+## Required test coverage for resource modules
+
+A complete resource module test file should cover all applicable states:
+- `merged` (including an idempotent case)
+- `replaced`
+- `overridden`
+- `deleted`
+- `rendered` — assert `result["rendered"]` matches expected CLI commands
+- `gathered` — assert `result["gathered"]` contains expected structured data
+- `parsed` — pass `running_config=raw_string` and assert `result["parsed"]`
+
+Flag test files that are missing `rendered`, `gathered`, or `parsed` tests without explanation.
+
diff --git a/.github/mergify.yml b/.github/mergify.yml
new file mode 100644
index 00000000..43353ea4
--- /dev/null
+++ b/.github/mergify.yml
@@ -0,0 +1,34 @@
+# yaml-language-server: $schema=https://docs.mergify.com/mergify-configuration-schema.json
+extends: mergify
+merge_protections_settings:
+ reporting_method: check-runs
+
+pull_request_rules:
+ - name: Flag product T-ID format violation in PR title or commit messages
+ description: >
+ Product-repo convention: the PR title AND every commit's first line must
+ match `T<digits>: <text>` (optional `scope: ` prefix). The title is always
+ checked; the per-commit check is exempted when a maintainer applies the
+ `legacy` label — an escape hatch for grandfathered PRs whose commit history
+ cannot be rewritten (repos that squash-merge AND block force-push, e.g.
+ vyos.vyos enforces `non_fast_forward` on ~ALL branches with zero bypass).
+ New PRs are still nudged toward the convention; `legacy` is the deliberate,
+ maintainer-controlled opt-out. Relocated from the central config (T8966)
+ so the convention is opt-in per product repo. Name is intentionally
+ distinct from any central rule name so this stays additive (not an
+ `extends:` override). Legacy-label escape added 2026-06-08 (T8966).
+ conditions:
+ - '-closed'
+ - '-merged'
+ - '-author~=\[bot\]$'
+ - 'author!=copilot-swe-agent'
+ - 'author!=vyosbot'
+ - or:
+ - '-title~=^(([a-zA-Z0-9\-_.]+:\s)?)T\d+:\s+[^\s]+.*'
+ - and:
+ - 'label!=legacy'
+ - 'commits[*].commit_message~=^(?!(([a-zA-Z0-9\-_.]+:\s)?)T\d+:\s+[^\s]+).*'
+ actions:
+ label:
+ toggle:
+ - invalid-task-id
diff --git a/.github/workflows/ah_token_refresh.yml b/.github/workflows/ah_token_refresh.yml
index 0346920e..09d2f9db 100644
--- a/.github/workflows/ah_token_refresh.yml
+++ b/.github/workflows/ah_token_refresh.yml
@@ -7,7 +7,7 @@ on:
jobs:
refresh:
- uses: ansible/team-devtools/.github/workflows/ah_token_refresh.yml@v26.1.0
+ uses: ansible/team-devtools/.github/workflows/ah_token_refresh.yml@v26.4.0
with:
environment: release
secrets:
diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml
index da3e6efd..fa96d19d 100644
--- a/.github/workflows/cla-check.yml
+++ b/.github/workflows/cla-check.yml
@@ -11,5 +11,5 @@ on:
types: [created]
jobs:
call-cla-assistant:
- uses: vyos/vyos-cla-signatures/.github/workflows/cla-reusable.yml@current
+ uses: vyos/vyos-cla-signatures/.github/workflows/cla-reusable.yml@production
secrets: inherit
diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml
index 878ae247..ee71e179 100644
--- a/.github/workflows/codecoverage.yml
+++ b/.github/workflows/codecoverage.yml
@@ -62,7 +62,7 @@ jobs:
working-directory: ${{ steps.identify.outputs.collection_path }}
- name: Upload coverage report to Codecov
- uses: codecov/codecov-action@v5
+ uses: codecov/codecov-action@v7
with:
directory: ${{ steps.identify.outputs.collection_path }}
fail_ci_if_error: false
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000..b83b94e0
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,30 @@
+name: "Perform CodeQL Analysis"
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - '**'
+ - '!.github/**'
+ - '!**/*.md'
+ pull_request:
+ branches:
+ - main
+ paths:
+ - '**'
+ - '!.github/**'
+ - '!**/*.md'
+ schedule:
+ - cron: '52 13 * * 0'
+
+permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+jobs:
+ codeql-analysis-call:
+ uses: vyos/.github/.github/workflows/codeql-analysis.yml@production
+ with:
+ languages: "['python']"
diff --git a/.gitignore b/.gitignore
index cbe347c4..1dbe8b0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,3 +120,9 @@ changelogs/.plugin-cache.yaml
inventory.network
*.bak
+
+# Git worktrees
+.worktrees/
+
+# Claude Code
+.claude/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 00000000..f14e1246
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,39 @@
+# AGENTS.md
+
+## Project purpose
+The official Ansible Collection for managing VyOS network appliances (`vyos.vyos` namespace). Provides modules, plugins, action handlers, terminal plugins, and resource modules for BGP, OSPF, firewall, interfaces, NTP, etc.
+
+## Tech stack
+- Ansible Collection (Galaxy). Python control-plane code under `plugins/`.
+- `galaxy.yml` declares `namespace: vyos`, `name: vyos`, `version: 6.0.0`, dep `ansible.netcommon >= 2.5.1`, license_file `LICENSE` (GPL-3.0).
+- Test stack: `pytest` + `tox-ansible.ini`; lint via flake8, isort, black (line-length 100), pre-commit, ansible-lint.
+- Runtime deps: `paramiko`, `scp` (`requirements.txt`); `bindep.txt` for system deps.
+
+## Build / test / run
+- Build: `ansible-galaxy collection build` produces a `vyos-vyos-<version>.tar.gz`.
+- Install local dev: `ansible-galaxy collection install . --force`.
+- Test (unit): `ansible-test units` (matches `unit-galaxy` CI job; requires collection installed under `~/.ansible/collections/`). Fast local alternative: `source .venv/bin/activate && PYTHONPATH=".collections" python -m pytest tests/unit` (matches `unit-source` CI path; config in `pyproject.toml`). CI (`.github/workflows/tests.yml`) runs the changelog / build-import / ansible-lint / sanity / unit-galaxy / unit-source jobs; integration tests live under `tests/integration/` but are not yet wired into CI. Per `README.md`, the collection targets VyOS 1.3.8 / 1.4.1 / 1.5-rolling (no version matrix in the workflow itself).
+
+## Repository layout
+- `plugins/{action,cliconf,doc_fragments,filter,inventory,module_utils,modules,terminal}/` — collection content.
+- `tests/` — sanity, unit, integration directories. CI (`.github/workflows/tests.yml`) runs sanity + unit-galaxy + unit-source (plus changelog / build-import / ansible-lint); integration is not yet wired into CI.
+- `docs/` — generated module docs.
+- `meta/`, `changelogs/`, `CHANGELOG.rst` — Galaxy + release metadata.
+- `pyproject.toml` (black/pytest config), `.flake8`, `.isort.cfg`, `.ansible-lint`, `.pre-commit-config.yaml`.
+- `.github/workflows/` — `tests.yml`, `release.yml`, `codecoverage.yml`, `cla-check.yml`, `ah_token_refresh.yml`, `check_label.yaml`.
+
+## Cross-repo context
+- Consumed by Ansible users running playbooks against VyOS routers built by `vyos/vyos-build`.
+- The `vyos.vyos` collection talks to VyOS via `network_cli` connections; supports the same train branches (`rolling`, `circinus`, `sagitta`, `equuleus`).
+
+## Conventions
+- Commit headline: `T12345: description` (Phorge ID at https://vyos.dev mandatory). No workflow enforces PR title format in this repo.
+- Every PR must include exactly one changelog fragment under `changelogs/fragments/`; use `doc_changes` for documentation-only updates, or `trivial` for tooling / housekeeping changes.
+- Default branch `main` (not `current` — this repo predates the rename convention).
+- Issues tracked at https://vyos.dev (see `galaxy.yml`).
+- Codecov + CodeRabbit configured (`codecov.yml`, `.coderabbit.yaml`).
+
+## Notes for future contributors
+- Galaxy versioning is independent of VyOS train versioning — bump in `galaxy.yml` per release.
+- Tested matrix is in README; expand only after smoketesting against real images.
+- `PR408_README.md` plus `pr408-diagram.png` document a non-trivial historical refactor; read before touching resource-module structure.
diff --git a/changelogs/fragments/T8512-isort-fix.yml b/changelogs/fragments/T8512-isort-fix.yml
new file mode 100644
index 00000000..5209017c
--- /dev/null
+++ b/changelogs/fragments/T8512-isort-fix.yml
@@ -0,0 +1,3 @@
+---
+trivial:
+ - Fix isort import ordering violations across module_utils to satisfy pre-commit checks.
diff --git a/changelogs/fragments/T8518-vlan-unit-tests.yml b/changelogs/fragments/T8518-vlan-unit-tests.yml
new file mode 100644
index 00000000..65fcdfd5
--- /dev/null
+++ b/changelogs/fragments/T8518-vlan-unit-tests.yml
@@ -0,0 +1,3 @@
+---
+trivial:
+ - Add unit tests for the vyos_vlan module covering present, absent, aggregate, purge, and address scenarios.
diff --git a/changelogs/fragments/T8595_add_agents_md.yml b/changelogs/fragments/T8595_add_agents_md.yml
new file mode 100644
index 00000000..5d7fb0a4
--- /dev/null
+++ b/changelogs/fragments/T8595_add_agents_md.yml
@@ -0,0 +1,2 @@
+trivial:
+ - Add AGENTS.md tool-neutral contributor documentation, with .github/copilot-instructions.md symlink for Copilot code review compatibility.
diff --git a/changelogs/fragments/T8609_rm_templates_regex_backtracking.yml b/changelogs/fragments/T8609_rm_templates_regex_backtracking.yml
new file mode 100644
index 00000000..b02dbf19
--- /dev/null
+++ b/changelogs/fragments/T8609_rm_templates_regex_backtracking.yml
@@ -0,0 +1,12 @@
+---
+bugfixes:
+ - bgp_address_family.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - bgp_address_family_14.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - bgp_global.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - bgp_global_14.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - ospf_interfaces.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - ospf_interfaces_14.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - route_maps.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - route_maps_14.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - snmp_server.py - Fix slow parse() to stop regex backtracking on prefix-sharing inputs.
+ - test_rm_templates_perf.py - Add unit test for the bugfixes
diff --git a/changelogs/fragments/T9082-codeql-workflow.yml b/changelogs/fragments/T9082-codeql-workflow.yml
new file mode 100644
index 00000000..5d09acd7
--- /dev/null
+++ b/changelogs/fragments/T9082-codeql-workflow.yml
@@ -0,0 +1,2 @@
+trivial:
+ - Add CodeQL analysis workflow calling the VyOS org-wide central reusable (T9082).
diff --git a/changelogs/fragments/coderabbit-config.yml b/changelogs/fragments/coderabbit-config.yml
new file mode 100644
index 00000000..9442337f
--- /dev/null
+++ b/changelogs/fragments/coderabbit-config.yml
@@ -0,0 +1,3 @@
+trivial:
+ - Add CodeRabbit review configuration with path-specific guidelines for review,
+ docstring generation, and unit test generation.
diff --git a/changelogs/fragments/copilot-instructions.yml b/changelogs/fragments/copilot-instructions.yml
new file mode 100644
index 00000000..c7af5c74
--- /dev/null
+++ b/changelogs/fragments/copilot-instructions.yml
@@ -0,0 +1,4 @@
+---
+trivial:
+ - Add Copilot custom review instructions to guide automated code review with
+ project-specific conventions for tests, changelog fragments, and module patterns.
diff --git a/changelogs/fragments/deprecated-cleanup.yml b/changelogs/fragments/deprecated-cleanup.yml
new file mode 100644
index 00000000..cf361adc
--- /dev/null
+++ b/changelogs/fragments/deprecated-cleanup.yml
@@ -0,0 +1,3 @@
+---
+minor_changes:
+ - vyos_bgp_global - remove commented-out pre-1.3 deprecated parameter documentation artifacts.
diff --git a/changelogs/fragments/fix-icmp-parse-attr.yml b/changelogs/fragments/fix-icmp-parse-attr.yml
new file mode 100644
index 00000000..5cf0e199
--- /dev/null
+++ b/changelogs/fragments/fix-icmp-parse-attr.yml
@@ -0,0 +1,5 @@
+---
+bugfixes:
+ - firewall_rules - fix parse_icmp_attr() using wrong split delimiter ('.' instead of '/')
+ and referencing undefined variable type_no in the numeric-only branch, which caused
+ ValueError or UnboundLocalError when gathering firewall rules with ICMP type conditions.
diff --git a/changelogs/fragments/fix-snmp-server-redirect.yml b/changelogs/fragments/fix-snmp-server-redirect.yml
new file mode 100644
index 00000000..59147f4a
--- /dev/null
+++ b/changelogs/fragments/fix-snmp-server-redirect.yml
@@ -0,0 +1,3 @@
+---
+bugfixes:
+ - Fix meta/runtime.yml redirect for snmp_server pointing to non-existent vyos_snmp_servers module.
diff --git a/changelogs/fragments/fix-vlan-purge.yml b/changelogs/fragments/fix-vlan-purge.yml
new file mode 100644
index 00000000..bbc1d08a
--- /dev/null
+++ b/changelogs/fragments/fix-vlan-purge.yml
@@ -0,0 +1,3 @@
+---
+bugfixes:
+ - vyos_vlan - fix purge generating invalid ``delete ... vif None`` commands for bare interfaces without VLAN sub-interfaces.
diff --git a/changelogs/fragments/formatting-compliance.yml b/changelogs/fragments/formatting-compliance.yml
new file mode 100644
index 00000000..d48896a9
--- /dev/null
+++ b/changelogs/fragments/formatting-compliance.yml
@@ -0,0 +1,3 @@
+---
+trivial:
+ - Add .git-blame-ignore-revs to exclude bulk formatting commits from git blame output.
diff --git a/changelogs/fragments/missing-unit-tests.yml b/changelogs/fragments/missing-unit-tests.yml
new file mode 100644
index 00000000..a83b336c
--- /dev/null
+++ b/changelogs/fragments/missing-unit-tests.yml
@@ -0,0 +1,3 @@
+---
+minor_changes:
+ - Add unit tests for vyos_vlan module.
diff --git a/changelogs/fragments/t6818_password_filtering.yml b/changelogs/fragments/t6818_password_filtering.yml
new file mode 100644
index 00000000..d52283b9
--- /dev/null
+++ b/changelogs/fragments/t6818_password_filtering.yml
@@ -0,0 +1,2 @@
+minor_changes:
+ - plugins/modules/vyos_config.py - Added an argument to control password filtering in vyos_config. Current filtering behavior is still the default.
diff --git a/changelogs/fragments/t8851-coderabbit-centralized.yml b/changelogs/fragments/t8851-coderabbit-centralized.yml
new file mode 100644
index 00000000..b9be774e
--- /dev/null
+++ b/changelogs/fragments/t8851-coderabbit-centralized.yml
@@ -0,0 +1,6 @@
+trivial:
+ - Migrate ``.coderabbit.yaml`` from standalone configuration to centralized
+ inheritance under the org-level baseline at ``vyos/coderabbit``. No effect
+ on consumers; the file shrinks from 591 lines to 205 while preserving all
+ Ansible-collection-specific ``path_instructions`` and adding a
+ ``knowledge_base.jira`` block scoped to ``VD`` (T8851).
diff --git a/changelogs/fragments/vyos-user-quote-plaintext-password.yml b/changelogs/fragments/vyos-user-quote-plaintext-password.yml
new file mode 100644
index 00000000..487b592a
--- /dev/null
+++ b/changelogs/fragments/vyos-user-quote-plaintext-password.yml
@@ -0,0 +1,3 @@
+---
+bugfixes:
+ - vyos_user - Quote and escape plaintext-password values in set commands so VyOS accepts special characters and embedded single quotes.
diff --git a/meta/runtime.yml b/meta/runtime.yml
index 36579e05..f0a53ee7 100644
--- a/meta/runtime.yml
+++ b/meta/runtime.yml
@@ -55,7 +55,7 @@ plugin_routing:
prefix_lists:
redirect: vyos.vyos.vyos_prefix_lists
snmp_server:
- redirect: vyos.vyos.vyos_snmp_servers
+ redirect: vyos.vyos.vyos_snmp_server
static_routes:
redirect: vyos.vyos.vyos_static_routes
system:
diff --git a/plugins/module_utils/network/vyos/config/vrf/vrf.old b/plugins/module_utils/network/vyos/config/vrf/vrf.old
deleted file mode 100644
index b9f56cc6..00000000
--- a/plugins/module_utils/network/vyos/config/vrf/vrf.old
+++ /dev/null
@@ -1,313 +0,0 @@
-#
-# -*- 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_vrf 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.
-"""
-
-import importlib
-
-from copy import deepcopy
-
-from ansible.module_utils.six import iteritems
-from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import (
- ResourceModule,
-)
-
-from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import (
- Bgp_global,
-)
-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.vrf import (
- VrfTemplate,
-)
-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
-
-
-# from ansible.plugins.filter.core import combine
-
-
-class Vrf(ResourceModule):
- """
- The vyos_vrf config class
- """
-
- def __init__(self, module):
- super(Vrf, self).__init__(
- empty_fact_val={},
- facts_module=Facts(module),
- module=module,
- resource="vrf",
- tmplt=VrfTemplate(),
- )
- self.parsers = [
- "bind_to_all",
- ]
- self.bgp = Bgp_global(module)
-
- def _validate_template(self):
- version = get_os_version(self._module)
- if LooseVersion(version) >= LooseVersion("1.4"):
- self._tmplt = VrfTemplate()
- else:
- self._module.fail_json(msg="VRF is not supported in this version of VyOS")
-
- def parse(self):
- """override parse to check template"""
- self._validate_template()
- return super().parse()
-
- def get_parser(self, name):
- """get_parsers"""
- self._validate_template()
- return super().get_parser(name)
-
- def execute_module(self):
- """Execute the module
-
- :rtype: A dictionary
- :returns: The result from module execution
- """
- if self.state not in ["parsed", "gathered"]:
- self.generate_commands()
- self.run_commands()
-
- return self.result
-
- def generate_commands(self):
- """Generate configuration commands to send based on
- want, have and desired state.
- """
- wantd = {}
- haved = {}
- wantd = deepcopy(self.want)
- haved = deepcopy(self.have)
-
- # self._module.fail_json(msg="WanT: " + str(self.want) + "**** H: " + str(self.have))
-
- # if state is merged, merge want onto have and then compare
- if self.state in ["merged", "replaced"]:
- # wantd = dict_merge(wantd, haved)
- # wantd = haved | combine(wantd, recursive=True)
- wantd = combine(haved, wantd, recursive=True)
- # self._module.fail_json(msg="Want: " + str(wantd) + "**** H: " + str(haved))
-
- # if state is deleted, delete and empty out wantd
- if self.state == "deleted":
- w = deepcopy(wantd)
- if w == {} and haved != {}:
- self.commands = ["delete vrf"]
- return
- for k, want in iteritems(w):
- if not (k in haved and haved[k]):
- del wantd[k]
- else:
- if isinstance(want, list):
- for entry in want:
- wname = entry.get("name")
- haved["instances"] = [
- i for i in haved.get("instances", []) if i.get("name") != wname
- ]
- self.commands.append("delete vrf name {}".format(wname))
- else:
- self.commands.append("delete vrf {}".format(k.replace("_", "-")))
- del wantd[k]
-
- if self.state == "overridden":
- w = deepcopy(wantd)
- h = deepcopy(haved)
- for k, want in iteritems(w):
- if k in haved and haved[k] != want:
- if isinstance(want, list):
- for entry in want:
- wname = entry.get("name")
- hdict = next(
- (inst for inst in haved["instances"] if inst["name"] == wname),
- None,
- )
- if entry != hdict:
- # self._module.fail_json(msg="Want: " + str(entry) + "**** H: " + str(hdict))
- haved["instances"] = [
- i for i in haved.get("instances", []) if i.get("name") != wname
- ]
- self.commands.append("delete vrf name {}".format(wname))
- self.commands.append("commit")
-
- for k, want in iteritems(wantd):
- if isinstance(want, list):
- self._compare_instances(want=want, have=haved.pop(k, {}))
- self.compare(
- parsers=self.parsers,
- want={k: want},
- have={k: haved.pop(k, {})},
- )
- self._module.fail_json(msg=self.commands)
-
- def _compare_instances(self, want, have):
- """Compare the instances of the VRF"""
- parsers = [
- "table_id",
- "vni",
- "description",
- "disable_vrf",
- ]
- # self._module.fail_json(msg="want: " + str(want) + "**** have: " + str(have))
-
- for entry in want:
- h = {}
- wname = entry.get("name")
- # h = next((vrf for vrf in have if vrf["name"] == wname), {})
- h = {
- k: v
- for vrf in have
- if vrf.get("name") == wname
- for k, v in vrf.items()
- if k != "address_family"
- }
- self.compare(parsers=parsers, want=entry, have=h)
-
- if "address_family" in entry:
- wafi = {"name": wname, "address_family": entry.get("address_family", [])}
- # hdict = next((item for item in have if item["name"] == wname), None)
- hdict = next((d for d in have if d.get("name") == wname), None)
-
- hafi = {
- "name": (hdict or {"name": wname})["name"],
- "address_family": hdict.get("address_family", []) if hdict else [],
- }
-
- # self._module.fail_json(msg="wafi: " + str(wafi) + "**** hafi: " + str(hafi))
-
- self._compare_addr_family(wafi, hafi)
-
- if "protocols" in entry:
- for protocol_name in entry["protocols"]:
- protocol_module = self._load_protocol_module(protocol_name)
- w_p_dict = entry["protocols"][protocol_name]
- h_p_dict = next(
- (
- v.get("protocols", {}).get(protocol_name)
- for v in have
- if v.get("name") == wname
- ),
- {},
- )
- if protocol_name == "bgp":
- protocol_module._validate_template()
- protocol_module.want = w_p_dict
- protocol_module.have = h_p_dict
- protocol_module.generate_commands()
- elif protocol_name in [
- # "ospf",
- # "ospfv3",
- "static",
- ]:
- self._module.fail_json(msg=str(protocol_module))
- protocol_module._module.params["config"] = w_p_dict
- protocol_module.state = self.state
- self._module.fail_json(msg=str(protocol_module.set_config(h_p_dict)))
-
- protocol_module.commands = protocol_module.set_config(h_p_dict)
- self.commands.extend(
- [
- cmd.replace("protocols", "vrf name " + wname + " protocols", 1)
- for cmd in protocol_module.commands
- ],
- )
- protocol_module = None # Clear the module to free resources
-
- def _compare_addr_family(self, want, have):
- """Compare the address families of the VRF"""
- afi_parsers = [
- # "address_family",
- "disable_forwarding",
- "disable_nht",
- ]
- # self._module.fail_json(msg="wAfi: " + str(want) + "**** hAfi: " + str(have))
-
- wafi = self.afi_to_list(want)
- hafi = self.afi_to_list(have)
-
- lookup = {(d["name"], d["afi"]): d for d in hafi}
- pairs = [(d1, lookup.get((d1["name"], d1["afi"]), {})) for d1 in wafi]
-
- for wafd, hafd in pairs:
- # self._module.fail_json(msg="wAfd: " + str(wafd) + "**** hAfd: " + str(hafd))
- if "route_maps" in wafd:
- self._compare_route_maps(wafd, hafd)
- self.compare(parsers=afi_parsers, want=wafd, have=hafd)
- # self.compare(parsers=afi_parsers, want=wafi, have=hafi)
-
- def afi_to_list(self, data):
- """Convert address family dict to list"""
-
- return [
- {"name": data["name"], **{**af, "afi": "ip" if af["afi"] == "ipv4" else af["afi"]}}
- for af in data["address_family"]
- ]
-
- def _compare_route_maps(self, wafd, hafd):
- want_rms = wafd.get("route_maps", [])
- have_rms = hafd.get("route_maps", [])
-
- for want in want_rms:
- match = next(
- (
- h
- for h in have_rms
- if h["rm_name"] == want["rm_name"] and h["protocol"] == want["protocol"]
- ),
- {},
- )
- base = {"name": wafd["name"], "afi": wafd["afi"]}
-
- self.compare(
- parsers="route_maps",
- want={**base, "route_maps": want},
- have={**base, "route_maps": match},
- )
-
- def _load_protocol_module(self, protocol_name):
- if protocol_name == "bgp":
- from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_global.bgp_global import (
- Bgp_global,
- )
-
- return Bgp_global(self._module)
- elif protocol_name == "ospf":
- from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv2.ospfv2 import (
- Ospfv2,
- )
-
- return Ospfv2(self._module)
- elif protocol_name == "ospfv3":
- from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.ospfv3.ospfv3 import (
- Ospfv3,
- )
-
- return Ospfv3(self._module)
- elif protocol_name == "static":
- from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.static_routes.static_routes import (
- Static_routes,
- )
-
- return Static_routes(self._module)
- else:
- self._module.fail_json(msg="The protocol is not supported")
diff --git a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py
index 65495070..dab92612 100644
--- a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py
+++ b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py
@@ -27,12 +27,10 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_template
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family_14 import (
Bgp_address_familyTemplate14,
)
-
-from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
-
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 Bgp_address_familyFacts(object):
diff --git a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py
index d04791ee..cf2e4475 100644
--- a/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py
+++ b/plugins/module_utils/network/vyos/facts/bgp_global/bgp_global.py
@@ -24,16 +24,13 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global import (
Bgp_globalTemplate,
)
-
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global_14 import (
Bgp_globalTemplate14,
)
-
-from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
-
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 Bgp_globalFacts(object):
diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
index 84f423cf..31cc1fa5 100644
--- a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
@@ -412,11 +412,11 @@ class Firewall_rulesFacts(object):
# <1.3 could be # (type), #/# (type/code) or 'type' (type_name)
# recent this is only for strings
if "/" in val: # type/code
- type_no, code = val.split(".")
- config["type"] = type_no
- config["code"] = code
+ (type_no, code) = val.split("/")
+ config["type"] = int(type_no)
+ config["code"] = int(code)
elif val.isnumeric():
- config["type"] = type_no
+ config["type"] = int(val)
else:
config["type_name"] = val
return config
diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py
index 6c945151..fe7889aa 100644
--- a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py
+++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py
@@ -292,8 +292,8 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+aggregate-address
\s+(?P<address>\S+)
- \s*(?P<as_set>as-set)*
- \s*(?P<summary_only>summary-only)*
+ \s*(?P<as_set>as-set)?
+ \s*(?P<summary_only>summary-only)?
$""",
re.VERBOSE,
),
@@ -396,8 +396,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+network
\s+(?P<address>\S+)
\s+path-limit
- \s+(?P<limit>\S+)
- *$""",
+ \s+(?P<limit>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_network,
@@ -431,8 +430,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+network
\s+(?P<address>\S+)
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_network,
@@ -498,8 +496,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+redistribute
\s+(?P<proto>\S+)
\s+metric
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_redistribute,
@@ -533,8 +530,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+redistribute
\s+(?P<proto>\S+)
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_redistribute,
@@ -567,8 +563,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+redistribute
\s+table
- \s+(?P<tab>\S+)
- *$""",
+ \s+(?P<tab>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_redistribute,
@@ -658,8 +653,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+allowas-in
\s+number
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -876,8 +870,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+capability
\s+prefix-list
- \s+(?P<orf>\S+)
- *$""",
+ \s+(?P<orf>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -914,8 +907,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+default-originate
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -991,8 +983,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+prefix-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor_prefix_list,
@@ -1032,8 +1023,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+filter-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor_filter_list,
@@ -1072,8 +1062,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+maximum-prefix
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -1175,8 +1164,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+peer-group
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -1245,8 +1233,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+route-map
\s+(?P<action>export|import)
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor_route_map,
@@ -1388,8 +1375,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+unsuppress-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -1423,8 +1409,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+weight
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py
index e8ea9473..9936cf6d 100644
--- a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py
+++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family_14.py
@@ -308,8 +308,8 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+aggregate-address
\s+(?P<address>\S+)
- \s*(?P<as_set>as-set)*
- \s*(?P<summary_only>summary-only)*
+ \s*(?P<as_set>as-set)?
+ \s*(?P<summary_only>summary-only)?
$""",
re.VERBOSE,
),
@@ -409,8 +409,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+network
\s+(?P<address>\S+)
\s+path-limit
- \s+(?P<limit>\S+)
- *$""",
+ \s+(?P<limit>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_network,
@@ -443,8 +442,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+network
\s+(?P<address>\S+)
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_network,
@@ -507,8 +505,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+redistribute
\s+(?P<proto>\S+)
- \s+metric\s+(?P<val>\S+)
- *$""",
+ \s+metric\s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_redistribute,
@@ -541,8 +538,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+redistribute
\s+(?P<proto>\S+)
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_redistribute,
@@ -574,8 +570,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+redistribute
\s+table
- \s+(?P<tab>\S+)
- *$""",
+ \s+(?P<tab>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_redistribute,
@@ -662,8 +657,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+allowas-in
\s+number
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -874,8 +868,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+capability
\s+prefix-list
- \s+(?P<orf>\S+)
- *$""",
+ \s+(?P<orf>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -911,8 +904,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+default-originate
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -986,8 +978,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+prefix-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor_prefix_list,
@@ -1026,8 +1017,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+filter-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor_filter_list,
@@ -1065,8 +1055,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+maximum-prefix
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -1165,8 +1154,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+peer-group
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -1233,8 +1221,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+(?P<afi>\S+)-unicast
\s+route-map
\s+(?P<action>export|import)
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor_route_map,
@@ -1372,8 +1359,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+unsuppress-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
@@ -1406,8 +1392,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate):
\s+address-family
\s+(?P<afi>\S+)-unicast
\s+weight
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_af_neighbor,
diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py
index 4eac2449..730fa5ee 100644
--- a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py
+++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py
@@ -236,8 +236,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+advertisement-interval
- \s+(?P<interval>\S+)
- *$""",
+ \s+(?P<interval>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}",
@@ -436,8 +435,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+capability
\s+orf
\s+prefix-list
- \s+(?P<orf>\S+)
- *$""",
+ \s+(?P<orf>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}",
@@ -466,8 +464,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<address>\S+)
\s+default-originate
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}",
@@ -493,8 +490,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+description
- \s+(?P<desc>\S+)
- *$""",
+ \s+(?P<desc>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} description {{ neighbor.description }}",
@@ -572,8 +568,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+disable-send-community
- \s+(?P<comm>\S+)
- *$""",
+ \s+(?P<comm>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}",
@@ -632,8 +627,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+ebgp-multihop
- \s+(?P<hop>\S+)
- *$""",
+ \s+(?P<hop>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}",
@@ -660,8 +654,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<address>\S+)
\s+filter-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_filter_list,
@@ -720,8 +713,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+maximum-prefix
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}",
@@ -825,8 +817,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+password
- \s+(?P<pwd>\S+)
- *$""",
+ \s+(?P<pwd>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} password {{ neighbor.password }}",
@@ -852,8 +843,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+peer-group
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}",
@@ -879,8 +869,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+port
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} port {{ neighbor.port }}",
@@ -907,8 +896,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<address>\S+)
\s+prefix-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_prefix_list,
@@ -939,8 +927,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+remote-as
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}",
@@ -993,8 +980,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<address>\S+)
\s+route-map
\s+(?P<action>export|import)
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_route_map,
@@ -1156,8 +1142,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+unsuppress-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}",
@@ -1183,8 +1168,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+update-source
- \s+(?P<src>\S+)
- *$""",
+ \s+(?P<src>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}",
@@ -1210,8 +1194,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+weight
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} weight {{ neighbor.weight }}",
@@ -1237,8 +1220,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+ttl-security
- \s+(?P<ttl>\S+)
- *$""",
+ \s+(?P<ttl>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}",
@@ -1265,8 +1247,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<address>\S+)
\s+timers
\s+(?P<type>connect|holdtime|keepalive)
- \s+(?P<sec>\S+)
- *$""",
+ \s+(?P<sec>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_timers,
@@ -1294,8 +1275,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<as_num>\d+)
\s+timers
\s+(?P<type>\S+)
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_timers,
@@ -1417,8 +1397,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<as_num>\d+)
\s+parameters
\s+cluster-id
- \s+(?P<id>\S+)
- *$""",
+ \s+(?P<id>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters cluster-id {{ bgp_params.cluster_id }}",
@@ -1441,8 +1420,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+parameters
\s+confederation
\s+(?P<type>identifier|peers)
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_params_confederation,
@@ -1470,8 +1448,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+parameters
\s+dampening
\s+half-life
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters dampening half-life {{ bgp_params.dampening.half_life}}",
@@ -1496,8 +1473,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+parameters
\s+dampening
\s+max-suppress-time
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}",
@@ -1522,8 +1498,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+parameters
\s+dampening
\s+re-use
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters dampening re-use {{ bgp_params.dampening.re_use}}",
@@ -1548,8 +1523,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+parameters
\s+dampening
\s+start-suppress-time
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}",
@@ -1573,9 +1547,8 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<as_num>\d+)
\s+parameters
\s+default
- \s*(?P<no_ipv4_unicast>no-ipv4-unicast)*
- \s*(?P<local_pref>local-pref\s\S+)
- *$""",
+ \s*(?P<no_ipv4_unicast>no-ipv4-unicast)?
+ \s*(?P<local_pref>local-pref\s\S+)?\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_params_default,
@@ -1647,8 +1620,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+distance\sprefix
\s+(?P<prefix>\S+)
\s+distance
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}",
@@ -1677,8 +1649,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+parameters
\s+distance\sglobal
\s+(?P<type>\S+)
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_params_distance,
@@ -1728,8 +1699,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<as_num>\d+)
\s+parameters
\s+graceful-restart\s+stalepath-time
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}",
@@ -1817,8 +1787,7 @@ class Bgp_globalTemplate(NetworkTemplate):
\s+(?P<as_num>\d+)
\s+parameters
\s+router-id
- \s+(?P<id>\S+)
- *$""",
+ \s+(?P<id>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp {{ as_number }} parameters router-id {{ bgp_params.router_id }}",
diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py b/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py
index 6d6588bc..b86e233c 100644
--- a/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py
+++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global_14.py
@@ -233,8 +233,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+advertisement-interval
- \s+(?P<interval>\S+)
- *$""",
+ \s+(?P<interval>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}",
@@ -426,8 +425,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+capability
\s+orf
\s+prefix-list
- \s+(?P<orf>\S+)
- *$""",
+ \s+(?P<orf>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}",
@@ -455,8 +453,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+(?P<address>\S+)
\s+default-originate
\s+route-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}",
@@ -481,8 +478,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+description
- \s+(?P<desc>\S+)
- *$""",
+ \s+(?P<desc>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} description {{ neighbor.description }}",
@@ -557,8 +553,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+disable-send-community
- \s+(?P<comm>\S+)
- *$""",
+ \s+(?P<comm>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}",
@@ -615,8 +610,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+ebgp-multihop
- \s+(?P<hop>\S+)
- *$""",
+ \s+(?P<hop>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}",
@@ -642,8 +636,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+(?P<address>\S+)
\s+filter-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_filter_list,
@@ -700,8 +693,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+maximum-prefix
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}",
@@ -801,8 +793,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+password
- \s+(?P<pwd>\S+)
- *$""",
+ \s+(?P<pwd>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} password {{ neighbor.password }}",
@@ -827,8 +818,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+peer-group
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}",
@@ -853,8 +843,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+port
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} port {{ neighbor.port }}",
@@ -880,8 +869,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+(?P<address>\S+)
\s+prefix-list
\s+(?P<action>export|import)
- \s+(?P<list>\S+)
- *$""",
+ \s+(?P<list>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_prefix_list,
@@ -911,8 +899,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+remote-as
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}",
@@ -963,8 +950,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+(?P<address>\S+)
\s+route-map
\s+(?P<action>export|import)
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_route_map,
@@ -1120,8 +1106,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+unsuppress-map
- \s+(?P<map>\S+)
- *$""",
+ \s+(?P<map>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}",
@@ -1146,8 +1131,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+update-source
- \s+(?P<src>\S+)
- *$""",
+ \s+(?P<src>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}",
@@ -1172,8 +1156,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+weight
- \s+(?P<num>\S+)
- *$""",
+ \s+(?P<num>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} weight {{ neighbor.weight }}",
@@ -1198,8 +1181,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+neighbor
\s+(?P<address>\S+)
\s+ttl-security
- \s+(?P<ttl>\S+)
- *$""",
+ \s+(?P<ttl>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}",
@@ -1225,8 +1207,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+(?P<address>\S+)
\s+timers
\s+(?P<type>connect|holdtime|keepalive)
- \s+(?P<sec>\S+)
- *$""",
+ \s+(?P<sec>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_neighbor_timers,
@@ -1253,8 +1234,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+bgp
\s+timers
\s+(?P<type>\S+)
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_timers,
@@ -1371,8 +1351,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+bgp
\s+parameters
\s+cluster-id
- \s+(?P<id>\S+)
- *$""",
+ \s+(?P<id>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters cluster-id {{ bgp_params.cluster_id }}",
@@ -1394,8 +1373,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+parameters
\s+confederation
\s+(?P<type>identifier|peers)
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_params_confederation,
@@ -1422,8 +1400,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+parameters
\s+dampening
\s+half-life
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters dampening half-life {{ bgp_params.dampening.half_life}}",
@@ -1447,8 +1424,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+parameters
\s+dampening
\s+max-suppress-time
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}",
@@ -1472,8 +1448,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+parameters
\s+dampening
\s+re-use
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters dampening re-use {{ bgp_params.dampening.re_use}}",
@@ -1497,8 +1472,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+parameters
\s+dampening
\s+start-suppress-time
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}",
@@ -1521,9 +1495,8 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+bgp
\s+parameters
\s+default
- \s*(?P<no_ipv4_unicast>no-ipv4-unicast)*
- \s*(?P<local_pref>local-pref\s\S+)
- *$""",
+ \s*(?P<no_ipv4_unicast>no-ipv4-unicast)?
+ \s*(?P<local_pref>local-pref\s\S+)?\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_params_default,
@@ -1592,8 +1565,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+distance\sprefix
\s+(?P<prefix>\S+)
\s+distance
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}",
@@ -1621,8 +1593,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+parameters
\s+distance\sglobal
\s+(?P<type>\S+)
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_bgp_params_distance,
@@ -1670,8 +1641,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+bgp
\s+parameters
\s+graceful-restart\s+stalepath-time
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}",
@@ -1755,8 +1725,7 @@ class Bgp_globalTemplate14(NetworkTemplate):
\s+bgp
\s+parameters
\s+router-id
- \s+(?P<id>\S+)
- *$""",
+ \s+(?P<id>\S+)\s*$""",
re.VERBOSE,
),
"setval": "protocols bgp parameters router-id {{ bgp_params.router_id }}",
diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py
index f57ed68b..5183aec4 100644
--- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py
+++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py
@@ -364,8 +364,7 @@ class Ospf_interfacesTemplate(NetworkTemplate):
\s+(?P<proto>ospf|ospfv3)
\s+authentication
\s+plaintext-password
- \s+(?P<text>\S+)
- *$""",
+ \s+(?P<text>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_ospf_int_auth_password,
@@ -398,8 +397,7 @@ class Ospf_interfacesTemplate(NetworkTemplate):
\s+key-id
\s+(?P<id>\d+)
\s+md5-key
- \s+(?P<text>\S+)
- *$""",
+ \s+(?P<text>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_ospf_int_auth_md5,
@@ -571,8 +569,7 @@ class Ospf_interfacesTemplate(NetworkTemplate):
\s+(?P<afi>ip|ipv6)
\s+(?P<proto>ospf|ospfv3)
\s+network
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_ospf_int_network,
diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py
index 18939707..8d09011f 100644
--- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py
+++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py
@@ -5,6 +5,7 @@
from __future__ import absolute_import, division, print_function
+
__metaclass__ = type
"""
@@ -224,8 +225,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate):
\s+protocols
\s+(?P<proto>ospf|ospfv3)
\s+interface
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"remval": _tmplt_ospf_int_delete,
@@ -250,8 +250,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate):
\s+(?P<name>\S+)
\s+authentication
\s+plaintext-password
- \s+(?P<text>\S+)
- *$""",
+ \s+(?P<text>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_ospf_int_auth_password,
@@ -282,8 +281,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate):
\s+key-id
\s+(?P<id>\d+)
\s+md5-key
- \s+(?P<text>\S+)
- *$""",
+ \s+(?P<text>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_ospf_int_auth_md5,
@@ -443,8 +441,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate):
\s+interface
\s+(?P<name>\S+)
\s+network
- \s+(?P<val>\S+)
- *$""",
+ \s+(?P<val>\S+)\s*$""",
re.VERBOSE,
),
"setval": _tmplt_ospf_int_network,
diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps.py b/plugins/module_utils/network/vyos/rm_templates/route_maps.py
index d837a49c..fd8fdd9d 100644
--- a/plugins/module_utils/network/vyos/rm_templates/route_maps.py
+++ b/plugins/module_utils/network/vyos/rm_templates/route_maps.py
@@ -32,8 +32,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "route_map",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\s*$""",
re.VERBOSE,
),
"compval": "route_map",
@@ -50,8 +49,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "sequence",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\s*$""",
re.VERBOSE,
),
"compval": "sequence",
@@ -74,8 +72,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "call",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}",
@@ -98,8 +95,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "description",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}",
@@ -122,8 +118,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "action",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}",
@@ -146,8 +141,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "continue_sequence",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}",
@@ -170,8 +164,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "on_match_next",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\s(?P<next>next)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\s(?P<next>next)\s*$""",
re.VERBOSE,
),
"compval": "on_match.next",
@@ -197,8 +190,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "on_match_goto",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+)\s*$""",
re.VERBOSE,
),
"compval": "on_match.goto",
@@ -224,8 +216,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_aggregator_ip",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.aggregator.ip",
@@ -253,8 +244,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_aggregator_as",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.aggregator.as",
@@ -282,8 +272,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_as_path_exclude",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-exclude\s(?P<as>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path-exclude\s(?P<as>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.as_path_exclude",
@@ -336,8 +325,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_atomic_aggregate",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\s(?P<as>atomic-aggregate)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\s(?P<as>atomic-aggregate)\s*$""",
re.VERBOSE,
),
"compval": "set.atomic_aggregate",
@@ -363,8 +351,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_bgp_extcommunity_rt",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.bgp_extcommunity_rt",
@@ -391,8 +378,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_comm_list",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.community.community_list",
@@ -419,8 +405,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_comm_list_delete",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\sdelete(?P<delete>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\s(?P<delete>delete)\s*$""",
re.VERBOSE,
),
"compval": "set.comm_list.comm_list",
@@ -447,8 +432,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_extcommunity_rt",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-rt\s(?P<extcommunity_rt>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-rt\s(?P<extcommunity_rt>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.extcommunity_rt",
@@ -475,8 +459,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_extcommunity_soo",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-soo\s(?P<extcommunity_soo>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity-soo\s(?P<extcommunity_soo>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.extcommunity_soo",
@@ -503,8 +486,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_extcommunity_bandwidth",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.extcommunity_bandwidth",
@@ -531,8 +513,8 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_extcommunity_bandwidth_non_transitive",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\s(?P<extcommunity_bw_nt>bandwidth-non-transitive)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)
+ \sset\sextcommunity\s(?P<extcommunity_bw_nt>bandwidth-non-transitive)\s*$""",
re.VERBOSE,
),
"compval": "set.extcommunity_bandwidth_non_transitive",
@@ -559,8 +541,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_ip_next_hop",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.ip_next_hop",
@@ -589,8 +570,7 @@ class Route_mapsTemplate(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sipv6-next-hop
\s(?P<type>global|local)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.ipv6_next_hop",
@@ -620,8 +600,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_large_community",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slarge-community\s(?P<large_community>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slarge-community\s(?P<large_community>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.large_community",
@@ -648,8 +627,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_local_preference",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.local_preference",
@@ -676,8 +654,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_metric",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.metric",
@@ -704,8 +681,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_metric_type",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.metric_type",
@@ -732,8 +708,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_origin",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.origin",
@@ -760,8 +735,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_originator_id",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.originator_id",
@@ -788,8 +762,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_src",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.src",
@@ -816,8 +789,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_tag",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.tag",
@@ -844,8 +816,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_weight",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.weight",
@@ -872,8 +843,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_table",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.weight",
@@ -900,8 +870,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "set_community",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scommunity\s(?P<value>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scommunity\s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.community.value",
@@ -930,8 +899,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_as_path",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.as_path",
@@ -958,8 +926,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_community_community_list",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.community.community_list",
@@ -986,8 +953,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_community_exact_match",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\sexact-match(?P<exact_match>)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\sexact-match(?P<exact_match>)\s*$""",
re.VERBOSE,
),
"compval": "match.community.exact_match",
@@ -1014,8 +980,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_extcommunity",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.extcommunity",
@@ -1042,8 +1007,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_interface",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.interface",
@@ -1070,8 +1034,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_large_community_large_community_list",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.large_community_large_community_list",
@@ -1098,8 +1061,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_metric",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.metric",
@@ -1126,8 +1088,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_origin",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.origin",
@@ -1154,8 +1115,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_peer",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.peer",
@@ -1184,8 +1144,7 @@ class Route_mapsTemplate(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\saddress
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ip.address",
@@ -1218,8 +1177,7 @@ class Route_mapsTemplate(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\snexthop
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ip.next_hop",
@@ -1252,8 +1210,7 @@ class Route_mapsTemplate(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\sroute-source
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ip.route_source",
@@ -1286,8 +1243,7 @@ class Route_mapsTemplate(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\saddress
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ipv6.address",
@@ -1319,8 +1275,7 @@ class Route_mapsTemplate(NetworkTemplate):
"getval": re.compile(
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\snexthop
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ipv6.next_hop",
@@ -1348,8 +1303,7 @@ class Route_mapsTemplate(NetworkTemplate):
"name": "match_protocol",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.protocol",
@@ -1376,8 +1330,7 @@ class Route_mapsTemplate(NetworkTemplate):
"getval": re.compile(
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\srpki
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.rpki",
diff --git a/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py
index 30c22df4..cf2d6b67 100644
--- a/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py
+++ b/plugins/module_utils/network/vyos/rm_templates/route_maps_14.py
@@ -32,8 +32,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "route_map",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\s*$""",
re.VERBOSE,
),
"compval": "route_map",
@@ -74,8 +73,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "call",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scall\s(?P<call>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}",
@@ -98,8 +96,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "description",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sdescription\s(?P<description>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}",
@@ -122,8 +119,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "action",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\saction\s(?P<action>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}",
@@ -146,8 +142,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "continue_sequence",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\scontinue\s(?P<continue>\S+)\s*$""",
re.VERBOSE,
),
"setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}",
@@ -197,8 +192,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "on_match_goto",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\son-match\sgoto\s(?P<goto>\S+)\s*$""",
re.VERBOSE,
),
"compval": "on_match.goto",
@@ -224,8 +218,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_aggregator_ip",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sip\s(?P<ip>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.aggregator.ip",
@@ -253,8 +246,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_aggregator_as",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\saggregator\sas\s(?P<as>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.aggregator.as",
@@ -282,8 +274,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_as_path_exclude",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path\sexclude\s(?P<as>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sas-path\sexclude\s(?P<as>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.as_path_exclude",
@@ -363,8 +354,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_bgp_extcommunity_rt",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sbgp-extcommunity-rt\s(?P<bgp>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.bgp_extcommunity_rt",
@@ -391,8 +381,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_comm_list",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<comm_list>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.community.community_list",
@@ -419,8 +408,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_comm_list_delete",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\sdelete(?P<delete>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\scomm-list\s(?P<delete>delete)\s*$""",
re.VERBOSE,
),
"compval": "set.comm_list.comm_list",
@@ -447,8 +435,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_extcommunity_rt",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\srt\s(?P<extcommunity_rt>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\srt\s(?P<extcommunity_rt>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.extcommunity_rt",
@@ -475,8 +462,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_extcommunity_soo",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\ssoo\s(?P<extcommunity_soo>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\ssoo\s(?P<extcommunity_soo>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.extcommunity_soo",
@@ -503,8 +489,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_extcommunity_bandwidth",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sextcommunity\sbandwidth\s(?P<extcommunity_bw>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.extcommunity_bandwidth",
@@ -559,8 +544,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_ip_next_hop",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sip-next-hop\s(?P<ip_next_hop>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.ip_next_hop",
@@ -589,8 +573,7 @@ class Route_mapsTemplate14(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sipv6-next-hop
\s(?P<type>global|local)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.ipv6_next_hop",
@@ -648,8 +631,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_local_preference",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\slocal-preference\s(?P<local_preference>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.local_preference",
@@ -676,8 +658,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_metric",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric\s(?P<metric>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.metric",
@@ -704,8 +685,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_metric_type",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\smetric-type\s(?P<metric_type>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.metric_type",
@@ -732,8 +712,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_origin",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sorigin\s(?P<origin>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.origin",
@@ -760,8 +739,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_originator_id",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\soriginator-id\s(?P<originator_id>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.originator_id",
@@ -788,8 +766,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_src",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\ssrc\s(?P<src>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.src",
@@ -816,8 +793,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_tag",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stag\s(?P<tag>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.tag",
@@ -844,8 +820,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_weight",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\sweight\s(?P<weight>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.weight",
@@ -872,8 +847,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "set_table",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\sset\stable\s(?P<table>\S+)\s*$""",
re.VERBOSE,
),
"compval": "set.weight",
@@ -930,8 +904,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_as_path",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sas-path\s(?P<as_path>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.as_path",
@@ -958,8 +931,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_community_community_list",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\scommunity\scommunity-list\s(?P<community_list>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.community.community_list",
@@ -1014,8 +986,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_extcommunity",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sextcommunity\s(?P<extcommunity>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.extcommunity",
@@ -1042,8 +1013,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_interface",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sinterface\s(?P<interface>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.interface",
@@ -1070,8 +1040,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_large_community_large_community_list",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\slarge-community\slarge-community-list\s(?P<lc>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.large_community_large_community_list",
@@ -1098,8 +1067,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_metric",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\smetric\s(?P<metric>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.metric",
@@ -1126,8 +1094,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_origin",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sorigin\s(?P<origin>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.origin",
@@ -1154,8 +1121,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_peer",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\speer\s(?P<peer>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.peer",
@@ -1184,8 +1150,7 @@ class Route_mapsTemplate14(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\saddress
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ip.address",
@@ -1218,8 +1183,7 @@ class Route_mapsTemplate14(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\snexthop
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ip.next_hop",
@@ -1252,8 +1216,7 @@ class Route_mapsTemplate14(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sip\sroute-source
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ip.route_source",
@@ -1286,8 +1249,7 @@ class Route_mapsTemplate14(NetworkTemplate):
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\saddress
\s(?P<list_type>access-list|prefix-list)
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ipv6.address",
@@ -1319,8 +1281,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"getval": re.compile(
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sipv6\snexthop
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.ipv6.next_hop",
@@ -1348,8 +1309,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"name": "match_protocol",
"getval": re.compile(
r"""
- ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)
- *$""",
+ ^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\sprotocol\s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.protocol",
@@ -1376,8 +1336,7 @@ class Route_mapsTemplate14(NetworkTemplate):
"getval": re.compile(
r"""
^set\spolicy\sroute-map\s(?P<route_map>\S+)\srule\s(?P<sequence>\d+)\smatch\srpki
- \s(?P<value>\S+)
- *$""",
+ \s(?P<value>\S+)\s*$""",
re.VERBOSE,
),
"compval": "match.rpki",
diff --git a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py
index bf97d47b..bd76a5ae 100644
--- a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py
+++ b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py
@@ -139,9 +139,9 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\scommunity
\s+(?P<name>\S+)
- \s*(?P<auth>authorization\srw|authorization\sro)*
- \s*(client\s(?P<client>\S+))*
- \s*(network\s(?P<network>\S+))*
+ \s*(?P<auth>authorization\srw|authorization\sro)?
+ \s*(client\s(?P<client>\S+))?
+ \s*(network\s(?P<network>\S+))?
$""",
re.VERBOSE,
),
@@ -163,8 +163,7 @@ class Snmp_serverTemplate(NetworkTemplate):
"getval": re.compile(
r"""
^set\sservice\ssnmp\scontact
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": "service snmp contact {{ contact }}",
@@ -178,8 +177,7 @@ class Snmp_serverTemplate(NetworkTemplate):
"getval": re.compile(
r"""
^set\sservice\ssnmp\sdescription
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": "service snmp description {{ description }}",
@@ -194,8 +192,8 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\slisten-address
\s+(?P<addr>\S+)
- \s*(port)*
- \s*(?P<port>\d+)*
+ \s*(port)?
+ \s*(?P<port>\d+)?
$""",
re.VERBOSE,
),
@@ -231,8 +229,7 @@ class Snmp_serverTemplate(NetworkTemplate):
"getval": re.compile(
r"""
^set\sservice\ssnmp\ssmux-peer
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": "service snmp smux-peer {{ smux_peer }}",
@@ -246,8 +243,7 @@ class Snmp_serverTemplate(NetworkTemplate):
"getval": re.compile(
r"""
^set\sservice\ssnmp\strap-source
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": "service snmp trap-source {{ trap_source }}",
@@ -262,9 +258,8 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\strap-target
\s+(?P<name>\S+)
- \s*(?P<comm>community\s\S+)*
- \s*(?P<port>port\s\d+)*
- $""",
+ \s*(?P<comm>community\s\S+)?
+ \s*(?P<port>port\s\d+)? $""",
re.VERBOSE,
),
"setval": _tmplt_snmp_server_trap_target,
@@ -282,8 +277,7 @@ class Snmp_serverTemplate(NetworkTemplate):
"getval": re.compile(
r"""
^set\sservice\ssnmp\sv3\sengineid
- \s+(?P<name>\S+)
- *$""",
+ \s+(?P<name>\S+)\s*$""",
re.VERBOSE,
),
"setval": "service snmp v3 engineid {{ snmp_v3.engine_id }}",
@@ -300,9 +294,9 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\sv3\sgroup
\s+(?P<name>\S+)
- \s*(?P<mode>mode\s\S+)*
- \s*(?P<sec>seclevel\s\S+)*
- \s*(?P<view>view\s\S+)*
+ \s*(?P<mode>mode\s\S+)?
+ \s*(?P<sec>seclevel\s\S+)?
+ \s*(?P<view>view\s\S+)?
$""",
re.VERBOSE,
),
@@ -328,9 +322,9 @@ class Snmp_serverTemplate(NetworkTemplate):
^set\sservice\ssnmp\sv3\strap-target
\s+(?P<name>\S+)
\s+auth
- \s*(?P<enc>encrypted-password\s\S+)*
- \s*(?P<plain>plaintext-password\s\S+)*
- \s*(?P<type>type\s\S+)*
+ \s*(?P<enc>encrypted-password\s\S+)?
+ \s*(?P<plain>plaintext-password\s\S+)?
+ \s*(?P<type>type\s\S+)?
$""",
re.VERBOSE,
),
@@ -357,8 +351,7 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\sv3\strap-target
\s+(?P<name>\S+)
- \s+(?P<port>port\s\d+)*
- $""",
+ \s+(?P<port>port\s\d+)? $""",
re.VERBOSE,
),
"setval": "service snmp v3 trap-target port {{ snmp_v3.trap_targets.port }}",
@@ -380,7 +373,7 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\sv3\strap-target
\s+(?P<name>\S+)
- \s+(?P<protocol>protocol\s\S+)*
+ \s+(?P<protocol>protocol\s\S+)?
$""",
re.VERBOSE,
),
@@ -403,7 +396,7 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\sv3\strap-target
\s+(?P<name>\S+)
- \s+(?P<type>type\s\S+)*
+ \s+(?P<type>type\s\S+)?
$""",
re.VERBOSE,
),
@@ -426,7 +419,7 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\sv3\strap-target
\s+(?P<name>\S+)
- \s+(?P<user>user\s\S+)*
+ \s+(?P<user>user\s\S+)?
$""",
re.VERBOSE,
),
@@ -450,9 +443,9 @@ class Snmp_serverTemplate(NetworkTemplate):
^set\sservice\ssnmp\sv3\strap-target
\s+(?P<name>\S+)
\s+privacy
- \s*(?P<enc>encrypted-password\s\S+)*
- \s*(?P<plain>plaintext-password\s\S+)*
- \s*(?P<type>type\s\S+)*
+ \s*(?P<enc>encrypted-password\s\S+)?
+ \s*(?P<plain>plaintext-password\s\S+)?
+ \s*(?P<type>type\s\S+)?
$""",
re.VERBOSE,
),
@@ -480,9 +473,9 @@ class Snmp_serverTemplate(NetworkTemplate):
^set\sservice\ssnmp\sv3\suser
\s+(?P<name>\S+)
\s+auth
- \s*(?P<enc>encrypted-password\s\S+)*
- \s*(?P<plain>plaintext-password\s\S+)*
- \s*(?P<type>type\s\S+)*
+ \s*(?P<enc>encrypted-password\s\S+)?
+ \s*(?P<plain>plaintext-password\s\S+)?
+ \s*(?P<type>type\s\S+)?
$""",
re.VERBOSE,
),
@@ -510,9 +503,9 @@ class Snmp_serverTemplate(NetworkTemplate):
^set\sservice\ssnmp\sv3\suser
\s+(?P<name>\S+)
\s+privacy
- \s*(?P<enc>encrypted-password\s\S+)*
- \s*(?P<plain>plaintext-password\s\S+)*
- \s*(?P<type>type\s\S+)*
+ \s*(?P<enc>encrypted-password\s\S+)?
+ \s*(?P<plain>plaintext-password\s\S+)?
+ \s*(?P<type>type\s\S+)?
$""",
re.VERBOSE,
),
@@ -539,8 +532,7 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\sv3\suser
\s+(?P<name>\S+)
- \s+(?P<group>group\s.+)*
- $""",
+ \s+(?P<group>group\s.+)? $""",
re.VERBOSE,
),
"setval": "service snmp v3 user {{ snmp_v3.users.user }} group {{ snmp_v3.users.group }}",
@@ -562,7 +554,7 @@ class Snmp_serverTemplate(NetworkTemplate):
r"""
^set\sservice\ssnmp\sv3\suser
\s+(?P<name>\S+)
- \s+(?P<mode>mode\s\S+)*
+ \s+(?P<mode>mode\s\S+)?
$""",
re.VERBOSE,
),
@@ -586,8 +578,8 @@ class Snmp_serverTemplate(NetworkTemplate):
^set\sservice\ssnmp\sv3\sview
\s+(?P<name>\S+)
\s+(?P<oid>oid\s\S+)
- \s*(?P<ex>exclude\s\S+)*
- \s*(?P<mask>mask\s\S+)*
+ \s*(?P<ex>exclude\s\S+)?
+ \s*(?P<mask>mask\s\S+)?
$""",
re.VERBOSE,
),
diff --git a/plugins/module_utils/network/vyos/utils/version.py b/plugins/module_utils/network/vyos/utils/version.py
index 27de3f99..6d84ef1c 100644
--- a/plugins/module_utils/network/vyos/utils/version.py
+++ b/plugins/module_utils/network/vyos/utils/version.py
@@ -8,6 +8,7 @@
from __future__ import absolute_import, division, print_function
+
__metaclass__ = type
diff --git a/plugins/modules/vyos_bgp_global.py b/plugins/modules/vyos_bgp_global.py
index 2ac4f645..02f5e590 100644
--- a/plugins/modules/vyos_bgp_global.py
+++ b/plugins/modules/vyos_bgp_global.py
@@ -73,37 +73,6 @@ options:
description:
- Minimum interval for sending routing updates.
type: int
- # bfd: # <-- added in 1.3
- # description: Enable Bidirectional Forwarding Detection (BFD) support
- # type: dict
- # suboptions:
- # check-control-plane-failure:
- # description:
- # - Allow to write CBIT independence in BFD outgoing packets
- # and read both C-BIT value of BFD and lookup BGP peer status
- # type: bool
- # allowas_in: --> Moved to address-family before 1.3
- # description:
- # - Number of occurrences of AS number.
- # type: int
- # as_override: --> Moved to address-family before 1.3
- # description:
- # - AS for routes sent to this neighbor to be the local AS.
- # type: bool
- # attribute_unchanged: --> Moved to address-family before 1.3
- # description:
- # - BGP attributes are sent unchanged.
- # type: dict
- # suboptions:
- # as_path:
- # description: as_path
- # type: bool
- # med:
- # description: med
- # type: bool
- # next_hop:
- # description: next_hop
- # type: bool
capability:
description:
- Advertise capabilities to this neighbor.
@@ -117,13 +86,6 @@ options:
description:
- Advertise extended nexthop capability to this neighbor.
type: bool
- # orf: --> Removed before 1.3
- # description:
- # - Advertise ORF capability to this neighbor.
- # type: str
- # choices:
- # - send
- # - receive
default_originate:
description:
- Send default route to this neighbor
@@ -145,70 +107,14 @@ options:
- Disable sending community attributes to this neighbor.
type: str
choices: ['extended', 'standard']
- # distribute_list: --> Moved to address-family before 1.3
- # description: Access-list to filter route updates to/from this neighbor.
- # type: list
- # elements: dict
- # suboptions:
- # action:
- # description: Access-list to filter outgoing/incoming route updates to this neighbor
- # type: str
- # choices: ['export', 'import']
- # acl:
- # description: Access-list number.
- # type: int
ebgp_multihop:
description:
- Allow this EBGP neighbor to not be on a directly connected network. Specify
- the number hops.
+ the number of hops.
type: int
- # interface: # <-- added in 1.3
- # description: interface parameters
- # type: dict
- # suboptions:
- # peer_group:
- # description: Peer group for this neighbor
- # type: str
- # remote_as:
- # description:
- # - Remote AS number
- # - Or 'external' for any number except this AS number
- # - or 'internal' for this AS number
- # type: str
- # v6only:
- # description: Enable BGP with v6 link-local only
- # type: dict
- # suboptions:
- # peer_group:
- # description: Peer group for this neighbor
- # type: str
- # remote_as:
- # description:
- # - Remote AS number
- # - Or 'external' for any number except this AS number
- # - or 'internal' for this AS number
- # filter_list: --> Moved to address-family before 1.3
- # description: As-path-list to filter route updates to/from this neighbor.
- # type: list
- # elements: dict
- # suboptions:
- # action:
- # description: filter outgoing/incoming route updates
- # type: str
- # choices: ['export', 'import']
- # path_list:
- # description: As-path-list to filter
- # type: str
local_as:
description: local as number not to be prepended to updates from EBGP peers
type: int
- # maximum_prefix: --> Moved to address-family before 1.3
- # description: Maximum number of prefixes to accept from this neighbor
- # nexthop-self Nexthop for routes sent to this neighbor to be the local router.
- # type: int
- # nexthop_self: --> Moved to address-family before 1.3
- # description: Nexthop for routes sent to this neighbor to be the local router.
- # type: bool
override_capability:
description: Ignore capability negotiation with specified neighbor.
type: bool
@@ -227,61 +133,18 @@ options:
port:
description: Neighbor's BGP port
type: int
- # prefix_list: --> Moved to address-family before 1.3
- # description: Prefix-list to filter route updates to/from this neighbor.
- # type: list
- # elements: dict
- # suboptions:
- # action:
- # description: filter outgoing/incoming route updates
- # type: str
- # choices: ['export', 'import']
- # prefix_list:
- # description: Prefix-list to filter
- # type: str
remote_as:
description: Neighbor BGP AS number
type: int
- # remove_private_as: --> Moved to address-family before 1.3
- # description: Remove private AS numbers from AS path in outbound route updates
- # type: bool
- # route_map: --> Moved to address-family before 1.3
- # description: Route-map to filter route updates to/from this neighbor.
- # type: list
- # elements: dict
- # suboptions:
- # action:
- # description: filter outgoing/incoming route updates
- # type: str
- # choices: ['export', 'import']
- # route_map:
- # description: route-map to filter
- # type: str
- # route_reflector_client: --> Moved to address-family before 1.3
- # description: Neighbor as a route reflector client
- # type: bool
- # route_server_client: --> Removed prior to 1.3
- # description: Neighbor is route server client
- # type: bool
shutdown:
description: Administratively shut down neighbor
type: bool
- # soft_reconfiguration: --> Moved to address-family before 1.3
- # description: Soft reconfiguration for neighbor
- # type: bool
solo: # <-- added in 1.3
description: Do not send back prefixes learned from the neighbor
type: bool
strict_capability_match:
description: Enable strict capability negotiation
type: bool
- # unsuppress_map: --> Moved to address-family before 1.3
- # description: Route-map to selectively unsuppress suppressed routes
- # type: str
-
- # weight: --> Moved to address-family before 1.3
- # description: Default weight for routes from this neighbor
- # type: int
timers:
description: Neighbor timers
type: dict
diff --git a/plugins/modules/vyos_config.py b/plugins/modules/vyos_config.py
index 1a4ec351..717e1aa2 100644
--- a/plugins/modules/vyos_config.py
+++ b/plugins/modules/vyos_config.py
@@ -141,6 +141,20 @@ options:
in C(filename) within I(backup) directory.
type: path
type: dict
+ allow_password_change:
+ description:
+ - The C(allow_password_change) argument specifies whether any configuration lines which
+ would change a user's password should be filtered out. By default only plaintext
+ password changes are allowed and any encrypted-password keys are filtered out. In
+ order to allow all password updates, both plaintext and encrypted, set this argument
+ to C(all).
+ type: str
+ default: plaintext
+ choices:
+ - all
+ - plaintext
+ - encrypted
+ - none
"""
EXAMPLES = """
@@ -231,9 +245,7 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import
DEFAULT_COMMENT = "configured by vyos_config"
-CONFIG_FILTERS = [
- re.compile(r"set system login user \S+ authentication encrypted-password"),
-]
+PASSWORD_NEEDLE = re.compile(r"set system login user \S+ authentication (encrypted|plaintext)-password")
def get_candidate(module):
@@ -292,14 +304,26 @@ def diff_config(commands, config):
return list(updates)
-def sanitize_config(config, result):
+def sanitize_config(config, result, allow):
result["filtered"] = list()
+
+ if allow == "all":
+ return
+
index_to_filter = list()
- for regex in CONFIG_FILTERS:
- for index, line in enumerate(list(config)):
- if regex.search(line):
- result["filtered"].append(line)
- index_to_filter.append(index)
+
+ for index, line in enumerate(list(config)):
+ found = PASSWORD_NEEDLE.search(line)
+
+ if found is None:
+ continue
+
+ if allow == found[1]:
+ continue
+
+ result["filtered"].append(line)
+ index_to_filter.append(index)
+
# Delete all filtered configs
for filter_index in sorted(index_to_filter, reverse=True):
del config[filter_index]
@@ -326,7 +350,9 @@ def run(module, result):
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
commands = response.get("config_diff")
- sanitize_config(commands, result)
+
+ allow_password_change = module.params["allow_password_change"]
+ sanitize_config(commands, result, allow=allow_password_change)
result["commands"] = commands
@@ -366,6 +392,7 @@ def main():
backup=dict(type="bool", default=False),
backup_options=dict(type="dict", options=backup_spec),
save=dict(type="bool", default=False),
+ allow_password_change=dict(default="plaintext", choices=["all", "encrypted", "plaintext", "none"])
)
mutually_exclusive = [("lines", "src")]
diff --git a/plugins/modules/vyos_user.py b/plugins/modules/vyos_user.py
index 6ee6b3aa..6ca6f0c6 100644
--- a/plugins/modules/vyos_user.py
+++ b/plugins/modules/vyos_user.py
@@ -204,6 +204,7 @@ commands:
"""
import re
+import shlex
from copy import deepcopy
from functools import partial
@@ -275,7 +276,8 @@ def spec_to_commands(updates, module):
add(
commands,
want,
- "authentication plaintext-password %s" % want["configured_password"],
+ "authentication plaintext-password %s"
+ % shlex.quote(want["configured_password"]),
)
return commands
diff --git a/plugins/modules/vyos_vlan.py b/plugins/modules/vyos_vlan.py
index 81192bd7..d2e004f8 100644
--- a/plugins/modules/vyos_vlan.py
+++ b/plugins/modules/vyos_vlan.py
@@ -278,15 +278,11 @@ def map_config_to_obj(module):
obj = {}
eth = splitted_line[0].strip("'")
- if eth.startswith("eth"):
+ if eth.startswith("eth") and "." in eth:
obj["interfaces"] = []
- if "." in eth:
- interface = eth.split(".")[0]
- obj["interfaces"].append(interface)
- obj["vlan_id"] = eth.split(".")[-1]
- else:
- obj["interfaces"].append(eth)
- obj["vlan_id"] = None
+ interface = eth.split(".")[0]
+ obj["interfaces"].append(interface)
+ obj["vlan_id"] = eth.split(".")[-1]
if splitted_line[1].strip("'") != "-":
obj["address"] = splitted_line[1].strip("'")
diff --git a/tests/integration/targets/vyos_config/tests/cli/check_config.yaml b/tests/integration/targets/vyos_config/tests/cli/check_config.yaml
index 8e2e8372..83a62197 100644
--- a/tests/integration/targets/vyos_config/tests/cli/check_config.yaml
+++ b/tests/integration/targets/vyos_config/tests/cli/check_config.yaml
@@ -49,9 +49,43 @@
- set system login user esa authentication encrypted-password '!abc!'
- set system login user vyos full-name 'VyOS admin'
- set system login user vyos authentication encrypted-password 'abc'
+ - set system login user john full-name 'John'
+ - set system login user john authentication plaintext-password 'xyz'
- assert:
that:
- result.filtered|length == 2
+- name: check multiple line config filter is working
+ register: result
+ vyos.vyos.vyos_config:
+ allow_password_change: none
+ lines:
+ - set system login user esa full-name 'ESA admin'
+ - set system login user esa authentication encrypted-password '!abc!'
+ - set system login user vyos full-name 'VyOS admin'
+ - set system login user vyos authentication encrypted-password 'abc'
+ - set system login user john full-name 'John'
+ - set system login user john authentication plaintext-password 'xyz'
+
+- assert:
+ that:
+ - result.filtered|length == 3
+
+- name: check multiple line config filter is working
+ register: result
+ vyos.vyos.vyos_config:
+ allow_password_change: all
+ lines:
+ - set system login user esa full-name 'ESA admin'
+ - set system login user esa authentication encrypted-password '!abc!'
+ - set system login user vyos full-name 'VyOS admin'
+ - set system login user vyos authentication encrypted-password 'abc'
+ - set system login user john full-name 'John'
+ - set system login user john authentication plaintext-password 'xyz'
+
+- assert:
+ that:
+ - result.filtered|length == 0
+
- debug: msg="END cli/config_check.yaml on connection={{ ansible_connection }}"
diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces.cfg
new file mode 100644
index 00000000..b877a4b6
--- /dev/null
+++ b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces.cfg
@@ -0,0 +1,10 @@
+Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
+Interface IP Address S/L Description
+--------- ---------- --- -----------
+eth0 10.0.2.15/24 u/u
+eth0.100 - u/u vlan-100
+eth1 - u/u
+eth1.200 192.0.2.1/24 u/u vlan-200
+eth2 - u/u
+lo 127.0.0.1/8 u/u
+ ::1/128
diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces_empty.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces_empty.cfg
new file mode 100644
index 00000000..06ae56a0
--- /dev/null
+++ b/tests/unit/modules/network/vyos/fixtures/vyos_vlan_show_interfaces_empty.cfg
@@ -0,0 +1,7 @@
+Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
+Interface IP Address S/L Description
+--------- ---------- --- -----------
+eth0 10.0.2.15/24 u/u
+eth1 - u/u
+eth2 - u/u
+lo 127.0.0.1/8 u/u
diff --git a/tests/unit/modules/network/vyos/test_rm_templates_perf.py b/tests/unit/modules/network/vyos/test_rm_templates_perf.py
new file mode 100644
index 00000000..d2060f46
--- /dev/null
+++ b/tests/unit/modules/network/vyos/test_rm_templates_perf.py
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+# Copyright 2026 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""Performance budget tests for rm_templates parsers.
+
+These tests guard against re-introducing catastrophic regex backtracking
+in the rm_template parsers (T8609). Pre-fix, parse() over realistic
+device-output input could take 50+ seconds because of `(group)*`
+quantifiers on groups containing `\\S+`. Post-fix, the same input
+parses in single-digit milliseconds.
+
+A 1-second budget is comfortably above post-fix runtime and well below
+the pre-regression cliff, so the test fails sharply if the bug returns.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+import time
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family_14 import (
+ Bgp_address_familyTemplate14,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_global_14 import (
+ Bgp_globalTemplate14,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.route_maps_14 import (
+ Route_mapsTemplate14,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.snmp_server import (
+ Snmp_serverTemplate,
+)
+
+
+PARSE_BUDGET_SECONDS = 1.0
+
+
+def _time_parse(parser_class, lines):
+ """Time a single ``parse()`` call against ``lines``; returns elapsed seconds."""
+ parser = parser_class(lines=lines)
+ t0 = time.perf_counter()
+ parser.parse()
+ return time.perf_counter() - t0
+
+
+def test_route_maps_14_parse_budget():
+ """Realistic route-map config: parse() must finish under 1s.
+
+ Pre-T8609: ~50s. Post-fix: <50ms. Inputs use 20+ char names
+ because the backtracking is exponential in the first \\S+ run
+ after the prefix.
+ """
+ lines = [
+ "set policy route-map ADVERTISE-ANYCAST-v6 rule 10 action 'permit'",
+ "set policy route-map ADVERTISE-ANYCAST-v6 rule 10 match ipv6 address prefix-list 'ANYCAST-AGGREGATE-v6'",
+ "set policy route-map DEFAULT-ORIGINATE-SENTINEL-v6 rule 10 action 'permit'",
+ "set policy route-map DEFAULT-ORIGINATE-SENTINEL-v6 rule 10 match ipv6 address prefix-list 'AS64496-SENTINEL-v6'",
+ "set policy route-map DEFAULT-ORIGINATE-SENTINEL-v6 rule 10 set local-preference '120'",
+ "set policy route-map IXP-PEER-INGRESS-v4 rule 10 action 'permit'",
+ "set policy route-map IXP-PEER-INGRESS-v4 rule 10 match ip address prefix-list 'IXP-INBOUND-v4'",
+ "set policy route-map IXP-PEER-INGRESS-v4 rule 10 set community 'additive 65000:100'",
+ "set policy route-map TRANSIT-EGRESS-v4 rule 100 action 'permit'",
+ "set policy route-map TRANSIT-EGRESS-v4 rule 100 match ip address prefix-list 'CUSTOMER-PREFIXES-v4'",
+ "set policy route-map TRANSIT-EGRESS-v4 rule 100 set as-path prepend '65000 65000'",
+ "set policy route-map UPSTREAM-INGRESS-v4 rule 10 action 'permit'",
+ ]
+ elapsed = _time_parse(Route_mapsTemplate14, lines)
+ assert elapsed < PARSE_BUDGET_SECONDS, (
+ "Route_mapsTemplate14.parse() took %.2fs (budget %.2fs); "
+ "possible regression of T8609 (rm_templates regex backtracking)."
+ % (elapsed, PARSE_BUDGET_SECONDS)
+ )
+
+
+def test_bgp_global_14_parse_budget():
+ """Realistic BGP neighbor/address-family config: parse() under 1s."""
+ lines = [
+ "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 remote-as '65002'",
+ "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 description 'IXP-PEER-1'",
+ "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 address-family ipv6-unicast route-map import 'IXP-INGRESS-v6'",
+ "set protocols bgp 65001 neighbor 2001:db8:abcd:1234::1 address-family ipv6-unicast route-map export 'IXP-EGRESS-v6'",
+ "set protocols bgp 65001 neighbor 192.0.2.1 remote-as '65003'",
+ "set protocols bgp 65001 neighbor 192.0.2.1 description 'TRANSIT-PROVIDER-1'",
+ "set protocols bgp 65001 neighbor 192.0.2.1 address-family ipv4-unicast route-map import 'TRANSIT-INGRESS-v4'",
+ "set protocols bgp 65001 neighbor 192.0.2.1 address-family ipv4-unicast route-map export 'TRANSIT-EGRESS-v4'",
+ ]
+ elapsed = _time_parse(Bgp_globalTemplate14, lines)
+ assert elapsed < PARSE_BUDGET_SECONDS, (
+ "Bgp_globalTemplate14.parse() took %.2fs (budget %.2fs); "
+ "possible regression of T8609 (rm_templates regex backtracking)."
+ % (elapsed, PARSE_BUDGET_SECONDS)
+ )
+
+
+def test_snmp_server_parse_budget():
+ """Realistic SNMP v3 config: parse() under 1s."""
+ lines = [
+ "set service snmp community PUBLIC-COMMUNITY-NAME-1 authorization 'ro'",
+ "set service snmp community PUBLIC-COMMUNITY-NAME-1 client '192.0.2.0/24'",
+ "set service snmp v3 trap-target TRAP-TARGET-LONG-NAME-1 user 'monitor'",
+ "set service snmp v3 trap-target TRAP-TARGET-LONG-NAME-1 protocol 'udp'",
+ "set service snmp v3 trap-target TRAP-TARGET-LONG-NAME-1 port '162'",
+ "set service snmp v3 user TRAP-USER-LONG-NAME-1 mode 'auth'",
+ "set service snmp v3 user TRAP-USER-LONG-NAME-1 group 'monitor'",
+ ]
+ elapsed = _time_parse(Snmp_serverTemplate, lines)
+ assert elapsed < PARSE_BUDGET_SECONDS, (
+ "Snmp_serverTemplate.parse() took %.2fs (budget %.2fs); "
+ "possible regression of T8609 (rm_templates regex backtracking)."
+ % (elapsed, PARSE_BUDGET_SECONDS)
+ )
+
+
+def test_bgp_address_family_14_parse_budget():
+ """Realistic BGP address-family aggregate config: parse() under 1s."""
+ lines = [
+ "set protocols bgp 65001 address-family ipv4-unicast network 198.51.100.0/24 backdoor",
+ "set protocols bgp 65001 address-family ipv4-unicast network 198.51.100.0/24 path-limit '4'",
+ "set protocols bgp 65001 address-family ipv4-unicast network 198.51.100.0/24 route-map 'NET-IN-v4'",
+ "set protocols bgp 65001 address-family ipv4-unicast aggregate-address 203.0.113.0/24 as-set",
+ "set protocols bgp 65001 address-family ipv4-unicast aggregate-address 203.0.113.0/24 summary-only",
+ "set protocols bgp 65001 address-family ipv6-unicast network 2001:db8:abcd:1234::/64 backdoor",
+ "set protocols bgp 65001 address-family ipv6-unicast network 2001:db8:abcd:1234::/64 route-map 'NET-IN-v6'",
+ "set protocols bgp 65001 address-family ipv6-unicast aggregate-address 2001:db8::/32 as-set",
+ "set protocols bgp 65001 address-family ipv6-unicast aggregate-address 2001:db8::/32 summary-only",
+ ]
+ elapsed = _time_parse(Bgp_address_familyTemplate14, lines)
+ assert elapsed < PARSE_BUDGET_SECONDS, (
+ "Bgp_address_familyTemplate14.parse() took %.2fs (budget %.2fs); "
+ "possible regression of T8609 (rm_templates regex backtracking)."
+ % (elapsed, PARSE_BUDGET_SECONDS)
+ )
+
+
+def test_route_maps_14_set_comm_list_delete_matches_setval():
+ """Round-trip check: the `set_comm_list_delete` parser must match the line its setval generates.
+
+ `set_comm_list_delete`'s setval emits `set policy route-map X rule N set
+ comm-list delete` with no token after `delete`. Pre-T8609 the getval
+ happened to match this by accident (a `*` quantifier on the trailing
+ `(?P<delete>\\S+)` made the group optional after VERBOSE-strip). An
+ earlier draft of T8609's fix made the group required, causing the
+ parser to silently ignore its own output. This test guards the
+ round-trip.
+ """
+ line = "set policy route-map MY-MAP rule 10 set comm-list delete"
+ parser = Route_mapsTemplate14(lines=[line])
+ result = parser.parse()
+ rm = result.get("route_maps", {}).get("MY-MAP")
+ assert rm is not None, (
+ "route_maps_14: set_comm_list_delete parser failed to match its "
+ "own setval-generated line %r; the parser is broken." % line
+ )
+ entry = rm.get("entries", {}).get(10, {})
+ comm_list = entry.get("set", {}).get("comm_list", {})
+ assert comm_list.get("delete"), (
+ "route_maps_14: set_comm_list_delete matched the line but did not "
+ "populate set.comm_list.delete; check the result template."
+ )
diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py b/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py
index 07482f8e..457fc76b 100644
--- a/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py
+++ b/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py
@@ -1575,3 +1575,17 @@ class TestVyosFirewallRulesModule13(TestVyosModule):
]
self.maxDiff = None
self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_firewall_rules_parsed_icmp_type_code(self):
+ """parse_icmp_attr: legacy 'type/code' token parses into integer type and code."""
+ raw = (
+ "set firewall name TEST rule 1 action 'accept'\n"
+ "set firewall name TEST rule 1 protocol 'icmp'\n"
+ "set firewall name TEST rule 1 icmp type '3/4'\n"
+ )
+ set_module_args(dict(running_config=raw, state="parsed"))
+ result = self.execute_module(changed=False)
+ parsed = result["parsed"]
+ rule = parsed[0]["rule_sets"][0]["rules"][0]
+ self.assertEqual(rule["icmp"]["type"], 3)
+ self.assertEqual(rule["icmp"]["code"], 4)
diff --git a/tests/unit/modules/network/vyos/test_vyos_user.py b/tests/unit/modules/network/vyos/test_vyos_user.py
index 9d11c904..0e60f07e 100644
--- a/tests/unit/modules/network/vyos/test_vyos_user.py
+++ b/tests/unit/modules/network/vyos/test_vyos_user.py
@@ -61,6 +61,36 @@ class TestVyosUserModule(TestVyosModule):
["set system login user ansible authentication plaintext-password test"],
)
+ def test_vyos_user_password_special_chars(self):
+ set_module_args(dict(name="ansible", configured_password="test$123!@"))
+ result = self.execute_module(changed=True)
+ self.assertEqual(
+ result["commands"],
+ [
+ "set system login user ansible authentication plaintext-password 'test$123!@'",
+ ],
+ )
+
+ def test_vyos_user_password_embedded_quote(self):
+ set_module_args(dict(name="ansible", configured_password="pa'ss"))
+ result = self.execute_module(changed=True)
+ self.assertEqual(
+ result["commands"],
+ [
+ "set system login user ansible authentication plaintext-password 'pa'\"'\"'ss'",
+ ],
+ )
+
+ def test_vyos_user_password_complex_special_chars(self):
+ set_module_args(dict(name="ansible", configured_password="P@ss w0rd!$#'xy\\"))
+ result = self.execute_module(changed=True)
+ self.assertEqual(
+ result["commands"],
+ [
+ "set system login user ansible authentication plaintext-password 'P@ss w0rd!$#'\"'\"'xy\\'",
+ ],
+ )
+
def test_vyos_user_delete(self):
set_module_args(dict(name="ansible", state="absent"))
result = self.execute_module(changed=True)
diff --git a/tests/unit/modules/network/vyos/test_vyos_vlan.py b/tests/unit/modules/network/vyos/test_vyos_vlan.py
new file mode 100644
index 00000000..5bbf87c2
--- /dev/null
+++ b/tests/unit/modules/network/vyos/test_vyos_vlan.py
@@ -0,0 +1,158 @@
+# (c) 2016 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_vlan
+from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args
+
+from .vyos_module import TestVyosModule, load_fixture
+
+
+class TestVyosVlanModule(TestVyosModule):
+ module = vyos_vlan
+
+ def setUp(self):
+ super(TestVyosVlanModule, self).setUp()
+
+ self.mock_load_config = patch(
+ "ansible_collections.vyos.vyos.plugins.modules.vyos_vlan.load_config",
+ )
+ self.load_config = self.mock_load_config.start()
+
+ self.mock_run_commands = patch(
+ "ansible_collections.vyos.vyos.plugins.modules.vyos_vlan.run_commands",
+ )
+ self.run_commands = self.mock_run_commands.start()
+
+ def tearDown(self):
+ super(TestVyosVlanModule, self).tearDown()
+ self.mock_load_config.stop()
+ self.mock_run_commands.stop()
+
+ def load_fixtures(self, commands=None, filename=None):
+ self.load_config.return_value = dict(diff=None, session="session")
+ if filename == "empty":
+ self.run_commands.return_value = [
+ load_fixture("vyos_vlan_show_interfaces_empty.cfg"),
+ ]
+ else:
+ self.run_commands.return_value = [
+ load_fixture("vyos_vlan_show_interfaces.cfg"),
+ ]
+
+ def test_vyos_vlan_present(self):
+ """Create a new VLAN with a description on eth2 (not in have)."""
+ set_module_args(
+ dict(
+ vlan_id=300,
+ name="vlan-300",
+ interfaces=["eth2"],
+ state="present",
+ )
+ )
+ commands = ["set interfaces ethernet eth2 vif 300 description vlan-300"]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_vlan_present_no_change(self):
+ """Existing VLAN 100 on eth0 — no commands should be generated."""
+ set_module_args(
+ dict(
+ vlan_id=100,
+ name="vlan-100",
+ interfaces=["eth0"],
+ state="present",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_vlan_absent(self):
+ """Delete an existing VLAN (200 on eth1)."""
+ set_module_args(
+ dict(
+ vlan_id=200,
+ interfaces=["eth1"],
+ state="absent",
+ )
+ )
+ commands = ["delete interfaces ethernet eth1 vif 200"]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_vlan_absent_no_change(self):
+ """Delete a VLAN that does not exist — no commands."""
+ set_module_args(
+ dict(
+ vlan_id=999,
+ interfaces=["eth0"],
+ state="absent",
+ )
+ )
+ self.execute_module(changed=False, commands=[])
+
+ def test_vyos_vlan_aggregate(self):
+ """Create two new VLANs via aggregate; neither is in have."""
+ set_module_args(
+ dict(
+ aggregate=[
+ dict(vlan_id=301, interfaces=["eth2"], name="vlan-301"),
+ dict(vlan_id=302, interfaces=["eth2"], name="vlan-302"),
+ ],
+ )
+ )
+ commands = [
+ "set interfaces ethernet eth2 vif 301 description vlan-301",
+ "set interfaces ethernet eth2 vif 302 description vlan-302",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_vlan_purge(self):
+ """Purge VLANs not in want. Want only VLAN 100; VLAN 200 should be removed.
+
+ The fixed parser only maps ethX.Y sub-interfaces, so bare ethX interfaces
+ (vlan_id=None) are not in have. Only real VLANs (eth1.200) are purged.
+ """
+ set_module_args(
+ dict(
+ vlan_id=100,
+ interfaces=["eth0"],
+ state="present",
+ purge=True,
+ )
+ )
+ commands = [
+ "delete interfaces ethernet eth1 vif 200",
+ ]
+ self.execute_module(changed=True, commands=commands)
+
+ def test_vyos_vlan_with_address(self):
+ """Create a VLAN with an IP address and no description."""
+ set_module_args(
+ dict(
+ vlan_id=400,
+ address="10.10.40.1/24",
+ interfaces=["eth1"],
+ state="present",
+ )
+ )
+ commands = ["set interfaces ethernet eth1 vif 400 address 10.10.40.1/24"]
+ self.execute_module(changed=True, commands=commands, filename="empty")