diff options
author | Christian Poessinger <christian@poessinger.com> | 2022-10-06 20:47:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-06 20:47:11 +0200 |
commit | 975eaa55f85a2855f5551447c9f120cca8815351 (patch) | |
tree | 14b27a3393002820ef0da4a2c81462c0f929bdf1 | |
parent | 50f26c54d095420907a1d31168c162ad3c27ee36 (diff) | |
parent | 507f6ac423403b57f375309b483c6ccc1c83ad06 (diff) | |
download | vyos-1x-975eaa55f85a2855f5551447c9f120cca8815351.tar.gz vyos-1x-975eaa55f85a2855f5551447c9f120cca8815351.zip |
Merge pull request #1567 from aapostoliuk/T4660-sagitta
policy: T4660: Changed CLI syntax in route-map set community
-rw-r--r-- | data/templates/frr/policy.frr.j2 | 37 | ||||
-rw-r--r-- | interface-definitions/include/policy/community-clear.xml.i | 8 | ||||
-rw-r--r-- | interface-definitions/include/policy/community-value-list.xml.i | 90 | ||||
-rw-r--r-- | interface-definitions/include/policy/extended-community-value-list.xml.i | 15 | ||||
-rw-r--r-- | interface-definitions/include/policy/large-community-value-list.xml.i | 10 | ||||
-rw-r--r-- | interface-definitions/include/version/policy-version.xml.i | 2 | ||||
-rw-r--r-- | interface-definitions/policy.xml.in | 224 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_policy.py | 198 | ||||
-rwxr-xr-x | src/conf_mode/policy.py | 132 | ||||
-rwxr-xr-x | src/migration-scripts/policy/3-to-4 | 162 | ||||
-rwxr-xr-x | src/validators/bgp-extended-community | 55 | ||||
-rwxr-xr-x | src/validators/bgp-large-community | 53 | ||||
-rwxr-xr-x | src/validators/bgp-regular-community | 50 |
13 files changed, 851 insertions, 185 deletions
diff --git a/data/templates/frr/policy.frr.j2 b/data/templates/frr/policy.frr.j2 index 33df17770..5ad4bd28c 100644 --- a/data/templates/frr/policy.frr.j2 +++ b/data/templates/frr/policy.frr.j2 @@ -274,11 +274,17 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }} {% if rule_config.set.atomic_aggregate is vyos_defined %} set atomic-aggregate {% endif %} -{% if rule_config.set.comm_list.comm_list is vyos_defined %} - set comm-list {{ rule_config.set.comm_list.comm_list }} {{ 'delete' if rule_config.set.comm_list.delete is vyos_defined }} +{% if rule_config.set.community.delete is vyos_defined %} + set comm-list {{ rule_config.set.community.delete }} delete {% endif %} -{% if rule_config.set.community is vyos_defined %} - set community {{ rule_config.set.community }} +{% if rule_config.set.community.replace is vyos_defined %} + set community {{ rule_config.set.community.replace | join(' ') | replace("local-as" , "local-AS") }} +{% endif %} +{% if rule_config.set.community.add is vyos_defined %} + set community {{ rule_config.set.community.add | join(' ') | replace("local-as" , "local-AS") }} additive +{% endif %} +{% if rule_config.set.community.none is vyos_defined %} + set community none {% endif %} {% if rule_config.set.distance is vyos_defined %} set distance {{ rule_config.set.distance }} @@ -290,13 +296,16 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }} set evpn gateway-ip ipv6 {{ rule_config.set.evpn.gateway.ipv6 }} {% endif %} {% if rule_config.set.extcommunity.bandwidth is vyos_defined %} - set extcommunity bandwidth {{ rule_config.set.extcommunity.bandwidth }} + set extcommunity bandwidth {{ rule_config.set.extcommunity.bandwidth }} {{ 'non-transitive' if rule_config.set.extcommunity.bandwidth_non_transitive is vyos_defined }} {% endif %} {% if rule_config.set.extcommunity.rt is vyos_defined %} - set extcommunity rt {{ rule_config.set.extcommunity.rt }} + set extcommunity rt {{ rule_config.set.extcommunity.rt | join(' ') }} {% endif %} {% if rule_config.set.extcommunity.soo is vyos_defined %} - set extcommunity soo {{ rule_config.set.extcommunity.soo }} + set extcommunity soo {{ rule_config.set.extcommunity.soo | join(' ') }} +{% endif %} +{% if rule_config.set.extcommunity.none is vyos_defined %} + set extcommunity none {% endif %} {% if rule_config.set.ip_next_hop is vyos_defined %} set ip next-hop {{ rule_config.set.ip_next_hop }} @@ -313,11 +322,17 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }} {% if rule_config.set.ipv6_next_hop.prefer_global is vyos_defined %} set ipv6 next-hop prefer-global {% endif %} -{% if rule_config.set.large_community is vyos_defined %} - set large-community {{ rule_config.set.large_community }} +{% if rule_config.set.large_community.replace is vyos_defined %} + set large-community {{ rule_config.set.large_community.replace | join(' ') }} +{% endif %} +{% if rule_config.set.large_community.add is vyos_defined %} + set large-community {{ rule_config.set.large_community.add | join(' ') }} additive +{% endif %} +{% if rule_config.set.large_community.none is vyos_defined %} + set large-community none {% endif %} -{% if rule_config.set.large_comm_list_delete is vyos_defined %} - set large-comm-list {{ rule_config.set.large_comm_list_delete }} delete +{% if rule_config.set.large_community.delete is vyos_defined %} + set large-comm-list {{ rule_config.set.large_community.delete }} delete {% endif %} {% if rule_config.set.local_preference is vyos_defined %} set local-preference {{ rule_config.set.local_preference }} diff --git a/interface-definitions/include/policy/community-clear.xml.i b/interface-definitions/include/policy/community-clear.xml.i new file mode 100644 index 000000000..0fd57cdf0 --- /dev/null +++ b/interface-definitions/include/policy/community-clear.xml.i @@ -0,0 +1,8 @@ +<!-- include start from policy/community-clear.xml.i --> +<leafNode name="none"> + <properties> + <help>Completely remove communities attribute from a prefix</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/policy/community-value-list.xml.i b/interface-definitions/include/policy/community-value-list.xml.i new file mode 100644 index 000000000..8c665c5f0 --- /dev/null +++ b/interface-definitions/include/policy/community-value-list.xml.i @@ -0,0 +1,90 @@ +<!-- include start from policy/community-value-list.xml.i --> +<completionHelp> + <list> + local-as + no-advertise + no-export + internet + graceful-shutdown + accept-own + route-filter-translated-v4 + route-filter-v4 + route-filter-translated-v6 + route-filter-v6 + llgr-stale + no-llgr + accept-own-nexthop + blackhole + no-peer + </list> +</completionHelp> +<valueHelp> + <format><AS:VAL></format> + <description>Community number in <0-65535:0-65535> format</description> +</valueHelp> +<valueHelp> + <format>local-as</format> + <description>Well-known communities value NO_EXPORT_SUBCONFED 0xFFFFFF03</description> +</valueHelp> +<valueHelp> + <format>no-advertise</format> + <description>Well-known communities value NO_ADVERTISE 0xFFFFFF02</description> +</valueHelp> +<valueHelp> + <format>no-export</format> + <description>Well-known communities value NO_EXPORT 0xFFFFFF01</description> +</valueHelp> +<valueHelp> + <format>internet</format> + <description>Well-known communities value 0</description> +</valueHelp> +<valueHelp> + <format>graceful-shutdown</format> + <description>Well-known communities value GRACEFUL_SHUTDOWN 0xFFFF0000</description> +</valueHelp> +<valueHelp> + <format>accept-own</format> + <description>Well-known communities value ACCEPT_OWN 0xFFFF0001</description> +</valueHelp> +<valueHelp> + <format>route-filter-translated-v4</format> + <description>Well-known communities value ROUTE_FILTER_TRANSLATED_v4 0xFFFF0002</description> +</valueHelp> +<valueHelp> + <format>route-filter-v4</format> + <description>Well-known communities value ROUTE_FILTER_v4 0xFFFF0003</description> +</valueHelp> +<valueHelp> + <format>route-filter-translated-v6</format> + <description>Well-known communities value ROUTE_FILTER_TRANSLATED_v6 0xFFFF0004</description> +</valueHelp> +<valueHelp> + <format>route-filter-v6</format> + <description>Well-known communities value ROUTE_FILTER_v6 0xFFFF0005</description> +</valueHelp> +<valueHelp> + <format>llgr-stale</format> + <description>Well-known communities value LLGR_STALE 0xFFFF0006</description> +</valueHelp> +<valueHelp> + <format>no-llgr</format> + <description>Well-known communities value NO_LLGR 0xFFFF0007</description> +</valueHelp> +<valueHelp> + <format>accept-own-nexthop</format> + <description>Well-known communities value accept-own-nexthop 0xFFFF0008</description> +</valueHelp> +<valueHelp> + <format>blackhole</format> + <description>Well-known communities value BLACKHOLE 0xFFFF029A</description> +</valueHelp> +<valueHelp> + <format>no-peer</format> + <description>Well-known communities value NOPEER 0xFFFFFF04</description> +</valueHelp> +<multi/> +<constraint> + <regex>local-as|no-advertise|no-export|internet|graceful-shutdown|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|accept-own-nexthop|blackhole|no-peer</regex> + <validator name="bgp-regular-community"/> +</constraint> + <!-- include end --> diff --git a/interface-definitions/include/policy/extended-community-value-list.xml.i b/interface-definitions/include/policy/extended-community-value-list.xml.i new file mode 100644 index 000000000..c79f78c67 --- /dev/null +++ b/interface-definitions/include/policy/extended-community-value-list.xml.i @@ -0,0 +1,15 @@ +<!-- include start from policy/community-value-list.xml.i --> +<valueHelp> + <format>ASN:NN</format> + <description>based on autonomous system number in format <0-65535:0-4294967295></description> +</valueHelp> +<valueHelp> + <format>IP:NN</format> + <description>Based on a router-id IP address in format <IP:0-65535></description> +</valueHelp> +<constraint> + <validator name="bgp-extended-community"/> +</constraint> +<constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage> +<multi/> + <!-- include end --> diff --git a/interface-definitions/include/policy/large-community-value-list.xml.i b/interface-definitions/include/policy/large-community-value-list.xml.i new file mode 100644 index 000000000..33b1f13a2 --- /dev/null +++ b/interface-definitions/include/policy/large-community-value-list.xml.i @@ -0,0 +1,10 @@ +<!-- include start from policy/community-value-list.xml.i --> +<valueHelp> + <description>Community in format <0-4294967295:0-4294967295:0-4294967295></description> + <format><GA:LDP1:LDP2></format> +</valueHelp> +<multi/> +<constraint> + <validator name="bgp-large-community"/> +</constraint> + <!-- include end --> diff --git a/interface-definitions/include/version/policy-version.xml.i b/interface-definitions/include/version/policy-version.xml.i index 426173a19..89bde20c7 100644 --- a/interface-definitions/include/version/policy-version.xml.i +++ b/interface-definitions/include/version/policy-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/policy-version.xml.i --> -<syntaxVersion component='policy' version='3'></syntaxVersion> +<syntaxVersion component='policy' version='4'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index e794c4b90..6c60276d5 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -1118,67 +1118,120 @@ <valueless/> </properties> </leafNode> - <node name="comm-list"> + <node name="community"> <properties> - <help>BGP communities matching a community-list</help> + <help>BGP community attribute</help> </properties> <children> - <leafNode name="comm-list"> + <leafNode name="add"> + <properties> + <help>Add communities to a prefix</help> + #include <include/policy/community-value-list.xml.i> + </properties> + </leafNode> + <leafNode name="replace"> + <properties> + <help>Set communities for a prefix</help> + #include <include/policy/community-value-list.xml.i> + </properties> + </leafNode> + #include <include/policy/community-clear.xml.i> + <leafNode name="delete"> <properties> - <help>BGP communities with a community-list</help> + <help>Remove communities defined in a list from a prefix</help> <completionHelp> <path>policy community-list</path> </completionHelp> <valueHelp> + <description>Community-list</description> <format>txt</format> - <description>BGP communities with a community-list</description> </valueHelp> </properties> </leafNode> + </children> + </node> + <node name="large-community"> + <properties> + <help>BGP large community attribute</help> + </properties> + <children> + <leafNode name="add"> + <properties> + <help>Add large communities to a prefix ;</help> + #include <include/policy/large-community-value-list.xml.i> + </properties> + </leafNode> + <leafNode name="replace"> + <properties> + <help>Set large communities for a prefix</help> + #include <include/policy/large-community-value-list.xml.i> + </properties> + </leafNode> + #include <include/policy/community-clear.xml.i> <leafNode name="delete"> <properties> - <help>Delete BGP communities matching the community-list</help> - <valueless/> + <help>Remove communities defined in a list from a prefix</help> + <completionHelp> + <path>policy large-community-list</path> + </completionHelp> + <valueHelp> + <description>Community-list</description> + <format>txt</format> + </valueHelp> </properties> </leafNode> </children> </node> - <leafNode name="community"> + <node name="extcommunity"> <properties> - <help>Border Gateway Protocl (BGP) community attribute</help> - <completionHelp> - <list>local-AS no-advertise no-export internet additive none</list> - </completionHelp> - <valueHelp> - <format><aa:nn></format> - <description>Community number in AA:NN format</description> - </valueHelp> - <valueHelp> - <format>local-AS</format> - <description>Well-known communities value NO_EXPORT_SUBCONFED 0xFFFFFF03</description> - </valueHelp> - <valueHelp> - <format>no-advertise</format> - <description>Well-known communities value NO_ADVERTISE 0xFFFFFF02</description> - </valueHelp> - <valueHelp> - <format>no-export</format> - <description>Well-known communities value NO_EXPORT 0xFFFFFF01</description> - </valueHelp> - <valueHelp> - <format>internet</format> - <description>Well-known communities value 0</description> - </valueHelp> - <valueHelp> - <format>additive</format> - <description>New value is appended to the existing value</description> - </valueHelp> - <valueHelp> - <format>none</format> - <description>No community attribute</description> - </valueHelp> + <help>BGP extended community attribute</help> </properties> - </leafNode> + <children> + <leafNode name="bandwidth"> + <properties> + <help>Bandwidth value in Mbps</help> + <completionHelp> + <list>cumulative num-multipaths</list> + </completionHelp> + <valueHelp> + <format>u32:1-25600</format> + <description>Bandwidth value in Mbps</description> + </valueHelp> + <valueHelp> + <format>cumulative</format> + <description>Cumulative bandwidth of all multipaths (outbound-only)</description> + </valueHelp> + <valueHelp> + <format>num-multipaths</format> + <description>Internally computed bandwidth based on number of multipaths (outbound-only)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-25600"/> + <regex>(cumulative|num-multipaths)</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="bandwidth-non-transitive"> + <properties> + <help>The link bandwidth extended community is encoded as non-transitive</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="rt"> + <properties> + <help>Set route target value</help> + #include <include/policy/extended-community-value-list.xml.i> + </properties> + </leafNode> + <leafNode name="soo"> + <properties> + <help>Set Site of Origin value</help> + #include <include/policy/extended-community-value-list.xml.i> + </properties> + </leafNode> + #include <include/policy/community-clear.xml.i> + </children> + </node> <leafNode name="distance"> <properties> <help>Locally significant administrative distance</help> @@ -1229,71 +1282,6 @@ </node> </children> </node> - <node name="extcommunity"> - <properties> - <help>BGP extended community attribute</help> - </properties> - <children> - <leafNode name="bandwidth"> - <properties> - <help>Bandwidth value in Mbps</help> - <completionHelp> - <list>cumulative num-multipaths</list> - </completionHelp> - <valueHelp> - <format>u32:1-25600</format> - <description>Bandwidth value in Mbps</description> - </valueHelp> - <valueHelp> - <format>cumulative</format> - <description>Cumulative bandwidth of all multipaths (outbound-only)</description> - </valueHelp> - <valueHelp> - <format>num-multipaths</format> - <description>Internally computed bandwidth based on number of multipaths (outbound-only)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-25600"/> - <regex>(cumulative|num-multipaths)</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="rt"> - <properties> - <help>Set route target value</help> - <valueHelp> - <format>ASN:NN</format> - <description>based on autonomous system number</description> - </valueHelp> - <valueHelp> - <format>IP:NN</format> - <description>Based on a router-id IP address</description> - </valueHelp> - <constraint> - <regex>(((\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b)|(\d+)):(\d+) ?)+</regex> - </constraint> - <constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="soo"> - <properties> - <help>Set Site of Origin value</help> - <valueHelp> - <format>ASN:NN</format> - <description>based on autonomous system number</description> - </valueHelp> - <valueHelp> - <format>IP:NN</format> - <description>Based on a router-id IP address</description> - </valueHelp> - <constraint> - <regex>((?:[0-9]{1,3}\.){3}[0-9]{1,3}|\d+):\d+</regex> - </constraint> - <constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage> - </properties> - </leafNode> - </children> - </node> <leafNode name="ip-next-hop"> <properties> <help>Nexthop IP address</help> @@ -1368,30 +1356,6 @@ </leafNode> </children> </node> - <leafNode name="large-community"> - <properties> - <help>Set BGP large community value</help> - <valueHelp> - <format>txt</format> - <description>ASN:nn:mm BGP large community</description> - </valueHelp> - <completionHelp> - <path>policy large-community-list</path> - </completionHelp> - </properties> - </leafNode> - <leafNode name="large-comm-list-delete"> - <properties> - <help>Delete BGP communities matching the large community-list</help> - <completionHelp> - <path>policy large-community-list</path> - </completionHelp> - <valueHelp> - <format>txt</format> - <description>BGP large community-list</description> - </valueHelp> - </properties> - </leafNode> <leafNode name="local-preference"> <properties> <help>BGP local preference attribute</help> diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index 3d37d22ae..2166e63ec 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -698,6 +698,184 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): for rule in test_range: tmp = f'ip prefix-list {prefix_list} seq {rule} permit {prefix} le {rule}' self.assertIn(tmp, config) + def test_route_map_community_set(self): + test_data = { + "community-configuration": { + "rule": { + "10": { + "action": "permit", + "set": { + "community": { + "replace": [ + "65000:10", + "65001:11" + ] + }, + "extcommunity": { + "bandwidth": "200", + "rt": [ + "65000:10", + "192.168.0.1:11" + ], + "soo": [ + "192.168.0.1:11", + "65000:10" + ] + }, + "large-community": { + "replace": [ + "65000:65000:10", + "65000:65000:11" + ] + } + } + }, + "20": { + "action": "permit", + "set": { + "community": { + "add": [ + "65000:10", + "65001:11" + ] + }, + "extcommunity": { + "bandwidth": "200", + "bandwidth-non-transitive": {} + }, + "large-community": { + "add": [ + "65000:65000:10", + "65000:65000:11" + ] + } + } + }, + "30": { + "action": "permit", + "set": { + "community": { + "none": {} + }, + "extcommunity": { + "none": {} + }, + "large-community": { + "none": {} + } + } + } + } + } + } + for route_map, route_map_config in test_data.items(): + path = base_path + ['route-map', route_map] + self.cli_set(path + ['description', f'VyOS ROUTE-MAP {route_map}']) + if 'rule' not in route_map_config: + continue + + for rule, rule_config in route_map_config['rule'].items(): + if 'action' in rule_config: + self.cli_set(path + ['rule', rule, 'action', rule_config['action']]) + if 'set' in rule_config: + + #Add community in configuration + if 'community' in rule_config['set']: + if 'none' in rule_config['set']['community']: + self.cli_set(path + ['rule', rule, 'set', 'community', 'none']) + else: + community_path = path + ['rule', rule, 'set', 'community'] + if 'add' in rule_config['set']['community']: + for community_unit in rule_config['set']['community']['add']: + self.cli_set(community_path + ['add', community_unit]) + if 'replace' in rule_config['set']['community']: + for community_unit in rule_config['set']['community']['replace']: + self.cli_set(community_path + ['replace', community_unit]) + + #Add large-community in configuration + if 'large-community' in rule_config['set']: + if 'none' in rule_config['set']['large-community']: + self.cli_set(path + ['rule', rule, 'set', 'large-community', 'none']) + else: + community_path = path + ['rule', rule, 'set', 'large-community'] + if 'add' in rule_config['set']['large-community']: + for community_unit in rule_config['set']['large-community']['add']: + self.cli_set(community_path + ['add', community_unit]) + if 'replace' in rule_config['set']['large-community']: + for community_unit in rule_config['set']['large-community']['replace']: + self.cli_set(community_path + ['replace', community_unit]) + + #Add extcommunity in configuration + if 'extcommunity' in rule_config['set']: + if 'none' in rule_config['set']['extcommunity']: + self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'none']) + else: + if 'bandwidth' in rule_config['set']['extcommunity']: + self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'bandwidth', rule_config['set']['extcommunity']['bandwidth']]) + if 'bandwidth-non-transitive' in rule_config['set']['extcommunity']: + self.cli_set(path + ['rule', rule, 'set','extcommunity', 'bandwidth-non-transitive']) + if 'rt' in rule_config['set']['extcommunity']: + for community_unit in rule_config['set']['extcommunity']['rt']: + self.cli_set(path + ['rule', rule, 'set', 'extcommunity','rt',community_unit]) + if 'soo' in rule_config['set']['extcommunity']: + for community_unit in rule_config['set']['extcommunity']['soo']: + self.cli_set(path + ['rule', rule, 'set', 'extcommunity','soo',community_unit]) + self.cli_commit() + + for route_map, route_map_config in test_data.items(): + if 'rule' not in route_map_config: + continue + for rule, rule_config in route_map_config['rule'].items(): + name = f'route-map {route_map} {rule_config["action"]} {rule}' + config = self.getFRRconfig(name) + self.assertIn(name, config) + + if 'set' in rule_config: + #Check community + if 'community' in rule_config['set']: + if 'none' in rule_config['set']['community']: + tmp = f'set community none' + self.assertIn(tmp, config) + if 'replace' in rule_config['set']['community']: + values = ' '.join(rule_config['set']['community']['replace']) + tmp = f'set community {values}' + self.assertIn(tmp, config) + if 'add' in rule_config['set']['community']: + values = ' '.join(rule_config['set']['community']['add']) + tmp = f'set community {values} additive' + self.assertIn(tmp, config) + #Check large-community + if 'large-community' in rule_config['set']: + if 'none' in rule_config['set']['large-community']: + tmp = f'set large-community none' + self.assertIn(tmp, config) + if 'replace' in rule_config['set']['large-community']: + values = ' '.join(rule_config['set']['large-community']['replace']) + tmp = f'set large-community {values}' + self.assertIn(tmp, config) + if 'add' in rule_config['set']['large-community']: + values = ' '.join(rule_config['set']['large-community']['add']) + tmp = f'set large-community {values} additive' + self.assertIn(tmp, config) + #Check extcommunity + if 'extcommunity' in rule_config['set']: + if 'none' in rule_config['set']['extcommunity']: + tmp = 'set extcommunity none' + self.assertIn(tmp, config) + if 'bandwidth' in rule_config['set']['extcommunity']: + values = rule_config['set']['extcommunity']['bandwidth'] + tmp = f'set extcommunity bandwidth {values}' + if 'bandwidth-non-transitive' in rule_config['set']['extcommunity']: + tmp = tmp + ' non-transitive' + self.assertIn(tmp, config) + if 'rt' in rule_config['set']['extcommunity']: + values = ' '.join(rule_config['set']['extcommunity']['rt']) + tmp = f'set extcommunity rt {values}' + self.assertIn(tmp, config) + if 'soo' in rule_config['set']['extcommunity']: + values = ' '.join(rule_config['set']['extcommunity']['soo']) + tmp = f'set extcommunity soo {values}' + self.assertIn(tmp, config) def test_route_map(self): access_list = '50' @@ -845,13 +1023,9 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): 'as-path-prepend-last-as' : '5', 'atomic-aggregate' : '', 'distance' : '110', - 'extcommunity-bw' : '20000', - 'extcommunity-rt' : '123:456', - 'extcommunity-soo' : '456:789', 'ipv6-next-hop-global' : '2001::1', 'ipv6-next-hop-local' : 'fe80::1', 'ip-next-hop' : '192.168.1.1', - 'large-community' : '100:200:300', 'local-preference' : '500', 'metric' : '150', 'metric-type' : 'type-1', @@ -1049,20 +1223,12 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): self.cli_set(path + ['rule', rule, 'set', 'atomic-aggregate']) if 'distance' in rule_config['set']: self.cli_set(path + ['rule', rule, 'set', 'distance', rule_config['set']['distance']]) - if 'extcommunity-bw' in rule_config['set']: - self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'bandwidth', rule_config['set']['extcommunity-bw']]) - if 'extcommunity-rt' in rule_config['set']: - self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'rt', rule_config['set']['extcommunity-rt']]) - if 'extcommunity-soo' in rule_config['set']: - self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'soo', rule_config['set']['extcommunity-soo']]) if 'ipv6-next-hop-global' in rule_config['set']: self.cli_set(path + ['rule', rule, 'set', 'ipv6-next-hop', 'global', rule_config['set']['ipv6-next-hop-global']]) if 'ipv6-next-hop-local' in rule_config['set']: self.cli_set(path + ['rule', rule, 'set', 'ipv6-next-hop', 'local', rule_config['set']['ipv6-next-hop-local']]) if 'ip-next-hop' in rule_config['set']: self.cli_set(path + ['rule', rule, 'set', 'ip-next-hop', rule_config['set']['ip-next-hop']]) - if 'large-community' in rule_config['set']: - self.cli_set(path + ['rule', rule, 'set', 'large-community', rule_config['set']['large-community']]) if 'local-preference' in rule_config['set']: self.cli_set(path + ['rule', rule, 'set', 'local-preference', rule_config['set']['local-preference']]) if 'metric' in rule_config['set']: @@ -1236,20 +1402,12 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): tmp += 'atomic-aggregate' elif 'distance' in rule_config['set']: tmp += 'distance ' + rule_config['set']['distance'] - elif 'extcommunity-bw' in rule_config['set']: - tmp += 'extcommunity bandwidth' + rule_config['set']['extcommunity-bw'] - elif 'extcommunity-rt' in rule_config['set']: - tmp += 'extcommunity rt' + rule_config['set']['extcommunity-rt'] - elif 'extcommunity-soo' in rule_config['set']: - tmp += 'extcommunity rt' + rule_config['set']['extcommunity-soo'] elif 'ip-next-hop' in rule_config['set']: tmp += 'ip next-hop ' + rule_config['set']['ip-next-hop'] elif 'ipv6-next-hop-global' in rule_config['set']: tmp += 'ipv6 next-hop global ' + rule_config['set']['ipv6-next-hop-global'] elif 'ipv6-next-hop-local' in rule_config['set']: tmp += 'ipv6 next-hop local ' + rule_config['set']['ipv6-next-hop-local'] - elif 'large-community' in rule_config['set']: - tmp += 'large-community ' + rule_config['set']['large-community'] elif 'local-preference' in rule_config['set']: tmp += 'local-preference ' + rule_config['set']['local-preference'] elif 'metric' in rule_config['set']: diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py index 3008a20e0..a0d288e91 100755 --- a/src/conf_mode/policy.py +++ b/src/conf_mode/policy.py @@ -23,8 +23,42 @@ from vyos.util import dict_search from vyos import ConfigError from vyos import frr from vyos import airbag + airbag.enable() + +def community_action_compatibility(actions: dict) -> bool: + """ + Check compatibility of values in community and large community sections + :param actions: dictionary with community + :type actions: dict + :return: true if compatible, false if not + :rtype: bool + """ + if ('none' in actions) and ('replace' in actions or 'add' in actions): + return False + if 'replace' in actions and 'add' in actions: + return False + if ('delete' in actions) and ('none' in actions or 'replace' in actions): + return False + return True + + +def extcommunity_action_compatibility(actions: dict) -> bool: + """ + Check compatibility of values in extended community sections + :param actions: dictionary with community + :type actions: dict + :return: true if compatible, false if not + :rtype: bool + """ + if ('none' in actions) and ( + 'rt' in actions or 'soo' in actions or 'bandwidth' in actions or 'bandwidth_non_transitive' in actions): + return False + if ('bandwidth_non_transitive' in actions) and ('bandwidth' not in actions): + return False + return True + def routing_policy_find(key, dictionary): # Recursively traverse a dictionary and extract the value assigned to # a given key as generator object. This is made for routing policies, @@ -46,6 +80,7 @@ def routing_policy_find(key, dictionary): for result in routing_policy_find(key, d): yield result + def get_config(config=None): if config: conf = config @@ -53,7 +88,8 @@ def get_config(config=None): conf = Config() base = ['policy'] - policy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, + policy = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) # We also need some additional information from the config, prefix-lists @@ -67,12 +103,14 @@ def get_config(config=None): policy = dict_merge(tmp, policy) return policy + def verify(policy): if not policy: return None for policy_type in ['access_list', 'access_list6', 'as_path_list', - 'community_list', 'extcommunity_list', 'large_community_list', + 'community_list', 'extcommunity_list', + 'large_community_list', 'prefix_list', 'prefix_list6', 'route_map']: # Bail out early and continue with next policy type if policy_type not in policy: @@ -97,15 +135,18 @@ def verify(policy): if 'source' not in rule_config: raise ConfigError(f'A source {mandatory_error}') - if int(instance) in range(100, 200) or int(instance) in range(2000, 2700): + if int(instance) in range(100, 200) or int( + instance) in range(2000, 2700): if 'destination' not in rule_config: - raise ConfigError(f'A destination {mandatory_error}') + raise ConfigError( + f'A destination {mandatory_error}') if policy_type == 'access_list6': if 'source' not in rule_config: raise ConfigError(f'A source {mandatory_error}') - if policy_type in ['as_path_list', 'community_list', 'extcommunity_list', + if policy_type in ['as_path_list', 'community_list', + 'extcommunity_list', 'large_community_list']: if 'regex' not in rule_config: raise ConfigError(f'A regex {mandatory_error}') @@ -115,10 +156,10 @@ def verify(policy): raise ConfigError(f'A prefix {mandatory_error}') if rule_config in entries: - raise ConfigError(f'Rule "{rule}" contains a duplicate prefix definition!') + raise ConfigError( + f'Rule "{rule}" contains a duplicate prefix definition!') entries.append(rule_config) - # route-maps tend to be a bit more complex so they get their own verify() section if 'route_map' in policy: for route_map, route_map_config in policy['route_map'].items(): @@ -127,19 +168,23 @@ def verify(policy): for rule, rule_config in route_map_config['rule'].items(): # Specified community-list must exist - tmp = dict_search('match.community.community_list', rule_config) + tmp = dict_search('match.community.community_list', + rule_config) if tmp and tmp not in policy.get('community_list', []): raise ConfigError(f'community-list {tmp} does not exist!') # Specified extended community-list must exist tmp = dict_search('match.extcommunity', rule_config) if tmp and tmp not in policy.get('extcommunity_list', []): - raise ConfigError(f'extcommunity-list {tmp} does not exist!') + raise ConfigError( + f'extcommunity-list {tmp} does not exist!') # Specified large-community-list must exist - tmp = dict_search('match.large_community.large_community_list', rule_config) + tmp = dict_search('match.large_community.large_community_list', + rule_config) if tmp and tmp not in policy.get('large_community_list', []): - raise ConfigError(f'large-community-list {tmp} does not exist!') + raise ConfigError( + f'large-community-list {tmp} does not exist!') # Specified prefix-list must exist tmp = dict_search('match.ip.address.prefix_list', rule_config) @@ -147,49 +192,87 @@ def verify(policy): raise ConfigError(f'prefix-list {tmp} does not exist!') # Specified prefix-list must exist - tmp = dict_search('match.ipv6.address.prefix_list', rule_config) + tmp = dict_search('match.ipv6.address.prefix_list', + rule_config) if tmp and tmp not in policy.get('prefix_list6', []): raise ConfigError(f'prefix-list6 {tmp} does not exist!') - + # Specified access_list6 in nexthop must exist - tmp = dict_search('match.ipv6.nexthop.access_list', rule_config) + tmp = dict_search('match.ipv6.nexthop.access_list', + rule_config) if tmp and tmp not in policy.get('access_list6', []): raise ConfigError(f'access_list6 {tmp} does not exist!') # Specified prefix-list6 in nexthop must exist - tmp = dict_search('match.ipv6.nexthop.prefix_list', rule_config) + tmp = dict_search('match.ipv6.nexthop.prefix_list', + rule_config) if tmp and tmp not in policy.get('prefix_list6', []): raise ConfigError(f'prefix-list6 {tmp} does not exist!') + tmp = dict_search('set.community.delete', rule_config) + if tmp and tmp not in policy.get('community_list', []): + raise ConfigError(f'community-list {tmp} does not exist!') + + tmp = dict_search('set.large_community.delete', + rule_config) + if tmp and tmp not in policy.get('large_community_list', []): + raise ConfigError( + f'large-community-list {tmp} does not exist!') + + if 'set' in rule_config: + rule_action = rule_config['set'] + if 'community' in rule_action: + if not community_action_compatibility( + rule_action['community']): + raise ConfigError( + f'Unexpected combination between action replace, add, delete or none in community') + if 'large_community' in rule_action: + if not community_action_compatibility( + rule_action['large_community']): + raise ConfigError( + f'Unexpected combination between action replace, add, delete or none in large-community') + if 'extcommunity' in rule_action: + if not extcommunity_action_compatibility( + rule_action['extcommunity']): + raise ConfigError( + f'Unexpected combination between none, rt, soo, bandwidth, bandwidth-non-transitive in extended-community') # When routing protocols are active some use prefix-lists, route-maps etc. # to apply the systems routing policy to the learned or redistributed routes. # When the "routing policy" changes and policies, route-maps etc. are deleted, # it is our responsibility to verify that the policy can not be deleted if it # is used by any routing protocol if 'protocols' in policy: - for policy_type in ['access_list', 'access_list6', 'as_path_list', 'community_list', - 'extcommunity_list', 'large_community_list', 'prefix_list', 'route_map']: + for policy_type in ['access_list', 'access_list6', 'as_path_list', + 'community_list', + 'extcommunity_list', 'large_community_list', + 'prefix_list', 'route_map']: if policy_type in policy: - for policy_name in list(set(routing_policy_find(policy_type, policy['protocols']))): + for policy_name in list(set(routing_policy_find(policy_type, + policy[ + 'protocols']))): found = False if policy_name in policy[policy_type]: found = True # BGP uses prefix-list for selecting both an IPv4 or IPv6 AFI related # list - we need to go the extra mile here and check both prefix-lists - if policy_type == 'prefix_list' and 'prefix_list6' in policy and policy_name in policy['prefix_list6']: + if policy_type == 'prefix_list' and 'prefix_list6' in policy and policy_name in \ + policy['prefix_list6']: found = True if not found: - tmp = policy_type.replace('_','-') - raise ConfigError(f'Can not delete {tmp} "{policy_name}", still in use!') + tmp = policy_type.replace('_', '-') + raise ConfigError( + f'Can not delete {tmp} "{policy_name}", still in use!') return None + def generate(policy): if not policy: return None policy['new_frr_config'] = render_to_string('frr/policy.frr.j2', policy) return None + def apply(policy): bgp_daemon = 'bgpd' zebra_daemon = 'zebra' @@ -203,7 +286,8 @@ def apply(policy): frr_cfg.modify_section(r'^bgp community-list .*') frr_cfg.modify_section(r'^bgp extcommunity-list .*') frr_cfg.modify_section(r'^bgp large-community-list .*') - frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', remove_stop_mark=True) + frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', + remove_stop_mark=True) if 'new_frr_config' in policy: frr_cfg.add_before(frr.default_add_before, policy['new_frr_config']) frr_cfg.commit_configuration(bgp_daemon) @@ -214,13 +298,15 @@ def apply(policy): frr_cfg.modify_section(r'^ipv6 access-list .*') frr_cfg.modify_section(r'^ip prefix-list .*') frr_cfg.modify_section(r'^ipv6 prefix-list .*') - frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', remove_stop_mark=True) + frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', + remove_stop_mark=True) if 'new_frr_config' in policy: frr_cfg.add_before(frr.default_add_before, policy['new_frr_config']) frr_cfg.commit_configuration(zebra_daemon) return None + if __name__ == '__main__': try: c = get_config() diff --git a/src/migration-scripts/policy/3-to-4 b/src/migration-scripts/policy/3-to-4 new file mode 100755 index 000000000..bae30cffc --- /dev/null +++ b/src/migration-scripts/policy/3-to-4 @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# T4660: change cli +# from: set policy route-map FOO rule 10 set community 'TEXT' +# Multiple value +# to: set policy route-map FOO rule 10 set community replace <community> +# Multiple value +# to: set policy route-map FOO rule 10 set community add <community> +# to: set policy route-map FOO rule 10 set community none +# +# from: set policy route-map FOO rule 10 set large-community 'TEXT' +# Multiple value +# to: set policy route-map FOO rule 10 set large-community replace <community> +# Multiple value +# to: set policy route-map FOO rule 10 set large-community add <community> +# to: set policy route-map FOO rule 10 set large-community none +# +# from: set policy route-map FOO rule 10 set extecommunity [rt|soo] 'TEXT' +# Multiple value +# to: set policy route-map FOO rule 10 set extcommunity [rt|soo] <community> + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + + +# Migration function for large and regular communities +def community_migrate(config: ConfigTree, rule: list[str]) -> bool: + """ + + :param config: configuration object + :type config: ConfigTree + :param rule: Path to variable + :type rule: list[str] + :return: True if additive presents in community string + :rtype: bool + """ + community_list = list((config.return_value(rule)).split(" ")) + config.delete(rule) + if 'none' in community_list: + config.set(rule + ['none']) + return False + else: + community_action: str = 'replace' + if 'additive' in community_list: + community_action = 'add' + community_list.remove('additive') + for community in community_list: + config.set(rule + [community_action], value=community, + replace=False) + if community_action == 'replace': + return False + else: + return True + + +# Migration function for extcommunities +def extcommunity_migrate(config: ConfigTree, rule: list[str]) -> None: + """ + + :param config: configuration object + :type config: ConfigTree + :param rule: Path to variable + :type rule: list[str] + """ + # if config.exists(rule + ['bandwidth']): + # bandwidth: str = config.return_value(rule + ['bandwidth']) + # config.delete(rule + ['bandwidth']) + # config.set(rule + ['bandwidth'], value=bandwidth) + + if config.exists(rule + ['rt']): + community_list = list((config.return_value(rule + ['rt'])).split(" ")) + config.delete(rule + ['rt']) + for community in community_list: + config.set(rule + ['rt'], value=community, replace=False) + + if config.exists(rule + ['soo']): + community_list = list((config.return_value(rule + ['soo'])).split(" ")) + config.delete(rule + ['soo']) + for community in community_list: + config.set(rule + ['soo'], value=community, replace=False) + + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name: str = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base: list[str] = ['policy', 'route-map'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +for route_map in config.list_nodes(base): + if not config.exists(base + [route_map, 'rule']): + continue + for rule in config.list_nodes(base + [route_map, 'rule']): + base_rule: list[str] = base + [route_map, 'rule', rule, 'set'] + + # IF additive presents in coummunity then comm-list is redundant + isAdditive: bool = True + #### Change Set community ######## + if config.exists(base_rule + ['community']): + isAdditive = community_migrate(config, + base_rule + ['community']) + + #### Change Set community-list delete migrate ######## + if config.exists(base_rule + ['comm-list', 'comm-list']): + if isAdditive: + tmp = config.return_value( + base_rule + ['comm-list', 'comm-list']) + config.delete(base_rule + ['comm-list']) + config.set(base_rule + ['community', 'delete'], value=tmp) + else: + config.delete(base_rule + ['comm-list']) + + isAdditive = False + #### Change Set large-community ######## + if config.exists(base_rule + ['large-community']): + isAdditive = community_migrate(config, + base_rule + ['large-community']) + + #### Change Set large-community delete by List ######## + if config.exists(base_rule + ['large-comm-list-delete']): + if isAdditive: + tmp = config.return_value( + base_rule + ['large-comm-list-delete']) + config.delete(base_rule + ['large-comm-list-delete']) + config.set(base_rule + ['large-community', 'delete'], + value=tmp) + else: + config.delete(base_rule + ['large-comm-list-delete']) + + #### Change Set extcommunity ######## + extcommunity_migrate(config, base_rule + ['extcommunity']) +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/validators/bgp-extended-community b/src/validators/bgp-extended-community new file mode 100755 index 000000000..b69ae3449 --- /dev/null +++ b/src/validators/bgp-extended-community @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +from argparse import ArgumentParser +from sys import exit + +from vyos.template import is_ipv4 + +COMM_MAX_2_OCTET: int = 65535 +COMM_MAX_4_OCTET: int = 4294967295 + +if __name__ == '__main__': + # add an argument with community + parser: ArgumentParser = ArgumentParser() + parser.add_argument('community', type=str) + args = parser.parse_args() + community: str = args.community + if community.count(':') != 1: + print("Invalid community format") + exit(1) + try: + # try to extract community parts from an argument + comm_left: str = community.split(':')[0] + comm_right: int = int(community.split(':')[1]) + + # check if left part is an IPv4 address + if is_ipv4(comm_left) and 0 <= comm_right <= COMM_MAX_2_OCTET: + exit() + # check if a left part is a number + if 0 <= int(comm_left) <= COMM_MAX_2_OCTET \ + and 0 <= comm_right <= COMM_MAX_4_OCTET: + exit() + + except Exception: + # fail if something was wrong + print("Invalid community format") + exit(1) + + # fail if none of validators catched the value + print("Invalid community format") + exit(1)
\ No newline at end of file diff --git a/src/validators/bgp-large-community b/src/validators/bgp-large-community new file mode 100755 index 000000000..386398308 --- /dev/null +++ b/src/validators/bgp-large-community @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +from argparse import ArgumentParser +from sys import exit + +from vyos.template import is_ipv4 + +COMM_MAX_4_OCTET: int = 4294967295 + +if __name__ == '__main__': + # add an argument with community + parser: ArgumentParser = ArgumentParser() + parser.add_argument('community', type=str) + args = parser.parse_args() + community: str = args.community + if community.count(':') != 2: + print("Invalid community format") + exit(1) + try: + # try to extract community parts from an argument + comm_part1: int = int(community.split(':')[0]) + comm_part2: int = int(community.split(':')[1]) + comm_part3: int = int(community.split(':')[2]) + + # check compatibilities of left and right parts + if 0 <= comm_part1 <= COMM_MAX_4_OCTET \ + and 0 <= comm_part2 <= COMM_MAX_4_OCTET \ + and 0 <= comm_part3 <= COMM_MAX_4_OCTET: + exit(0) + + except Exception: + # fail if something was wrong + print("Invalid community format") + exit(1) + + # fail if none of validators catched the value + print("Invalid community format") + exit(1)
\ No newline at end of file diff --git a/src/validators/bgp-regular-community b/src/validators/bgp-regular-community new file mode 100755 index 000000000..d43a71eae --- /dev/null +++ b/src/validators/bgp-regular-community @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +from argparse import ArgumentParser +from sys import exit + +from vyos.template import is_ipv4 + +COMM_MAX_2_OCTET: int = 65535 + +if __name__ == '__main__': + # add an argument with community + parser: ArgumentParser = ArgumentParser() + parser.add_argument('community', type=str) + args = parser.parse_args() + community: str = args.community + if community.count(':') != 1: + print("Invalid community format") + exit(1) + try: + # try to extract community parts from an argument + comm_left: int = int(community.split(':')[0]) + comm_right: int = int(community.split(':')[1]) + + # check compatibilities of left and right parts + if 0 <= comm_left <= COMM_MAX_2_OCTET \ + and 0 <= comm_right <= COMM_MAX_2_OCTET: + exit(0) + except Exception: + # fail if something was wrong + print("Invalid community format") + exit(1) + + # fail if none of validators catched the value + print("Invalid community format") + exit(1)
\ No newline at end of file |