summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2022-10-06 20:47:11 +0200
committerGitHub <noreply@github.com>2022-10-06 20:47:11 +0200
commit975eaa55f85a2855f5551447c9f120cca8815351 (patch)
tree14b27a3393002820ef0da4a2c81462c0f929bdf1
parent50f26c54d095420907a1d31168c162ad3c27ee36 (diff)
parent507f6ac423403b57f375309b483c6ccc1c83ad06 (diff)
downloadvyos-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.j237
-rw-r--r--interface-definitions/include/policy/community-clear.xml.i8
-rw-r--r--interface-definitions/include/policy/community-value-list.xml.i90
-rw-r--r--interface-definitions/include/policy/extended-community-value-list.xml.i15
-rw-r--r--interface-definitions/include/policy/large-community-value-list.xml.i10
-rw-r--r--interface-definitions/include/version/policy-version.xml.i2
-rw-r--r--interface-definitions/policy.xml.in224
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py198
-rwxr-xr-xsrc/conf_mode/policy.py132
-rwxr-xr-xsrc/migration-scripts/policy/3-to-4162
-rwxr-xr-xsrc/validators/bgp-extended-community55
-rwxr-xr-xsrc/validators/bgp-large-community53
-rwxr-xr-xsrc/validators/bgp-regular-community50
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>&lt;AS:VAL&gt;</format>
+ <description>Community number in &lt;0-65535:0-65535&gt; 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 &lt;0-65535:0-4294967295&gt;</description>
+</valueHelp>
+<valueHelp>
+ <format>IP:NN</format>
+ <description>Based on a router-id IP address in format &lt;IP:0-65535&gt;</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 &lt;0-4294967295:0-4294967295:0-4294967295&gt;</description>
+ <format>&lt;GA:LDP1:LDP2&gt;</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>&lt;aa:nn&gt;</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