diff options
22 files changed, 249 insertions, 91 deletions
diff --git a/.github/workflows/add-pr-labels.yml b/.github/workflows/add-pr-labels.yml index adef2b857..a7ee8446f 100644 --- a/.github/workflows/add-pr-labels.yml +++ b/.github/workflows/add-pr-labels.yml @@ -8,6 +8,7 @@ on: - crux - equuleus - sagitta + - circinus permissions: pull-requests: write diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml deleted file mode 100644 index 0200aceb4..000000000 --- a/.github/workflows/build-package.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Debian Package Build -on: - pull_request: - branches: - - current - -jobs: - package-build: - runs-on: ubuntu-latest - container: - image: vyos/vyos-build:current - options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build Debian package - run: dpkg-buildpackage -uc -us -tc -b diff --git a/.github/workflows/chceck-pr-message.yml b/.github/workflows/chceck-pr-message.yml index a9548f909..c567a5934 100644 --- a/.github/workflows/chceck-pr-message.yml +++ b/.github/workflows/chceck-pr-message.yml @@ -8,6 +8,7 @@ on: - crux - equuleus - sagitta + - circinus types: [opened, synchronize, edited] permissions: diff --git a/.github/workflows/check-unused-imports.yml b/.github/workflows/check-unused-imports.yml index 835cc1180..322d4f3a8 100644 --- a/.github/workflows/check-unused-imports.yml +++ b/.github/workflows/check-unused-imports.yml @@ -5,6 +5,7 @@ on: - current - equuleus - sagitta + - circinus workflow_dispatch: permissions: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3b654c0db..12654e42e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,7 +2,7 @@ name: "Perform CodeQL Analysis" on: push: - branches: [ "current", "sagitta", "equuleus" ] + branches: [ "current", "sagitta", "equuleus", "circinus" ] pull_request: # The branches below must be a subset of the branches above branches: [ "current" ] diff --git a/.github/workflows/package-smoketest.yml b/.github/workflows/package-smoketest.yml new file mode 100644 index 000000000..49bd91669 --- /dev/null +++ b/.github/workflows/package-smoketest.yml @@ -0,0 +1,106 @@ +name: Package ISO Test + +on: + pull_request: + branches: + - current + +jobs: + build: + runs-on: ubuntu-24.04 + container: + image: vyos/vyos-build:current + options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged + env: + BUILD_BY: autobuild@vyos.net + DEBIAN_MIRROR: http://deb.debian.org/debian/ + outputs: + build_version: ${{ steps.version.outputs.build_version }} + steps: + - name: Clone vyos-build source code + uses: actions/checkout@v4 + with: + repository: vyos/vyos-build + - name: Clone vyos-1x source code + uses: actions/checkout@v4 + with: + repository: vyos/vyos-1x + path: packages/vyos-1x + fetch-tags: true # required for Debian package version + - name: Build vyos-1x package + run: | + cd packages/vyos-1x; dpkg-buildpackage -uc -us -tc -b + - name: Generate ISO version string + id: version + run: | + echo "build_version=1.5-integration-$(date -u +%Y%m%d%H%M)" >> $GITHUB_OUTPUT + - name: Build custom ISO image + run: | + sudo --preserve-env ./build-vyos-image \ + --architecture amd64 \ + --build-by $BUILD_BY \ + --debian-mirror $DEBIAN_MIRROR \ + --version ${{ steps.version.outputs.build_version }} \ + --build-type release \ + generic + - uses: actions/upload-artifact@v4 + with: + name: vyos-${{ steps.version.outputs.build_version }} + path: build/live-image-amd64.hybrid.iso + + cli-smoketests: + needs: build + runs-on: ubuntu-24.04 + container: + image: vyos/vyos-build:current + options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged + steps: + # We need the test script from vyos-build repo + - name: Clone vyos-build source code + uses: actions/checkout@v4 + with: + repository: vyos/vyos-build + - uses: actions/download-artifact@v4 + with: + name: vyos-${{ needs.build.outputs.build_version }} + path: build + - name: VyOS CLI smoketests + run: ls -al; ls -al build; sudo make test + + config-load-tests: + needs: build + runs-on: ubuntu-24.04 + container: + image: vyos/vyos-build:current + options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged + steps: + # We need the test script from vyos-build repo + - name: Clone vyos-build source code + uses: actions/checkout@v4 + with: + repository: vyos/vyos-build + - uses: actions/download-artifact@v4 + with: + name: vyos-${{ needs.build.outputs.build_version }} + path: build + - name: VyOS config tests + run: sudo make testc + + raid1-install-test: + needs: build + runs-on: ubuntu-24.04 + container: + image: vyos/vyos-build:current + options: --sysctl net.ipv6.conf.lo.disable_ipv6=0 --privileged + steps: + # We need the test script from vyos-build repo + - name: Clone vyos-build source code + uses: actions/checkout@v4 + with: + repository: vyos/vyos-build + - uses: actions/download-artifact@v4 + with: + name: vyos-${{ needs.build.outputs.build_version }} + path: build + - name: VyOS RAID1 install test + run: sudo make testraid diff --git a/data/configd-include.json b/data/configd-include.json index 633d898a5..b92d58c72 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -79,6 +79,7 @@ "service_router-advert.py", "service_salt-minion.py", "service_sla.py", +"service_snmp.py", "service_ssh.py", "service_tftp-server.py", "service_webproxy.py", diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2 index 343917fee..ee34f58fc 100644 --- a/data/templates/firewall/nftables.j2 +++ b/data/templates/firewall/nftables.j2 @@ -86,7 +86,7 @@ table ip vyos_filter { {% for prior, conf in ipv4.output.items() %} chain VYOS_OUTPUT_{{ prior }} { type filter hook output priority {{ prior }}; policy accept; -{% if global_options.state_policy is vyos_defined %} +{% if global_options.state_policy is vyos_defined and prior == 'filter' %} jump VYOS_STATE_POLICY {% endif %} {% if conf.rule is vyos_defined %} diff --git a/data/templates/system/40_usb_autosuspend.j2 b/data/templates/system/40_usb_autosuspend.j2 new file mode 100644 index 000000000..01ba86420 --- /dev/null +++ b/data/templates/system/40_usb_autosuspend.j2 @@ -0,0 +1,5 @@ +{% set autosuspend = "auto" %} +{% if disable_usb_autosuspend is vyos_defined %} +{% set autosuspend = "on" %} +{% endif %} +ACTION=="add", SUBSYSTEM=="usb", TEST=="power/control", ATTR{power/control}="{{ autosuspend }}" diff --git a/interface-definitions/system_option.xml.in b/interface-definitions/system_option.xml.in index fe517d17d..e78a53552 100644 --- a/interface-definitions/system_option.xml.in +++ b/interface-definitions/system_option.xml.in @@ -49,6 +49,19 @@ <valueless/> </properties> </leafNode> + <node name="debug"> + <properties> + <help>Dynamic debugging for kernel module</help> + </properties> + <children> + <leafNode name="wireguard"> + <properties> + <help>Dynamic debugging for Wireguard module</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> </children> </node> <leafNode name="keyboard-layout"> @@ -183,6 +196,12 @@ </properties> <defaultValue>12-hour</defaultValue> </leafNode> + <leafNode name="disable-usb-autosuspend"> + <properties> + <help>Disable autosuspend for all USB devices</help> + <valueless/> + </properties> + </leafNode> </children> </node> </children> diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in index 7849d6886..a2f040b2f 100644 --- a/interface-definitions/vpn_openconnect.xml.in +++ b/interface-definitions/vpn_openconnect.xml.in @@ -275,7 +275,7 @@ <help>SSL Certificate, SSL Key and CA</help> </properties> <children> - #include <include/pki/ca-certificate.xml.i> + #include <include/pki/ca-certificate-multi.xml.i> #include <include/pki/certificate-key.xml.i> </children> </node> diff --git a/op-mode-definitions/restart-ssh.xml.in b/op-mode-definitions/restart-ssh.xml.in index 6504cc18a..543cafc24 100644 --- a/op-mode-definitions/restart-ssh.xml.in +++ b/op-mode-definitions/restart-ssh.xml.in @@ -6,7 +6,7 @@ <properties> <help>Restart SSH service</help> </properties> - <command>if cli-shell-api existsActive service ssh; then sudo systemctl restart ssh.service; else echo "Service SSH not configured"; fi</command> + <command>if cli-shell-api existsActive service ssh; then sudo systemctl restart "ssh@*.service"; else echo "Service SSH not configured"; fi</command> </node> </children> </node> diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py index 70b6ea203..d518737ca 100644 --- a/python/vyos/config_mgmt.py +++ b/python/vyos/config_mgmt.py @@ -81,9 +81,11 @@ def save_config(target, json_out=None): if rc != 0: logger.critical(f'save config failed: {out}') -def unsaved_commits() -> bool: +def unsaved_commits(allow_missing_config=False) -> bool: if get_full_version_data()['boot_via'] == 'livecd': return False + if allow_missing_config and not os.path.exists(config_file): + return True tmp_save = '/tmp/config.running' save_config(tmp_save) ret = not cmp(tmp_save, config_file, shallow=False) diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py index bde1d9aec..383905814 100644 --- a/python/vyos/ifconfig/macsec.py +++ b/python/vyos/ifconfig/macsec.py @@ -66,7 +66,7 @@ class MACsecIf(Interface): cmd = 'ip macsec add {ifname} rx port 1 address'.format(**self.config) cmd += f' {peer_config["mac"]}' self._cmd(cmd) - # Add the rx-key to the address + # Add the encryption key to the address cmd += f' sa 0 pn 1 on key 01 {peer_config["key"]}' self._cmd(cmd) diff --git a/python/vyos/utils/system.py b/python/vyos/utils/system.py index 55813a5f7..f427032a4 100644 --- a/python/vyos/utils/system.py +++ b/python/vyos/utils/system.py @@ -1,4 +1,4 @@ -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2023-2024 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 @@ -98,3 +98,33 @@ def load_as_module(name: str, path: str): mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod + +def get_uptime_seconds(): + """ Returns system uptime in seconds """ + from re import search + from vyos.utils.file import read_file + + data = read_file("/proc/uptime") + seconds = search(r"([0-9\.]+)\s", data).group(1) + res = int(float(seconds)) + + return res + +def get_load_averages(): + """ Returns load averages for 1, 5, and 15 minutes as a dict """ + from re import search + from vyos.utils.file import read_file + from vyos.utils.cpu import get_core_count + + data = read_file("/proc/loadavg") + matches = search(r"\s*(?P<one>[0-9\.]+)\s+(?P<five>[0-9\.]+)\s+(?P<fifteen>[0-9\.]+)\s*", data) + + core_count = get_core_count() + + res = {} + res[1] = float(matches["one"]) / core_count + res[5] = float(matches["five"]) / core_count + res[15] = float(matches["fifteen"]) / core_count + + return res + diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index a4e6840ca..d73895b7f 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -225,11 +225,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): self.cli_commit() self.cli_delete(self._base_path + [interface, 'security', 'mka']) - # check validate() - tx-key required + # check validate() - key required with self.assertRaises(ConfigSessionError): self.cli_commit() - # check validate() - tx-key length must match cipher + # check validate() - key length must match cipher self.cli_set(self._base_path + [interface, 'security', 'static', 'key', tx_key_2]) with self.assertRaises(ConfigSessionError): self.cli_commit() @@ -239,7 +239,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): with self.assertRaises(ConfigSessionError): self.cli_commit() - # check validate() - enabled peer must have both rx-key and MAC defined + # check validate() - enabled peer must have both key and MAC defined self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER']) with self.assertRaises(ConfigSessionError): self.cli_commit() @@ -252,7 +252,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): self.cli_commit() self.cli_set(self._base_path + [interface, 'security', 'static', 'peer', 'TESTPEER', 'mac', peer_mac]) - # check validate() - peer rx-key length must match cipher + # check validate() - peer key length must match cipher self.cli_set(self._base_path + [interface, 'security', 'cipher', cipher2]) self.cli_set(self._base_path + [interface, 'security', 'static', 'key', tx_key_2]) with self.assertRaises(ConfigSessionError): diff --git a/src/conf_mode/interfaces_macsec.py b/src/conf_mode/interfaces_macsec.py index eb0ca9a8b..3ede4377a 100755 --- a/src/conf_mode/interfaces_macsec.py +++ b/src/conf_mode/interfaces_macsec.py @@ -103,9 +103,9 @@ def verify(macsec): # Logic to check static configuration if dict_search('security.static', macsec) != None: - # tx-key must be defined + # key must be defined if dict_search('security.static.key', macsec) == None: - raise ConfigError('Static MACsec tx-key must be defined.') + raise ConfigError('Static MACsec key must be defined.') tx_len = len(dict_search('security.static.key', macsec)) @@ -119,12 +119,12 @@ def verify(macsec): if 'peer' not in macsec['security']['static']: raise ConfigError('Must have at least one peer defined for static MACsec') - # For every enabled peer, make sure a MAC and rx-key is defined + # For every enabled peer, make sure a MAC and key is defined for peer, peer_config in macsec['security']['static']['peer'].items(): if 'disable' not in peer_config and ('mac' not in peer_config or 'key' not in peer_config): - raise ConfigError('Every enabled MACsec static peer must have a MAC address and rx-key defined.') + raise ConfigError('Every enabled MACsec static peer must have a MAC address and key defined!') - # check rx-key length against cipher suite + # check key length against cipher suite rx_len = len(peer_config['key']) if dict_search('security.cipher', macsec) == 'gcm-aes-128' and rx_len != GCM_AES_128_LEN: diff --git a/src/conf_mode/service_snmp.py b/src/conf_mode/service_snmp.py index 6565ffd60..6f025cc23 100755 --- a/src/conf_mode/service_snmp.py +++ b/src/conf_mode/service_snmp.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 @@ -26,10 +26,12 @@ from vyos.snmpv3_hashgen import plaintext_to_md5 from vyos.snmpv3_hashgen import plaintext_to_sha1 from vyos.snmpv3_hashgen import random from vyos.template import render -from vyos.utils.process import call -from vyos.utils.permission import chmod_755 +from vyos.utils.configfs import delete_cli_node +from vyos.utils.configfs import add_cli_node from vyos.utils.dict import dict_search from vyos.utils.network import is_addr_assigned +from vyos.utils.process import call +from vyos.utils.permission import chmod_755 from vyos.version import get_version_data from vyos import ConfigError from vyos import airbag @@ -192,12 +194,8 @@ def generate(snmp): return None if 'v3' in snmp: - # net-snmp is now regenerating the configuration file in the background - # thus we need to re-open and re-read the file as the content changed. - # After that we can no read the encrypted password from the config and - # replace the CLI plaintext password with its encrypted version. - os.environ['vyos_libexec_dir'] = '/usr/libexec/vyos' - + # SNMPv3 uses a hashed password. If CLI defines a plaintext password, + # we will hash it in the background and replace the CLI node! if 'user' in snmp['v3']: for user, user_config in snmp['v3']['user'].items(): if dict_search('auth.type', user_config) == 'sha': @@ -212,8 +210,9 @@ def generate(snmp): snmp['v3']['user'][user]['auth']['encrypted_password'] = tmp del snmp['v3']['user'][user]['auth']['plaintext_password'] - call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" auth encrypted-password "{tmp}" > /dev/null') - call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" auth plaintext-password > /dev/null') + cli_base = ['service', 'snmp', 'v3', 'user', user, 'auth'] + delete_cli_node(cli_base + ['plaintext-password']) + add_cli_node(cli_base + ['encrypted-password'], value=tmp) if dict_search('privacy.plaintext_password', user_config) is not None: tmp = hash(dict_search('privacy.plaintext_password', user_config), @@ -222,8 +221,9 @@ def generate(snmp): snmp['v3']['user'][user]['privacy']['encrypted_password'] = tmp del snmp['v3']['user'][user]['privacy']['plaintext_password'] - call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" privacy encrypted-password "{tmp}" > /dev/null') - call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" privacy plaintext-password > /dev/null') + cli_base = ['service', 'snmp', 'v3', 'user', user, 'privacy'] + delete_cli_node(cli_base + ['plaintext-password']) + add_cli_node(cli_base + ['encrypted-password'], value=tmp) # Write client config file render(config_file_client, 'snmp/etc.snmp.conf.j2', snmp) @@ -246,7 +246,7 @@ def apply(snmp): return None # start SNMP daemon - call(f'systemctl restart {systemd_service}') + call(f'systemctl reload-or-restart {systemd_service}') # Enable AgentX in FRR # This should be done for each daemon individually because common command diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py index a2e5db575..571ce55ec 100755 --- a/src/conf_mode/system_option.py +++ b/src/conf_mode/system_option.py @@ -24,6 +24,9 @@ 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.dict import dict_search +from vyos.utils.file import write_file +from vyos.utils.kernel import check_kmod from vyos.utils.process import cmd from vyos.utils.process import is_systemd_service_running from vyos.utils.network import is_addr_assigned @@ -35,6 +38,8 @@ airbag.enable() curlrc_config = r'/etc/curlrc' 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' @@ -85,6 +90,7 @@ def verify(options): def generate(options): render(curlrc_config, 'system/curlrc.j2', options) render(ssh_config, 'system/ssh_config.j2', options) + render(usb_autosuspend, 'system/40_usb_autosuspend.j2', options) cmdline_options = [] if 'kernel' in options: @@ -155,6 +161,19 @@ def apply(options): time_format = time_format_to_locale.get(options['time_format']) cmd(f'localectl set-locale LC_TIME={time_format}') + # Reload UDEV, required for USB auto suspend + cmd('udevadm control --reload-rules') + + # Enable/disable dynamic debugging for kernel modules + modules = ['wireguard'] + modules_enabled = dict_search('kernel.debug', options) or [] + for module in modules: + if module in modules_enabled: + check_kmod(module) + write_file(kernel_dynamic_debug, f'module {module} +p') + else: + write_file(kernel_dynamic_debug, f'module {module} -p') + if __name__ == '__main__': try: c = get_config() diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py index 8159fedea..42785134f 100755 --- a/src/conf_mode/vpn_openconnect.py +++ b/src/conf_mode/vpn_openconnect.py @@ -21,14 +21,17 @@ from vyos.base import Warning from vyos.config import Config from vyos.configverify import verify_pki_certificate from vyos.configverify import verify_pki_ca_certificate -from vyos.pki import wrap_certificate +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.utils.process import call +from vyos.utils.dict import dict_search +from vyos.utils.file import write_file from vyos.utils.network import check_port_availability -from vyos.utils.process import is_systemd_service_running from vyos.utils.network import is_listen_port_bind_service -from vyos.utils.dict import dict_search +from vyos.utils.process import call +from vyos.utils.process import is_systemd_service_running from vyos import ConfigError from passlib.hash import sha512_crypt from time import sleep @@ -142,7 +145,8 @@ def verify(ocserv): verify_pki_certificate(ocserv, ocserv['ssl']['certificate']) if 'ca_certificate' in ocserv['ssl']: - verify_pki_ca_certificate(ocserv, ocserv['ssl']['ca_certificate']) + for ca_cert in ocserv['ssl']['ca_certificate']: + verify_pki_ca_certificate(ocserv, ca_cert) # Check network settings if "network_settings" in ocserv: @@ -219,25 +223,36 @@ def generate(ocserv): if "ssl" in ocserv: cert_file_path = os.path.join(cfg_dir, 'cert.pem') cert_key_path = os.path.join(cfg_dir, 'cert.key') - ca_cert_file_path = os.path.join(cfg_dir, 'ca.pem') + if 'certificate' in ocserv['ssl']: cert_name = ocserv['ssl']['certificate'] pki_cert = ocserv['pki']['certificate'][cert_name] - with open(cert_file_path, 'w') as f: - f.write(wrap_certificate(pki_cert['certificate'])) + loaded_pki_cert = load_certificate(pki_cert['certificate']) + loaded_ca_certs = {load_certificate(c['certificate']) + for c in ocserv['pki']['ca'].values()} if 'ca' in ocserv['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)) if 'private' in pki_cert and 'key' in pki_cert['private']: - with open(cert_key_path, 'w') as f: - f.write(wrap_private_key(pki_cert['private']['key'])) + write_file(cert_key_path, wrap_private_key(pki_cert['private']['key'])) if 'ca_certificate' in ocserv['ssl']: - ca_name = ocserv['ssl']['ca_certificate'] - pki_ca_cert = ocserv['pki']['ca'][ca_name] + ca_cert_file_path = os.path.join(cfg_dir, 'ca.pem') + ca_chains = [] + + for ca_name in ocserv['ssl']['ca_certificate']: + pki_ca_cert = ocserv['pki']['ca'][ca_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)) - with open(ca_cert_file_path, 'w') as f: - f.write(wrap_certificate(pki_ca_cert['certificate'])) + write_file(ca_cert_file_path, '\n'.join(ca_chains)) # Render config render(ocserv_conf, 'ocserv/ocserv_config.j2', ocserv) diff --git a/src/op_mode/powerctrl.py b/src/op_mode/powerctrl.py index 6c8f802b5..cb4a175dd 100755 --- a/src/op_mode/powerctrl.py +++ b/src/op_mode/powerctrl.py @@ -110,7 +110,7 @@ def check_unsaved_config(): from vyos.config_mgmt import unsaved_commits from vyos.utils.boot import boot_configuration_success - if unsaved_commits() and boot_configuration_success(): + if unsaved_commits(allow_missing_config=True) and boot_configuration_success(): print("Warning: there are unsaved configuration changes!") print("Run 'save' command if you do not want to lose those changes after reboot/shutdown.") else: diff --git a/src/op_mode/uptime.py b/src/op_mode/uptime.py index 559eed24c..1c1a149ec 100755 --- a/src/op_mode/uptime.py +++ b/src/op_mode/uptime.py @@ -18,39 +18,14 @@ import sys import vyos.opmode -def _get_uptime_seconds(): - from re import search - from vyos.utils.file import read_file - - data = read_file("/proc/uptime") - seconds = search("([0-9\.]+)\s", data).group(1) - - return int(float(seconds)) - -def _get_load_averages(): - from re import search - from vyos.utils.cpu import get_core_count - from vyos.utils.process import cmd - - data = cmd("uptime") - matches = search(r"load average:\s*(?P<one>[0-9\.]+)\s*,\s*(?P<five>[0-9\.]+)\s*,\s*(?P<fifteen>[0-9\.]+)\s*", data) - - core_count = get_core_count() - - res = {} - res[1] = float(matches["one"]) / core_count - res[5] = float(matches["five"]) / core_count - res[15] = float(matches["fifteen"]) / core_count - - return res - def _get_raw_data(): + from vyos.utils.system import get_uptime_seconds, get_load_averages from vyos.utils.convert import seconds_to_human res = {} - res["uptime_seconds"] = _get_uptime_seconds() - res["uptime"] = seconds_to_human(_get_uptime_seconds(), separator=' ') - res["load_average"] = _get_load_averages() + uptime_seconds = get_uptime_seconds() + res["uptime"] = seconds_to_human(uptime_seconds, separator=' ') + res["load_average"] = get_load_averages() return res |