summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/auto-author-assign.yml2
-rw-r--r--data/templates/accel-ppp/config_ip_pool.j212
-rw-r--r--data/templates/accel-ppp/pppoe.config.j215
-rw-r--r--data/templates/chrony/chrony.conf.j23
-rw-r--r--data/templates/dhcp-relay/dhcrelay.conf.j25
-rw-r--r--data/templates/frr/bgpd.frr.j213
-rw-r--r--debian/vyos-1x.install1
-rw-r--r--interface-definitions/dhcp-relay.xml.in32
-rw-r--r--interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i18
-rw-r--r--interface-definitions/include/bgp/neighbor-local-role.xml.i42
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/qos/bandwidth-auto.xml.i2
-rw-r--r--interface-definitions/include/qos/bandwidth.xml.i2
-rw-r--r--interface-definitions/include/qos/class-match.xml.i4
-rw-r--r--interface-definitions/qos.xml.in12
-rw-r--r--interface-definitions/service-ipoe-server.xml.in17
-rw-r--r--interface-definitions/service-pppoe-server.xml.in2
-rw-r--r--python/vyos/configverify.py15
-rw-r--r--python/vyos/template.py20
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py20
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-relay.py37
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py29
-rwxr-xr-xsrc/conf_mode/dhcp_relay.py15
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py5
-rwxr-xr-xsrc/conf_mode/qos.py15
-rwxr-xr-xsrc/etc/commit/post-hooks.d/00vyos-sync7
-rw-r--r--src/services/api/graphql/generate/config_session_function.py6
-rw-r--r--src/services/api/graphql/session/session.py27
28 files changed, 341 insertions, 39 deletions
diff --git a/.github/workflows/auto-author-assign.yml b/.github/workflows/auto-author-assign.yml
index 13bfd9bb1..1a7f8ef0b 100644
--- a/.github/workflows/auto-author-assign.yml
+++ b/.github/workflows/auto-author-assign.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Assign Author to PR"
- uses: toshimaru/auto-author-assign@v1.3.5
+ uses: toshimaru/auto-author-assign@v1.6.2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/data/templates/accel-ppp/config_ip_pool.j2 b/data/templates/accel-ppp/config_ip_pool.j2
index 0bef4ad69..f7511e445 100644
--- a/data/templates/accel-ppp/config_ip_pool.j2
+++ b/data/templates/accel-ppp/config_ip_pool.j2
@@ -11,4 +11,14 @@ gw-ip-address={{ gateway_address }}
{{ subnet }}
{% endfor %}
{% endif %}
-{% endif %}
+{% if client_ip_pool.name is vyos_defined %}
+{% for pool, pool_config in client_ip_pool.name.items() %}
+{% if pool_config.subnet is vyos_defined %}
+{{ pool_config.subnet }},name={{ pool }}
+{% endif %}
+{% if pool_config.gateway_address is vyos_defined %}
+gw-ip-address={{ pool_config.gateway_address }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %} \ No newline at end of file
diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2
index f4129d3e2..811c4ccc0 100644
--- a/data/templates/accel-ppp/pppoe.config.j2
+++ b/data/templates/accel-ppp/pppoe.config.j2
@@ -69,8 +69,6 @@ ccp={{ "1" if ppp_options.ccp is vyos_defined else "0" }}
unit-preallocate={{ "1" if authentication.radius.preallocate_vif is vyos_defined else "0" }}
{% if ppp_options.min_mtu is vyos_defined %}
min-mtu={{ ppp_options.min_mtu }}
-{% else %}
-min-mtu={{ mtu }}
{% endif %}
{% if ppp_options.mru is vyos_defined %}
mru={{ ppp_options.mru }}
@@ -135,6 +133,19 @@ pado-delay={{ pado_delay_param.value }}
called-sid={{ authentication.radius.called_sid_format }}
{% endif %}
+{% if authentication.mode is vyos_defined("local") %}
+{% if client_ip_pool.name is vyos_defined %}
+{% for pool, pool_config in client_ip_pool.name.items() %}
+{% if pool_config.subnet is vyos_defined %}
+ip-pool={{ pool }}
+{% endif %}
+{% if pool_config.gateway_address is vyos_defined %}
+gw-ip-address={{ pool_config.gateway_address }}/{{ pool_config.subnet.split('/')[1] }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
+
{% if limits is vyos_defined %}
[connlimit]
{% if limits.connection_limit is vyos_defined %}
diff --git a/data/templates/chrony/chrony.conf.j2 b/data/templates/chrony/chrony.conf.j2
index b3bfc8c0c..711bbbec7 100644
--- a/data/templates/chrony/chrony.conf.j2
+++ b/data/templates/chrony/chrony.conf.j2
@@ -40,8 +40,9 @@ user {{ user }}
{% for address in allow_client.address %}
allow {{ address }}
{% endfor %}
-{% endif %}
+{% else %}
deny all
+{% endif %}
{% if listen_address is vyos_defined or interface is vyos_defined %}
# NTP should listen on configured addresses only
diff --git a/data/templates/dhcp-relay/dhcrelay.conf.j2 b/data/templates/dhcp-relay/dhcrelay.conf.j2
index 11710bd8e..c26c263fd 100644
--- a/data/templates/dhcp-relay/dhcrelay.conf.j2
+++ b/data/templates/dhcp-relay/dhcrelay.conf.j2
@@ -2,5 +2,8 @@
{% set max_size = '-A ' ~ relay_options.max_size if relay_options.max_size is vyos_defined %}
{# hop_count and relay_agents_packets is a default option, thus it is always present #}
+{% if interface is vyos_defined %}
OPTIONS="-c {{ relay_options.hop_count }} -a -m {{ relay_options.relay_agents_packets }} {{ max_size }} -i {{ interface | join(' -i ') }} {{ server | join(' ') }}"
-
+{% else %}
+OPTIONS="-c {{ relay_options.hop_count }} -a -m {{ relay_options.relay_agents_packets }} {{ max_size }} -id {{ listen_interface | join(' -id ') }} -iu {{ upstream_interface | join(' -iu ') }} {{ server | join(' ') }}"
+{% endif %} \ No newline at end of file
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index 5febd7c66..b8692f344 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -9,6 +9,11 @@
{% if config.remote_as is vyos_defined %}
neighbor {{ neighbor }} remote-as {{ config.remote_as }}
{% endif %}
+{% if config.local_role is vyos_defined %}
+{% for role, strict in config.local_role.items() %}
+ neighbor {{ neighbor }} local-role {{ role }} {{ 'strict-mode' if strict }}
+{% endfor %}
+{% endif %}
{% if config.interface.remote_as is vyos_defined %}
neighbor {{ neighbor }} interface remote-as {{ config.interface.remote_as }}
{% endif %}
@@ -414,10 +419,14 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
route-target both {{ vni_config.route_target.both }}
{% endif %}
{% if vni_config.route_target.export is vyos_defined %}
- route-target export {{ vni_config.route_target.export }}
+{% for route_target in vni_config.route_target.export %}
+ route-target export {{ route_target }}
+{% endfor %}
{% endif %}
{% if vni_config.route_target.import is vyos_defined %}
- route-target import {{ vni_config.route_target.import }}
+{% for route_target in vni_config.route_target.import %}
+ route-target import {{ route_target }}
+{% endfor %}
{% endif %}
exit-vni
{% endfor %}
diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install
index 11b488b22..6b5c7793d 100644
--- a/debian/vyos-1x.install
+++ b/debian/vyos-1x.install
@@ -1,3 +1,4 @@
+etc/commit
etc/dhcp
etc/ipsec.d
etc/logrotate.d
diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in
index 27d0a3e6c..df2821881 100644
--- a/interface-definitions/dhcp-relay.xml.in
+++ b/interface-definitions/dhcp-relay.xml.in
@@ -10,6 +10,38 @@
</properties>
<children>
#include <include/generic-interface-multi-broadcast.xml.i>
+ <leafNode name="listen-interface">
+ <properties>
+ <help>Interface for DHCP Relay Agent to listen for requests</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/interface-name.xml.in>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="upstream-interface">
+ <properties>
+ <help>Interface for DHCP Relay Agent forward requests out</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/interface-name.xml.in>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
<node name="relay-options">
<properties>
<help>Relay options</help>
diff --git a/interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i b/interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i
new file mode 100644
index 000000000..654b6727e
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/client-ip-pool-name.xml.i
@@ -0,0 +1,18 @@
+<!-- include start from accel-ppp/client-ip-pool-name.xml.i -->
+<tagNode name="name">
+ <properties>
+ <help>Pool name</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Name of IP pool</description>
+ </valueHelp>
+ <constraint>
+ <regex>[-_a-zA-Z0-9.]+</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/accel-ppp/gateway-address.xml.i>
+ #include <include/accel-ppp/client-ip-pool-subnet-single.xml.i>
+ </children>
+</tagNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/neighbor-local-role.xml.i b/interface-definitions/include/bgp/neighbor-local-role.xml.i
new file mode 100644
index 000000000..6ddb4908f
--- /dev/null
+++ b/interface-definitions/include/bgp/neighbor-local-role.xml.i
@@ -0,0 +1,42 @@
+<!-- include start from bgp/neigbhor-local-role.xml.i -->
+<tagNode name="local-role">
+ <properties>
+ <help>Local role for BGP neighbor (RFC9234)</help>
+ <completionHelp>
+ <list>customer peer provider rs-client rs-server</list>
+ </completionHelp>
+ <valueHelp>
+ <format>customer</format>
+ <description>Using Transit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>peer</format>
+ <description>Public/Private Peering</description>
+ </valueHelp>
+ <valueHelp>
+ <format>provider</format>
+ <description>Providing Transit</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rs-client</format>
+ <description>RS Client</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rs-server</format>
+ <description>Route Server</description>
+ </valueHelp>
+ <constraint>
+ <regex>(provider|rs-server|rs-client|customer|peer)</regex>
+ </constraint>
+ <constraintErrorMessage>BGP local-role must be one of the following: customer, peer, provider, rs-client or rs-server</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="strict">
+ <properties>
+ <help>Neighbor must send this exact capability, otherwise a role missmatch notification will be sent</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 366630f78..ec065347c 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -987,6 +987,7 @@
</children>
</node>
#include <include/bgp/neighbor-local-as.xml.i>
+ #include <include/bgp/neighbor-local-role.xml.i>
#include <include/bgp/neighbor-override-capability.xml.i>
#include <include/bgp/neighbor-passive.xml.i>
#include <include/bgp/neighbor-password.xml.i>
@@ -1503,6 +1504,7 @@
#include <include/bgp/neighbor-graceful-restart.xml.i>
#include <include/bgp/neighbor-graceful-restart.xml.i>
#include <include/bgp/neighbor-local-as.xml.i>
+ #include <include/bgp/neighbor-local-role.xml.i>
#include <include/bgp/neighbor-override-capability.xml.i>
#include <include/bgp/neighbor-passive.xml.i>
#include <include/bgp/neighbor-password.xml.i>
diff --git a/interface-definitions/include/qos/bandwidth-auto.xml.i b/interface-definitions/include/qos/bandwidth-auto.xml.i
index a86f28296..fa16a6cb0 100644
--- a/interface-definitions/include/qos/bandwidth-auto.xml.i
+++ b/interface-definitions/include/qos/bandwidth-auto.xml.i
@@ -39,7 +39,7 @@
</valueHelp>
<constraint>
<validator name="numeric" argument="--positive"/>
- <regex>(auto|\d+(bit|kbit|mbit|gbit|tbit)|(100|\d(\d)?)%)</regex>
+ <regex>(auto|\d+(bit|kbit|mbit|gbit|tbit)?|(100|\d(\d)?)%)</regex>
</constraint>
</properties>
<defaultValue>auto</defaultValue>
diff --git a/interface-definitions/include/qos/bandwidth.xml.i b/interface-definitions/include/qos/bandwidth.xml.i
index f2848f066..cc923f642 100644
--- a/interface-definitions/include/qos/bandwidth.xml.i
+++ b/interface-definitions/include/qos/bandwidth.xml.i
@@ -32,7 +32,7 @@
</valueHelp>
<constraint>
<validator name="numeric" argument="--positive"/>
- <regex>(\d+(bit|kbit|mbit|gbit|tbit)|(100|\d(\d)?)%)</regex>
+ <regex>(\d+(bit|kbit|mbit|gbit|tbit)?|(100|\d(\d)?)%)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/qos/class-match.xml.i b/interface-definitions/include/qos/class-match.xml.i
index d9c35731d..4ba12f8f7 100644
--- a/interface-definitions/include/qos/class-match.xml.i
+++ b/interface-definitions/include/qos/class-match.xml.i
@@ -151,11 +151,11 @@
<properties>
<help>Match on mark applied by firewall</help>
<valueHelp>
- <format>txt</format>
+ <format>u32</format>
<description>FW mark to match</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 0x0-0xffff"/>
+ <validator name="numeric" argument="--range 0-4294967295"/>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in
index 8809369ff..757c1f856 100644
--- a/interface-definitions/qos.xml.in
+++ b/interface-definitions/qos.xml.in
@@ -29,8 +29,12 @@
</completionHelp>
<valueHelp>
<format>txt</format>
- <description>QoS Policy name</description>
+ <description>QoS policy to use</description>
</valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="egress">
@@ -51,8 +55,12 @@
</completionHelp>
<valueHelp>
<format>txt</format>
- <description>QoS Policy name</description>
+ <description>QoS policy to use</description>
</valueHelp>
+ <constraint>
+ <regex>[[:alnum:]][-_[:alnum:]]*</regex>
+ </constraint>
+ <constraintErrorMessage>Only alpha-numeric policy name allowed</constraintErrorMessage>
</properties>
</leafNode>
</children>
diff --git a/interface-definitions/service-ipoe-server.xml.in b/interface-definitions/service-ipoe-server.xml.in
index d778f9de0..ca4929249 100644
--- a/interface-definitions/service-ipoe-server.xml.in
+++ b/interface-definitions/service-ipoe-server.xml.in
@@ -108,22 +108,7 @@
<help>Client IP pools and gateway setting</help>
</properties>
<children>
- <tagNode name="name">
- <properties>
- <help>Pool name</help>
- <valueHelp>
- <format>txt</format>
- <description>Name of IP pool</description>
- </valueHelp>
- <constraint>
- <regex>[-_a-zA-Z0-9.]+</regex>
- </constraint>
- </properties>
- <children>
- #include <include/accel-ppp/gateway-address.xml.i>
- #include <include/accel-ppp/client-ip-pool-subnet-single.xml.i>
- </children>
- </tagNode>
+ #include <include/accel-ppp/client-ip-pool-name.xml.i>
</children>
</node>
#include <include/accel-ppp/client-ipv6-pool.xml.i>
diff --git a/interface-definitions/service-pppoe-server.xml.in b/interface-definitions/service-pppoe-server.xml.in
index 68592b96b..47f60d27d 100644
--- a/interface-definitions/service-pppoe-server.xml.in
+++ b/interface-definitions/service-pppoe-server.xml.in
@@ -56,6 +56,7 @@
<children>
#include <include/accel-ppp/client-ip-pool-start-stop.xml.i>
#include <include/accel-ppp/client-ip-pool-subnet.xml.i>
+ #include <include/accel-ppp/client-ip-pool-name.xml.i>
</children>
</node>
#include <include/accel-ppp/client-ipv6-pool.xml.i>
@@ -122,6 +123,7 @@
<validator name="numeric" argument="--range 68-65535"/>
</constraint>
</properties>
+ <defaultValue>1280</defaultValue>
</leafNode>
<leafNode name="mru">
<properties>
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 8e0ce701e..63edacc81 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2020-2023 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
@@ -23,6 +23,7 @@
from vyos import ConfigError
from vyos.util import dict_search
+from vyos.util import dict_search_recursive
def verify_mtu(config):
"""
@@ -414,7 +415,17 @@ def verify_accel_ppp_base_service(config, local_users=True):
if 'key' not in radius_config:
raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
- if 'gateway_address' not in config:
+ # Check global gateway or gateway in named pool
+ gateway = False
+ if 'gateway_address' in config:
+ gateway = True
+ else:
+ if dict_search_recursive(config, 'gateway_address', ['client_ip_pool', 'name']):
+ for _, v in config['client_ip_pool']['name'].items():
+ if 'gateway_address' in v:
+ gateway = True
+ break
+ if not gateway:
raise ConfigError('Server requires gateway-address to be configured!')
if 'name_server_ipv4' in config:
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 15240f815..6367f51e5 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -158,6 +158,24 @@ def force_to_list(value):
else:
return [value]
+@register_filter('seconds_to_human')
+def seconds_to_human(seconds, separator=""):
+ """ Convert seconds to human-readable values like 1d6h15m23s """
+ from vyos.util import seconds_to_human
+ return seconds_to_human(seconds, separator=separator)
+
+@register_filter('bytes_to_human')
+def bytes_to_human(bytes, initial_exponent=0, precision=2):
+ """ Convert bytes to human-readable values like 1.44M """
+ from vyos.util import bytes_to_human
+ return bytes_to_human(bytes, initial_exponent=initial_exponent, precision=precision)
+
+@register_filter('human_to_bytes')
+def human_to_bytes(value):
+ """ Convert a data amount with a unit suffix to bytes, like 2K to 2048 """
+ from vyos.util import human_to_bytes
+ return human_to_bytes(value)
+
@register_filter('ip_from_cidr')
def ip_from_cidr(prefix):
""" Take an IPv4/IPv6 CIDR host and strip cidr mask.
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index e33be6644..bf5b2e0f3 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -68,6 +68,7 @@ neighbor_config = {
'pfx_list_in' : prefix_list_in,
'pfx_list_out' : prefix_list_out,
'no_send_comm_std' : '',
+ 'local_role' : 'rs-client',
},
'192.0.2.3' : {
'advertise_map' : route_map_in,
@@ -98,6 +99,8 @@ neighbor_config = {
'no_send_comm_std' : '',
'addpath_per_as' : '',
'peer_group' : 'foo-bar',
+ 'local_role' : 'customer',
+ 'local_role_strict': '',
},
'2001:db8::2' : {
'remote_as' : '456',
@@ -154,6 +157,8 @@ peer_group_config = {
'update_src' : 'lo',
'route_map_in' : route_map_in,
'route_map_out' : route_map_out,
+ 'local_role' : 'peer',
+ 'local_role_strict': '',
},
}
@@ -221,6 +226,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {peer} ebgp-multihop {peer_config["multi_hop"]}', frrconfig)
if 'local_as' in peer_config:
self.assertIn(f' neighbor {peer} local-as {peer_config["local_as"]} no-prepend replace-as', frrconfig)
+ if 'local_role' in peer_config:
+ tmp = f' neighbor {peer} local-role {peer_config["local_role"]}'
+ if 'local_role_strict' in peer_config:
+ tmp += ' strict'
+ self.assertIn(tmp, frrconfig)
if 'cap_over' in peer_config:
self.assertIn(f' neighbor {peer} override-capability', frrconfig)
if 'passive' in peer_config:
@@ -307,7 +317,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time])
self.cli_set(base_path + ['parameters', 'no-suppress-duplicates'])
self.cli_set(base_path + ['parameters', 'reject-as-sets'])
- self.cli_set(base_path + ['parameters', 'route-reflector-allow-outbound-policy'])
+ self.cli_set(base_path + ['parameters', 'route-reflector-allow-outbound-policy'])
self.cli_set(base_path + ['parameters', 'shutdown'])
self.cli_set(base_path + ['parameters', 'suppress-fib-pending'])
@@ -380,6 +390,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]])
if 'local_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'no-prepend', 'replace-as'])
+ if 'local_role' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'local-role', peer_config["local_role"]])
+ if 'local_role_strict' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'local-role', peer_config["local_role"], 'strict'])
if 'cap_over' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'override-capability'])
if 'passive' in peer_config:
@@ -476,6 +490,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]])
if 'local_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'no-prepend', 'replace-as'])
+ if 'local_role' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'local-role', config["local_role"]])
+ if 'local_role_strict' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'local-role', config["local_role"], 'strict'])
if 'cap_over' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'override-capability'])
if 'passive' in config:
diff --git a/smoketest/scripts/cli/test_service_dhcp-relay.py b/smoketest/scripts/cli/test_service_dhcp-relay.py
index bbfd9e032..92f87c06c 100755
--- a/smoketest/scripts/cli/test_service_dhcp-relay.py
+++ b/smoketest/scripts/cli/test_service_dhcp-relay.py
@@ -82,6 +82,43 @@ class TestServiceDHCPRelay(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
+ def test_relay_interfaces(self):
+ max_size = '800'
+ hop_count = '20'
+ agents_packets = 'append'
+ servers = ['192.0.2.1', '192.0.2.2']
+ listen_iface = 'eth0'
+ up_iface = 'eth1'
+
+ self.cli_set(base_path + ['interface', up_iface])
+ self.cli_set(base_path + ['listen-interface', listen_iface])
+ # check validate() - backward interface plus listen_interface
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['interface'])
+
+ self.cli_set(base_path + ['upstream-interface', up_iface])
+
+ for server in servers:
+ self.cli_set(base_path + ['server', server])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check configured port
+ config = read_file(RELAY_CONF)
+
+ # Test configured relay interfaces
+ self.assertIn(f'-id {listen_iface}', config)
+ self.assertIn(f'-iu {up_iface}', config)
+
+ # Test relay servers
+ for server in servers:
+ self.assertIn(f' {server}', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 7546c2e3d..53c14c5b0 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -165,6 +165,35 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
self.assertEqual(conf['ip-pool']['gw-ip-address'], self._gateway)
+ def test_pppoe_server_client_ip_pool_name(self):
+ # Test configuration of named client pools
+ self.basic_config()
+
+ subnet = '192.0.2.0/24'
+ gateway = '192.0.2.1'
+ pool = 'VYOS'
+
+ subnet_name = f'{subnet},name'
+ gw_ip_prefix = f'{gateway}/24'
+
+ self.set(['client-ip-pool', 'name', pool, 'subnet', subnet])
+ self.set(['client-ip-pool', 'name', pool, 'gateway-address', gateway])
+ self.cli_delete(self._base_path + ['gateway-address'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+
+ # Validate configuration
+ self.assertEqual(conf['ip-pool'][subnet_name], pool)
+ self.assertEqual(conf['ip-pool']['gw-ip-address'], gateway)
+ self.assertEqual(conf['pppoe']['ip-pool'], pool)
+ self.assertEqual(conf['pppoe']['gw-ip-address'], gw_ip_prefix)
+
+
def test_pppoe_server_client_ipv6_pool(self):
# Test configuration of IPv6 client pools
self.basic_config()
diff --git a/src/conf_mode/dhcp_relay.py b/src/conf_mode/dhcp_relay.py
index 4de2ca2f3..7e702a446 100755
--- a/src/conf_mode/dhcp_relay.py
+++ b/src/conf_mode/dhcp_relay.py
@@ -18,9 +18,11 @@ import os
from sys import exit
+from vyos.base import Warning
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.template import render
+from vyos.base import Warning
from vyos.util import call
from vyos.util import dict_search
from vyos.xml import defaults
@@ -59,6 +61,19 @@ def verify(relay):
raise ConfigError('No DHCP relay server(s) configured.\n' \
'At least one DHCP relay server required.')
+ if 'interface' in relay:
+ Warning('DHCP relay interface is DEPRECATED - please use upstream-interface and listen-interface instead!')
+ if 'upstream_interface' in relay or 'listen_interface' in relay:
+ raise ConfigError('<interface> configuration is not compatible with upstream/listen interface')
+ else:
+ Warning('<interface> is going to be deprecated.\n' \
+ 'Please use <listen-interface> and <upstream-interface>')
+
+ if 'upstream_interface' in relay and 'listen_interface' not in relay:
+ raise ConfigError('No listen-interface configured')
+ if 'listen_interface' in relay and 'upstream_interface' not in relay:
+ raise ConfigError('No upstream-interface configured')
+
return None
def generate(relay):
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index c410258ee..4f05957fa 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -235,6 +235,11 @@ def verify(bgp):
raise ConfigError(f'Specified peer-group "{peer_group}" for '\
f'neighbor "{neighbor}" does not exist!')
+ if 'local_role' in peer_config:
+ #Ensure Local Role has only one value.
+ if len(peer_config['local_role']) > 1:
+ raise ConfigError(f'Only one local role can be specified for peer "{peer}"!')
+
if 'local_as' in peer_config:
if len(peer_config['local_as']) > 1:
raise ConfigError(f'Only one local-as number can be specified for peer "{peer}"!')
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
index 0418e8d82..dca713283 100755
--- a/src/conf_mode/qos.py
+++ b/src/conf_mode/qos.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2023 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
@@ -14,12 +14,14 @@
# 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 os
+
from sys import exit
from netifaces import interfaces
+from vyos.base import Warning
from vyos.config import Config
from vyos.configdict import dict_merge
-from vyos.configverify import verify_interface_exists
from vyos.qos import CAKE
from vyos.qos import DropTail
from vyos.qos import FairQueue
@@ -194,8 +196,6 @@ def verify(qos):
# we should check interface ingress/egress configuration after verifying that
# the policy name is used only once - this makes the logic easier!
for interface, interface_config in qos['interface'].items():
- verify_interface_exists(interface)
-
for direction in ['egress', 'ingress']:
# bail out early if shaper for given direction is not used at all
if direction not in interface_config:
@@ -229,6 +229,13 @@ def apply(qos):
return None
for interface, interface_config in qos['interface'].items():
+ if not os.path.exists(f'/sys/class/net/{interface}'):
+ # When shaper is bound to a dialup (e.g. PPPoE) interface it is
+ # possible that it is yet not availbale when to QoS code runs.
+ # Skip the configuration and inform the user
+ Warning(f'Interface "{interface}" does not exist!')
+ continue
+
for direction in ['egress', 'ingress']:
# bail out early if shaper for given direction is not used at all
if direction not in interface_config:
diff --git a/src/etc/commit/post-hooks.d/00vyos-sync b/src/etc/commit/post-hooks.d/00vyos-sync
new file mode 100755
index 000000000..e3bde3abb
--- /dev/null
+++ b/src/etc/commit/post-hooks.d/00vyos-sync
@@ -0,0 +1,7 @@
+#!/bin/sh
+# When power is lost right after a commit modified files, the
+# system can be corrupted and e.g. login is no longer possible.
+# Always sync files to the backend storage after a commit.
+# https://phabricator.vyos.net/T4975
+sync
+
diff --git a/src/services/api/graphql/generate/config_session_function.py b/src/services/api/graphql/generate/config_session_function.py
index fc0dd7a87..20fc7cc1d 100644
--- a/src/services/api/graphql/generate/config_session_function.py
+++ b/src/services/api/graphql/generate/config_session_function.py
@@ -8,8 +8,12 @@ def show_config(path: list[str], configFormat: typing.Optional[str]):
def show(path: list[str]):
pass
+def show_user_info(user: str):
+ pass
+
queries = {'show_config': show_config,
- 'show': show}
+ 'show': show,
+ 'show_user_info': show_user_info}
def save_config_file(fileName: typing.Optional[str]):
pass
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index 0b77b1433..b2aef9bd9 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -46,6 +46,17 @@ class Session:
except Exception:
self._op_mode_list = None
+ @staticmethod
+ def _get_config_dict(path=[], effective=False, key_mangling=None,
+ get_first_key=False, no_multi_convert=False,
+ no_tag_node_value_mangle=False):
+ config = Config()
+ return config.get_config_dict(path=path, effective=effective,
+ key_mangling=key_mangling,
+ get_first_key=get_first_key,
+ no_multi_convert=no_multi_convert,
+ no_tag_node_value_mangle=no_tag_node_value_mangle)
+
def show_config(self):
session = self._session
data = self._data
@@ -116,6 +127,22 @@ class Session:
return res
+ def show_user_info(self):
+ session = self._session
+ data = self._data
+
+ user_info = {}
+ user = data['user']
+ try:
+ info = self._get_config_dict(['system', 'login', 'user', user,
+ 'full-name'])
+ user_info['user'] = user
+ user_info['full_name'] = info.get('full-name', '')
+ except Exception as error:
+ raise error
+
+ return user_info
+
def system_status(self):
import api.graphql.session.composite.system_status as system_status