summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/conntrackd/conntrackd.conf.j24
-rw-r--r--data/templates/firewall/nftables-zone.j222
-rw-r--r--data/templates/firewall/nftables.j26
-rw-r--r--data/templates/frr/ospfd.frr.j227
-rw-r--r--data/templates/frr/policy.frr.j237
-rw-r--r--data/templates/ssh/sshd_config.j24
-rw-r--r--debian/vyos-1x.preinst1
-rw-r--r--interface-definitions/firewall.xml.in3
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i12
-rw-r--r--interface-definitions/include/ospf/high-low-label-value.xml.i26
-rw-r--r--interface-definitions/include/ospf/protocol-common-config.xml.i80
-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/policy/route-common-rule-ipv6.xml.i4
-rw-r--r--interface-definitions/include/policy/route-common-rule.xml.i4
-rw-r--r--interface-definitions/include/version/policy-version.xml.i2
-rw-r--r--interface-definitions/policy.xml.in224
-rw-r--r--interface-definitions/ssh.xml.in31
-rw-r--r--interface-definitions/vpn-l2tp.xml.in24
-rw-r--r--python/vyos/template.py13
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_ethernet.py8
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py198
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py62
-rwxr-xr-xsrc/conf_mode/policy.py132
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py52
-rwxr-xr-xsrc/conf_mode/ssh.py3
-rwxr-xr-xsrc/migration-scripts/policy/3-to-4162
-rwxr-xr-xsrc/op_mode/conntrack.py11
-rw-r--r--src/services/api/graphql/graphql/directives.py64
-rw-r--r--src/services/api/graphql/graphql/mutations.py23
-rw-r--r--src/services/api/graphql/graphql/queries.py20
-rw-r--r--src/services/api/graphql/graphql/schema/config_file.graphql29
-rw-r--r--src/services/api/graphql/graphql/schema/configsession.graphql115
-rw-r--r--src/services/api/graphql/graphql/schema/dhcp_server.graphql36
-rw-r--r--src/services/api/graphql/graphql/schema/firewall_group.graphql101
-rw-r--r--src/services/api/graphql/graphql/schema/image.graphql31
-rw-r--r--src/services/api/graphql/graphql/schema/interface_ethernet.graphql19
-rw-r--r--src/services/api/graphql/graphql/schema/schema.graphql24
-rw-r--r--src/services/api/graphql/graphql/schema/show.graphql15
-rw-r--r--src/services/api/graphql/graphql/schema/show_config.graphql21
-rw-r--r--src/services/api/graphql/session/session.py44
-rw-r--r--src/services/api/graphql/utils/config_session_function.py28
-rwxr-xr-xsrc/services/api/graphql/utils/schema_from_config_session.py119
-rwxr-xr-xsrc/services/api/graphql/utils/schema_from_op_mode.py30
-rw-r--r--src/services/api/graphql/utils/util.py24
-rwxr-xr-xsrc/validators/accel-radius-dictionary13
-rwxr-xr-xsrc/validators/bgp-extended-community55
-rwxr-xr-xsrc/validators/bgp-large-community53
-rwxr-xr-xsrc/validators/bgp-regular-community50
51 files changed, 1520 insertions, 669 deletions
diff --git a/data/templates/conntrackd/conntrackd.conf.j2 b/data/templates/conntrackd/conntrackd.conf.j2
index 66024869d..808a77759 100644
--- a/data/templates/conntrackd/conntrackd.conf.j2
+++ b/data/templates/conntrackd/conntrackd.conf.j2
@@ -9,7 +9,9 @@ Sync {
{% if iface_config.peer is vyos_defined %}
UDP {
{% if listen_address is vyos_defined %}
- IPv4_address {{ listen_address }}
+{% for address in listen_address %}
+ IPv4_address {{ address }}
+{% endfor %}
{% endif %}
IPv4_Destination_Address {{ iface_config.peer }}
Port {{ iface_config.port if iface_config.port is vyos_defined else '3780' }}
diff --git a/data/templates/firewall/nftables-zone.j2 b/data/templates/firewall/nftables-zone.j2
index 919881e19..17ef5101d 100644
--- a/data/templates/firewall/nftables-zone.j2
+++ b/data/templates/firewall/nftables-zone.j2
@@ -39,18 +39,22 @@
{% if zone_conf.local_zone is vyos_defined %}
chain VZONE_{{ zone_name }}_IN {
iifname lo counter return
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
+{% if zone_conf.from is vyos_defined %}
+{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
+{% endfor %}
+{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name) }}
}
chain VZONE_{{ zone_name }}_OUT {
oifname lo counter return
-{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall[fw_name] is vyos_defined %}
+{% if zone_conf.from_local is vyos_defined %}
+{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall[fw_name] is vyos_defined %}
oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
oifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
+{% endfor %}
+{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name) }}
}
{% else %}
@@ -59,12 +63,14 @@
{% if zone_conf.intra_zone_filtering is vyos_defined %}
iifname { {{ zone_conf.interface | join(",") }} } counter return
{% endif %}
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
-{% if zone[from_zone].local_zone is not defined %}
+{% if zone_conf.from is vyos_defined %}
+{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
+{% if zone[from_zone].local_zone is not defined %}
iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endif %}
-{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name) }}
}
{% endif %}
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 9d609f73f..a0f0b8c11 100644
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -204,13 +204,13 @@ table ip6 vyos_filter {
{% if state_policy is vyos_defined %}
chain VYOS_STATE_POLICY6 {
{% if state_policy.established is vyos_defined %}
- {{ state_policy.established | nft_state_policy('established', ipv6=True) }}
+ {{ state_policy.established | nft_state_policy('established') }}
{% endif %}
{% if state_policy.invalid is vyos_defined %}
- {{ state_policy.invalid | nft_state_policy('invalid', ipv6=True) }}
+ {{ state_policy.invalid | nft_state_policy('invalid') }}
{% endif %}
{% if state_policy.related is vyos_defined %}
- {{ state_policy.related | nft_state_policy('related', ipv6=True) }}
+ {{ state_policy.related | nft_state_policy('related') }}
{% endif %}
return
}
diff --git a/data/templates/frr/ospfd.frr.j2 b/data/templates/frr/ospfd.frr.j2
index 427fc8be7..9cd9b03dc 100644
--- a/data/templates/frr/ospfd.frr.j2
+++ b/data/templates/frr/ospfd.frr.j2
@@ -181,6 +181,33 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if refresh.timers is vyos_defined %}
refresh timer {{ refresh.timers }}
{% endif %}
+{% if segment_routing is vyos_defined %}
+{% if segment_routing.maximum_label_depth is vyos_defined %}
+ segment-routing node-msd {{ segment_routing.maximum_label_depth }}
+{% endif %}
+{% if segment_routing.global_block is vyos_defined %}
+{% if segment_routing.local_block is vyos_defined %}
+ segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }} local-block {{ segment_routing.local_block.low_label_value }} {{ segment_routing.local_block.high_label_value }}
+{% else %}
+ segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }}
+{% endif %}
+{% endif %}
+{% if segment_routing.prefix is vyos_defined %}
+{% for prefix, prefix_config in segment_routing.prefix.items() %}
+{% if prefix_config.index is vyos_defined %}
+{% if prefix_config.index.value is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }}
+{% if prefix_config.index.explicit_null is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} explicit-null
+{% elif prefix_config.index.no_php_flag is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} no-php-flag
+{% endif %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ segment-routing on
+{% endif %}
{% if timers.throttle.spf.delay is vyos_defined and timers.throttle.spf.initial_holdtime is vyos_defined and timers.throttle.spf.max_holdtime is vyos_defined %}
{# Timer values have default values #}
timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }}
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/data/templates/ssh/sshd_config.j2 b/data/templates/ssh/sshd_config.j2
index e7dbca581..79b07478b 100644
--- a/data/templates/ssh/sshd_config.j2
+++ b/data/templates/ssh/sshd_config.j2
@@ -96,3 +96,7 @@ DenyGroups {{ access_control.deny.group | join(' ') }}
# sshd(8) will send a message through the encrypted channel to request a response from the client
ClientAliveInterval {{ client_keepalive_interval }}
{% endif %}
+
+{% if rekey.data is vyos_defined %}
+RekeyLimit {{ rekey.data }}M {{ rekey.time + 'M' if rekey.time is vyos_defined }}
+{% endif %}
diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst
index 71750b3a1..213a23d9e 100644
--- a/debian/vyos-1x.preinst
+++ b/debian/vyos-1x.preinst
@@ -2,3 +2,4 @@ dpkg-divert --package vyos-1x --add --rename /etc/securetty
dpkg-divert --package vyos-1x --add --rename /etc/security/capability.conf
dpkg-divert --package vyos-1x --add --rename /lib/systemd/system/lcdproc.service
dpkg-divert --package vyos-1x --add --rename /etc/logrotate.d/conntrackd
+dpkg-divert --package vyos-1x --add --rename /usr/share/pam-configs/radius
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 773e86f00..673461036 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -711,6 +711,7 @@
</properties>
<children>
#include <include/firewall/action-accept-drop-reject.xml.i>
+ #include <include/firewall/log.xml.i>
#include <include/firewall/rule-log-level.xml.i>
</children>
</node>
@@ -720,6 +721,7 @@
</properties>
<children>
#include <include/firewall/action-accept-drop-reject.xml.i>
+ #include <include/firewall/log.xml.i>
#include <include/firewall/rule-log-level.xml.i>
</children>
</node>
@@ -729,6 +731,7 @@
</properties>
<children>
#include <include/firewall/action-accept-drop-reject.xml.i>
+ #include <include/firewall/log.xml.i>
#include <include/firewall/rule-log-level.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
index c9ad0d3d4..b8dbe73b2 100644
--- a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
@@ -6,18 +6,24 @@
<children>
<leafNode name="attribute">
<properties>
- <help>Specifies which RADIUS attribute contains rate information. (default is Filter-Id)</help>
+ <help>RADIUS attribute that contains rate information</help>
</properties>
<defaultValue>Filter-Id</defaultValue>
</leafNode>
<leafNode name="vendor">
<properties>
- <help>Specifies the vendor dictionary. (dictionary needs to be in /usr/share/accel-ppp/radius)</help>
+ <help>Vendor dictionary</help>
+ <completionHelp>
+ <list>alcatel cisco microsoft mikrotik</list>
+ </completionHelp>
+ <constraint>
+ <validator name="accel-radius-dictionary" />
+ </constraint>
</properties>
</leafNode>
<leafNode name="enable">
<properties>
- <help>Enables Bandwidth shaping via RADIUS</help>
+ <help>Enable bandwidth shaping via RADIUS</help>
<valueless />
</properties>
</leafNode>
diff --git a/interface-definitions/include/ospf/high-low-label-value.xml.i b/interface-definitions/include/ospf/high-low-label-value.xml.i
new file mode 100644
index 000000000..4b6779922
--- /dev/null
+++ b/interface-definitions/include/ospf/high-low-label-value.xml.i
@@ -0,0 +1,26 @@
+<!-- include start from ospf/high-low-label-value.xml.i -->
+<leafNode name="low-label-value">
+ <properties>
+ <help>MPLS label lower bound</help>
+ <valueHelp>
+ <format>u32:100-1048575</format>
+ <description>Label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="high-label-value">
+ <properties>
+ <help>MPLS label upper bound</help>
+ <valueHelp>
+ <format>u32:100-1048575</format>
+ <description>Label value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i
index 791bbc0f8..78c168641 100644
--- a/interface-definitions/include/ospf/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospf/protocol-common-config.xml.i
@@ -621,6 +621,86 @@
</constraint>
</properties>
</leafNode>
+<node name="segment-routing">
+ <properties>
+ <help>Segment-Routing (SPRING) settings</help>
+ </properties>
+ <children>
+ <node name="global-block">
+ <properties>
+ <help>Segment Routing Global Block label range</help>
+ </properties>
+ <children>
+ #include <include/ospf/high-low-label-value.xml.i>
+ </children>
+ </node>
+ <node name="local-block">
+ <properties>
+ <help>Segment Routing Local Block label range</help>
+ </properties>
+ <children>
+ #include <include/ospf/high-low-label-value.xml.i>
+ </children>
+ </node>
+ <leafNode name="maximum-label-depth">
+ <properties>
+ <help>Maximum MPLS labels allowed for this router</help>
+ <valueHelp>
+ <format>u32:1-16</format>
+ <description>MPLS label depth</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-16"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="prefix">
+ <properties>
+ <help>Static IPv4 prefix segment/label mapping</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 prefix segment</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="index">
+ <properties>
+ <help>Specify the index value of prefix segment/label ID</help>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Specify the index value of prefix segment/label ID</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>The index segment/label ID value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="explicit-null">
+ <properties>
+ <help>Request upstream neighbor to replace segment/label with explicit null label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="no-php-flag">
+ <properties>
+ <help>Do not request penultimate hop popping for segment/label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+</node>
<node name="redistribute">
<properties>
<help>Redistribute information from another routing protocol</help>
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/policy/route-common-rule-ipv6.xml.i b/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
index cfeba1a6c..662206336 100644
--- a/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
+++ b/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
@@ -198,6 +198,10 @@
<validator name="numeric" argument="--range 1-200"/>
<regex>(main)</regex>
</constraint>
+ <completionHelp>
+ <list>main</list>
+ <path>protocols static table</path>
+ </completionHelp>
</properties>
</leafNode>
<leafNode name="tcp-mss">
diff --git a/interface-definitions/include/policy/route-common-rule.xml.i b/interface-definitions/include/policy/route-common-rule.xml.i
index 5a17dbc95..35fccca50 100644
--- a/interface-definitions/include/policy/route-common-rule.xml.i
+++ b/interface-definitions/include/policy/route-common-rule.xml.i
@@ -198,6 +198,10 @@
<validator name="numeric" argument="--range 1-200"/>
<regex>(main)</regex>
</constraint>
+ <completionHelp>
+ <list>main</list>
+ <path>protocols static table</path>
+ </completionHelp>
</properties>
</leafNode>
<leafNode name="tcp-mss">
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/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in
index 126183162..f3c731fe5 100644
--- a/interface-definitions/ssh.xml.in
+++ b/interface-definitions/ssh.xml.in
@@ -206,6 +206,37 @@
</properties>
<defaultValue>22</defaultValue>
</leafNode>
+ <node name="rekey">
+ <properties>
+ <help>SSH session rekey limit</help>
+ </properties>
+ <children>
+ <leafNode name="data">
+ <properties>
+ <help>Threshold data in megabytes</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Megabytes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="time">
+ <properties>
+ <help>Threshold time in minutes</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Minutes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="client-keepalive-interval">
<properties>
<help>Enable transmission of keepalives from server to client</help>
diff --git a/interface-definitions/vpn-l2tp.xml.in b/interface-definitions/vpn-l2tp.xml.in
index fd70a76dc..cb5900e0d 100644
--- a/interface-definitions/vpn-l2tp.xml.in
+++ b/interface-definitions/vpn-l2tp.xml.in
@@ -238,29 +238,7 @@
</leafNode>
</children>
</node>
- <node name="rate-limit">
- <properties>
- <help>Upload/Download speed limits</help>
- </properties>
- <children>
- <leafNode name="attribute">
- <properties>
- <help>Specifies which radius attribute contains rate information</help>
- </properties>
- </leafNode>
- <leafNode name="vendor">
- <properties>
- <help>Specifies the vendor dictionary. (dictionary needs to be in /usr/share/accel-ppp/radius)</help>
- </properties>
- </leafNode>
- <leafNode name="enable">
- <properties>
- <help>Enables Bandwidth shaping via RADIUS</help>
- <valueless />
- </properties>
- </leafNode>
- </children>
- </node>
+ #include <include/accel-ppp/radius-additions-rate-limit.xml.i>
</children>
</node>
</children>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 0870a0523..2a4135f9e 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -566,12 +566,17 @@ def nft_default_rule(fw_conf, fw_name, ipv6=False):
return " ".join(output)
@register_filter('nft_state_policy')
-def nft_state_policy(conf, state, ipv6=False):
+def nft_state_policy(conf, state):
out = [f'ct state {state}']
- if 'log' in conf:
- log_level = conf['log']
- out.append(f'log level {log_level}')
+ if 'log' in conf and 'enable' in conf['log']:
+ log_state = state[:3].upper()
+ log_action = (conf['action'] if 'action' in conf else 'accept')[:1].upper()
+ out.append(f'log prefix "[STATE-POLICY-{log_state}-{log_action}]"')
+
+ if 'log_level' in conf:
+ log_level = conf['log_level']
+ out.append(f'level {log_level}')
out.append('counter')
diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index feb2c0268..ed611062a 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -120,15 +120,13 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
cls._base_path = ['interfaces', 'ethernet']
cls._mirror_interfaces = ['dum21354']
- # we need to filter out VLAN interfaces identified by a dot (.)
- # in their name - just in case!
+ # We only test on physical interfaces and not VLAN (sub-)interfaces
if 'TEST_ETH' in os.environ:
tmp = os.environ['TEST_ETH'].split()
cls._interfaces = tmp
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._interfaces.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._interfaces.append(tmp)
cls._macs = {}
for interface in cls._interfaces:
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/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index e15ea478b..93bb761c1 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -14,8 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import logging
-import sys
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
@@ -23,15 +21,12 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.util import process_named_running
-from vyos.util import cmd
PROCESS_NAME = 'ospfd'
base_path = ['protocols', 'ospf']
route_map = 'foo-bar-baz10'
-log = logging.getLogger('TestProtocolsOSPF')
-
class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -210,25 +205,14 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map])
self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type])
- # enable FRR debugging to find the root cause of failing testcases
- cmd('touch /tmp/vyos.frr.debug')
-
# commit changes
self.cli_commit()
- # disable FRR debugging
- cmd('rm -f /tmp/vyos.frr.debug')
-
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf')
- try:
- self.assertIn(f'router ospf', frrconfig)
- for protocol in redistribute:
- self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
- except:
- log.debug(frrconfig)
- log.debug(cmd('sudo cat /tmp/vyos-configd-script-stdout'))
- self.fail('Now we can hopefully see why OSPF fails!')
+ self.assertIn(f'router ospf', frrconfig)
+ for protocol in redistribute:
+ self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
def test_ospf_08_virtual_link(self):
networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
@@ -396,6 +380,44 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' network {network} area {area}', frrconfig)
self.assertIn(f' area {area} export-list {acl}', frrconfig)
+
+ def test_ospf_14_segment_routing_configuration(self):
+ global_block_low = "100"
+ global_block_high = "199"
+ local_block_low = "200"
+ local_block_high = "299"
+ interface = 'lo'
+ maximum_stack_size = '5'
+ prefix_one = '192.168.0.1/32'
+ prefix_two = '192.168.0.2/32'
+ prefix_one_value = '1'
+ prefix_two_value = '2'
+
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(base_path + ['segment-routing', 'maximum-label-depth', maximum_stack_size])
+ self.cli_set(base_path + ['segment-routing', 'global-block', 'low-label-value', global_block_low])
+ self.cli_set(base_path + ['segment-routing', 'global-block', 'high-label-value', global_block_high])
+ self.cli_set(base_path + ['segment-routing', 'local-block', 'low-label-value', local_block_low])
+ self.cli_set(base_path + ['segment-routing', 'local-block', 'high-label-value', local_block_high])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'value', prefix_one_value])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'explicit-null'])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'value', prefix_two_value])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'no-php-flag'])
+
+ # Commit all changes
+ self.cli_commit()
+
+ # Verify all changes
+
+ frrconfig = self.getFRRconfig('router ospf')
+ self.assertIn(f' segment-routing global-block {global_block_low} {global_block_high} local-block {local_block_low} {local_block_high}', frrconfig)
+ self.assertIn(f' segment-routing node-msd {maximum_stack_size}', frrconfig)
+ self.assertIn(f' segment-routing prefix {prefix_one} index {prefix_one_value} explicit-null', frrconfig)
+ self.assertIn(f' segment-routing prefix {prefix_two} index {prefix_two_value} no-php-flag', frrconfig)
+
+ self.skipTest('https://github.com/FRRouting/frr/issues/12007')
+ self.assertIn(f' segment-routing on', frrconfig)
+
+
if __name__ == '__main__':
- logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
unittest.main(verbosity=2)
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/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 5b4874ba2..0582d32be 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -198,6 +198,58 @@ def verify(ospf):
if 'master' not in tmp or tmp['master'] != vrf:
raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!')
+ # Segment routing checks
+ if dict_search('segment_routing.global_block', ospf):
+ g_high_label_value = dict_search('segment_routing.global_block.high_label_value', ospf)
+ g_low_label_value = dict_search('segment_routing.global_block.low_label_value', ospf)
+
+ # If segment routing global block high or low value is blank, throw error
+ if not (g_low_label_value or g_high_label_value):
+ raise ConfigError('Segment routing global-block requires both low and high value!')
+
+ # If segment routing global block low value is higher than the high value, throw error
+ if int(g_low_label_value) > int(g_high_label_value):
+ raise ConfigError('Segment routing global-block low value must be lower than high value')
+
+ if dict_search('segment_routing.local_block', ospf):
+ if dict_search('segment_routing.global_block', ospf) == None:
+ raise ConfigError('Segment routing local-block requires global-block to be configured!')
+
+ l_high_label_value = dict_search('segment_routing.local_block.high_label_value', ospf)
+ l_low_label_value = dict_search('segment_routing.local_block.low_label_value', ospf)
+
+ # If segment routing local-block high or low value is blank, throw error
+ if not (l_low_label_value or l_high_label_value):
+ raise ConfigError('Segment routing local-block requires both high and low value!')
+
+ # If segment routing local-block low value is higher than the high value, throw error
+ if int(l_low_label_value) > int(l_high_label_value):
+ raise ConfigError('Segment routing local-block low value must be lower than high value')
+
+ # local-block most live outside global block
+ global_range = range(int(g_low_label_value), int(g_high_label_value) +1)
+ local_range = range(int(l_low_label_value), int(l_high_label_value) +1)
+
+ # Check for overlapping ranges
+ if list(set(global_range) & set(local_range)):
+ raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\
+ f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!')
+
+ # Check for a blank or invalid value per prefix
+ if dict_search('segment_routing.prefix', ospf):
+ for prefix, prefix_config in ospf['segment_routing']['prefix'].items():
+ if 'index' in prefix_config:
+ if prefix_config['index'].get('value') is None:
+ raise ConfigError(f'Segment routing prefix {prefix} index value cannot be blank.')
+
+ # Check for explicit-null and no-php-flag configured at the same time per prefix
+ if dict_search('segment_routing.prefix', ospf):
+ for prefix, prefix_config in ospf['segment_routing']['prefix'].items():
+ if 'index' in prefix_config:
+ if ("explicit_null" in prefix_config['index']) and ("no_php_flag" in prefix_config['index']):
+ raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\
+ f'and no-php-flag configured at the same time.')
+
return None
def generate(ospf):
diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py
index 2bbd7142a..8746cc701 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -73,6 +73,9 @@ def verify(ssh):
if not ssh:
return None
+ if 'rekey' in ssh and 'data' not in ssh['rekey']:
+ raise ConfigError(f'Rekey data is required!')
+
verify_vrf(ssh)
return None
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/op_mode/conntrack.py b/src/op_mode/conntrack.py
index b27aa6060..fff537936 100755
--- a/src/op_mode/conntrack.py
+++ b/src/op_mode/conntrack.py
@@ -48,6 +48,14 @@ def _get_raw_data(family):
Return: dictionary
"""
xml = _get_xml_data(family)
+ if len(xml) == 0:
+ output = {'conntrack':
+ {
+ 'error': True,
+ 'reason': 'entries not found'
+ }
+ }
+ return output
return _xml_to_dict(xml)
@@ -72,7 +80,8 @@ def get_formatted_output(dict_data):
:return: formatted output
"""
data_entries = []
- #dict_data = _get_raw_data(family)
+ if 'error' in dict_data['conntrack']:
+ return 'Entries not found'
for entry in dict_data['conntrack']['flow']:
orig_src, orig_dst, orig_sport, orig_dport = {}, {}, {}, {}
reply_src, reply_dst, reply_sport, reply_dport = {}, {}, {}, {}
diff --git a/src/services/api/graphql/graphql/directives.py b/src/services/api/graphql/graphql/directives.py
index d8ceefae6..d75d72582 100644
--- a/src/services/api/graphql/graphql/directives.py
+++ b/src/services/api/graphql/graphql/directives.py
@@ -31,54 +31,21 @@ class VyosDirective(SchemaDirectiveVisitor):
field.resolve = func
return field
-
-class ConfigureDirective(VyosDirective):
- """
- Class providing implementation of 'configure' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_configure_resolver)
-
-class ShowConfigDirective(VyosDirective):
- """
- Class providing implementation of 'show' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_show_config_resolver)
-
-class SystemStatusDirective(VyosDirective):
- """
- Class providing implementation of 'system_status' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_system_status_resolver)
-
-class ConfigFileDirective(VyosDirective):
- """
- Class providing implementation of 'configfile' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_config_file_resolver)
-
-class ShowDirective(VyosDirective):
+class ConfigSessionQueryDirective(VyosDirective):
"""
- Class providing implementation of 'show' directive in schema.
+ Class providing implementation of 'configsessionquery' directive in schema.
"""
def visit_field_definition(self, field, object_type):
super().visit_field_definition(field, object_type,
- make_resolver=make_show_resolver)
+ make_resolver=make_config_session_query_resolver)
-class ImageDirective(VyosDirective):
+class ConfigSessionMutationDirective(VyosDirective):
"""
- Class providing implementation of 'image' directive in schema.
+ Class providing implementation of 'configsessionmutation' directive in schema.
"""
def visit_field_definition(self, field, object_type):
super().visit_field_definition(field, object_type,
- make_resolver=make_image_resolver)
+ make_resolver=make_config_session_mutation_resolver)
class GenOpQueryDirective(VyosDirective):
"""
@@ -96,11 +63,16 @@ class GenOpMutationDirective(VyosDirective):
super().visit_field_definition(field, object_type,
make_resolver=make_gen_op_mutation_resolver)
-directives_dict = {"configure": ConfigureDirective,
- "showconfig": ShowConfigDirective,
- "systemstatus": SystemStatusDirective,
- "configfile": ConfigFileDirective,
- "show": ShowDirective,
- "image": ImageDirective,
+class SystemStatusDirective(VyosDirective):
+ """
+ Class providing implementation of 'system_status' directive in schema.
+ """
+ def visit_field_definition(self, field, object_type):
+ super().visit_field_definition(field, object_type,
+ make_resolver=make_system_status_resolver)
+
+directives_dict = {"configsessionquery": ConfigSessionQueryDirective,
+ "configsessionmutation": ConfigSessionMutationDirective,
"genopquery": GenOpQueryDirective,
- "genopmutation": GenOpMutationDirective}
+ "genopmutation": GenOpMutationDirective,
+ "systemstatus": SystemStatusDirective}
diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py
index 5ccc9b0b6..f7d285a77 100644
--- a/src/services/api/graphql/graphql/mutations.py
+++ b/src/services/api/graphql/graphql/mutations.py
@@ -106,24 +106,9 @@ def make_mutation_resolver(mutation_name, class_name, session_func):
return func_impl
-def make_prefix_resolver(mutation_name, prefix=[]):
- for pre in prefix:
- Pre = pre.capitalize()
- if Pre in mutation_name:
- class_name = mutation_name.replace(Pre, '', 1)
- return make_mutation_resolver(mutation_name, class_name, pre)
- raise Exception
-
-def make_configure_resolver(mutation_name):
- class_name = mutation_name
- return make_mutation_resolver(mutation_name, class_name, 'configure')
-
-def make_config_file_resolver(mutation_name):
- return make_prefix_resolver(mutation_name, prefix=['save', 'load'])
-
-def make_image_resolver(mutation_name):
- return make_prefix_resolver(mutation_name, prefix=['add', 'delete'])
+def make_config_session_mutation_resolver(mutation_name):
+ return make_mutation_resolver(mutation_name, mutation_name,
+ convert_camel_case_to_snake(mutation_name))
def make_gen_op_mutation_resolver(mutation_name):
- class_name = mutation_name
- return make_mutation_resolver(mutation_name, class_name, 'gen_op_mutation')
+ return make_mutation_resolver(mutation_name, mutation_name, 'gen_op_mutation')
diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py
index b46914dcc..5f3a7d005 100644
--- a/src/services/api/graphql/graphql/queries.py
+++ b/src/services/api/graphql/graphql/queries.py
@@ -106,18 +106,12 @@ def make_query_resolver(query_name, class_name, session_func):
return func_impl
-def make_show_config_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'show_config')
-
-def make_system_status_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'system_status')
-
-def make_show_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'show')
+def make_config_session_query_resolver(query_name):
+ return make_query_resolver(query_name, query_name,
+ convert_camel_case_to_snake(query_name))
def make_gen_op_query_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'gen_op_query')
+ return make_query_resolver(query_name, query_name, 'gen_op_query')
+
+def make_system_status_resolver(query_name):
+ return make_query_resolver(query_name, query_name, 'system_status')
diff --git a/src/services/api/graphql/graphql/schema/config_file.graphql b/src/services/api/graphql/graphql/schema/config_file.graphql
deleted file mode 100644
index a7263114b..000000000
--- a/src/services/api/graphql/graphql/schema/config_file.graphql
+++ /dev/null
@@ -1,29 +0,0 @@
-input SaveConfigFileInput {
- key: String!
- fileName: String
-}
-
-type SaveConfigFile {
- fileName: String
-}
-
-type SaveConfigFileResult {
- data: SaveConfigFile
- success: Boolean!
- errors: [String]
-}
-
-input LoadConfigFileInput {
- key: String!
- fileName: String!
-}
-
-type LoadConfigFile {
- fileName: String!
-}
-
-type LoadConfigFileResult {
- data: LoadConfigFile
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/configsession.graphql b/src/services/api/graphql/graphql/schema/configsession.graphql
new file mode 100644
index 000000000..b1deac4b3
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/configsession.graphql
@@ -0,0 +1,115 @@
+
+input ShowConfigInput {
+ key: String!
+ path: [String!]!
+ configFormat: String = null
+}
+
+type ShowConfig {
+ result: Generic
+}
+
+type ShowConfigResult {
+ data: ShowConfig
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Query {
+ ShowConfig(data: ShowConfigInput) : ShowConfigResult @configsessionquery
+}
+
+input ShowInput {
+ key: String!
+ path: [String!]!
+}
+
+type Show {
+ result: Generic
+}
+
+type ShowResult {
+ data: Show
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Query {
+ Show(data: ShowInput) : ShowResult @configsessionquery
+}
+
+input SaveConfigFileInput {
+ key: String!
+ fileName: String = null
+}
+
+type SaveConfigFile {
+ result: Generic
+}
+
+type SaveConfigFileResult {
+ data: SaveConfigFile
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ SaveConfigFile(data: SaveConfigFileInput) : SaveConfigFileResult @configsessionmutation
+}
+
+input LoadConfigFileInput {
+ key: String!
+ fileName: String!
+}
+
+type LoadConfigFile {
+ result: Generic
+}
+
+type LoadConfigFileResult {
+ data: LoadConfigFile
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ LoadConfigFile(data: LoadConfigFileInput) : LoadConfigFileResult @configsessionmutation
+}
+
+input AddSystemImageInput {
+ key: String!
+ location: String!
+}
+
+type AddSystemImage {
+ result: Generic
+}
+
+type AddSystemImageResult {
+ data: AddSystemImage
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ AddSystemImage(data: AddSystemImageInput) : AddSystemImageResult @configsessionmutation
+}
+
+input DeleteSystemImageInput {
+ key: String!
+ name: String!
+}
+
+type DeleteSystemImage {
+ result: Generic
+}
+
+type DeleteSystemImageResult {
+ data: DeleteSystemImage
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ DeleteSystemImage(data: DeleteSystemImageInput) : DeleteSystemImageResult @configsessionmutation
+} \ No newline at end of file
diff --git a/src/services/api/graphql/graphql/schema/dhcp_server.graphql b/src/services/api/graphql/graphql/schema/dhcp_server.graphql
deleted file mode 100644
index 345c349ac..000000000
--- a/src/services/api/graphql/graphql/schema/dhcp_server.graphql
+++ /dev/null
@@ -1,36 +0,0 @@
-input DhcpServerConfigInput {
- key: String!
- sharedNetworkName: String
- subnet: String
- defaultRouter: String
- nameServer: String
- domainName: String
- lease: Int
- range: Int
- start: String
- stop: String
- dnsForwardingAllowFrom: String
- dnsForwardingCacheSize: Int
- dnsForwardingListenAddress: String
-}
-
-type DhcpServerConfig {
- sharedNetworkName: String
- subnet: String
- defaultRouter: String
- nameServer: String
- domainName: String
- lease: Int
- range: Int
- start: String
- stop: String
- dnsForwardingAllowFrom: String
- dnsForwardingCacheSize: Int
- dnsForwardingListenAddress: String
-}
-
-type CreateDhcpServerResult {
- data: DhcpServerConfig
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/firewall_group.graphql b/src/services/api/graphql/graphql/schema/firewall_group.graphql
deleted file mode 100644
index 9454d2997..000000000
--- a/src/services/api/graphql/graphql/schema/firewall_group.graphql
+++ /dev/null
@@ -1,101 +0,0 @@
-input CreateFirewallAddressGroupInput {
- key: String!
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressGroup {
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressGroupResult {
- data: CreateFirewallAddressGroup
- success: Boolean!
- errors: [String]
-}
-
-input UpdateFirewallAddressGroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressGroupMembers {
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressGroupMembersResult {
- data: UpdateFirewallAddressGroupMembers
- success: Boolean!
- errors: [String]
-}
-
-input RemoveFirewallAddressGroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressGroupMembers {
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressGroupMembersResult {
- data: RemoveFirewallAddressGroupMembers
- success: Boolean!
- errors: [String]
-}
-
-input CreateFirewallAddressIpv6GroupInput {
- key: String!
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressIpv6Group {
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressIpv6GroupResult {
- data: CreateFirewallAddressIpv6Group
- success: Boolean!
- errors: [String]
-}
-
-input UpdateFirewallAddressIpv6GroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressIpv6GroupMembers {
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressIpv6GroupMembersResult {
- data: UpdateFirewallAddressIpv6GroupMembers
- success: Boolean!
- errors: [String]
-}
-
-input RemoveFirewallAddressIpv6GroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressIpv6GroupMembers {
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressIpv6GroupMembersResult {
- data: RemoveFirewallAddressIpv6GroupMembers
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/image.graphql b/src/services/api/graphql/graphql/schema/image.graphql
deleted file mode 100644
index 485033875..000000000
--- a/src/services/api/graphql/graphql/schema/image.graphql
+++ /dev/null
@@ -1,31 +0,0 @@
-input AddSystemImageInput {
- key: String!
- location: String!
-}
-
-type AddSystemImage {
- location: String
- result: String
-}
-
-type AddSystemImageResult {
- data: AddSystemImage
- success: Boolean!
- errors: [String]
-}
-
-input DeleteSystemImageInput {
- key: String!
- name: String!
-}
-
-type DeleteSystemImage {
- name: String
- result: String
-}
-
-type DeleteSystemImageResult {
- data: DeleteSystemImage
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/interface_ethernet.graphql b/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
deleted file mode 100644
index 8a17d919f..000000000
--- a/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
+++ /dev/null
@@ -1,19 +0,0 @@
-input InterfaceEthernetConfigInput {
- key: String!
- interface: String
- address: String
- replace: Boolean = true
- description: String
-}
-
-type InterfaceEthernetConfig {
- interface: String
- address: String
- description: String
-}
-
-type CreateInterfaceEthernetResult {
- data: InterfaceEthernetConfig
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/schema.graphql b/src/services/api/graphql/graphql/schema/schema.graphql
index 624be2620..2acecade4 100644
--- a/src/services/api/graphql/graphql/schema/schema.graphql
+++ b/src/services/api/graphql/graphql/schema/schema.graphql
@@ -3,34 +3,16 @@ schema {
mutation: Mutation
}
-directive @configure on FIELD_DEFINITION
-directive @configfile on FIELD_DEFINITION
-directive @show on FIELD_DEFINITION
-directive @showconfig on FIELD_DEFINITION
directive @systemstatus on FIELD_DEFINITION
-directive @image on FIELD_DEFINITION
+directive @configsessionquery on FIELD_DEFINITION
+directive @configsessionmutation on FIELD_DEFINITION
directive @genopquery on FIELD_DEFINITION
directive @genopmutation on FIELD_DEFINITION
scalar Generic
type Query {
- Show(data: ShowInput) : ShowResult @show
- ShowConfig(data: ShowConfigInput) : ShowConfigResult @showconfig
SystemStatus(data: SystemStatusInput) : SystemStatusResult @systemstatus
}
-type Mutation {
- CreateDhcpServer(data: DhcpServerConfigInput) : CreateDhcpServerResult @configure
- CreateInterfaceEthernet(data: InterfaceEthernetConfigInput) : CreateInterfaceEthernetResult @configure
- CreateFirewallAddressGroup(data: CreateFirewallAddressGroupInput) : CreateFirewallAddressGroupResult @configure
- UpdateFirewallAddressGroupMembers(data: UpdateFirewallAddressGroupMembersInput) : UpdateFirewallAddressGroupMembersResult @configure
- RemoveFirewallAddressGroupMembers(data: RemoveFirewallAddressGroupMembersInput) : RemoveFirewallAddressGroupMembersResult @configure
- CreateFirewallAddressIpv6Group(data: CreateFirewallAddressIpv6GroupInput) : CreateFirewallAddressIpv6GroupResult @configure
- UpdateFirewallAddressIpv6GroupMembers(data: UpdateFirewallAddressIpv6GroupMembersInput) : UpdateFirewallAddressIpv6GroupMembersResult @configure
- RemoveFirewallAddressIpv6GroupMembers(data: RemoveFirewallAddressIpv6GroupMembersInput) : RemoveFirewallAddressIpv6GroupMembersResult @configure
- SaveConfigFile(data: SaveConfigFileInput) : SaveConfigFileResult @configfile
- LoadConfigFile(data: LoadConfigFileInput) : LoadConfigFileResult @configfile
- AddSystemImage(data: AddSystemImageInput) : AddSystemImageResult @image
- DeleteSystemImage(data: DeleteSystemImageInput) : DeleteSystemImageResult @image
-}
+type Mutation
diff --git a/src/services/api/graphql/graphql/schema/show.graphql b/src/services/api/graphql/graphql/schema/show.graphql
deleted file mode 100644
index 278ed536b..000000000
--- a/src/services/api/graphql/graphql/schema/show.graphql
+++ /dev/null
@@ -1,15 +0,0 @@
-input ShowInput {
- key: String!
- path: [String!]!
-}
-
-type Show {
- path: [String]
- result: String
-}
-
-type ShowResult {
- data: Show
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/show_config.graphql b/src/services/api/graphql/graphql/schema/show_config.graphql
deleted file mode 100644
index 5a1fe43da..000000000
--- a/src/services/api/graphql/graphql/schema/show_config.graphql
+++ /dev/null
@@ -1,21 +0,0 @@
-"""
-Use 'scalar Generic' for show config output, to avoid attempts to
-JSON-serialize in case of JSON output.
-"""
-
-input ShowConfigInput {
- key: String!
- path: [String!]!
- configFormat: String
-}
-
-type ShowConfig {
- path: [String]
- result: Generic
-}
-
-type ShowConfigResult {
- data: ShowConfig
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index 93e1c328e..f990e63d0 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -45,40 +45,6 @@ class Session:
except Exception:
self._op_mode_list = None
- def configure(self):
- session = self._session
- data = self._data
- func_base_name = self._name
-
- tmpl_file = f'{func_base_name}.tmpl'
- cmd_file = f'/tmp/{func_base_name}.cmds'
- tmpl_dir = directories['api_templates']
-
- try:
- render(cmd_file, tmpl_file, data, location=tmpl_dir)
- commands = []
- with open(cmd_file) as f:
- lines = f.readlines()
- for line in lines:
- commands.append(line.split())
- for cmd in commands:
- if cmd[0] == 'set':
- session.set(cmd[1:])
- elif cmd[0] == 'delete':
- session.delete(cmd[1:])
- else:
- raise ValueError('Operation must be "set" or "delete"')
- session.commit()
- except Exception as error:
- raise error
-
- def delete_path_if_childless(self, path):
- session = self._session
- config = Config(session.get_session_env())
- if not config.list_nodes(path):
- session.delete(path)
- session.commit()
-
def show_config(self):
session = self._session
data = self._data
@@ -87,14 +53,14 @@ class Session:
try:
out = session.show_config(data['path'])
if data.get('config_format', '') == 'json':
- config_tree = vyos.configtree.ConfigTree(out)
+ config_tree = ConfigTree(out)
out = json.loads(config_tree.to_json())
except Exception as error:
raise error
return out
- def save(self):
+ def save_config_file(self):
session = self._session
data = self._data
if 'file_name' not in data or not data['file_name']:
@@ -105,7 +71,7 @@ class Session:
except Exception as error:
raise error
- def load(self):
+ def load_config_file(self):
session = self._session
data = self._data
@@ -127,7 +93,7 @@ class Session:
return out
- def add(self):
+ def add_system_image(self):
session = self._session
data = self._data
@@ -138,7 +104,7 @@ class Session:
return res
- def delete(self):
+ def delete_system_image(self):
session = self._session
data = self._data
diff --git a/src/services/api/graphql/utils/config_session_function.py b/src/services/api/graphql/utils/config_session_function.py
new file mode 100644
index 000000000..fc0dd7a87
--- /dev/null
+++ b/src/services/api/graphql/utils/config_session_function.py
@@ -0,0 +1,28 @@
+# typing information for native configsession functions; used to generate
+# schema definition files
+import typing
+
+def show_config(path: list[str], configFormat: typing.Optional[str]):
+ pass
+
+def show(path: list[str]):
+ pass
+
+queries = {'show_config': show_config,
+ 'show': show}
+
+def save_config_file(fileName: typing.Optional[str]):
+ pass
+def load_config_file(fileName: str):
+ pass
+def add_system_image(location: str):
+ pass
+def delete_system_image(name: str):
+ pass
+
+mutations = {'save_config_file': save_config_file,
+ 'load_config_file': load_config_file,
+ 'add_system_image': add_system_image,
+ 'delete_system_image': delete_system_image}
+
+
diff --git a/src/services/api/graphql/utils/schema_from_config_session.py b/src/services/api/graphql/utils/schema_from_config_session.py
new file mode 100755
index 000000000..ea78aaf88
--- /dev/null
+++ b/src/services/api/graphql/utils/schema_from_config_session.py
@@ -0,0 +1,119 @@
+#!/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/>.
+#
+#
+# A utility to generate GraphQL schema defintions from typing information of
+# (wrappers of) native configsession functions.
+
+import os
+import json
+from inspect import signature, getmembers, isfunction, isclass, getmro
+from jinja2 import Template
+
+if __package__ is None or __package__ == '':
+ from util import snake_to_pascal_case, map_type_name
+else:
+ from . util import snake_to_pascal_case, map_type_name
+
+# this will be run locally before the build
+SCHEMA_PATH = '../graphql/schema'
+
+schema_data: dict = {'schema_name': '',
+ 'schema_fields': []}
+
+query_template = """
+input {{ schema_name }}Input {
+ key: String!
+ {%- for field_entry in schema_fields %}
+ {{ field_entry }}
+ {%- endfor %}
+}
+
+type {{ schema_name }} {
+ result: Generic
+}
+
+type {{ schema_name }}Result {
+ data: {{ schema_name }}
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Query {
+ {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @configsessionquery
+}
+"""
+
+mutation_template = """
+input {{ schema_name }}Input {
+ key: String!
+ {%- for field_entry in schema_fields %}
+ {{ field_entry }}
+ {%- endfor %}
+}
+
+type {{ schema_name }} {
+ result: Generic
+}
+
+type {{ schema_name }}Result {
+ data: {{ schema_name }}
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @configsessionmutation
+}
+"""
+
+def create_schema(func_name: str, func: callable, template: str) -> str:
+ sig = signature(func)
+
+ field_dict = {}
+ for k in sig.parameters:
+ field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation)
+
+ schema_fields = []
+ for k,v in field_dict.items():
+ schema_fields.append(k+': '+v)
+
+ schema_data['schema_name'] = snake_to_pascal_case(func_name)
+ schema_data['schema_fields'] = schema_fields
+
+ j2_template = Template(template)
+ res = j2_template.render(schema_data)
+
+ return res
+
+def generate_config_session_definitions():
+ from config_session_function import queries, mutations
+
+ results = []
+ for name,func in queries.items():
+ res = create_schema(name, func, query_template)
+ results.append(res)
+
+ for name,func in mutations.items():
+ res = create_schema(name, func, mutation_template)
+ results.append(res)
+
+ out = '\n'.join(results)
+ with open(f'{SCHEMA_PATH}/configsession.graphql', 'w') as f:
+ f.write(out)
+
+if __name__ == '__main__':
+ generate_config_session_definitions()
diff --git a/src/services/api/graphql/utils/schema_from_op_mode.py b/src/services/api/graphql/utils/schema_from_op_mode.py
index 379d15250..57d63628b 100755
--- a/src/services/api/graphql/utils/schema_from_op_mode.py
+++ b/src/services/api/graphql/utils/schema_from_op_mode.py
@@ -20,15 +20,16 @@
import os
import json
-import typing
from inspect import signature, getmembers, isfunction, isclass, getmro
from jinja2 import Template
from vyos.defaults import directories
if __package__ is None or __package__ == '':
from util import load_as_module, is_op_mode_function_name, is_show_function_name
+ from util import snake_to_pascal_case, map_type_name
else:
from . util import load_as_module, is_op_mode_function_name, is_show_function_name
+ from . util import snake_to_pascal_case, map_type_name
OP_MODE_PATH = directories['op_mode']
SCHEMA_PATH = directories['api_schema']
@@ -103,35 +104,12 @@ type {{ name }} implements OpModeError {
{%- endfor %}
"""
-def _snake_to_pascal_case(name: str) -> str:
- res = ''.join(map(str.title, name.split('_')))
- return res
-
-def _map_type_name(type_name: type, optional: bool = False) -> str:
- if type_name == str:
- return 'String!' if not optional else 'String = null'
- if type_name == int:
- return 'Int!' if not optional else 'Int = null'
- if type_name == bool:
- return 'Boolean!' if not optional else 'Boolean = false'
- if typing.get_origin(type_name) == list:
- if not optional:
- return f'[{_map_type_name(typing.get_args(type_name)[0])}]!'
- return f'[{_map_type_name(typing.get_args(type_name)[0])}]'
- # typing.Optional is typing.Union[_, NoneType]
- if (typing.get_origin(type_name) is typing.Union and
- typing.get_args(type_name)[1] == type(None)):
- return f'{_map_type_name(typing.get_args(type_name)[0], optional=True)}'
-
- # scalar 'Generic' is defined in schema.graphql
- return 'Generic'
-
def create_schema(func_name: str, base_name: str, func: callable) -> str:
sig = signature(func)
field_dict = {}
for k in sig.parameters:
- field_dict[sig.parameters[k].name] = _map_type_name(sig.parameters[k].annotation)
+ field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation)
# It is assumed that if one is generating a schema for a 'show_*'
# function, that 'get_raw_data' is present and 'raw' is desired.
@@ -142,7 +120,7 @@ def create_schema(func_name: str, base_name: str, func: callable) -> str:
for k,v in field_dict.items():
schema_fields.append(k+': '+v)
- schema_data['schema_name'] = _snake_to_pascal_case(func_name + '_' + base_name)
+ schema_data['schema_name'] = snake_to_pascal_case(func_name + '_' + base_name)
schema_data['schema_fields'] = schema_fields
if is_show_function_name(func_name):
diff --git a/src/services/api/graphql/utils/util.py b/src/services/api/graphql/utils/util.py
index 073126853..da2bcdb5b 100644
--- a/src/services/api/graphql/utils/util.py
+++ b/src/services/api/graphql/utils/util.py
@@ -15,6 +15,7 @@
import os
import re
+import typing
import importlib.util
from vyos.defaults import directories
@@ -74,3 +75,26 @@ def split_compound_op_mode_name(name: str, files: list):
pair = (pair[0], f[0])
return pair
return (name, '')
+
+def snake_to_pascal_case(name: str) -> str:
+ res = ''.join(map(str.title, name.split('_')))
+ return res
+
+def map_type_name(type_name: type, optional: bool = False) -> str:
+ if type_name == str:
+ return 'String!' if not optional else 'String = null'
+ if type_name == int:
+ return 'Int!' if not optional else 'Int = null'
+ if type_name == bool:
+ return 'Boolean!' if not optional else 'Boolean = false'
+ if typing.get_origin(type_name) == list:
+ if not optional:
+ return f'[{map_type_name(typing.get_args(type_name)[0])}]!'
+ return f'[{map_type_name(typing.get_args(type_name)[0])}]'
+ # typing.Optional is typing.Union[_, NoneType]
+ if (typing.get_origin(type_name) is typing.Union and
+ typing.get_args(type_name)[1] == type(None)):
+ return f'{map_type_name(typing.get_args(type_name)[0], optional=True)}'
+
+ # scalar 'Generic' is defined in schema.graphql
+ return 'Generic'
diff --git a/src/validators/accel-radius-dictionary b/src/validators/accel-radius-dictionary
new file mode 100755
index 000000000..05287e770
--- /dev/null
+++ b/src/validators/accel-radius-dictionary
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+DICT_PATH=/usr/share/accel-ppp/radius
+NAME=$1
+
+if [ -n "$NAME" -a -e $DICT_PATH/dictionary.$NAME ]; then
+ exit 0
+else
+ echo "$NAME is not a valid RADIUS dictionary name"
+ echo "Please make sure that $DICT_PATH/dictionary.$NAME file exists"
+ exit 1
+fi
+
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