From 5b80cd0cbddd014e54b01e7fa1cd0c0777d94724 Mon Sep 17 00:00:00 2001 From: Robert Navarro Date: Fri, 19 Jun 2026 06:02:27 -0700 Subject: rm_templates: T8609: fix slow parse() from group-quantifier patterns (#470) * rm_templates: T8609: fix slow parse() from group-quantifier patterns Running an Ansible playbook that manages BGP route-maps on my VyOS 1.4 edge routers, the vyos.vyos.vyos_route_maps task (state=gathered or state=merged) takes ~50 seconds per host, sometimes 100s+, against devices with only a handful of route-maps configured. The persistent connection times out before the module finishes and the failure surfaces as a misleading "socket path does not exist". Three related quantifier shapes in nine rm_templates files cause O(2^n) regex backtracking on inputs that share a parser's prefix but don't match overall. parse() time on a representative 12-line route-map config drops from ~50s to <1ms post-fix. * Trailing form (188 sites): `(?P\S+)\n *$"""` -> `(?P\S+)\s*$"""`. Under re.VERBOSE the literal newline+indent between `\S+` and `*$` is stripped at compile time, so the source compiled to `(\S+)*$` with the `*` quantifying the named group. * Mid-pattern `(group containing \S+)*` (22 sites in snmp_server.py with a couple in bgp_global*.py): e.g. `(?Pprotocol\s\S+)*` -> `(?Pprotocol\s\S+)?`. Different position from the trailing form, but the same shape underneath (a group whose content includes \S+, quantified with `*`), so the same O(2^n) backtracking on prefix-sharing inputs. * Mid-pattern `(literal-only group)*` (14 sites in snmp_server.py, bgp_address_family*.py, bgp_global*.py): e.g. `(?Pas-set)*` -> `(?Pas-set)?`. No \S+ inside the quantified group, so the backtracking exposure is much smaller. On the inputs these patterns actually receive (no device line carries duplicate flags) `*` and `?` accept identical input sets, so changing to `?` is behavior-preserving. The first draft of this fix made set_comm_list_delete's `(?P\S+)` group required. The parser's setval emits `set comm-list delete` with no token after `delete`, so the parser stopped matching its own output (no existing fixture covered this case, which is why the regression initially shipped). CodeRabbit caught it during review. Replaced `delete(?P\S+)\s*$` with `\s(?Pdelete)\s*$` so the named group captures the literal word `delete`; the result template `{{True if delete is defined}}` continues to evaluate True. Affects: route_maps[/_14], bgp_global[/_14], bgp_address_family[/_14], snmp_server, ospf_interfaces[/_14]. Adds tests/unit/modules/network/vyos/test_rm_templates_perf.py with a 1-second budget against fixture inputs that have realistic-length identifiers, plus a round-trip test asserting set_comm_list_delete matches its own setval-generated line, plus a Bgp_address_familyTemplate14 budget test for symmetry with the other "hot" template families. * Fix regex backtracking issues in parse() method Fix slow parse() to prevent regex backtracking on prefix-sharing inputs across multiple files. Add unit test for the bugfixes. * Refactor bug fixes for regex backtracking improvements Updated bug fixes to include details on regex backtracking issues in multiple files. * Fix typo in bugfixes section of changelog * Update bugfixes for regex backtracking issues * rm_templates: T8609: route_maps.py: fix remaining trailing `*$` patterns Five sites in route_maps.py still had the `(?P...)\n *$` shape that collapses to `(?P...)*$` under `re.VERBOSE`, parsers `sequence`, `on_match_next`, `set_atomic_aggregate`, `set_extcommunity_bandwidth_non_transitive`, and `match_community_exact_match`. Collapsed each to `\s*$` on the same line as the named group, matching the rest of the PR. * rm_templates: T8609: route_maps.py: split overlong getval to satisfy E501 Line 517 was 161 chars (`set_extcommunity_bandwidth_non_transitive` parser) and tripped pycodestyle's E501 sanity test in CI. Split the regex source across two lines at the `\d+)` boundary; under `re.VERBOSE` the literal newline and indent between regex tokens are stripped at compile time, so the engine sees the same pattern. `\s*$` stays on the same line as the closing capture group, so the group-quantifier shape this PR fixes elsewhere isn't reintroduced. --------- Co-authored-by: omnom62 <75066712+omnom62@users.noreply.github.com> --- .../T8609_rm_templates_regex_backtracking.yml | 12 ++ .../vyos/rm_templates/bgp_address_family.py | 49 +++---- .../vyos/rm_templates/bgp_address_family_14.py | 49 +++---- .../network/vyos/rm_templates/bgp_global.py | 95 ++++-------- .../network/vyos/rm_templates/bgp_global_14.py | 95 ++++-------- .../network/vyos/rm_templates/ospf_interfaces.py | 9 +- .../vyos/rm_templates/ospf_interfaces_14.py | 12 +- .../network/vyos/rm_templates/route_maps.py | 145 +++++++----------- .../network/vyos/rm_templates/route_maps_14.py | 123 ++++++---------- .../network/vyos/rm_templates/snmp_server.py | 78 +++++----- .../modules/network/vyos/test_rm_templates_perf.py | 163 +++++++++++++++++++++ 11 files changed, 405 insertions(+), 425 deletions(-) create mode 100644 changelogs/fragments/T8609_rm_templates_regex_backtracking.yml create mode 100644 tests/unit/modules/network/vyos/test_rm_templates_perf.py 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/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py index f8f86cd2..fccfc9c2 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 @@ -293,8 +293,8 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+aggregate-address \s+(?P
\S+) - \s*(?Pas-set)* - \s*(?Psummary-only)* + \s*(?Pas-set)? + \s*(?Psummary-only)? $""", re.VERBOSE, ), @@ -397,8 +397,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+network \s+(?P
\S+) \s+path-limit - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -432,8 +431,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+network \s+(?P
\S+) \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -499,8 +497,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+redistribute \s+(?P\S+) \s+metric - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -534,8 +531,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+redistribute \s+(?P\S+) \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -568,8 +564,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+redistribute \s+table - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -659,8 +654,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+allowas-in \s+number - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -877,8 +871,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+capability \s+prefix-list - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -915,8 +908,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+default-originate \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -992,8 +984,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+prefix-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_prefix_list, @@ -1033,8 +1024,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+filter-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_filter_list, @@ -1073,8 +1063,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+maximum-prefix - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1176,8 +1165,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+peer-group - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1246,8 +1234,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+(?P\S+)-unicast \s+route-map \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_route_map, @@ -1389,8 +1376,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+unsuppress-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1424,8 +1410,7 @@ class Bgp_address_familyTemplate(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+weight - \s+(?P\S+) - *$""", + \s+(?P\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 fd4c9de9..09cd41a7 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 @@ -309,8 +309,8 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+aggregate-address \s+(?P
\S+) - \s*(?Pas-set)* - \s*(?Psummary-only)* + \s*(?Pas-set)? + \s*(?Psummary-only)? $""", re.VERBOSE, ), @@ -410,8 +410,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+network \s+(?P
\S+) \s+path-limit - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -444,8 +443,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+network \s+(?P
\S+) \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_network, @@ -508,8 +506,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+redistribute \s+(?P\S+) - \s+metric\s+(?P\S+) - *$""", + \s+metric\s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -542,8 +539,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+redistribute \s+(?P\S+) \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -575,8 +571,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+redistribute \s+table - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_redistribute, @@ -663,8 +658,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+allowas-in \s+number - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -875,8 +869,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+capability \s+prefix-list - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -912,8 +905,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+default-originate \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -987,8 +979,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+prefix-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_prefix_list, @@ -1027,8 +1018,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+filter-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_filter_list, @@ -1066,8 +1056,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+maximum-prefix - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1166,8 +1155,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+peer-group - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1234,8 +1222,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+(?P\S+)-unicast \s+route-map \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor_route_map, @@ -1373,8 +1360,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+unsuppress-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_af_neighbor, @@ -1407,8 +1393,7 @@ class Bgp_address_familyTemplate14(NetworkTemplate): \s+address-family \s+(?P\S+)-unicast \s+weight - \s+(?P\S+) - *$""", + \s+(?P\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 8c2e2f55..03f6265b 100644 --- a/plugins/module_utils/network/vyos/rm_templates/bgp_global.py +++ b/plugins/module_utils/network/vyos/rm_templates/bgp_global.py @@ -237,8 +237,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+advertisement-interval - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}", @@ -437,8 +436,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+capability \s+orf \s+prefix-list - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}", @@ -467,8 +465,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P
\S+) \s+default-originate \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}", @@ -494,8 +491,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+description - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} description {{ neighbor.description }}", @@ -573,8 +569,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+disable-send-community - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}", @@ -633,8 +628,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+ebgp-multihop - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}", @@ -661,8 +655,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P
\S+) \s+filter-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_filter_list, @@ -721,8 +714,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+maximum-prefix - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}", @@ -826,8 +818,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+password - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} password {{ neighbor.password }}", @@ -853,8 +844,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+peer-group - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}", @@ -880,8 +870,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+port - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} port {{ neighbor.port }}", @@ -908,8 +897,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P
\S+) \s+prefix-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_prefix_list, @@ -940,8 +928,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+remote-as - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}", @@ -994,8 +981,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P
\S+) \s+route-map \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_route_map, @@ -1157,8 +1143,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+unsuppress-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}", @@ -1184,8 +1169,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+update-source - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}", @@ -1211,8 +1195,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+weight - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} weight {{ neighbor.weight }}", @@ -1238,8 +1221,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+ttl-security - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}", @@ -1266,8 +1248,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P
\S+) \s+timers \s+(?Pconnect|holdtime|keepalive) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_timers, @@ -1295,8 +1276,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P\d+) \s+timers \s+(?P\S+) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_timers, @@ -1418,8 +1398,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P\d+) \s+parameters \s+cluster-id - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters cluster-id {{ bgp_params.cluster_id }}", @@ -1442,8 +1421,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+confederation \s+(?Pidentifier|peers) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_confederation, @@ -1471,8 +1449,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+half-life - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening half-life {{ bgp_params.dampening.half_life}}", @@ -1497,8 +1474,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+max-suppress-time - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}", @@ -1523,8 +1499,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+re-use - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening re-use {{ bgp_params.dampening.re_use}}", @@ -1549,8 +1524,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+dampening \s+start-suppress-time - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}", @@ -1574,9 +1548,8 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P\d+) \s+parameters \s+default - \s*(?Pno-ipv4-unicast)* - \s*(?Plocal-pref\s\S+) - *$""", + \s*(?Pno-ipv4-unicast)? + \s*(?Plocal-pref\s\S+)?\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_default, @@ -1648,8 +1621,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+distance\sprefix \s+(?P\S+) \s+distance - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}", @@ -1678,8 +1650,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+parameters \s+distance\sglobal \s+(?P\S+) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_distance, @@ -1729,8 +1700,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P\d+) \s+parameters \s+graceful-restart\s+stalepath-time - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp {{ as_number }} parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}", @@ -1818,8 +1788,7 @@ class Bgp_globalTemplate(NetworkTemplate): \s+(?P\d+) \s+parameters \s+router-id - \s+(?P\S+) - *$""", + \s+(?P\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 39855c1c..cbe21e3a 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 @@ -234,8 +234,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+advertisement-interval - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} advertisement-interval {{ neighbor.advertisement_interval }}", @@ -427,8 +426,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+capability \s+orf \s+prefix-list - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} capability orf prefix-list {{ neighbor.capability.orf }}", @@ -456,8 +454,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P
\S+) \s+default-originate \s+route-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} default-originate route-map {{ neighbor.default_originate }}", @@ -482,8 +479,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+description - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} description {{ neighbor.description }}", @@ -558,8 +554,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+disable-send-community - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} disable-send-community {{ neighbor.disable_send_community }}", @@ -616,8 +611,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+ebgp-multihop - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} ebgp-multihop {{ neighbor.ebgp_multihop }}", @@ -643,8 +637,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P
\S+) \s+filter-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_filter_list, @@ -701,8 +694,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+maximum-prefix - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} maximum-prefix {{ neighbor.maximum_prefix }}", @@ -802,8 +794,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+password - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} password {{ neighbor.password }}", @@ -828,8 +819,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+peer-group - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} peer-group {{ neighbor.peer_group_name }}", @@ -854,8 +844,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+port - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} port {{ neighbor.port }}", @@ -881,8 +870,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P
\S+) \s+prefix-list \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_prefix_list, @@ -912,8 +900,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+remote-as - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as }}", @@ -964,8 +951,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P
\S+) \s+route-map \s+(?Pexport|import) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_route_map, @@ -1121,8 +1107,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+unsuppress-map - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} unsuppress-map {{ neighbor.unsuppress_map }}", @@ -1147,8 +1132,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+update-source - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} update-source {{ neighbor.update_source }}", @@ -1173,8 +1157,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+weight - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} weight {{ neighbor.weight }}", @@ -1199,8 +1182,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+neighbor \s+(?P
\S+) \s+ttl-security - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp neighbor {{ neighbor.address }} ttl-security {{ neighbor.ttl_security }}", @@ -1226,8 +1208,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+(?P
\S+) \s+timers \s+(?Pconnect|holdtime|keepalive) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_neighbor_timers, @@ -1254,8 +1235,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+timers \s+(?P\S+) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_timers, @@ -1372,8 +1352,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+cluster-id - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters cluster-id {{ bgp_params.cluster_id }}", @@ -1395,8 +1374,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+confederation \s+(?Pidentifier|peers) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_confederation, @@ -1423,8 +1401,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+half-life - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening half-life {{ bgp_params.dampening.half_life}}", @@ -1448,8 +1425,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+max-suppress-time - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening max-suppress-time {{ bgp_params.dampening.max_suppress_time}}", @@ -1473,8 +1449,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+re-use - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening re-use {{ bgp_params.dampening.re_use}}", @@ -1498,8 +1473,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+dampening \s+start-suppress-time - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters dampening start-suppress-time {{ bgp_params.dampening.start_suppress_time}}", @@ -1522,9 +1496,8 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+default - \s*(?Pno-ipv4-unicast)* - \s*(?Plocal-pref\s\S+) - *$""", + \s*(?Pno-ipv4-unicast)? + \s*(?Plocal-pref\s\S+)?\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_default, @@ -1593,8 +1566,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+distance\sprefix \s+(?P\S+) \s+distance - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters distance prefix {{ bgp_params.distance.prefix }} distance {{ bgp_params.distance.value }}", @@ -1622,8 +1594,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+parameters \s+distance\sglobal \s+(?P\S+) - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_bgp_params_distance, @@ -1671,8 +1642,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+graceful-restart\s+stalepath-time - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "protocols bgp parameters graceful-restart stalepath-time {{ bgp_params.graceful_restart }}", @@ -1756,8 +1726,7 @@ class Bgp_globalTemplate14(NetworkTemplate): \s+bgp \s+parameters \s+router-id - \s+(?P\S+) - *$""", + \s+(?P\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 134effca..af04da3b 100644 --- a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py +++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces.py @@ -365,8 +365,7 @@ class Ospf_interfacesTemplate(NetworkTemplate): \s+(?Pospf|ospfv3) \s+authentication \s+plaintext-password - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_password, @@ -399,8 +398,7 @@ class Ospf_interfacesTemplate(NetworkTemplate): \s+key-id \s+(?P\d+) \s+md5-key - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_md5, @@ -572,8 +570,7 @@ class Ospf_interfacesTemplate(NetworkTemplate): \s+(?Pip|ipv6) \s+(?Pospf|ospfv3) \s+network - \s+(?P\S+) - *$""", + \s+(?P\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 0d3aa5a7..484d7c44 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 @@ -240,8 +240,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+protocols \s+(?Pospf|ospfv3) \s+interface - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "remval": _tmplt_ospf_int_delete, @@ -266,8 +265,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+(?P\S+) \s+authentication \s+plaintext-password - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_password, @@ -298,8 +296,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+key-id \s+(?P\d+) \s+md5-key - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": _tmplt_ospf_int_auth_md5, @@ -459,8 +456,7 @@ class Ospf_interfacesTemplate14(NetworkTemplate): \s+interface \s+(?P\S+) \s+network - \s+(?P\S+) - *$""", + \s+(?P\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 c6b88f7b..51d0ea1c 100644 --- a/plugins/module_utils/network/vyos/rm_templates/route_maps.py +++ b/plugins/module_utils/network/vyos/rm_templates/route_maps.py @@ -33,8 +33,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "route_map", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "route_map", @@ -51,8 +50,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "sequence", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\s*$""", re.VERBOSE, ), "compval": "sequence", @@ -75,8 +73,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "call", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scall\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scall\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}", @@ -99,8 +96,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "description", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sdescription\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sdescription\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}", @@ -123,8 +119,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "action", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\saction\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\saction\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}", @@ -147,8 +142,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "continue_sequence", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scontinue\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scontinue\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}", @@ -171,8 +165,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "on_match_next", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\s(?Pnext) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\s(?Pnext)\s*$""", re.VERBOSE, ), "compval": "on_match.next", @@ -198,8 +191,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "on_match_goto", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\sgoto\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\sgoto\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "on_match.goto", @@ -225,8 +217,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_aggregator_ip", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sip\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sip\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.ip", @@ -254,8 +245,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_aggregator_as", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sas\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sas\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.as", @@ -283,8 +273,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_as_path_exclude", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path-exclude\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path-exclude\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.as_path_exclude", @@ -337,8 +326,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_atomic_aggregate", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\s(?Patomic-aggregate) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\s(?Patomic-aggregate)\s*$""", re.VERBOSE, ), "compval": "set.atomic_aggregate", @@ -364,8 +352,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_bgp_extcommunity_rt", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sbgp-extcommunity-rt\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sbgp-extcommunity-rt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.bgp_extcommunity_rt", @@ -392,8 +379,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_comm_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", @@ -420,8 +406,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_comm_list_delete", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\sdelete(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\s(?Pdelete)\s*$""", re.VERBOSE, ), "compval": "set.comm_list.comm_list", @@ -448,8 +433,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_extcommunity_rt", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-rt\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-rt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_rt", @@ -476,8 +460,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_extcommunity_soo", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-soo\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity-soo\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_soo", @@ -504,8 +487,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_extcommunity_bandwidth", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\sbandwidth\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\sbandwidth\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_bandwidth", @@ -532,8 +514,8 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_extcommunity_bandwidth_non_transitive", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\s(?Pbandwidth-non-transitive) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+) + \sset\sextcommunity\s(?Pbandwidth-non-transitive)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_bandwidth_non_transitive", @@ -560,8 +542,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_ip_next_hop", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sip-next-hop\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sip-next-hop\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ip_next_hop", @@ -590,8 +571,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sipv6-next-hop \s(?Pglobal|local) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ipv6_next_hop", @@ -621,8 +601,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_large_community", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slarge-community\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slarge-community\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.large_community", @@ -649,8 +628,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_local_preference", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slocal-preference\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slocal-preference\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.local_preference", @@ -677,8 +655,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_metric", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric", @@ -705,8 +682,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_metric_type", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric-type\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric-type\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric_type", @@ -733,8 +709,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_origin", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sorigin\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.origin", @@ -761,8 +736,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_originator_id", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\soriginator-id\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\soriginator-id\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.originator_id", @@ -789,8 +763,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_src", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\ssrc\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\ssrc\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.src", @@ -817,8 +790,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_tag", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stag\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stag\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.tag", @@ -845,8 +817,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_weight", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sweight\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sweight\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", @@ -873,8 +844,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_table", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stable\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stable\s(?P
\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", @@ -901,8 +871,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "set_community", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scommunity\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scommunity\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.community.value", @@ -931,8 +900,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_as_path", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sas-path\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sas-path\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.as_path", @@ -959,8 +927,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_community_community_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", @@ -987,8 +954,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_community_exact_match", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\sexact-match(?P) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\sexact-match(?P)\s*$""", re.VERBOSE, ), "compval": "match.community.exact_match", @@ -1015,8 +981,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_extcommunity", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sextcommunity\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sextcommunity\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.extcommunity", @@ -1043,8 +1008,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_interface", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sinterface\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sinterface\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.interface", @@ -1071,8 +1035,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_large_community_large_community_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\slarge-community\slarge-community-list\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\slarge-community\slarge-community-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.large_community_large_community_list", @@ -1099,8 +1062,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_metric", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\smetric\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.metric", @@ -1127,8 +1089,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_origin", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sorigin\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.origin", @@ -1155,8 +1116,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_peer", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\speer\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\speer\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.peer", @@ -1185,8 +1145,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\saddress \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.address", @@ -1219,8 +1178,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\snexthop \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.next_hop", @@ -1253,8 +1211,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\sroute-source \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.route_source", @@ -1287,8 +1244,7 @@ class Route_mapsTemplate(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\saddress \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.address", @@ -1320,8 +1276,7 @@ class Route_mapsTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\snexthop - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.next_hop", @@ -1349,8 +1304,7 @@ class Route_mapsTemplate(NetworkTemplate): "name": "match_protocol", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sprotocol\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sprotocol\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.protocol", @@ -1377,8 +1331,7 @@ class Route_mapsTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\srpki - \s(?P\S+) - *$""", + \s(?P\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 6564280d..ea61a555 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 @@ -33,8 +33,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "route_map", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "route_map", @@ -75,8 +74,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "call", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scall\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scall\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} call {{call}}", @@ -99,8 +97,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "description", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sdescription\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sdescription\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} description {{description}}", @@ -123,8 +120,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "action", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\saction\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\saction\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} action {{action}}", @@ -147,8 +143,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "continue_sequence", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scontinue\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\scontinue\s(?P\S+)\s*$""", re.VERBOSE, ), "setval": "policy route-map {{route_map}} rule {{sequence}} continue {{continue_sequence}}", @@ -198,8 +193,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "on_match_goto", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\sgoto\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\son-match\sgoto\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "on_match.goto", @@ -225,8 +219,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_aggregator_ip", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sip\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sip\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.ip", @@ -254,8 +247,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_aggregator_as", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sas\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\saggregator\sas\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.aggregator.as", @@ -283,8 +275,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_as_path_exclude", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path\sexclude\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sas-path\sexclude\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.as_path_exclude", @@ -364,8 +355,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_bgp_extcommunity_rt", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sbgp-extcommunity-rt\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sbgp-extcommunity-rt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.bgp_extcommunity_rt", @@ -392,8 +382,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_comm_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", @@ -420,8 +409,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_comm_list_delete", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\sdelete(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\scomm-list\s(?Pdelete)\s*$""", re.VERBOSE, ), "compval": "set.comm_list.comm_list", @@ -448,8 +436,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_extcommunity_rt", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\srt\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\srt\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_rt", @@ -476,8 +463,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_extcommunity_soo", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\ssoo\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\ssoo\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_soo", @@ -504,8 +490,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_extcommunity_bandwidth", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\sbandwidth\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sextcommunity\sbandwidth\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.extcommunity_bandwidth", @@ -560,8 +545,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_ip_next_hop", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sip-next-hop\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sip-next-hop\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ip_next_hop", @@ -590,8 +574,7 @@ class Route_mapsTemplate14(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sipv6-next-hop \s(?Pglobal|local) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.ipv6_next_hop", @@ -649,8 +632,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_local_preference", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slocal-preference\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\slocal-preference\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.local_preference", @@ -677,8 +659,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_metric", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric", @@ -705,8 +686,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_metric_type", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric-type\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\smetric-type\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.metric_type", @@ -733,8 +713,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_origin", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sorigin\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.origin", @@ -761,8 +740,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_originator_id", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\soriginator-id\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\soriginator-id\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.originator_id", @@ -789,8 +767,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_src", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\ssrc\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\ssrc\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.src", @@ -817,8 +794,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_tag", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stag\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stag\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.tag", @@ -845,8 +821,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_weight", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sweight\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\sweight\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", @@ -873,8 +848,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "set_table", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stable\s(?P
\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\sset\stable\s(?P
\S+)\s*$""", re.VERBOSE, ), "compval": "set.weight", @@ -931,8 +905,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_as_path", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sas-path\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sas-path\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.as_path", @@ -959,8 +932,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_community_community_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\scommunity\scommunity-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.community.community_list", @@ -1015,8 +987,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_extcommunity", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sextcommunity\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sextcommunity\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.extcommunity", @@ -1043,8 +1014,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_interface", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sinterface\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sinterface\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.interface", @@ -1071,8 +1041,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_large_community_large_community_list", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\slarge-community\slarge-community-list\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\slarge-community\slarge-community-list\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.large_community_large_community_list", @@ -1099,8 +1068,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_metric", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\smetric\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\smetric\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.metric", @@ -1127,8 +1095,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_origin", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sorigin\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sorigin\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.origin", @@ -1155,8 +1122,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_peer", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\speer\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\speer\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.peer", @@ -1185,8 +1151,7 @@ class Route_mapsTemplate14(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\saddress \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.address", @@ -1219,8 +1184,7 @@ class Route_mapsTemplate14(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\snexthop \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.next_hop", @@ -1253,8 +1217,7 @@ class Route_mapsTemplate14(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sip\sroute-source \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ip.route_source", @@ -1287,8 +1250,7 @@ class Route_mapsTemplate14(NetworkTemplate): r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\saddress \s(?Paccess-list|prefix-list) - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.address", @@ -1320,8 +1282,7 @@ class Route_mapsTemplate14(NetworkTemplate): "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sipv6\snexthop - \s(?P\S+) - *$""", + \s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.ipv6.next_hop", @@ -1349,8 +1310,7 @@ class Route_mapsTemplate14(NetworkTemplate): "name": "match_protocol", "getval": re.compile( r""" - ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sprotocol\s(?P\S+) - *$""", + ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\sprotocol\s(?P\S+)\s*$""", re.VERBOSE, ), "compval": "match.protocol", @@ -1377,8 +1337,7 @@ class Route_mapsTemplate14(NetworkTemplate): "getval": re.compile( r""" ^set\spolicy\sroute-map\s(?P\S+)\srule\s(?P\d+)\smatch\srpki - \s(?P\S+) - *$""", + \s(?P\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 71753083..ff1c6465 100644 --- a/plugins/module_utils/network/vyos/rm_templates/snmp_server.py +++ b/plugins/module_utils/network/vyos/rm_templates/snmp_server.py @@ -140,9 +140,9 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\scommunity \s+(?P\S+) - \s*(?Pauthorization\srw|authorization\sro)* - \s*(client\s(?P\S+))* - \s*(network\s(?P\S+))* + \s*(?Pauthorization\srw|authorization\sro)? + \s*(client\s(?P\S+))? + \s*(network\s(?P\S+))? $""", re.VERBOSE, ), @@ -164,8 +164,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\scontact - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp contact {{ contact }}", @@ -179,8 +178,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\sdescription - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp description {{ description }}", @@ -195,8 +193,8 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\slisten-address \s+(?P\S+) - \s*(port)* - \s*(?P\d+)* + \s*(port)? + \s*(?P\d+)? $""", re.VERBOSE, ), @@ -232,8 +230,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\ssmux-peer - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp smux-peer {{ smux_peer }}", @@ -247,8 +244,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\strap-source - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp trap-source {{ trap_source }}", @@ -263,9 +259,8 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\strap-target \s+(?P\S+) - \s*(?Pcommunity\s\S+)* - \s*(?Pport\s\d+)* - $""", + \s*(?Pcommunity\s\S+)? + \s*(?Pport\s\d+)? $""", re.VERBOSE, ), "setval": _tmplt_snmp_server_trap_target, @@ -283,8 +278,7 @@ class Snmp_serverTemplate(NetworkTemplate): "getval": re.compile( r""" ^set\sservice\ssnmp\sv3\sengineid - \s+(?P\S+) - *$""", + \s+(?P\S+)\s*$""", re.VERBOSE, ), "setval": "service snmp v3 engineid {{ snmp_v3.engine_id }}", @@ -301,9 +295,9 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\sgroup \s+(?P\S+) - \s*(?Pmode\s\S+)* - \s*(?Pseclevel\s\S+)* - \s*(?Pview\s\S+)* + \s*(?Pmode\s\S+)? + \s*(?Pseclevel\s\S+)? + \s*(?Pview\s\S+)? $""", re.VERBOSE, ), @@ -329,9 +323,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+auth - \s*(?Pencrypted-password\s\S+)* - \s*(?Pplaintext-password\s\S+)* - \s*(?Ptype\s\S+)* + \s*(?Pencrypted-password\s\S+)? + \s*(?Pplaintext-password\s\S+)? + \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), @@ -358,8 +352,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) - \s+(?Pport\s\d+)* - $""", + \s+(?Pport\s\d+)? $""", re.VERBOSE, ), "setval": "service snmp v3 trap-target port {{ snmp_v3.trap_targets.port }}", @@ -381,7 +374,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) - \s+(?Pprotocol\s\S+)* + \s+(?Pprotocol\s\S+)? $""", re.VERBOSE, ), @@ -404,7 +397,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) - \s+(?Ptype\s\S+)* + \s+(?Ptype\s\S+)? $""", re.VERBOSE, ), @@ -427,7 +420,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) - \s+(?Puser\s\S+)* + \s+(?Puser\s\S+)? $""", re.VERBOSE, ), @@ -451,9 +444,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\strap-target \s+(?P\S+) \s+privacy - \s*(?Pencrypted-password\s\S+)* - \s*(?Pplaintext-password\s\S+)* - \s*(?Ptype\s\S+)* + \s*(?Pencrypted-password\s\S+)? + \s*(?Pplaintext-password\s\S+)? + \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), @@ -481,9 +474,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+auth - \s*(?Pencrypted-password\s\S+)* - \s*(?Pplaintext-password\s\S+)* - \s*(?Ptype\s\S+)* + \s*(?Pencrypted-password\s\S+)? + \s*(?Pplaintext-password\s\S+)? + \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), @@ -511,9 +504,9 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) \s+privacy - \s*(?Pencrypted-password\s\S+)* - \s*(?Pplaintext-password\s\S+)* - \s*(?Ptype\s\S+)* + \s*(?Pencrypted-password\s\S+)? + \s*(?Pplaintext-password\s\S+)? + \s*(?Ptype\s\S+)? $""", re.VERBOSE, ), @@ -540,8 +533,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) - \s+(?Pgroup\s.+)* - $""", + \s+(?Pgroup\s.+)? $""", re.VERBOSE, ), "setval": "service snmp v3 user {{ snmp_v3.users.user }} group {{ snmp_v3.users.group }}", @@ -563,7 +555,7 @@ class Snmp_serverTemplate(NetworkTemplate): r""" ^set\sservice\ssnmp\sv3\suser \s+(?P\S+) - \s+(?Pmode\s\S+)* + \s+(?Pmode\s\S+)? $""", re.VERBOSE, ), @@ -587,8 +579,8 @@ class Snmp_serverTemplate(NetworkTemplate): ^set\sservice\ssnmp\sv3\sview \s+(?P\S+) \s+(?Poid\s\S+) - \s*(?Pexclude\s\S+)* - \s*(?Pmask\s\S+)* + \s*(?Pexclude\s\S+)? + \s*(?Pmask\s\S+)? $""", re.VERBOSE, ), 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\\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." + ) -- cgit v1.2.3 From 6d7a2184e970624f74f333931f7f70a1f900f7b5 Mon Sep 17 00:00:00 2001 From: Stavros Kroustouris Date: Thu, 25 Jun 2026 22:30:57 +0300 Subject: T2295: vyos_user: quote plaintext-password in generated set commands (#480) * vyos_user: quote plaintext-password in generated set commands VyOS requires quoted values for passwords with special characters. Align with encrypted-password and integration test conventions. Co-authored-by: Cursor * T2295: escape plaintext-password values in vyos_user set commands Quote passwords for VyOS special-character handling and escape embedded single quotes. Update RETURN sample, add unit tests, refresh changelog. Co-authored-by: Cursor * T2295: shorten changelog fragment for ansible-lint line-length Co-authored-by: Cursor * T2295: use shlex.quote() for plaintext-password values Replace custom _quote_config_value() with stdlib shlex.quote() per review feedback; update unit tests and RETURN sample accordingly. Co-authored-by: Cursor * T2295: add complex plaintext-password quoting unit test Cover spaces, shell metacharacters, embedded quotes, and backslashes in one password; assert command output matches shlex.quote(). Co-authored-by: Cursor * T2295: assert explicit quoting in complex password unit test Replace shlex.quote()-derived expectation with a fixed command string, matching the other password quoting tests. Co-authored-by: Cursor * Fix RETURN doc sample, add docstrings and workflow permissions - RETURN sample was missing the username token in the example command - Add docstrings to all undocumented module-level functions to bring docstring coverage above the 80% threshold - Add explicit permissions: contents: read to codecoverage.yml Co-Authored-By: Claude Sonnet 4.6 * Revert "Fix RETURN doc sample, add docstrings and workflow permissions" This reverts commit 827809ee49cf472a8bf30ec0b24d347a75fcb083. --------- Co-authored-by: Cursor Co-authored-by: omnom62 <75066712+omnom62@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- .../vyos-user-quote-plaintext-password.yml | 3 +++ plugins/modules/vyos_user.py | 4 ++- tests/unit/modules/network/vyos/test_vyos_user.py | 30 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/vyos-user-quote-plaintext-password.yml 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/plugins/modules/vyos_user.py b/plugins/modules/vyos_user.py index d2f23509..e47f5a05 100644 --- a/plugins/modules/vyos_user.py +++ b/plugins/modules/vyos_user.py @@ -205,6 +205,7 @@ commands: """ import re +import shlex from copy import deepcopy from functools import partial @@ -276,7 +277,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/tests/unit/modules/network/vyos/test_vyos_user.py b/tests/unit/modules/network/vyos/test_vyos_user.py index d1e7f162..2cbd3820 100644 --- a/tests/unit/modules/network/vyos/test_vyos_user.py +++ b/tests/unit/modules/network/vyos/test_vyos_user.py @@ -62,6 +62,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) -- cgit v1.2.3