diff options
-rw-r--r-- | .github/workflows/add-pr-labels.yml | 1 | ||||
-rw-r--r-- | .github/workflows/build-package.yml | 17 | ||||
-rw-r--r-- | .github/workflows/chceck-pr-message.yml | 1 | ||||
-rw-r--r-- | .github/workflows/check-unused-imports.yml | 1 | ||||
-rw-r--r-- | .github/workflows/codeql.yml | 2 | ||||
-rw-r--r-- | .github/workflows/package-smoketest.yml | 106 | ||||
-rw-r--r-- | interface-definitions/system_option.xml.in | 13 | ||||
-rw-r--r-- | op-mode-definitions/restart-ssh.xml.in | 2 | ||||
-rw-r--r-- | op-mode-definitions/show-kernel-modules.xml.in | 20 | ||||
-rw-r--r-- | python/vyos/utils/kernel.py | 77 | ||||
-rwxr-xr-x | src/conf_mode/system_option.py | 14 | ||||
-rwxr-xr-x | src/op_mode/kernel_modules.py | 82 |
12 files changed, 316 insertions, 20 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/interface-definitions/system_option.xml.in b/interface-definitions/system_option.xml.in index ad423d9d1..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"> 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/op-mode-definitions/show-kernel-modules.xml.in b/op-mode-definitions/show-kernel-modules.xml.in new file mode 100644 index 000000000..28eb28212 --- /dev/null +++ b/op-mode-definitions/show-kernel-modules.xml.in @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="kernel"> + <properties> + <help>Show kernel information</help> + </properties> + <children> + <node name="modules"> + <properties> + <help>Show kernel modules</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/kernel_modules.py show</command> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/python/vyos/utils/kernel.py b/python/vyos/utils/kernel.py index 1f3bbdffe..847f80108 100644 --- a/python/vyos/utils/kernel.py +++ b/python/vyos/utils/kernel.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 @@ -36,3 +36,78 @@ def unload_kmod(k_mod): if os.path.exists(f'/sys/module/{module}'): if call(f'rmmod {module}') != 0: raise ConfigError(f'Unloading Kernel module {module} failed') + +def list_loaded_modules(): + """ Returns the list of currently loaded kernel modules """ + from os import listdir + return listdir('/sys/module/') + +def get_module_data(module: str): + """ Retrieves information about a module """ + from os import listdir + from os.path import isfile, dirname, basename, join + from vyos.utils.file import read_file + + def _get_file(path): + # Some files inside some modules are not readable at all, + # we just skip them. + try: + return read_file(path) + except PermissionError: + return None + + mod_path = join('/sys/module', module) + mod_data = {"name": module, "fields": {}, "parameters": {}} + + for f in listdir(mod_path): + if f in ["sections", "notes", "uevent"]: + # The uevent file is not readable + # and module build info and memory layout + # in notes and sections generally aren't useful + # for anything but kernel debugging. + pass + elif f == "drivers": + # Drivers are dir symlinks, + # we just list them + drivers = listdir(join(mod_path, f)) + if drivers: + mod_data["drivers"] = drivers + elif f == "holders": + # Holders (module that use this one) + # are always symlink to other modules. + # We only need the list. + holders = listdir(join(mod_path, f)) + if holders: + mod_data["holders"] = holders + elif f == "parameters": + # Many modules keep their configuration + # in the "parameters" subdir. + ppath = join(mod_path, "parameters") + ps = listdir(ppath) + for p in ps: + data = _get_file(join(ppath, p)) + if data: + mod_data["parameters"][p] = data + else: + # Everything else... + # There are standard fields like refcount and initstate, + # but many modules also keep custom information or settings + # in top-level fields. + # For now we don't separate well-known and custom fields. + if isfile(join(mod_path, f)): + data = _get_file(join(mod_path, f)) + if data: + mod_data["fields"][f] = data + else: + raise RuntimeError(f"Unexpected directory inside module {module}: {f}") + + return mod_data + +def lsmod(): + """ Returns information about all loaded modules. + Like lsmod(8), but more detailed. + """ + mods_data = [] + for m in list_loaded_modules(): + mods_data.append(get_module_data(m)) + return mods_data diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py index 2c31703e9..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 @@ -36,6 +39,7 @@ 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' @@ -157,8 +161,18 @@ 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: diff --git a/src/op_mode/kernel_modules.py b/src/op_mode/kernel_modules.py new file mode 100755 index 000000000..e381a1df7 --- /dev/null +++ b/src/op_mode/kernel_modules.py @@ -0,0 +1,82 @@ +#!/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/>. +# +# Purpose: +# Provides commands for retrieving information about kernel modules. + +import sys +import typing + +import vyos.opmode + + +lsmod_tmpl = """ +{% for m in modules -%} +Module: {{m.name}} + +{% if m.holders -%} +Holders: {{m.holders | join(", ")}} +{%- endif %} + +{% if m.drivers -%} +Drivers: {{m.drivers | join(", ")}} +{%- endif %} + +{% for k in m.fields -%} +{{k}}: {{m["fields"][k]}} +{% endfor %} +{% if m.parameters %} + +Parameters: + +{% for p in m.parameters -%} +{{p}}: {{m["parameters"][p]}} +{% endfor -%} +{% endif -%} + +------------- + +{% endfor %} +""" + +def _get_raw_data(module=None): + from vyos.utils.kernel import get_module_data, lsmod + + if module: + return [get_module_data(module)] + else: + return lsmod() + +def show(raw: bool, module: typing.Optional[str]): + from jinja2 import Template + + data = _get_raw_data(module=module) + + if raw: + return data + else: + t = Template(lsmod_tmpl) + output = t.render({"modules": data}) + return output + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) |