summaryrefslogtreecommitdiff
path: root/src/conf_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-xsrc/conf_mode/container.py4
-rwxr-xr-xsrc/conf_mode/interfaces_bonding.py4
-rwxr-xr-xsrc/conf_mode/interfaces_bridge.py40
-rwxr-xr-xsrc/conf_mode/interfaces_ethernet.py78
-rwxr-xr-xsrc/conf_mode/nat66.py28
-rwxr-xr-xsrc/conf_mode/policy.py8
-rw-r--r--src/conf_mode/protocols_openfabric.py145
-rwxr-xr-xsrc/conf_mode/service_dns_forwarding.py20
-rwxr-xr-xsrc/conf_mode/service_ntp.py20
-rwxr-xr-xsrc/conf_mode/system_option.py45
-rwxr-xr-xsrc/conf_mode/system_syslog.py20
11 files changed, 298 insertions, 114 deletions
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index ded370a7a..14387cbbf 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -421,6 +421,10 @@ def generate(container):
'driver': 'host-local'
}
}
+
+ if 'no_name_server' in network_config:
+ tmp['dns_enabled'] = False
+
for prefix in network_config['prefix']:
net = {'subnet': prefix, 'gateway': inc_ip(prefix, 1)}
tmp['subnets'].append(net)
diff --git a/src/conf_mode/interfaces_bonding.py b/src/conf_mode/interfaces_bonding.py
index 5e5d5fba1..bbbfb0385 100755
--- a/src/conf_mode/interfaces_bonding.py
+++ b/src/conf_mode/interfaces_bonding.py
@@ -25,6 +25,7 @@ from vyos.configdict import is_source_interface
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_dhcpv6
+from vyos.configverify import verify_eapol
from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vlan_config
@@ -73,7 +74,7 @@ def get_config(config=None):
else:
conf = Config()
base = ['interfaces', 'bonding']
- ifname, bond = get_interface_dict(conf, base)
+ ifname, bond = get_interface_dict(conf, base, with_pki=True)
# To make our own life easier transfor the list of member interfaces
# into a dictionary - we will use this to add additional information
@@ -196,6 +197,7 @@ def verify(bond):
verify_dhcpv6(bond)
verify_vrf(bond)
verify_mirror_redirect(bond)
+ verify_eapol(bond)
# use common function to verify VLAN configuration
verify_vlan_config(bond)
diff --git a/src/conf_mode/interfaces_bridge.py b/src/conf_mode/interfaces_bridge.py
index 7b2c1ee0b..637db442a 100755
--- a/src/conf_mode/interfaces_bridge.py
+++ b/src/conf_mode/interfaces_bridge.py
@@ -53,20 +53,22 @@ def get_config(config=None):
tmp = node_changed(conf, base + [ifname, 'member', 'interface'])
if tmp:
if 'member' in bridge:
- bridge['member'].update({'interface_remove' : tmp })
+ bridge['member'].update({'interface_remove': {t: {} for t in tmp}})
else:
- bridge.update({'member' : {'interface_remove' : tmp }})
- for interface in tmp:
- # When using VXLAN member interfaces that are configured for Single
- # VXLAN Device (SVD) we need to call the VXLAN conf-mode script to
- # re-create VLAN to VNI mappings if required, but only if the interface
- # is already live on the system - this must not be done on first commit
- if interface.startswith('vxlan') and interface_exists(interface):
- set_dependents('vxlan', conf, interface)
- # When using Wireless member interfaces we need to inform hostapd
- # to properly set-up the bridge
- elif interface.startswith('wlan') and interface_exists(interface):
- set_dependents('wlan', conf, interface)
+ bridge.update({'member': {'interface_remove': {t: {} for t in tmp}}})
+ for interface in tmp:
+ # When using VXLAN member interfaces that are configured for Single
+ # VXLAN Device (SVD) we need to call the VXLAN conf-mode script to
+ # re-create VLAN to VNI mappings if required, but only if the interface
+ # is already live on the system - this must not be done on first commit
+ if interface.startswith('vxlan') and interface_exists(interface):
+ set_dependents('vxlan', conf, interface)
+ _, vxlan = get_interface_dict(conf, ['interfaces', 'vxlan'], ifname=interface)
+ bridge['member']['interface_remove'].update({interface: vxlan})
+ # When using Wireless member interfaces we need to inform hostapd
+ # to properly set-up the bridge
+ elif interface.startswith('wlan') and interface_exists(interface):
+ set_dependents('wlan', conf, interface)
if dict_search('member.interface', bridge) is not None:
for interface in list(bridge['member']['interface']):
@@ -118,6 +120,16 @@ def get_config(config=None):
return bridge
def verify(bridge):
+ # to delete interface or remove a member interface VXLAN first need to check if
+ # VXLAN does not require to be a member of a bridge interface
+ if dict_search('member.interface_remove', bridge):
+ for iface, iface_config in bridge['member']['interface_remove'].items():
+ if iface.startswith('vxlan') and dict_search('parameters.neighbor_suppress', iface_config) != None:
+ raise ConfigError(
+ f'To detach interface {iface} from bridge you must first '
+ f'disable "neighbor-suppress" parameter in the VXLAN interface {iface}'
+ )
+
if 'deleted' in bridge:
return None
@@ -192,7 +204,7 @@ def apply(bridge):
try:
call_dependents()
except ConfigError:
- raise ConfigError('Error updating member interface configuration after changing bridge!')
+ raise ConfigError(f'Error updating member interface {interface} configuration after changing bridge!')
return None
diff --git a/src/conf_mode/interfaces_ethernet.py b/src/conf_mode/interfaces_ethernet.py
index afc48ead8..34ce7bc47 100755
--- a/src/conf_mode/interfaces_ethernet.py
+++ b/src/conf_mode/interfaces_ethernet.py
@@ -31,32 +31,20 @@ from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.configverify import verify_bond_bridge_member
-from vyos.configverify import verify_pki_certificate
-from vyos.configverify import verify_pki_ca_certificate
+from vyos.configverify import verify_eapol
from vyos.ethtool import Ethtool
from vyos.ifconfig import EthernetIf
from vyos.ifconfig import BondIf
-from vyos.pki import find_chain
-from vyos.pki import encode_certificate
-from vyos.pki import load_certificate
-from vyos.pki import wrap_private_key
-from vyos.template import render
from vyos.template import render_to_string
-from vyos.utils.process import call
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_to_paths_values
from vyos.utils.dict import dict_set
from vyos.utils.dict import dict_delete
-from vyos.utils.file import write_file
from vyos import ConfigError
from vyos import frr
from vyos import airbag
airbag.enable()
-# XXX: wpa_supplicant works on the source interface
-cfg_dir = '/run/wpa_supplicant'
-wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf'
-
def update_bond_options(conf: Config, eth_conf: dict) -> list:
"""
Return list of blocked options if interface is a bond member
@@ -277,23 +265,6 @@ def verify_allowedbond_changes(ethernet: dict):
f' on interface "{ethernet["ifname"]}".' \
f' Interface is a bond member')
-def verify_eapol(ethernet: dict):
- """
- Common helper function used by interface implementations to perform
- recurring validation of EAPoL configuration.
- """
- if 'eapol' not in ethernet:
- return
-
- if 'certificate' not in ethernet['eapol']:
- raise ConfigError('Certificate must be specified when using EAPoL!')
-
- verify_pki_certificate(ethernet, ethernet['eapol']['certificate'], no_password_protected=True)
-
- if 'ca_certificate' in ethernet['eapol']:
- for ca_cert in ethernet['eapol']['ca_certificate']:
- verify_pki_ca_certificate(ethernet, ca_cert)
-
def verify(ethernet):
if 'deleted' in ethernet:
return None
@@ -346,51 +317,10 @@ def verify_ethernet(ethernet):
verify_vlan_config(ethernet)
return None
-
def generate(ethernet):
- # render real configuration file once
- wpa_supplicant_conf = wpa_suppl_conf.format(**ethernet)
-
if 'deleted' in ethernet:
- # delete configuration on interface removal
- if os.path.isfile(wpa_supplicant_conf):
- os.unlink(wpa_supplicant_conf)
return None
- if 'eapol' in ethernet:
- ifname = ethernet['ifname']
-
- render(wpa_supplicant_conf, 'ethernet/wpa_supplicant.conf.j2', ethernet)
-
- cert_file_path = os.path.join(cfg_dir, f'{ifname}_cert.pem')
- cert_key_path = os.path.join(cfg_dir, f'{ifname}_cert.key')
-
- cert_name = ethernet['eapol']['certificate']
- pki_cert = ethernet['pki']['certificate'][cert_name]
-
- loaded_pki_cert = load_certificate(pki_cert['certificate'])
- loaded_ca_certs = {load_certificate(c['certificate'])
- for c in ethernet['pki']['ca'].values()} if 'ca' in ethernet['pki'] else {}
-
- cert_full_chain = find_chain(loaded_pki_cert, loaded_ca_certs)
-
- write_file(cert_file_path,
- '\n'.join(encode_certificate(c) for c in cert_full_chain))
- write_file(cert_key_path, wrap_private_key(pki_cert['private']['key']))
-
- if 'ca_certificate' in ethernet['eapol']:
- ca_cert_file_path = os.path.join(cfg_dir, f'{ifname}_ca.pem')
- ca_chains = []
-
- for ca_cert_name in ethernet['eapol']['ca_certificate']:
- pki_ca_cert = ethernet['pki']['ca'][ca_cert_name]
- loaded_ca_cert = load_certificate(pki_ca_cert['certificate'])
- ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
- ca_chains.append(
- '\n'.join(encode_certificate(c) for c in ca_full_chain))
-
- write_file(ca_cert_file_path, '\n'.join(ca_chains))
-
ethernet['frr_zebra_config'] = ''
if 'deleted' not in ethernet:
ethernet['frr_zebra_config'] = render_to_string('frr/evpn.mh.frr.j2', ethernet)
@@ -399,8 +329,6 @@ def generate(ethernet):
def apply(ethernet):
ifname = ethernet['ifname']
- # take care about EAPoL supplicant daemon
- eapol_action='stop'
e = EthernetIf(ifname)
if 'deleted' in ethernet:
@@ -408,10 +336,6 @@ def apply(ethernet):
e.remove()
else:
e.update(ethernet)
- if 'eapol' in ethernet:
- eapol_action='reload-or-restart'
-
- call(f'systemctl {eapol_action} wpa_supplicant-wired@{ifname}')
zebra_daemon = 'zebra'
# Save original configuration prior to starting any commit actions
diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py
index c44320f36..95dfae3a5 100755
--- a/src/conf_mode/nat66.py
+++ b/src/conf_mode/nat66.py
@@ -26,6 +26,7 @@ from vyos.utils.dict import dict_search
from vyos.utils.kernel import check_kmod
from vyos.utils.network import interface_exists
from vyos.utils.process import cmd
+from vyos.utils.process import run
from vyos.template import is_ipv6
from vyos import ConfigError
from vyos import airbag
@@ -48,6 +49,14 @@ def get_config(config=None):
if not conf.exists(base):
nat['deleted'] = ''
+ return nat
+
+ nat['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ # Remove dynamic firewall groups if present:
+ if 'dynamic_group' in nat['firewall_group']:
+ del nat['firewall_group']['dynamic_group']
return nat
@@ -99,22 +108,33 @@ def verify(nat):
if not interface_exists(interface_name):
Warning(f'Interface "{interface_name}" for destination NAT66 rule "{rule}" does not exist!')
+ if 'destination' in config and 'group' in config['destination']:
+ if len({'address_group', 'network_group', 'domain_group'} & set(config['destination']['group'])) > 1:
+ raise ConfigError('Only one address-group, network-group or domain-group can be specified')
+
return None
def generate(nat):
if not os.path.exists(nftables_nat66_config):
nat['first_install'] = True
- render(nftables_nat66_config, 'firewall/nftables-nat66.j2', nat, permission=0o755)
+ render(nftables_nat66_config, 'firewall/nftables-nat66.j2', nat)
+
+ # dry-run newly generated configuration
+ tmp = run(f'nft --check --file {nftables_nat66_config}')
+ if tmp > 0:
+ raise ConfigError('Configuration file errors encountered!')
+
return None
def apply(nat):
- if not nat:
- return None
-
check_kmod(k_mod)
cmd(f'nft --file {nftables_nat66_config}')
+
+ if not nat or 'deleted' in nat:
+ os.unlink(nftables_nat66_config)
+
call_dependents()
return None
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py
index 4df893ebf..a5963e72c 100755
--- a/src/conf_mode/policy.py
+++ b/src/conf_mode/policy.py
@@ -167,10 +167,10 @@ def verify(policy):
continue
for rule, rule_config in route_map_config['rule'].items():
- # Action 'deny' cannot be used with "continue"
- # FRR does not validate it T4827
- if rule_config['action'] == 'deny' and 'continue' in rule_config:
- raise ConfigError(f'rule {rule} "continue" cannot be used with action deny!')
+ # Action 'deny' cannot be used with "continue" or "on-match"
+ # FRR does not validate it T4827, T6676
+ if rule_config['action'] == 'deny' and ('continue' in rule_config or 'on_match' in rule_config):
+ raise ConfigError(f'rule {rule} "continue" or "on-match" cannot be used with action deny!')
# Specified community-list must exist
tmp = dict_search('match.community.community_list',
diff --git a/src/conf_mode/protocols_openfabric.py b/src/conf_mode/protocols_openfabric.py
new file mode 100644
index 000000000..8e8c50c06
--- /dev/null
+++ b/src/conf_mode/protocols_openfabric.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 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/>.
+
+from sys import exit
+
+from vyos.base import Warning
+from vyos.config import Config
+from vyos.configdict import node_changed
+from vyos.configverify import verify_interface_exists
+from vyos.template import render_to_string
+from vyos import ConfigError
+from vyos import frr
+from vyos import airbag
+
+airbag.enable()
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base_path = ['protocols', 'openfabric']
+
+ openfabric = conf.get_config_dict(base_path, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ # Remove per domain MPLS configuration - get a list of all changed Openfabric domains
+ # (removed and added) so that they will be properly rendered for the FRR config.
+ openfabric['domains_all'] = list(conf.list_nodes(' '.join(base_path) + f' domain') +
+ node_changed(conf, base_path + ['domain']))
+
+ # Get a list of all interfaces
+ openfabric['interfaces_all'] = []
+ for domain in openfabric['domains_all']:
+ interfaces_modified = list(node_changed(conf, base_path + ['domain', domain, 'interface']) +
+ conf.list_nodes(' '.join(base_path) + f' domain {domain} interface'))
+ openfabric['interfaces_all'].extend(interfaces_modified)
+
+ if not conf.exists(base_path):
+ openfabric.update({'deleted': ''})
+
+ return openfabric
+
+def verify(openfabric):
+ # bail out early - looks like removal from running config
+ if not openfabric or 'deleted' in openfabric:
+ return None
+
+ if 'net' not in openfabric:
+ raise ConfigError('Network entity is mandatory!')
+
+ # last byte in OpenFabric area address must be 0
+ tmp = openfabric['net'].split('.')
+ if int(tmp[-1]) != 0:
+ raise ConfigError('Last byte of OpenFabric network entity title must always be 0!')
+
+ if 'domain' not in openfabric:
+ raise ConfigError('OpenFabric domain name is mandatory!')
+
+ interfaces_used = []
+
+ for domain, domain_config in openfabric['domain'].items():
+ # If interface not set
+ if 'interface' not in domain_config:
+ raise ConfigError(f'Interface used for routing updates in OpenFabric "{domain}" is mandatory!')
+
+ for iface, iface_config in domain_config['interface'].items():
+ verify_interface_exists(openfabric, iface)
+
+ # interface can be activated only on one OpenFabric instance
+ if iface in interfaces_used:
+ raise ConfigError(f'Interface {iface} is already used in different OpenFabric instance!')
+
+ if 'address_family' not in iface_config or len(iface_config['address_family']) < 1:
+ raise ConfigError(f'Need to specify address family for the interface "{iface}"!')
+
+ # If md5 and plaintext-password set at the same time
+ if 'password' in iface_config:
+ if {'md5', 'plaintext_password'} <= set(iface_config['password']):
+ raise ConfigError(f'Can use either md5 or plaintext-password for password for the interface!')
+
+ if iface == 'lo' and 'passive' not in iface_config:
+ Warning('For loopback interface passive mode is implied!')
+
+ interfaces_used.append(iface)
+
+ # If md5 and plaintext-password set at the same time
+ password = 'domain_password'
+ if password in domain_config:
+ if {'md5', 'plaintext_password'} <= set(domain_config[password]):
+ raise ConfigError(f'Can use either md5 or plaintext-password for domain-password!')
+
+ return None
+
+def generate(openfabric):
+ if not openfabric or 'deleted' in openfabric:
+ return None
+
+ openfabric['frr_fabricd_config'] = render_to_string('frr/fabricd.frr.j2', openfabric)
+ return None
+
+def apply(openfabric):
+ openfabric_daemon = 'fabricd'
+
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+
+ frr_cfg.load_configuration(openfabric_daemon)
+ for domain in openfabric['domains_all']:
+ frr_cfg.modify_section(f'^router openfabric {domain}', stop_pattern='^exit', remove_stop_mark=True)
+
+ for interface in openfabric['interfaces_all']:
+ frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
+
+ if 'frr_fabricd_config' in openfabric:
+ frr_cfg.add_before(frr.default_add_before, openfabric['frr_fabricd_config'])
+
+ frr_cfg.commit_configuration(openfabric_daemon)
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/conf_mode/service_dns_forwarding.py b/src/conf_mode/service_dns_forwarding.py
index 70686534f..e3bdbc9f8 100755
--- a/src/conf_mode/service_dns_forwarding.py
+++ b/src/conf_mode/service_dns_forwarding.py
@@ -224,6 +224,18 @@ def get_config(config=None):
dns['authoritative_zones'].append(zone)
+ if 'zone_cache' in dns:
+ # convert refresh interval to sec:
+ for _, zone_conf in dns['zone_cache'].items():
+ if 'options' in zone_conf \
+ and 'refresh' in zone_conf['options']:
+
+ if 'on_reload' in zone_conf['options']['refresh']:
+ interval = 0
+ else:
+ interval = zone_conf['options']['refresh']['interval']
+ zone_conf['options']['refresh']['interval'] = interval
+
return dns
def verify(dns):
@@ -259,8 +271,16 @@ def verify(dns):
if not 'system_name_server' in dns:
print('Warning: No "system name-server" configured')
+ if 'zone_cache' in dns:
+ for name, conf in dns['zone_cache'].items():
+ if ('source' not in conf) \
+ or ('url' in conf['source'] and 'axfr' in conf['source']):
+ raise ConfigError(f'Invalid configuration for zone "{name}": '
+ f'Please select one source type "url" or "axfr".')
+
return None
+
def generate(dns):
# bail out early - looks like removal from running config
if not dns:
diff --git a/src/conf_mode/service_ntp.py b/src/conf_mode/service_ntp.py
index 83880fd72..32563aa0e 100755
--- a/src/conf_mode/service_ntp.py
+++ b/src/conf_mode/service_ntp.py
@@ -17,6 +17,7 @@
import os
from vyos.config import Config
+from vyos.config import config_dict_merge
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.configverify import verify_interface_exists
@@ -42,13 +43,21 @@ def get_config(config=None):
if not conf.exists(base):
return None
- ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, with_defaults=True)
+ ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
ntp['config_file'] = config_file
ntp['user'] = user_group
tmp = is_node_changed(conf, base + ['vrf'])
if tmp: ntp.update({'restart_required': {}})
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = conf.get_config_defaults(**ntp.kwargs, recursive=True)
+ # Only defined PTP default port, if PTP feature is in use
+ if 'ptp' not in ntp:
+ del default_values['ptp']
+
+ ntp = config_dict_merge(default_values, ntp)
return ntp
def verify(ntp):
@@ -87,6 +96,15 @@ def verify(ntp):
if ipv6_addresses > 1:
raise ConfigError(f'NTP Only admits one ipv6 value for listen-address parameter ')
+ if 'server' in ntp:
+ for host, server in ntp['server'].items():
+ if 'ptp' in server:
+ if 'ptp' not in ntp:
+ raise ConfigError('PTP must be enabled for the NTP service '\
+ f'before it can be used for server "{host}"')
+ else:
+ break
+
return None
def generate(ntp):
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py
index 52d0b7cda..a84572f83 100755
--- a/src/conf_mode/system_option.py
+++ b/src/conf_mode/system_option.py
@@ -19,11 +19,13 @@ import os
from sys import exit
from time import sleep
+
from vyos.config import Config
from vyos.configverify import verify_source_interface
from vyos.configverify import verify_interface_exists
from vyos.system import grub_util
from vyos.template import render
+from vyos.utils.cpu import get_cpus
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
from vyos.utils.kernel import check_kmod
@@ -35,6 +37,7 @@ from vyos.configdep import set_dependents
from vyos.configdep import call_dependents
from vyos import ConfigError
from vyos import airbag
+
airbag.enable()
curlrc_config = r'/etc/curlrc'
@@ -42,10 +45,8 @@ ssh_config = r'/etc/ssh/ssh_config.d/91-vyos-ssh-client-options.conf'
systemd_action_file = '/lib/systemd/system/ctrl-alt-del.target'
usb_autosuspend = r'/etc/udev/rules.d/40-usb-autosuspend.rules'
kernel_dynamic_debug = r'/sys/kernel/debug/dynamic_debug/control'
-time_format_to_locale = {
- '12-hour': 'en_US.UTF-8',
- '24-hour': 'en_GB.UTF-8'
-}
+time_format_to_locale = {'12-hour': 'en_US.UTF-8', '24-hour': 'en_GB.UTF-8'}
+
def get_config(config=None):
if config:
@@ -53,9 +54,9 @@ def get_config(config=None):
else:
conf = Config()
base = ['system', 'option']
- options = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- with_recursive_defaults=True)
+ options = conf.get_config_dict(
+ base, key_mangling=('-', '_'), get_first_key=True, with_recursive_defaults=True
+ )
if 'performance' in options:
# Update IPv4/IPv6 and sysctl options after tuned applied it's settings
@@ -64,6 +65,7 @@ def get_config(config=None):
return options
+
def verify(options):
if 'http_client' in options:
config = options['http_client']
@@ -71,7 +73,9 @@ def verify(options):
verify_interface_exists(options, config['source_interface'])
if {'source_address', 'source_interface'} <= set(config):
- raise ConfigError('Can not define both HTTP source-interface and source-address')
+ raise ConfigError(
+ 'Can not define both HTTP source-interface and source-address'
+ )
if 'source_address' in config:
if not is_addr_assigned(config['source_address']):
@@ -92,10 +96,20 @@ def verify(options):
address = config['source_address']
interface = config['source_interface']
if not is_intf_addr_assigned(interface, address):
- raise ConfigError(f'Address "{address}" not assigned on interface "{interface}"!')
+ raise ConfigError(
+ f'Address "{address}" not assigned on interface "{interface}"!'
+ )
+
+ if 'kernel' in options:
+ cpu_vendor = get_cpus()[0]['vendor_id']
+ if 'amd_pstate_driver' in options['kernel'] and cpu_vendor != 'AuthenticAMD':
+ raise ConfigError(
+ f'AMD pstate driver cannot be used with "{cpu_vendor}" CPU!'
+ )
return None
+
def generate(options):
render(curlrc_config, 'system/curlrc.j2', options)
render(ssh_config, 'system/ssh_config.j2', options)
@@ -107,10 +121,16 @@ def generate(options):
cmdline_options.append('mitigations=off')
if 'disable_power_saving' in options['kernel']:
cmdline_options.append('intel_idle.max_cstate=0 processor.max_cstate=1')
+ if 'amd_pstate_driver' in options['kernel']:
+ mode = options['kernel']['amd_pstate_driver']
+ cmdline_options.append(
+ f'initcall_blacklist=acpi_cpufreq_init amd_pstate={mode}'
+ )
grub_util.update_kernel_cmdline_options(' '.join(cmdline_options))
return None
+
def apply(options):
# System bootup beep
beep_service = 'vyos-beep.service'
@@ -149,7 +169,7 @@ def apply(options):
if 'performance' in options:
cmd('systemctl restart tuned.service')
# wait until daemon has started before sending configuration
- while (not is_systemd_service_running('tuned.service')):
+ while not is_systemd_service_running('tuned.service'):
sleep(0.250)
cmd('tuned-adm profile network-{performance}'.format(**options))
else:
@@ -164,9 +184,9 @@ def apply(options):
# Enable/diable root-partition-auto-resize SystemD service
if 'root_partition_auto_resize' in options:
- cmd('systemctl enable root-partition-auto-resize.service')
+ cmd('systemctl enable root-partition-auto-resize.service')
else:
- cmd('systemctl disable root-partition-auto-resize.service')
+ cmd('systemctl disable root-partition-auto-resize.service')
# Time format 12|24-hour
if 'time_format' in options:
@@ -186,6 +206,7 @@ def apply(options):
else:
write_file(kernel_dynamic_debug, f'module {module} -p')
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/system_syslog.py b/src/conf_mode/system_syslog.py
index 07fbb0734..eb2f02eb3 100755
--- a/src/conf_mode/system_syslog.py
+++ b/src/conf_mode/system_syslog.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 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
@@ -18,6 +18,7 @@ import os
from sys import exit
+from vyos.base import Warning
from vyos.config import Config
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
@@ -52,12 +53,29 @@ def get_config(config=None):
if syslog.from_defaults(['global']):
del syslog['global']
+ if (
+ 'global' in syslog
+ and 'preserve_fqdn' in syslog['global']
+ and conf.exists(['system', 'host-name'])
+ and conf.exists(['system', 'domain-name'])
+ ):
+ hostname = conf.return_value(['system', 'host-name'])
+ domain = conf.return_value(['system', 'domain-name'])
+ fqdn = f'{hostname}.{domain}'
+ syslog['global']['local_host_name'] = fqdn
+
return syslog
def verify(syslog):
if not syslog:
return None
+ if 'host' in syslog:
+ for host, host_options in syslog['host'].items():
+ if 'protocol' in host_options and host_options['protocol'] == 'udp':
+ if 'format' in host_options and 'octet_counted' in host_options['format']:
+ Warning(f'Syslog UDP transport for "{host}" should not use octet-counted format!')
+
verify_vrf(syslog)
def generate(syslog):