From c54decc406ba5ea8cff59f4d435112cf61b2fbea Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 21 Feb 2026 06:14:53 +0900 Subject: [PATCH 01/62] ruff: use single quotes for multiline strings --- ruff.toml | 10 ++++- src/boot/generate-hwids-section.py | 8 ++-- src/core/generate-bpf-delegate-configs.py | 4 +- src/test/generate-sym-test.py | 24 +++++------ src/ukify/ukify.py | 14 +++---- .../integration-test-wrapper.py | 40 +++++++++---------- 6 files changed, 53 insertions(+), 47 deletions(-) diff --git a/ruff.toml b/ruff.toml index 6c0ec6ceb8..d4b77fb1e7 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,6 +1,12 @@ target-version = "py39" line-length = 109 -lint.select = ["E", "F", "I", "UP"] +lint.select = ["E", "F", "I", "Q", "UP"] [format] -quote-style = "single" +# The formatter prefers double quotes for multiline quotes, +# Hence, let's make the formatter not change quotations. +quote-style = "preserve" + +[lint.flake8-quotes] +inline-quotes = "single" +multiline-quotes = "single" diff --git a/src/boot/generate-hwids-section.py b/src/boot/generate-hwids-section.py index cfe6aea739..df929af3b7 100755 --- a/src/boot/generate-hwids-section.py +++ b/src/boot/generate-hwids-section.py @@ -16,13 +16,13 @@ BYTES_PER_LINE = 16 hwids = ukify.parse_hwid_dir(Path(sys.argv[1])) print( - """/* SPDX-License-Identifier: LGPL-2.1-or-later */ + '''/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include // NOLINTNEXTLINE(misc-use-internal-linkage) const uint8_t hwids_section_data[] = { - """, + ''', end='', ) @@ -34,9 +34,9 @@ for i, b in enumerate(hwids): print('') print( - """}; + '''}; // NOLINTNEXTLINE(misc-use-internal-linkage) -const size_t hwids_section_len =""", +const size_t hwids_section_len =''', f'{len(hwids)};', ) diff --git a/src/core/generate-bpf-delegate-configs.py b/src/core/generate-bpf-delegate-configs.py index 200c913b8a..4e4a54322a 100755 --- a/src/core/generate-bpf-delegate-configs.py +++ b/src/core/generate-bpf-delegate-configs.py @@ -30,12 +30,12 @@ with open(header) as file: enumName = '' if output == 'doc': - print("""\ + print('''\ -""") +''') for line in file: line = line.strip() diff --git a/src/test/generate-sym-test.py b/src/test/generate-sym-test.py index b8d64d623f..aa6c916fc7 100755 --- a/src/test/generate-sym-test.py +++ b/src/test/generate-sym-test.py @@ -120,43 +120,43 @@ def process_source_file(file: IO[str]) -> None: continue -print("""/* SPDX-License-Identifier: LGPL-2.1-or-later */ +print('''/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include -""") +''') for header in sys.argv[3:]: with open(header, 'r') as f: if process_header_file(f): print('#include "{}"'.format(header.split('/')[-1])) -print(""" +print(''' /* We want to check deprecated symbols too, without complaining */ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -""") +''') -print(""" +print(''' struct symbol { const char *name; const void *symbol; }; -static struct symbol symbols_from_sym[] = {""") +static struct symbol symbols_from_sym[] = {''') with open(sys.argv[1], 'r') as f: process_sym_file(f) -print(""" {} -}, symbols_from_header[] = {""") +print(''' {} +}, symbols_from_header[] = {''') for header in sys.argv[3:]: with open(header, 'r') as f: print(process_header_file(f), end='') -print(""" {} -}, symbols_from_source[] = {""") +print(''' {} +}, symbols_from_source[] = {''') for dirpath, _, filenames in sorted(os.walk(sys.argv[2])): for filename in sorted(filenames): @@ -168,7 +168,7 @@ for dirpath, _, filenames in sorted(os.walk(sys.argv[2])): with p.open('rt') as f: process_source_file(f) -print(""" {} +print(''' {} }; static int sort_callback(const void *a, const void *b) { @@ -239,4 +239,4 @@ int main(void) { } return n_error == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -}""") +}''') diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index 03d475e5e7..11374f607d 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -599,7 +599,7 @@ class SystemdSbSign(SignTool): ) cmd = [ tool, - "sign", + 'sign', '--private-key', opts.sb_key, '--certificate', opts.sb_cert, *( @@ -1305,15 +1305,15 @@ def parse_efifw_dir(path: Path) -> bytes: return efifw_blob -STUB_SBAT = """\ +STUB_SBAT = '''\ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md uki,1,UKI,uki,1,https://uapi-group.org/specifications/specs/unified_kernel_image/ -""" +''' -ADDON_SBAT = """\ +ADDON_SBAT = '''\ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md uki-addon,1,UKI Addon,addon,1,https://www.freedesktop.org/software/systemd/man/latest/systemd-stub.html -""" +''' def make_uki(opts: UkifyConfig) -> None: @@ -2326,11 +2326,11 @@ def create_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser( description='Build and sign Unified Kernel Images', usage='\n ' - + textwrap.dedent("""\ + + textwrap.dedent('''\ ukify {b}build{e} [--linux=LINUX] [--initrd=INITRD] [options…] ukify {b}genkey{e} [options…] ukify {b}inspect{e} FILE… [options…] - """).format(b=Style.bold, e=Style.reset), + ''').format(b=Style.bold, e=Style.reset), allow_abbrev=False, add_help=False, epilog='\n '.join(('config file:', *config_example())), diff --git a/test/integration-tests/integration-test-wrapper.py b/test/integration-tests/integration-test-wrapper.py index 69b4333fee..38d0415a1b 100755 --- a/test/integration-tests/integration-test-wrapper.py +++ b/test/integration-tests/integration-test-wrapper.py @@ -21,13 +21,13 @@ from pathlib import Path from types import FrameType from typing import Optional -EMERGENCY_EXIT_DROPIN = """\ +EMERGENCY_EXIT_DROPIN = '''\ [Unit] Wants=emergency-exit.service -""" +''' -EMERGENCY_EXIT_SERVICE = """\ +EMERGENCY_EXIT_SERVICE = '''\ [Unit] DefaultDependencies=no Conflicts=shutdown.target @@ -38,7 +38,7 @@ FailureAction=exit [Service] ExecStart=false -""" +''' @dataclasses.dataclass(frozen=True) @@ -472,43 +472,43 @@ def main() -> None: name = args.name + (f'-{i}' if (i := os.getenv('MESON_TEST_ITERATION')) else '') dropin = textwrap.dedent( - """\ + '''\ [Service] StandardOutput=journal+console - """ + ''' ) if not shell: dropin += textwrap.dedent( - """ + ''' [Unit] SuccessAction=exit SuccessActionExitStatus=123 - """ + ''' ) if os.getenv('TEST_MATCH_SUBTEST'): dropin += textwrap.dedent( - f""" + f''' [Service] Environment=TEST_MATCH_SUBTEST={os.environ['TEST_MATCH_SUBTEST']} - """ + ''' ) if os.getenv('TEST_MATCH_TESTCASE'): dropin += textwrap.dedent( - f""" + f''' [Service] Environment=TEST_MATCH_TESTCASE={os.environ['TEST_MATCH_TESTCASE']} - """ + ''' ) if os.getenv('TEST_RUN_DFUZZER'): dropin += textwrap.dedent( - f""" + f''' [Service] Environment=TEST_RUN_DFUZZER={os.environ['TEST_RUN_DFUZZER']} - """ + ''' ) if os.getenv('TEST_JOURNAL_USE_TMP', '0') == '1': @@ -525,14 +525,14 @@ def main() -> None: if not sys.stdin.isatty(): dropin += textwrap.dedent( - """ + ''' [Unit] FailureAction=exit - """ + ''' ) elif not shell: dropin += textwrap.dedent( - """ + ''' [Unit] Wants=multi-user.target getty-pre.target Before=getty-pre.target @@ -546,17 +546,17 @@ def main() -> None: IgnoreSIGPIPE=no # bash ignores SIGTERM KillSignal=SIGHUP - """ + ''' ) if sys.stdin.isatty(): dropin += textwrap.dedent( - """ + ''' [Service] ExecStartPre=/usr/lib/systemd/tests/testdata/integration-test-setup.sh setup ExecStopPost=/usr/lib/systemd/tests/testdata/integration-test-setup.sh finalize StateDirectory=%N - """ + ''' ) if args.rtc: From 75f7304512307d5b4c7d59394c50cbb8191cf91b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 22 Feb 2026 04:36:23 +0900 Subject: [PATCH 02/62] ruff: do not warn about too long line and lambda assignment --- ruff.toml | 7 ++++++- src/test/generate-sym-test.py | 2 +- src/ukify/ukify.py | 20 +++++++++---------- .../integration-test-wrapper.py | 4 ++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/ruff.toml b/ruff.toml index d4b77fb1e7..35a4f5dc43 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,6 +1,11 @@ target-version = "py39" line-length = 109 -lint.select = ["E", "F", "I", "Q", "UP"] + +[lint] +select = ["E", "F", "I", "Q", "UP"] +# E501: line-too-long +# E731: lambda-assignment +ignore = ["E501", "E731"] [format] # The formatter prefers double quotes for multiline quotes, diff --git a/src/test/generate-sym-test.py b/src/test/generate-sym-test.py index aa6c916fc7..065ad14447 100755 --- a/src/test/generate-sym-test.py +++ b/src/test/generate-sym-test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later # -# ruff: noqa: E501 UP015 +# ruff: noqa: UP015 import os import re diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index 11374f607d..4a13019e24 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -1109,7 +1109,7 @@ def pe_add_sections(opts: UkifyConfig, uki: UKI, output: str) -> None: encoded = json.dumps(j).encode() if len(encoded) > section.SizeOfRawData: raise PEError( - f'Not enough space in existing section .pcrsig of size {section.SizeOfRawData} to append new data of size {len(encoded)}' # noqa: E501 + f'Not enough space in existing section .pcrsig of size {section.SizeOfRawData} to append new data of size {len(encoded)}' ) section.Misc_VirtualSize = len(encoded) # bytes(n) results in an array of n zeroes @@ -1498,7 +1498,7 @@ def make_uki(opts: UkifyConfig) -> None: if names[0] != '.profile': raise ValueError( - f'Expected .profile section as first valid section in PE profile binary {profile} but got {names[0]}' # noqa: E501 + f'Expected .profile section as first valid section in PE profile binary {profile} but got {names[0]}' ) if names.count('.profile') > 1: @@ -1688,7 +1688,7 @@ def generate_keys(opts: UkifyConfig) -> None: if not work: raise ValueError( - 'genkey: --secureboot-private-key=/--secureboot-certificate= or --pcr-private-key/--pcr-public-key must be specified' # noqa: E501 + 'genkey: --secureboot-private-key=/--secureboot-certificate= or --pcr-private-key/--pcr-public-key must be specified' ) @@ -1879,7 +1879,7 @@ class ConfigItem: elif self.type: conv = self.type else: - conv = lambda s: s # noqa: E731 + conv = lambda s: s # This is a bit ugly, but --initrd and --devicetree-auto are the only options # with multiple args on the command line and a space-separated list in the @@ -2116,14 +2116,14 @@ CONFIG_ITEMS = [ ConfigItem( '--secureboot-private-key', dest='sb_key', - help='required by --signtool=sbsign|systemd-sbsign. Path to key file or engine/provider designation for SB signing', # noqa: E501 + help='required by --signtool=sbsign|systemd-sbsign. Path to key file or engine/provider designation for SB signing', config_key='UKI/SecureBootPrivateKey', ), ConfigItem( '--secureboot-certificate', dest='sb_cert', help=( - 'required by --signtool=sbsign. sbsign needs a path to certificate file or engine-specific designation for SB signing' # noqa: E501 + 'required by --signtool=sbsign. sbsign needs a path to certificate file or engine-specific designation for SB signing' ), config_key='UKI/SecureBootCertificate', ), @@ -2132,7 +2132,7 @@ CONFIG_ITEMS = [ dest='sb_certdir', default='/etc/pki/pesign', help=( - 'required by --signtool=pesign. Path to nss certificate database directory for PE signing. Default is /etc/pki/pesign' # noqa: E501 + 'required by --signtool=pesign. Path to nss certificate database directory for PE signing. Default is /etc/pki/pesign' ), config_key='UKI/SecureBootCertificateDir', config_push=ConfigItem.config_set, @@ -2141,7 +2141,7 @@ CONFIG_ITEMS = [ '--secureboot-certificate-name', dest='sb_cert_name', help=( - 'required by --signtool=pesign. pesign needs a certificate nickname of nss certificate database entry to use for PE signing' # noqa: E501 + 'required by --signtool=pesign. pesign needs a certificate nickname of nss certificate database entry to use for PE signing' ), config_key='UKI/SecureBootCertificateName', ), @@ -2458,7 +2458,7 @@ def finalize_options(opts: argparse.Namespace) -> None: # both param given, infer sbsign and in case it was given, ensure signtool=sbsign if opts.signtool and opts.signtool not in ('sbsign', 'systemd-sbsign'): raise ValueError( - f'Cannot provide --signtool={opts.signtool} with --secureboot-private-key= and --secureboot-certificate=' # noqa: E501 + f'Cannot provide --signtool={opts.signtool} with --secureboot-private-key= and --secureboot-certificate=' ) if not opts.signtool: opts.signtool = 'sbsign' @@ -2478,7 +2478,7 @@ def finalize_options(opts: argparse.Namespace) -> None: if opts.sign_kernel and not opts.sb_key and not opts.sb_cert_name: raise ValueError( - '--sign-kernel requires either --secureboot-private-key= and --secureboot-certificate= (for sbsign) or --secureboot-certificate-name= (for pesign) to be specified' # noqa: E501 + '--sign-kernel requires either --secureboot-private-key= and --secureboot-certificate= (for sbsign) or --secureboot-certificate-name= (for pesign) to be specified' ) opts.profile = resolve_at_path(opts.profile) diff --git a/test/integration-tests/integration-test-wrapper.py b/test/integration-tests/integration-test-wrapper.py index 38d0415a1b..43d11e0e2e 100755 --- a/test/integration-tests/integration-test-wrapper.py +++ b/test/integration-tests/integration-test-wrapper.py @@ -589,7 +589,7 @@ def main() -> None: *(['--forward-journal', journal_file] if journal_file else []), *( [ - '--credential', f'systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)}', # noqa: E501 + '--credential', f'systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)}', '--credential', f'systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}', ] if not sys.stdin.isatty() @@ -700,7 +700,7 @@ def main() -> None: iter = os.environ['GITHUB_RUN_ATTEMPT'] runner = os.environ['TEST_RUNNER'] artifact = ( - f'ci-{wf}-{id}-{iter}-{summary.distribution}-{summary.release}-{runner}-failed-test-journals' # noqa: E501 + f'ci-{wf}-{id}-{iter}-{summary.distribution}-{summary.release}-{runner}-failed-test-journals' ) ops += [f'gh run download {id} --name {artifact} -D ci/{artifact}'] journal_file = Path(f'ci/{artifact}/test/journal/{name}.journal') From f74118be4f1588fda57f74fa3259dd0b5d980daf Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:43:03 +0900 Subject: [PATCH 03/62] ycm: apply "ruff format" and "ruff check --fix" --- .ycm_extra_conf.py | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index 46b5ecd9d9..c64cc48a7b 100755 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -43,11 +43,11 @@ used by the 'systemd' project. import glob import os + import ycm_core - -SOURCE_EXTENSIONS = (".C", ".cpp", ".cxx", ".cc", ".c", ".m", ".mm") -HEADER_EXTENSIONS = (".H", ".h", ".hxx", ".hpp", ".hh") +SOURCE_EXTENSIONS = ('.C', '.cpp', '.cxx', '.cc', '.c', '.m', '.mm') +HEADER_EXTENSIONS = ('.H', '.h', '.hxx', '.hpp', '.hh') def DirectoryOfThisScript(): @@ -69,19 +69,18 @@ def GuessBuildDirectory(): containing '.ninja_log' file two levels above the current directory; returns this single directory only if there is one candidate. """ - result = os.path.join(DirectoryOfThisScript(), "build") + result = os.path.join(DirectoryOfThisScript(), 'build') if os.path.exists(result): return result - result = glob.glob(os.path.join(DirectoryOfThisScript(), - "..", "..", "*", ".ninja_log")) + result = glob.glob(os.path.join(DirectoryOfThisScript(), '..', '..', '*', '.ninja_log')) if not result: - return "" + return '' if 1 != len(result): - return "" + return '' return os.path.split(result[0])[0] @@ -106,9 +105,7 @@ def TraverseByDepth(root, include_extensions): # print(subdirs) if include_extensions: get_ext = os.path.splitext - subdir_extensions = { - get_ext(f)[-1] for f in file_list if get_ext(f)[-1] - } + subdir_extensions = {get_ext(f)[-1] for f in file_list if get_ext(f)[-1]} if subdir_extensions & include_extensions: result.add(root_dir) else: @@ -119,11 +116,11 @@ def TraverseByDepth(root, include_extensions): return result -_project_src_dir = os.path.join(DirectoryOfThisScript(), "src") -_include_dirs_set = TraverseByDepth(_project_src_dir, frozenset({".h"})) +_project_src_dir = os.path.join(DirectoryOfThisScript(), 'src') +_include_dirs_set = TraverseByDepth(_project_src_dir, frozenset({'.h'})) flags = [ - "-x", - "c" + '-x', + 'c', # The following flags are partially redundant due to the existence of # 'compile_commands.json'. # '-Wall', @@ -135,7 +132,7 @@ flags = [ ] for include_dir in _include_dirs_set: - flags.append("-I" + include_dir) + flags.append('-I' + include_dir) # Set this to the absolute path to the folder (NOT the file!) containing the # compile_commands.json file to use that instead of 'flags'. See here for @@ -165,13 +162,13 @@ def MakeRelativePathsInFlagsAbsolute(flags, working_directory): return list(flags) new_flags = [] make_next_absolute = False - path_flags = ["-isystem", "-I", "-iquote", "--sysroot="] + path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] for flag in flags: new_flag = flag if make_next_absolute: make_next_absolute = False - if not flag.startswith("/"): + if not flag.startswith('/'): new_flag = os.path.join(working_directory, flag) for path_flag in path_flags: @@ -180,7 +177,7 @@ def MakeRelativePathsInFlagsAbsolute(flags, working_directory): break if flag.startswith(path_flag): - path = flag[len(path_flag):] + path = flag[len(path_flag) :] new_flag = path_flag + os.path.join(working_directory, path) break @@ -213,8 +210,7 @@ def GetCompilationInfoForFile(filename): for extension in SOURCE_EXTENSIONS: replacement_file = basename + extension if os.path.exists(replacement_file): - compilation_info = \ - database.GetCompilationInfoForFile(replacement_file) + compilation_info = database.GetCompilationInfoForFile(replacement_file) if compilation_info.compiler_flags_: return compilation_info return None @@ -238,13 +234,14 @@ def FlagsForFile(filename, **kwargs): final_flags = MakeRelativePathsInFlagsAbsolute( compilation_info.compiler_flags_, - compilation_info.compiler_working_dir_) + compilation_info.compiler_working_dir_, + ) else: relative_to = DirectoryOfThisScript() final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) return { - "flags": final_flags, - "do_cache": True + 'flags': final_flags, + 'do_cache': True, } From a3dc3dc61b3aa019d0f2cf3a5274a73096d3edd6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:47:18 +0900 Subject: [PATCH 04/62] hwdb/acpi-update: apply "ruff format" --- hwdb.d/acpi-update.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hwdb.d/acpi-update.py b/hwdb.d/acpi-update.py index 41670b32bb..66a11d8502 100755 --- a/hwdb.d/acpi-update.py +++ b/hwdb.d/acpi-update.py @@ -5,6 +5,7 @@ from csv import reader # pylint: disable=consider-using-with + def read_table(filename): table = list(reader(open(filename, newline=''))) table = table[1:] # Skip header @@ -15,6 +16,7 @@ def read_table(filename): # a mistake, strip it. print(f'\nacpi:{row[1].strip()}*:\n ID_VENDOR_FROM_DATABASE={row[0].strip()}') + print('''\ # This file is part of systemd. # From 1c98dff834fe72dce12c812206ab0a9247bf28b3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 21 Feb 2026 03:29:00 +0900 Subject: [PATCH 05/62] hwdb/ids_parser: apply "ruff format" and "ruff check --fix" --- hwdb.d/ids_parser.py | 115 +++++++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 38 deletions(-) diff --git a/hwdb.d/ids_parser.py b/hwdb.d/ids_parser.py index ed2c615508..7efd934f46 100755 --- a/hwdb.d/ids_parser.py +++ b/hwdb.d/ids_parser.py @@ -3,11 +3,24 @@ import re import sys -from pyparsing import (Word, White, Literal, Regex, - LineEnd, SkipTo, - ZeroOrMore, OneOrMore, Combine, Optional, Suppress, - Group, ParserElement, - stringEnd, pythonStyleComment) + +from pyparsing import ( + Combine, + Group, + LineEnd, + Literal, + OneOrMore, + Optional, + ParserElement, + Regex, + SkipTo, + Suppress, + White, + Word, + ZeroOrMore, + pythonStyleComment, + stringEnd, +) EOL = LineEnd().suppress() NUM1 = Word('0123456789abcdefABCDEF', exact=1) @@ -22,7 +35,9 @@ text_eol = lambda name: Regex(r'[^\n]+')(name) + EOL ParserElement.setDefaultWhitespaceChars(' \n') + def klass_grammar(): + # fmt: off klass_line = Literal('C ').suppress() + NUM2('klass') + text_eol('text') subclass_line = TAB + NUM2('subclass') + text_eol('text') protocol_line = TAB + TAB + NUM2('protocol') + text_eol('name') @@ -32,9 +47,12 @@ def klass_grammar(): klass = (klass_line('KLASS') - ZeroOrMore(Group(subclass)('SUBCLASSES*') ^ COMMENTLINE.suppress())) + # fmt: on return klass + def usb_ids_grammar(): + # fmt: off vendor_line = NUM4('vendor') + text_eol('text') device_line = TAB + NUM4('device') + text_eol('text') interface_line = TAB + TAB + NUM4('interface') + NUM4('interface2') + text_eol('text') @@ -55,11 +73,14 @@ def usb_ids_grammar(): grammar = OneOrMore(Group(vendor)('VENDORS*') ^ Group(klass)('CLASSES*') ^ other_group.suppress() ^ commentgroup) + stringEnd() + # fmt: on grammar.parseWithTabs() return grammar + def pci_ids_grammar(): + # fmt: off vendor_line = NUM4('vendor') + text_eol('text') device_line = TAB + NUM4('device') + text_eol('text') subvendor_line = TAB + TAB + NUM4('a') + White(' ') + NUM4('b') + text_eol('name') @@ -75,11 +96,14 @@ def pci_ids_grammar(): grammar = OneOrMore(Group(vendor)('VENDORS*') ^ Group(klass)('CLASSES*') ^ commentgroup) + stringEnd() + # fmt: on grammar.parseWithTabs() return grammar + def sdio_ids_grammar(): + # fmt: off vendor_line = NUM4('vendor') + text_eol('text') device_line = TAB + NUM4('device') + text_eol('text') vendor = (vendor_line('VENDOR') + @@ -91,11 +115,14 @@ def sdio_ids_grammar(): grammar = OneOrMore(Group(vendor)('VENDORS*') ^ Group(klass)('CLASSES*') ^ commentgroup) + stringEnd() + # fmt: on grammar.parseWithTabs() return grammar + def oui_grammar(type): + # fmt: off prefix_line = (Combine(NUM2 - Suppress('-') - NUM2 - Suppress('-') - NUM2)('prefix') - Literal('(hex)') - text_eol('text')) if type == 'small': @@ -115,18 +142,23 @@ def oui_grammar(type): grammar = (Literal('OUI') + text_eol('header') + text_eol('header') + text_eol('header') + EMPTYLINE + OneOrMore(Group(vendor)('VENDORS*')) + stringEnd()) + # fmt: on grammar.parseWithTabs() return grammar def header(file, *sources): - print('''\ + sep = ' ' if len(sources) == 1 else '\n# ' + joined = sep + sep.join(sources) + print( + f'''\ # This file is part of systemd. # -# Data imported from:{}{}'''.format(' ' if len(sources) == 1 else '\n# ', - '\n# '.join(sources)), - file=file) +# Data imported from:{joined}''', + file=file, + ) + def add_item(items, key, value): if key in items: @@ -134,6 +166,7 @@ def add_item(items, key, value): else: items[key] = value + def usb_vendor_model(p): items = {} @@ -147,19 +180,19 @@ def usb_vendor_model(p): text = vendor_dev.text.strip() add_item(items, (vendor, device), text) - with open('20-usb-vendor-model.hwdb', 'wt') as out: + with open('20-usb-vendor-model.hwdb', 'w') as out: header(out, 'http://www.linux-usb.org/usb.ids') for key in sorted(items): if len(key) == 1: p, n = 'usb:v{}*', 'VENDOR' else: - p, n = 'usb:v{}p{}*', 'MODEL', - print('', p.format(*key), - f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) + p, n = 'usb:v{}p{}*', 'MODEL' + print('', p.format(*key), f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) print(f'Wrote {out.name}') + def usb_classes(p): items = {} @@ -182,7 +215,7 @@ def usb_classes(p): if klass != '00' and not re.match(r'(\?|None|Unused)\s*$', text): add_item(items, (klass, subclass, protocol), text) - with open('20-usb-classes.hwdb', 'wt') as out: + with open('20-usb-classes.hwdb', 'w') as out: header(out, 'http://www.linux-usb.org/usb.ids') for key in sorted(items): @@ -192,11 +225,11 @@ def usb_classes(p): p, n = 'usb:v*p*d*dc{}dsc{}*', 'SUBCLASS' else: p, n = 'usb:v*p*d*dc{}dsc{}dp{}*', 'PROTOCOL' - print('', p.format(*key), - f' ID_USB_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) + print('', p.format(*key), f' ID_USB_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) print(f'Wrote {out.name}') + def pci_vendor_model(p): items = {} @@ -215,12 +248,12 @@ def pci_vendor_model(p): sub_model = subvendor_group.b.upper() sub_text = subvendor_group.name.strip() if sub_text.startswith(text): - sub_text = sub_text[len(text):].lstrip() + sub_text = sub_text[len(text) :].lstrip() if sub_text: sub_text = f' ({sub_text})' add_item(items, (vendor, device, sub_vendor, sub_model), text + sub_text) - with open('20-pci-vendor-model.hwdb', 'wt') as out: + with open('20-pci-vendor-model.hwdb', 'w') as out: header(out, 'http://pci-ids.ucw.cz/v2.2/pci.ids') for key in sorted(items): @@ -230,11 +263,11 @@ def pci_vendor_model(p): p, n = 'pci:v0000{}d0000{}*', 'MODEL' else: p, n = 'pci:v0000{}d0000{}sv0000{}sd0000{}*', 'MODEL' - print('', p.format(*key), - f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) + print('', p.format(*key), f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) print(f'Wrote {out.name}') + def pci_classes(p): items = {} @@ -253,7 +286,7 @@ def pci_classes(p): text = protocol_group.name.strip() add_item(items, (klass, subclass, protocol), text) - with open('20-pci-classes.hwdb', 'wt') as out: + with open('20-pci-classes.hwdb', 'w') as out: header(out, 'http://pci-ids.ucw.cz/v2.2/pci.ids') for key in sorted(items): @@ -263,11 +296,11 @@ def pci_classes(p): p, n = 'pci:v*d*sv*sd*bc{}sc{}*', 'SUBCLASS' else: p, n = 'pci:v*d*sv*sd*bc{}sc{}i{}*', 'INTERFACE' - print('', p.format(*key), - f' ID_PCI_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) + print('', p.format(*key), f' ID_PCI_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) print(f'Wrote {out.name}') + def sdio_vendor_model(p): items = {} @@ -281,7 +314,7 @@ def sdio_vendor_model(p): text = device_group.text.strip() add_item(items, (vendor, device), text) - with open('20-sdio-vendor-model.hwdb', 'wt') as out: + with open('20-sdio-vendor-model.hwdb', 'w') as out: header(out, 'hwdb.d/sdio.ids') for key in sorted(items): @@ -289,11 +322,11 @@ def sdio_vendor_model(p): p, n = 'sdio:c*v{}*', 'VENDOR' else: p, n = 'sdio:c*v{}d{}*', 'MODEL' - print('', p.format(*key), - f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) + print('', p.format(*key), f' ID_{n}_FROM_DATABASE={items[key]}', sep='\n', file=out) print(f'Wrote {out.name}') + def sdio_classes(p): items = {} @@ -302,16 +335,21 @@ def sdio_classes(p): text = klass_group.text.strip() add_item(items, klass, text) - with open('20-sdio-classes.hwdb', 'wt') as out: + with open('20-sdio-classes.hwdb', 'w') as out: header(out, 'hwdb.d/sdio.ids') for klass in sorted(items): - print(f'', - f'sdio:c{klass}v*d*', - f' ID_SDIO_CLASS_FROM_DATABASE={items[klass]}', sep='\n', file=out) + print( + '', + f'sdio:c{klass}v*d*', + f' ID_SDIO_CLASS_FROM_DATABASE={items[klass]}', + sep='\n', + file=out, + ) print(f'Wrote {out.name}') + # MAC Address Block Large/Medium/Small # Large MA-L 24/24 bit (OUI) # Medium MA-M 28/20 bit (OUI prefix owned by IEEE) @@ -338,19 +376,20 @@ def oui(p1, p2, p3): key = prefix + start if end else prefix add_item(items, key, text) - with open('20-OUI.hwdb', 'wt') as out: - header(out, - 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-L&format=txt', - 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-M&format=txt', - 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-S&format=txt') + with open('20-OUI.hwdb', 'w') as out: + header( + out, + 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-L&format=txt', + 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-M&format=txt', + 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-S&format=txt', + ) for pattern in sorted(items): - print(f'', - f'OUI:{pattern}*', - f' ID_OUI_FROM_DATABASE={items[pattern]}', sep='\n', file=out) + print('', f'OUI:{pattern}*', f' ID_OUI_FROM_DATABASE={items[pattern]}', sep='\n', file=out) print(f'Wrote {out.name}') + if __name__ == '__main__': args = sys.argv[1:] From 4fa9302fa48380727b025d5774f1717d6b054f47 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:48:13 +0900 Subject: [PATCH 06/62] hwdb/parse_hwdb: apply "ruff format" and "ruff check --fix" --- hwdb.d/parse_hwdb.py | 379 ++++++++++++++++++++++++------------------- 1 file changed, 210 insertions(+), 169 deletions(-) diff --git a/hwdb.d/parse_hwdb.py b/hwdb.d/parse_hwdb.py index e70b0ff04e..5b9d528119 100755 --- a/hwdb.d/parse_hwdb.py +++ b/hwdb.d/parse_hwdb.py @@ -26,16 +26,31 @@ # SOFTWARE. import glob +import os import string import sys -import os try: - from pyparsing import (Word, White, Literal, ParserElement, Regex, LineEnd, - OneOrMore, Combine, Or, Optional, Suppress, Group, - nums, alphanums, printables, - stringEnd, pythonStyleComment, - ParseBaseException) + from pyparsing import ( + Combine, + Group, + LineEnd, + Literal, + OneOrMore, + Optional, + Or, + ParseBaseException, + ParserElement, + Regex, + Suppress, + White, + Word, + alphanums, + nums, + printables, + pythonStyleComment, + stringEnd, + ) except ImportError: print('pyparsing is not available') sys.exit(77) @@ -57,11 +72,7 @@ except ImportError: ecodes = None print('WARNING: evdev is not available') -try: - from functools import lru_cache -except ImportError: - # don't do caching on old python - lru_cache = lambda: (lambda f: f) +from functools import lru_cache EOL = LineEnd().suppress() EMPTYLINE = LineEnd() @@ -72,78 +83,90 @@ SIGNED_REAL = Combine(Optional(Word('-+')) + REAL) UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_') # Those patterns are used in type-specific matches -TYPES = {'mouse': ('usb', 'bluetooth', 'ps2', '*'), - 'evdev': ('name', 'atkbd', 'input'), - 'fb': ('pci', 'vmbus'), - 'id-input': ('modalias', 'bluetooth', 'i2c', 'usb'), - 'touchpad': ('i8042', 'rmi', 'bluetooth', 'usb'), - 'joystick': ('i8042', 'rmi', 'bluetooth', 'usb'), - 'keyboard': ('name', ), - 'sensor': ('modalias', - 'accel-base', - 'accel-display', - 'accel-camera', - 'proximity-palmrest', - 'proximity-palmrest-left', - 'proximity-palmrest-right', - 'proximity-lap', - 'proximity-wifi', - 'proximity-lte', - 'proximity-wifi-lte', - 'proximity-wifi-left', - 'proximity-wifi-right', - ), - 'ieee1394-unit-function' : ('node', ), - 'camera': ('usb'), - } +TYPES = { + 'mouse': ('usb', 'bluetooth', 'ps2', '*'), + 'evdev': ('name', 'atkbd', 'input'), + 'fb': ('pci', 'vmbus'), + 'id-input': ('modalias', 'bluetooth', 'i2c', 'usb'), + 'touchpad': ('i8042', 'rmi', 'bluetooth', 'usb'), + 'joystick': ('i8042', 'rmi', 'bluetooth', 'usb'), + 'keyboard': ('name',), + 'sensor': ( + 'modalias', + 'accel-base', + 'accel-display', + 'accel-camera', + 'proximity-palmrest', + 'proximity-palmrest-left', + 'proximity-palmrest-right', + 'proximity-lap', + 'proximity-wifi', + 'proximity-lte', + 'proximity-wifi-lte', + 'proximity-wifi-left', + 'proximity-wifi-right', + ), + 'ieee1394-unit-function': ('node',), + 'camera': ('usb'), +} # Patterns that are used to set general properties on a device -GENERAL_MATCHES = {'acpi', - 'bluetooth', - 'dmi', - 'ieee1394', - 'OUI', - 'pci', - 'sdio', - 'tpm2', - 'usb', - 'vmbus', - } +GENERAL_MATCHES = { + 'acpi', + 'bluetooth', + 'dmi', + 'ieee1394', + 'OUI', + 'pci', + 'sdio', + 'tpm2', + 'usb', + 'vmbus', +} + def upperhex_word(length): return Word(nums + 'ABCDEF', exact=length) -@lru_cache() + +@lru_cache def hwdb_grammar(): ParserElement.setDefaultWhitespaceChars('') - prefix = Or(category + ':' + Or(conn) + ':' - for category, conn in TYPES.items()) + prefix = Or(category + ':' + Or(conn) + ':' for category, conn in TYPES.items()) matchline_typed = Combine(prefix + Word(printables + ' ' + '®')) matchline_general = Combine(Or(GENERAL_MATCHES) + ':' + Word(printables + ' ' + '®')) matchline = (matchline_typed | matchline_general) + EOL - propertyline = (White(' ', exact=1).suppress() + - Combine(UDEV_TAG - '=' - Optional(Word(alphanums + '_=:@*.!-;, "/?&')) - - Optional(pythonStyleComment)) + - EOL) + propertyline = ( + White(' ', exact=1).suppress() + + Combine( + UDEV_TAG - '=' - Optional(Word(alphanums + '_=:@*.!-;, "/?&')) - Optional(pythonStyleComment) + ) + + EOL + ) propertycomment = White(' ', exact=1) + pythonStyleComment + EOL - group = (OneOrMore(matchline('MATCHES*') ^ COMMENTLINE.suppress()) - - OneOrMore(propertyline('PROPERTIES*') ^ propertycomment.suppress()) - - (EMPTYLINE ^ stringEnd()).suppress()) + group = ( + OneOrMore(matchline('MATCHES*') ^ COMMENTLINE.suppress()) + - OneOrMore(propertyline('PROPERTIES*') ^ propertycomment.suppress()) + - (EMPTYLINE ^ stringEnd()).suppress() + ) commentgroup = OneOrMore(COMMENTLINE).suppress() - EMPTYLINE.suppress() grammar = OneOrMore(Group(group)('GROUPS*') ^ commentgroup) + stringEnd() return grammar -@lru_cache() + +@lru_cache def property_grammar(): ParserElement.setDefaultWhitespaceChars(' ') - dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Optional(Suppress('@') + INTEGER('HZ')))('SETTINGS*') + dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Optional(Suppress('@') + INTEGER('HZ')))( + 'SETTINGS*' + ) mount_matrix_row = SIGNED_REAL + ',' + SIGNED_REAL + ',' + SIGNED_REAL mount_matrix = Group(mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX') xkb_setting = Optional(Word(alphanums + '+-/@._')) @@ -153,132 +176,139 @@ def property_grammar(): # Although this set doesn't cover all of characters in database entries, it's enough for test targets. name_literal = Word(printables + ' ') - props = (('MOUSE_DPI', Group(OneOrMore(dpi_setting))), - ('MOUSE_WHEEL_CLICK_ANGLE', INTEGER), - ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER), - ('MOUSE_WHEEL_CLICK_COUNT', INTEGER), - ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER), - ('ID_INPUT_3D_MOUSE', zero_one), - ('ID_AUTOSUSPEND', zero_one), - ('ID_AUTOSUSPEND_DELAY_MS', INTEGER), - ('ID_AV_PRODUCTION_CONTROLLER', zero_one), - ('ID_AV_LIGHTS', zero_one), - ('ID_PERSIST', zero_one), - ('ID_PDA', zero_one), - ('ID_INPUT', id_input_setting), - ('ID_INPUT_ACCELEROMETER', id_input_setting), - ('ID_INPUT_JOYSTICK', id_input_setting), - ('ID_INPUT_KEY', id_input_setting), - ('ID_INPUT_KEYBOARD', id_input_setting), - ('ID_INPUT_MOUSE', id_input_setting), - ('ID_INPUT_POINTINGSTICK', id_input_setting), - ('ID_INPUT_SWITCH', id_input_setting), - ('ID_INPUT_TABLET', id_input_setting), - ('ID_INPUT_TABLET_PAD', id_input_setting), - ('ID_INPUT_TOUCHPAD', id_input_setting), - ('ID_INPUT_TOUCHSCREEN', id_input_setting), - ('ID_INPUT_TRACKBALL', id_input_setting), - ('ID_SIGNAL_ANALYZER', zero_one), - ('ID_MAKER_TOOL', zero_one), - ('ID_HARDWARE_WALLET', zero_one), - ('ID_SOFTWARE_RADIO', zero_one), - ('ID_MM_DEVICE_IGNORE', zero_one), - ('ID_NET_AUTO_LINK_LOCAL_ONLY', zero_one), - ('POINTINGSTICK_SENSITIVITY', INTEGER), - ('ID_INTEGRATION', Or(('internal', 'external'))), - ('ID_INPUT_TOUCHPAD_INTEGRATION', Or(('internal', 'external'))), - ('XKB_FIXED_LAYOUT', xkb_setting), - ('XKB_FIXED_VARIANT', xkb_setting), - ('XKB_FIXED_MODEL', xkb_setting), - ('KEYBOARD_LED_NUMLOCK', Literal('0')), - ('KEYBOARD_LED_CAPSLOCK', Literal('0')), - ('ACCEL_MOUNT_MATRIX', mount_matrix), - ('ACCEL_LOCATION', Or(('display', 'base'))), - ('PROXIMITY_NEAR_LEVEL', INTEGER), - ('IEEE1394_UNIT_FUNCTION_MIDI', zero_one), - ('IEEE1394_UNIT_FUNCTION_AUDIO', zero_one), - ('IEEE1394_UNIT_FUNCTION_VIDEO', zero_one), - ('ID_VENDOR_FROM_DATABASE', name_literal), - ('ID_MODEL_FROM_DATABASE', name_literal), - ('ID_TAG_MASTER_OF_SEAT', Literal('1')), - ('ID_INFRARED_CAMERA', zero_one), - ('ID_CAMERA_DIRECTION', Or(('front', 'rear'))), - ('SOUND_FORM_FACTOR', Or(('internal', 'webcam', 'speaker', 'headphone', 'headset', 'handset', 'microphone'))), - ('ID_SYS_VENDOR_IS_RUBBISH', zero_one), - ('ID_PRODUCT_NAME_IS_RUBBISH', zero_one), - ('ID_PRODUCT_VERSION_IS_RUBBISH', zero_one), - ('ID_BOARD_VERSION_IS_RUBBISH', zero_one), - ('ID_PRODUCT_SKU_IS_RUBBISH', zero_one), - ('ID_CHASSIS_ASSET_TAG_IS_RUBBISH', zero_one), - ('ID_CHASSIS', name_literal), - ('ID_SYSFS_ATTRIBUTE_MODEL', name_literal), - ('ID_NET_NAME_FROM_DATABASE', name_literal), - ('ID_NET_NAME_INCLUDE_DOMAIN', zero_one), - ('TPM2_BROKEN_NVPCR', zero_one), - ('IMDS_VENDOR', name_literal), - ('IMDS_TOKEN_URL', name_literal), - ('IMDS_REFRESH_HEADER_NAME', name_literal), - ('IMDS_DATA_URL', name_literal), - ('IMDS_DATA_URL_SUFFIX', name_literal), - ('IMDS_TOKEN_HEADER_NAME', name_literal), - ('IMDS_EXTRA_HEADER', name_literal), - ('IMDS_ADDRESS_IPV4', name_literal), - ('IMDS_ADDRESS_IPV6', name_literal), - ('IMDS_KEY_HOSTNAME', name_literal), - ('IMDS_KEY_REGION', name_literal), - ('IMDS_KEY_ZONE', name_literal), - ('IMDS_KEY_IPV4_PUBLIC', name_literal), - ('IMDS_KEY_IPV6_PUBLIC', name_literal), - ('IMDS_KEY_SSH_KEY', name_literal), - ('IMDS_KEY_USERDATA', name_literal), - ('IMDS_KEY_USERDATA_BASE', name_literal), - ('IMDS_KEY_USERDATA_BASE64', name_literal), - ) - fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE') - for name, val in props] - kbd_props = [Regex(r'KEYBOARD_KEY_[0-9a-f]+')('NAME') - - Suppress('=') - - Group('!' ^ (Optional('!') - Word(alphanums + '_')))('VALUE') - ] - abs_props = [Regex(r'EVDEV_ABS_[0-9a-f]{2}')('NAME') - - Suppress('=') - - Word('-' + nums + ':')('VALUE') - ] + props = ( + ('MOUSE_DPI', Group(OneOrMore(dpi_setting))), + ('MOUSE_WHEEL_CLICK_ANGLE', INTEGER), + ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER), + ('MOUSE_WHEEL_CLICK_COUNT', INTEGER), + ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER), + ('ID_INPUT_3D_MOUSE', zero_one), + ('ID_AUTOSUSPEND', zero_one), + ('ID_AUTOSUSPEND_DELAY_MS', INTEGER), + ('ID_AV_PRODUCTION_CONTROLLER', zero_one), + ('ID_AV_LIGHTS', zero_one), + ('ID_PERSIST', zero_one), + ('ID_PDA', zero_one), + ('ID_INPUT', id_input_setting), + ('ID_INPUT_ACCELEROMETER', id_input_setting), + ('ID_INPUT_JOYSTICK', id_input_setting), + ('ID_INPUT_KEY', id_input_setting), + ('ID_INPUT_KEYBOARD', id_input_setting), + ('ID_INPUT_MOUSE', id_input_setting), + ('ID_INPUT_POINTINGSTICK', id_input_setting), + ('ID_INPUT_SWITCH', id_input_setting), + ('ID_INPUT_TABLET', id_input_setting), + ('ID_INPUT_TABLET_PAD', id_input_setting), + ('ID_INPUT_TOUCHPAD', id_input_setting), + ('ID_INPUT_TOUCHSCREEN', id_input_setting), + ('ID_INPUT_TRACKBALL', id_input_setting), + ('ID_SIGNAL_ANALYZER', zero_one), + ('ID_MAKER_TOOL', zero_one), + ('ID_HARDWARE_WALLET', zero_one), + ('ID_SOFTWARE_RADIO', zero_one), + ('ID_MM_DEVICE_IGNORE', zero_one), + ('ID_NET_AUTO_LINK_LOCAL_ONLY', zero_one), + ('POINTINGSTICK_SENSITIVITY', INTEGER), + ('ID_INTEGRATION', Or(('internal', 'external'))), + ('ID_INPUT_TOUCHPAD_INTEGRATION', Or(('internal', 'external'))), + ('XKB_FIXED_LAYOUT', xkb_setting), + ('XKB_FIXED_VARIANT', xkb_setting), + ('XKB_FIXED_MODEL', xkb_setting), + ('KEYBOARD_LED_NUMLOCK', Literal('0')), + ('KEYBOARD_LED_CAPSLOCK', Literal('0')), + ('ACCEL_MOUNT_MATRIX', mount_matrix), + ('ACCEL_LOCATION', Or(('display', 'base'))), + ('PROXIMITY_NEAR_LEVEL', INTEGER), + ('IEEE1394_UNIT_FUNCTION_MIDI', zero_one), + ('IEEE1394_UNIT_FUNCTION_AUDIO', zero_one), + ('IEEE1394_UNIT_FUNCTION_VIDEO', zero_one), + ('ID_VENDOR_FROM_DATABASE', name_literal), + ('ID_MODEL_FROM_DATABASE', name_literal), + ('ID_TAG_MASTER_OF_SEAT', Literal('1')), + ('ID_INFRARED_CAMERA', zero_one), + ('ID_CAMERA_DIRECTION', Or(('front', 'rear'))), + ( + 'SOUND_FORM_FACTOR', + Or(('internal', 'webcam', 'speaker', 'headphone', 'headset', 'handset', 'microphone')), + ), + ('ID_SYS_VENDOR_IS_RUBBISH', zero_one), + ('ID_PRODUCT_NAME_IS_RUBBISH', zero_one), + ('ID_PRODUCT_VERSION_IS_RUBBISH', zero_one), + ('ID_BOARD_VERSION_IS_RUBBISH', zero_one), + ('ID_PRODUCT_SKU_IS_RUBBISH', zero_one), + ('ID_CHASSIS_ASSET_TAG_IS_RUBBISH', zero_one), + ('ID_CHASSIS', name_literal), + ('ID_SYSFS_ATTRIBUTE_MODEL', name_literal), + ('ID_NET_NAME_FROM_DATABASE', name_literal), + ('ID_NET_NAME_INCLUDE_DOMAIN', zero_one), + ('TPM2_BROKEN_NVPCR', zero_one), + ('IMDS_VENDOR', name_literal), + ('IMDS_TOKEN_URL', name_literal), + ('IMDS_REFRESH_HEADER_NAME', name_literal), + ('IMDS_DATA_URL', name_literal), + ('IMDS_DATA_URL_SUFFIX', name_literal), + ('IMDS_TOKEN_HEADER_NAME', name_literal), + ('IMDS_EXTRA_HEADER', name_literal), + ('IMDS_ADDRESS_IPV4', name_literal), + ('IMDS_ADDRESS_IPV6', name_literal), + ('IMDS_KEY_HOSTNAME', name_literal), + ('IMDS_KEY_REGION', name_literal), + ('IMDS_KEY_ZONE', name_literal), + ('IMDS_KEY_IPV4_PUBLIC', name_literal), + ('IMDS_KEY_IPV6_PUBLIC', name_literal), + ('IMDS_KEY_SSH_KEY', name_literal), + ('IMDS_KEY_USERDATA', name_literal), + ('IMDS_KEY_USERDATA_BASE', name_literal), + ('IMDS_KEY_USERDATA_BASE64', name_literal), + ) + fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE') for name, val in props] + kbd_props = [ + Regex(r'KEYBOARD_KEY_[0-9a-f]+')('NAME') + - Suppress('=') + - Group('!' ^ (Optional('!') - Word(alphanums + '_')))('VALUE') + ] + abs_props = [Regex(r'EVDEV_ABS_[0-9a-f]{2}')('NAME') - Suppress('=') - Word('-' + nums + ':')('VALUE')] grammar = Or(fixed_props + kbd_props + abs_props) + EOL return grammar + ERROR = False + + def error(fmt, *args, **kwargs): global ERROR ERROR = True print(fmt.format(*args, **kwargs)) + def convert_properties(group): matches = [m[0] for m in group.MATCHES] props = [p[0] for p in group.PROPERTIES] return matches, props + def parse(fname): grammar = hwdb_grammar() try: - with open(fname, 'r', encoding='UTF-8') as f: + with open(fname, encoding='UTF-8') as f: parsed = grammar.parseFile(f) except ParseBaseException as e: error('Cannot parse {}: {}', fname, e) return [] return [convert_properties(g) for g in parsed.GROUPS] + def check_matches(groups): matches = sum((group[0] for group in groups), []) # This is a partial check. The other cases could be also done, but those # three are the most commonly wrong. grammars = { - 'bluetooth' : 'v' + upperhex_word(4) + Optional('p' + upperhex_word(4) + Optional(':')) + '*', - 'usb' : 'v' + upperhex_word(4) + Optional('p' + upperhex_word(4) + Optional(':')) + '*', - 'pci' : 'v' + upperhex_word(8) + Optional('d' + upperhex_word(8) + Optional(':')) + '*', + 'bluetooth': 'v' + upperhex_word(4) + Optional('p' + upperhex_word(4) + Optional(':')) + '*', + 'usb': 'v' + upperhex_word(4) + Optional('p' + upperhex_word(4) + Optional(':')) + '*', + 'pci': 'v' + upperhex_word(8) + Optional('d' + upperhex_word(8) + Optional(':')) + '*', } for match in matches: @@ -301,11 +331,13 @@ def check_matches(groups): error('Match {!r} is duplicated', match) prev = match + def check_one_default(prop, settings): defaults = [s for s in settings if s.DEFAULT] if len(defaults) > 1: error('More than one star entry: {!r}', prop) + def check_one_mount_matrix(prop, value): numbers = [s for s in value if s not in {';', ','}] if len(numbers) != 9: @@ -316,28 +348,33 @@ def check_one_mount_matrix(prop, value): error('Wrong accel matrix: {!r}', prop) bad_x, bad_y, bad_z = max(numbers[0:3]) == 0, max(numbers[3:6]) == 0, max(numbers[6:9]) == 0 if bad_x or bad_y or bad_z: - error('Mount matrix is all zero in {} row: {!r}', - 'x' if bad_x else ('y' if bad_y else 'z'), - prop) + error('Mount matrix is all zero in {} row: {!r}', 'x' if bad_x else ('y' if bad_y else 'z'), prop) + def check_one_keycode(value): if value != '!' and ecodes is not None: key = 'KEY_' + value.upper() - if not (key in ecodes or - value.upper() in ecodes or - # new keys added in kernel 5.5 - 'KBD_LCD_MENU' in key): + if not ( + key in ecodes + or value.upper() in ecodes + # new keys added in kernel 5.5 + or 'KBD_LCD_MENU' in key + ): # fmt: skip error('Keycode {} unknown', key) + def check_wheel_clicks(properties): - pairs = (('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', 'MOUSE_WHEEL_CLICK_COUNT'), - ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', 'MOUSE_WHEEL_CLICK_ANGLE'), - ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', 'MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL'), - ('MOUSE_WHEEL_CLICK_COUNT', 'MOUSE_WHEEL_CLICK_ANGLE')) + pairs = ( + ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', 'MOUSE_WHEEL_CLICK_COUNT'), + ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', 'MOUSE_WHEEL_CLICK_ANGLE'), + ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', 'MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL'), + ('MOUSE_WHEEL_CLICK_COUNT', 'MOUSE_WHEEL_CLICK_ANGLE'), + ) for pair in pairs: if pair[0] in properties and pair[1] not in properties: error('{} requires {} to be specified', *pair) + def check_properties(groups): grammar = property_grammar() for _, props in groups: @@ -364,6 +401,7 @@ def check_properties(groups): check_wheel_clicks(seen_props) + def print_summary(fname, groups): n_matches = sum(len(matches) for matches, props in groups) n_props = sum(len(props) for matches, props in groups) @@ -372,12 +410,15 @@ def print_summary(fname, groups): if n_matches == 0 or n_props == 0: print(f'{fname}: no matches or props') + if __name__ == '__main__': - args = sys.argv[1:] or sorted([ - os.path.dirname(sys.argv[0]) + '/20-dmi-id.hwdb', - os.path.dirname(sys.argv[0]) + '/20-net-ifname.hwdb', - *glob.glob(os.path.dirname(sys.argv[0]) + '/[678][0-9]-*.hwdb'), - ]) + args = sys.argv[1:] or sorted( + [ + os.path.dirname(sys.argv[0]) + '/20-dmi-id.hwdb', + os.path.dirname(sys.argv[0]) + '/20-net-ifname.hwdb', + *glob.glob(os.path.dirname(sys.argv[0]) + '/[678][0-9]-*.hwdb'), + ] + ) for fname in args: groups = parse(fname) From 522fdd7d9661b0aeede9a106d434e9200c60a4fb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:48:59 +0900 Subject: [PATCH 07/62] man/90-rearrange-path: apply "ruff format" and "ruff check --fix" --- man/90-rearrange-path.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/man/90-rearrange-path.py b/man/90-rearrange-path.py index b5b6294754..278311f0ab 100755 --- a/man/90-rearrange-path.py +++ b/man/90-rearrange-path.py @@ -17,6 +17,7 @@ interesting generators. import os import pathlib + def rearrange_bin_sbin(path): """Make sure any pair of …/bin, …/sbin directories is in this order @@ -27,15 +28,16 @@ def rearrange_bin_sbin(path): for i in range(len(items)): if 'sbin' in items[i].parts: ind = items[i].parts.index('sbin') - bin = pathlib.Path(*items[i].parts[:ind], 'bin', *items[i].parts[ind+1:]) - if bin in items[i+1:]: - j = i + 1 + items[i+1:].index(bin) + bin = pathlib.Path(*items[i].parts[:ind], 'bin', *items[i].parts[ind + 1 :]) + if bin in items[i + 1 :]: + j = i + 1 + items[i + 1 :].index(bin) items[i], items[j] = items[j], items[i] return ':'.join(p.as_posix() for p in items) + if __name__ == '__main__': - path = os.environ['PATH'] # This should be always set. - # If it is not, we will just crash, which is OK too. + # This should be always set. If it is not, we will just crash, which is OK too. + path = os.environ['PATH'] new = rearrange_bin_sbin(path) if new != path: - print('PATH={}'.format(new)) + print(f'PATH={new}') From 14848f1f71bca0cc159bc31b912d0727afad86e4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:49:47 +0900 Subject: [PATCH 08/62] man/check-os-release-simple: apply "ruff format" --- man/check-os-release-simple.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/check-os-release-simple.py b/man/check-os-release-simple.py index ce73c77b14..8c64982790 100755 --- a/man/check-os-release-simple.py +++ b/man/check-os-release-simple.py @@ -2,11 +2,11 @@ # SPDX-License-Identifier: MIT-0 import platform + os_release = platform.freedesktop_os_release() pretty_name = os_release.get('PRETTY_NAME', 'Linux') print(f'Running on {pretty_name!r}') -if 'fedora' in [os_release.get('ID', 'linux'), - *os_release.get('ID_LIKE', '').split()]: +if 'fedora' in [os_release.get('ID', 'linux'), *os_release.get('ID_LIKE', '').split()]: print('Looks like Fedora!') From 548196170047967c40b57897282cbb98427c1fac Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:50:19 +0900 Subject: [PATCH 09/62] man/check-os-release: apply "ruff format" --- man/check-os-release.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/check-os-release.py b/man/check-os-release.py index f0a64f3496..ec71e57a31 100755 --- a/man/check-os-release.py +++ b/man/check-os-release.py @@ -5,6 +5,7 @@ import ast import re import sys + def read_os_release(): try: filename = '/etc/os-release' @@ -23,14 +24,13 @@ def read_os_release(): val = ast.literal_eval(val) yield name, val else: - print(f'{filename}:{line_number}: bad line {line!r}', - file=sys.stderr) + print(f'{filename}:{line_number}: bad line {line!r}', file=sys.stderr) + os_release = dict(read_os_release()) pretty_name = os_release.get('PRETTY_NAME', 'Linux') print(f'Running on {pretty_name!r}') -if 'debian' in [os_release.get('ID', 'linux'), - *os_release.get('ID_LIKE', '').split()]: +if 'debian' in [os_release.get('ID', 'linux'), *os_release.get('ID_LIKE', '').split()]: print('Looks like Debian!') From 4758d1f71a0c5e3d0122d8cd0ce8c8500545d9cb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:50:59 +0900 Subject: [PATCH 10/62] man/notify-example: apply "ruff format" --- man/notify-selfcontained-example.py | 48 +++++++++++++++++------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/man/notify-selfcontained-example.py b/man/notify-selfcontained-example.py index 6a1e25b99b..1302116221 100755 --- a/man/notify-selfcontained-example.py +++ b/man/notify-selfcontained-example.py @@ -18,49 +18,56 @@ import time reloading = False terminating = False + def notify(message): if not message: - raise ValueError("notify() requires a message") + raise ValueError('notify() requires a message') - socket_path = os.environ.get("NOTIFY_SOCKET") + socket_path = os.environ.get('NOTIFY_SOCKET') if not socket_path: return - if socket_path[0] not in ("/", "@"): - raise OSError(errno.EAFNOSUPPORT, "Unsupported socket type") + if socket_path[0] not in ('/', '@'): + raise OSError(errno.EAFNOSUPPORT, 'Unsupported socket type') # Handle abstract socket. - if socket_path[0] == "@": - socket_path = "\0" + socket_path[1:] + if socket_path[0] == '@': + socket_path = '\0' + socket_path[1:] with socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM | socket.SOCK_CLOEXEC) as sock: sock.connect(socket_path) sock.sendall(message) + def notify_ready(): - notify(b"READY=1") + notify(b'READY=1') + def notify_reloading(): microsecs = time.clock_gettime_ns(time.CLOCK_MONOTONIC) // 1000 - notify(f"RELOADING=1\nMONOTONIC_USEC={microsecs}".encode()) + notify(f'RELOADING=1\nMONOTONIC_USEC={microsecs}'.encode()) + def notify_stopping(): - notify(b"STOPPING=1") + notify(b'STOPPING=1') + def reload(signum, frame): global reloading reloading = True + def terminate(signum, frame): global terminating terminating = True + def main(): - print("Doing initial setup") + print('Doing initial setup') global reloading, terminating # Set up signal handlers. - print("Setting up signal handlers") + print('Setting up signal handlers') signal.signal(signal.SIGHUP, reload) signal.signal(signal.SIGINT, terminate) signal.signal(signal.SIGTERM, terminate) @@ -68,13 +75,13 @@ def main(): # Do any other setup work here. # Once all setup is done, signal readiness. - print("Done setting up") + print('Done setting up') notify_ready() - print("Starting loop") + print('Starting loop') while not terminating: if reloading: - print("Reloading") + print('Reloading') reloading = False # Support notifying the manager when reloading configuration. @@ -86,19 +93,20 @@ def main(): # Do some reconfiguration work here. - print("Done reloading") + print('Done reloading') notify_ready() # Do the real work here ... - print("Sleeping for five seconds") + print('Sleeping for five seconds') time.sleep(5) - print("Terminating") + print('Terminating') notify_stopping() -if __name__ == "__main__": + +if __name__ == '__main__': sys.stdout.reconfigure(line_buffering=True) - print("Starting app") + print('Starting app') main() - print("Stopped app") + print('Stopped app') From a89c11b67219c4aee65254f733a2f0311b113acf Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:52:37 +0900 Subject: [PATCH 11/62] filesystem-sets: apply "ruff format" and "ruff check --fix" --- src/basic/filesystem-sets.py | 207 ++++++++++++++++++----------------- 1 file changed, 109 insertions(+), 98 deletions(-) diff --git a/src/basic/filesystem-sets.py b/src/basic/filesystem-sets.py index 18daa48681..be08664aef 100755 --- a/src/basic/filesystem-sets.py +++ b/src/basic/filesystem-sets.py @@ -124,12 +124,12 @@ NAME_TO_MAGIC = { 'z3fold': ['Z3FOLD_MAGIC'], 'zonefs': ['ZONEFS_MAGIC'], 'zsmalloc': ['ZSMALLOC_MAGIC'], -} +} # fmt: skip # System magics are sometimes not unique, because file systems got new # revisions or got renamed. Let's prefer newer over older here, and thus ignore # the old names. -OBSOLETE_NAMES = { +OBSOLETE_NAMES = { 'cpuset', # magic taken over by cgroupfs 'devtmpfs', # not a file system of its own, but just a "named superblock" of tmpfs 'ext2', # ext4 is the newest revision of ext2 + ext3 @@ -142,115 +142,116 @@ OBSOLETE_NAMES = { 'nfs', # nfs4 is the newest revision of nfs 'pvfs2', # orangefs is the new name of pvfs2 'smb3', # smb3 is an alias for cifs -} +} # fmt: skip FILESYSTEM_SETS = [ ( - "@basic-api", - "Basic filesystem API", - "cgroup", - "cgroup2", - "devpts", - "devtmpfs", - "mqueue", - "proc", - "sysfs", + '@basic-api', + 'Basic filesystem API', + 'cgroup', + 'cgroup2', + 'devpts', + 'devtmpfs', + 'mqueue', + 'proc', + 'sysfs', ), ( - "@anonymous", - "Anonymous inodes", - "anon_inodefs", - "pipefs", - "sockfs", + '@anonymous', + 'Anonymous inodes', + 'anon_inodefs', + 'pipefs', + 'sockfs', ), ( - "@application", - "Application virtual filesystems", - "autofs", - "fuse", - "overlay", + '@application', + 'Application virtual filesystems', + 'autofs', + 'fuse', + 'overlay', ), ( - "@auxiliary-api", - "Auxiliary filesystem API", - "binfmt_misc", - "configfs", - "efivarfs", - "fusectl", - "hugetlbfs", - "rpc_pipefs", - "securityfs", + '@auxiliary-api', + 'Auxiliary filesystem API', + 'binfmt_misc', + 'configfs', + 'efivarfs', + 'fusectl', + 'hugetlbfs', + 'rpc_pipefs', + 'securityfs', ), ( - "@common-block", - "Common block device filesystems", - "btrfs", - "erofs", - "exfat", - "ext4", - "f2fs", - "iso9660", - "ntfs3", - "squashfs", - "udf", - "vfat", - "xfs", + '@common-block', + 'Common block device filesystems', + 'btrfs', + 'erofs', + 'exfat', + 'ext4', + 'f2fs', + 'iso9660', + 'ntfs3', + 'squashfs', + 'udf', + 'vfat', + 'xfs', ), ( - "@historical-block", - "Historical block device filesystems", - "ext2", - "ext3", - "minix", + '@historical-block', + 'Historical block device filesystems', + 'ext2', + 'ext3', + 'minix', ), ( - "@network", - "Well-known network filesystems", - "afs", - "ceph", - "cifs", - "gfs", - "gfs2", - "ncp", - "ncpfs", - "nfs", - "nfs4", - "ocfs2", - "orangefs", - "pvfs2", - "smb3", - "smbfs", + '@network', + 'Well-known network filesystems', + 'afs', + 'ceph', + 'cifs', + 'gfs', + 'gfs2', + 'ncp', + 'ncpfs', + 'nfs', + 'nfs4', + 'ocfs2', + 'orangefs', + 'pvfs2', + 'smb3', + 'smbfs', ), ( - "@privileged-api", - "Privileged filesystem API", - "bpf", - "debugfs", - "pstore", - "tracefs", + '@privileged-api', + 'Privileged filesystem API', + 'bpf', + 'debugfs', + 'pstore', + 'tracefs', ), ( - "@security", - "Security/MAC API VFS", - "apparmorfs", - "selinuxfs", - "smackfs", + '@security', + 'Security/MAC API VFS', + 'apparmorfs', + 'selinuxfs', + 'smackfs', ), ( - "@temporary", - "Temporary filesystems", - "ramfs", - "tmpfs", + '@temporary', + 'Temporary filesystems', + 'ramfs', + 'tmpfs', ), ( - "@known", - "All known filesystems declared in the kernel", + '@known', + 'All known filesystems declared in the kernel', *NAME_TO_MAGIC.keys(), ), ] + def generate_gperf(): - print("""\ + print('''\ /* SPDX-License-Identifier: LGPL-2.1-or-later */ %{ #if __GNUC__ >= 15 @@ -275,12 +276,13 @@ struct FilesystemMagic; %omit-struct-type %struct-type %includes -%%""") +%%''') for name, magics in NAME_TO_MAGIC.items(): - print(f"{name + ',':16} {{{', '.join(magics)}}}") + print(f'{name + ",":16} {{{", ".join(magics)}}}') + def generate_fs_type_to_string(): - print("""\ + print('''\ #include #include "filesystems.h" @@ -290,7 +292,7 @@ def generate_fs_type_to_string(): #define PROJECT_FILE __FILE__ const char* fs_type_to_string(statfs_f_type_t magic) { - switch (magic) {""") + switch (magic) {''') for name, magics in NAME_TO_MAGIC.items(): if name in OBSOLETE_NAMES: @@ -299,10 +301,11 @@ const char* fs_type_to_string(statfs_f_type_t magic) { print(f' case (statfs_f_type_t) {magic}:') print(f' return "{name}";') - print("""\ + print('''\ } return NULL; -}""") +}''') + def generate_fs_in_group(): print('bool fs_in_group(const struct statfs *st, FilesystemGroups fs_group) {') @@ -312,14 +315,14 @@ def generate_fs_in_group(): magics = sorted(set(sum((NAME_TO_MAGIC[fs] for fs in filesystems), start=[]))) enum = 'FILESYSTEM_SET_' + name[1:].upper().replace('-', '_') print(f' case {enum}:') - opts = '\n || '.join(f'F_TYPE_EQUAL(st->f_type, {magic})' - for magic in magics) + opts = '\n || '.join(f'F_TYPE_EQUAL(st->f_type, {magic})' for magic in magics) print(f' return {opts};') print(' default: assert_not_reached();') print(' }') print('}') + def generate_filesystem_sets(): print('const FilesystemSet filesystem_sets[_FILESYSTEM_SET_MAX] = {') @@ -329,37 +332,45 @@ def generate_filesystem_sets(): print(f' [{enum}] = {{') print(f' .name = "{name}",') print(f' .help = "{desc}",') - print(f' .value =') + print(' .value =') for filesystem in filesystems: print(f' "{filesystem}\\0"') print(' },') print('};') + def magic_defines(): cpp = os.environ['CPP'].split() out = subprocess.check_output( [*cpp, '-dM', '-include', 'linux/magic.h', '-'], stdin=subprocess.DEVNULL, - text=True) + text=True, + ) for line in out.splitlines(): _, name, *rest = line.split() - if ('_MAGIC' in name - and rest and rest[0].startswith('0x') - and name not in { + if ( + '_MAGIC' in name + and rest + and rest[0].startswith('0x') + and name + not in { 'STACK_END_MAGIC', 'MTD_INODE_FS_MAGIC', 'FUTEXFS_SUPER_MAGIC', 'CRAMFS_MAGIC_WEND', - }): + } + ): yield name + def check(): kernel_magics = set(magic_defines()) our_magics = set(sum(NAME_TO_MAGIC.values(), start=[])) extra = kernel_magics - our_magics if extra: - sys.exit(f"kernel knows additional filesystem magics: {', '.join(sorted(extra))}") + sys.exit(f'kernel knows additional filesystem magics: {", ".join(sorted(extra))}') + if __name__ == '__main__': for arg in sys.argv[1:]: From c5abf64d3775adfdcd64dcdf23bdf79fe4d52bb3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:53:29 +0900 Subject: [PATCH 12/62] fuzz-bootspec: apply "ruff format" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also replaces open().read() with Path().read_text(). Co-authored-by: Jörg Behrmann --- src/fuzz/fuzz-bootspec-gen.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/fuzz/fuzz-bootspec-gen.py b/src/fuzz/fuzz-bootspec-gen.py index a73e59203b..927c5ed529 100755 --- a/src/fuzz/fuzz-bootspec-gen.py +++ b/src/fuzz/fuzz-bootspec-gen.py @@ -4,14 +4,12 @@ """Generate sample input for fuzz-bootspec""" import json -import os import sys +from pathlib import Path -config = open(sys.argv[1]).read() -loader = [entry for entry in open(sys.argv[2], encoding='utf-16-le').read().split('\0') - if len(entry) > 2] # filter out fluff from bad decoding -entries = [(os.path.basename(name), open(name).read()) - for name in sys.argv[3:]] +config = Path(sys.argv[1]).read_text() +loader = [entry for entry in Path(sys.argv[2]).read_text(encoding='utf-16-le').split('\0') if len(entry) > 2] +entries = [(Path(name).name, Path(name).read_text()) for name in sys.argv[3:]] data = { 'config': config, From b0a06e2a81f9ff5f03926857f1986af74b213185 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:54:24 +0900 Subject: [PATCH 13/62] generate-syscall: apply "ruff format" and "ruff check --fix" Also, this makes opened files closed when not necessary. --- src/include/override/sys/generate-syscall.py | 63 ++++++++++++-------- src/include/override/sys/syscall.h | 2 +- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/include/override/sys/generate-syscall.py b/src/include/override/sys/generate-syscall.py index 4391df6da3..ba44c93eb2 100755 --- a/src/include/override/sys/generate-syscall.py +++ b/src/include/override/sys/generate-syscall.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later -import sys import functools +import sys # We only generate numbers for a dozen or so syscalls SYSCALLS = [ @@ -12,24 +12,29 @@ SYSCALLS = [ 'quotactl_fd', # defined in glibc header since glibc-2.35 'removexattrat', 'setxattrat', -] +] # fmt: skip + def dictify(f): def wrap(*args, **kwargs): return dict(f(*args, **kwargs)) + return functools.update_wrapper(wrap, f) + @dictify def parse_syscall_table(filename): print(f'Reading {filename}…') - for line in open(filename): - items = line.split() - if len(items) >= 2: - yield items[0], int(items[1]) + with open(filename) as f: + for line in f: + items = line.split() + if len(items) >= 2: + yield items[0], int(items[1]) + def parse_syscall_tables(filenames): - return {filename.split('-')[-1][:-4]: parse_syscall_table(filename) - for filename in filenames} + return {filename.split('-')[-1][:-4]: parse_syscall_table(filename) for filename in filenames} + HEADER = '''\ /* SPDX-License-Identifier: LGPL-2.1-or-later @@ -108,12 +113,15 @@ DEF_TEMPLATE_B = '''\ # else # define systemd_NR_{syscall} {nr_x86_64} # endif -# elif !defined(missing_arch_template) -%s -# endif ''' DEF_TEMPLATE_C = '''\ +# elif !defined(missing_arch_template) +# warning "{syscall}() syscall number is unknown for your architecture" +# endif +''' + +DEF_TEMPLATE_D = '''\ /* may be an (invalid) negative number due to libseccomp, see PR 13319 */ # if defined __NR_{syscall} && __NR_{syscall} >= 0 @@ -130,23 +138,29 @@ static_assert(__NR_{syscall} == systemd_NR_{syscall}, ""); # endif #endif''' -DEF_TEMPLATE = (DEF_TEMPLATE_A + - DEF_TEMPLATE_B % '# warning "{syscall}() syscall number is unknown for your architecture"' + - DEF_TEMPLATE_C) +DEF_TEMPLATE = DEF_TEMPLATE_A + DEF_TEMPLATE_B + DEF_TEMPLATE_C + DEF_TEMPLATE_D -ARCH_CHECK = '''\ +ARCH_CHECK_A = '''\ /* Note: if this code looks strange, this is because it is derived from the same * template as the per-syscall blocks below. */ -''' + '\n'.join(line for line in DEF_TEMPLATE_B.splitlines() - if ' define ' not in line) % '''\ +''' + +ARCH_CHECK_B = '\n'.join(line for line in DEF_TEMPLATE_B.splitlines() if ' define ' not in line) + +ARCH_CHECK_C = '''\ + +# else # warning "Current architecture is missing from the template" -# define missing_arch_template 1''' +# define missing_arch_template 1 +# endif''' + +ARCH_CHECK = ARCH_CHECK_A + ARCH_CHECK_B + ARCH_CHECK_C + def print_syscall_def(syscall, tables, out): - mappings = {f'nr_{arch}':t.get(syscall, -1) - for arch, t in tables.items()} - print(DEF_TEMPLATE.format(syscall=syscall, **mappings), - file=out) + mappings = {f'nr_{arch}': t.get(syscall, -1) for arch, t in tables.items()} + print(DEF_TEMPLATE.format(syscall=syscall, **mappings), file=out) + def print_syscall_defs(syscalls, tables, out): print(HEADER, file=out) @@ -154,12 +168,13 @@ def print_syscall_defs(syscalls, tables, out): for syscall in syscalls: print_syscall_def(syscall, tables, out) + if __name__ == '__main__': output_file = sys.argv[1] arch_files = sys.argv[2:] - out = open(output_file, 'wt') tables = parse_syscall_tables(arch_files) - print_syscall_defs(SYSCALLS, tables, out) + with open(output_file, 'w') as out: + print_syscall_defs(SYSCALLS, tables, out) print(f'Wrote {output_file}') diff --git a/src/include/override/sys/syscall.h b/src/include/override/sys/syscall.h index 108885b725..da555e7908 100644 --- a/src/include/override/sys/syscall.h +++ b/src/include/override/sys/syscall.h @@ -49,7 +49,7 @@ # if defined(__ILP32__) # else # endif -# elif !defined(missing_arch_template) +# else # warning "Current architecture is missing from the template" # define missing_arch_template 1 # endif From cd7bb9604966600b2d9b46a8d3c3876a48772f8d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:54:49 +0900 Subject: [PATCH 14/62] log-generator: apply "ruff format" and "ruff check --fix" --- src/journal-remote/log-generator.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/journal-remote/log-generator.py b/src/journal-remote/log-generator.py index 2843afb4c9..1e2c0e5c3e 100755 --- a/src/journal-remote/log-generator.py +++ b/src/journal-remote/log-generator.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later -import sys import argparse +import sys PARSER = argparse.ArgumentParser() PARSER.add_argument('n', type=int) @@ -12,7 +12,7 @@ PARSER.add_argument('-d', '--data-size', type=int, default=4000) PARSER.add_argument('--data-type', choices={'random', 'simple'}) OPTIONS = PARSER.parse_args() -template = """\ +template = '''\ __CURSOR=s=6863c726210b4560b7048889d8ada5c5;i=3e931;b=f446871715504074bf7049ef0718fa93;m={m:x};t=4fd05c __REALTIME_TIMESTAMP={realtime_ts} __MONOTONIC_TIMESTAMP={monotonic_ts} @@ -30,7 +30,7 @@ SYSLOG_PID=25721 _PID=25721 _SOURCE_REALTIME_TIMESTAMP={source_realtime_ts} DATA={data} -""" +''' priority = 3 facility = 6 @@ -51,14 +51,16 @@ for i in range(OPTIONS.n): data = '{:0{}}'.format(counter, OPTIONS.data_size) counter += 1 - entry = template.format(m=0x198603b12d7 + i, - realtime_ts=1404101101501873 + i, - monotonic_ts=1753961140951 + i, - source_realtime_ts=1404101101483516 + i, - priority=priority, - facility=facility, - message=message, - data=data) + entry = template.format( + m=0x198603B12D7 + i, + realtime_ts=1404101101501873 + i, + monotonic_ts=1753961140951 + i, + source_realtime_ts=1404101101483516 + i, + priority=priority, + facility=facility, + message=message, + data=data, + ) bytes += len(entry) @@ -69,4 +71,4 @@ for i in range(OPTIONS.n): if OPTIONS.dots: print(file=sys.stderr) -print('Wrote {} bytes'.format(bytes), file=sys.stderr) +print(f'Wrote {bytes} bytes', file=sys.stderr) From 408445901469db12996c155e62c8b764de885b2b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:55:31 +0900 Subject: [PATCH 15/62] ethtool-link-mode: apply "ruff format" --- src/shared/ethtool-link-mode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/ethtool-link-mode.py b/src/shared/ethtool-link-mode.py index 6d23f3c43a..c64fa6c456 100755 --- a/src/shared/ethtool-link-mode.py +++ b/src/shared/ethtool-link-mode.py @@ -7,7 +7,7 @@ import subprocess import sys OVERRIDES = { - 'autoneg' : 'autonegotiation', + 'autoneg': 'autonegotiation', } mode = sys.argv[1] From 246e4594ed3b7d7ff1bdd312ebcb846869d96b76 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:56:07 +0900 Subject: [PATCH 16/62] generate-dns_type: apply "ruff format" and "ruff check --fix" --- src/shared/generate-dns_type-gperf.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/shared/generate-dns_type-gperf.py b/src/shared/generate-dns_type-gperf.py index 0fd003a802..79d92cb617 100755 --- a/src/shared/generate-dns_type-gperf.py +++ b/src/shared/generate-dns_type-gperf.py @@ -1,26 +1,24 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later -"""Generate %-from-name.gperf from %-list.txt -""" +"""Generate %-from-name.gperf from %-list.txt""" import sys name, prefix, input = sys.argv[1:] -print("""\ -%{ +print(f'''\ +%{{ _Pragma("GCC diagnostic ignored \\"-Wimplicit-fallthrough\\"") #if __GNUC__ >= 15 _Pragma("GCC diagnostic ignored \\"-Wzero-as-null-pointer-constant\\"") #endif -%}""") -print("""\ -struct {}_name {{ const char* name; int id; }}; +%}} +struct {name}_name {{ const char* name; int id; }}; %null-strings -%%""".format(name)) +%%''') for line in open(input): line = line.rstrip() s = line.replace('_', '-') - print("{}, {}{}".format(s, prefix, line)) + print(f'{s}, {prefix}{line}') From d86df93f96db391b381a7eca31574372da6abfa2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:35:03 +0900 Subject: [PATCH 17/62] generate-syscall-list: apply "ruff check --fix" --- src/shared/generate-syscall-list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/generate-syscall-list.py b/src/shared/generate-syscall-list.py index c0975a06da..111e8b7367 100755 --- a/src/shared/generate-syscall-list.py +++ b/src/shared/generate-syscall-list.py @@ -4,4 +4,4 @@ import sys for line in open(sys.argv[1]): - print('"{}\\0"'.format(line.strip())) + print(f'"{line.strip()}\\0"') From 58aa8824f3d611473b9e4e4b732e2d6b826c98ba Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:56:52 +0900 Subject: [PATCH 18/62] test_ukify: apply "ruff format" Also drops unnecessary noqa attribute for importing pefile. --- src/ukify/test/test_ukify.py | 545 ++++++++++++++++++++--------------- 1 file changed, 319 insertions(+), 226 deletions(-) diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py index 224a38569f..0fe27601f1 100755 --- a/src/ukify/test/test_ukify.py +++ b/src/ukify/test/test_ukify.py @@ -33,8 +33,7 @@ except ImportError as e: sys.exit(77) try: - # pyflakes: noqa - import pefile # noqa + import pefile except ImportError as e: print(str(e), file=sys.stderr) sys.exit(77) @@ -59,42 +58,51 @@ except ValueError: slow_tests = True arg_tools = ['--tools', build_root] if build_root else [] -if build_root and ( - p := pathlib.Path(f"{build_root}/linux{ukify.guess_efi_arch()}.efi.stub") -).exists(): +if ( + build_root + and (p := pathlib.Path(f'{build_root}/linux{ukify.guess_efi_arch()}.efi.stub')).exists() +): # fmt: skip arg_tools += ['--stub', p] + def systemd_measure(): opts = ukify.create_parser().parse_args(arg_tools) return ukify.find_tool('systemd-measure', opts=opts) + def test_guess_efi_arch(): arch = ukify.guess_efi_arch() assert arch in ukify.EFI_ARCHES + def test_shell_join(): assert ukify.shell_join(['a', 'b', ' ']) == "a b ' '" + def test_round_up(): assert ukify.round_up(0) == 0 assert ukify.round_up(4095) == 4096 assert ukify.round_up(4096) == 4096 assert ukify.round_up(4097) == 8192 + def test_namespace_creation(): ns = ukify.create_parser().parse_args(()) assert ns.linux is None assert ns.initrd is None + def test_config_example(): ex = ukify.config_example() assert '[UKI]' in ex assert 'Splash = BMP' in ex + def test_apply_config(tmp_path): config = tmp_path / 'config1.conf' - config.write_text(textwrap.dedent( - f''' + config.write_text( + textwrap.dedent( + f''' [UKI] Linux = LINUX Initrd = initrd1 initrd2 @@ -117,7 +125,9 @@ def test_apply_config(tmp_path): PCRPrivateKey = some/path7 PCRPublicKey = some/path8 Phases = {':'.join(ukify.KNOWN_PHASES)} - ''')) + ''' + ) + ) ns = ukify.create_parser().parse_args(['build']) ns.linux = None @@ -125,9 +135,11 @@ def test_apply_config(tmp_path): ukify.apply_config(ns, config) assert ns.linux == pathlib.Path('LINUX') - assert ns.initrd == [pathlib.Path('initrd1'), - pathlib.Path('initrd2'), - pathlib.Path('initrd3')] + assert ns.initrd == [ + pathlib.Path('initrd1'), + pathlib.Path('initrd2'), + pathlib.Path('initrd3'), + ] assert ns.cmdline == '1 2 3 4 5\n6 7 8' assert ns.os_release == '@some/path1' assert ns.devicetree == pathlib.Path('some/path2') @@ -148,9 +160,11 @@ def test_apply_config(tmp_path): ukify.finalize_options(ns) assert ns.linux == pathlib.Path('LINUX') - assert ns.initrd == [pathlib.Path('initrd1'), - pathlib.Path('initrd2'), - pathlib.Path('initrd3')] + assert ns.initrd == [ + pathlib.Path('initrd1'), + pathlib.Path('initrd2'), + pathlib.Path('initrd3'), + ] assert ns.cmdline == '1 2 3 4 5 6 7 8' assert ns.os_release == pathlib.Path('some/path1') assert ns.devicetree == pathlib.Path('some/path2') @@ -168,6 +182,7 @@ def test_apply_config(tmp_path): assert ns.pcr_public_keys == ['some/path8'] assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']] + def test_parse_args_minimal(): with pytest.raises(ValueError): ukify.parse_args([]) @@ -175,32 +190,39 @@ def test_parse_args_minimal(): opts = ukify.parse_args('arg1 arg2'.split()) assert opts.linux == pathlib.Path('arg1') assert opts.initrd == [pathlib.Path('arg2')] - assert opts.os_release in (pathlib.Path('/etc/os-release'), - pathlib.Path('/usr/lib/os-release')) + assert opts.os_release in ( + pathlib.Path('/etc/os-release'), + pathlib.Path('/usr/lib/os-release'), + ) + def test_parse_args_many_deprecated(): opts = ukify.parse_args( - ['/ARG1', '///ARG2', '/ARG3 WITH SPACE', - '--cmdline=a b c', - '--os-release=K1=V1\nK2=V2', - '--devicetree=DDDDTTTT', - '--splash=splash', - '--pcrpkey=PATH', - '--uname=1.2.3', - '--stub=STUBPATH', - '--pcr-private-key=PKEY1', - '--pcr-public-key=PKEY2', - '--pcr-banks=SHA1,SHA256', - '--signing-engine=ENGINE', - '--secureboot-private-key=SBKEY', - '--secureboot-certificate=SBCERT', - '--sign-kernel', - '--no-sign-kernel', - '--tools=TOOLZ///', - '--output=OUTPUT', - '--measure', - '--no-measure', - ]) + [ + '/ARG1', + '///ARG2', + '/ARG3 WITH SPACE', + '--cmdline=a b c', + '--os-release=K1=V1\nK2=V2', + '--devicetree=DDDDTTTT', + '--splash=splash', + '--pcrpkey=PATH', + '--uname=1.2.3', + '--stub=STUBPATH', + '--pcr-private-key=PKEY1', + '--pcr-public-key=PKEY2', + '--pcr-banks=SHA1,SHA256', + '--signing-engine=ENGINE', + '--secureboot-private-key=SBKEY', + '--secureboot-certificate=SBCERT', + '--sign-kernel', + '--no-sign-kernel', + '--tools=TOOLZ///', + '--output=OUTPUT', + '--measure', + '--no-measure', + ] + ) assert opts.linux == pathlib.Path('/ARG1') assert opts.initrd == [pathlib.Path('/ARG2'), pathlib.Path('/ARG3 WITH SPACE')] assert opts.cmdline == 'a b c' @@ -221,34 +243,37 @@ def test_parse_args_many_deprecated(): assert opts.output == pathlib.Path('OUTPUT') assert opts.measure is False + def test_parse_args_many(): opts = ukify.parse_args( - ['build', - '--linux=/ARG1', - '--initrd=///ARG2', - '--initrd=/ARG3 WITH SPACE', - '--cmdline=a b c', - '--os-release=K1=V1\nK2=V2', - '--devicetree=DDDDTTTT', - '--splash=splash', - '--pcrpkey=PATH', - '--uname=1.2.3', - '--stub=STUBPATH', - '--pcr-private-key=PKEY1', - '--pcr-public-key=PKEY2', - '--pcr-banks=SHA1,SHA256', - '--signing-engine=ENGINE', - '--secureboot-private-key=SBKEY', - '--secureboot-certificate=SBCERT', - '--sign-kernel', - '--no-sign-kernel', - '--tools=TOOLZ///', - '--output=OUTPUT', - '--measure', - '--no-measure', - '--policy-digest', - '--no-policy-digest', - ]) + [ + 'build', + '--linux=/ARG1', + '--initrd=///ARG2', + '--initrd=/ARG3 WITH SPACE', + '--cmdline=a b c', + '--os-release=K1=V1\nK2=V2', + '--devicetree=DDDDTTTT', + '--splash=splash', + '--pcrpkey=PATH', + '--uname=1.2.3', + '--stub=STUBPATH', + '--pcr-private-key=PKEY1', + '--pcr-public-key=PKEY2', + '--pcr-banks=SHA1,SHA256', + '--signing-engine=ENGINE', + '--secureboot-private-key=SBKEY', + '--secureboot-certificate=SBCERT', + '--sign-kernel', + '--no-sign-kernel', + '--tools=TOOLZ///', + '--output=OUTPUT', + '--measure', + '--no-measure', + '--policy-digest', + '--no-policy-digest', + ] + ) assert opts.linux == pathlib.Path('/ARG1') assert opts.initrd == [pathlib.Path('/ARG2'), pathlib.Path('/ARG3 WITH SPACE')] assert opts.cmdline == 'a b c' @@ -270,14 +295,17 @@ def test_parse_args_many(): assert opts.measure is False assert opts.policy_digest is False + def test_parse_sections(): opts = ukify.parse_args( - ['build', - '--linux=/ARG1', - '--initrd=/ARG2', - '--section=test:TESTTESTTEST', - '--section=test2:@FILE', - ]) + [ + 'build', + '--linux=/ARG1', + '--initrd=/ARG2', + '--section=test:TESTTESTTEST', + '--section=test2:@FILE', + ] + ) assert opts.linux == pathlib.Path('/ARG1') assert opts.initrd == [pathlib.Path('/ARG2')] @@ -293,11 +321,13 @@ def test_parse_sections(): assert opts.sections[1].tmpfile is None assert opts.sections[1].measure is False + def test_config_priority(tmp_path): config = tmp_path / 'config1.conf' # config: use pesign and give certdir + certname - config.write_text(textwrap.dedent( - f''' + config.write_text( + textwrap.dedent( + f''' [UKI] Linux = LINUX Initrd = initrd1 initrd2 @@ -321,44 +351,50 @@ def test_config_priority(tmp_path): PCRPrivateKey = some/path7 PCRPublicKey = some/path8 Phases = {':'.join(ukify.KNOWN_PHASES)} - ''')) + ''' + ) + ) # args: use sbsign and give key + cert, should override pesign opts = ukify.parse_args( - ['build', - '--linux=/ARG1', - '--initrd=///ARG2', - '--initrd=/ARG3 WITH SPACE', - '--cmdline= a b c ', - '--os-release=K1=V1\nK2=V2', - '--devicetree=DDDDTTTT', - '--splash=splash', - '--pcrpkey=PATH', - '--uname=1.2.3', - '--stub=STUBPATH', - '--pcr-private-key=PKEY1', - '--pcr-public-key=PKEY2', - '--pcr-banks=SHA1,SHA256', - '--signing-engine=ENGINE', - '--signtool=sbsign', - '--secureboot-private-key=SBKEY', - '--secureboot-certificate=SBCERT', - '--sign-kernel', - '--no-sign-kernel', - '--tools=TOOLZ///', - '--output=OUTPUT', - '--measure', - ]) + [ + 'build', + '--linux=/ARG1', + '--initrd=///ARG2', + '--initrd=/ARG3 WITH SPACE', + '--cmdline= a b c ', + '--os-release=K1=V1\nK2=V2', + '--devicetree=DDDDTTTT', + '--splash=splash', + '--pcrpkey=PATH', + '--uname=1.2.3', + '--stub=STUBPATH', + '--pcr-private-key=PKEY1', + '--pcr-public-key=PKEY2', + '--pcr-banks=SHA1,SHA256', + '--signing-engine=ENGINE', + '--signtool=sbsign', + '--secureboot-private-key=SBKEY', + '--secureboot-certificate=SBCERT', + '--sign-kernel', + '--no-sign-kernel', + '--tools=TOOLZ///', + '--output=OUTPUT', + '--measure', + ] + ) ukify.apply_config(opts, config) ukify.finalize_options(opts) assert opts.linux == pathlib.Path('/ARG1') - assert opts.initrd == [pathlib.Path('initrd1'), - pathlib.Path('initrd2'), - pathlib.Path('initrd3'), - pathlib.Path('/ARG2'), - pathlib.Path('/ARG3 WITH SPACE')] + assert opts.initrd == [ + pathlib.Path('initrd1'), + pathlib.Path('initrd2'), + pathlib.Path('initrd3'), + pathlib.Path('/ARG2'), + pathlib.Path('/ARG3 WITH SPACE'), + ] assert opts.cmdline == 'a b c' assert opts.os_release == 'K1=V1\nK2=V2' assert opts.devicetree == pathlib.Path('DDDDTTTT') @@ -370,16 +406,17 @@ def test_config_priority(tmp_path): assert opts.pcr_public_keys == ['PKEY2', 'some/path8'] assert opts.pcr_banks == ['SHA1', 'SHA256'] assert opts.signing_engine == 'ENGINE' - assert opts.signtool == 'sbsign' # from args - assert opts.sb_key == 'SBKEY' # from args - assert opts.sb_cert == pathlib.Path('SBCERT') # from args - assert opts.sb_certdir == 'some/path5' # from config - assert opts.sb_cert_name == 'some/name1' # from config + assert opts.signtool == 'sbsign' # from args + assert opts.sb_key == 'SBKEY' # from args + assert opts.sb_cert == pathlib.Path('SBCERT') # from args + assert opts.sb_certdir == 'some/path5' # from config + assert opts.sb_cert_name == 'some/name1' # from config assert opts.sign_kernel is False assert opts.tools == [pathlib.Path('TOOLZ/')] assert opts.output == pathlib.Path('OUTPUT') assert opts.measure is True + def test_help(capsys): with pytest.raises(SystemExit): ukify.parse_args(['--help']) @@ -387,6 +424,7 @@ def test_help(capsys): assert '--section' in out.out assert not out.err + def test_help_display(capsys): with pytest.raises(SystemExit): ukify.parse_args(['inspect', '--help']) @@ -394,6 +432,7 @@ def test_help_display(capsys): assert '--section' in out.out assert not out.err + def test_help_error_deprecated(capsys): with pytest.raises(SystemExit): ukify.parse_args(['a', 'b', '--no-such-option']) @@ -402,6 +441,7 @@ def test_help_error_deprecated(capsys): assert '--no-such-option' in out.err assert len(out.err.splitlines()) == 1 + def test_help_error(capsys): with pytest.raises(SystemExit): ukify.parse_args(['build', '--no-such-option']) @@ -410,6 +450,7 @@ def test_help_error(capsys): assert '--no-such-option' in out.err assert len(out.err.splitlines()) == 1 + @pytest.fixture(scope='session') def kernel_initrd(): items = sorted(glob.glob('/lib/modules/*/vmlinuz')) @@ -426,6 +467,7 @@ def kernel_initrd(): # We don't look _into_ the initrd. Any file is OK. return ['--linux', linux, '--initrd', ukify.__file__] + def test_check_splash(): try: # pyflakes: noqa @@ -436,16 +478,19 @@ def test_check_splash(): with pytest.raises(OSError): ukify.check_splash(os.devnull) + def test_basic_operation(kernel_initrd, tmp_path): if kernel_initrd is None: pytest.skip('linux+initrd not found') output = f'{tmp_path}/basic.efi' - opts = ukify.parse_args([ - 'build', - *kernel_initrd, - f'--output={output}', - ]) + opts = ukify.parse_args( + [ + 'build', + *kernel_initrd, + f'--output={output}', + ] + ) try: ukify.check_inputs(opts) except OSError as e: @@ -458,20 +503,23 @@ def test_basic_operation(kernel_initrd, tmp_path): shutil.rmtree(tmp_path) + def test_sections(kernel_initrd, tmp_path): if kernel_initrd is None: pytest.skip('linux+initrd not found') output = f'{tmp_path}/basic.efi' - opts = ukify.parse_args([ - 'build', - *kernel_initrd, - f'--output={output}', - '--uname=1.2.3', - '--cmdline=ARG1 ARG2 ARG3', - '--os-release=K1=V1\nK2=V2\n', - '--section=.test:CONTENTZ', - ]) + opts = ukify.parse_args( + [ + 'build', + *kernel_initrd, + f'--output={output}', + '--uname=1.2.3', + '--cmdline=ARG1 ARG2 ARG3', + '--os-release=K1=V1\nK2=V2\n', + '--section=.test:CONTENTZ', + ] + ) try: ukify.check_inputs(opts) @@ -484,24 +532,25 @@ def test_sections(kernel_initrd, tmp_path): dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text osrel cmdline linux initrd uname test'.split(): - assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) + assert re.search(rf'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) shutil.rmtree(tmp_path) + def test_addon(tmp_path): output = f'{tmp_path}/addon.efi' args = [ 'build', f'--output={output}', '--cmdline=ARG1 ARG2 ARG3', - """--sbat=sbat,1,foo + '''--sbat=sbat,1,foo foo,1 bar,2 -""", +''', '--section=.test:CONTENTZ', - """--sbat=sbat,1,foo + '''--sbat=sbat,1,foo baz,3 -""" +''', ] if stub := os.getenv('EFI_ADDON'): args += [f'--stub={stub}'] @@ -521,26 +570,33 @@ baz,3 dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text cmdline test sbat'.split(): - assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) + assert re.search(rf'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) pe = pefile.PE(output, fast_load=True) found = False for section in pe.sections: - if section.Name.rstrip(b"\x00").decode() == ".sbat": + if section.Name.rstrip(b'\x00').decode() == '.sbat': assert found is False - split = section.get_data().rstrip(b"\x00").decode().splitlines() - assert split == ["sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md", "foo,1", "bar,2", "baz,3"] + split = section.get_data().rstrip(b'\x00').decode().splitlines() + assert split == [ + 'sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md', + 'foo,1', + 'bar,2', + 'baz,3', + ] found = True assert found is True + def unbase64(filename): tmp = tempfile.NamedTemporaryFile() base64.decode(filename.open('rb'), tmp) tmp.flush() return tmp + def test_uname_scraping(kernel_initrd): if kernel_initrd is None: pytest.skip('linux+initrd not found') @@ -549,8 +605,9 @@ def test_uname_scraping(kernel_initrd): uname = ukify.Uname.scrape(kernel_initrd[1]) assert re.match(r'\d+\.\d+\.\d+', uname) + @pytest.mark.skipif(not slow_tests, reason='slow') -@pytest.mark.parametrize("days", [365*10, None]) +@pytest.mark.parametrize('days', [365 * 10, None]) def test_efi_signing_sbsign(days, kernel_initrd, tmp_path): if kernel_initrd is None: pytest.skip('linux+initrd not found') @@ -585,16 +642,20 @@ def test_efi_signing_sbsign(days, kernel_initrd, tmp_path): if shutil.which('sbverify'): # let's check that sbverify likes the resulting file - dump = subprocess.check_output([ - 'sbverify', - '--cert', cert.name, - output, - ], text=True) + dump = subprocess.check_output( + [ + 'sbverify', + '--cert', cert.name, + output, + ], + text=True, + ) # fmt: skip assert 'Signature verification OK' in dump shutil.rmtree(tmp_path) + @pytest.mark.skipif(not slow_tests, reason='slow') def test_efi_signing_pesign(kernel_initrd, tmp_path): if kernel_initrd is None: @@ -613,16 +674,18 @@ def test_efi_signing_pesign(kernel_initrd, tmp_path): subprocess.check_call(cmd) output = f'{tmp_path}/signed.efi' - opts = ukify.parse_args([ - 'build', - *kernel_initrd, - f'--output={output}', - '--uname=1.2.3', - '--signtool=pesign', - '--cmdline=ARG1 ARG2 ARG3', - f'--secureboot-certificate-name={name}', - f'--secureboot-certificate-dir={nss_db}', - ]) + opts = ukify.parse_args( + [ + 'build', + *kernel_initrd, + f'--output={output}', + '--uname=1.2.3', + '--signtool=pesign', + '--cmdline=ARG1 ARG2 ARG3', + f'--secureboot-certificate-name={name}', + f'--secureboot-certificate-dir={nss_db}', + ] + ) try: ukify.check_inputs(opts) @@ -632,15 +695,20 @@ def test_efi_signing_pesign(kernel_initrd, tmp_path): ukify.make_uki(opts) # let's check that pesign likes the resulting file - dump = subprocess.check_output([ - 'pesign', '-S', - '-i', output, - ], text=True) + dump = subprocess.check_output( + [ + 'pesign', + '-S', + '-i', output, + ], + text=True, + ) # fmt: skip assert f"The signer's common name is {author}" in dump shutil.rmtree(tmp_path) + def test_inspect(kernel_initrd, tmp_path, capsys, osrel=True): if kernel_initrd is None: pytest.skip('linux+initrd not found') @@ -652,9 +720,9 @@ def test_inspect(kernel_initrd, tmp_path, capsys, osrel=True): key = unbase64(ourdir / 'example.signing.key.base64') output = f'{tmp_path}/signed2.efi' - uname_arg='1.2.3' - osrel_arg='Linux' if osrel else '' - cmdline_arg='ARG1 ARG2 ARG3' + uname_arg = '1.2.3' + osrel_arg = 'Linux' if osrel else '' + cmdline_arg = 'ARG1 ARG2 ARG3' args = [ 'build', @@ -698,9 +766,11 @@ def test_inspect(kernel_initrd, tmp_path, capsys, osrel=True): shutil.rmtree(tmp_path) + def test_inspect_no_osrel(kernel_initrd, tmp_path, capsys): test_inspect(kernel_initrd, tmp_path, capsys, osrel=False) + @pytest.mark.skipif(not slow_tests, reason='slow') def test_pcr_signing(kernel_initrd, tmp_path): if kernel_initrd is None: @@ -722,7 +792,7 @@ def test_pcr_signing(kernel_initrd, tmp_path): '--uname=1.2.3', '--cmdline=ARG1 ARG2 ARG3', '--os-release=ID=foobar\n', - '--pcr-banks=sha384', # sha1 might not be allowed, use something else + '--pcr-banks=sha384', # sha1 might not be allowed, use something else f'--pcr-private-key={priv.name}', ] + arg_tools @@ -743,21 +813,25 @@ def test_pcr_signing(kernel_initrd, tmp_path): dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text osrel cmdline linux initrd uname pcrsig'.split(): - assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) + assert re.search(rf'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) # objcopy fails when called without an output argument (EPERM). # It also fails when called with /dev/null (file truncated). # It also fails when called with /dev/zero (because it reads the # output file, infinitely in this case.) # So let's just call it with a dummy output argument. - subprocess.check_call([ - 'objcopy', - *(f'--dump-section=.{n}={tmp_path}/out.{n}' for n in ( - 'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline')), - output, - tmp_path / 'dummy', - ], - text=True) + subprocess.check_call( + [ + 'objcopy', + *( + f'--dump-section=.{n}={tmp_path}/out.{n}' + for n in ('pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline') + ), + output, + tmp_path / 'dummy', + ], + text=True, + ) assert open(tmp_path / 'out.pcrpkey').read() == open(pub.name).read() assert open(tmp_path / 'out.osrel').read() == 'ID=foobar\n' @@ -766,10 +840,11 @@ def test_pcr_signing(kernel_initrd, tmp_path): sig = open(tmp_path / 'out.pcrsig').read() sig = json.loads(sig) assert list(sig.keys()) == ['sha384'] - assert len(sig['sha384']) == 4 # four items for four phases + assert len(sig['sha384']) == 4 # four items for four phases shutil.rmtree(tmp_path) + @pytest.mark.skipif(not slow_tests, reason='slow') def test_pcr_signing2(kernel_initrd, tmp_path): if kernel_initrd is None: @@ -791,24 +866,27 @@ def test_pcr_signing2(kernel_initrd, tmp_path): output = f'{tmp_path}/signed.efi' assert kernel_initrd[0] == '--linux' - opts = ukify.parse_args([ - 'build', - *kernel_initrd[:2], - f'--initrd={microcode.name}', - *kernel_initrd[2:], - f'--output={output}', - '--uname=1.2.3', - '--cmdline=ARG1 ARG2 ARG3', - '--os-release=ID=foobar\n', - '--pcr-banks=sha384', - f'--pcrpkey={pub2.name}', - f'--pcr-public-key={pub.name}', - f'--pcr-private-key={priv.name}', - '--phases=enter-initrd enter-initrd:leave-initrd', - f'--pcr-public-key={pub2.name}', - f'--pcr-private-key={priv2.name}', - '--phases=sysinit ready shutdown final', # yes, those phase paths are not reachable - ] + arg_tools) + opts = ukify.parse_args( + [ + 'build', + *kernel_initrd[:2], + f'--initrd={microcode.name}', + *kernel_initrd[2:], + f'--output={output}', + '--uname=1.2.3', + '--cmdline=ARG1 ARG2 ARG3', + '--os-release=ID=foobar\n', + '--pcr-banks=sha384', + f'--pcrpkey={pub2.name}', + f'--pcr-public-key={pub.name}', + f'--pcr-private-key={priv.name}', + '--phases=enter-initrd enter-initrd:leave-initrd', + f'--pcr-public-key={pub2.name}', + f'--pcr-private-key={priv2.name}', + '--phases=sysinit ready shutdown final', # yes, those phase paths are not reachable + ] + + arg_tools + ) try: ukify.check_inputs(opts) @@ -821,16 +899,20 @@ def test_pcr_signing2(kernel_initrd, tmp_path): dump = subprocess.check_output(['objdump', '-h', output], text=True) for sect in 'text osrel cmdline linux initrd uname pcrsig'.split(): - assert re.search(fr'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) + assert re.search(rf'^\s*\d+\s+\.{sect}\s+[0-9a-f]+', dump, re.MULTILINE) - subprocess.check_call([ - 'objcopy', - *(f'--dump-section=.{n}={tmp_path}/out.{n}' for n in ( - 'pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline', 'initrd')), - output, - tmp_path / 'dummy', - ], - text=True) + subprocess.check_call( + [ + 'objcopy', + *( + f'--dump-section=.{n}={tmp_path}/out.{n}' + for n in ('pcrpkey', 'pcrsig', 'osrel', 'uname', 'cmdline', 'initrd') + ), + output, + tmp_path / 'dummy', + ], + text=True, + ) assert open(tmp_path / 'out.pcrpkey').read() == open(pub2.name).read() assert open(tmp_path / 'out.osrel').read() == 'ID=foobar\n' @@ -841,22 +923,25 @@ def test_pcr_signing2(kernel_initrd, tmp_path): sig = open(tmp_path / 'out.pcrsig').read() sig = json.loads(sig) assert list(sig.keys()) == ['sha384'] - assert len(sig['sha384']) == 6 # six items for six phases paths + assert len(sig['sha384']) == 6 # six items for six phases paths shutil.rmtree(tmp_path) + def test_key_cert_generation(tmp_path): - opts = ukify.parse_args([ - 'genkey', - f"--pcr-public-key={tmp_path / 'pcr1.pub.pem'}", - f"--pcr-private-key={tmp_path / 'pcr1.priv.pem'}", - '--phases=enter-initrd enter-initrd:leave-initrd', - f"--pcr-public-key={tmp_path / 'pcr2.pub.pem'}", - f"--pcr-private-key={tmp_path / 'pcr2.priv.pem'}", - '--phases=sysinit ready', - f"--secureboot-private-key={tmp_path / 'sb.priv.pem'}", - f"--secureboot-certificate={tmp_path / 'sb.cert.pem'}", - ]) + opts = ukify.parse_args( + [ + 'genkey', + f'--pcr-public-key={tmp_path / "pcr1.pub.pem"}', + f'--pcr-private-key={tmp_path / "pcr1.priv.pem"}', + '--phases=enter-initrd enter-initrd:leave-initrd', + f'--pcr-public-key={tmp_path / "pcr2.pub.pem"}', + f'--pcr-private-key={tmp_path / "pcr2.priv.pem"}', + '--phases=sysinit ready', + f'--secureboot-private-key={tmp_path / "sb.priv.pem"}', + f'--secureboot-certificate={tmp_path / "sb.cert.pem"}', + ] + ) assert opts.verb == 'genkey' ukify.check_cert_and_keys_nonexistent(opts) @@ -867,39 +952,46 @@ def test_key_cert_generation(tmp_path): if not shutil.which('openssl'): return - for key in (tmp_path / 'pcr1.priv.pem', - tmp_path / 'pcr2.priv.pem', - tmp_path / 'sb.priv.pem'): - out = subprocess.check_output([ - 'openssl', 'rsa', - '-in', key, - '-text', - '-noout', - ], text = True) + for key in (tmp_path / 'pcr1.priv.pem', tmp_path / 'pcr2.priv.pem', tmp_path / 'sb.priv.pem'): + out = subprocess.check_output( + [ + 'openssl', 'rsa', + '-in', key, + '-text', + '-noout', + ], + text=True, + ) # fmt: skip assert 'Private-Key' in out assert '2048 bit' in out - for pub in (tmp_path / 'pcr1.pub.pem', - tmp_path / 'pcr2.pub.pem'): - out = subprocess.check_output([ - 'openssl', 'rsa', - '-pubin', - '-in', pub, - '-text', - '-noout', - ], text = True) + for pub in (tmp_path / 'pcr1.pub.pem', tmp_path / 'pcr2.pub.pem'): + out = subprocess.check_output( + [ + 'openssl', 'rsa', + '-pubin', + '-in', pub, + '-text', + '-noout', + ], + text=True, + ) # fmt: skip assert 'Public-Key' in out assert '2048 bit' in out - out = subprocess.check_output([ - 'openssl', 'x509', - '-in', tmp_path / 'sb.cert.pem', - '-text', - '-noout', - ], text = True) + out = subprocess.check_output( + [ + 'openssl', 'x509', + '-in', tmp_path / 'sb.cert.pem', + '-text', + '-noout', + ], + text=True, + ) # fmt: skip assert 'Certificate' in out assert re.search(r'Issuer: CN\s?=\s?SecureBoot signing key on host', out) + @pytest.mark.skipif(not slow_tests, reason='slow') def test_join_pcrsig(capsys, kernel_initrd, tmp_path): if kernel_initrd is None: @@ -965,5 +1057,6 @@ def test_join_pcrsig(capsys, kernel_initrd, tmp_path): shutil.rmtree(tmp_path) + if __name__ == '__main__': sys.exit(pytest.main(sys.argv)) From 8fd2385709238660b900ae699a791b275b27b985 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:57:35 +0900 Subject: [PATCH 19/62] create-sys-script: apply "ruff format" and "ruff check --fix" --- test/create-sys-script.py | 59 ++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/test/create-sys-script.py b/test/create-sys-script.py index 11ed185de0..f2995bcfd6 100755 --- a/test/create-sys-script.py +++ b/test/create-sys-script.py @@ -1,28 +1,29 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later -OUTFILE_HEADER = """#!/usr/bin/env python3 +import filecmp +import os +import stat +import subprocess +import sys +import tempfile + +OUTFILE_HEADER = '''#!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later # # create-sys-script.py # # © 2017 Canonical Ltd. # Author: Dan Streetman -""" +''' # Use this only to (re-)create the test/sys-script.py script, # after adding or modifying anything in the test/sys/ directory -import os, sys -import stat -import tempfile -import filecmp -import subprocess - OUTFILE_MODE = 0o775 -OUTFILE_FUNCS = r""" +OUTFILE_FUNCS = r''' import os, sys import shutil @@ -36,9 +37,9 @@ def f(path, mode, contents): with open(path, "wb") as f: f.write(contents) os.chmod(path, mode) -""" +''' -OUTFILE_MAIN = """ +OUTFILE_MAIN = ''' if len(sys.argv) < 2: exit("Usage: {} ".format(sys.argv[0])) @@ -49,7 +50,7 @@ os.chdir(sys.argv[1]) if os.path.exists('sys'): shutil.rmtree('sys') -""" +''' def handle_dir(outfile, path): @@ -67,17 +68,17 @@ def escape_single_quotes(b): r = repr(b)[2:-1] # python escapes all ' only if there are ' and " in the string if '"' not in r: - r = r.replace("'", r"\'") + r = r.replace("'", r'\'') # return line with all ' escaped return r def handle_file(outfile, path): m = os.lstat(path).st_mode & 0o777 - with open(path, "rb") as f: + with open(path, 'rb') as f: b = f.read() - if b.count(b"\n") > 1: - r = "\n".join( escape_single_quotes(l) for l in b.split(b"\n") ) + if b.count(b'\n') > 1: + r = '\n'.join(escape_single_quotes(line) for line in b.split(b'\n')) r = f"b'''{r}'''" else: r = repr(b) @@ -85,7 +86,7 @@ def handle_file(outfile, path): def process_sysdir(outfile): - for (dirpath, dirnames, filenames) in os.walk('sys'): + for dirpath, dirnames, filenames in os.walk('sys'): handle_dir(outfile, dirpath) for d in dirnames: path = os.path.join(dirpath, d) @@ -105,17 +106,17 @@ def verify_dir(tmpd, path_a): mode_a = os.lstat(path_a).st_mode mode_b = os.lstat(path_b).st_mode if not stat.S_ISDIR(mode_b): - raise Exception("Not directory") + raise Exception('Not directory') if (mode_a & 0o777) != (mode_b & 0o777): - raise Exception("Permissions mismatch") + raise Exception('Permissions mismatch') def verify_link(tmpd, path_a): path_b = os.path.join(tmpd, path_a) if not stat.S_ISLNK(os.lstat(path_b).st_mode): - raise Exception("Not symlink") + raise Exception('Not symlink') if os.readlink(path_a) != os.readlink(path_b): - raise Exception("Symlink dest mismatch") + raise Exception('Symlink dest mismatch') def verify_file(tmpd, path_a): @@ -123,16 +124,16 @@ def verify_file(tmpd, path_a): mode_a = os.lstat(path_a).st_mode mode_b = os.lstat(path_b).st_mode if not stat.S_ISREG(mode_b): - raise Exception("Not file") + raise Exception('Not file') if (mode_a & 0o777) != (mode_b & 0o777): - raise Exception("Permissions mismatch") + raise Exception('Permissions mismatch') if not filecmp.cmp(path_a, path_b, shallow=False): - raise Exception("File contents mismatch") + raise Exception('File contents mismatch') def verify_script(tmpd): any = False - for (dirpath, dirnames, filenames) in os.walk("sys"): + for dirpath, dirnames, filenames in os.walk('sys'): any = True try: path = dirpath @@ -154,7 +155,8 @@ def verify_script(tmpd): if not any: exit('Nothing found!') -if __name__ == "__main__": + +if __name__ == '__main__': if len(sys.argv) < 2: exit('Usage: create-sys-script.py /path/to/test/') @@ -163,10 +165,9 @@ if __name__ == "__main__": os.chdir(sys.argv[1]) - with open(outfile, "w") as f: + with open(outfile, 'w') as f: os.chmod(outfile, OUTFILE_MODE) - f.write(OUTFILE_HEADER.replace(os.path.basename(sys.argv[0]), - os.path.basename(outfile))) + f.write(OUTFILE_HEADER.replace(os.path.basename(sys.argv[0]), os.path.basename(outfile))) f.write(OUTFILE_FUNCS) f.write(OUTFILE_MAIN) process_sysdir(f) From a562d59fd9f9f0f07c82910c6336a5c7bd135518 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:42:27 +0900 Subject: [PATCH 20/62] generate-directives: apply "ruff check --fix" --- test/fuzz/generate-directives.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/fuzz/generate-directives.py b/test/fuzz/generate-directives.py index d05108962f..0f6a744e34 100755 --- a/test/fuzz/generate-directives.py +++ b/test/fuzz/generate-directives.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later +import collections +import re import sys -import collections, re d = collections.defaultdict(list) for line in open(sys.argv[1]): From f6b2a27e5956b510be992c6dc0fe52a5d4789833 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:58:16 +0900 Subject: [PATCH 21/62] TEST-04-JOURNAL: apply "ruff format" --- .../TEST-04-JOURNAL.units/logs-filtering-syslog.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/integration-tests/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering-syslog.py b/test/integration-tests/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering-syslog.py index e1d3ad7c11..e56348a7a1 100755 --- a/test/integration-tests/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering-syslog.py +++ b/test/integration-tests/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering-syslog.py @@ -5,10 +5,7 @@ import subprocess import syslog if __name__ == '__main__': - syslog.openlog(ident="logs-filtering", logoption=syslog.LOG_PID) - syslog.syslog(syslog.LOG_NOTICE, "Logging from the service, and ~more~ foo bar") + syslog.openlog(ident='logs-filtering', logoption=syslog.LOG_PID) + syslog.syslog(syslog.LOG_NOTICE, 'Logging from the service, and ~more~ foo bar') - subprocess.check_output( - ['journalctl', '--sync'], - stdin=subprocess.DEVNULL, - text=True) + subprocess.check_output(['journalctl', '--sync'], stdin=subprocess.DEVNULL, text=True) From ba90ffaef20081a177005e8aa42c70a08aa438d5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:59:13 +0900 Subject: [PATCH 22/62] networkd-test: apply "ruff format" and "ruff check --fix" --- test/networkd-test.py | 561 ++++++++++++++++++++++++++---------------- 1 file changed, 343 insertions(+), 218 deletions(-) diff --git a/test/networkd-test.py b/test/networkd-test.py index d9f7af3678..27619da6ff 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -33,8 +33,7 @@ IS_CONTAINER = subprocess.call(['systemd-detect-virt', '--quiet', '--container'] NETWORK_UNITDIR = '/run/systemd/network' -NETWORKD_WAIT_ONLINE = shutil.which('systemd-networkd-wait-online', - path='/usr/lib/systemd:/lib/systemd') +NETWORKD_WAIT_ONLINE = shutil.which('systemd-networkd-wait-online', path='/usr/lib/systemd:/lib/systemd') RESOLV_CONF = '/run/systemd/resolve/resolv.conf' @@ -44,10 +43,10 @@ stopped_units = [] def setUpModule(): - global tmpmounts - """Initialize the environment, and perform sanity checks on it.""" + global tmpmounts + if shutil.which('networkctl') is None: raise unittest.SkipTest('networkd not installed') if shutil.which('resolvectl') is None: @@ -57,8 +56,10 @@ def setUpModule(): raise OSError(errno.ENOENT, 'systemd-networkd-wait-online not found') # Do not run any tests if the system is using networkd already and it's not virtualized - if (subprocess.call(['systemctl', 'is-active', '--quiet', 'systemd-networkd.service']) == 0 and - subprocess.call(['systemd-detect-virt', '--quiet']) != 0): + if ( + subprocess.call(['systemctl', 'is-active', '--quiet', 'systemd-networkd.service']) == 0 + and subprocess.call(['systemd-detect-virt', '--quiet']) != 0 + ): raise unittest.SkipTest('not virtualized and networkd is already active') # Ensure we don't mess with an existing networkd config @@ -80,7 +81,9 @@ def setUpModule(): # Generate debugging logs. os.makedirs('/run/systemd/system/systemd-networkd.service.d', exist_ok=True) - with open(f'/run/systemd/system/systemd-networkd.service.d/00-debug.conf', mode='w', encoding='utf-8') as f: + with open( + '/run/systemd/system/systemd-networkd.service.d/00-debug.conf', mode='w', encoding='utf-8' + ) as f: f.write('[Service]\nEnvironment=SYSTEMD_LOG_LEVEL=debug\n') subprocess.call(['systemctl', 'daemon-reload']) @@ -91,10 +94,15 @@ def setUpModule(): if subprocess.call(['getent', 'passwd', 'systemd-network']) != 0: subprocess.call(['useradd', '--system', '--no-create-home', 'systemd-network']) - for d in ['/etc/systemd/network', '/run/systemd/network', - '/run/systemd/netif', '/run/systemd/report', - '/run/systemd/resolve', '/run/systemd/resolve.hook']: - subprocess.check_call(["mount", "-m", "-t", "tmpfs", "none", d]) + for d in [ + '/etc/systemd/network', + '/run/systemd/network', + '/run/systemd/netif', + '/run/systemd/report', + '/run/systemd/resolve', + '/run/systemd/resolve.hook', + ]: + subprocess.check_call(['mount', '-m', '-t', 'tmpfs', 'none', d]) tmpmounts.append(d) if os.path.isdir('/run/systemd/resolve'): os.chmod('/run/systemd/resolve', 0o755) @@ -119,11 +127,11 @@ def setUpModule(): def tearDownModule(): global tmpmounts for d in tmpmounts: - subprocess.check_call(["umount", "--lazy", d]) + subprocess.check_call(['umount', '--lazy', d]) for u in stopped_units: - subprocess.call(["systemctl", "stop", u]) + subprocess.call(['systemctl', 'stop', u]) for u in running_units: - subprocess.call(["systemctl", "restart", u]) + subprocess.call(['systemctl', 'restart', u]) class NetworkdTestingUtilities: @@ -135,14 +143,12 @@ class NetworkdTestingUtilities: def add_veth_pair(self, veth, peer, veth_options=(), peer_options=()): """Add a veth interface pair, and queue them to be removed.""" - subprocess.check_call(['ip', 'link', 'add', 'name', veth] + - list(veth_options) + - ['type', 'veth', 'peer', 'name', peer] + - list(peer_options)) + subprocess.check_call(['ip', 'link', 'add', 'name', veth, *veth_options, + 'type', 'veth', 'peer', 'name', peer, *peer_options]) # fmt: skip self.addCleanup(subprocess.call, ['ip', 'link', 'del', 'dev', peer]) def write_config(self, path, contents): - """"Write a configuration file, and queue it to be removed.""" + """ "Write a configuration file, and queue it to be removed.""" with open(path, 'w') as f: f.write(contents) @@ -155,8 +161,8 @@ class NetworkdTestingUtilities: def write_network_dropin(self, unit_name, dropin_name, contents): """Write a network unit drop-in, and queue it to be removed.""" - dropin_dir = os.path.join(NETWORK_UNITDIR, "{}.d".format(unit_name)) - dropin_path = os.path.join(dropin_dir, "{}.conf".format(dropin_name)) + dropin_dir = os.path.join(NETWORK_UNITDIR, f'{unit_name}.d') + dropin_path = os.path.join(dropin_dir, f'{dropin_name}.conf') os.makedirs(dropin_dir, exist_ok=True) self.addCleanup(os.rmdir, dropin_dir) @@ -169,7 +175,7 @@ class NetworkdTestingUtilities: # Note we don't want to check if interface `link' is managed, we # want to evaluate link variable and pass the value of the link to # assert_link_states e.g. eth0=managed. - self.assert_link_states(**{link:'managed'}) + self.assert_link_states(**{link: 'managed'}) with open(os.path.join('/sys/class/net', link, attribute)) as f: return f.readline().strip() @@ -191,8 +197,7 @@ class NetworkdTestingUtilities: interfaces = set(kwargs) # Wait for the requested interfaces, but don't fail for them. - subprocess.call([NETWORKD_WAIT_ONLINE, '--timeout=5'] + - ['--interface={}'.format(iface) for iface in kwargs]) + subprocess.call([NETWORKD_WAIT_ONLINE, '--timeout=5'] + [f'--interface={iface}' for iface in kwargs]) # Validate each link state found in the networkctl output. out = subprocess.check_output(['networkctl', '--no-legend']).rstrip() @@ -202,14 +207,13 @@ class NetworkdTestingUtilities: iface = fields[1] expected = kwargs[iface] actual = fields[-1] - if (actual != expected and - not (expected == 'managed' and actual != 'unmanaged')): - self.fail("Link {} expects state {}, found {}".format(iface, expected, actual)) + if actual != expected and not (expected == 'managed' and actual != 'unmanaged'): + self.fail(f'Link {iface} expects state {expected}, found {actual}') interfaces.remove(iface) # Ensure that all requested interfaces have been covered. if interfaces: - self.fail("Missing links in status output: {}".format(interfaces)) + self.fail(f'Missing links in status output: {interfaces}') class BridgeTest(NetworkdTestingUtilities, unittest.TestCase): @@ -217,7 +221,9 @@ class BridgeTest(NetworkdTestingUtilities, unittest.TestCase): def wait_online(self): try: - subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', 'port1', '--interface', 'port2', '--timeout=10']) + subprocess.check_call( + [NETWORKD_WAIT_ONLINE, '--interface', 'port1', '--interface', 'port2', '--timeout=10'] + ) except (AssertionError, subprocess.CalledProcessError): # show networkd status, journal, and DHCP server log on failure print('---- interface status ----') @@ -233,40 +239,59 @@ class BridgeTest(NetworkdTestingUtilities, unittest.TestCase): print('---- journal ----') subprocess.check_output(['journalctl', '--sync']) sys.stdout.flush() - subprocess.call(['journalctl', '-b', '--no-pager', '--quiet', '-I', '-u', 'systemd-networkd.service']) + subprocess.call( + ['journalctl', '-b', '--no-pager', '--quiet', '-I', '-u', 'systemd-networkd.service'] + ) raise def setUp(self): - self.write_network('50-port1.netdev', '''\ + self.write_network( + '50-port1.netdev', + '''\ [NetDev] Name=port1 Kind=dummy MACAddress=12:34:56:78:9a:bc -''') - self.write_network('50-port2.netdev', '''\ +''', + ) + self.write_network( + '50-port2.netdev', + '''\ [NetDev] Name=port2 Kind=dummy MACAddress=12:34:56:78:9a:bd -''') - self.write_network('50-mybridge.netdev', '''\ +''', + ) + self.write_network( + '50-mybridge.netdev', + '''\ [NetDev] Name=mybridge Kind=bridge -''') - self.write_network('50-port1.network', '''\ +''', + ) + self.write_network( + '50-port1.network', + '''\ [Match] Name=port1 [Network] Bridge=mybridge -''') - self.write_network('50-port2.network', '''\ +''', + ) + self.write_network( + '50-port2.network', + '''\ [Match] Name=port2 [Network] Bridge=mybridge -''') - self.write_network('50-mybridge.network', '''\ +''', + ) + self.write_network( + '50-mybridge.network', + '''\ [Match] Name=mybridge [Network] @@ -274,7 +299,8 @@ IPv6AcceptRA=no DNS=192.168.250.1 Address=192.168.250.33/24 Gateway=192.168.250.1 -''') +''', + ) subprocess.call(['systemctl', 'reset-failed', 'systemd-networkd', 'systemd-resolved']) subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.wait_online() @@ -290,17 +316,18 @@ Gateway=192.168.250.1 subprocess.check_call(['ip', 'link', 'del', 'port2']) def test_bridge_init(self): - self.assert_link_states( - port1='managed', - port2='managed', - mybridge='managed') + self.assert_link_states(port1='managed', port2='managed', mybridge='managed') def test_bridge_port_priority(self): self.assertEqual(self.read_attr('port1', 'brport/priority'), '32') - self.write_network_dropin('50-port1.network', 'priority', '''\ + self.write_network_dropin( + '50-port1.network', + 'priority', + '''\ [Bridge] Priority=28 -''') +''', + ) subprocess.check_call(['ip', 'link', 'set', 'dev', 'port1', 'down']) subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.wait_online() @@ -309,10 +336,14 @@ Priority=28 def test_bridge_port_priority_set_zero(self): """It should be possible to set the bridge port priority to 0""" self.assertEqual(self.read_attr('port2', 'brport/priority'), '32') - self.write_network_dropin('50-port2.network', 'priority', '''\ + self.write_network_dropin( + '50-port2.network', + 'priority', + '''\ [Bridge] Priority=0 -''') +''', + ) subprocess.check_call(['ip', 'link', 'set', 'dev', 'port2', 'down']) subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.wait_online() @@ -321,7 +352,10 @@ Priority=0 def test_bridge_port_property(self): """Test the "[Bridge]" section keys""" self.assertEqual(self.read_attr('port2', 'brport/priority'), '32') - self.write_network_dropin('50-port2.network', 'property', '''\ + self.write_network_dropin( + '50-port2.network', + 'property', + '''\ [Bridge] UnicastFlood=true HairPin=true @@ -331,7 +365,8 @@ FastLeave=true AllowPortToBeRoot=true Cost=555 Priority=23 -''') +''', + ) subprocess.check_call(['ip', 'link', 'set', 'dev', 'port2', 'down']) subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.wait_online() @@ -345,14 +380,15 @@ Priority=23 self.assertEqual(self.read_attr('port2', 'brport/bpdu_guard'), '0') self.assertEqual(self.read_attr('port2', 'brport/root_block'), '0') + class ClientTestBase(NetworkdTestingUtilities): """Provide common methods for testing networkd against servers.""" @classmethod def setUpClass(klass): klass.orig_log_level = subprocess.check_output( - ['systemctl', 'show', '--value', '--property', 'LogLevel'], - universal_newlines=True).strip() + ['systemctl', 'show', '--value', '--property', 'LogLevel'], universal_newlines=True + ).strip() subprocess.check_call(['systemd-analyze', 'log-level', 'debug']) @classmethod @@ -368,9 +404,9 @@ class ClientTestBase(NetworkdTestingUtilities): # get current journal cursor subprocess.check_output(['journalctl', '--sync']) - out = subprocess.check_output(['journalctl', '-b', '--quiet', - '--no-pager', '-n0', '--show-cursor'], - universal_newlines=True) + out = subprocess.check_output( + ['journalctl', '-b', '--quiet', '--no-pager', '-n0', '--show-cursor'], universal_newlines=True + ) self.assertTrue(out.startswith('-- cursor:')) self.journal_cursor = out.split()[-1] @@ -383,44 +419,44 @@ class ClientTestBase(NetworkdTestingUtilities): subprocess.call(['systemctl', 'stop', 'systemd-networkd-varlink.socket']) subprocess.call(['systemctl', 'stop', 'systemd-networkd-varlink-metrics.socket']) subprocess.call(['systemctl', 'stop', 'systemd-networkd.service']) - subprocess.call(['ip', 'link', 'del', 'dummy0'], - stderr=subprocess.DEVNULL) + subprocess.call(['ip', 'link', 'del', 'dummy0'], stderr=subprocess.DEVNULL) def show_journal(self, unit): - '''Show journal of given unit since start of the test''' + """Show journal of given unit since start of the test""" - print('---- {} ----'.format(unit)) + print(f'---- {unit} ----') subprocess.check_output(['journalctl', '--sync']) sys.stdout.flush() - subprocess.call(['journalctl', '-b', '--no-pager', '--quiet', - '--cursor', self.journal_cursor, '-u', unit]) + subprocess.call( + ['journalctl', '-b', '--no-pager', '--quiet', '--cursor', self.journal_cursor, '-u', unit] + ) def show_ifaces(self): - '''Show network interfaces''' + """Show network interfaces""" print('--- networkctl ---') sys.stdout.flush() subprocess.call(['networkctl', 'status', '-n', '0', '-a']) def show_resolvectl(self): - '''Show resolved settings''' + """Show resolved settings""" print('--- resolvectl ---') sys.stdout.flush() subprocess.call(['resolvectl']) def create_iface(self, ipv6=False): - '''Create test interface with DHCP server behind it''' + """Create test interface with DHCP server behind it""" raise NotImplementedError('must be implemented by a subclass') def shutdown_iface(self): - '''Remove test interface and stop DHCP server''' + """Remove test interface and stop DHCP server""" raise NotImplementedError('must be implemented by a subclass') def print_server_log(self): - '''Print DHCP server log for debugging failures''' + """Print DHCP server log for debugging failures""" raise NotImplementedError('must be implemented by a subclass') @@ -432,16 +468,18 @@ class ClientTestBase(NetworkdTestingUtilities): self.show_journal(unit) raise - def do_test(self, coldplug=True, ipv6=False, extra_opts='', - online_timeout=10, dhcp_mode='yes'): + def do_test(self, coldplug=True, ipv6=False, extra_opts='', online_timeout=10, dhcp_mode='yes'): self.start_unit('systemd-resolved') - self.write_network(self.config, '''\ + self.write_network( + self.config, + f'''\ [Match] -Name={iface} +Name={self.iface} [Network] DHCP={dhcp_mode} {extra_opts} -'''.format(iface=self.iface, dhcp_mode=dhcp_mode, extra_opts=extra_opts)) +''', + ) if coldplug: # create interface first, then start networkd @@ -456,8 +494,9 @@ DHCP={dhcp_mode} self.start_unit('systemd-networkd') try: - subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', - self.iface, '--timeout=%i' % online_timeout]) + subprocess.check_call( + [NETWORKD_WAIT_ONLINE, '--interface', self.iface, f'--timeout={online_timeout:d}'] + ) if ipv6: # check iface state and IP 6 address; FIXME: we need to wait a bit @@ -465,7 +504,12 @@ DHCP={dhcp_mode} # IPv6, but we want to wait for both for _ in range(10): out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.iface]) - if b'state UP' in out and b'inet6 2600' in out and b'inet 192.168' in out and b'tentative' not in out: + if ( + b'state UP' in out + and b'inet6 2600' in out + and b'inet 192.168' in out + and b'tentative' not in out + ): break time.sleep(1) else: @@ -476,43 +520,43 @@ DHCP={dhcp_mode} else: # should have link-local address on IPv6 only out = subprocess.check_output(['ip', '-6', 'a', 'show', 'dev', self.iface]) - self.assertRegex(out, br'inet6 fe80::.* scope link') + self.assertRegex(out, rb'inet6 fe80::.* scope link') self.assertNotIn(b'scope global', out) # should have IPv4 address out = subprocess.check_output(['ip', '-4', 'a', 'show', 'dev', self.iface]) self.assertIn(b'state UP', out) - self.assertRegex(out, br'inet 192.168.5.\d+/.* scope global dynamic') + self.assertRegex(out, rb'inet 192.168.5.\d+/.* scope global dynamic') # check networkctl state out = subprocess.check_output(['networkctl']) - self.assertRegex(out, (r'{}\s+ether\s+[a-z-]+\s+unmanaged'.format(self.if_router)).encode()) - self.assertRegex(out, (r'{}\s+ether\s+routable\s+configured'.format(self.iface)).encode()) + self.assertRegex(out, (rf'{self.if_router}\s+ether\s+[a-z-]+\s+unmanaged').encode()) + self.assertRegex(out, (rf'{self.iface}\s+ether\s+routable\s+configured').encode()) out = subprocess.check_output(['networkctl', '-n', '0', 'status', self.iface]) - self.assertRegex(out, br'Type:\s+ether') - self.assertRegex(out, br'State:\s+routable.*configured') - self.assertRegex(out, br'Online state:\s+online') - self.assertRegex(out, br'Address:\s+192.168.5.\d+') + self.assertRegex(out, rb'Type:\s+ether') + self.assertRegex(out, rb'State:\s+routable.*configured') + self.assertRegex(out, rb'Online state:\s+online') + self.assertRegex(out, rb'Address:\s+192.168.5.\d+') if ipv6: - self.assertRegex(out, br'2600::') + self.assertRegex(out, rb'2600::') else: - self.assertNotIn(br'2600::', out) - self.assertRegex(out, br'fe80::') - self.assertRegex(out, br'Gateway:\s+192.168.5.1') - self.assertRegex(out, br'DNS:\s+192.168.5.1') + self.assertNotIn(rb'2600::', out) + self.assertRegex(out, rb'fe80::') + self.assertRegex(out, rb'Gateway:\s+192.168.5.1') + self.assertRegex(out, rb'DNS:\s+192.168.5.1') except (AssertionError, subprocess.CalledProcessError): # show networkd status, journal, and DHCP server log on failure with open(os.path.join(NETWORK_UNITDIR, self.config)) as f: - print('\n---- {} ----\n{}'.format(self.config, f.read())) + print(f'\n---- {self.config} ----\n{f.read()}') print('---- interface status ----') sys.stdout.flush() subprocess.call(['ip', 'a', 'show', 'dev', self.iface]) - print('---- networkctl status {} ----'.format(self.iface)) + print(f'---- networkctl status {self.iface} ----') sys.stdout.flush() rc = subprocess.call(['networkctl', '-n', '0', 'status', self.iface]) if rc != 0: - print("'networkctl status' exited with an unexpected code {}".format(rc)) + print(f"'networkctl status' exited with an unexpected code {rc}") self.show_journal('systemd-networkd.service') self.print_server_log() raise @@ -536,18 +580,15 @@ DHCP={dhcp_mode} def test_coldplug_dhcp_yes_ip4_no_ra(self): # with disabling RA explicitly things should be fast - self.do_test(coldplug=True, ipv6=False, - extra_opts='IPv6AcceptRA=no') + self.do_test(coldplug=True, ipv6=False, extra_opts='IPv6AcceptRA=no') def test_coldplug_dhcp_ip4_only(self): # we have a 12s timeout on RA, so we need to wait longer - self.do_test(coldplug=True, ipv6=False, dhcp_mode='ipv4', - online_timeout=15) + self.do_test(coldplug=True, ipv6=False, dhcp_mode='ipv4', online_timeout=15) def test_coldplug_dhcp_ip4_only_no_ra(self): # with disabling RA explicitly things should be fast - self.do_test(coldplug=True, ipv6=False, dhcp_mode='ipv4', - extra_opts='IPv6AcceptRA=no') + self.do_test(coldplug=True, ipv6=False, dhcp_mode='ipv4', extra_opts='IPv6AcceptRA=no') def test_coldplug_dhcp_ip6(self): self.do_test(coldplug=True, ipv6=True) @@ -560,13 +601,18 @@ DHCP={dhcp_mode} self.do_test(coldplug=False, ipv6=True) def test_route_only_dns(self): - self.write_network('50-myvpn.netdev', '''\ + self.write_network( + '50-myvpn.netdev', + '''\ [NetDev] Name=dummy0 Kind=dummy MACAddress=12:34:56:78:9a:bc -''') - self.write_network('50-myvpn.network', '''\ +''', + ) + self.write_network( + '50-myvpn.network', + '''\ [Match] Name=dummy0 [Network] @@ -574,11 +620,11 @@ IPv6AcceptRA=no Address=192.168.42.100/24 DNS=192.168.42.1 Domains= ~company -''') +''', + ) try: - self.do_test(coldplug=True, ipv6=False, - extra_opts='IPv6AcceptRA=no') + self.do_test(coldplug=True, ipv6=False, extra_opts='IPv6AcceptRA=no') except subprocess.CalledProcessError as e: # networkd often fails to start in LXC: https://github.com/systemd/systemd/issues/11848 if IS_CONTAINER and e.cmd == ['systemctl', 'restart', 'systemd-networkd']: @@ -596,23 +642,28 @@ Domains= ~company self.assertNotIn('nameserver 192.168.42.1\n', contents) def test_route_only_dns_all_domains(self): - self.write_network('50-myvpn.netdev', '''[NetDev] + self.write_network( + '50-myvpn.netdev', + '''[NetDev] Name=dummy0 Kind=dummy MACAddress=12:34:56:78:9a:bc -''') - self.write_network('50-myvpn.network', '''[Match] +''', + ) + self.write_network( + '50-myvpn.network', + '''[Match] Name=dummy0 [Network] IPv6AcceptRA=no Address=192.168.42.100/24 DNS=192.168.42.1 Domains= ~company ~. -''') +''', + ) try: - self.do_test(coldplug=True, ipv6=False, - extra_opts='IPv6AcceptRA=no') + self.do_test(coldplug=True, ipv6=False, extra_opts='IPv6AcceptRA=no') except subprocess.CalledProcessError as e: # networkd often fails to start in LXC: https://github.com/systemd/systemd/issues/11848 if IS_CONTAINER and e.cmd == ['systemctl', 'restart', 'systemd-networkd']: @@ -634,7 +685,7 @@ Domains= ~company ~. @unittest.skipUnless(HAVE_DNSMASQ, 'dnsmasq not installed') class DnsmasqClientTest(ClientTestBase, unittest.TestCase): - '''Test networkd client against dnsmasq''' + """Test networkd client against dnsmasq""" def setUp(self): super().setUp() @@ -642,12 +693,25 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): self.iface_mac = 'de:ad:be:ef:47:11' def create_iface(self, ipv6=False, dnsmasq_opts=None): - '''Create test interface with DHCP server behind it''' + """Create test interface with DHCP server behind it""" # add veth pair - subprocess.check_call(['ip', 'link', 'add', 'name', self.iface, - 'address', self.iface_mac, - 'type', 'veth', 'peer', 'name', self.if_router]) + subprocess.check_call( + [ + 'ip', + 'link', + 'add', + 'name', + self.iface, + 'address', + self.iface_mac, + 'type', + 'veth', + 'peer', + 'name', + self.if_router, + ] + ) # give our router an IP subprocess.check_call(['ip', 'a', 'flush', 'dev', self.if_router]) @@ -666,14 +730,24 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): if dnsmasq_opts: extra_opts += dnsmasq_opts self.dnsmasq = subprocess.Popen( - ['dnsmasq', '--keep-in-foreground', '--log-queries=extra', '--log-dhcp', - '--log-facility=' + self.dnsmasq_log, '--conf-file=/dev/null', - '--dhcp-leasefile=' + lease_file, '--bind-interfaces', - '--interface=' + self.if_router, '--except-interface=lo', - '--dhcp-range=192.168.5.10,192.168.5.200'] + extra_opts) + [ + 'dnsmasq', + '--keep-in-foreground', + '--log-queries=extra', + '--log-dhcp', + '--log-facility=' + self.dnsmasq_log, + '--conf-file=/dev/null', + '--dhcp-leasefile=' + lease_file, + '--bind-interfaces', + '--interface=' + self.if_router, + '--except-interface=lo', + '--dhcp-range=192.168.5.10,192.168.5.200', + ] + + extra_opts + ) def shutdown_iface(self): - '''Remove test interface and stop DHCP server''' + """Remove test interface and stop DHCP server""" if self.if_router: subprocess.check_call(['ip', 'link', 'del', 'dev', self.if_router]) @@ -684,16 +758,17 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): self.dnsmasq = None def print_server_log(self, log_file=None): - '''Print DHCP server log for debugging failures''' + """Print DHCP server log for debugging failures""" path = log_file if log_file else self.dnsmasq_log with open(path) as f: - sys.stdout.write('\n\n---- {} ----\n{}\n------\n\n'.format(os.path.basename(path), f.read())) + sys.stdout.write(f'\n\n---- {os.path.basename(path)} ----\n{f.read()}\n------\n\n') def test_resolved_domain_restricted_dns(self): - '''resolved: domain-restricted DNS servers''' + """resolved: domain-restricted DNS servers""" - # enable DNSSEC in allow downgrade mode, and turn off stuff we don't want to test to make looking at logs easier + # enable DNSSEC in allow downgrade mode, and turn off stuff we don't want to test to make looking at + # logs easier conf = '/run/systemd/resolved.conf.d/test-enable-dnssec.conf' os.makedirs(os.path.dirname(conf), exist_ok=True) with open(conf, 'w') as f: @@ -703,14 +778,17 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): # create interface for generic connections; this will map all DNS names # to 192.168.42.1 self.create_iface(dnsmasq_opts=['--address=/#/192.168.42.1']) - self.write_network('50-general.network', '''\ + self.write_network( + '50-general.network', + f'''\ [Match] -Name={} +Name={self.iface} [Network] DHCP=ipv4 IPv6AcceptRA=no DNSSECNegativeTrustAnchors=search.example.com -'''.format(self.iface)) +''', + ) # create second device/dnsmasq for a .company/.lab VPN interface # static IPs for simplicity @@ -721,15 +799,26 @@ DNSSECNegativeTrustAnchors=search.example.com vpn_dnsmasq_log = os.path.join(self.workdir, 'dnsmasq-vpn.log') vpn_dnsmasq = subprocess.Popen( - ['dnsmasq', '--keep-in-foreground', '--log-queries=extra', - '--log-facility=' + vpn_dnsmasq_log, '--conf-file=/dev/null', - '--dhcp-leasefile=/dev/null', '--bind-interfaces', - '--interface=testvpnrouter', '--except-interface=lo', - '--address=/math.lab/10.241.3.3', '--address=/cantina.company/10.241.4.4']) + [ + 'dnsmasq', + '--keep-in-foreground', + '--log-queries=extra', + '--log-facility=' + vpn_dnsmasq_log, + '--conf-file=/dev/null', + '--dhcp-leasefile=/dev/null', + '--bind-interfaces', + '--interface=testvpnrouter', + '--except-interface=lo', + '--address=/math.lab/10.241.3.3', + '--address=/cantina.company/10.241.4.4', + ] + ) self.addCleanup(vpn_dnsmasq.wait) self.addCleanup(vpn_dnsmasq.kill) - self.write_network('50-vpn.network', '''\ + self.write_network( + '50-vpn.network', + '''\ [Match] Name=testvpnclient [Network] @@ -738,11 +827,13 @@ Address=10.241.3.2/24 DNS=10.241.3.1 Domains=~company ~lab DNSSECNegativeTrustAnchors=company lab -''') +''', + ) self.start_unit('systemd-networkd') - subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', self.iface, - '--interface=testvpnclient', '--timeout=20']) + subprocess.check_call( + [NETWORKD_WAIT_ONLINE, '--interface', self.iface, '--interface=testvpnclient', '--timeout=20'] + ) # ensure we start fresh with every test subprocess.check_call(['systemctl', 'restart', 'systemd-resolved']) @@ -784,7 +875,7 @@ DNSSECNegativeTrustAnchors=company lab raise def test_resolved_etc_hosts(self): - '''resolved queries to /etc/hosts''' + """resolved queries to /etc/hosts""" # enabled DNSSEC in allow-downgrade mode conf = '/run/systemd/resolved.conf.d/test-enable-dnssec.conf' @@ -811,10 +902,14 @@ DNSSECNegativeTrustAnchors=company lab # note: different IPv4 address here, so that it's easy to tell apart # what resolved the query - self.create_iface(dnsmasq_opts=['--host-record=my.example.com,172.16.99.1,2600::99:99', - '--host-record=other.example.com,172.16.0.42,2600::42', - '--mx-host=example.com,mail.example.com'], - ipv6=True) + self.create_iface( + dnsmasq_opts=[ + '--host-record=my.example.com,172.16.99.1,2600::99:99', + '--host-record=other.example.com,172.16.0.42,2600::42', + '--mx-host=example.com,mail.example.com', + ], + ipv6=True, + ) self.do_test(coldplug=None, ipv6=True) try: @@ -848,7 +943,7 @@ DNSSECNegativeTrustAnchors=company lab raise def test_transient_hostname(self): - '''networkd sets transient hostname from DHCP''' + """networkd sets transient hostname from DHCP""" orig_hostname = socket.gethostname() self.addCleanup(socket.sethostname, orig_hostname) @@ -859,7 +954,7 @@ DNSSECNegativeTrustAnchors=company lab subprocess.check_call(['systemctl', 'stop', 'systemd-hostnamed.service']) self.addCleanup(subprocess.call, ['systemctl', 'stop', 'systemd-hostnamed.service']) - self.create_iface(dnsmasq_opts=['--dhcp-host={},192.168.5.210,testgreen'.format(self.iface_mac)]) + self.create_iface(dnsmasq_opts=[f'--dhcp-host={self.iface_mac},192.168.5.210,testgreen']) self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=no', dhcp_mode='ipv4') try: @@ -873,10 +968,10 @@ DNSSECNegativeTrustAnchors=company lab if b'testgreen' in out: break time.sleep(5) - sys.stdout.write('[retry %i] ' % retry) + sys.stdout.write(f'[retry {retry}] ') sys.stdout.flush() else: - self.fail('Transient hostname not found in hostnamectl:\n{}'.format(out.decode())) + self.fail(f'Transient hostname not found in hostnamectl:\n{out.decode()}') # and also applied to the system self.assertEqual(socket.gethostname(), 'testgreen') except AssertionError: @@ -886,24 +981,23 @@ DNSSECNegativeTrustAnchors=company lab raise def test_transient_hostname_with_static(self): - '''transient hostname is not applied if static hostname exists''' + """transient hostname is not applied if static hostname exists""" orig_hostname = socket.gethostname() self.addCleanup(socket.sethostname, orig_hostname) if not os.path.exists('/etc/hostname'): - self.write_config('/etc/hostname', "foobarqux") + self.write_config('/etc/hostname', 'foobarqux') else: - self.write_config('/run/hostname.tmp', "foobarqux") + self.write_config('/run/hostname.tmp', 'foobarqux') subprocess.check_call(['mount', '--bind', '/run/hostname.tmp', '/etc/hostname']) self.addCleanup(subprocess.call, ['umount', '/etc/hostname']) - socket.sethostname("foobarqux"); - + socket.sethostname('foobarqux') subprocess.check_call(['systemctl', 'stop', 'systemd-hostnamed.service']) self.addCleanup(subprocess.call, ['systemctl', 'stop', 'systemd-hostnamed.service']) - self.create_iface(dnsmasq_opts=['--dhcp-host={},192.168.5.210,testgreen'.format(self.iface_mac)]) + self.create_iface(dnsmasq_opts=[f'--dhcp-host={self.iface_mac},192.168.5.210,testgreen']) self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=no', dhcp_mode='ipv4') try: @@ -911,7 +1005,7 @@ DNSSECNegativeTrustAnchors=company lab out = subprocess.check_output(['ip', '-4', 'a', 'show', 'dev', self.iface]) self.assertRegex(out, b'inet 192.168.5.210/24 .* scope global dynamic') # static hostname wins over transient one, thus *not* applied - self.assertEqual(socket.gethostname(), "foobarqux") + self.assertEqual(socket.gethostname(), 'foobarqux') except AssertionError: self.show_journal('systemd-networkd.service') self.show_journal('systemd-hostnamed.service') @@ -920,21 +1014,22 @@ DNSSECNegativeTrustAnchors=company lab class NetworkdClientTest(ClientTestBase, unittest.TestCase): - '''Test networkd client against networkd server''' + """Test networkd client against networkd server""" def setUp(self): super().setUp() self.dnsmasq = None def create_iface(self, ipv6=False, dhcpserver_opts=None): - '''Create test interface with DHCP server behind it''' + """Create test interface with DHCP server behind it""" # run "router-side" networkd in own mount namespace to shield it from # "client-side" configuration and networkd (fd, script) = tempfile.mkstemp(prefix='networkd-router.sh') self.addCleanup(os.remove, script) with os.fdopen(fd, 'w+') as f: - f.write('''\ + f.write( + '''\ #!/bin/sh set -eu mkdir -p /run/systemd/network @@ -984,34 +1079,46 @@ export SYSTEMD_LOG_LEVEL=debug # run networkd as in systemd-networkd.service exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=//; s/^[@+-]//; s/^!*//; p}}') -'''.format(ifr=self.if_router, - ifc=self.iface, - addr6=('Address=2600::1/64' if ipv6 else ''), - dhopts=(dhcpserver_opts or ''))) +'''.format( + ifr=self.if_router, + ifc=self.iface, + addr6=('Address=2600::1/64' if ipv6 else ''), + dhopts=(dhcpserver_opts or ''), + ) + ) os.fchmod(fd, 0o755) - subprocess.check_call(['systemd-run', '--unit=networkd-test-router.service', - '-p', 'InaccessibleDirectories=-/etc/systemd/network', - '-p', 'InaccessibleDirectories=-/run/systemd/network', - '-p', 'InaccessibleDirectories=-/run/systemd/netif', - '-p', 'InaccessibleDirectories=-/run/systemd/report', - '-p', 'InaccessibleDirectories=-/run/systemd/resolve.hook', - '-p', 'InaccessibleDirectories=-/var/lib/systemd/network', - '--service-type=notify', script]) + subprocess.check_call( + [ + 'systemd-run', + '--unit=networkd-test-router.service', + '-p', 'InaccessibleDirectories=-/etc/systemd/network', + '-p', 'InaccessibleDirectories=-/run/systemd/network', + '-p', 'InaccessibleDirectories=-/run/systemd/netif', + '-p', 'InaccessibleDirectories=-/run/systemd/report', + '-p', 'InaccessibleDirectories=-/run/systemd/resolve.hook', + '-p', 'InaccessibleDirectories=-/var/lib/systemd/network', + '--service-type=notify', + script, + ] + ) # fmt: skip # wait until devices got created for _ in range(50): - if subprocess.run(['ip', 'link', 'show', 'dev', self.if_router], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0: + if subprocess.run( + ['ip', 'link', 'show', 'dev', self.if_router], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ).returncode == 0: # fmt: skip break time.sleep(0.1) else: subprocess.call(['ip', 'link', 'show', 'dev', self.if_router]) - self.fail('Timed out waiting for {ifr} created.'.format(ifr=self.if_router)) + self.fail(f'Timed out waiting for {self.if_router} created.') def shutdown_iface(self): - '''Remove test interface and stop DHCP server''' + """Remove test interface and stop DHCP server""" if self.if_router: subprocess.check_call(['systemctl', 'stop', 'networkd-test-router.service']) @@ -1021,7 +1128,7 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=/ self.if_router = None def print_server_log(self): - '''Print DHCP server log for debugging failures''' + """Print DHCP server log for debugging failures""" self.show_journal('networkd-test-router.service') @@ -1038,13 +1145,18 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=/ # we don't use this interface for this test self.if_router = None - self.write_network('50-test.netdev', '''\ + self.write_network( + '50-test.netdev', + '''\ [NetDev] Name=dummy0 Kind=dummy MACAddress=12:34:56:78:9a:bc -''') - self.write_network('50-test.network', '''\ +''', + ) + self.write_network( + '50-test.network', + '''\ [Match] Name=dummy0 [Network] @@ -1052,7 +1164,8 @@ IPv6AcceptRA=no Address=192.168.42.100/24 DNS=192.168.42.1 Domains= one two three four five six seven eight nine ten -''') +''', + ) self.start_unit('systemd-networkd') @@ -1068,24 +1181,34 @@ Domains= one two three four five six seven eight nine ten # we don't use this interface for this test self.if_router = None - self.write_network('50-test.netdev', '''\ + self.write_network( + '50-test.netdev', + '''\ [NetDev] Name=dummy0 Kind=dummy MACAddress=12:34:56:78:9a:bc -''') - self.write_network('50-test.network', '''\ +''', + ) + self.write_network( + '50-test.network', + '''\ [Match] Name=dummy0 [Network] IPv6AcceptRA=no Address=192.168.42.100/24 DNS=192.168.42.1 -''') - self.write_network_dropin('50-test.network', 'dns', '''\ +''', + ) + self.write_network_dropin( + '50-test.network', + 'dns', + '''\ [Network] DNS=127.0.0.1 -''') +''', + ) self.start_unit('systemd-resolved') self.start_unit('systemd-networkd') @@ -1106,11 +1229,19 @@ DNS=127.0.0.1 self.fail(f'Expected DNS servers not found in resolv.conf: {contents}') def test_dhcp_timezone(self): - '''networkd sets time zone from DHCP''' + """networkd sets time zone from DHCP""" def get_tz(): - out = subprocess.check_output(['busctl', 'get-property', 'org.freedesktop.timedate1', - '/org/freedesktop/timedate1', 'org.freedesktop.timedate1', 'Timezone']) + out = subprocess.check_output( + [ + 'busctl', + 'get-property', + 'org.freedesktop.timedate1', + '/org/freedesktop/timedate1', + 'org.freedesktop.timedate1', + 'Timezone', + ] + ) assert out.startswith(b's "') out = out.strip() assert out.endswith(b'"') @@ -1120,7 +1251,9 @@ DNS=127.0.0.1 self.addCleanup(subprocess.call, ['timedatectl', 'set-timezone', orig_timezone]) self.create_iface(dhcpserver_opts='EmitTimezone=yes\nTimezone=Pacific/Honolulu') - self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=false\n[DHCP]\nUseTimezone=true', dhcp_mode='ipv4') + self.do_test( + coldplug=None, extra_opts='IPv6AcceptRA=false\n[DHCP]\nUseTimezone=true', dhcp_mode='ipv4' + ) # Should have applied the received timezone. This is asynchronous, so we need to wait for a while: for _ in range(20): @@ -1151,12 +1284,15 @@ class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities): def test_basic_matching(self): """Verify the Name= line works throughout this class.""" self.add_veth_pair('test_if1', 'fake_if2') - self.write_network('50-test.network', '''\ + self.write_network( + '50-test.network', + '''\ [Match] Name=test_* [Network] IPv6AcceptRA=no -''') +''', + ) subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.assert_link_states(test_if1='managed', fake_if2='unmanaged') @@ -1165,15 +1301,17 @@ IPv6AcceptRA=no # Use a MAC address as the interfaces' common matching attribute # to avoid depending on udev, to support testing in containers. mac = '00:01:02:03:98:99' - self.add_veth_pair('test_veth', 'test_peer', - ['addr', mac], ['addr', mac]) - self.write_network('50-no-veth.network', '''\ + self.add_veth_pair('test_veth', 'test_peer', ['addr', mac], ['addr', mac]) + self.write_network( + '50-no-veth.network', + f'''\ [Match] -MACAddress={} +MACAddress={mac} Name=!nonexistent *peer* [Network] IPv6AcceptRA=no -'''.format(mac)) +''', + ) subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.assert_link_states(test_veth='managed', test_peer='unmanaged') @@ -1192,14 +1330,14 @@ class UnmanagedClientTest(unittest.TestCase, NetworkdTestingUtilities): # Define the contents of .network files to be read in order. self.configs = ( - "[Match]\nName=m1def\n", - "[Match]\nName=m1unm\n[Link]\nUnmanaged=yes\n", - "[Match]\nName=m1*\n[Link]\nUnmanaged=no\n", + '[Match]\nName=m1def\n', + '[Match]\nName=m1unm\n[Link]\nUnmanaged=yes\n', + '[Match]\nName=m1*\n[Link]\nUnmanaged=no\n', ) # Write out the .network files to be cleaned up automatically. for i, config in enumerate(self.configs): - self.write_network("%02d-test.network" % i, config) + self.write_network(f'{i:02d}-test.network', config) def tearDown(self): """Stop networkd.""" @@ -1215,43 +1353,30 @@ class UnmanagedClientTest(unittest.TestCase, NetworkdTestingUtilities): """Verify link states with Unmanaged= settings, hot-plug.""" subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.create_iface() - self.assert_link_states(m1def='managed', - m1man='managed', - m1unm='unmanaged', - m0unm='unmanaged') + self.assert_link_states(m1def='managed', m1man='managed', m1unm='unmanaged', m0unm='unmanaged') def test_unmanaged_setting_coldplug(self): """Verify link states with Unmanaged= settings, cold-plug.""" self.create_iface() subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) - self.assert_link_states(m1def='managed', - m1man='managed', - m1unm='unmanaged', - m0unm='unmanaged') + self.assert_link_states(m1def='managed', m1man='managed', m1unm='unmanaged', m0unm='unmanaged') def test_catchall_config(self): """Verify link states with a catch-all config, hot-plug.""" # Don't actually catch ALL interfaces. It messes up the host. - self.write_network('50-all.network', "[Match]\nName=m[01]???\n") + self.write_network('50-all.network', '[Match]\nName=m[01]???\n') subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) self.create_iface() - self.assert_link_states(m1def='managed', - m1man='managed', - m1unm='unmanaged', - m0unm='managed') + self.assert_link_states(m1def='managed', m1man='managed', m1unm='unmanaged', m0unm='managed') def test_catchall_config_coldplug(self): """Verify link states with a catch-all config, cold-plug.""" # Don't actually catch ALL interfaces. It messes up the host. - self.write_network('50-all.network', "[Match]\nName=m[01]???\n") + self.write_network('50-all.network', '[Match]\nName=m[01]???\n') self.create_iface() subprocess.check_call(['systemctl', 'restart', 'systemd-networkd']) - self.assert_link_states(m1def='managed', - m1man='managed', - m1unm='unmanaged', - m0unm='managed') + self.assert_link_states(m1def='managed', m1man='managed', m1unm='unmanaged', m0unm='managed') if __name__ == '__main__': - unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, - verbosity=2)) + unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) From 2c73e5b973ac4e1469a50a9474c5a9ac5cedefb2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 01:59:51 +0900 Subject: [PATCH 23/62] rule-syntax-check: apply "ruff format" and "ruff check --fix" --- test/rule-syntax-check.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/test/rule-syntax-check.py b/test/rule-syntax-check.py index ec1c75a854..777012a2ac 100755 --- a/test/rule-syntax-check.py +++ b/test/rule-syntax-check.py @@ -14,21 +14,21 @@ if not rules_files: sys.exit('Specify files to test as arguments') quoted_string_re = r'"(?:[^\\"]|\\.)*"' -no_args_tests = re.compile(r'(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|PROGRAM|RESULT|TEST)\s*(?:=|!)=\s*' + quoted_string_re + '$') +no_args_tests = re.compile(rf'(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|PROGRAM|RESULT|TEST)\s*(?:=|!)=\s*{quoted_string_re}$') # fmt: skip # PROGRAM can also be specified as an assignment. program_assign = re.compile(r'PROGRAM\s*=\s*' + quoted_string_re + '$') -args_tests = re.compile(r'(ATTRS?|ENV|CONST|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*' + quoted_string_re + '$') -no_args_assign = re.compile(r'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|RUN|LABEL|GOTO|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*' + quoted_string_re + '$') -args_assign = re.compile(r'(ATTR|ENV|IMPORT|RUN){([a-zA-Z0-9/_.*%-]+)}\s*(=|\+=)\s*' + quoted_string_re + '$') +args_tests = re.compile(rf'(ATTRS?|ENV|CONST|TEST){{([a-zA-Z0-9/_.*%-]+)}}\s*(?:=|!)=\s*{quoted_string_re}$') +no_args_assign = re.compile(rf'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|RUN|LABEL|GOTO|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*{quoted_string_re}$') # fmt: skip +args_assign = re.compile(rf'(ATTR|ENV|IMPORT|RUN){{([a-zA-Z0-9/_.*%-]+)}}\s*(=|\+=)\s*{quoted_string_re}$') # Find comma-separated groups, but allow commas that are inside quoted strings. # Using quoted_string_re + '?' so that strings missing the last double quote # will still match for this part that splits on commas. -comma_separated_group_re = re.compile(r'(?:[^,"]|' + quoted_string_re + '?)+') +comma_separated_group_re = re.compile(rf'(?:[^,"]|{quoted_string_re}?)+') result = 0 buffer = '' for path in rules_files: - print('# looking at {}'.format(path)) + print(f'# looking at {path}') lineno = 0 for line in open(path): lineno += 1 @@ -50,11 +50,14 @@ for path in rules_files: # it generally improves the readability of the rules. for clause_match in comma_separated_group_re.finditer(line): clause = clause_match.group().strip() - if not (no_args_tests.match(clause) or args_tests.match(clause) or - no_args_assign.match(clause) or args_assign.match(clause) or - program_assign.match(clause)): - - print('Invalid line {}:{}: {}'.format(path, lineno, line)) + if not ( + no_args_tests.match(clause) + or args_tests.match(clause) + or no_args_assign.match(clause) + or args_assign.match(clause) + or program_assign.match(clause) + ): + print(f'Invalid line {path}:{lineno}: {line}') print(' clause:', clause) print() result = 1 From d89d23bc8ff89972a12ecbe80b7119542205270d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:00:21 +0900 Subject: [PATCH 24/62] run-unit-tests: apply "ruff format" --- test/run-unit-tests.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/test/run-unit-tests.py b/test/run-unit-tests.py index de8ac5c26c..1b790d7a5c 100755 --- a/test/run-unit-tests.py +++ b/test/run-unit-tests.py @@ -6,8 +6,10 @@ import os import pathlib import subprocess import sys + try: import colorama as c + GREEN = c.Fore.GREEN YELLOW = c.Fore.YELLOW RED = c.Fore.RED @@ -16,23 +18,38 @@ try: except ImportError: GREEN = YELLOW = RED = RESET_ALL = BRIGHT = '' + class total: total = None good = 0 skip = 0 fail = 0 + def argument_parser(): p = argparse.ArgumentParser() - p.add_argument('-u', '--unsafe', action='store_true', - help='run "unsafe" tests too') - p.add_argument('-A', '--artifact_directory', - help='store output from failed tests in this dir') - p.add_argument('-s', '--skip', action='append', default=[], - help='skip the named test') + p.add_argument( + '-u', + '--unsafe', + action='store_true', + help='run "unsafe" tests too', + ) + p.add_argument( + '-A', + '--artifact_directory', + help='store output from failed tests in this dir', + ) + p.add_argument( + '-s', + '--skip', + action='append', + default=[], + help='skip the named test', + ) return p + opts = argument_parser().parse_args() unittestdir = pathlib.Path(__file__).parent.absolute() / 'unit-tests' From 87c0cf15fe24442e5c6ebd00a1517f6018c60054 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:00:40 +0900 Subject: [PATCH 25/62] sd-script: apply "ruff format" and "ruff check --fix" --- test/sd-script.py | 104 ++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/test/sd-script.py b/test/sd-script.py index 51ebf70c39..432136e692 100755 --- a/test/sd-script.py +++ b/test/sd-script.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later # +# ruff: noqa: F821 +# # sd-script.py: create LOTS of sd device entries in fake sysfs # # (C) 2018 Martin Wilck, SUSE Linux GmbH @@ -15,63 +17,67 @@ # Note that sys-script.py already creates 10 sd device nodes # (sda+sdb and partitions). This script starts with sdc. -import re -import os import errno +import os +import re import sys + def d(path, mode): os.mkdir(path, mode) -def l(path, src): + +def l(path, src): # noqa: E743 os.symlink(src, path) + def f(path, mode, contents): - with open(path, "wb") as f: + with open(path, 'wb') as f: f.write(contents) os.chmod(path, mode) -class SD(object): +class SD: sd_major = [8] + list(range(65, 72)) + list(range(128, 136)) _name_re = re.compile(r'sd(?P[a-z]*)$') def _init_from_name(self, name): mt = self._name_re.match(name) if mt is None: - raise RuntimeError("invalid name %s" % name) - nm = mt.group("f") + raise RuntimeError(f'invalid name {name}') + nm = mt.group('f') base = 1 ls = nm[-1] nm = nm[:-1] - n = base * (ord(ls)-ord('a')) + n = base * (ord(ls) - ord('a')) while len(nm) > 0: ls = nm[-1] nm = nm[:-1] base *= 26 - n += base * (1 + ord(ls)-ord('a')) + n += base * (1 + ord(ls) - ord('a')) self._num = n def _init_from_dev(self, dev): - maj, min = dev.split(":") + maj, min = dev.split(':') maj = self.sd_major.index(int(maj, 10)) min = int(min, 10) num = int(min / 16) - self._num = 16*maj + num%16 + 256*int(num/16) + self._num = 16 * maj + num % 16 + 256 * int(num / 16) @staticmethod def _disk_num(a, b): - n = ord(a)-ord('a') + n = ord(a) - ord('a') if b != '': - n = 26 * (n+1) + ord(b)-ord('a') + n = 26 * (n + 1) + ord(b) - ord('a') return n @staticmethod def _get_major(n): - return SD.sd_major[(n%256)//16] + return SD.sd_major[(n % 256) // 16] + @staticmethod def _get_minor(n): - return 16 * (n % 16 + 16 * n//256) + return 16 * (n % 16 + 16 * n // 256) @staticmethod def _get_name(n): @@ -81,7 +87,7 @@ class SD(object): while n >= 0: s = chr(n % 26 + ord('a')) + s n = n // 26 - 1 - return "sd" + s + return 'sd' + s @staticmethod def _get_dev_t(n): @@ -90,9 +96,9 @@ class SD(object): return (maj << 20) + min def __init__(self, arg): - if type(arg) is type(0): + if type(arg) is int: self._num = arg - elif arg.startswith("sd"): + elif arg.startswith('sd'): self._init_from_name(arg) else: self._init_from_dev(arg) @@ -110,9 +116,7 @@ class SD(object): return hash(self._num) def __str__(self): - return "%s/%s" % ( - self.devstr(), - self._get_name(self._num)) + return f'{self.devstr()}/{self._get_name(self._num)}' def major(self): return self._get_major(self._num) @@ -121,29 +125,27 @@ class SD(object): return self._get_minor(self._num) def devstr(self): - return "%d:%d" % (self._get_major(self._num), - self._get_minor(self._num)) + return f'{self._get_major(self._num)}:{self._get_minor(self._num)}' def namestr(self): return self._get_name(self._num) def longstr(self): - return "%d\t%s\t%s\t%08x" % (self._num, - self.devstr(), - self.namestr(), - self._get_dev_t(self._num)) + return f'{self._num}\t{self.devstr()}\t{self.namestr()}\t{self._get_dev_t(self._num):08x}' + class MySD(SD): def subst(self, first_sg): disk = { - "lun": self._num, - "major": self.major(), - "devnode": self.namestr(), - "disk_minor": self.minor(), - "sg_minor": first_sg + self._num, + 'lun': self._num, + 'major': self.major(), + 'devnode': self.namestr(), + 'disk_minor': self.minor(), + 'sg_minor': first_sg + self._num, } return disk + disk_template = r"""\ l('sys/bus/scsi/drivers/sd/7:0:0:{lun}', '../../../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}') l('sys/bus/scsi/devices/7:0:0:{lun}', '../../../devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:{lun}') @@ -282,29 +284,33 @@ DEVNAME={devnode}{part_num} """ if len(sys.argv) != 3: - exit("Usage: {} ".format(sys.argv[0])) + exit(f'Usage: {sys.argv[0]} ') if not os.path.isdir(sys.argv[1]): - exit("Target dir {} not found".format(sys.argv[1])) + exit(f'Target dir {sys.argv[1]} not found') + def create_part_sysfs(disk, sd, prt): part = disk - part.update ({ - "part_num": prt, - "part_minor": disk["disk_minor"] + prt, - }) + part.update( + { + 'part_num': prt, + 'part_minor': disk['disk_minor'] + prt, + } + ) try: exec(part_template.format(**part)) except OSError: si = sys.exc_info()[1] - if (si.errno == errno.EEXIST): - print("sysfs structures for %s%d exist" % (sd.namestr(), prt)) + if si.errno == errno.EEXIST: + print(f'sysfs structures for {sd.namestr()}{prt} exist') else: - print("error for %s%d: %s" % (sd.namestr(), prt, si[1])) + print(f'error for {sd.namestr()}{prt}: {si[1]}') raise else: - print("sysfs structures for %s%d created" % (sd.namestr(), prt)) + print(f'sysfs structures for {sd.namestr()}{prt} created') + def create_disk_sysfs(dsk, first_sg, n): sd = MySD(dsk) @@ -314,17 +320,16 @@ def create_disk_sysfs(dsk, first_sg, n): exec(disk_template.format(**disk)) except OSError: si = sys.exc_info()[1] - if (si.errno == errno.EEXIST): - print("sysfs structures for %s exist" % sd.namestr()) - elif (si.errno == errno.ENOENT): - print("error for %s: %s - have you run sys-script py first?" % - (sd.namestr(), si.strerror)) + if si.errno == errno.EEXIST: + print(f'sysfs structures for {sd.namestr()} exist') + elif si.errno == errno.ENOENT: + print(f'error for {sd.namestr()}: {si.strerror} - have you run sys-script py first?') return -1 else: - print("error for %s: %s" % (sd.namestr(), si.strerror)) + print(f'error for {sd.namestr()}: {si.strerror}') raise else: - print("sysfs structures for %s created" % sd.namestr()) + print(f'sysfs structures for {sd.namestr()} created') n += 1 if n >= last: @@ -338,6 +343,7 @@ def create_disk_sysfs(dsk, first_sg, n): return n + os.chdir(sys.argv[1]) n = 0 last = int(sys.argv[2]) From 124bbf37de2b2191110c13b8a0857d031cc79fb5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 26 Feb 2026 00:40:08 +0900 Subject: [PATCH 26/62] sys-script: apply "ruff format" and "ruff check --fix" --- test/sys-script.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/sys-script.py b/test/sys-script.py index e9ce665313..be45a211e2 100755 --- a/test/sys-script.py +++ b/test/sys-script.py @@ -6,30 +6,36 @@ # © 2017 Canonical Ltd. # Author: Dan Streetman -import os, sys +import os import shutil +import sys + def d(path, mode): os.mkdir(path, mode) -def l(path, src): + +def l(path, src): # noqa: E743 os.symlink(src, path) + def f(path, mode, contents): - with open(path, "wb") as f: + with open(path, 'wb') as f: f.write(contents) os.chmod(path, mode) + if len(sys.argv) < 2: - exit("Usage: {} ".format(sys.argv[0])) + exit(f'Usage: {sys.argv[0]} ') if not os.path.isdir(sys.argv[1]): - exit("Target dir {} not found".format(sys.argv[1])) + exit(f'Target dir {sys.argv[1]} not found') os.chdir(sys.argv[1]) if os.path.exists('sys'): shutil.rmtree('sys') +# fmt: off d('sys', 0o755) d('sys/kernel', 0o775) f('sys/kernel/kexec_crash_loaded', 0o664, b'0\n') @@ -16855,3 +16861,4 @@ f('sys/devices/platform/i8042/serio1/input/input1/capabilities/rel', 0o644, b'3\ f('sys/devices/platform/i8042/serio1/input/input1/capabilities/sw', 0o644, b'0\n') f('sys/devices/platform/i8042/serio1/input/input1/capabilities/ev', 0o644, b'7\n') f('sys/devices/platform/i8042/serio1/input/input1/capabilities/led', 0o644, b'0\n') +# fmt: on From cbc2990335bfe73d383eccf6f6889e31cb9034b8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 21 Feb 2026 04:40:36 +0900 Subject: [PATCH 27/62] test-network: drop old kernel support and workaround for fixed kernel bug - l3mdev in fib rules is supported since https://github.com/torvalds/linux/commit/96c63fa7393d0a346acfe5a91e0c7d4c7782641b (v4.8). - uid range in fib rules is supported since https://github.com/torvalds/linux/commit/622ec2c9d52405973c9f1ca5116eb1c393adfc7d (v4.10). - port range and ip proto in fib rules is supported since https://github.com/torvalds/linux/commit/bfff4862653bb96001ab57c1edd6d03f48e5f035 (v4.17). - interface alternative name is supported since https://github.com/torvalds/linux/commit/36fbf1e52bd3ff8a5cb604955eedfc9350c2e6cc (v5.5). - SRIOV on netdevsim is supported since https://github.com/torvalds/linux/commit/79579220566cd33fe3b15ce8249c57e10251b258 (4.16). - nexthop is supported since https://github.com/torvalds/linux/commit/65ee00a9409f751188a8cdc0988167858eb4a536 (v5.3). - bridge MDB entries on bridge master is supported since https://github.com/torvalds/linux/commit/1bc844ee0faa1b92e3ede00bdd948021c78d7088 (v5.4). --- test/test-network/systemd-networkd-tests.py | 156 +++----------------- 1 file changed, 19 insertions(+), 137 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index a6d4ff033c..329cbe1e1d 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -178,114 +178,6 @@ def expectedFailureIfModuleIsNotAvailable(*module_names): return f -def expectedFailureIfERSPANv0IsNotSupported(): - # erspan version 0 is supported since f989d546a2d5a9f001f6f8be49d98c10ab9b1897 (v5.8) - def f(func): - rc = call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 0') - remove_link('erspan99') - return func if rc == 0 else unittest.expectedFailure(func) - - return f - -def expectedFailureIfERSPANv2IsNotSupported(): - # erspan version 2 is supported since f551c91de262ba36b20c3ac19538afb4f4507441 (v4.16) - def f(func): - rc = call_quiet('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 2') - remove_link('erspan99') - return func if rc == 0 else unittest.expectedFailure(func) - - return f - -def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable(): - def f(func): - rc = call_quiet('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7') - call_quiet('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7') - return func if rc == 0 else unittest.expectedFailure(func) - - return f - -def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable(): - def f(func): - # IP protocol name is parsed by getprotobyname(), and it requires /etc/protocols. - # Hence. here we use explicit number: 6 == tcp. - rc = call_quiet('ip rule add not from 192.168.100.19 ipproto 6 table 7') - call_quiet('ip rule del not from 192.168.100.19 ipproto 6 table 7') - return func if rc == 0 else unittest.expectedFailure(func) - - return f - -def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable(): - def f(func): - supported = False - if call_quiet('ip rule add from 192.168.100.19 table 7 uidrange 200-300') == 0: - ret = run('ip rule list from 192.168.100.19 table 7') - supported = ret.returncode == 0 and 'uidrange 200-300' in ret.stdout - call_quiet('ip rule del from 192.168.100.19 table 7 uidrange 200-300') - return func if supported else unittest.expectedFailure(func) - - return f - -def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable(): - def f(func): - rc = call_quiet('ip rule add not from 192.168.100.19 l3mdev') - call_quiet('ip rule del not from 192.168.100.19 l3mdev') - return func if rc == 0 else unittest.expectedFailure(func) - - return f - -def expectedFailureIfNexthopIsNotAvailable(): - def f(func): - rc = call_quiet('ip nexthop list') - return func if rc == 0 else unittest.expectedFailure(func) - - return f - -def expectedFailureIfAlternativeNameIsNotAvailable(): - def f(func): - call_quiet('ip link add dummy98 type dummy') - supported = \ - call_quiet('ip link prop add dev dummy98 altname hogehogehogehogehoge') == 0 and \ - call_quiet('ip link show dev hogehogehogehogehoge') == 0 - remove_link('dummy98') - return func if supported else unittest.expectedFailure(func) - - return f - -def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable(): - def f(func): - def finalize(func, supported): - call_quiet('rmmod netdevsim') - return func if supported else unittest.expectedFailure(func) - - call_quiet('rmmod netdevsim') - if call_quiet('modprobe netdevsim') != 0: - return finalize(func, False) - - try: - with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f: - f.write('99 1') - except OSError: - return finalize(func, False) - - return finalize(func, os.path.exists('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs')) - - return f - -def expectedFailureIfKernelReturnsInvalidFlags(): - ''' - This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9), - fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3). - ''' - def f(func): - call_quiet('ip link add dummy98 type dummy') - call_quiet('ip link set up dev dummy98') - call_quiet('ip address add 192.0.2.1/24 dev dummy98 noprefixroute') - output = check_output('ip address show dev dummy98') - remove_link('dummy98') - return func if 'noprefixroute' in output else unittest.expectedFailure(func) - - return f - # pylint: disable=C0415 def compare_kernel_version(min_kernel_version): try: @@ -1454,7 +1346,6 @@ class NetworkctlTests(unittest.TestCase, Utilities): def tearDown(self): tear_down_common() - @expectedFailureIfAlternativeNameIsNotAvailable() def test_altname(self): copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link') start_networkd() @@ -1463,7 +1354,6 @@ class NetworkctlTests(unittest.TestCase, Utilities): output = networkctl_status('dummy98') self.assertRegex(output, 'hogehogehogehogehogehoge') - @expectedFailureIfAlternativeNameIsNotAvailable() def test_rename_to_altname(self): copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy-rename-to-altname.link') @@ -1666,7 +1556,6 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): def tearDown(self): tear_down_common() - @expectedFailureIfAlternativeNameIsNotAvailable() def test_match(self): copy_network_unit('12-dummy-mac.netdev', '12-dummy-match-mac-01.network', @@ -2689,9 +2578,13 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network') start_networkd() - # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here. - - self.wait_links('dummy98', 'ip6gretun99', 'ip6gretun98', 'ip6gretun97', 'ip6gretun96') + self.wait_online( + 'ip6gretun99:routable', + 'ip6gretun98:routable', + 'ip6gretun97:routable', + 'ip6gretun96:routable', + 'dummy98:degraded', + ) output = check_output('ip -d link show ip6gretun99') print(output) @@ -2712,12 +2605,13 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-any-any.netdev') networkctl_reload() - self.wait_links( - 'dummy98', - 'ip6gretun99', - 'ip6gretun98', - 'ip6gretun97', - 'ip6gretun96') + self.wait_online( + 'ip6gretun99:routable', + 'ip6gretun98:routable', + 'ip6gretun97:routable', + 'ip6gretun96:routable', + 'dummy98:degraded', + ) def test_gretap_tunnel(self): copy_network_unit('12-dummy.netdev', '25-gretap.network', @@ -3036,7 +2930,6 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): networkctl_reload() self.wait_online('sittun99:routable', 'dummy98:degraded') - @expectedFailureIfERSPANv0IsNotSupported() def test_erspan_tunnel_v0(self): copy_network_unit('12-dummy.netdev', '25-erspan.network', '25-erspan0-tunnel.netdev', '25-tunnel.network', @@ -3119,7 +3012,6 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'erspan98:routable', 'dummy98:degraded') - @expectedFailureIfERSPANv2IsNotSupported() def test_erspan_tunnel_v2(self): copy_network_unit('12-dummy.netdev', '25-erspan.network', '25-erspan2-tunnel.netdev', '25-tunnel.network', @@ -3696,7 +3588,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - @expectedFailureIfKernelReturnsInvalidFlags() def test_address_static(self): copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False) self.setup_nftset('addr4', 'ipv4_addr') @@ -4289,7 +4180,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): else: self.assertFalse(True) - @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable() def test_routing_policy_rule_port_range(self): copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev') start_networkd() @@ -4304,7 +4194,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'ipproto (tcp|ipproto-6) ') self.assertIn('lookup 7 ', output) - @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable() def test_routing_policy_rule_invert(self): copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev') start_networkd() @@ -4318,7 +4207,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'ipproto (tcp|ipproto-6) ') self.assertIn('lookup 7 ', output) - @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable() def test_routing_policy_rule_l3mdev(self): copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev') start_networkd() @@ -4329,7 +4217,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertIn('1500: from all lookup [l3mdev-table]', output) self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output) - @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable() def test_routing_policy_rule_uidrange(self): copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev') start_networkd() @@ -5610,7 +5497,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertEqual(output, '') - @expectedFailureIfNexthopIsNotAvailable() def test_nexthop(self): first = True for manage_foreign_nexthops in [True, False]: @@ -6354,13 +6240,10 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'dev bridge99 port test1 grp ff02:aaaa:fee5::1:3 permanent *vid 4064') self.assertRegex(output, 'dev bridge99 port test1 grp 224.0.1.1 permanent *vid 4065') + self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066') + self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067') - # Old kernel may not support bridge MDB entries on bridge master - if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 224.0.1.3 temp vid 4068') == 0: - self.assertRegex(output, 'dev bridge99 port bridge99 grp ff02:aaaa:fee5::1:4 temp *vid 4066') - self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067') - - # Old kernel may not support L2 bridge MDB entries + # The kernels older than 955062b03fa62b802a1ee34fbb04e39f7a70ae73 (v5.11) do not support L2 bridge MDB entries if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070') == 0: self.assertRegex(output, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069') @@ -6625,7 +6508,7 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): with open(f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8') as f: f.write(f'{num_vfs}') - @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable() + @expectedFailureIfModuleIsNotAvailable('netdevsim') def test_sriov(self): copy_network_unit('25-netdevsim.link', '25-sriov.network') @@ -6642,7 +6525,7 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off' ) - @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable() + @expectedFailureIfModuleIsNotAvailable('netdevsim') def test_sriov_udev(self): copy_network_unit('25-sriov.link', '25-sriov-udev.network') @@ -7785,7 +7668,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertIn('DHCPREPLY(veth-peer)', output) self.assertIn('sent size: 0 option: 14 rapid-commit', output) - @expectedFailureIfKernelReturnsInvalidFlags() def test_dhcp_client_ipv4_only(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network', '25-sit-dhcp4.netdev', '25-sit-dhcp4.network') From 9c0c203db4f989f9c66c59d34afa9842713db73d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 22 Feb 2026 05:15:44 +0900 Subject: [PATCH 28/62] test-network: assume the test is running with newer udevd --- test/test-network/systemd-networkd-tests.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 329cbe1e1d..7123f052ac 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1529,10 +1529,6 @@ class NetworkctlTests(unittest.TestCase, Utilities): self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).') - # This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249). - # In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property. - # Let's reprocess the interface and drop the property. - udevadm_trigger('/sys/class/net/lo') output = networkctl_status('lo') print(output) self.assertIn('Link File: n/a', output) From 59199ec3bdacef79e366761a5c5a31c187a608a7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 22 Feb 2026 05:25:49 +0900 Subject: [PATCH 29/62] test-network: drop old ip command support --- test/test-network/systemd-networkd-tests.py | 28 +++++++++------------ 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 7123f052ac..d68305f1fa 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -2174,13 +2174,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show testtun99') print(output) - # Old ip command does not support IFF_ flags - self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ') + self.assertIn('tun type tun pi on vnet_hdr on multi_queue', output) self.assertIn('UP,LOWER_UP', output) output = check_output('ip -d link show testtap99') print(output) - self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ') + self.assertIn('tun type tap pi on vnet_hdr on multi_queue', output) self.assertIn('UP,LOWER_UP', output) else: @@ -2190,7 +2189,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): for _ in range(20): output = check_output('ip -d link show testtun99') print(output) - self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ') + self.assertIn('tun type tun pi on vnet_hdr on multi_queue', output) if 'NO-CARRIER' in output: break time.sleep(0.5) @@ -2200,7 +2199,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): for _ in range(20): output = check_output('ip -d link show testtap99') print(output) - self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ') + self.assertIn('tun type tap pi on vnet_hdr on multi_queue', output) if 'NO-CARRIER' in output: break time.sleep(0.5) @@ -4313,24 +4312,21 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print('### ip route show 192.168.10.2') output = check_output('ip route show 192.168.10.2') print(output) - # old ip command does not show IPv6 gateways... self.assertIn('192.168.10.2 proto static', output) - self.assertIn('nexthop', output) - self.assertIn('dev test1 weight 20', output) - self.assertIn('dev test1 weight 30', output) - self.assertIn('dev dummy98 weight 10', output) - self.assertIn('dev dummy98 weight 5', output) + self.assertIn('nexthop via inet6 2001:1234:5:6fff:ff:ff:ff:ff dev test1 weight 20', output) + self.assertIn('nexthop via inet6 2001:1234:5:7fff:ff:ff:ff:ff dev test1 weight 30', output) + self.assertIn('nexthop via inet6 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98 weight 10', output) + self.assertIn('nexthop via inet6 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98 weight 5', output) print('### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff') output = check_output('ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff') print(output) - # old ip command does not show 'nexthop' keyword and weight... self.assertIn('2001:1234:5:bfff:ff:ff:ff:ff', output) if test1_is_managed: - self.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output) - self.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output) - self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output) - self.assertIn('via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98', output) + self.assertIn('nexthop via 2001:1234:5:6fff:ff:ff:ff:ff dev test1 weight 20', output) + self.assertIn('nexthop via 2001:1234:5:7fff:ff:ff:ff:ff dev test1 weight 30', output) + self.assertIn('nexthop via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98 weight 10', output) + self.assertIn('nexthop via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98 weight 5', output) print('### ip route show 192.168.20.0/24') output = check_output('ip route show 192.168.20.0/24') From 1b94d7d7ecbfac9698c199e198722258299e6940 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:02:04 +0900 Subject: [PATCH 30/62] test-network: apply "ruff format" and "ruff check --fix" --- test/test-network/systemd-networkd-tests.py | 3472 +++++++++++++------ 1 file changed, 2458 insertions(+), 1014 deletions(-) diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index d68305f1fa..3d3931adec 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -3,7 +3,8 @@ # systemd-networkd tests # These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM, -# simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py. +# simply run this file which can be found in the VM at +# /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py. # # To run an individual test, specify it as a command line argument in the form # of .. E.g. the NetworkdMTUTests class has a test @@ -27,8 +28,8 @@ import argparse import datetime import enum import errno -import itertools import ipaddress +import itertools import json import os import pathlib @@ -85,8 +86,8 @@ wait_online_env = {} asan_options = os.getenv('ASAN_OPTIONS') lsan_options = os.getenv('LSAN_OPTIONS') ubsan_options = os.getenv('UBSAN_OPTIONS') -with_coverage = os.getenv('COVERAGE_BUILD_DIR') != None -show_journal = True # When true, show journal on stopping networkd. +with_coverage = os.getenv('COVERAGE_BUILD_DIR') is not None +show_journal = True # When true, show journal on stopping networkd. active_units = [] protected_links = { @@ -108,54 +109,98 @@ saved_ipv4_rules = None saved_ipv6_rules = None saved_timezone = None + def rm_f(path): if os.path.exists(path): os.remove(path) + def rm_rf(path): shutil.rmtree(path, ignore_errors=True) + def cp(src, dst): shutil.copy(src, dst) + def cp_r(src, dst): shutil.copytree(src, dst, copy_function=shutil.copy, dirs_exist_ok=True) + def mkdir_p(path): os.makedirs(path, exist_ok=True) + def touch(path): pathlib.Path(path).touch() + # pylint: disable=R1710 def check_output(*command, **kwargs): # This checks the result and returns stdout (and stderr) on success. command = command[0].split() + list(command[1:]) - ret = subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs) + ret = subprocess.run( + command, + check=False, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + **kwargs, + ) if ret.returncode == 0: return ret.stdout.rstrip() # When returncode != 0, print stdout and stderr, then trigger CalledProcessError. print(ret.stdout) ret.check_returncode() + def call(*command, **kwargs): # This returns returncode. stdout and stderr are merged and shown in console command = command[0].split() + list(command[1:]) - return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).returncode + return subprocess.run( + command, + check=False, + text=True, + stderr=subprocess.STDOUT, + **kwargs, + ).returncode + def call_check(*command, **kwargs): # Same as call() above, but it triggers CalledProcessError if rc != 0 command = command[0].split() + list(command[1:]) - return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).check_returncode() + return subprocess.run( + command, + check=False, + text=True, + stderr=subprocess.STDOUT, + **kwargs, + ).check_returncode() + def call_quiet(*command, **kwargs): command = command[0].split() + list(command[1:]) - return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode + return subprocess.run( + command, + check=False, + text=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + **kwargs, + ).returncode + def run(*command, **kwargs): # This returns CompletedProcess instance. command = command[0].split() + list(command[1:]) - return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) + return subprocess.run( + command, + check=False, + text=True, + capture_output=True, + **kwargs, + ) + def check_json(string): try: @@ -164,6 +209,7 @@ def check_json(string): print(f"String is not a valid JSON: '{string}'") raise + def is_module_available(*module_names): for module_name in module_names: lsmod_output = check_output('lsmod') @@ -172,16 +218,19 @@ def is_module_available(*module_names): return False return True + def expectedFailureIfModuleIsNotAvailable(*module_names): def f(func): return func if is_module_available(*module_names) else unittest.expectedFailure(func) return f + # pylint: disable=C0415 def compare_kernel_version(min_kernel_version): try: import platform + from packaging import version except ImportError: print('Failed to import either platform or packaging module, assuming the comparison failed') @@ -195,6 +244,7 @@ def compare_kernel_version(min_kernel_version): return version.parse(kver) >= version.parse(min_kernel_version) + def copy_network_unit(*units, copy_dropins=True): """ Copy networkd unit files into the testbed. @@ -210,7 +260,10 @@ def copy_network_unit(*units, copy_dropins=True): mkdir_p(network_unit_dir) for unit in units: if copy_dropins and os.path.exists(os.path.join(networkd_ci_temp_dir, unit + '.d')): - cp_r(os.path.join(networkd_ci_temp_dir, unit + '.d'), os.path.join(network_unit_dir, unit + '.d')) + cp_r( + os.path.join(networkd_ci_temp_dir, unit + '.d'), + os.path.join(network_unit_dir, unit + '.d'), + ) if unit.endswith('.conf'): dropin = unit @@ -227,10 +280,11 @@ def copy_network_unit(*units, copy_dropins=True): if has_link: udevadm_reload() + def copy_credential(src, target): - mkdir_p(credstore_dir) - cp(os.path.join(networkd_ci_temp_dir, src), - os.path.join(credstore_dir, target)) + mkdir_p(credstore_dir) + cp(os.path.join(networkd_ci_temp_dir, src), os.path.join(credstore_dir, target)) + def remove_network_unit(*units): """ @@ -249,10 +303,12 @@ def remove_network_unit(*units): if has_link: udevadm_reload() + def touch_network_unit(*units): for unit in units: touch(os.path.join(network_unit_dir, unit)) + def clear_network_units(): has_link = False if os.path.exists(network_unit_dir): @@ -266,20 +322,24 @@ def clear_network_units(): if has_link: udevadm_reload() + def copy_networkd_conf_dropin(*dropins): """Copy networkd.conf dropin files into the testbed.""" mkdir_p(networkd_conf_dropin_dir) for dropin in dropins: cp(os.path.join(networkd_ci_temp_dir, dropin), networkd_conf_dropin_dir) + def remove_networkd_conf_dropin(*dropins): """Remove previously copied networkd.conf dropin files from the testbed.""" for dropin in dropins: rm_f(os.path.join(networkd_conf_dropin_dir, dropin)) + def clear_networkd_conf_dropins(): rm_rf(networkd_conf_dropin_dir) + def setup_systemd_udev_rules(): if not build_dir and not source_dir: return @@ -290,49 +350,55 @@ def setup_systemd_udev_rules(): if not path: continue - path = os.path.join(path, "rules.d") - print(f"Copying udev rules from {path} to {udev_rules_dir}") + path = os.path.join(path, 'rules.d') + print(f'Copying udev rules from {path} to {udev_rules_dir}') for rule in os.listdir(path): - if not rule.endswith(".rules"): + if not rule.endswith('.rules'): continue cp(os.path.join(path, rule), udev_rules_dir) + def clear_networkd_state_files(): rm_rf('/var/lib/systemd/network/') + def copy_udev_rule(*rules): """Copy udev rules""" mkdir_p(udev_rules_dir) for rule in rules: cp(os.path.join(networkd_ci_temp_dir, rule), udev_rules_dir) + def remove_udev_rule(*rules): """Remove previously copied udev rules""" for rule in rules: rm_f(os.path.join(udev_rules_dir, rule)) + def clear_udev_rules(): rm_rf(udev_rules_dir) + def save_active_units(): for u in [ - 'systemd-networkd.socket', - 'systemd-networkd-resolve-hook.socket', - 'systemd-networkd-varlink.socket', - 'systemd-networkd-varlink-metrics.socket', - 'systemd-networkd.service', - 'systemd-resolved-monitor.socket', - 'systemd-resolved-varlink.socket', - 'systemd-resolved.service', - 'systemd-timesyncd.service', - 'firewalld.service', - 'nftables.service', + 'systemd-networkd.socket', + 'systemd-networkd-resolve-hook.socket', + 'systemd-networkd-varlink.socket', + 'systemd-networkd-varlink-metrics.socket', + 'systemd-networkd.service', + 'systemd-resolved-monitor.socket', + 'systemd-resolved-varlink.socket', + 'systemd-resolved.service', + 'systemd-timesyncd.service', + 'firewalld.service', + 'nftables.service', ]: if call(f'systemctl is-active --quiet {u}') == 0: call(f'systemctl stop {u}') active_units.append(u) + def restore_active_units(): has_network_socket = False has_resolve_socket = False @@ -370,11 +436,13 @@ def restore_active_units(): for u in active_units: call(f'systemctl restart {u}') + def create_unit_dropin(unit, contents): mkdir_p(f'/run/systemd/system/{unit}.d') with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f: f.write('\n'.join(contents)) + def create_service_dropin(service, command, additional_settings=None): drop_in = ['[Service]'] if command: @@ -412,37 +480,43 @@ def create_service_dropin(service, command, additional_settings=None): create_unit_dropin(f'{service}.service', drop_in) + def setup_system_units(): if build_dir or source_dir: mkdir_p('/run/systemd/system/') for unit in [ - 'systemd-networkd.service', - 'systemd-networkd.socket', - 'systemd-networkd-persistent-storage.service', - 'systemd-networkd-resolve-hook.socket', - 'systemd-networkd-varlink.socket', - 'systemd-networkd-varlink-metrics.socket', - 'systemd-resolved.service', - 'systemd-timesyncd.service', - 'systemd-udevd.service', + 'systemd-networkd.service', + 'systemd-networkd.socket', + 'systemd-networkd-persistent-storage.service', + 'systemd-networkd-resolve-hook.socket', + 'systemd-networkd-varlink.socket', + 'systemd-networkd-varlink-metrics.socket', + 'systemd-resolved.service', + 'systemd-timesyncd.service', + 'systemd-udevd.service', ]: for path in [build_dir, source_dir]: if not path: continue - fullpath = os.path.join(os.path.join(path, "units"), unit) + fullpath = os.path.join(os.path.join(path, 'units'), unit) if os.path.exists(fullpath): - print(f"Copying unit file from {fullpath} to /run/systemd/system/") + print(f'Copying unit file from {fullpath} to /run/systemd/system/') cp(fullpath, '/run/systemd/system/') break - create_service_dropin('systemd-networkd', networkd_bin, - ['[Service]', - 'Restart=no', - 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes', - '[Unit]', - 'StartLimitIntervalSec=0']) + create_service_dropin( + 'systemd-networkd', + networkd_bin, + [ + '[Service]', + 'Restart=no', + 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes', + '[Unit]', + 'StartLimitIntervalSec=0', + ], + ) create_service_dropin('systemd-resolved', resolved_bin) create_service_dropin('systemd-timesyncd', timesyncd_bin) @@ -452,7 +526,7 @@ def setup_system_units(): [ '[Unit]', 'StartLimitIntervalSec=0', - ] + ], ) create_unit_dropin( 'systemd-networkd-persistent-storage.service', @@ -465,28 +539,28 @@ def setup_system_units(): 'ExecStop=', f'ExecStop={networkctl_bin} persistent-storage no', 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug else '', - ] + ], ) create_unit_dropin( 'systemd-networkd-resolve-hook.socket', [ '[Unit]', 'StartLimitIntervalSec=0', - ] + ], ) create_unit_dropin( 'systemd-networkd-varlink.socket', [ '[Unit]', 'StartLimitIntervalSec=0', - ] + ], ) create_unit_dropin( 'systemd-networkd-varlink-metrics.socket', [ '[Unit]', 'StartLimitIntervalSec=0', - ] + ], ) create_unit_dropin( 'systemd-udevd.service', @@ -494,7 +568,7 @@ def setup_system_units(): '[Service]', 'ExecStart=', f'ExecStart=@{udevadm_bin} systemd-udevd', - ] + ], ) check_output('systemctl daemon-reload') @@ -507,6 +581,7 @@ def setup_system_units(): check_output('systemctl restart systemd-timesyncd.service') check_output('systemctl restart systemd-udevd.service') + def clear_system_units(): def rm_unit(name): rm_f(f'/run/systemd/system/{name}') @@ -524,15 +599,18 @@ def clear_system_units(): check_output('systemctl daemon-reload') check_output('systemctl restart systemd-udevd.service') + def link_exists(*links): for link in links: if call_quiet(f'ip link show {link}') != 0: return False return True + def link_resolve(link): return check_output(f'ip link show {link}').split(':')[1].strip().split('@')[0] + def remove_link(*links, protect=False): for link in links: if protect and link in protected_links: @@ -540,6 +618,7 @@ def remove_link(*links, protect=False): if link_exists(link): call(f'ip link del dev {link}') + def save_existing_links(): links = os.listdir('/sys/class/net') for link in links: @@ -549,6 +628,7 @@ def save_existing_links(): print('### The following links will be protected:') print(', '.join(sorted(list(protected_links)))) + def unmanage_existing_links(): mkdir_p(network_unit_dir) @@ -558,16 +638,19 @@ def unmanage_existing_links(): f.write(f'Name={link}\n') f.write('\n[Link]\nUnmanaged=yes\n') + def flush_links(): links = os.listdir('/sys/class/net') remove_link(*links, protect=True) + def flush_nexthops(): # Currently, the 'ip nexthop' command does not have 'save' and 'restore'. # Hence, we cannot restore nexthops in a simple way. # Let's assume there is no nexthop used in the system call_quiet('ip nexthop flush') + def save_routes(): # pylint: disable=global-statement global saved_routes @@ -575,6 +658,7 @@ def save_routes(): print('### The following routes will be protected:') print(saved_routes) + def flush_routes(): have = False output = check_output('ip route show table all') @@ -583,7 +667,7 @@ def flush_routes(): continue if 'proto kernel' in line: continue - if ' dev ' in line and not ' dev lo ' in line: + if ' dev ' in line and ' dev lo ' not in line: continue if not have: have = True @@ -591,9 +675,11 @@ def flush_routes(): print(f'# {line}') call(f'ip route del {line}') + def save_routing_policy_rules(): # pylint: disable=global-statement global saved_ipv4_rules, saved_ipv6_rules + def save(ipv): output = check_output(f'ip -{ipv} rule show') print(f'### The following IPv{ipv} routing policy rules will be protected:') @@ -603,6 +689,7 @@ def save_routing_policy_rules(): saved_ipv4_rules = save(4) saved_ipv6_rules = save(6) + def flush_routing_policy_rules(): def flush(ipv, saved_rules): have = False @@ -612,7 +699,7 @@ def flush_routing_policy_rules(): continue if not have: have = True - print(f'### Removing IPv{ipv} routing policy rules that did not exist when the test started.') + print(f'### Removing IPv{ipv} routing policy rules that did not exist when the test started.') # fmt: skip print(f'# {line}') words = line.replace('lookup [l3mdev-table]', 'l3mdev').replace('[detached]', '').split() priority = words[0].rstrip(':') @@ -621,19 +708,21 @@ def flush_routing_policy_rules(): flush(4, saved_ipv4_rules) flush(6, saved_ipv6_rules) + def flush_fou_ports(): ret = run('ip fou show') if ret.returncode != 0: - return # fou may not be supported + return # fou may not be supported for line in ret.stdout.splitlines(): port = line.split()[1] call(f'ip fou del port {port}') + def flush_l2tp_tunnels(): tids = [] ret = run('ip l2tp show tunnel') if ret.returncode != 0: - return # l2tp may not be supported + return # l2tp may not be supported for line in ret.stdout.splitlines(): words = line.split() if words[0] == 'Tunnel': @@ -647,10 +736,11 @@ def flush_l2tp_tunnels(): r = run(f'ip l2tp show tunnel tunnel_id {tid}') if r.returncode != 0 or len(r.stdout.rstrip()) == 0: break - time.sleep(.2) + time.sleep(0.2) else: print(f'Cannot remove L2TP tunnel {tid}, ignoring.') + def save_timezone(): # pylint: disable=global-statement global saved_timezone @@ -659,66 +749,79 @@ def save_timezone(): saved_timezone = r.stdout.rstrip() print(f'### Saved timezone: {saved_timezone}') + def restore_timezone(): if saved_timezone: call(*timedatectl_cmd, 'set-timezone', f'{saved_timezone}', env=env) + def read_link_attr(*args): with open(os.path.join('/sys/class/net', *args), encoding='utf-8') as f: return f.readline().strip() + def read_manager_state_file(): with open('/run/systemd/netif/state', encoding='utf-8') as f: return f.read() + def read_link_state_file(link): ifindex = read_link_attr(link, 'ifindex') path = os.path.join('/run/systemd/netif/links', ifindex) with open(path, encoding='utf-8') as f: return f.read() + def read_ip_sysctl_attr(link, attribute, ipv): with open(os.path.join('/proc/sys/net', ipv, 'conf', link, attribute), encoding='utf-8') as f: return f.readline().strip() + def read_ip_neigh_sysctl_attr(link, attribute, ipv): with open(os.path.join('/proc/sys/net', ipv, 'neigh', link, attribute), encoding='utf-8') as f: return f.readline().strip() + def read_ipv6_sysctl_attr(link, attribute): return read_ip_sysctl_attr(link, attribute, 'ipv6') + def read_ipv6_neigh_sysctl_attr(link, attribute): return read_ip_neigh_sysctl_attr(link, attribute, 'ipv6') + def read_ipv4_sysctl_attr(link, attribute): return read_ip_sysctl_attr(link, attribute, 'ipv4') + def read_mpls_sysctl_attr(link, attribute): return read_ip_sysctl_attr(link, attribute, 'mpls') + def stop_by_pid_file(pid_file): if not os.path.exists(pid_file): return - with open(pid_file, 'r', encoding='utf-8') as f: + with open(pid_file, encoding='utf-8') as f: pid = f.read().rstrip(' \t\r\n\0') os.kill(int(pid), signal.SIGTERM) for _ in range(25): try: os.kill(int(pid), 0) - print(f"PID {pid} is still alive, waiting...") - time.sleep(.2) + print(f'PID {pid} is still alive, waiting...') + time.sleep(0.2) except OSError as e: if e.errno == errno.ESRCH: break - print(f"Unexpected exception when waiting for {pid} to die: {e.errno}") + print(f'Unexpected exception when waiting for {pid} to die: {e.errno}') rm_f(pid_file) -def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None): - b = bytes() - pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c - pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n) + +def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=('dot',), dohpath=None): + b = b'' + pack = lambda c, w=1: struct.pack('>' + '_BH_I'[w], len(c)) + c + pyton = lambda n, w=2: struct.pack('>' + '_BH_I'[w], n) ipv4 = ipaddress.IPv4Address + class SvcParam(enum.Enum): ALPN = 1 DOHPATH = 7 @@ -728,7 +831,7 @@ def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None): adn = adn.rstrip('.') + '.' data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.'))) - if not addrs: # adn-only mode + if not addrs: # adn-only mode return pack(data, 2) data += pack(b.join(ipv4(addr).packed for addr in addrs)) @@ -738,11 +841,13 @@ def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None): return pack(data, 2) -def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None): - b = bytes() - pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c - pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n) + +def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=('dot',), dohpath=None): + b = b'' + pack = lambda c, w=1: struct.pack('>' + '_BH_I'[w], len(c)) + c + pyton = lambda n, w=2: struct.pack('>' + '_BH_I'[w], n) ipv6 = ipaddress.IPv6Address + class SvcParam(enum.Enum): ALPN = 1 DOHPATH = 7 @@ -752,7 +857,7 @@ def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None): adn = adn.rstrip('.') + '.' data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')), 2) - if not addrs: # adn-only mode + if not addrs: # adn-only mode return data data += pack(b.join(ipv6(addr).packed for addr in addrs), 2) @@ -762,7 +867,16 @@ def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None): return data -def start_dnsmasq(*additional_options, namespace=None, interface='veth-peer', ra_mode=None, ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20'): + +def start_dnsmasq( + *additional_options, + namespace=None, + interface='veth-peer', + ra_mode=None, + ipv4_range='192.168.5.10,192.168.5.200', + ipv4_router='192.168.5.1', + ipv6_range='2600::10,2600::20', +): if ra_mode: ra_mode = f',{ra_mode}' else: @@ -773,49 +887,67 @@ def start_dnsmasq(*additional_options, namespace=None, interface='veth-peer', ra else: ns_command = () - command = ns_command + ( - 'dnsmasq', - f'--log-facility={dnsmasq_log_file}', - '--log-queries=extra', - '--log-dhcp', - f'--pid-file={dnsmasq_pid_file}', - '--conf-file=/dev/null', - '--bind-interfaces', - f'--interface={interface}', - f'--dhcp-leasefile={dnsmasq_lease_file}', - '--enable-ra', - f'--dhcp-range={ipv6_range}{ra_mode},2m', - f'--dhcp-range={ipv4_range},2m', - '--dhcp-option=option:mtu,1492', - f'--dhcp-option=option:router,{ipv4_router}', - '--port=0', - '--no-resolv', - ) + additional_options + command = ( + ns_command + + ( + 'dnsmasq', + f'--log-facility={dnsmasq_log_file}', + '--log-queries=extra', + '--log-dhcp', + f'--pid-file={dnsmasq_pid_file}', + '--conf-file=/dev/null', + '--bind-interfaces', + f'--interface={interface}', + f'--dhcp-leasefile={dnsmasq_lease_file}', + '--enable-ra', + f'--dhcp-range={ipv6_range}{ra_mode},2m', + f'--dhcp-range={ipv4_range},2m', + '--dhcp-option=option:mtu,1492', + f'--dhcp-option=option:router,{ipv4_router}', + '--port=0', + '--no-resolv', + ) + + additional_options + ) check_output(*command) + def stop_dnsmasq(): stop_by_pid_file(dnsmasq_pid_file) rm_f(dnsmasq_lease_file) rm_f(dnsmasq_log_file) + def read_dnsmasq_log_file(): with open(dnsmasq_log_file, encoding='utf-8') as f: return f.read() + def start_isc_dhcpd(conf_file, ipv, interface='veth-peer'): conf_file_path = os.path.join(networkd_ci_temp_dir, conf_file) - isc_dhcpd_command = f'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}' + isc_dhcpd_command = f'dhcpd {ipv} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}' # fmt: skip touch(isc_dhcpd_lease_file) check_output(isc_dhcpd_command) + def stop_isc_dhcpd(): stop_by_pid_file(isc_dhcpd_pid_file) rm_f(isc_dhcpd_lease_file) + def get_dbus_link_path(link): - out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1', - '/org/freedesktop/network1', 'org.freedesktop.network1.Manager', - 'GetLinkByName', 's', link]) + out = subprocess.check_output( + [ + 'busctl', + 'call', + 'org.freedesktop.network1', + '/org/freedesktop/network1', + 'org.freedesktop.network1.Manager', + 'GetLinkByName', + 's', + link, + ] + ) assert out.startswith(b'io ') out = out.strip() @@ -823,42 +955,72 @@ def get_dbus_link_path(link): out = out.decode() return out[:-1].split('"')[1] + def get_dhcp_server_property(link, prop): link_path = get_dbus_link_path(link) - out = subprocess.check_output(['busctl', 'get-property', 'org.freedesktop.network1', - link_path, 'org.freedesktop.network1.DHCPServer', prop]) + out = subprocess.check_output( + [ + 'busctl', + 'get-property', + 'org.freedesktop.network1', + link_path, + 'org.freedesktop.network1.DHCPServer', + prop, + ] + ) return out.strip().decode() + def get_dhcp_client_state(link, family): link_path = get_dbus_link_path(link) - out = subprocess.check_output(['busctl', 'get-property', 'org.freedesktop.network1', - link_path, f'org.freedesktop.network1.DHCPv{family}Client', 'State']) + out = subprocess.check_output( + [ + 'busctl', + 'get-property', + 'org.freedesktop.network1', + link_path, + f'org.freedesktop.network1.DHCPv{family}Client', + 'State', + ] + ) assert out.startswith(b's "') out = out.strip() assert out.endswith(b'"') return out[3:-1].decode() + def get_dhcp4_client_state(link): return get_dhcp_client_state(link, '4') + def get_dhcp6_client_state(link): return get_dhcp_client_state(link, '6') + def get_link_description(link): link_path = get_dbus_link_path(link) - out = subprocess.check_output(['busctl', 'call', 'org.freedesktop.network1', - link_path, 'org.freedesktop.network1.Link', 'Describe']) + out = subprocess.check_output( + [ + 'busctl', + 'call', + 'org.freedesktop.network1', + link_path, + 'org.freedesktop.network1.Link', + 'Describe', + ] + ) assert out.startswith(b's "') out = out.strip() assert out.endswith(b'"') json_raw = out[2:].decode() check_json(json_raw) - description = json.loads(json_raw) # Convert from escaped sequences to json + description = json.loads(json_raw) # Convert from escaped sequences to json check_json(description) - return json.loads(description) # Now parse the json + return json.loads(description) # Now parse the json + def start_radvd(*additional_options, config_file): config_file_path = os.path.join(networkd_ci_temp_dir, 'radvd', config_file) @@ -870,9 +1032,11 @@ def start_radvd(*additional_options, config_file): ) + additional_options check_output(*command) + def stop_radvd(): stop_by_pid_file(radvd_pid_file) + def start_modem_manager_mock(*additional_options): dbus_policy_src = os.path.join(networkd_ci_temp_dir, 'mock-modem-manager.conf') cp(dbus_policy_src, '/etc/dbus-1/system.d/mock-modem-manager.conf') @@ -880,21 +1044,25 @@ def start_modem_manager_mock(*additional_options): command = ' '.join([test_modem_manager_mock] + list(additional_options)) with open('/run/systemd/system/test-modem-manager-mock.service', mode='w', encoding='utf-8') as f: - f.write('[Unit]\n' - 'Description=Mock ModemManager for networkd testing\n' - '[Service]\n' - 'Type=notify\n' - 'BusName=org.freedesktop.ModemManager1\n' - f'ExecStart={command}\n') + f.write( + '[Unit]\n' + 'Description=Mock ModemManager for networkd testing\n' + '[Service]\n' + 'Type=notify\n' + 'BusName=org.freedesktop.ModemManager1\n' + f'ExecStart={command}\n' + ) check_output('systemctl daemon-reload') check_output('systemctl start test-modem-manager-mock.service') + def stop_modem_manager_mock(): call('systemctl stop test-modem-manager-mock.service') rm_f('/run/systemd/system/test-modem-manager-mock.service') call('systemctl daemon-reload') rm_f('/etc/dbus-1/system.d/mock-modem-manager.conf') + def radvd_check_config(config_file): if not shutil.which('radvd'): print('radvd is not installed, assuming the config check failed') @@ -905,12 +1073,15 @@ def radvd_check_config(config_file): config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf/radvd', config_file) return call(f'radvd --config={config_file_path} --configtest') == 0 + def networkd_invocation_id(): return check_output('systemctl show --value -p InvocationID systemd-networkd.service') + def networkd_pid(): return int(check_output('systemctl show --value -p MainPID systemd-networkd.service')) + def read_networkd_log(invocation_id=None, since=None): if not invocation_id: invocation_id = networkd_invocation_id() @@ -925,9 +1096,11 @@ def read_networkd_log(invocation_id=None, since=None): check_output('journalctl --sync') return check_output(*command) + def networkd_is_failed(): return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1 + def stop_networkd(show_logs=True, check_failed=True): global show_journal show_logs = show_logs and show_journal @@ -954,12 +1127,14 @@ def stop_networkd(show_logs=True, check_failed=True): if check_failed: assert not networkd_is_failed() + def start_networkd(): check_output('systemctl start systemd-networkd') invocation_id = networkd_invocation_id() pid = networkd_pid() print(f'Started systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}') + def restart_networkd(show_logs=True): global show_journal show_logs = show_logs and show_journal @@ -973,39 +1148,50 @@ def restart_networkd(show_logs=True): pid = networkd_pid() print(f'Restarted systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}') + def networkctl(*args): # Do not call networkctl if networkd is in failed state. # Otherwise, networkd may be restarted and we may get wrong results. assert not networkd_is_failed() return check_output(*(networkctl_cmd + list(args)), env=env) + def networkctl_status(*args): return networkctl('-n', '0', 'status', *args) + def networkctl_json(*args): return networkctl('--json=short', 'status', *args) + def networkctl_reconfigure(*links): networkctl('reconfigure', *links) + def networkctl_reload(): networkctl('reload') + def resolvectl(*args): return check_output(*(resolvectl_cmd + list(args)), env=env) + def timedatectl(*args): return check_output(*(timedatectl_cmd + list(args)), env=env) + def udevadm(*args): return check_output(*(udevadm_cmd + list(args))) + def udevadm_reload(): udevadm('control', '--reload') + def udevadm_trigger(*args, action='add'): udevadm('trigger', '--settle', f'--action={action}', *args) + def setup_common(): # Protect existing links unmanage_existing_links() @@ -1016,6 +1202,7 @@ def setup_common(): sys.stdout.flush() check_output('journalctl --sync') + def tear_down_common(): # 1. stop DHCP/RA servers stop_dnsmasq() @@ -1057,6 +1244,7 @@ def tear_down_common(): # 9. check the status of networkd assert not networkd_is_failed() + def setUpModule(): rm_rf(networkd_ci_temp_dir) cp_r(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_temp_dir) @@ -1078,6 +1266,7 @@ def setUpModule(): setup_system_units() + def tearDownModule(): rm_rf(networkd_ci_temp_dir) clear_udev_rules() @@ -1094,7 +1283,8 @@ def tearDownModule(): sys.stdout.flush() check_output('journalctl --sync') -class Utilities(): + +class Utilities: # pylint: disable=no-member def check_link_exists(self, *link, expected=True): @@ -1146,7 +1336,9 @@ class Utilities(): else: self.fail(f'Timed out waiting for {link} activated.') - def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True): + def wait_operstate( + self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True + ): """Wait for the link to reach the specified operstate and/or setup state. Specify None or '' for either operstate or setup_state to ignore that state. @@ -1184,7 +1376,17 @@ class Utilities(): self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}') return False - def wait_online(self, *links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5, bool_dns=False): + def wait_online( + self, + *links_with_operstate, + timeout='20s', + bool_any=False, + ipv4=False, + ipv6=False, + setup_state='configured', + setup_timeout=5, + bool_dns=False, + ): """Wait for the links to reach the specified operstate and/or setup state. This is similar to wait_operstate() but can be used for multiple links, @@ -1200,8 +1402,8 @@ class Utilities(): Set 'bool_dns' to True to wait for DNS servers to be accessible. - Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the given links. - This is applied only for the operational state 'degraded' or above. + Set 'ipv4' or 'ipv6' to True to wait for IPv4 address or IPv6 address, respectively, of each of the + given links. This is applied only for the operational state 'degraded' or above. Note that this function waits for the links to reach *or exceed* the given operstate. However, the setup_state, if specified, must be matched *exactly*. @@ -1209,7 +1411,12 @@ class Utilities(): This returns if the links reached the requested operstate/setup_state; otherwise it raises CalledProcessError or fails test assertion. """ - args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] + [f'--ignore={link}' for link in protected_links] + args = ( + wait_online_cmd + + [f'--timeout={timeout}'] + + [f'--interface={link}' for link in links_with_operstate] + + [f'--ignore={link}' for link in protected_links] + ) if bool_any: args += ['--any'] if bool_dns: @@ -1278,7 +1485,7 @@ class Utilities(): print('## Checking NetLabel skipped: selinuxenabled command not found.') elif call_quiet('selinuxenabled') != 0: print('## Checking NetLabel skipped: SELinux disabled.') - elif not shutil.which('netlabelctl'): # not packaged by all distros + elif not shutil.which('netlabelctl'): # not packaged by all distros print('## Checking NetLabel skipped: netlabelctl command not found.') else: output = check_output('netlabelctl unlbl list') @@ -1289,7 +1496,7 @@ class Utilities(): if not shutil.which('nft'): print('## Setting up NFT sets skipped: nft command not found.') else: - if call(f'nft add table inet sd_test') != 0: + if call('nft add table inet sd_test') != 0: print('## Setting up NFT table failed.') self.fail() if call(f'nft add set inet sd_test {filter_name} {{ type {filter_type}; {flags} }}') != 0: @@ -1304,7 +1511,7 @@ class Utilities(): if call(f'nft delete set inet sd_test {filter_name}') != 0: print('## Tearing down NFT sets failed.') self.fail() - if call(f'nft delete table inet sd_test') != 0: + if call('nft delete table inet sd_test') != 0: print('## Tearing down NFT table failed.') self.fail() @@ -1338,8 +1545,8 @@ class Utilities(): if link_file: self.assertRegex(output, rf'Link File: .*/{link_file}\.link') -class NetworkctlTests(unittest.TestCase, Utilities): +class NetworkctlTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -1355,8 +1562,11 @@ class NetworkctlTests(unittest.TestCase, Utilities): self.assertRegex(output, 'hogehogehogehogehogehoge') def test_rename_to_altname(self): - copy_network_unit('26-netdev-link-local-addressing-yes.network', - '12-dummy.netdev', '12-dummy-rename-to-altname.link') + copy_network_unit( + '26-netdev-link-local-addressing-yes.network', + '12-dummy.netdev', + '12-dummy-rename-to-altname.link', + ) start_networkd() self.wait_online('dummyalt:degraded') @@ -1527,7 +1737,10 @@ class NetworkctlTests(unittest.TestCase, Utilities): self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output) self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output) - self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).') + self.check_networkd_log( + 'test1: Configuring with /run/systemd/network/11-test-unit-file.network ' + '(dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).' + ) output = networkctl_status('lo') print(output) @@ -1544,8 +1757,8 @@ class NetworkctlTests(unittest.TestCase, Utilities): def test_label(self): networkctl('label') -class NetworkdMatchTests(unittest.TestCase, Utilities): +class NetworkdMatchTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -1553,12 +1766,14 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): tear_down_common() def test_match(self): - copy_network_unit('12-dummy-mac.netdev', - '12-dummy-match-mac-01.network', - '12-dummy-match-mac-02.network', - '12-dummy-match-renamed.network', - '12-dummy-match-altname.network', - '12-dummy-altname.link') + copy_network_unit( + '12-dummy-mac.netdev', + '12-dummy-match-mac-01.network', + '12-dummy-match-mac-02.network', + '12-dummy-match-renamed.network', + '12-dummy-match-altname.network', + '12-dummy-altname.link', + ) start_networkd() self.wait_online('dummy98:routable') @@ -1593,7 +1808,11 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output) def test_match_udev_property(self): - copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network') + copy_network_unit( + '12-dummy.netdev', + '13-not-match-udev-property.network', + '14-match-udev-property.network', + ) start_networkd() self.wait_online('dummy98:routable') @@ -1601,8 +1820,8 @@ class NetworkdMatchTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property') -class WaitOnlineTests(unittest.TestCase, Utilities): +class WaitOnlineTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -1630,7 +1849,7 @@ class WaitOnlineTests(unittest.TestCase, Utilities): if network_dropin is not None: network_dropin_path = os.path.join( network_unit_dir, - '25-dhcp-client-use-dns-ipv4.network.d/test.conf' + '25-dhcp-client-use-dns-ipv4.network.d/test.conf', ) mkdir_p(os.path.dirname(network_dropin_path)) with open(network_dropin_path, 'w') as f: @@ -1639,7 +1858,7 @@ class WaitOnlineTests(unittest.TestCase, Utilities): copy_network_unit( '25-veth.netdev', '25-dhcp-client-use-dns-ipv4.network', - '25-dhcp-server.network' + '25-dhcp-server.network', ) start_networkd() self.wait_online('veth-peer:routable') @@ -1648,11 +1867,11 @@ class WaitOnlineTests(unittest.TestCase, Utilities): resolved_dropin = '/run/systemd/resolved.conf.d/global-dns.conf' mkdir_p(os.path.dirname(resolved_dropin)) with open(resolved_dropin, 'w') as f: - f.write(( - '[Resolve]\n' - f'DNS={global_dns}\n' - f'FallbackDNS={fallback_dns}\n' - )) + f.write(f''' +[Resolve] +DNS={global_dns} +FallbackDNS={fallback_dns} +''') self.addCleanup(os.remove, resolved_dropin) check_output('systemctl reload systemd-resolved') @@ -1666,61 +1885,61 @@ class WaitOnlineTests(unittest.TestCase, Utilities): if expect_timeout: # The above should have thrown an exception. - self.fail( - 'Expected systemd-networkd-wait-online to time out' - ) + self.fail('Expected systemd-networkd-wait-online to time out') except subprocess.CalledProcessError as e: if expect_timeout: self.assertRegex( e.output, - f'veth99: No link-specific DNS server is accessible', - f'Missing expected log message:\n{e.output}' + 'veth99: No link-specific DNS server is accessible', + f'Missing expected log message:\n{e.output}', ) else: - self.fail( - f'Command timed out:\n{e.output}' - ) + self.fail(f'Command timed out:\n{e.output}') finally: wait_online_env = wait_online_env_copy def test_wait_online_dns(self): - ''' test systemd-networkd-wait-online with --dns ''' + """test systemd-networkd-wait-online with --dns""" self.do_test_wait_online_dns() def test_wait_online_dns_global(self): - ''' + """ test systemd-networkd-wait-online with --dns, expect pass due to global DNS - ''' + """ # Set UseDNS=no, and allow global DNS to be used. self.do_test_wait_online_dns( global_dns='192.168.5.1', network_dropin=( - '[DHCPv4]\n' - 'UseDNS=no\n' - ) + ''' +[DHCPv4] +UseDNS=no +''' + ), ) def test_wait_online_dns_expect_timeout(self): - ''' test systemd-networkd-wait-online with --dns, and expect timeout ''' + """test systemd-networkd-wait-online with --dns, and expect timeout""" # Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used. self.do_test_wait_online_dns( expect_timeout=True, network_dropin=( - '[Network]\n' - 'DNSDefaultRoute=yes\n' - '[DHCPv4]\n' - 'UseDNS=no\n' - ) + ''' +[Network] +DNSDefaultRoute=yes +[DHCPv4] +UseDNS=no +''' + ), ) def test_wait_online_dns_expect_timeout_global(self): - ''' + """ test systemd-networkd-wait-online with --dns, and expect timeout despite global DNS - ''' + """ # Configure Domains=~., and expect timeout despite global DNS servers # being available. @@ -1728,16 +1947,17 @@ class WaitOnlineTests(unittest.TestCase, Utilities): expect_timeout=True, global_dns='192.168.5.1', network_dropin=( - '[Network]\n' - 'Domains=~.\n' - '[DHCPv4]\n' - 'UseDNS=no\n' - ) + ''' +[Network] +Domains=~. +[DHCPv4] +UseDNS=no +''' + ), ) class NetworkdNetDevTests(unittest.TestCase, Utilities): - def setUp(self): setup_common() @@ -1800,12 +2020,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick)) self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'forward_delay')) / tick)) self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'ageing_time')) / tick)) - self.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority'))) - self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier'))) - self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping'))) - self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state'))) - self.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version'))) - self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'no_linklocal_learn'))) + self.assertEqual(9, int(read_link_attr('bridge99', 'bridge', 'priority'))) + self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_querier'))) + self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'multicast_snooping'))) + self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'stp_state'))) + self.assertEqual(3, int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version'))) + self.assertEqual(1, int(read_link_attr('bridge99', 'bridge', 'no_linklocal_learn'))) output = networkctl_status('bridge99') print(output) @@ -1832,20 +2052,20 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.networkctl_check_unit('bond98', '25-bond-balanced-tlb') self.networkctl_check_unit('bond97', '25-bond-property') - self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4') - self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1') - self.check_link_attr('bond99', 'bonding', 'miimon', '1000') - self.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1') - self.check_link_attr('bond99', 'bonding', 'updelay', '2000') - self.check_link_attr('bond99', 'bonding', 'downdelay', '2000') - self.check_link_attr('bond99', 'bonding', 'resend_igmp', '4') - self.check_link_attr('bond99', 'bonding', 'min_links', '1') + self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4') + self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1') + self.check_link_attr('bond99', 'bonding', 'miimon', '1000') + self.check_link_attr('bond99', 'bonding', 'lacp_rate', 'fast 1') + self.check_link_attr('bond99', 'bonding', 'updelay', '2000') + self.check_link_attr('bond99', 'bonding', 'downdelay', '2000') + self.check_link_attr('bond99', 'bonding', 'resend_igmp', '4') + self.check_link_attr('bond99', 'bonding', 'min_links', '1') self.check_link_attr('bond99', 'bonding', 'ad_actor_sys_prio', '1218') - self.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811') - self.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55') + self.check_link_attr('bond99', 'bonding', 'ad_user_port_key', '811') + self.check_link_attr('bond99', 'bonding', 'ad_actor_system', '00:11:22:33:44:55') - self.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5') - self.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1') + self.check_link_attr('bond98', 'bonding', 'mode', 'balance-tlb 5') + self.check_link_attr('bond98', 'bonding', 'tlb_dynamic_lb', '1') output = networkctl_status('bond99') print(output) @@ -1861,7 +2081,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = networkctl_status('bond97') print(output) - self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10') + self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10') self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000') def check_vlan(self, id, flags): @@ -1900,26 +2120,36 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99') def test_vlan(self): - copy_network_unit('21-vlan.netdev', '11-dummy.netdev', - '21-vlan.network', '21-vlan-test1.network') + copy_network_unit( + '21-vlan.netdev', + '11-dummy.netdev', + '21-vlan.network', + '21-vlan-test1.network', + ) start_networkd() self.check_vlan(id=99, flags=True) # Test for reloading .netdev file. See issue #34907. - with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8' + ) as f: f.write('[VLAN]\nId=42\n') # VLAN ID cannot be changed, so we need to remove the existing netdev. - check_output("ip link del vlan99") + check_output('ip link del vlan99') networkctl_reload() self.check_vlan(id=42, flags=True) - with open(os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8') as f: - f.write('[VLAN]\n' - 'GVRP=no\n' - 'MVRP=no\n' - 'LooseBinding=no\n' - 'ReorderHeader=no\n') + with open( + os.path.join(network_unit_dir, '21-vlan.netdev.d/override.conf'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +[VLAN] +GVRP=no +MVRP=no +LooseBinding=no +ReorderHeader=no +''') # flags can be changed, hence it is not necessary to remove the existing netdev. networkctl_reload() @@ -1929,8 +2159,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): # For issue #24377 (https://github.com/systemd/systemd/issues/24377), # which is fixed by b05e52000b4eee764b383cc3031da0a3739e996e (PR#24020). - copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network', - '21-vlan-on-bond.netdev', '21-vlan-on-bond.network') + copy_network_unit( + '21-bond-802.3ad.netdev', + '21-bond-802.3ad.network', + '21-vlan-on-bond.netdev', + '21-vlan-on-bond.network', + ) start_networkd() self.wait_online('bond99:off') self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10) @@ -1953,14 +2187,22 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(f'### test_macvtap(mode={mode})') with self.subTest(mode=mode): - copy_network_unit('21-macvtap.netdev', '26-netdev-link-local-addressing-yes.network', - '11-dummy.netdev', '25-macvtap.network') - with open(os.path.join(network_unit_dir, '21-macvtap.netdev'), mode='a', encoding='utf-8') as f: + copy_network_unit( + '21-macvtap.netdev', + '26-netdev-link-local-addressing-yes.network', + '11-dummy.netdev', + '25-macvtap.network', + ) + with open( + os.path.join(network_unit_dir, '21-macvtap.netdev'), mode='a', encoding='utf-8' + ) as f: f.write('[MACVTAP]\nMode=' + mode) start_networkd() - self.wait_online('macvtap99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded') + self.wait_online( + 'macvtap99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded', + ) self.networkctl_check_unit('macvtap99', '21-macvtap', '26-netdev-link-local-addressing-yes') self.networkctl_check_unit('test1', '11-dummy', '25-macvtap') @@ -1970,8 +2212,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): touch_network_unit('21-macvtap.netdev') networkctl_reload() - self.wait_online('macvtap99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded') + self.wait_online( + 'macvtap99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded', + ) @expectedFailureIfModuleIsNotAvailable('macvlan') def test_macvlan(self): @@ -1984,14 +2228,22 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(f'### test_macvlan(mode={mode})') with self.subTest(mode=mode): - copy_network_unit('21-macvlan.netdev', '26-netdev-link-local-addressing-yes.network', - '11-dummy.netdev', '25-macvlan.network') - with open(os.path.join(network_unit_dir, '21-macvlan.netdev'), mode='a', encoding='utf-8') as f: + copy_network_unit( + '21-macvlan.netdev', + '26-netdev-link-local-addressing-yes.network', + '11-dummy.netdev', + '25-macvlan.network', + ) + with open( + os.path.join(network_unit_dir, '21-macvlan.netdev'), mode='a', encoding='utf-8' + ) as f: f.write('[MACVLAN]\nMode=' + mode) start_networkd() - self.wait_online('macvlan99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded') + self.wait_online( + 'macvlan99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded', + ) self.networkctl_check_unit('macvlan99', '21-macvlan', '26-netdev-link-local-addressing-yes') self.networkctl_check_unit('test1', '11-dummy', '25-macvlan') @@ -2007,9 +2259,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): remove_link('test1') time.sleep(1) - check_output("ip link add test1 type dummy") - self.wait_online('macvlan99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded') + check_output('ip link add test1 type dummy') + self.wait_online( + 'macvlan99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded', + ) output = check_output('ip -d link show test1') print(output) @@ -2020,13 +2274,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn(' mtu 2000 ', output) self.assertIn(f' macvlan mode {mode} ', output) self.assertIn(' bcqueuelen 1234 ', output) - if ' bclim ' in output: # This is new in kernel and iproute2 v6.4 + if ' bclim ' in output: # This is new in kernel and iproute2 v6.4 self.assertIn(' bclim 2147483647 ', output) touch_network_unit('21-macvlan.netdev') networkctl_reload() - self.wait_online('macvlan99:degraded', - 'test1:carrier' if mode == 'passthru' else 'test1:degraded') + self.wait_online( + 'macvlan99:degraded', + 'test1:carrier' if mode == 'passthru' else 'test1:degraded', + ) @expectedFailureIfModuleIsNotAvailable('ipvlan') def test_ipvlan(self): @@ -2039,9 +2295,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(f'### test_ipvlan(mode={mode}, flag={flag})') with self.subTest(mode=mode, flag=flag): - copy_network_unit('25-ipvlan.netdev', '26-netdev-link-local-addressing-yes.network', - '11-dummy.netdev', '25-ipvlan.network') - with open(os.path.join(network_unit_dir, '25-ipvlan.netdev'), mode='a', encoding='utf-8') as f: + copy_network_unit( + '25-ipvlan.netdev', + '26-netdev-link-local-addressing-yes.network', + '11-dummy.netdev', + '25-ipvlan.network', + ) + with open( + os.path.join(network_unit_dir, '25-ipvlan.netdev'), mode='a', encoding='utf-8' + ) as f: f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag) start_networkd() @@ -2068,9 +2330,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(f'### test_hsr(proto={proto}, supervision={supervision})') with self.subTest(proto=proto, supervision=supervision): - copy_network_unit('25-hsr.netdev', '25-hsr.network', - '11-dummy.netdev', '11-dummy.network', - '12-dummy.netdev', '12-dummy-no-address.network') + copy_network_unit( + '25-hsr.netdev', + '25-hsr.network', + '11-dummy.netdev', + '11-dummy.network', + '12-dummy.netdev', + '12-dummy-no-address.network', + ) with open(os.path.join(network_unit_dir, '25-hsr.netdev'), mode='a', encoding='utf-8') as f: f.write('Protocol=' + proto + '\nSupervision=' + str(supervision)) @@ -2101,9 +2368,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(f'### test_ipvtap(mode={mode}, flag={flag})') with self.subTest(mode=mode, flag=flag): - copy_network_unit('25-ipvtap.netdev', '26-netdev-link-local-addressing-yes.network', - '11-dummy.netdev', '25-ipvtap.network') - with open(os.path.join(network_unit_dir, '25-ipvtap.netdev'), mode='a', encoding='utf-8') as f: + copy_network_unit( + '25-ipvtap.netdev', + '26-netdev-link-local-addressing-yes.network', + '11-dummy.netdev', + '25-ipvtap.network', + ) + with open( + os.path.join(network_unit_dir, '25-ipvtap.netdev'), mode='a', encoding='utf-8' + ) as f: f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag) start_networkd() @@ -2120,11 +2393,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.wait_online('ipvtap99:degraded', 'test1:degraded') def test_veth(self): - copy_network_unit('25-veth.netdev', '26-netdev-link-local-addressing-yes.network', - '25-veth-mtu.netdev') + copy_network_unit( + '25-veth.netdev', + '26-netdev-link-local-addressing-yes.network', + '25-veth-mtu.netdev', + ) start_networkd() - self.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded') + self.wait_online( + 'veth99:degraded', + 'veth-peer:degraded', + 'veth-mtu:degraded', + 'veth-mtu-peer:degraded', + ) self.networkctl_check_unit('veth99', '25-veth', '26-netdev-link-local-addressing-yes') self.networkctl_check_unit('veth-peer', '25-veth', '26-netdev-link-local-addressing-yes') self.networkctl_check_unit('veth-mtu', '25-veth-mtu', '26-netdev-link-local-addressing-yes') @@ -2149,13 +2430,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): touch_network_unit( '25-veth.netdev', '26-netdev-link-local-addressing-yes.network', - '25-veth-mtu.netdev') + '25-veth-mtu.netdev', + ) networkctl_reload() self.wait_online( 'veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', - 'veth-mtu-peer:degraded') + 'veth-mtu-peer:degraded', + ) def check_tuntap(self, attached): pid = networkd_pid() @@ -2163,12 +2446,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d -oneline tuntap show') print(output) + # fmt: off self.assertRegex(output, r'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:') self.assertRegex(output, r'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:') + # fmt: on if attached: - self.assertRegex(output, fr'testtap99: .*{name}\({pid}\)') - self.assertRegex(output, fr'testtun99: .*{name}\({pid}\)') + self.assertRegex(output, rf'testtap99: .*{name}\({pid}\)') + self.assertRegex(output, rf'testtun99: .*{name}\({pid}\)') self.assertRegex(output, r'testtap99: .*systemd\(1\)') self.assertRegex(output, r'testtun99: .*systemd\(1\)') @@ -2252,8 +2537,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('vcan') def test_vcan(self): - copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network', - '25-vcan98.netdev', '25-vcan98.network') + copy_network_unit( + '25-vcan.netdev', + '26-netdev-link-local-addressing-yes.network', + '25-vcan98.netdev', + '25-vcan98.network', + ) start_networkd() self.wait_online('vcan99:carrier', 'vcan98:carrier') @@ -2275,7 +2564,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vcan.netdev', '26-netdev-link-local-addressing-yes.network', '25-vcan98.netdev', - '25-vcan98.network') + '25-vcan98.network', + ) networkctl_reload() self.wait_online('vcan99:carrier', 'vcan98:carrier') @@ -2296,14 +2586,30 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('wireguard') def test_wireguard(self): - copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint') - copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk') - copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer') + copy_credential( + '25-wireguard-endpoint-peer0-cred.txt', + 'network.wireguard.peer0.endpoint', + ) + copy_credential( + '25-wireguard-preshared-key-peer2-cred.txt', + 'network.wireguard.peer2.psk', + ) + copy_credential( + '25-wireguard-no-peer-private-key-cred.txt', + 'network.wireguard.private.25-wireguard-no-peer', + ) - copy_network_unit('25-wireguard.netdev', '25-wireguard.network', - '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network', - '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt', - '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network') + copy_network_unit( + '25-wireguard.netdev', + '25-wireguard.network', + '25-wireguard-23-peers.netdev', + '25-wireguard-23-peers.network', + '25-wireguard-public-key.txt', + '25-wireguard-preshared-key.txt', + '25-wireguard-private-key.txt', + '25-wireguard-no-peer.netdev', + '25-wireguard-no-peer.network', + ) start_networkd() self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier') self.networkctl_check_unit('wg99', '25-wireguard', '25-wireguard') @@ -2397,10 +2703,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('wg show wg99 private-key') self.assertEqual(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=') output = check_output('wg show wg99 allowed-ips') + # fmt: off self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t192.168.124.3/32', output) self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\t192.168.124.2/32', output) self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128', output) self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48', output) + # fmt: on output = check_output('wg show wg99 persistent-keepalive') self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\toff', output) self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\toff', output) @@ -2412,10 +2720,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\t(none)', output) self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820', output) output = check_output('wg show wg99 preshared-keys') + # fmt: off self.assertIn('9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=\t6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=', output) self.assertIn('TTiCUpCxS7zDn/ax4p5W6Evg41r8hOrnWQw2Sq6Nh10=\tit7nd33chCT/tKT2ZZWfYyp43Zs+6oif72hexnSNMqA=', output) self.assertIn('lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tcPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=', output) self.assertIn('RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\tIIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=', output) + # fmt: on output = check_output('wg show wg98 private-key') self.assertEqual(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=') @@ -2426,10 +2736,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertEqual(output, '0x4d3') touch_network_unit( - '25-wireguard.netdev', '25-wireguard.network', - '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network', - '25-wireguard-public-key.txt', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt', - '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network') + '25-wireguard.netdev', + '25-wireguard.network', + '25-wireguard-23-peers.netdev', + '25-wireguard-23-peers.network', + '25-wireguard-public-key.txt', + '25-wireguard-preshared-key.txt', + '25-wireguard-private-key.txt', + '25-wireguard-no-peer.netdev', + '25-wireguard-no-peer.network', + ) networkctl_reload() self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier') @@ -2452,24 +2768,39 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.wait_online('geneve99:degraded') def _test_ipip_tunnel(self, mode): - copy_network_unit('12-dummy.netdev', '25-ipip.network', - '25-ipip-tunnel.netdev', '25-tunnel.network', - '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network', - '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', - '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-ipip.network', + '25-ipip-tunnel.netdev', + '25-tunnel.network', + '25-ipip-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + '25-ipip-tunnel-remote-any.netdev', + '25-tunnel-remote-any.network', + '25-ipip-tunnel-any-any.netdev', + '25-tunnel-any-any.network', + ) if mode: - for netdev in ['25-ipip-tunnel.netdev', - '25-ipip-tunnel-local-any.netdev', - '25-ipip-tunnel-remote-any.netdev', - '25-ipip-tunnel-any-any.netdev']: + for netdev in [ + '25-ipip-tunnel.netdev', + '25-ipip-tunnel-local-any.netdev', + '25-ipip-tunnel-remote-any.netdev', + '25-ipip-tunnel-any-any.netdev', + ]: with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f: f.write(f'[Tunnel]\nMode={mode}\n') else: - mode = 'ipip' # kernel default + mode = 'ipip' # kernel default start_networkd() - self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded') + self.wait_online( + 'ipiptun99:routable', + 'ipiptun98:routable', + 'ipiptun97:routable', + 'ipiptun96:routable', + 'dummy98:degraded', + ) output = check_output('ip -d link show ipiptun99') print(output) @@ -2488,14 +2819,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ipip-tunnel.netdev', '25-ipip-tunnel-local-any.netdev', '25-ipip-tunnel-remote-any.netdev', - '25-ipip-tunnel-any-any.netdev') + '25-ipip-tunnel-any-any.netdev', + ) networkctl_reload() self.wait_online( 'ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_ipip_tunnel(self): first = True @@ -2510,13 +2843,26 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self._test_ipip_tunnel(mode) def test_gre_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-gretun.network', - '25-gre-tunnel.netdev', '25-tunnel.network', - '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network', - '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', - '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-gretun.network', + '25-gre-tunnel.netdev', + '25-tunnel.network', + '25-gre-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + '25-gre-tunnel-remote-any.netdev', + '25-tunnel-remote-any.network', + '25-gre-tunnel-any-any.netdev', + '25-tunnel-any-any.network', + ) start_networkd() - self.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded') + self.wait_online( + 'gretun99:routable', + 'gretun98:routable', + 'gretun97:routable', + 'gretun96:routable', + 'dummy98:degraded', + ) self.networkctl_check_unit('gretun99', '25-gre-tunnel', '25-tunnel') self.networkctl_check_unit('gretun98', '25-gre-tunnel-local-any', '25-tunnel-local-any') self.networkctl_check_unit('gretun97', '25-gre-tunnel-remote-any', '25-tunnel-remote-any') @@ -2556,21 +2902,30 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-gre-tunnel.netdev', '25-gre-tunnel-local-any.netdev', '25-gre-tunnel-remote-any.netdev', - '25-gre-tunnel-any-any.netdev') + '25-gre-tunnel-any-any.netdev', + ) networkctl_reload() self.wait_online( 'gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_ip6gre_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-ip6gretun.network', - '25-ip6gre-tunnel.netdev', '25-tunnel.network', - '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network', - '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', - '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-ip6gretun.network', + '25-ip6gre-tunnel.netdev', + '25-tunnel.network', + '25-ip6gre-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + '25-ip6gre-tunnel-remote-any.netdev', + '25-tunnel-remote-any.network', + '25-ip6gre-tunnel-any-any.netdev', + '25-tunnel-any-any.network', + ) start_networkd() self.wait_online( @@ -2583,7 +2938,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show ip6gretun99') print(output) - self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') + self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') # fmt: skip output = check_output('ip -d link show ip6gretun98') print(output) self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98') @@ -2598,7 +2953,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6gre-tunnel.netdev', '25-ip6gre-tunnel-local-any.netdev', '25-ip6gre-tunnel-remote-any.netdev', - '25-ip6gre-tunnel-any-any.netdev') + '25-ip6gre-tunnel-any-any.netdev', + ) networkctl_reload() self.wait_online( 'ip6gretun99:routable', @@ -2609,9 +2965,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): ) def test_gretap_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-gretap.network', - '25-gretap-tunnel.netdev', '25-tunnel.network', - '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-gretap.network', + '25-gretap-tunnel.netdev', + '25-tunnel.network', + '25-gretap-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + ) start_networkd() self.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded') self.networkctl_check_unit('gretap99', '25-gretap-tunnel', '25-tunnel') @@ -2637,17 +2998,24 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): touch_network_unit( '25-gretap-tunnel.netdev', - '25-gretap-tunnel-local-any.netdev') + '25-gretap-tunnel-local-any.netdev', + ) networkctl_reload() self.wait_online( 'gretap99:routable', 'gretap98:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_ip6gretap_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-ip6gretap.network', - '25-ip6gretap-tunnel.netdev', '25-tunnel.network', - '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-ip6gretap.network', + '25-ip6gretap-tunnel.netdev', + '25-tunnel.network', + '25-ip6gretap-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + ) start_networkd() self.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded') self.networkctl_check_unit('ip6gretap99', '25-ip6gretap-tunnel', '25-tunnel') @@ -2656,28 +3024,43 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show ip6gretap99') print(output) - self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') + self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') # fmt: skip output = check_output('ip -d link show ip6gretap98') print(output) self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98') touch_network_unit( '25-ip6gretap-tunnel.netdev', - '25-ip6gretap-tunnel-local-any.netdev') + '25-ip6gretap-tunnel-local-any.netdev', + ) networkctl_reload() self.wait_online( 'ip6gretap99:routable', 'ip6gretap98:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_vti_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-vti.network', - '25-vti-tunnel.netdev', '25-tunnel.network', - '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network', - '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', - '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-vti.network', + '25-vti-tunnel.netdev', + '25-tunnel.network', + '25-vti-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + '25-vti-tunnel-remote-any.netdev', + '25-tunnel-remote-any.network', + '25-vti-tunnel-any-any.netdev', + '25-tunnel-any-any.network', + ) start_networkd() - self.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded') + self.wait_online( + 'vtitun99:routable', + 'vtitun98:routable', + 'vtitun97:routable', + 'vtitun96:routable', + 'dummy98:degraded', + ) self.networkctl_check_unit('vtitun99', '25-vti-tunnel', '25-tunnel') self.networkctl_check_unit('vtitun98', '25-vti-tunnel-local-any', '25-tunnel-local-any') self.networkctl_check_unit('vtitun97', '25-vti-tunnel-remote-any', '25-tunnel-remote-any') @@ -2701,22 +3084,35 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vti-tunnel.netdev', '25-vti-tunnel-local-any.netdev', '25-vti-tunnel-remote-any.netdev', - '25-vti-tunnel-any-any.netdev') + '25-vti-tunnel-any-any.netdev', + ) networkctl_reload() self.wait_online( 'vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_vti6_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-vti6.network', - '25-vti6-tunnel.netdev', '25-tunnel.network', - '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network', - '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-vti6.network', + '25-vti6-tunnel.netdev', + '25-tunnel.network', + '25-vti6-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + '25-vti6-tunnel-remote-any.netdev', + '25-tunnel-remote-any.network', + ) start_networkd() - self.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded') + self.wait_online( + 'vti6tun99:routable', + 'vti6tun98:routable', + 'vti6tun97:routable', + 'dummy98:degraded', + ) self.networkctl_check_unit('vti6tun99', '25-vti6-tunnel', '25-tunnel') self.networkctl_check_unit('vti6tun98', '25-vti6-tunnel-local-any', '25-tunnel-local-any') self.networkctl_check_unit('vti6tun97', '25-vti6-tunnel-remote-any', '25-tunnel-remote-any') @@ -2724,7 +3120,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show vti6tun99') print(output) - self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') + self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') # fmt: skip output = check_output('ip -d link show vti6tun98') print(output) self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98') @@ -2735,50 +3131,103 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): touch_network_unit( '25-vti6-tunnel.netdev', '25-vti6-tunnel-local-any.netdev', - '25-vti6-tunnel-remote-any.netdev') + '25-vti6-tunnel-remote-any.netdev', + ) networkctl_reload() self.wait_online( 'vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def _test_ip6tnl_tunnel(self, mode): - copy_network_unit('12-dummy.netdev', '25-ip6tnl.network', - '25-ip6tnl-tunnel.netdev', '25-tunnel.network', - '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network', - '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', - '25-veth.netdev', '25-ip6tnl-slaac.network', '25-ipv6-prefix.network', - '25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network', - '25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network') + copy_network_unit( + '12-dummy.netdev', + '25-ip6tnl.network', + '25-ip6tnl-tunnel.netdev', + '25-tunnel.network', + '25-ip6tnl-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + '25-ip6tnl-tunnel-remote-any.netdev', + '25-tunnel-remote-any.network', + '25-veth.netdev', + '25-ip6tnl-slaac.network', + '25-ipv6-prefix.network', + '25-ip6tnl-tunnel-local-slaac.netdev', + '25-ip6tnl-tunnel-local-slaac.network', + '25-ip6tnl-tunnel-external.netdev', + '26-netdev-link-local-addressing-yes.network', + ) if mode: - for netdev in ['25-ip6tnl-tunnel.netdev', - '25-ip6tnl-tunnel-local-any.netdev', - '25-ip6tnl-tunnel-remote-any.netdev', - '25-ip6tnl-tunnel-local-slaac.netdev', - '25-ip6tnl-tunnel-external.netdev']: + for netdev in [ + '25-ip6tnl-tunnel.netdev', + '25-ip6tnl-tunnel-local-any.netdev', + '25-ip6tnl-tunnel-remote-any.netdev', + '25-ip6tnl-tunnel-local-slaac.netdev', + '25-ip6tnl-tunnel-external.netdev', + ]: with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f: f.write(f'[Tunnel]\nMode={mode}\n') else: - mode = 'any' # kernel default + mode = 'any' # kernel default start_networkd() - self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', - 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded', - 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded') - self.networkctl_check_unit('ip6tnl99', '25-ip6tnl-tunnel', '25-tunnel') - self.networkctl_check_unit('ip6tnl98', '25-ip6tnl-tunnel-local-any', '25-tunnel-local-any') - self.networkctl_check_unit('ip6tnl97', '25-ip6tnl-tunnel-remote-any', '25-tunnel-remote-any') - self.networkctl_check_unit('ip6tnl-slaac', '25-ip6tnl-tunnel-local-slaac', '25-ip6tnl-tunnel-local-slaac') - self.networkctl_check_unit('ip6tnl-external', '25-ip6tnl-tunnel-external', '26-netdev-link-local-addressing-yes') - self.networkctl_check_unit('dummy98', '12-dummy', '25-ip6tnl') - self.networkctl_check_unit('veth99', '25-veth', '25-ip6tnl-slaac') - self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix') + self.wait_online( + 'ip6tnl99:routable', + 'ip6tnl98:routable', + 'ip6tnl97:routable', + 'ip6tnl-slaac:degraded', + 'ip6tnl-external:degraded', + 'dummy98:degraded', + 'veth99:routable', + 'veth-peer:degraded', + ) + self.networkctl_check_unit( + 'ip6tnl99', + '25-ip6tnl-tunnel', + '25-tunnel', + ) + self.networkctl_check_unit( + 'ip6tnl98', + '25-ip6tnl-tunnel-local-any', + '25-tunnel-local-any', + ) + self.networkctl_check_unit( + 'ip6tnl97', + '25-ip6tnl-tunnel-remote-any', + '25-tunnel-remote-any', + ) + self.networkctl_check_unit( + 'ip6tnl-slaac', + '25-ip6tnl-tunnel-local-slaac', + '25-ip6tnl-tunnel-local-slaac', + ) + self.networkctl_check_unit( + 'ip6tnl-external', + '25-ip6tnl-tunnel-external', + '26-netdev-link-local-addressing-yes', + ) + self.networkctl_check_unit( + 'dummy98', + '12-dummy', + '25-ip6tnl', + ) + self.networkctl_check_unit( + 'veth99', + '25-veth', + '25-ip6tnl-slaac', + ) + self.networkctl_check_unit( + 'veth-peer', + '25-veth', + '25-ipv6-prefix', + ) output = check_output('ip -d link show ip6tnl99') print(output) - self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output) + self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output) # fmt: skip output = check_output('ip -d link show ip6tnl98') print(output) self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local any dev dummy98', output) @@ -2791,7 +3240,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('ip6tnl external ', output) output = check_output('ip -d link show ip6tnl-slaac') print(output) - self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output) + self.assertIn(f'ip6tnl {mode} remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output) # fmt: skip output = check_output('ip -6 address show veth99') print(output) @@ -2806,7 +3255,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-ip6tnl-tunnel-local-any.netdev', '25-ip6tnl-tunnel-remote-any.netdev', '25-ip6tnl-tunnel-local-slaac.netdev', - '25-ip6tnl-tunnel-external.netdev') + '25-ip6tnl-tunnel-external.netdev', + ) networkctl_reload() self.wait_online( 'ip6tnl99:routable', @@ -2816,7 +3266,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'ip6tnl-external:degraded', 'dummy98:degraded', 'veth99:routable', - 'veth-peer:degraded') + 'veth-peer:degraded', + ) def test_ip6tnl_tunnel(self): first = True @@ -2831,24 +3282,39 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self._test_ip6tnl_tunnel(mode) def _test_sit_tunnel(self, mode): - copy_network_unit('12-dummy.netdev', '25-sit.network', - '25-sit-tunnel.netdev', '25-tunnel.network', - '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network', - '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network', - '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-sit.network', + '25-sit-tunnel.netdev', + '25-tunnel.network', + '25-sit-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + '25-sit-tunnel-remote-any.netdev', + '25-tunnel-remote-any.network', + '25-sit-tunnel-any-any.netdev', + '25-tunnel-any-any.network', + ) if mode: - for netdev in ['25-sit-tunnel.netdev', - '25-sit-tunnel-local-any.netdev', - '25-sit-tunnel-remote-any.netdev', - '25-sit-tunnel-any-any.netdev']: + for netdev in [ + '25-sit-tunnel.netdev', + '25-sit-tunnel-local-any.netdev', + '25-sit-tunnel-remote-any.netdev', + '25-sit-tunnel-any-any.netdev', + ]: with open(os.path.join(network_unit_dir, netdev), mode='a', encoding='utf-8') as f: f.write(f'[Tunnel]\nMode={mode}\n') else: - mode = 'ip6ip' # kernel default + mode = 'ip6ip' # kernel default start_networkd() - self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded') + self.wait_online( + 'sittun99:routable', + 'sittun98:routable', + 'sittun97:routable', + 'sittun96:routable', + 'dummy98:degraded', + ) self.networkctl_check_unit('sittun99', '25-sit-tunnel', '25-tunnel') self.networkctl_check_unit('sittun98', '25-sit-tunnel-local-any', '25-tunnel-local-any') self.networkctl_check_unit('sittun97', '25-sit-tunnel-remote-any', '25-tunnel-remote-any') @@ -2872,14 +3338,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-sit-tunnel.netdev', '25-sit-tunnel-local-any.netdev', '25-sit-tunnel-remote-any.netdev', - '25-sit-tunnel-any-any.netdev') + '25-sit-tunnel-any-any.netdev', + ) networkctl_reload() self.wait_online( 'sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_sit_tunnel(self): first = True @@ -2894,8 +3362,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self._test_sit_tunnel(mode) def test_isatap_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-isatap.network', - '25-isatap-tunnel.netdev', '25-tunnel.network') + copy_network_unit( + '12-dummy.netdev', + '25-isatap.network', + '25-isatap-tunnel.netdev', + '25-tunnel.network', + ) start_networkd() self.wait_online('isataptun99:routable', 'dummy98:degraded') self.networkctl_check_unit('isataptun99', '25-isatap-tunnel', '25-tunnel') @@ -2903,15 +3375,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('ip -d link show isataptun99') print(output) - self.assertRegex(output, "isatap ") + self.assertRegex(output, 'isatap ') touch_network_unit('25-isatap-tunnel.netdev') networkctl_reload() self.wait_online('isataptun99:routable', 'dummy98:degraded') def test_6rd_tunnel(self): - copy_network_unit('12-dummy.netdev', '25-6rd.network', - '25-6rd-tunnel.netdev', '25-tunnel.network') + copy_network_unit( + '12-dummy.netdev', + '25-6rd.network', + '25-6rd-tunnel.netdev', + '25-tunnel.network', + ) start_networkd() self.wait_online('sittun99:routable', 'dummy98:degraded') self.networkctl_check_unit('sittun99', '25-6rd-tunnel', '25-tunnel') @@ -2926,9 +3402,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.wait_online('sittun99:routable', 'dummy98:degraded') def test_erspan_tunnel_v0(self): - copy_network_unit('12-dummy.netdev', '25-erspan.network', - '25-erspan0-tunnel.netdev', '25-tunnel.network', - '25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-erspan.network', + '25-erspan0-tunnel.netdev', + '25-tunnel.network', + '25-erspan0-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + ) start_networkd() self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded') self.networkctl_check_unit('erspan99', '25-erspan0-tunnel', '25-tunnel') @@ -2958,17 +3439,24 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): touch_network_unit( '25-erspan0-tunnel.netdev', - '25-erspan0-tunnel-local-any.netdev') + '25-erspan0-tunnel-local-any.netdev', + ) networkctl_reload() self.wait_online( 'erspan99:routable', 'erspan98:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_erspan_tunnel_v1(self): - copy_network_unit('12-dummy.netdev', '25-erspan.network', - '25-erspan1-tunnel.netdev', '25-tunnel.network', - '25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-erspan.network', + '25-erspan1-tunnel.netdev', + '25-tunnel.network', + '25-erspan1-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + ) start_networkd() self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded') self.networkctl_check_unit('erspan99', '25-erspan1-tunnel', '25-tunnel') @@ -3000,17 +3488,24 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): touch_network_unit( '25-erspan1-tunnel.netdev', - '25-erspan1-tunnel-local-any.netdev') + '25-erspan1-tunnel-local-any.netdev', + ) networkctl_reload() self.wait_online( 'erspan99:routable', 'erspan98:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_erspan_tunnel_v2(self): - copy_network_unit('12-dummy.netdev', '25-erspan.network', - '25-erspan2-tunnel.netdev', '25-tunnel.network', - '25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network') + copy_network_unit( + '12-dummy.netdev', + '25-erspan.network', + '25-erspan2-tunnel.netdev', + '25-tunnel.network', + '25-erspan2-tunnel-local-any.netdev', + '25-tunnel-local-any.network', + ) start_networkd() self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded') self.networkctl_check_unit('erspan99', '25-erspan2-tunnel', '25-tunnel') @@ -3042,32 +3537,52 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): touch_network_unit( '25-erspan2-tunnel.netdev', - '25-erspan2-tunnel-local-any.netdev') + '25-erspan2-tunnel-local-any.netdev', + ) networkctl_reload() self.wait_online( 'erspan99:routable', 'erspan98:routable', - 'dummy98:degraded') + 'dummy98:degraded', + ) def test_tunnel_independent(self): - copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network') + copy_network_unit( + '25-ipip-tunnel-independent.netdev', + '26-netdev-link-local-addressing-yes.network', + ) start_networkd() self.wait_online('ipiptun99:carrier') - self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent', '26-netdev-link-local-addressing-yes') + self.networkctl_check_unit( + 'ipiptun99', + '25-ipip-tunnel-independent', + '26-netdev-link-local-addressing-yes', + ) def test_tunnel_independent_loopback(self): - copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network') + copy_network_unit( + '25-ipip-tunnel-independent-loopback.netdev', + '26-netdev-link-local-addressing-yes.network', + ) start_networkd() self.wait_online('ipiptun99:carrier') - self.networkctl_check_unit('ipiptun99', '25-ipip-tunnel-independent-loopback', '26-netdev-link-local-addressing-yes') + self.networkctl_check_unit( + 'ipiptun99', + '25-ipip-tunnel-independent-loopback', + '26-netdev-link-local-addressing-yes', + ) @expectedFailureIfModuleIsNotAvailable('xfrm_interface') def test_xfrm(self): - copy_network_unit('12-dummy.netdev', '25-xfrm.network', - '25-xfrm.netdev', '25-xfrm-independent.netdev', - '26-netdev-link-local-addressing-yes.network') + copy_network_unit( + '12-dummy.netdev', + '25-xfrm.network', + '25-xfrm.netdev', + '25-xfrm-independent.netdev', + '26-netdev-link-local-addressing-yes.network', + ) start_networkd() self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded') @@ -3091,12 +3606,23 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('fou') def test_fou(self): - copy_network_unit('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev', - '25-fou-ipip.netdev', '25-fou-sit.netdev', - '25-fou-gre.netdev', '25-fou-gretap.netdev') + copy_network_unit( + '25-fou-ipproto-ipip.netdev', + '25-fou-ipproto-gre.netdev', + '25-fou-ipip.netdev', + '25-fou-sit.netdev', + '25-fou-gre.netdev', + '25-fou-gretap.netdev', + ) start_networkd() - self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged') + self.wait_online( + 'ipiptun96:off', + 'sittun96:off', + 'gretun96:off', + 'gretap96:off', + setup_state='unmanaged', + ) self.networkctl_check_unit('ipiptun96', '25-fou-ipip') self.networkctl_check_unit('sittun96', '25-fou-sit') self.networkctl_check_unit('gretun96', '25-fou-gre') @@ -3121,25 +3647,52 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556') touch_network_unit( - '25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev', - '25-fou-ipip.netdev', '25-fou-sit.netdev', - '25-fou-gre.netdev', '25-fou-gretap.netdev') + '25-fou-ipproto-ipip.netdev', + '25-fou-ipproto-gre.netdev', + '25-fou-ipip.netdev', + '25-fou-sit.netdev', + '25-fou-gre.netdev', + '25-fou-gretap.netdev', + ) networkctl_reload() - self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged') + self.wait_online( + 'ipiptun96:off', + 'sittun96:off', + 'gretun96:off', + 'gretap96:off', + setup_state='unmanaged', + ) def test_vxlan(self): - copy_network_unit('11-dummy.netdev', '25-vxlan-test1.network', - '25-vxlan.netdev', '25-vxlan.network', - '25-vxlan-ipv6.netdev', '25-vxlan-ipv6.network', - '25-vxlan-independent.netdev', '26-netdev-link-local-addressing-yes.network', - '25-veth.netdev', '25-vxlan-veth99.network', '25-ipv6-prefix.network', - '25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network', - '25-vxlan-external.netdev', '25-vxlan-external.network') + copy_network_unit( + '11-dummy.netdev', + '25-vxlan-test1.network', + '25-vxlan.netdev', + '25-vxlan.network', + '25-vxlan-ipv6.netdev', + '25-vxlan-ipv6.network', + '25-vxlan-independent.netdev', + '26-netdev-link-local-addressing-yes.network', + '25-veth.netdev', + '25-vxlan-veth99.network', + '25-ipv6-prefix.network', + '25-vxlan-local-slaac.netdev', + '25-vxlan-local-slaac.network', + '25-vxlan-external.netdev', + '25-vxlan-external.network', + ) start_networkd() - self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded', - 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded', - 'vxlan-external:degraded') + self.wait_online( + 'test1:degraded', + 'veth99:routable', + 'veth-peer:degraded', + 'vxlan99:degraded', + 'vxlan98:degraded', + 'vxlan97:degraded', + 'vxlan-slaac:degraded', + 'vxlan-external:degraded', + ) self.networkctl_check_unit('test1', '11-dummy', '25-vxlan-test1') self.networkctl_check_unit('veth99', '25-veth', '25-vxlan-veth99') self.networkctl_check_unit('veth-peer', '25-veth', '25-ipv6-prefix') @@ -3198,10 +3751,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertIn('external', output) self.assertIn('vnifilter', output) - @unittest.skipUnless(compare_kernel_version("6"), reason="Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315") + @unittest.skipUnless( + compare_kernel_version('6'), + reason='Causes kernel panic on unpatched kernels: https://bugzilla.kernel.org/show_bug.cgi?id=208315', + ) def test_macsec(self): - copy_network_unit('25-macsec.netdev', '25-macsec.network', '25-macsec.key', - '26-macsec.network', '12-dummy.netdev') + copy_network_unit( + '25-macsec.netdev', + '25-macsec.network', + '25-macsec.key', + '26-macsec.network', + '12-dummy.netdev', + ) start_networkd() self.wait_online('dummy98:degraded', 'macsec99:routable') @@ -3256,7 +3817,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): networkctl_reload() self.wait_online('ifb99:degraded') - @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test") + @unittest.skipUnless(os.cpu_count() >= 2, reason='CPU count should be >= 2 to pass this test') def test_rps_cpu_1(self): copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link') start_networkd() @@ -3268,7 +3829,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertEqual(int(output.replace(',', ''), base=16), 2) - @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test") + @unittest.skipUnless(os.cpu_count() >= 2, reason='CPU count should be >= 2 to pass this test') def test_rps_cpu_0_1(self): copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link') start_networkd() @@ -3280,7 +3841,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): print(output) self.assertEqual(int(output.replace(',', ''), base=16), 3) - @unittest.skipUnless(os.cpu_count() >= 4, reason="CPU count should be >= 4 to pass this test") + @unittest.skipUnless(os.cpu_count() >= 4, reason='CPU count should be >= 4 to pass this test') def test_rps_cpu_multi(self): copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link') start_networkd() @@ -3316,7 +3877,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all') output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') print(output) - self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + self.assertEqual(f'{int(output.replace(",", ""), base=16):x}', f'{(1 << cpu_count) - 1:x}') remove_network_unit('25-rps-cpu-all.link') # disable @@ -3334,7 +3895,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-all') output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') print(output) - self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + self.assertEqual(f'{int(output.replace(",", ""), base=16):x}', f'{(1 << cpu_count) - 1:x}') remove_network_unit('25-rps-cpu-all.link') # empty -> unchanged @@ -3343,7 +3904,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '24-rps-cpu-empty') output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') print(output) - self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + self.assertEqual(f'{int(output.replace(",", ""), base=16):x}', f'{(1 << cpu_count) - 1:x}') remove_network_unit('24-rps-cpu-empty.link') # 0, then empty -> unchanged @@ -3352,7 +3913,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.networkctl_check_unit('dummy98', '12-dummy', '12-dummy', '25-rps-cpu-0-empty') output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus') print(output) - self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}') + self.assertEqual(f'{int(output.replace(",", ""), base=16):x}', f'{(1 << cpu_count) - 1:x}') remove_network_unit('25-rps-cpu-0-empty.link') # 0, then invalid -> 0 @@ -3373,8 +3934,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertEqual(int(output.replace(',', ''), base=16), 1) remove_network_unit('24-rps-cpu-invalid.link') -class NetworkdL2TPTests(unittest.TestCase, Utilities): +class NetworkdL2TPTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -3383,8 +3944,12 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink') def test_l2tp_udp(self): - copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network', - '25-l2tp-udp.netdev', '25-l2tp.network') + copy_network_unit( + '11-dummy.netdev', + '25-l2tp-dummy.network', + '25-l2tp-udp.netdev', + '25-l2tp.network', + ) start_networkd() self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded') @@ -3394,34 +3959,41 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities): output = check_output('ip l2tp show tunnel tunnel_id 10') print(output) - self.assertRegex(output, "Tunnel 10, encap UDP") - self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101") - self.assertRegex(output, "Peer tunnel 11") - self.assertRegex(output, "UDP source / dest ports: 3000/4000") - self.assertRegex(output, "UDP checksum: enabled") + self.assertRegex(output, 'Tunnel 10, encap UDP') + self.assertRegex(output, 'From 192.168.30.100 to 192.168.30.101') + self.assertRegex(output, 'Peer tunnel 11') + self.assertRegex(output, 'UDP source / dest ports: 3000/4000') + self.assertRegex(output, 'UDP checksum: enabled') output = check_output('ip l2tp show session tid 10 session_id 15') print(output) - self.assertRegex(output, "Session 15 in tunnel 10") - self.assertRegex(output, "Peer session 16, tunnel 11") - self.assertRegex(output, "interface name: l2tp-ses1") + self.assertRegex(output, 'Session 15 in tunnel 10') + self.assertRegex(output, 'Peer session 16, tunnel 11') + self.assertRegex(output, 'interface name: l2tp-ses1') output = check_output('ip l2tp show session tid 10 session_id 17') print(output) - self.assertRegex(output, "Session 17 in tunnel 10") - self.assertRegex(output, "Peer session 18, tunnel 11") - self.assertRegex(output, "interface name: l2tp-ses2") + self.assertRegex(output, 'Session 17 in tunnel 10') + self.assertRegex(output, 'Peer session 18, tunnel 11') + self.assertRegex(output, 'interface name: l2tp-ses2') touch_network_unit( - '11-dummy.netdev', '25-l2tp-dummy.network', - '25-l2tp-udp.netdev', '25-l2tp.network') + '11-dummy.netdev', + '25-l2tp-dummy.network', + '25-l2tp-udp.netdev', + '25-l2tp.network', + ) networkctl_reload() self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded') @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink') def test_l2tp_ip(self): - copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network', - '25-l2tp-ip.netdev', '25-l2tp.network') + copy_network_unit( + '11-dummy.netdev', + '25-l2tp-dummy.network', + '25-l2tp-ip.netdev', + '25-l2tp.network', + ) start_networkd() self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded') @@ -3431,30 +4003,33 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities): output = check_output('ip l2tp show tunnel tunnel_id 10') print(output) - self.assertRegex(output, "Tunnel 10, encap IP") - self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101") - self.assertRegex(output, "Peer tunnel 12") + self.assertRegex(output, 'Tunnel 10, encap IP') + self.assertRegex(output, 'From 192.168.30.100 to 192.168.30.101') + self.assertRegex(output, 'Peer tunnel 12') output = check_output('ip l2tp show session tid 10 session_id 25') print(output) - self.assertRegex(output, "Session 25 in tunnel 10") - self.assertRegex(output, "Peer session 26, tunnel 12") - self.assertRegex(output, "interface name: l2tp-ses3") + self.assertRegex(output, 'Session 25 in tunnel 10') + self.assertRegex(output, 'Peer session 26, tunnel 12') + self.assertRegex(output, 'interface name: l2tp-ses3') output = check_output('ip l2tp show session tid 10 session_id 27') print(output) - self.assertRegex(output, "Session 27 in tunnel 10") - self.assertRegex(output, "Peer session 28, tunnel 12") - self.assertRegex(output, "interface name: l2tp-ses4") + self.assertRegex(output, 'Session 27 in tunnel 10') + self.assertRegex(output, 'Peer session 28, tunnel 12') + self.assertRegex(output, 'interface name: l2tp-ses4') touch_network_unit( - '11-dummy.netdev', '25-l2tp-dummy.network', - '25-l2tp-ip.netdev', '25-l2tp.network') + '11-dummy.netdev', + '25-l2tp-dummy.network', + '25-l2tp-ip.netdev', + '25-l2tp.network', + ) networkctl_reload() self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded') -class NetworkdNetworkTests(unittest.TestCase, Utilities): +class NetworkdNetworkTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -3473,34 +4048,34 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.wait_online('test1:off', setup_state='unmanaged') def verify_address_static( - self, - label1: str, - label2: str, - label3: str, - broadcast1: str, - broadcast2: str, - broadcast3: str, - peer1: str, - peer2: str, - peer3: str, - peer4: str, - peer5: str, - peer6: str, - scope1: str, - scope2: str, - deprecated1: str, - deprecated2: str, - deprecated3: str, - deprecated4: str, - route_metric: int, - flag1: str, - flag2: str, - flag3: str, - flag4: str, - ip4_null_16: str, - ip4_null_24: str, - ip6_null_73: str, - ip6_null_74: str, + self, + label1: str, + label2: str, + label3: str, + broadcast1: str, + broadcast2: str, + broadcast3: str, + peer1: str, + peer2: str, + peer3: str, + peer4: str, + peer5: str, + peer6: str, + scope1: str, + scope2: str, + deprecated1: str, + deprecated2: str, + deprecated3: str, + deprecated4: str, + route_metric: int, + flag1: str, + flag2: str, + flag3: str, + flag4: str, + ip4_null_16: str, + ip4_null_24: str, + ip6_null_73: str, + ip6_null_74: str, ): output = check_output('ip address show dev dummy98') print(output) @@ -3542,12 +4117,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertIn(f'inet6 2001:db8:0:f104::2/64 scope global{deprecated4}', output) # route metric - self.assertRegex(output, rf'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98') + self.assertRegex(output, rf'inet 10.8.1.1/24 (metric {route_metric} |)brd 10.8.1.255 scope global dummy98') # fmt: skip self.assertRegex(output, rf'inet6 2001:db8:0:f105::1/64 (metric {route_metric} |)scope global') output_route = check_output('ip -4 route show dev dummy98 10.8.1.0/24') print(output_route) - self.assertIn(f'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route) + self.assertIn(f'10.8.1.0/24 proto kernel scope link src 10.8.1.1 metric {route_metric}', output_route) # fmt: skip output_route = check_output('ip -6 route show dev dummy98 2001:db8:0:f105::/64') print(output_route) @@ -3561,9 +4136,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # null address self.assertTrue(ip4_null_16.endswith('.0.1')) - prefix16 = ip4_null_16[:-len('.0.1')] + prefix16 = ip4_null_16[: -len('.0.1')] self.assertTrue(ip4_null_24.endswith('.1')) - prefix24 = ip4_null_24[:-len('.1')] + prefix24 = ip4_null_24[: -len('.1')] self.assertIn(f'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output) self.assertIn(f'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output) self.assertIn(f'inet6 {ip6_null_73}/73 scope global', output) @@ -3824,9 +4399,14 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('vrf') def test_prefix_route(self): - copy_network_unit('25-prefix-route-with-vrf.network', '12-dummy.netdev', - '25-prefix-route-without-vrf.network', '11-dummy.netdev', - '25-vrf.netdev', '25-vrf.network') + copy_network_unit( + '25-prefix-route-with-vrf.network', + '12-dummy.netdev', + '25-prefix-route-without-vrf.network', + '11-dummy.netdev', + '25-vrf.netdev', + '25-vrf.network', + ) for trial in range(2): if trial == 0: start_networkd() @@ -3851,7 +4431,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): if trial == 0: # Kernel's bug? self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium') - #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium') + # self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium') self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium') self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium') self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium') @@ -3925,7 +4505,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): carrier_map = {'on': '1', 'off': '0'} routable_map = {'on': 'routable', 'off': 'no-carrier'} - for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]: + for carrier, have_config in [('off', True), ('on', True), ('off', False)]: with self.subTest(carrier=carrier, have_config=have_config): if carrier_map[carrier] != read_link_attr('test1', 'carrier'): check_output(f'ip link set dev test1 carrier {carrier}') @@ -3946,7 +4526,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): output = check_output('ip rule list iif test1 priority 111') print(output) - self.assertRegex(output, r'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7') + self.assertRegex(output, r'111: from 192.168.100.18 tos (0x08|throughput) iif test1 oif test1 lookup 7') # fmt: skip output = check_output('ip -6 rule list iif test1 priority 100') print(output) @@ -4005,7 +4585,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): output = check_output('ip rule list priority 112') print(output) - self.assertRegex(output, r'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8') + self.assertRegex(output, r'112: from 192.168.101.18 tos (0x08|throughput) iif dummy98 oif dummy98 lookup 8') # fmt: skip def _test_routing_policy_rule(self, manage_foreign_routes): if not manage_foreign_routes: @@ -4052,8 +4632,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self._test_routing_policy_rule(manage_foreign_routes) def test_routing_policy_rule_restart_and_reconfigure(self): - copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev', - '25-routing-policy-rule-dummy98.network', '12-dummy.netdev') + copy_network_unit( + '25-routing-policy-rule-test1.network', + '11-dummy.netdev', + '25-routing-policy-rule-dummy98.network', + '12-dummy.netdev', + ) # For #11280 and #34068. @@ -4140,7 +4724,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # For issue #36244. copy_network_unit( '11-dummy.netdev', - '25-routing-policy-rule-manual.network') + '25-routing-policy-rule-manual.network', + ) start_networkd() self.wait_operstate('test1', operstate='off', setup_state='configuring', setup_timeout=20) @@ -4374,8 +4959,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): if not manage_foreign_routes: copy_networkd_conf_dropin('networkd-manage-foreign-routes-no.conf') - copy_network_unit('25-route-static.network', '12-dummy.netdev', - '25-route-static-test1.network', '11-dummy.netdev') + copy_network_unit( + '25-route-static.network', + '12-dummy.netdev', + '25-route-static-test1.network', + '11-dummy.netdev', + ) start_networkd() self.wait_online('dummy98:routable', 'test1:routable') self._check_route_static(test1_is_managed=True) @@ -4432,7 +5021,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '25-route-static-issue-35047.network', '25-route-static-issue-35047.network.d/step1.conf', '12-dummy.netdev', - copy_dropins=False) + copy_dropins=False, + ) start_networkd() self.wait_online('dummy98:routable') @@ -4444,9 +5034,27 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertIn('198.51.100.0/24 via 192.0.2.2 proto static', output) check_output('ip link set dev dummy98 down') - self.wait_route_dropped('dummy98', '192.0.2.2 proto kernel scope link src 192.0.2.1', ipv='-4', table='all', timeout_sec=10) - self.wait_route_dropped('dummy98', 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', ipv='-4', table='all', timeout_sec=10) - self.wait_route_dropped('dummy98', '198.51.100.0/24 via 192.0.2.2 proto static', ipv='-4', table='all', timeout_sec=10) + self.wait_route_dropped( + 'dummy98', + '192.0.2.2 proto kernel scope link src 192.0.2.1', + ipv='-4', + table='all', + timeout_sec=10, + ) + self.wait_route_dropped( + 'dummy98', + 'local 192.0.2.1 table local proto kernel scope host src 192.0.2.1', + ipv='-4', + table='all', + timeout_sec=10, + ) + self.wait_route_dropped( + 'dummy98', + '198.51.100.0/24 via 192.0.2.2 proto static', + ipv='-4', + table='all', + timeout_sec=10, + ) print('### ip -4 route show table all dev dummy98') output = check_output('ip -4 route show table all dev dummy98') @@ -4485,6 +5093,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print('### ip -4 route show table all dev dummy98') output = check_output('ip -4 route show table all dev dummy98') print(output) + # fmt: off self.assertIn('default via 192.168.0.193 table 249 proto static src 192.168.0.227 metric 128 onlink', output) self.assertIn('192.168.0.192/26 table 249 proto static scope link src 192.168.0.227 metric 128', output) self.assertIn('10.1.2.2 via 192.168.0.193 proto static src 192.168.0.227 metric 128 onlink', output) @@ -4492,16 +5101,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertIn('192.168.0.193 proto static scope link src 192.168.0.227 metric 128', output) self.assertIn('local 192.168.0.227 table local proto kernel scope host src 192.168.0.227', output) self.assertIn('broadcast 192.168.0.255 table local proto kernel scope link src 192.168.0.227', output) + # fmt: on print('### ip -6 route show table all dev dummy98') output = check_output('ip -6 route show table all dev dummy98') print(output) + # fmt: off self.assertIn('2000:f00::/64 table 249 proto static src 2000:f00::227 metric 128 pref medium', output) self.assertIn('default via 2000:f00::1 table 249 proto static src 2000:f00::227 metric 128 onlink pref medium', output) self.assertIn('fe80::/64 proto kernel metric 256 pref medium', output) self.assertIn('local 2000:f00::227 table local proto kernel metric 0 pref medium', output) - self.assertRegex(output, 'local fe80:[a-f0-9:]* table local proto kernel metric 0 pref medium', output) + self.assertRegex(output, 'local fe80:[a-f0-9:]* table local proto kernel metric 0 pref medium') self.assertIn('multicast ff00::/8 table local proto kernel metric 256 pref medium', output) + # fmt: on def test_route_via_ipv6(self): copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev') @@ -4585,8 +5197,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('vrf') def test_route_vrf(self): - copy_network_unit('25-route-vrf.network', '12-dummy.netdev', - '25-vrf.netdev', '25-vrf.network') + copy_network_unit( + '25-route-vrf.network', + '12-dummy.netdev', + '25-vrf.netdev', + '25-vrf.network', + ) start_networkd() self.wait_online('dummy98:routable', 'vrf99:carrier') @@ -4637,7 +5253,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # a dummy device does not make the addresses go through tentative state, so we # reuse a bond from an earlier test, which does make the addresses go through # tentative state, and do our test on that - copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') + copy_network_unit( + '23-active-slave.network', + '25-route-ipv6-src.network', + '25-bond-active-backup-slave.netdev', + '12-dummy.netdev', + ) start_networkd() self.wait_online('dummy98:enslaved', 'bond199:routable') @@ -4658,7 +5279,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): output = check_output('ip -6 route list dev dummy98') print(output) - self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output) + self.assertIn('abcd::/16 via 2001:1234:56:8f63::1:1 proto static src 2001:1234:56:8f63::1', output) # fmt: skip output = check_output('ip -4 route list dev dummy98') print(output) @@ -4702,7 +5323,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy') def test_ipv6_neigh_retrans_time(self): - link='test25' + link = 'test25' copy_network_unit('25-dummy.netdev', '25-dummy.network') start_networkd() @@ -4752,10 +5373,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): remove_network_unit('25-ipv6-neigh-retrans-time-4s.network') def test_neighbor(self): - copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf', - '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network', - '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network', - copy_dropins=False) + copy_network_unit( + '12-dummy.netdev', + '25-neighbor-dummy.network', + '25-neighbor-dummy.network.d/10-step1.conf', + '25-gre-tunnel-remote-any.netdev', + '25-neighbor-ip.network', + '25-ip6gre-tunnel-remote-any.netdev', + '25-neighbor-ipv6.network', + copy_dropins=False, + ) start_networkd() self.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable') @@ -4768,7 +5395,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print('### ip neigh list dev ip6gretun97') output = check_output('ip neigh list dev ip6gretun97') print(output) - self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT') + self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT') # fmt: skip self.assertNotIn('2001:db8:0:f102::18', output) print('### ip neigh list dev dummy98') @@ -4799,8 +5426,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf', - '25-neighbor-dummy.network.d/10-step2.conf') + remove_network_unit( + '25-neighbor-dummy.network.d/10-step1.conf', + '25-neighbor-dummy.network.d/10-step2.conf', + ) copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf') networkctl_reload() self.wait_online('dummy98:degraded') @@ -4815,8 +5444,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertNotIn('2004:da8:1::1', output) def test_link_local_addressing(self): - copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev', - '25-link-local-addressing-no.network', '12-dummy.netdev') + copy_network_unit( + '25-link-local-addressing-yes.network', + '11-dummy.netdev', + '25-link-local-addressing-no.network', + '12-dummy.netdev', + ) start_networkd() self.wait_online('test1:degraded', 'dummy98:carrier') @@ -5039,7 +5672,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): start_networkd() always = test.startswith('always') - initial_up = test != 'manual' and not test.endswith('down') # note: default is up + initial_up = test != 'manual' and not test.endswith('down') # note: default is up expect_up = initial_up next_up = not expect_up @@ -5128,7 +5761,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): else: self.tearDown() - print(f'### test_activation_policy_required_for_online(policy={policy}, required={required})') + print(f'### test_activation_policy_required_for_online(policy={policy}, required={required})') # fmt: skip with self.subTest(policy=policy, required=required): self._test_activation_policy_required_for_online(policy, required) @@ -5211,20 +5844,28 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): call('ip -6 addr add 2001:db8:9999:f101::15/64 dev unmanaged0') # Wait for all addresses + # fmt: off self.wait_address('unmanaged0', 'inet 10.20.30.40/32', scope='global', ipv='-4', timeout_sec=10) self.wait_address('unmanaged0', 'inet6 2001:db8:9999:f101::15/64', scope='global', ipv='-6', timeout_sec=10) self.wait_address('unmanaged0', 'inet6 fe80::[0-9a-f:]*/64', scope='link', ipv='-6', timeout_sec=10) + # fmt: on # Wait for all routes + # fmt: off self.wait_route('unmanaged0', 'local 10.20.30.40 proto kernel', table='local', ipv='-4', timeout_sec=10) self.wait_route('unmanaged0', 'local fe80::[0-9a-f:]* proto kernel', table='local', ipv='-6', timeout_sec=10) self.wait_route('unmanaged0', 'multicast ff00::/8 proto kernel', table='local', ipv='-6', timeout_sec=10) self.wait_route('unmanaged0', '2001:db8:9999:f101::/64 proto kernel', table='main', ipv='-6', timeout_sec=10) self.wait_route('unmanaged0', 'fe80::/64 proto kernel', table='main', ipv='-6', timeout_sec=10) + # fmt: on # Start `ip monitor` with output to a temporary file with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor_u') as logfile_unmanaged: - process_u = subprocess.Popen(['ip', 'monitor', 'dev', 'unmanaged0'], stdout=logfile_unmanaged, text=True) + process_u = subprocess.Popen( + ['ip', 'monitor', 'dev', 'unmanaged0'], + stdout=logfile_unmanaged, + text=True, + ) start_networkd() self.check_keep_configuration_on_restart() @@ -5368,10 +6009,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1') print(output) + # fmt: off if first: self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) else: self.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output) + # fmt: on output = check_output('ip route show 10.10.10.13') print(output) @@ -5382,10 +6025,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): output = check_output('ip -6 route show 2001:1234:5:8f62::2') print(output) + # fmt: off if first: self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output) else: self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output) + # fmt: on output = check_output('ip route show 10.10.10.14') print(output) @@ -5410,8 +6055,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): check_output('ip address add 192.168.20.20/24 dev dummy98') check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98') - copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network', - '12-dummy.netdev', '25-nexthop-dummy-1.network') + copy_network_unit( + '25-nexthop-1.network', + '25-veth.netdev', + '25-veth-peer.network', + '12-dummy.netdev', + '25-nexthop-dummy-1.network', + ) start_networkd() self.check_nexthop(manage_foreign_nexthops, first=True) @@ -5438,8 +6088,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional # here to test reconfiguring with different .network files does not trigger race. # See also comments in link_drop_requests(). - networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network - networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network + # fmt: off + networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network + networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network + # fmt: on self.check_nexthop(manage_foreign_nexthops, first=True) @@ -5501,8 +6153,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops): self._test_nexthop(manage_foreign_nexthops) -class NetworkdTCTests(unittest.TestCase, Utilities): +class NetworkdTCTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -5553,7 +6205,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): output = check_output('tc qdisc show dev dummy98') print(output) self.assertRegex(output, 'qdisc codel 33: root') - self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn') + self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn') # fmt: skip @expectedFailureIfModuleIsNotAvailable('sch_drr') def test_qdisc_drr(self): @@ -5605,7 +6257,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): output = check_output('tc qdisc show dev dummy98') print(output) self.assertRegex(output, 'qdisc fq_codel 34: root') - self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn') + self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn') # fmt: skip @expectedFailureIfModuleIsNotAvailable('sch_fq_pie') def test_qdisc_fq_pie(self): @@ -5679,8 +6331,12 @@ class NetworkdTCTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('sch_ingress') def test_qdisc_ingress(self): - copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev', - '25-qdisc-ingress.network', '11-dummy.netdev') + copy_network_unit( + '25-qdisc-clsact.network', + '12-dummy.netdev', + '25-qdisc-ingress.network', + '11-dummy.netdev', + ) start_networkd() self.wait_online('dummy98:routable', 'test1:routable') @@ -5713,8 +6369,12 @@ class NetworkdTCTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('sch_netem') def test_qdisc_netem(self): - copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev', - '25-qdisc-netem-compat.network', '11-dummy.netdev') + copy_network_unit( + '25-qdisc-netem.network', + '12-dummy.netdev', + '25-qdisc-netem-compat.network', + '11-dummy.netdev', + ) start_networkd() self.wait_online('dummy98:routable', 'test1:routable') @@ -5784,7 +6444,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities): output = check_output('tc qdisc show dev dummy98') print(output) self.assertRegex(output, 'qdisc tbf 35: root') - self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst (987500b|999200b) lat 70(.0)?ms') + self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst (987500b|999200b) lat 70(.0)?ms') # fmt: skip @expectedFailureIfModuleIsNotAvailable('sch_teql') def test_qdisc_teql(self): @@ -5812,13 +6472,13 @@ class NetworkdTCTests(unittest.TestCase, Utilities): self.assertFalse(networkd_is_failed()) check_output('tc qdisc replace dev dummy98 root fq pacing') self.assertFalse(networkd_is_failed()) - check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540') + check_output('tc qdisc replace dev dummy98 handle 10: root tbf rate 0.5mbit burst 5kb latency 70ms peakrate 1mbit minburst 1540') # fmt: skip self.assertFalse(networkd_is_failed()) check_output('tc qdisc add dev dummy98 parent 10:1 handle 100: sfq') self.assertFalse(networkd_is_failed()) -class NetworkdStateFileTests(unittest.TestCase, Utilities): +class NetworkdStateFileTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -5846,7 +6506,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): self.assertIn('REQUIRED_FAMILY_FOR_ONLINE=both', output) self.assertIn('ACTIVATION_POLICY=up', output) self.assertIn('NETWORK_FILE=/run/systemd/network/25-state-file-tests.network', output) - self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output) + self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output) # fmt: skip self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output) self.assertIn('DOMAINS=hogehoge', output) self.assertIn('ROUTE_DOMAINS=foofoo', output) @@ -5893,7 +6553,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): output = read_link_state_file('dummy98') print(output) - self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output) + self.assertIn('DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com', output) # fmt: skip self.assertIn('NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org', output) self.assertIn('DOMAINS=hogehoge', output) self.assertIn('ROUTE_DOMAINS=foofoo', output) @@ -5931,8 +6591,8 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): self.assertIn('IPV4_ADDRESS_STATE=off', output) self.assertIn('IPV6_ADDRESS_STATE=routable', output) -class NetworkdBondTests(unittest.TestCase, Utilities): +class NetworkdBondTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -5972,7 +6632,12 @@ class NetworkdBondTests(unittest.TestCase, Utilities): self.wait_online('dummy98:enslaved') def test_bond_active_slave(self): - copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') + copy_network_unit( + '23-active-slave.network', + '23-bond199.network', + '25-bond-active-backup-slave.netdev', + '12-dummy.netdev', + ) start_networkd() self.wait_online('dummy98:enslaved', 'bond199:degraded') @@ -5991,12 +6656,18 @@ class NetworkdBondTests(unittest.TestCase, Utilities): '23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', - '12-dummy.netdev') + '12-dummy.netdev', + ) networkctl_reload() self.wait_online('dummy98:enslaved', 'bond199:degraded') def test_bond_primary_slave(self): - copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') + copy_network_unit( + '23-primary-slave.network', + '23-bond199.network', + '25-bond-active-backup-slave.netdev', + '12-dummy.netdev', + ) start_networkd() self.wait_online('dummy98:enslaved', 'bond199:degraded') @@ -6007,7 +6678,9 @@ class NetworkdBondTests(unittest.TestCase, Utilities): # for issue #25627 mkdir_p(os.path.join(network_unit_dir, '23-bond199.network.d')) for mac in ['00:11:22:33:44:55', '00:11:22:33:44:56']: - with open(os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '23-bond199.network.d/mac.conf'), mode='w', encoding='utf-8' + ) as f: f.write(f'[Link]\nMACAddress={mac}\n') networkctl_reload() @@ -6018,8 +6691,13 @@ class NetworkdBondTests(unittest.TestCase, Utilities): self.assertIn(f'link/ether {mac}', output) def test_bond_operstate(self): - copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev', - '25-bond99.network', '25-bond-slave.network') + copy_network_unit( + '25-bond.netdev', + '11-dummy.netdev', + '12-dummy.netdev', + '25-bond99.network', + '25-bond-slave.network', + ) start_networkd() self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable') @@ -6068,8 +6746,8 @@ class NetworkdBondTests(unittest.TestCase, Utilities): print(output) self.assertNotRegex(output, 'NO-CARRIER') -class NetworkdBridgeTests(unittest.TestCase, Utilities): +class NetworkdBridgeTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -6077,8 +6755,13 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): tear_down_common() def test_bridge_mac_none(self): - copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network', - '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link') + copy_network_unit( + '12-dummy-mac.netdev', + '26-bridge-mac-slave.network', + '26-bridge-mac.netdev', + '26-bridge-mac-master.network', + '26-bridge-mac.link', + ) start_networkd() self.wait_online('dummy98:enslaved', 'bridge99:degraded') @@ -6091,9 +6774,13 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertIn('link/ether 12:34:56:78:9a:01', output) def test_bridge_vlan(self): - copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network', - '26-bridge.netdev', '26-bridge-vlan-master.network', - copy_dropins=False) + copy_network_unit( + '11-dummy.netdev', + '26-bridge-vlan-slave.network', + '26-bridge.netdev', + '26-bridge-vlan-master.network', + copy_dropins=False, + ) start_networkd() self.wait_online('test1:enslaved', 'bridge99:degraded') @@ -6126,8 +6813,10 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertNotIn(f'{i}', output) # Change vlan IDs - copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf', - '26-bridge-vlan-master.network.d/10-override.conf') + copy_network_unit( + '26-bridge-vlan-slave.network.d/10-override.conf', + '26-bridge-vlan-master.network.d/10-override.conf', + ) networkctl_reload() self.wait_online('test1:enslaved', 'bridge99:degraded') @@ -6156,8 +6845,10 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertNotIn(f'{i}', output) # Remove several vlan IDs - copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf', - '26-bridge-vlan-master.network.d/20-override.conf') + copy_network_unit( + '26-bridge-vlan-slave.network.d/20-override.conf', + '26-bridge-vlan-master.network.d/20-override.conf', + ) networkctl_reload() self.wait_online('test1:enslaved', 'bridge99:degraded') @@ -6186,8 +6877,10 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertNotIn(f'{i}', output) # Remove all vlan IDs - copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf', - '26-bridge-vlan-master.network.d/30-override.conf') + copy_network_unit( + '26-bridge-vlan-slave.network.d/30-override.conf', + '26-bridge-vlan-master.network.d/30-override.conf', + ) networkctl_reload() self.wait_online('test1:enslaved', 'bridge99:degraded') @@ -6204,9 +6897,14 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertNotIn(f'{i}', output) def test_bridge_vlan_issue_20373(self): - copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network', - '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network', - '21-vlan.netdev', '21-vlan.network') + copy_network_unit( + '11-dummy.netdev', + '26-bridge-vlan-slave-issue-20373.network', + '26-bridge-issue-20373.netdev', + '26-bridge-vlan-master-issue-20373.network', + '21-vlan.netdev', + '21-vlan.network', + ) start_networkd() self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable') @@ -6223,8 +6921,12 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertIn('600', output) def test_bridge_mdb(self): - copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network', - '26-bridge.netdev', '26-bridge-mdb-master.network') + copy_network_unit( + '11-dummy.netdev', + '26-bridge-mdb-slave.network', + '26-bridge.netdev', + '26-bridge-mdb-master.network', + ) start_networkd() self.wait_online('test1:enslaved', 'bridge99:degraded') @@ -6236,7 +6938,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertRegex(output, 'dev bridge99 port bridge99 grp 224.0.1.2 temp *vid 4067') # The kernels older than 955062b03fa62b802a1ee34fbb04e39f7a70ae73 (v5.11) do not support L2 bridge MDB entries - if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070') == 0: + if call_quiet('bridge mdb add dev bridge99 port bridge99 grp 01:80:c2:00:00:0f permanent vid 4070') == 0: # fmt: skip self.assertRegex(output, 'dev bridge99 port bridge99 grp 01:80:c2:00:00:0e permanent *vid 4069') def test_bridge_keep_master(self): @@ -6256,18 +6958,18 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): output = check_output('bridge -d link show dummy98') print(output) - self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400') - self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1') + self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400') + self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1') self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1') - self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1') - self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1') + self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0') # CONFIG_BRIDGE_IGMP_SNOOPING=y self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True) - self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True) - self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0') - self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23') - self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0') - self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True) + self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23') + self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0') def check_bridge_property(self): self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable') @@ -6294,31 +6996,38 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): output = check_output('bridge -d link show dummy98') print(output) - self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400') - self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1') - self.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1') + self.check_bridge_port_attr('bridge99', 'dummy98', 'path_cost', '400') + self.check_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode', '1') + self.check_bridge_port_attr('bridge99', 'dummy98', 'isolated', '1') self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave', '1') - self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1') - self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood', '1') + self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood', '0') # CONFIG_BRIDGE_IGMP_SNOOPING=y self.check_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast', '1', allow_enoent=True) - self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True) - self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0') - self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23') - self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0') - self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True) + self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23') + self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0') + self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0') output = check_output('bridge -d link show test1') print(output) - self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0') + self.check_bridge_port_attr('bridge99', 'test1', 'priority', '0') self.assertIn('locked on', output) - if ' mab ' in output: # This is new in kernel and iproute2 v6.2 + if ' mab ' in output: # This is new in kernel and iproute2 v6.2 self.assertIn('mab on', output) def test_bridge_property(self): - copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev', - '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', - '25-bridge99.network', '14-dummy.netdev', '26-bridge-vlan-tunnel.network') + copy_network_unit( + '11-dummy.netdev', + '12-dummy.netdev', + '26-bridge.netdev', + '26-bridge-slave-interface-1.network', + '26-bridge-slave-interface-2.network', + '25-bridge99.network', + '14-dummy.netdev', + '26-bridge-vlan-tunnel.network', + ) start_networkd() self.check_bridge_property() @@ -6330,7 +7039,8 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', '26-bridge-vlan-tunnel.network', - '25-bridge99.network') + '25-bridge99.network', + ) networkctl_reload() self.check_bridge_property() @@ -6373,7 +7083,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): output = check_output('ip address show bridge99') print(output) self.assertNotIn('192.168.0.15/24', output) - self.assertIn('192.168.0.16/24', output) # foreign address is kept + self.assertIn('192.168.0.16/24', output) # foreign address is kept print('### ip -6 route list table all dev bridge99') output = check_output('ip -6 route list table all dev bridge99') @@ -6394,8 +7104,11 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertIn('mtu 9000 ', output) def test_bridge_configure_without_carrier(self): - copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network', - '11-dummy.netdev') + copy_network_unit( + '26-bridge.netdev', + '26-bridge-configure-without-carrier.network', + '11-dummy.netdev', + ) start_networkd() # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations @@ -6403,13 +7116,18 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): with self.subTest(test=test): if test == 'no-slave': # bridge has no slaves; it's up but *might* not have carrier - self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30) + self.wait_operstate( + 'bridge99', + operstate=r'(no-carrier|routable)', + setup_state=None, + setup_timeout=30, + ) # due to a bug in the kernel, newly-created bridges are brought up # *with* carrier, unless they have had any setting changed; e.g. # their mac set, priority set, etc. Then, they will lose carrier # as soon as a (down) slave interface is added, and regain carrier # again once the slave interface is brought up. - #self.check_link_attr('bridge99', 'carrier', '0') + # self.check_link_attr('bridge99', 'carrier', '0') elif test == 'add-slave': # add slave to bridge, but leave it down; bridge is definitely no-carrier self.check_link_attr('test1', 'operstate', 'down') @@ -6442,9 +7160,14 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertRegex(output, '10.1.2.1') def test_bridge_ignore_carrier_loss(self): - copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev', - '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', - '25-bridge99-ignore-carrier-loss.network') + copy_network_unit( + '11-dummy.netdev', + '12-dummy.netdev', + '26-bridge.netdev', + '26-bridge-slave-interface-1.network', + '26-bridge-slave-interface-2.network', + '25-bridge99-ignore-carrier-loss.network', + ) start_networkd() self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable') @@ -6459,8 +7182,11 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99') def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self): - copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network', - '25-bridge99-ignore-carrier-loss.network') + copy_network_unit( + '26-bridge.netdev', + '26-bridge-slave-interface-1.network', + '25-bridge99-ignore-carrier-loss.network', + ) start_networkd() self.wait_online('bridge99:no-carrier') @@ -6480,8 +7206,8 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): print(output) self.assertIn('from all to 8.8.8.8 lookup 100', output) -class NetworkdSRIOVTests(unittest.TestCase, Utilities): +class NetworkdSRIOVTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -6497,7 +7223,9 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): # Create VF. if num_vfs > 0: - with open(f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8') as f: + with open( + f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8' + ) as f: f.write(f'{num_vfs}') @expectedFailureIfModuleIsNotAvailable('netdevsim') @@ -6511,11 +7239,12 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): output = check_output('ip link show dev sim99') print(output) - self.assertRegex(output, - 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' - 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' - 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off' - ) + self.assertRegex( + output, + 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' + 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' + 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off', + ) @expectedFailureIfModuleIsNotAvailable('netdevsim') def test_sriov_udev(self): @@ -6531,11 +7260,12 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): output = check_output('ip link show dev sim99') print(output) - self.assertRegex(output, - 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' - 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' - 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off' - ) + self.assertRegex( + output, + 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' + 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' + 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off', + ) self.assertNotIn('vf 3', output) self.assertNotIn('vf 4', output) @@ -6547,12 +7277,13 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): output = check_output('ip link show dev sim99') print(output) - self.assertRegex(output, - 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' - 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' - 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *' - 'vf 3' - ) + self.assertRegex( + output, + 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' + 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' + 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *' + 'vf 3', + ) self.assertNotIn('vf 4', output) with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f: @@ -6563,12 +7294,13 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): output = check_output('ip link show dev sim99') print(output) - self.assertRegex(output, - 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' - 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' - 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *' - 'vf 3' - ) + self.assertRegex( + output, + 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' + 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' + 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *' + 'vf 3', + ) self.assertNotIn('vf 4', output) with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f: @@ -6579,10 +7311,11 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): output = check_output('ip link show dev sim99') print(output) - self.assertRegex(output, - 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' - 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off' - ) + self.assertRegex( + output, + 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' + 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off', + ) self.assertNotIn('vf 2', output) self.assertNotIn('vf 3', output) self.assertNotIn('vf 4', output) @@ -6595,16 +7328,17 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities): output = check_output('ip link show dev sim99') print(output) - self.assertRegex(output, - 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' - 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' - 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off' - ) + self.assertRegex( + output, + 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' + 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' + 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off', + ) self.assertNotIn('vf 3', output) self.assertNotIn('vf 4', output) -class NetworkdLLDPTests(unittest.TestCase, Utilities): +class NetworkdLLDPTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -6626,12 +7360,12 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities): self.fail() # With interface name - output = networkctl('lldp', 'veth99'); + output = networkctl('lldp', 'veth99') print(output) self.assertRegex(output, r'veth99 .* veth-peer .* .......a...') # With interface name pattern - output = networkctl('lldp', 've*9'); + output = networkctl('lldp', 've*9') print(output) self.assertRegex(output, r'veth99 .* veth-peer .* .......a...') @@ -6678,12 +7412,12 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities): self.fail() # With interface name - output = networkctl('lldp', 'veth99'); + output = networkctl('lldp', 'veth99') print(output) self.assertRegex(output, r'veth99 .* veth-peer .* ....r......') # With interface name pattern - output = networkctl('lldp', 've*9'); + output = networkctl('lldp', 've*9') print(output) self.assertRegex(output, r'veth99 .* veth-peer .* ....r......') @@ -6715,13 +7449,13 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities): # Compare the json output from sender and receiver sender_json = get_link_description('veth-peer')['LLDP'] - receiver_json = json.loads(networkctl('--json=short', 'lldp', 'veth99'))['Neighbors'][0]['Neighbors'][0] + receiver_json = json.loads(networkctl('--json=short', 'lldp', 'veth99'))['Neighbors'][0]['Neighbors'][0] # fmt: skip print(sender_json) print(receiver_json) self.assertEqual(sender_json, receiver_json) -class NetworkdRATests(unittest.TestCase, Utilities): +class NetworkdRATests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -6777,7 +7511,11 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe') def test_ipv6_token_static(self): - copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network') + copy_network_unit( + '25-veth.netdev', + '25-ipv6-prefix.network', + '25-ipv6-prefix-veth-token-static.network', + ) start_networkd() self.check_ipv6_token_static() @@ -6798,35 +7536,63 @@ class NetworkdRATests(unittest.TestCase, Utilities): def test_ndisc_redirect(self): if not os.path.exists(test_ndisc_send): - self.skipTest(f"{test_ndisc_send} does not exist.") + self.skipTest(f'{test_ndisc_send} does not exist.') - copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network') + copy_network_unit( + '25-veth.netdev', + '25-ipv6-prefix.network', + '25-ipv6-prefix-veth-token-static.network', + ) start_networkd() self.check_ipv6_token_static() # Introduce three redirect routes. + # fmt: off check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d') check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d') check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:3:1a:2b:3c:4d --redirect-destination 2002:da8:1:3:1a:2b:3c:4d') + # fmt: on self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) self.wait_route('veth99', '2002:da8:1:3:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) # Change the target address of the redirects. + # fmt: off check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d') check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d') - self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) - self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10) - self.wait_route('veth99', r'2002:da8:1:1:1a:2b:3c:4d nhid [0-9]* via fe80::1 proto redirect', ipv='-6', timeout_sec=10) - self.wait_route('veth99', r'2002:da8:1:2:1a:2b:3c:4d nhid [0-9]* via fe80::2 proto redirect', ipv='-6', timeout_sec=10) + # fmt: on + self.wait_route_dropped( + 'veth99', + '2002:da8:1:1:1a:2b:3c:4d proto redirect', + ipv='-6', + timeout_sec=10, + ) + self.wait_route_dropped( + 'veth99', + '2002:da8:1:2:1a:2b:3c:4d proto redirect', + ipv='-6', + timeout_sec=10, + ) + self.wait_route( + 'veth99', + r'2002:da8:1:1:1a:2b:3c:4d nhid [0-9]* via fe80::1 proto redirect', + ipv='-6', + timeout_sec=10, + ) + self.wait_route( + 'veth99', + r'2002:da8:1:2:1a:2b:3c:4d nhid [0-9]* via fe80::2 proto redirect', + ipv='-6', + timeout_sec=10, + ) - # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore. - # Then, verify that all redirect routes and the default route are dropped. + # Send Neighbor Advertisement without the router flag to announce the default router is not available + # anymore. Then, verify that all redirect routes and the default route are dropped. output = check_output('ip -6 address show dev veth-peer scope link') veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group() print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}') - check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no') + check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no') # fmt: skip self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10) self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10) @@ -6834,7 +7600,10 @@ class NetworkdRATests(unittest.TestCase, Utilities): # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306 since = datetime.datetime.now() check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}') - self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since) + self.check_networkd_log( + 'veth-peer: RADV: Received RS from the same interface, ignoring.', + since=since, + ) def check_ndisc_mtu(self, mtu): for _ in range(20): @@ -6847,11 +7616,13 @@ class NetworkdRATests(unittest.TestCase, Utilities): def test_ndisc_mtu(self): if not os.path.exists(test_ndisc_send): - self.skipTest(f"{test_ndisc_send} does not exist.") + self.skipTest(f'{test_ndisc_send} does not exist.') - copy_network_unit('25-veth.netdev', - '25-veth-peer-no-address.network', - '25-ipv6-prefix-veth-token-static.network') + copy_network_unit( + '25-veth.netdev', + '25-veth-peer-no-address.network', + '25-ipv6-prefix-veth-token-static.network', + ) start_networkd() self.wait_online('veth-peer:degraded') @@ -6875,16 +7646,24 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.check_ndisc_mtu(1700) def test_ipv6_token_prefixstable(self): - copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network') + copy_network_unit( + '25-veth.netdev', + '25-ipv6-prefix.network', + '25-ipv6-prefix-veth-token-prefixstable.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:degraded') output = check_output('ip -6 address show dev veth99') print(output) - self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable - self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64 + self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable + self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64 - with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), + mode='a', + encoding='utf-8', + ) as f: f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n') networkctl_reload() @@ -6892,28 +7671,57 @@ class NetworkdRATests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev veth99') print(output) - self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable - self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64 + self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable + self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64 check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99') check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad') networkctl_reconfigure('veth99') self.wait_online('veth99:routable') - self.wait_address('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable - self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable + self.wait_address( + 'veth99', + '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', + ipv='-6', + timeout_sec=10, + ) # the 2nd prefixstable + self.wait_address_dropped( + 'veth99', + '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', + ipv='-6', + timeout_sec=10, + ) # the 1st prefixstable check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99') check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad') networkctl_reconfigure('veth99') self.wait_online('veth99:routable') - self.wait_address('veth99', '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', ipv='-6', timeout_sec=10) # the 3rd prefixstable - self.wait_address_dropped('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable - self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable + self.wait_address( + 'veth99', + '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', + ipv='-6', + timeout_sec=10, + ) # the 3rd prefixstable + self.wait_address_dropped( + 'veth99', + '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', + ipv='-6', + timeout_sec=10, + ) # the 2nd prefixstable + self.wait_address_dropped( + 'veth99', + '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', + ipv='-6', + timeout_sec=10, + ) # the 1st prefixstable def test_ipv6_token_prefixstable_without_address(self): - copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network') + copy_network_unit( + '25-veth.netdev', + '25-ipv6-prefix.network', + '25-ipv6-prefix-veth-token-prefixstable-without-address.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:degraded') @@ -6923,21 +7731,29 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output) def test_router_hop_limit(self): - copy_network_unit('25-veth-client.netdev', - '25-veth-router.netdev', - '26-bridge.netdev', - '25-veth-bridge.network', - '25-veth-client.network', - '25-veth-router-hop-limit.network', - '25-bridge99.network') + copy_network_unit( + '25-veth-client.netdev', + '25-veth-router.netdev', + '26-bridge.netdev', + '25-veth-bridge.network', + '25-veth-client.network', + '25-veth-router-hop-limit.network', + '25-bridge99.network', + ) start_networkd() - self.wait_online('client:routable', 'client-p:enslaved', - 'router:degraded', 'router-p:enslaved', - 'bridge99:routable') + self.wait_online( + 'client:routable', + 'client-p:enslaved', + 'router:degraded', + 'router-p:enslaved', + 'bridge99:routable', + ) self.check_ipv6_sysctl_attr('client', 'hop_limit', '42') - with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8' + ) as f: f.write('\n[IPv6SendRA]\nHopLimit=43\n') networkctl_reload() @@ -6954,14 +7770,26 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.wait_online('client:routable') self.wait_address('client', f'2002:da8:1:99:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10) self.wait_address('client', f'2002:da8:1:98:1034:56ff:fe78:9a{suffix}/64', ipv='-6', timeout_sec=10) - self.wait_route('client', rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1}', ipv='-6', timeout_sec=10) - self.wait_route('client', rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2}', ipv='-6', timeout_sec=10) + self.wait_route( + 'client', + rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1}', + ipv='-6', + timeout_sec=10, + ) + self.wait_route( + 'client', + rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2}', + ipv='-6', + timeout_sec=10, + ) print('### ip -6 route show dev client default') output = check_output('ip -6 route show dev client default') print(output) + # fmt: off self.assertRegex(output, rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a99 proto ra metric {metric_1} expires [0-9]*sec pref {preference_1}') self.assertRegex(output, rf'default nhid [0-9]* via fe80::1034:56ff:fe78:9a98 proto ra metric {metric_2} expires [0-9]*sec pref {preference_2}') + # fmt: on for i in [100, 200, 300, 512, 1024, 2048]: if i not in [metric_1, metric_2]: @@ -6972,20 +7800,26 @@ class NetworkdRATests(unittest.TestCase, Utilities): self.assertNotIn(f'pref {i}', output) def test_router_preference(self): - copy_network_unit('25-veth-client.netdev', - '25-veth-router-high.netdev', - '25-veth-router-low.netdev', - '26-bridge.netdev', - '25-veth-bridge.network', - '25-veth-client.network', - '25-veth-router-high.network', - '25-veth-router-low.network', - '25-bridge99.network') + copy_network_unit( + '25-veth-client.netdev', + '25-veth-router-high.netdev', + '25-veth-router-low.netdev', + '26-bridge.netdev', + '25-veth-bridge.network', + '25-veth-client.network', + '25-veth-router-high.network', + '25-veth-router-low.network', + '25-bridge99.network', + ) start_networkd() - self.wait_online('client-p:enslaved', - 'router-high:degraded', 'router-high-p:enslaved', - 'router-low:degraded', 'router-low-p:enslaved', - 'bridge99:routable') + self.wait_online( + 'client-p:enslaved', + 'router-high:degraded', + 'router-high-p:enslaved', + 'router-low:degraded', + 'router-low-p:enslaved', + 'bridge99:routable', + ) networkctl_reconfigure('client') self.wait_online('client:routable') @@ -6993,64 +7827,135 @@ class NetworkdRATests(unittest.TestCase, Utilities): # change the map from preference to metric. with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f: - f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n') + f.write(''' +[Link] +MACAddress=12:34:56:78:9a:01 +[IPv6AcceptRA] +RouteMetric=100:200:300 +''') networkctl_reload() self.check_router_preference('01', 100, 'high', 300, 'low') # swap the preference (for issue #28439) - with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f: - f.write('\n[IPv6SendRA]\nRouterPreference=low\n') - with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f: - f.write('\n[IPv6SendRA]\nRouterPreference=high\n') + with open( + os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +[IPv6SendRA] +RouterPreference=low +''') + with open( + os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +[IPv6SendRA] +RouterPreference=high +''') networkctl_reload() self.check_router_preference('01', 300, 'low', 100, 'high') # Use the same preference, and check if the two routes are not coalesced. See issue #33470. - with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f: - f.write('\n[IPv6SendRA]\nRouterPreference=medium\n') - with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f: - f.write('\n[IPv6SendRA]\nRouterPreference=medium\n') + with open( + os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +[IPv6SendRA] +RouterPreference=medium +''') + with open( + os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +[IPv6SendRA] +RouterPreference=medium +''') networkctl_reload() self.check_router_preference('01', 200, 'medium', 200, 'medium') # Use route options to configure default routes. # The preference specified in the RA header should be ignored. See issue #33468. - with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f: - f.write('\n[IPv6SendRA]\nRouterPreference=high\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n') - with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f: - f.write('\n[IPv6SendRA]\nRouterPreference=low\n[IPv6RoutePrefix]\nRoute=::/0\nLifetimeSec=1200\n') + with open( + os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +[IPv6SendRA] +RouterPreference=high +[IPv6RoutePrefix] +Route=::/0 +LifetimeSec=1200 +''') + with open( + os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +[IPv6SendRA] +RouterPreference=low +[IPv6RoutePrefix] +Route=::/0 +LifetimeSec=1200 +''') networkctl_reload() self.check_router_preference('01', 200, 'medium', 200, 'medium') # Set zero lifetime to the route options. # The preference specified in the RA header should be used. - with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f: - f.write('LifetimeSec=0\n') - with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f: - f.write('LifetimeSec=0\n') + with open( + os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +LifetimeSec=0 +''') + with open( + os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +LifetimeSec=0 +''') networkctl_reload() self.check_router_preference('01', 100, 'high', 300, 'low') # Use route options with preference to configure default routes. - with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f: - f.write('LifetimeSec=1200\nPreference=low\n') - with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f: - f.write('LifetimeSec=1200\nPreference=high\n') + with open( + os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +LifetimeSec=1200 +Preference=low +''') + with open( + os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +LifetimeSec=1200 +Preference=high +''') networkctl_reload() self.check_router_preference('01', 300, 'low', 100, 'high') # Set zero lifetime again to the route options. - with open(os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8') as f: - f.write('LifetimeSec=0\n') - with open(os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8') as f: - f.write('LifetimeSec=0\n') + with open( + os.path.join(network_unit_dir, '25-veth-router-high.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +LifetimeSec=0 +''') + with open( + os.path.join(network_unit_dir, '25-veth-router-low.network'), mode='a', encoding='utf-8' + ) as f: + f.write(''' +LifetimeSec=0 +''') networkctl_reload() self.check_router_preference('01', 100, 'high', 300, 'low') def _test_ndisc_vs_static_route(self, manage_foreign_nexthops): if not manage_foreign_nexthops: copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf') - copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-static-route.network') + copy_network_unit( + '25-veth.netdev', + '25-ipv6-prefix.network', + '25-ipv6-prefix-veth-static-route.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:degraded') @@ -7060,7 +7965,7 @@ class NetworkdRATests(unittest.TestCase, Utilities): print(output) self.assertIn('via fe80::1034:56ff:fe78:9abd proto static metric 256 pref medium', output) if manage_foreign_nexthops: - self.assertRegex(output, r'default nhid [0-9]* via fe80::1034:56ff:fe78:9abd proto ra metric 256 expires [0-9]*sec pref medium') + self.assertRegex(output, r'default nhid [0-9]* via fe80::1034:56ff:fe78:9abd proto ra metric 256 expires [0-9]*sec pref medium') # fmt: skip else: self.assertNotIn('proto ra', output) @@ -7068,15 +7973,23 @@ class NetworkdRATests(unittest.TestCase, Utilities): output = check_output('ip -6 nexthop show dev veth99') print(output) if manage_foreign_nexthops: - self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abd dev veth99 scope link proto ra') + self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abd dev veth99 scope link proto ra') # fmt: skip else: self.assertEqual(output, '') # Also check if the static route is protected from RA with zero lifetime with open(os.path.join(network_unit_dir, '25-ipv6-prefix.network'), mode='a', encoding='utf-8') as f: - f.write('\n[Network]\nIPv6SendRA=no\n') - networkctl_reload() # This makes veth-peer being reconfigured, and send RA with zero lifetime - self.wait_route_dropped('veth99', r'default (nhid [0-9]* |)via fe80::1034:56ff:fe78:9abd proto ra metric 256', ipv='-6', timeout_sec=10) + f.write(''' +[Network] +IPv6SendRA=no +''') + networkctl_reload() # This makes veth-peer being reconfigured, and send RA with zero lifetime + self.wait_route_dropped( + 'veth99', + r'default (nhid [0-9]* |)via fe80::1034:56ff:fe78:9abd proto ra metric 256', + ipv='-6', + timeout_sec=10, + ) print('### ip -6 route show dev veth99 default') output = check_output('ip -6 route show dev veth99 default') @@ -7104,18 +8017,27 @@ class NetworkdRATests(unittest.TestCase, Utilities): # radvd supports captive portal since v2.20. # https://github.com/radvd-project/radvd/commit/791179a7f730decbddb2290ef0e34aa85d71b1bc - @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals") + @unittest.skipUnless( + radvd_check_config('captive-portal.conf'), + "Installed radvd doesn't support captive portals", + ) def test_captive_portal(self): - copy_network_unit('25-veth-client.netdev', - '25-veth-router-captive.netdev', - '26-bridge.netdev', - '25-veth-client-captive.network', - '25-veth-router-captive.network', - '25-veth-bridge-captive.network', - '25-bridge99.network') + copy_network_unit( + '25-veth-client.netdev', + '25-veth-router-captive.netdev', + '26-bridge.netdev', + '25-veth-client-captive.network', + '25-veth-router-captive.network', + '25-veth-bridge-captive.network', + '25-bridge99.network', + ) start_networkd() - self.wait_online('bridge99:routable', 'client-p:enslaved', - 'router-captive:degraded', 'router-captivep:enslaved') + self.wait_online( + 'bridge99:routable', + 'client-p:enslaved', + 'router-captive:degraded', + 'router-captivep:enslaved', + ) start_radvd(config_file='captive-portal.conf') networkctl_reconfigure('client') @@ -7126,31 +8048,53 @@ class NetworkdRATests(unittest.TestCase, Utilities): print(output) self.assertIn('Captive Portal: http://systemd.io', output) - @unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals") + @unittest.skipUnless( + radvd_check_config('captive-portal.conf'), + "Installed radvd doesn't support captive portals", + ) def test_invalid_captive_portal(self): def radvd_write_config(captive_portal_uri): - with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f: - f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};') + with open( + os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), + mode='w', + encoding='utf-8', + ) as f: + f.write(f''' +interface router-captive {{ + AdvSendAdvert on; + AdvCaptivePortalAPI "{captive_portal_uri}"; + prefix 2002:da8:1:99::/64 {{ + AdvOnLink on; + AdvAutonomous on; + }}; +}}; +''') captive_portal_uris = [ - "42ěščěškd ěšč ě s", - " ", - "🤔", + '42ěščěškd ěšč ě s', + ' ', + '🤔', ] - copy_network_unit('25-veth-client.netdev', - '25-veth-router-captive.netdev', - '26-bridge.netdev', - '25-veth-client-captive.network', - '25-veth-router-captive.network', - '25-veth-bridge-captive.network', - '25-bridge99.network') + copy_network_unit( + '25-veth-client.netdev', + '25-veth-router-captive.netdev', + '26-bridge.netdev', + '25-veth-client-captive.network', + '25-veth-router-captive.network', + '25-veth-bridge-captive.network', + '25-bridge99.network', + ) start_networkd() - self.wait_online('bridge99:routable', 'client-p:enslaved', - 'router-captive:degraded', 'router-captivep:enslaved') + self.wait_online( + 'bridge99:routable', + 'client-p:enslaved', + 'router-captive:degraded', + 'router-captivep:enslaved', + ) for uri in captive_portal_uris: - print(f"Captive portal: {uri}") + print(f'Captive portal: {uri}') radvd_write_config(uri) stop_radvd() start_radvd(config_file='bogus-captive-portal.conf') @@ -7162,8 +8106,8 @@ class NetworkdRATests(unittest.TestCase, Utilities): print(output) self.assertNotIn('Captive Portal:', output) -class NetworkdDHCPServerTests(unittest.TestCase, Utilities): +class NetworkdDHCPServerTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -7180,7 +8124,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): output = networkctl_status('veth-peer') print(output) - self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*") + self.assertRegex(output, 'Offered DHCP leases: 192.168.5.[0-9]*') self.assertEqual(get_dhcp_server_property('veth-peer', 'PoolSize'), 'u 50') self.assertEqual(get_dhcp_server_property('veth-peer', 'PoolOffset'), 'u 10') @@ -7210,7 +8154,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env) if 'Offered DHCP leases: 192.168.5.' in output: break - time.sleep(.2) + time.sleep(0.2) else: self.fail() @@ -7247,7 +8191,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.check_dhcp_server(persist_leases='runtime') def test_dhcp_server_null_server_address(self): - copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-client.network', + '25-dhcp-server-null-server-address.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7292,8 +8240,13 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.assertIn(f'Offered DHCP leases: {client_address}', output) def test_dhcp_server_with_uplink(self): - copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network', - '12-dummy.netdev', '25-dhcp-server-uplink.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-client.network', + '25-dhcp-server-downstream.network', + '12-dummy.netdev', + '25-dhcp-server-uplink.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7305,7 +8258,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.assertIn('NTP: 192.168.5.1', output) def test_emit_router_timezone(self): - copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-client-timezone-router.network', + '25-dhcp-server-timezone-router.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7316,7 +8273,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.assertIn('Time Zone: Europe/Berlin', output) def test_dhcp_server_static_lease_mac_by_network(self): - copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-client-static-lease.network', + '25-dhcp-server-static-lease.network', + ) copy_networkd_conf_dropin('10-dhcp-client-id-duid.conf') start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7348,9 +8309,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID') def test_dhcp_server_static_lease_hostname_simple(self): - copy_network_unit('25-veth.netdev', - '25-dhcp-client-simple-hostname.network', - '25-dhcp-server-static-hostname.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-client-simple-hostname.network', + '25-dhcp-server-static-hostname.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7360,9 +8323,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.assertEqual(data['DHCPv4Client']['Lease']['Hostname'], 'simple-host') def test_dhcp_server_static_lease_hostname_fqdn(self): - copy_network_unit('25-veth.netdev', - '25-dhcp-client-fqdn-hostname.network', - '25-dhcp-server-static-hostname.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-client-fqdn-hostname.network', + '25-dhcp-server-static-hostname.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7372,7 +8337,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): self.assertEqual(data['DHCPv4Client']['Lease']['Hostname'], 'fqdn.example.com') def test_dhcp_server_resolve_hook(self): - copy_network_unit('25-veth.netdev', '25-dhcp-client-resolve-hook.network', '25-dhcp-server-resolve-hook.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-client-resolve-hook.network', + '25-dhcp-server-resolve-hook.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7382,7 +8351,6 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities): - def setUp(self): setup_common() @@ -7390,12 +8358,14 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities): tear_down_common() def test_relay_agent(self): - copy_network_unit('25-agent-veth-client.netdev', - '25-agent-veth-server.netdev', - '25-agent-client.network', - '25-agent-server.network', - '25-agent-client-peer.network', - '25-agent-server-peer.network') + copy_network_unit( + '25-agent-veth-client.netdev', + '25-agent-veth-server.netdev', + '25-agent-client.network', + '25-agent-server.network', + '25-agent-client-peer.network', + '25-agent-server-peer.network', + ) start_networkd() self.wait_online('client:routable') @@ -7405,19 +8375,21 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities): self.assertRegex(output, r'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)') def test_relay_agent_on_bridge(self): - copy_network_unit('25-agent-bridge.netdev', - '25-agent-veth-client.netdev', - '25-agent-bridge.network', - '25-agent-bridge-port.network', - '25-agent-client.network') + copy_network_unit( + '25-agent-bridge.netdev', + '25-agent-veth-client.netdev', + '25-agent-bridge.network', + '25-agent-bridge-port.network', + '25-agent-client.network', + ) start_networkd() self.wait_online('bridge-relay:routable', 'client-peer:enslaved') # For issue #30763. self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED') -class NetworkdDHCPClientTests(unittest.TestCase, Utilities): +class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -7425,7 +8397,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): tear_down_common() def test_dhcp_client_ipv6_only(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-ipv6-only.network', + ) start_networkd() self.wait_online('veth-peer:carrier') @@ -7433,10 +8409,12 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): # information request mode # The name ipv6-only option may not be supported by older dnsmasq # start_dnsmasq('--dhcp-option=option:ipv6-only,300') - start_dnsmasq('--dhcp-option=108,00:00:02:00', - '--dhcp-option=option6:dns-server,[2600::ee]', - '--dhcp-option=option6:ntp-server,[2600::ff]', - ra_mode='ra-stateless') + start_dnsmasq( + '--dhcp-option=108,00:00:02:00', + '--dhcp-option=option6:dns-server,[2600::ee]', + '--dhcp-option=option6:ntp-server,[2600::ff]', + ra_mode='ra-stateless', + ) self.wait_online('veth99:routable', 'veth-peer:routable') # DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state. @@ -7445,7 +8423,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): output = read_link_state_file('veth99') if 'DNS=2600::ee' in output: break - time.sleep(.2) + time.sleep(0.2) # Check link state file print('## link state file') @@ -7475,9 +8453,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): # solicit mode stop_dnsmasq() - start_dnsmasq('--dhcp-option=108,00:00:02:00', - '--dhcp-option=option6:dns-server,[2600::ee]', - '--dhcp-option=option6:ntp-server,[2600::ff]') + start_dnsmasq( + '--dhcp-option=108,00:00:02:00', + '--dhcp-option=option6:dns-server,[2600::ee]', + '--dhcp-option=option6:ntp-server,[2600::ff]', + ) networkctl_reconfigure('veth99') self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7528,13 +8508,17 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check_json(networkctl_json('veth99')) # Testing without rapid commit support - with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8' + ) as f: f.write('\n[DHCPv6]\nRapidCommit=no\n') stop_dnsmasq() - start_dnsmasq('--dhcp-option=108,00:00:02:00', - '--dhcp-option=option6:dns-server,[2600::ee]', - '--dhcp-option=option6:ntp-server,[2600::ff]') + start_dnsmasq( + '--dhcp-option=108,00:00:02:00', + '--dhcp-option=option6:dns-server,[2600::ee]', + '--dhcp-option=option6:ntp-server,[2600::ff]', + ) networkctl_reload() self.wait_online('veth99:routable', 'veth-peer:routable') @@ -7581,25 +8565,29 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check_json(networkctl_json('veth99')) def test_dhcp_client_ipv6_dbus_status(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-ipv6-only.network', + ) start_networkd() self.wait_online('veth-peer:carrier') # Note that at this point the DHCPv6 client has not been started because no RA (with managed # bit set) has yet been received and the configuration does not include WithoutRA=true state = get_dhcp6_client_state('veth99') - print(f"DHCPv6 client state = {state}") + print(f'DHCPv6 client state = {state}') self.assertEqual(state, 'stopped') state = get_dhcp4_client_state('veth99') - print(f"DHCPv4 client state = {state}") + print(f'DHCPv4 client state = {state}') self.assertEqual(state, 'selecting') start_dnsmasq('--dhcp-option=108,00:00:02:00') self.wait_online('veth99:routable', 'veth-peer:routable') state = get_dhcp6_client_state('veth99') - print(f"DHCPv6 client state = {state}") + print(f'DHCPv6 client state = {state}') self.assertEqual(state, 'bound') # DHCPv4 client will stop after an DHCPOFFER message received, so we need to wait for a while. @@ -7607,9 +8595,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): state = get_dhcp4_client_state('veth99') if state == 'stopped': break - time.sleep(.2) + time.sleep(0.2) - print(f"DHCPv4 client state = {state}") + print(f'DHCPv4 client state = {state}') self.assertEqual(state, 'stopped') # restart dnsmasq to clear log @@ -7624,9 +8612,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): state = get_dhcp4_client_state('veth99') if state == 'stopped': break - time.sleep(.2) + time.sleep(0.2) - print(f"DHCPv4 client state = {state}") + print(f'DHCPv4 client state = {state}') self.assertEqual(state, 'stopped') print('## dnsmasq log') @@ -7638,7 +8626,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertNotIn('DHCPACK(veth-peer)', output) def test_dhcp_client_ipv6_only_with_custom_client_identifier(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-ipv6-only-custom-client-identifier.network', + ) start_networkd() self.wait_online('veth-peer:carrier') @@ -7661,8 +8653,13 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertIn('sent size: 0 option: 14 rapid-commit', output) def test_dhcp_client_ipv4_only(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network', - '25-sit-dhcp4.netdev', '25-sit-dhcp4.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-ipv4-only.network', + '25-sit-dhcp4.netdev', + '25-sit-dhcp4.network', + ) self.setup_nftset('addr4', 'ipv4_addr') self.setup_nftset('network4', 'ipv4_addr', 'flags interval;') @@ -7670,11 +8667,13 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): start_networkd() self.wait_online('veth-peer:carrier') - start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', - '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22', - '--dhcp-option=option:domain-search,example.com', - '--dhcp-alternate-port=67,5555', - ipv4_range='192.168.5.110,192.168.5.119') + start_dnsmasq( + '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', + '--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22', + '--dhcp-option=option:domain-search,example.com', + '--dhcp-alternate-port=67,5555', + ipv4_range='192.168.5.110,192.168.5.119', + ) self.wait_online('veth99:routable', 'veth-peer:routable', 'sit-dhcp4:carrier') self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4') @@ -7683,7 +8682,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print(output) self.assertIn('mtu 1492', output) self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output) - self.assertRegex(output, r'inet 192.168.5.11[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label') + self.assertRegex(output, r'inet 192.168.5.11[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label') # fmt: skip self.assertNotIn('2600::', output) output = check_output('ip -4 --json address show dev veth99') @@ -7758,7 +8757,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print('## tunnel') output = check_output('ip -d link show sit-dhcp4') print(output) - self.assertRegex(output, fr'sit (ip6ip )?remote any local {address1} dev veth99') + self.assertRegex(output, rf'sit (ip6ip )?remote any local {address1} dev veth99') print('## dnsmasq log') output = read_dnsmasq_log_file() @@ -7770,11 +8769,13 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): # change address range, DNS servers, and Domains stop_dnsmasq() - start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8', - '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24', - '--dhcp-option=option:domain-search,foo.example.com', - '--dhcp-alternate-port=67,5555', - ipv4_range='192.168.5.120,192.168.5.129',) + start_dnsmasq( + '--dhcp-option=option:dns-server,192.168.5.1,192.168.5.7,192.168.5.8', + '--dhcp-option=option:sip-server,192.168.5.23,192.168.5.24', + '--dhcp-option=option:domain-search,foo.example.com', + '--dhcp-alternate-port=67,5555', + ipv4_range='192.168.5.120,192.168.5.129', + ) # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120 print('Wait for the DHCP lease to be expired') @@ -7789,7 +8790,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertIn('mtu 1492', output) self.assertIn('inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99', output) self.assertNotIn(f'{address1}', output) - self.assertRegex(output, r'inet 192.168.5.12[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label') + self.assertRegex(output, r'inet 192.168.5.12[0-9]/24 metric 24 brd 192.168.5.255 scope global secondary dynamic noprefixroute test-label') # fmt: skip self.assertNotIn('2600::', output) output = check_output('ip -4 --json address show dev veth99') @@ -7865,7 +8866,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print('## tunnel') output = check_output('ip -d link show sit-dhcp4') print(output) - self.assertRegex(output, fr'sit (ip6ip )?remote any local {address2} dev veth99') + self.assertRegex(output, rf'sit (ip6ip )?remote any local {address2} dev veth99') print('## dnsmasq log') output = read_dnsmasq_log_file() @@ -7941,7 +8942,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): try: output = read_dnsmasq_log_file() except FileNotFoundError: - output = "" + output = '' if 'DHCPRELEASE' in output: success = True break @@ -8007,48 +9008,71 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.fail('Timed out waiting for DHCPRELEASE in dnsmasq log (on bringing down interface)') def test_dhcp_client_ipv4_dbus_status(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-ipv4-only.network', + ) start_networkd() self.wait_online('veth-peer:carrier') state = get_dhcp4_client_state('veth99') - print(f"State = {state}") + print(f'State = {state}') self.assertEqual(state, 'rebooting') - start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', - '--dhcp-option=option:domain-search,example.com', - '--dhcp-alternate-port=67,5555', - ipv4_range='192.168.5.110,192.168.5.119') + start_dnsmasq( + '--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', + '--dhcp-option=option:domain-search,example.com', + '--dhcp-alternate-port=67,5555', + ipv4_range='192.168.5.110,192.168.5.119', + ) self.wait_online('veth99:routable', 'veth-peer:routable') self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4') state = get_dhcp4_client_state('veth99') - print(f"State = {state}") + print(f'State = {state}') self.assertEqual(state, 'bound') def test_dhcp_client_allow_list(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False) + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-allow-list.network', + copy_dropins=False, + ) start_networkd() self.wait_online('veth-peer:carrier') since = datetime.datetime.now() start_dnsmasq() - self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since) + self.check_networkd_log( + 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', + since=since, + ) copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf') since = datetime.datetime.now() networkctl_reload() - self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since) + self.check_networkd_log( + 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', + since=since, + ) copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf') since = datetime.datetime.now() networkctl_reload() - self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since) + self.check_networkd_log( + 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', + since=since, + ) - @unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support") + @unittest.skipUnless( + '--dhcp-rapid-commit' in run('dnsmasq --help').stdout, + reason='dnsmasq is missing dhcp-rapid-commit support', + ) def test_dhcp_client_rapid_commit(self): copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network') start_networkd() @@ -8059,7 +9083,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4') state = get_dhcp4_client_state('veth99') - print(f"DHCPv4 client state = {state}") + print(f'DHCPv4 client state = {state}') self.assertEqual(state, 'bound') output = read_dnsmasq_log_file() @@ -8075,7 +9099,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertRegex(output, r'inet 192.168.5.[0-9]*/24') state = get_dhcp4_client_state('veth99') - print(f"DHCPv4 client state = {state}") + print(f'DHCPv4 client state = {state}') self.assertEqual(state, 'bound') if check_log: @@ -8098,7 +9122,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): networkctl_reload() self.check_bootp_client(check_log=True) - with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8' + ) as f: f.write('[DHCPv4]\nBOOTP=no\n') networkctl_reload() @@ -8113,7 +9139,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertIn('DHCPREQUEST(veth-peer)', output) self.assertIn('DHCPACK(veth-peer)', output) - with open(os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-bootp-client.network'), mode='a', encoding='utf-8' + ) as f: f.write('[DHCPv4]\nBOOTP=yes\n') since = datetime.datetime.now() @@ -8125,30 +9153,36 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.check_networkd_log('veth99: DHCPv4 client: RELEASE', since=since) def test_dhcp_client_ipv6_only_mode_without_ipv6_connectivity(self): - copy_network_unit('25-veth.netdev', - '25-dhcp-server-ipv6-only-mode.network', - '25-dhcp-client-ipv6-only-mode.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-ipv6-only-mode.network', + '25-dhcp-client-ipv6-only-mode.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s') self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4') state = get_dhcp4_client_state('veth99') - print(f"State = {state}") + print(f'State = {state}') self.assertEqual(state, 'bound') def test_dhcp_client_ipv4_use_routes_gateway(self): first = True - for (routes, gateway, dns_and_ntp_routes, classless) in itertools.product([True, False], repeat=4): + for routes, gateway, dns_and_ntp_routes, classless in itertools.product([True, False], repeat=4): if first: first = False else: self.tearDown() + # fmt: off print(f'### test_dhcp_client_ipv4_use_routes_gateway(routes={routes}, gateway={gateway}, dns_and_ntp_routes={dns_and_ntp_routes}, classless={classless})') with self.subTest(routes=routes, gateway=gateway, dns_and_ntp_routes=dns_and_ntp_routes, classless=classless): self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dns_and_ntp_routes, classless) + # fmt: on - def _test_dhcp_client_ipv4_use_routes_gateway(self, use_routes, use_gateway, dns_and_ntp_routes, classless): + def _test_dhcp_client_ipv4_use_routes_gateway( + self, use_routes, use_gateway, dns_and_ntp_routes, classless + ): testunit = '25-dhcp-client-ipv4-use-routes-use-gateway.network' testunits = ['25-veth.netdev', '25-dhcp-server-veth-peer.network', testunit] testunits.append(f'{testunit}.d/use-routes-{use_routes}.conf') @@ -8161,7 +9195,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): additional_options = [ '--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8', '--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9', - '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3' + '--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3', ] if classless: additional_options += [ @@ -8174,6 +9208,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print(output) # Check UseRoutes= + # fmt: off if use_routes: if classless: self.assertRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024') @@ -8195,20 +9230,22 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024') self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') + # fmt: on # Check UseGateway= if use_gateway and (not classless or not use_routes): self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024') else: - self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024') + self.assertNotRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024') # fmt: skip # Check route to gateway if (use_gateway or dns_and_ntp_routes) and (not classless or not use_routes): self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') else: - self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') + self.assertNotRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') # fmt: skip # Check RoutesToDNS= and RoutesToNTP= + # fmt: off if dns_and_ntp_routes: self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') @@ -8227,11 +9264,16 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertNotRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024') self.assertNotRegex(output, r'8.8.8.8 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024') self.assertNotRegex(output, r'9.9.9.9 via 192.168.5.[0-9]* proto dhcp src 192.168.5.[0-9]* metric 1024') + # fmt: on check_json(networkctl_json()) def test_dhcp_client_settings_anonymize(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-anonymize.network', + ) start_networkd() self.wait_online('veth-peer:carrier') start_dnsmasq() @@ -8245,9 +9287,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertNotIn('26:mtu', output) def test_dhcp_keep_configuration_dynamic(self): - copy_network_unit('25-veth.netdev', - '25-dhcp-server-veth-peer.network', - '25-dhcp-client-keep-configuration-dynamic.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-keep-configuration-dynamic.network', + ) start_networkd() self.wait_online('veth-peer:carrier') start_dnsmasq() @@ -8255,8 +9299,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): output = check_output('ip address show dev veth99 scope global') print(output) - self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *' - 'valid_lft forever preferred_lft forever') + self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *valid_lft forever preferred_lft forever') # fmt: skip # Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease. stop_dnsmasq() @@ -8268,18 +9311,20 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): # The lease address should be kept after the lease expired output = check_output('ip address show dev veth99 scope global') print(output) - self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *' - 'valid_lft forever preferred_lft forever') + self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *valid_lft forever preferred_lft forever') # fmt: skip stop_networkd() # The lease address should be kept after networkd stopped output = check_output('ip address show dev veth99 scope global') print(output) - self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *' - 'valid_lft forever preferred_lft forever') + self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *valid_lft forever preferred_lft forever') # fmt: skip - with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dynamic.network'), mode='a', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dynamic.network'), + mode='a', + encoding='utf-8', + ) as f: f.write('[Network]\nDHCP=no\n') start_networkd() @@ -8288,13 +9333,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): # Still the lease address should be kept after networkd restarted output = check_output('ip address show dev veth99 scope global') print(output) - self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *' - 'valid_lft forever preferred_lft forever') + self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *valid_lft forever preferred_lft forever') # fmt: skip def test_dhcp_keep_configuration_dynamic_on_stop(self): - copy_network_unit('25-veth.netdev', - '25-dhcp-server-veth-peer.network', - '25-dhcp-client-keep-configuration-dynamic-on-stop.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-keep-configuration-dynamic-on-stop.network', + ) start_networkd() self.wait_online('veth-peer:carrier') start_dnsmasq() @@ -8302,14 +9348,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): output = check_output('ip address show dev veth99 scope global') print(output) - self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') + self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') # fmt: skip stop_dnsmasq() stop_networkd() output = check_output('ip address show dev veth99 scope global') print(output) - self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') + self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') # fmt: skip start_networkd() self.wait_online('veth-peer:routable') @@ -8326,13 +9372,28 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.wait_online('veth99:routable', 'veth-peer:routable') # link become 'routable' when at least one protocol provide an valid address. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) output = check_output('ip address show dev veth99 scope global') ipv4_address = re.search(r'192.168.5.[0-9]*/24', output).group() ipv6_address = re.search(r'2600::[0-9a-f:]*/128', output).group() - static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address, 'Address=' + ipv6_address]) + static_network = f''' +[Match] +Name=veth99 +[Network] +IPv6AcceptRA=no +Address={ipv4_address} +Address={ipv6_address} +''' print(static_network) remove_network_unit('25-dhcp-client.network') @@ -8345,26 +9406,37 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): output = check_output('ip -4 address show dev veth99 scope global') print(output) - self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *' - 'valid_lft forever preferred_lft forever') + self.assertRegex(output, f'inet {ipv4_address} brd 192.168.5.255 scope global veth99\n *valid_lft forever preferred_lft forever') # fmt: skip output = check_output('ip -6 address show dev veth99 scope global') print(output) - self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *' - 'valid_lft forever preferred_lft forever') + self.assertRegex(output, f'inet6 {ipv6_address} scope global *\n *valid_lft forever preferred_lft forever') # fmt: skip @expectedFailureIfModuleIsNotAvailable('vrf') def test_dhcp_client_vrf(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network', - '25-vrf.netdev', '25-vrf.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-vrf.network', + '25-vrf.netdev', + '25-vrf.network', + ) start_networkd() self.wait_online('veth-peer:carrier') start_dnsmasq() self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier') # link become 'routable' when at least one protocol provide an valid address. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) print('## ip -d link show dev vrf99') output = check_output('ip -d link show dev vrf99') @@ -8374,16 +9446,20 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): print('## ip address show vrf vrf99') output = check_output('ip address show vrf vrf99') print(output) + # fmt: off self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)') self.assertRegex(output, 'inet6 .* scope link') + # fmt: on print('## ip address show dev veth99') output = check_output('ip address show dev veth99') print(output) + # fmt: off self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99') self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)') self.assertRegex(output, 'inet6 .* scope link') + # fmt: on print('## ip route show vrf vrf99') output = check_output('ip route show vrf vrf99') @@ -8398,8 +9474,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertEqual(output, '') def test_dhcp_client_gateway_onlink_implicit(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', - '25-dhcp-client-gateway-onlink-implicit.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-gateway-onlink-implicit.network', + ) start_networkd() self.wait_online('veth-peer:carrier') start_dnsmasq() @@ -8417,8 +9496,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertRegex(output, 'onlink') def test_dhcp_client_with_ipv4ll(self): - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', - '25-dhcp-client-with-ipv4ll.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client-with-ipv4ll.network', + ) start_networkd() # we need to increase timeout above default, as this will need to wait for # systemd-networkd to get the dhcpv4 transient failure event @@ -8431,20 +9513,39 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): start_dnsmasq() print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped') - self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4') - self.wait_address_dropped('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4') + self.wait_address( + 'veth99', + r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', + ipv='-4', + ) + self.wait_address_dropped( + 'veth99', + r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link', + scope='link', + ipv='-4', + ) self.wait_online('veth99:routable') output = check_output('ip -4 address show dev veth99') print(output) - self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99') + self.assertRegex(output, r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic veth99') # fmt: skip self.assertNotIn('169.254.', output) self.assertNotIn('scope link', output) stop_dnsmasq() print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired') - self.wait_address_dropped('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4', timeout_sec=130) - self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4') + self.wait_address_dropped( + 'veth99', + r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', + ipv='-4', + timeout_sec=130, + ) + self.wait_address( + 'veth99', + r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', + scope='link', + ipv='-4', + ) output = check_output('ip -4 address show dev veth99') print(output) @@ -8454,7 +9555,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_use_dns(self): def check(self, ipv4, ipv6, needs_reconfigure=False): os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True) - with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), + mode='w', + encoding='utf-8', + ) as f: f.write('[DHCPv4]\nUseDNS=') f.write('yes' if ipv4 else 'no') f.write('\n[DHCPv6]\nUseDNS=') @@ -8466,9 +9571,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): networkctl_reconfigure('veth99') self.wait_online('veth99:routable') - # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + # link becomes 'routable' when at least one protocol provide an valid address. + # Hence, we need to explicitly wait for both addresses. + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) # make resolved re-read the link state file resolvectl('revert', 'veth99') @@ -8486,12 +9600,19 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client.network', + copy_dropins=False, + ) start_networkd() self.wait_online('veth-peer:carrier') - start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1', - '--dhcp-option=option6:dns-server,[2600::1]') + start_dnsmasq( + '--dhcp-option=option:dns-server,192.168.5.1', + '--dhcp-option=option6:dns-server,[2600::1]', + ) check(self, True, True) check(self, True, False) @@ -8501,7 +9622,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_default_use_domains(self): def check(self, common, ipv4, ipv6): mkdir_p(networkd_conf_dropin_dir) - with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f: + with open( + os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), + mode='w', + encoding='utf-8', + ) as f: f.write('[Network]\nUseDomains=') f.write('yes\n' if common else 'no\n') f.write('[DHCPv4]\nUseDomains=') @@ -8511,16 +9636,27 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): restart_networkd() self.wait_online('veth-peer:carrier') - start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1', - '--dhcp-option=option6:dns-server,[2600::1]', - '--dhcp-option=option:domain-search,example.com', - '--dhcp-option=option6:domain-search,example.com') + start_dnsmasq( + '--dhcp-option=option:dns-server,192.168.5.1', + '--dhcp-option=option6:dns-server,[2600::1]', + '--dhcp-option=option:domain-search,example.com', + '--dhcp-option=option6:domain-search,example.com', + ) self.wait_online('veth99:routable') - # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + # link becomes 'routable' when at least one protocol provide an valid address. + # Hence, we need to explicitly wait for both addresses. + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) for _ in range(20): output = resolvectl('domain', 'veth99') @@ -8539,7 +9675,12 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): stop_dnsmasq() remove_networkd_conf_dropin('default_use_domains.conf') - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client.network', + copy_dropins=False, + ) check(self, True, False, False) check(self, False, True, True) check(self, False, True, False) @@ -8549,7 +9690,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_use_dnr(self): def check(self, ipv4, ipv6, needs_reconfigure=False): os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True) - with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), + mode='w', + encoding='utf-8', + ) as f: f.write('[DHCPv4]\nUseDNS=') f.write('yes' if ipv4 else 'no') f.write('\n[DHCPv6]\nUseDNS=') @@ -8561,9 +9706,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): networkctl_reconfigure('veth99') self.wait_online('veth99:routable') - # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + # link becomes 'routable' when at least one protocol provide an valid address. + # Hence, we need to explicitly wait for both addresses. + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) # make resolved re-read the link state file resolvectl('revert', 'veth99') @@ -8582,16 +9736,31 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client.network', + copy_dropins=False, + ) start_networkd() self.wait_online('veth-peer:carrier') - dnr_v4 = dnr_v4_instance_data(adn = "dns.google", addrs = ["8.8.8.8", "8.8.4.4"]) - dnr_v4 += dnr_v4_instance_data(adn = "homer.simpson", addrs = ["0.7.4.2"], alpns = ("dot","h2","h3"), dohpath = "/springfield{?dns}") - dnr_v6 = dnr_v6_instance_data(adn = "dns.google", addrs = ["2001:4860:4860::8888", "2001:4860:4860::8844"]) - masq = lambda bs: ':'.join(f"{b:02x}" for b in bs) - start_dnsmasq(f'--dhcp-option=162,{masq(dnr_v4)}', - f'--dhcp-option=option6:144,{masq(dnr_v6)}') + dnr_v4 = dnr_v4_instance_data( + adn='dns.google', + addrs=['8.8.8.8', '8.8.4.4'], + ) + dnr_v4 += dnr_v4_instance_data( + adn='homer.simpson', + addrs=['0.7.4.2'], + alpns=('dot', 'h2', 'h3'), + dohpath='/springfield{?dns}', + ) + dnr_v6 = dnr_v6_instance_data( + adn='dns.google', + addrs=['2001:4860:4860::8888', '2001:4860:4860::8844'], + ) + masq = lambda bs: ':'.join(f'{b:02x}' for b in bs) + start_dnsmasq(f'--dhcp-option=162,{masq(dnr_v4)}', f'--dhcp-option=option6:144,{masq(dnr_v6)}') check(self, True, True) check(self, True, False) @@ -8601,7 +9770,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_use_sip(self): def check(self, ipv4, ipv6, needs_reconfigure=False): os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True) - with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), + mode='w', + encoding='utf-8', + ) as f: f.write('[DHCPv4]\nUseSIP=') f.write('yes' if ipv4 else 'no') f.write('\n[DHCPv6]\nUseSIP=') @@ -8612,9 +9785,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): networkctl_reconfigure('veth99') self.wait_online('veth99:routable') - # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + # link becomes 'routable' when at least one protocol provide an valid address. + # Hence, we need to explicitly wait for both addresses. + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) output = networkctl_status('veth99') print(output) @@ -8631,13 +9813,20 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client.network', + copy_dropins=False, + ) start_networkd() self.wait_online('veth-peer:carrier') - start_dnsmasq('--dhcp-option=option:sip-server,192.168.5.1', - '--dhcp-option=option6:sip-server,[2600::1]', - '--dhcp-option=option6:sip-server-domain,foo.example.com') + start_dnsmasq( + '--dhcp-option=option:sip-server,192.168.5.1', + '--dhcp-option=option6:sip-server,[2600::1]', + '--dhcp-option=option6:sip-server-domain,foo.example.com', + ) check(self, True, True) check(self, True, False) @@ -8647,7 +9836,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_use_captive_portal(self): def check(self, ipv4, ipv6, needs_reconfigure=False): os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True) - with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), + mode='w', + encoding='utf-8', + ) as f: f.write('[DHCPv4]\nUseCaptivePortal=') f.write('yes' if ipv4 else 'no') f.write('\n[DHCPv6]\nUseCaptivePortal=') @@ -8659,9 +9852,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): networkctl_reconfigure('veth99') self.wait_online('veth99:routable') - # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + # link becomes 'routable' when at least one protocol provide an valid address. + # Hence, we need to explicitly wait for both addresses. + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) output = networkctl_status('veth99') print(output) @@ -8672,12 +9874,19 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client.network', + copy_dropins=False, + ) start_networkd() self.wait_online('veth-peer:carrier') - start_dnsmasq('--dhcp-option=114,http://systemd.io', - '--dhcp-option=option6:103,http://systemd.io') + start_dnsmasq( + '--dhcp-option=114,http://systemd.io', + '--dhcp-option=option6:103,http://systemd.io', + ) check(self, True, True) check(self, True, False) @@ -8687,7 +9896,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): def test_dhcp_client_reject_captive_portal(self): def check(self, ipv4, ipv6): os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True) - with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f: + with open( + os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), + mode='w', + encoding='utf-8', + ) as f: f.write('[DHCPv4]\nUseCaptivePortal=') f.write('yes' if ipv4 else 'no') f.write('\n[DHCPv6]\nUseCaptivePortal=') @@ -8697,9 +9910,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): networkctl_reload() self.wait_online('veth99:routable') - # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses. - self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4') - self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6') + # link becomes 'routable' when at least one protocol provide an valid address. + # Hence, we need to explicitly wait for both addresses. + self.wait_address( + 'veth99', + r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', + ipv='-4', + ) + self.wait_address( + 'veth99', + r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', + ipv='-6', + ) output = networkctl_status('veth99') print(output) @@ -8708,20 +9930,27 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): check_json(networkctl_json()) - copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False) + copy_network_unit( + '25-veth.netdev', + '25-dhcp-server-veth-peer.network', + '25-dhcp-client.network', + copy_dropins=False, + ) start_networkd() self.wait_online('veth-peer:carrier') - start_dnsmasq('--dhcp-option=114,http://|invalid/url', - '--dhcp-option=option6:103,http://|invalid/url') + start_dnsmasq( + '--dhcp-option=114,http://|invalid/url', + '--dhcp-option=option6:103,http://|invalid/url', + ) check(self, True, True) check(self, True, False) check(self, False, True) check(self, False, False) -class NetworkdDHCPPDTests(unittest.TestCase, Utilities): +class NetworkdDHCPPDTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -8748,10 +9977,14 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.assertGreater(prefixInfo[0]['PreferredLifetimeUSec'], 0) self.assertGreater(prefixInfo[0]['ValidLifetimeUSec'], 0) - @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available") + @unittest.skipUnless(shutil.which('dhcpd'), reason='dhcpd is not available') def test_dhcp6pd_no_address(self): # For issue #29979. - copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp6pd-server.network', + '25-dhcp6pd-upstream-no-address.network', + ) start_networkd() self.wait_online('veth-peer:routable') @@ -8770,11 +10003,15 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.check_dhcp6_prefix('veth99') - @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available") + @unittest.skipUnless(shutil.which('dhcpd'), reason='dhcpd is not available') def test_dhcp6pd_no_assign(self): # Similar to test_dhcp6pd_no_assign(), but in this case UseAddress=yes (default), # However, the server does not provide IA_NA. For issue #31349. - copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp6pd-server.network', + '25-dhcp6pd-upstream-no-assign.network', + ) start_networkd() self.wait_online('veth-peer:routable') @@ -8793,21 +10030,40 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.check_dhcp6_prefix('veth99') - @unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available") + @unittest.skipUnless(shutil.which('dhcpd'), reason='dhcpd is not available') def test_dhcp6pd(self): - copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream.network', - '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network', - '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network', - '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network', - '25-dhcp-pd-downstream-dummy97.network', - '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network', - '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp6pd-server.network', + '25-dhcp6pd-upstream.network', + '25-veth-downstream-veth97.netdev', + '25-dhcp-pd-downstream-veth97.network', + '25-dhcp-pd-downstream-veth97-peer.network', + '25-veth-downstream-veth98.netdev', + '25-dhcp-pd-downstream-veth98.network', + '25-dhcp-pd-downstream-veth98-peer.network', + '11-dummy.netdev', + '25-dhcp-pd-downstream-test1.network', + '25-dhcp-pd-downstream-dummy97.network', + '12-dummy.netdev', + '25-dhcp-pd-downstream-dummy98.network', + '13-dummy.netdev', + '25-dhcp-pd-downstream-dummy99.network', + ) start_networkd() self.wait_online('veth-peer:routable') start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6') - self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', - 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable') + self.wait_online( + 'veth99:routable', + 'test1:routable', + 'dummy98:routable', + 'dummy99:degraded', + 'veth97:routable', + 'veth97-peer:routable', + 'veth98:routable', + 'veth98-peer:routable', + ) self.setup_nftset('addr6', 'ipv6_addr') self.setup_nftset('network6', 'ipv6_addr', 'flags interval;') @@ -8833,31 +10089,45 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): print('### ip -6 address show dev veth99 scope global') output = check_output('ip -6 address show dev veth99 scope global') print(output) + # fmt: off # IA_NA self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)') # address in IA_PD (Token=static) self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic') # address in IA_PD (Token=eui64) self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic') + # fmt: on # address in IA_PD (temporary) # Note that the temporary addresses may appear after the link enters configured state - self.wait_address('veth99', 'inet6 3ffe:501:ffff:[2-9a-f]10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth99', + 'inet6 3ffe:501:ffff:[2-9a-f]10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev test1 scope global') output = check_output('ip -6 address show dev test1 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('test1', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'test1', + 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev dummy98 scope global') output = check_output('ip -6 address show dev dummy98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'dummy98', + 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev dummy99 scope global') output = check_output('ip -6 address show dev dummy99 scope global') @@ -8869,41 +10139,57 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev veth97 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (Token=eui64) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('veth97', 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth97', + 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev veth97-peer scope global') output = check_output('ip -6 address show dev veth97-peer scope global') print(output) # NDisc address (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (Token=eui64) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (temporary) - self.wait_address('veth97-peer', 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth97-peer', + 'inet6 3ffe:501:ffff:[2-9a-f]08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev veth98 scope global') output = check_output('ip -6 address show dev veth98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (Token=eui64) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('veth98', 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth98', + 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev veth98-peer scope global') output = check_output('ip -6 address show dev veth98-peer scope global') print(output) # NDisc address (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (Token=eui64) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (temporary) - self.wait_address('veth98-peer', 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth98-peer', + 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 route show type unreachable') output = check_output('ip -6 route show type unreachable') @@ -8958,9 +10244,13 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev dummy97 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('dummy97', 'inet6 3ffe:501:ffff:[2-9a-f]01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'dummy97', + 'inet6 3ffe:501:ffff:[2-9a-f]01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 route show dev dummy97') output = check_output('ip -6 route show dev dummy97') @@ -8975,9 +10265,13 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev dummy98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'dummy98', + 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev dummy99 scope global') output = check_output('ip -6 address show dev dummy99 scope global') @@ -9023,34 +10317,46 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): print('### ip -4 address show dev veth99 scope global') output = check_output('ip -4 address show dev veth99 scope global') print(output) - self.assertRegex(output, fr'inet {address_prefix}[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99') + self.assertRegex(output, rf'inet {address_prefix}[0-9]*/8 (metric 1024 |)brd 10.255.255.255 scope global dynamic veth99') # fmt: skip print('### ip -6 address show dev veth99 scope global') output = check_output('ip -6 address show dev veth99 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (Token=eui64) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) # Note that the temporary addresses may appear after the link enters configured state - self.wait_address('veth99', 'inet6 2001:db8:6464:[0-9a-f]+10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth99', + 'inet6 2001:db8:6464:[0-9a-f]+10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev test1 scope global') output = check_output('ip -6 address show dev test1 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('test1', 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'test1', + 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev dummy98 scope global') output = check_output('ip -6 address show dev dummy98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'dummy98', + 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev dummy99 scope global') output = check_output('ip -6 address show dev dummy99 scope global') @@ -9062,41 +10368,57 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev veth97 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (Token=eui64) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9ace/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('veth97', 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth97', + 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev veth97-peer scope global') output = check_output('ip -6 address show dev veth97-peer scope global') print(output) # NDisc address (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (Token=eui64) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+08:1034:56ff:fe78:9acf/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (temporary) - self.wait_address('veth97-peer', 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth97-peer', + 'inet6 2001:db8:6464:[0-9a-f]+08:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev veth98 scope global') output = check_output('ip -6 address show dev veth98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (Token=eui64) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('veth98', 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth98', + 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 address show dev veth98-peer scope global') output = check_output('ip -6 address show dev veth98-peer scope global') print(output) # NDisc address (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (Token=eui64) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # NDisc address (temporary) - self.wait_address('veth98-peer', 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'veth98-peer', + 'inet6 2001:db8:6464:[0-9a-f]+09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 route show type unreachable') output = check_output('ip -6 route show type unreachable') @@ -9147,9 +10469,13 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev dummy97 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+01:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # fmt: skip # address in IA_PD (temporary) - self.wait_address('dummy97', 'inet6 2001:db8:6464:[0-9a-f]+01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address( + 'dummy97', + 'inet6 2001:db8:6464:[0-9a-f]+01:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', + ipv='-6', + ) print('### ip -6 route show dev dummy97') output = check_output('ip -6 route show dev dummy97') @@ -9168,8 +10494,8 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): print(f'### ip -6 address show dev {tunnel_name}') output = check_output(f'ip -6 address show dev {tunnel_name}') print(output) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic') - self.assertRegex(output, fr'inet6 ::{address_prefix_re}/96 scope global') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic') # fmt: skip + self.assertRegex(output, rf'inet6 ::{address_prefix_re}/96 scope global') print(f'### ip -6 route show dev {tunnel_name}') output = check_output(f'ip -6 route show dev {tunnel_name}') @@ -9181,7 +10507,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 route show default') print(output) self.assertIn('default', output) - self.assertRegex(output, fr'via ::{border_router} dev {tunnel_name}') + self.assertRegex(output, rf'via ::{border_router} dev {tunnel_name}') def test_dhcp4_6rd(self): def get_dhcp_6rd_prefix(link): @@ -9198,14 +10524,25 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): return prefixInfo - copy_network_unit('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network', - '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network', - '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network', - '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network', - '25-dhcp-pd-downstream-dummy97.network', - '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network', - '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network', - '80-6rd-tunnel.network') + copy_network_unit( + '25-veth.netdev', + '25-dhcp4-6rd-server.network', + '25-dhcp4-6rd-upstream.network', + '25-veth-downstream-veth97.netdev', + '25-dhcp-pd-downstream-veth97.network', + '25-dhcp-pd-downstream-veth97-peer.network', + '25-veth-downstream-veth98.netdev', + '25-dhcp-pd-downstream-veth98.network', + '25-dhcp-pd-downstream-veth98-peer.network', + '11-dummy.netdev', + '25-dhcp-pd-downstream-test1.network', + '25-dhcp-pd-downstream-dummy97.network', + '12-dummy.netdev', + '25-dhcp-pd-downstream-dummy98.network', + '13-dummy.netdev', + '25-dhcp-pd-downstream-dummy99.network', + '80-6rd-tunnel.network', + ) start_networkd() self.wait_online('veth-peer:routable') @@ -9214,19 +10551,29 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): # 6rd-prefix: 2001:db8::/32 # br-address: 10.0.0.1 - start_dnsmasq('--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:01', - ipv4_range='10.100.100.100,10.100.100.200', - ipv4_router='10.0.0.1') - self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', - 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable') + start_dnsmasq( + '--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:01', + ipv4_range='10.100.100.100,10.100.100.200', + ipv4_router='10.0.0.1', + ) + self.wait_online( + 'veth99:routable', + 'test1:routable', + 'dummy98:routable', + 'dummy99:degraded', + 'veth97:routable', + 'veth97-peer:routable', + 'veth98:routable', + 'veth98-peer:routable', + ) # Check the DBus interface for assigned prefix information prefixInfo = get_dhcp_6rd_prefix('veth99') - self.assertEqual(prefixInfo['Prefix'], [32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,0]) # 2001:db8:: + self.assertEqual(prefixInfo['Prefix'], [32, 1, 13, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # 2001:db8:: # fmt: skip self.assertEqual(prefixInfo['PrefixLength'], 32) self.assertEqual(prefixInfo['IPv4MaskLength'], 8) - self.assertEqual(prefixInfo['BorderRouters'], [[10,0,0,1]]) + self.assertEqual(prefixInfo['BorderRouters'], [[10, 0, 0, 1]]) # Test case for a downstream which appears later check_output('ip link add dummy97 type dummy') @@ -9241,30 +10588,56 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.wait_online(f'{tunnel_name}:routable') - self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '(10.100.100.1[0-9][0-9]|a64:64[6-9a-c][0-9a-f])', '(10.0.0.1|a00:1)') + self.verify_dhcp4_6rd( + tunnel_name, + '10.100.100.1', + '(10.100.100.1[0-9][0-9]|a64:64[6-9a-c][0-9a-f])', + '(10.0.0.1|a00:1)', + ) # Test case for reconfigure networkctl_reconfigure('dummy98', 'dummy99') self.wait_online('dummy98:routable', 'dummy99:degraded') - self.verify_dhcp4_6rd(tunnel_name, '10.100.100.1', '(10.100.100.1[0-9][0-9]|a64:64[6-9a-c][0-9a-f])', '(10.0.0.1|a00:1)') + self.verify_dhcp4_6rd( + tunnel_name, + '10.100.100.1', + '(10.100.100.1[0-9][0-9]|a64:64[6-9a-c][0-9a-f])', + '(10.0.0.1|a00:1)', + ) # Change the address range and (border) router, then if check the same tunnel is reused. stop_dnsmasq() - start_dnsmasq('--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:02', - ipv4_range='10.100.100.200,10.100.100.250', - ipv4_router='10.0.0.2') + start_dnsmasq( + '--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:02', + ipv4_range='10.100.100.200,10.100.100.250', + ipv4_router='10.0.0.2', + ) print('Wait for the DHCP lease to be renewed/rebind') time.sleep(120) - self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded', - 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable') + self.wait_online( + 'veth99:routable', + 'test1:routable', + 'dummy97:routable', + 'dummy98:routable', + 'dummy99:degraded', + 'veth97:routable', + 'veth97-peer:routable', + 'veth98:routable', + 'veth98-peer:routable', + ) + + self.verify_dhcp4_6rd( + tunnel_name, + '10.100.100.2', + '(10.100.100.2[0-5][0-9]|a64:64[c-f][0-9a-f])', + '(10.0.0.2|a00:2)', + ) - self.verify_dhcp4_6rd(tunnel_name, '10.100.100.2', '(10.100.100.2[0-5][0-9]|a64:64[c-f][0-9a-f])', '(10.0.0.2|a00:2)') class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): - def setUp(self): setup_common() @@ -9272,8 +10645,13 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): tear_down_common() def test_ipv6_route_prefix(self): - copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network', - '12-dummy.netdev', '25-ipv6ra-uplink.network') + copy_network_unit( + '25-veth.netdev', + '25-ipv6ra-prefix-client.network', + '25-ipv6ra-prefix.network', + '12-dummy.netdev', + '25-ipv6ra-uplink.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable') @@ -9298,7 +10676,7 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): print('### ip -6 nexthop show dev veth-peer') output = check_output('ip -6 nexthop show dev veth-peer') print(output) - self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra') + self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra') # fmt: skip print('### ip -6 address show dev veth99') output = check_output('ip -6 address show dev veth99') @@ -9331,8 +10709,13 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): self.assertEqual(prefix_length, 96) def test_ipv6_route_prefix_deny_list(self): - copy_network_unit('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network', - '12-dummy.netdev', '25-ipv6ra-uplink.network') + copy_network_unit( + '25-veth.netdev', + '25-ipv6ra-prefix-client-deny-list.network', + '25-ipv6ra-prefix.network', + '12-dummy.netdev', + '25-ipv6ra-uplink.network', + ) start_networkd() self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable') @@ -9354,7 +10737,7 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): print('### ip -6 nexthop show dev veth-peer') output = check_output('ip -6 nexthop show dev veth-peer') print(output) - self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra') + self.assertRegex(output, r'id [0-9]* via fe80::1034:56ff:fe78:9abc dev veth-peer scope link proto ra') # fmt: skip print('### ip -6 address show dev veth99') output = check_output('ip -6 address show dev veth99') @@ -9370,8 +10753,8 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): print(output) self.assertIn('example.com', output) -class NetworkdMTUTests(unittest.TestCase, Utilities): +class NetworkdMTUTests(unittest.TestCase, Utilities): def setUp(self): setup_common() @@ -9398,7 +10781,7 @@ class NetworkdMTUTests(unittest.TestCase, Utilities): self.reset_check_mtu(mtu, ipv6_mtu) def reset_check_mtu(self, mtu, ipv6_mtu=None): - ''' test setting mtu/ipv6_mtu with interface already up ''' + """test setting mtu/ipv6_mtu with interface already up""" stop_networkd() # note - changing the device mtu resets the ipv6 mtu @@ -9424,39 +10807,46 @@ class NetworkdMTUTests(unittest.TestCase, Utilities): self.check_mtu('1600', reset=False) def test_ipv6_mtu(self): - ''' set ipv6 mtu without setting device mtu ''' + """set ipv6 mtu without setting device mtu""" copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf') self.check_mtu('1500', '1400') def test_ipv6_mtu_toolarge(self): - ''' try set ipv6 mtu over device mtu (it shouldn't work) ''' + """try set ipv6 mtu over device mtu (it shouldn't work)""" copy_network_unit('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf') self.check_mtu('1500', '1500') def test_mtu_network_ipv6_mtu(self): - ''' set ipv6 mtu and set device mtu via network file ''' - copy_network_unit('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf') + """set ipv6 mtu and set device mtu via network file""" + copy_network_unit( + '12-dummy.netdev', + '12-dummy.network.d/mtu.conf', + '12-dummy.network.d/ipv6-mtu-1550.conf', + ) self.check_mtu('1600', '1550') def test_mtu_netdev_ipv6_mtu(self): - ''' set ipv6 mtu and set device mtu via netdev file ''' + """set ipv6 mtu and set device mtu via netdev file""" copy_network_unit('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf') self.check_mtu('1600', '1550', reset=False) def test_mtu_link_ipv6_mtu(self): - ''' set ipv6 mtu and set device mtu via link file ''' + """set ipv6 mtu and set device mtu via link file""" copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf') self.check_mtu('1600', '1550', reset=False) -class NetworkdSysctlTest(unittest.TestCase, Utilities): +class NetworkdSysctlTest(unittest.TestCase, Utilities): def setUp(self): setup_common() def tearDown(self): tear_down_common() - @unittest.skipUnless(compare_kernel_version("6.12"), reason="On kernels <= 6.12, bpf_current_task_under_cgroup() isn't available for program types BPF_PROG_TYPE_CGROUP_SYSCTL") + @unittest.skipUnless( + compare_kernel_version('6.12'), + reason='On kernels <= 6.12, bpf_current_task_under_cgroup() is not available for program types BPF_PROG_TYPE_CGROUP_SYSCTL', + ) def test_sysctl_monitor(self): copy_network_unit('12-dummy.network', '12-dummy.netdev', '12-dummy.link') start_networkd() @@ -9473,17 +10863,19 @@ class NetworkdSysctlTest(unittest.TestCase, Utilities): call('sysctl -w net.ipv6.conf.dummy98.hop_limit=4') call('sysctl -w net.ipv6.conf.dummy98.max_addresses=10') - log=read_networkd_log() + log = read_networkd_log() + # fmt: off self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/accept_ra' from '0' to '1', conflicting with our setting to '0'") self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/mtu' from '1550' to '1360', conflicting with our setting to '1550'") self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv4/conf/dummy98/promote_secondaries' from '1' to '0', conflicting with our setting to '1'") self.assertRegex(log, r"Foreign process 'sysctl\[\d+\]' changed sysctl '/proc/sys/net/ipv6/conf/dummy98/proxy_ndp' from '0' to '1', conflicting with our setting to '0'") + # fmt: on self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/hop_limit'", log) self.assertNotIn("changed sysctl '/proc/sys/net/ipv6/conf/dummy98/max_addresses'", log) - self.assertNotIn("Sysctl monitor BPF returned error", log) + self.assertNotIn('Sysctl monitor BPF returned error', log) + class NetworkdWWANTests(unittest.TestCase, Utilities): - def setUp(self): setup_common() @@ -9508,7 +10900,7 @@ class NetworkdWWANTests(unittest.TestCase, Utilities): '--ipv6-address', '2001:db8::1', '--ipv6-gateway', '2001:db8::2', '--ipv6-prefix', '64', - ) + ) # fmt: skip except (subprocess.CalledProcessError, PermissionError, OSError) as e: self.skipTest(f'Failed to start mock ModemManager: {e}') start_networkd() @@ -9530,17 +10922,67 @@ class NetworkdWWANTests(unittest.TestCase, Utilities): print(output) self.assertIn('default via 2001:db8::2', output) + if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir') - parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir') - parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind) - parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug) - parser.add_argument('--asan-options', help='ASAN options', dest='asan_options') - parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options') - parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options') - parser.add_argument('--with-coverage', help='Loosen certain sandbox restrictions to make gcov happy', dest='with_coverage', type=bool, nargs='?', const=True, default=with_coverage) - parser.add_argument('--no-journal', help='Do not show journal of systemd-networkd on stop', dest='show_journal', action='store_false') + parser.add_argument( + '--build-dir', + help='Path to build dir', + dest='build_dir', + ) + parser.add_argument( + '--source-dir', + help='Path to source dir/git tree', + dest='source_dir', + ) + parser.add_argument( + '--valgrind', + help='Enable valgrind', + dest='use_valgrind', + type=bool, + nargs='?', + const=True, + default=use_valgrind, + ) + parser.add_argument( + '--debug', + help='Generate debugging logs', + dest='enable_debug', + type=bool, + nargs='?', + const=True, + default=enable_debug, + ) + parser.add_argument( + '--asan-options', + help='ASAN options', + dest='asan_options', + ) + parser.add_argument( + '--lsan-options', + help='LSAN options', + dest='lsan_options', + ) + parser.add_argument( + '--ubsan-options', + help='UBSAN options', + dest='ubsan_options', + ) + parser.add_argument( + '--with-coverage', + help='Loosen certain sandbox restrictions to make gcov happy', + dest='with_coverage', + type=bool, + nargs='?', + const=True, + default=with_coverage, + ) + parser.add_argument( + '--no-journal', + help='Do not show journal of systemd-networkd on stop', + dest='show_journal', + action='store_false', + ) ns, unknown_args = parser.parse_known_args(namespace=unittest) if ns.build_dir: @@ -9554,16 +10996,18 @@ if __name__ == '__main__': udevadm_bin = os.path.join(ns.build_dir, 'udevadm') build_dir = ns.build_dir + # fmt: off if ns.source_dir: source_dir = ns.source_dir - assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree." - elif os.path.exists(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../meson_options.txt"))): - source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) + assert os.path.exists(os.path.join(source_dir, 'meson_options.txt')), f"{source_dir} doesn't appear to be a systemd source tree." + elif os.path.exists(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../meson_options.txt'))): + source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')) else: source_dir = None + # fmt: on if networkd_bin is None or resolved_bin is None or timesyncd_bin is None: - print("networkd tests require networkd/resolved/timesyncd to be enabled") + print('networkd tests require networkd/resolved/timesyncd to be enabled') sys.exit(77) use_valgrind = ns.use_valgrind @@ -9571,7 +11015,7 @@ if __name__ == '__main__': asan_options = ns.asan_options lsan_options = ns.lsan_options ubsan_options = ns.ubsan_options - with_coverage = ns.with_coverage or "COVERAGE_BUILD_DIR" in os.environ + with_coverage = ns.with_coverage or 'COVERAGE_BUILD_DIR' in os.environ show_journal = ns.show_journal if use_valgrind: @@ -9612,6 +11056,6 @@ if __name__ == '__main__': argv=[ sys.argv[0], *unknown_args, - *(["-k", match] if (match := os.getenv("TEST_MATCH_TESTCASE")) else []) + *(['-k', match] if (match := os.getenv('TEST_MATCH_TESTCASE')) else []), ], ) From 6e4c7f91f90aa02cacba9ee09a07f1c00b403240 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:02:20 +0900 Subject: [PATCH 31/62] test-shutdown: apply "ruff format" --- test/test-shutdown.py | 81 ++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/test/test-shutdown.py b/test/test-shutdown.py index d19a03742c..1fe96c8828 100755 --- a/test/test-shutdown.py +++ b/test/test-shutdown.py @@ -13,27 +13,34 @@ import pexpect def run(args): ret = 1 - logger = logging.getLogger("test-shutdown") + logger = logging.getLogger('test-shutdown') logfile = None if args.logfile: - logger.debug("Logging pexpect IOs to %s", args.logfile) + logger.debug('Logging pexpect IOs to %s', args.logfile) logfile = open(args.logfile, 'w') elif args.verbose: logfile = sys.stdout - logger.info("spawning test") - console = pexpect.spawn(args.command, args.arg, logfile=logfile, env={ - "TERM": "dumb", - }, encoding='utf-8', timeout=60) + logger.info('spawning test') + console = pexpect.spawn( + args.command, + args.arg, + logfile=logfile, + env={ + 'TERM': 'dumb', + }, + encoding='utf-8', + timeout=60, + ) - logger.debug("child pid %d", console.pid) + logger.debug('child pid %d', console.pid) try: - logger.info("waiting for login prompt") + logger.info('waiting for login prompt') console.expect('H login: ', 10) - logger.info("log in and start screen") + logger.info('log in and start screen') console.sendline('root') console.expect('bash.*# ', 10) console.sendline('screen') @@ -46,42 +53,42 @@ def run(args): console.sendline('systemctl is-system-running --wait') console.expect(r'\b(running|degraded)\b', 60) -# console.interact() + # console.interact() console.sendline('tty') console.expect(r'/dev/(pts/\d+)') pty = console.match.group(1) - logger.info("window 1 at tty %s", pty) + logger.info('window 1 at tty %s', pty) - logger.info("schedule reboot") + logger.info('schedule reboot') console.sendline('shutdown -r') console.expect("Reboot scheduled for (?P.*), use 'shutdown -c' to cancel", 2) date = console.match.group('date') - logger.info("reboot scheduled for %s", date) + logger.info('reboot scheduled for %s', date) console.sendcontrol('a') console.send('0') - logger.info("verify broadcast message") + logger.info('verify broadcast message') console.expect(f'Broadcast message from root@H on {pty}', 2) console.expect(f'The system will reboot at {date}', 2) - logger.info("check show output") + logger.info('check show output') console.sendline('shutdown --show') console.expect(f"Reboot scheduled for {date}, use 'shutdown -c' to cancel", 2) - logger.info("cancel shutdown") + logger.info('cancel shutdown') console.sendline('shutdown -c') console.sendcontrol('a') console.send('1') console.expect('System shutdown has been cancelled', 2) - logger.info("call for reboot") + logger.info('call for reboot') console.sendline('sleep 10; shutdown -r now') console.sendcontrol('a') console.send('0') - console.expect("The system will reboot now!", 12) + console.expect('The system will reboot now!', 12) - logger.info("waiting for reboot") + logger.info('waiting for reboot') console.expect('H login: ', 60) console.sendline('root') @@ -89,16 +96,16 @@ def run(args): console.sendline('> /testok') - logger.info("power off") + logger.info('power off') console.sendline('poweroff') - logger.info("expect termination now") + logger.info('expect termination now') console.expect(pexpect.EOF) ret = 0 except Exception as e: logger.error(e) - logger.info("killing child pid %d", console.pid) + logger.info('killing child pid %d', console.pid) # Ask systemd-nspawn to stop and release the container's resources properly. console.kill(signal.SIGTERM) @@ -116,12 +123,31 @@ def run(args): return ret + def main(): - parser = argparse.ArgumentParser(description='test logind shutdown feature') - parser.add_argument("-v", "--verbose", action="store_true", help="verbose") - parser.add_argument("--logfile", metavar='FILE', help="Save all test input/output to the given path") - parser.add_argument("command", help="command to run") - parser.add_argument("arg", nargs='*', help="args for command") + parser = argparse.ArgumentParser( + description='test logind shutdown feature', + ) + parser.add_argument( + '-v', + '--verbose', + action='store_true', + help='verbose', + ) + parser.add_argument( + '--logfile', + metavar='FILE', + help='Save all test input/output to the given path', + ) + parser.add_argument( + 'command', + help='command to run', + ) + parser.add_argument( + 'arg', + nargs='*', + help='args for command', + ) args = parser.parse_args() @@ -134,6 +160,7 @@ def main(): return run(args) + if __name__ == '__main__': sys.exit(main()) From f8a3f9d13506c3aa0dc6a161ddf866203508213e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:02:36 +0900 Subject: [PATCH 32/62] test-tmpfiles: apply "ruff format" and "ruff check --fix" This also dropped unnecessary existence check for subprocess.run. --- test/test-systemd-tmpfiles.py | 119 +++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/test/test-systemd-tmpfiles.py b/test/test-systemd-tmpfiles.py index 37a5e9ef3e..03022a9d5d 100755 --- a/test/test-systemd-tmpfiles.py +++ b/test/test-systemd-tmpfiles.py @@ -6,13 +6,13 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. +import grp import os -import sys +import pwd import socket import subprocess +import sys import tempfile -import pwd -import grp from pathlib import Path try: @@ -20,40 +20,41 @@ try: except ImportError: id128 = None -EX_DATAERR = 65 # from sysexits.h +EX_DATAERR = 65 # from sysexits.h EXIT_TEST_SKIP = 77 -try: - subprocess.run -except AttributeError: - sys.exit(EXIT_TEST_SKIP) - exe_with_args = sys.argv[1:] temp_dir = tempfile.TemporaryDirectory(prefix='test-systemd-tmpfiles.') # If /tmp isn't owned by either 'root' or the current user # systemd-tmpfiles will exit with "Detected unsafe path transition" # breaking this test -tmpowner = os.stat("/tmp").st_uid +tmpowner = os.stat('/tmp').st_uid if tmpowner != 0 and tmpowner != os.getuid(): print("Skip: /tmp is not owned by 'root' or current user") sys.exit(EXIT_TEST_SKIP) + def test_line(line, *, user, returncode=EX_DATAERR, extra={}): args = ['--user'] if user else [] print('Running {} on {!r}'.format(' '.join(exe_with_args + args), line)) - c = subprocess.run(exe_with_args + ['--create', '-'] + args, - input=line, stdout=subprocess.PIPE, universal_newlines=True, - **extra) + c = subprocess.run( + exe_with_args + ['--create', '-'] + args, + input=line, + stdout=subprocess.PIPE, + text=True, + **extra, + ) assert c.returncode == returncode, c + def test_invalids(*, user): test_line('asdfa', user=user) test_line('f "open quote', user=user) test_line('f closed quote""', user=user) test_line('Y /unknown/letter', user=user) test_line('w non/absolute/path', user=user) - test_line('s', user=user) # s is for short + test_line('s', user=user) # s is for short test_line('f!! /too/many/bangs', user=user) test_line('f++ /too/many/plusses', user=user) test_line('f+!+ /too/many/plusses', user=user) @@ -77,12 +78,18 @@ def test_invalids(*, user): test_line('h - - -', user=user) test_line('H - - -', user=user) + def test_uninitialized_t(): if os.getuid() == 0: return - test_line('w /foo - - - - "specifier for --user %t"', - user=True, returncode=0, extra={'env':{'HOME': os.getenv('HOME')}}) + test_line( + 'w /foo - - - - "specifier for --user %t"', + user=True, + returncode=0, + extra={'env': {'HOME': os.getenv('HOME')}}, + ) + def test_content(line, expected, *, user, extra={}, subpath='/arg', path_cb=None): d = tempfile.TemporaryDirectory(prefix='test-content.', dir=temp_dir.name) @@ -92,24 +99,25 @@ def test_content(line, expected, *, user, extra={}, subpath='/arg', path_cb=None spec = line.format(arg) test_line(spec, user=user, returncode=0, extra=extra) content = open(arg).read() - print('expect: {!r}\nactual: {!r}'.format(expected, content)) + print(f'expect: {expected!r}\nactual: {content!r}') assert content == expected + def test_valid_specifiers(*, user): test_content('f {} - - - - two words', 'two words', user=user) if id128 and os.path.isfile('/etc/machine-id'): try: - test_content('f {} - - - - %m', '{}'.format(id128.get_machine().hex), user=user) + test_content('f {} - - - - %m', f'{id128.get_machine().hex}', user=user) except AssertionError as e: print(e) print('/etc/machine-id: {!r}'.format(open('/etc/machine-id').read())) print('/proc/cmdline: {!r}'.format(open('/proc/cmdline').read())) print('skipping') - test_content('f {} - - - - %b', '{}'.format(id128.get_boot().hex), user=user) - test_content('f {} - - - - %H', '{}'.format(socket.gethostname()), user=user) - test_content('f {} - - - - %v', '{}'.format(os.uname().release), user=user) - test_content('f {} - - - - %U', '{}'.format(os.getuid() if user else 0), user=user) - test_content('f {} - - - - %G', '{}'.format(os.getgid() if user else 0), user=user) + test_content('f {} - - - - %b', f'{id128.get_boot().hex}', user=user) + test_content('f {} - - - - %H', f'{socket.gethostname()}', user=user) + test_content('f {} - - - - %v', f'{os.uname().release}', user=user) + test_content('f {} - - - - %U', f'{os.getuid() if user else 0}', user=user) + test_content('f {} - - - - %G', f'{os.getgid() if user else 0}', user=user) try: puser = pwd.getpwuid(os.getuid() if user else 0) @@ -117,7 +125,7 @@ def test_valid_specifiers(*, user): puser = None if puser: - test_content('f {} - - - - %u', '{}'.format(puser.pw_name), user=user) + test_content('f {} - - - - %u', f'{puser.pw_name}', user=user) try: pgroup = grp.getgrgid(os.getgid() if user else 0) @@ -125,52 +133,55 @@ def test_valid_specifiers(*, user): pgroup = None if pgroup: - test_content('f {} - - - - %g', '{}'.format(pgroup.gr_name), user=user) + test_content('f {} - - - - %g', f'{pgroup.gr_name}', user=user) # Note that %h is the only specifier in which we look the environment, # because we check $HOME. Should we even be doing that? - home = os.path.expanduser("~") - test_content('f {} - - - - %h', '{}'.format(home), user=user) + home = os.path.expanduser('~') + test_content('f {} - - - - %h', f'{home}', user=user) xdg_runtime_dir = os.getenv('XDG_RUNTIME_DIR') if xdg_runtime_dir is not None or not user: test_content('f {} - - - - %t', xdg_runtime_dir if user else '/run', - user=user) + user=user) # fmt: skip xdg_state_home = os.getenv('XDG_STATE_HOME') if xdg_state_home is None and user: - xdg_state_home = os.path.join(home, ".local/state") + xdg_state_home = os.path.join(home, '.local/state') test_content('f {} - - - - %S', xdg_state_home if user else '/var/lib', - user=user) + user=user) # fmt: skip xdg_cache_home = os.getenv('XDG_CACHE_HOME') if xdg_cache_home is None and user: - xdg_cache_home = os.path.join(home, ".cache") + xdg_cache_home = os.path.join(home, '.cache') test_content('f {} - - - - %C', xdg_cache_home if user else '/var/cache', - user=user) + user=user) # fmt: skip test_content('f {} - - - - %L', os.path.join(xdg_state_home, 'log') if user else '/var/log', - user=user) + user=user) # fmt: skip test_content('f {} - - - - %%', '%', user=user) + def mkfifo(parent, subpath): os.makedirs(parent, mode=0o755, exist_ok=True) first_component = subpath.split('/')[1] path = parent + '/' + first_component - print('path: {}'.format(path)) + print(f'path: {path}') os.mkfifo(path) + def mkdir(parent, subpath): first_component = subpath.split('/')[1] path = parent + '/' + first_component os.makedirs(path, mode=0o755, exist_ok=True) os.symlink(path, path + '/self', target_is_directory=True) + def symlink(parent, subpath): link_path = parent + '/link-target' os.makedirs(parent, mode=0o755, exist_ok=True) @@ -180,6 +191,7 @@ def symlink(parent, subpath): path = parent + '/' + first_component os.symlink(link_path, path, target_is_directory=True) + def file(parent, subpath): content = 'file-' + subpath.split('/')[1] path = parent + subpath @@ -187,6 +199,7 @@ def file(parent, subpath): with open(path, 'wb') as f: f.write(content.encode()) + def valid_symlink(parent, subpath): target = 'link-target' link_path = parent + target @@ -195,6 +208,7 @@ def valid_symlink(parent, subpath): path = parent + '/' + first_component os.symlink(target, path, target_is_directory=True) + def test_hard_cleanup(*, user): type_cbs = [None, file, mkdir, symlink] if 'mkfifo' in dir(os): @@ -209,29 +223,44 @@ def test_hard_cleanup(*, user): label = 'valid_symlink-deep' test_content('f= {} - - - - ' + label, label, user=user, subpath='/deep/1/2', path_cb=valid_symlink) + def test_base64(): - test_content('f~ {} - - - - UGlmZgpQYWZmClB1ZmYgCg==', "Piff\nPaff\nPuff \n", user=False) + test_content('f~ {} - - - - UGlmZgpQYWZmClB1ZmYgCg==', 'Piff\nPaff\nPuff \n', user=False) + def test_conditionalized_execute_bit(): - c = subprocess.run(exe_with_args + ['--version', '|', 'grep', '-F', '+ACL'], shell=True, stdout=subprocess.DEVNULL) + c = subprocess.run( + exe_with_args + ['--version', '|', 'grep', '-F', '+ACL'], + shell=True, + stdout=subprocess.DEVNULL, + ) if c.returncode != 0: return 0 d = tempfile.TemporaryDirectory(prefix='test-acl.', dir=temp_dir.name) - temp = Path(d.name) / "cond_exec" + temp = Path(d.name) / 'cond_exec' temp.touch() temp.chmod(0o644) - test_line(f"a {temp} - - - - u:root:Xwr", user=False, returncode=0) - c = subprocess.run(["getfacl", "-Ec", temp], - stdout=subprocess.PIPE, check=True, text=True) - assert "user:root:rw-" in c.stdout + test_line(f'a {temp} - - - - u:root:Xwr', user=False, returncode=0) + c = subprocess.run( + ['getfacl', '-Ec', temp], + stdout=subprocess.PIPE, + check=True, + text=True, + ) + assert 'user:root:rw-' in c.stdout temp.chmod(0o755) - test_line(f"a+ {temp} - - - - u:root:Xwr,g:root:rX", user=False, returncode=0) - c = subprocess.run(["getfacl", "-Ec", temp], - stdout=subprocess.PIPE, check=True, text=True) - assert "user:root:rwx" in c.stdout and "group:root:r-x" in c.stdout + test_line(f'a+ {temp} - - - - u:root:Xwr,g:root:rX', user=False, returncode=0) + c = subprocess.run( + ['getfacl', '-Ec', temp], + stdout=subprocess.PIPE, + check=True, + text=True, + ) + assert 'user:root:rwx' in c.stdout and 'group:root:r-x' in c.stdout + if __name__ == '__main__': test_invalids(user=False) From 81b1f3dd9fc11b52ee839b301195ec6b5b2e7eeb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:02:48 +0900 Subject: [PATCH 33/62] test-udev: apply "ruff format" and "ruff check --fix" --- test/test-udev.py | 2372 +++++++++++++++++++++++---------------------- 1 file changed, 1191 insertions(+), 1181 deletions(-) diff --git a/test/test-udev.py b/test/test-udev.py index 20b55de7d6..da5d41efe0 100755 --- a/test/test-udev.py +++ b/test/test-udev.py @@ -19,8 +19,9 @@ import dataclasses import functools +import grp import os -import pwd, grp +import pwd import re import stat import subprocess @@ -37,11 +38,11 @@ except ImportError as e: sys.exit(77) -SYS_SCRIPT = Path(__file__).with_name('sys-script.py') +SYS_SCRIPT = Path(__file__).with_name('sys-script.py') try: - UDEV_BIN = Path(os.environ['UDEV_RULE_RUNNER']) + UDEV_BIN = Path(os.environ['UDEV_RULE_RUNNER']) except KeyError: - UDEV_BIN = Path(__file__).parent / 'manual/test-udev-rule-runner' + UDEV_BIN = Path(__file__).parent / 'manual/test-udev-rule-runner' UDEV_BIN = UDEV_BIN.absolute() # Those will be set by the udev_setup() fixture @@ -53,11 +54,12 @@ os.environ['SYSTEMD_DEVICE_VERIFY_SYSFS'] = '0' rules_10k_tags = \ '\n'.join(f'KERNEL=="sda", TAG+="test{i + 1}"' - for i in range(10_000)) + for i in range(10_000)) # fmt: skip rules_10k_tags_continuation = \ ',\\\n'.join(('KERNEL=="sda"', - *(f'TAG+="test{i + 1}"' for i in range(10_000)))) + *(f'TAG+="test{i + 1}"' for i in range(10_000)))) # fmt: skip + @dataclasses.dataclass class Device: @@ -149,8 +151,10 @@ class Device: def listify(f): def wrap(*args, **kwargs): return list(f(*args, **kwargs)) + return functools.update_wrapper(wrap, f) + @listify def all_block_devs(exp_func) -> list[Device]: # Create a device list with all block devices under /sys @@ -167,9 +171,7 @@ def all_block_devs(exp_func) -> list[Device]: tgt = tgt[5:] exp, not_exp = exp_func(tgt) - yield Device(devpath=tgt, - exp_links=exp, - not_exp_links=not_exp) + yield Device(devpath=tgt, exp_links=exp, not_exp_links=not_exp) @dataclasses.dataclass @@ -200,71 +202,71 @@ class Rules: UDEV_RULES.parent.mkdir(exist_ok=True, parents=True) UDEV_RULES.write_text(self.rules) + RULES = [ Rules.new( 'no rules', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', ), - rules = r""" + rules=r''' # - """), - + ''', + ), Rules.new( 'label test of scsi disc', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["boot_disk"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['boot_disk'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n" KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "label test of scsi disc", + 'label test of scsi disc', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["boot_disk"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['boot_disk'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n" KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "label test of scsi disc", + 'label test of scsi disc', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["boot_disk"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['boot_disk'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n" KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "label test of scsi partition", + 'label test of scsi partition', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["boot_disk1"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['boot_disk1'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n" - """), - + ''', + ), Rules.new( - "label test of pattern match", + 'label test of pattern match', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["boot_disk1", "boot_disk1-4", "boot_disk1-5"], - not_exp_links = ["boot_disk1-1", "boot_disk1-2", "boot_disk1-3", "boot_disk1-6", "boot_disk1-7"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['boot_disk1', 'boot_disk1-4', 'boot_disk1-5'], + not_exp_links=['boot_disk1-1', 'boot_disk1-2', 'boot_disk1-3', 'boot_disk1-6', 'boot_disk1-7'], ), - - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="?ATA", SYMLINK+="boot_disk%n-1" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA?", SYMLINK+="boot_disk%n-2" SUBSYSTEMS=="scsi", ATTRS{vendor}=="A??", SYMLINK+="boot_disk%n" @@ -277,48 +279,61 @@ RULES = [ SUBSYSTEMS=="scsi", GOTO="skip-7" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n-7" LABEL="skip-7" - """), - + ''', + ), Rules.new( - "label test of multiple sysfs files", + 'label test of multiple sysfs files', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["boot_disk1"], - not_exp_links = ["boot_diskX1"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['boot_disk1'], + not_exp_links=['boot_diskX1'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS X ", SYMLINK+="boot_diskX%n" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK+="boot_disk%n" - """), - + ''', + ), Rules.new( - "label test of max sysfs files (skip invalid rule)", + 'label test of max sysfs files (skip invalid rule)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["boot_disk1", "boot_diskXY1"], - not_exp_links = ["boot_diskXX1"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['boot_disk1', 'boot_diskXY1'], + not_exp_links=['boot_diskXX1'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", ATTRS{queue_depth}=="32", SYMLINK+="boot_diskXX%n" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", ATTRS{queue_depth}=="1", SYMLINK+="boot_diskXY%n" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", SYMLINK+="boot_disk%n" - """), - + ''', + ), Rules.new( - "SYMLINK tests", + 'SYMLINK tests', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["link1", "link2/foo", "link3/aaa/bbb", - "abs1", "abs2/foo", "abs3/aaa/bbb", - "default___replace_test/foo_aaa", - "string_escape___replace/foo_bbb", - "env_with_space", - "default/replace/mode_foo__hoge", - "replace_env_harder_foo__hoge", - "match", "unmatch"], - not_exp_links = ["removed1", "removed2", "removed3", "unsafe/../../path", "/nondev/path/will/be/refused"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=[ + 'link1', + 'link2/foo', + 'link3/aaa/bbb', + 'abs1', + 'abs2/foo', + 'abs3/aaa/bbb', + 'default___replace_test/foo_aaa', + 'string_escape___replace/foo_bbb', + 'env_with_space', + 'default/replace/mode_foo__hoge', + 'replace_env_harder_foo__hoge', + 'match', + 'unmatch', + ], + not_exp_links=[ + 'removed1', + 'removed2', + 'removed3', + 'unsafe/../../path', + '/nondev/path/will/be/refused', + ], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK+="removed1" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK-="removed1" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK+="/./dev///removed2" @@ -336,98 +351,98 @@ RULES = [ SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", OPTIONS="string_escape=replace", ENV{.HOGE}="replace/env/harder?foo;;hoge", SYMLINK+="%E{.HOGE}" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK=="link1", SYMLINK+="match" SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK!="removed1", SYMLINK+="unmatch" - """), - + ''', + ), Rules.new( - "catch device by *", + 'catch device by *', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem/0", "catch-all"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem/0', 'catch-all'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM*", SYMLINK+="modem/%n" KERNEL=="*", SYMLINK+="catch-all" - """), - + ''', + ), Rules.new( - "catch device by * - take 2", + 'catch device by * - take 2', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem/0"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem/0'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="*ACM1", SYMLINK+="bad" KERNEL=="*ACM0", SYMLINK+="modem/%n" - """), - + ''', + ), Rules.new( - "catch device by ?", + 'catch device by ?', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem/0"], - not_exp_links = ["modem/0-1", "modem/0-2"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem/0'], + not_exp_links=['modem/0-1', 'modem/0-2'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM??*", SYMLINK+="modem/%n-1" KERNEL=="ttyACM??", SYMLINK+="modem/%n-2" KERNEL=="ttyACM?", SYMLINK+="modem/%n" - """), - + ''', + ), Rules.new( - "catch device by character class", + 'catch device by character class', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem/0"], - not_exp_links = ["modem/0-1", "modem/0-2"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem/0'], + not_exp_links=['modem/0-1', 'modem/0-2'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[A-Z]*", SYMLINK+="modem/%n-1" KERNEL=="ttyACM?[0-9]", SYMLINK+="modem/%n-2" KERNEL=="ttyACM[0-9]*", SYMLINK+="modem/%n" - """), - + ''', + ), Rules.new( "don't replace kernel name", Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( "comment lines in config file (and don't replace kernel name)", Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem'], ), - rules = r""" + rules=r''' # this is a comment KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( "comment lines in config file with whitespace (and don't replace kernel name)", Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem'], ), - rules = r""" + rules=r''' # this is a comment with whitespace before the comment KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( "whitespace only lines (and don't replace kernel name)", Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["whitespace"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['whitespace'], ), - rules = r""" + rules=r''' @@ -436,49 +451,49 @@ RULES = [ - """), - + ''', + ), Rules.new( "empty lines in config file (and don't replace kernel name)", Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( "backslashed multi lines in config file (and don't replace kernel name)", Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM0", \ SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "preserve backslashes, if they are not for a newline", + 'preserve backslashes, if they are not for a newline', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["aaa"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['aaa'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM0", PROGRAM=="/bin/echo -e \101", RESULT=="A", SYMLINK+="aaa" - """), - + ''', + ), Rules.new( "stupid backslashed multi lines in config file (and don't replace kernel name)", Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem'], ), - rules = r""" + rules=r''' # \ @@ -490,1364 +505,1361 @@ RULES = [ KERNEL=="ttyACM0", \ SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "subdirectory handling", + 'subdirectory handling', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["sub/direct/ory/modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['sub/direct/ory/modem'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM0", SYMLINK+="sub/direct/ory/modem" - """), - + ''', + ), Rules.new( - "parent device name match of scsi partition", + 'parent device name match of scsi partition', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["first_disk5"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['first_disk5'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="first_disk%n" - """), - + ''', + ), Rules.new( - "test substitution chars", + 'test substitution chars', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["Major:8:minor:5:kernelnumber:5:id:0:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['Major:8:minor:5:kernelnumber:5:id:0:0:0:0'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="Major:%M:minor:%m:kernelnumber:%n:id:%b" - """), - + ''', + ), Rules.new( - "import of shell-value returned from program", + 'import of shell-value returned from program', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node12345678"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node12345678'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", IMPORT{program}="/bin/echo -e ' TEST_KEY=12345678\n TEST_key2=98765'", SYMLINK+="node$env{TEST_KEY}" KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "substitution of sysfs value (%s{file})", + 'substitution of sysfs value (%s{file})', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["disk-ATA-sda"], - not_exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['disk-ATA-sda'], + not_exp_links=['modem'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="disk-%s{vendor}-%k" KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "program result substitution", + 'program result substitution', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["special-device-5"], - not_exp_links = ["not"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['special-device-5'], + not_exp_links=['not'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="-special-*", SYMLINK+="not" SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="special-*", SYMLINK+="%c-%n" - """), - + ''', + ), Rules.new( - "program result substitution (newline removal)", + 'program result substitution (newline removal)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["newline_removed"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['newline_removed'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo test", RESULT=="test", SYMLINK+="newline_removed" - """), - + ''', + ), Rules.new( - "program result substitution", + 'program result substitution', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["test-0:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['test-0:0:0:0'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n test-%b", RESULT=="test-0:0*", SYMLINK+="%c" - """), - + ''', + ), Rules.new( - "program with lots of arguments", + 'program with lots of arguments', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["foo9"], - not_exp_links = ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['foo9'], + not_exp_links=['foo3', 'foo4', 'foo5', 'foo6', 'foo7', 'foo8'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="%c{7}" - """), - + ''', + ), Rules.new( - "program with subshell", + 'program with subshell', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["bar9"], - not_exp_links = ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['bar9'], + not_exp_links=['foo3', 'foo4', 'foo5', 'foo6', 'foo7', 'foo8'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c 'echo foo3 foo4 foo5 foo6 foo7 foo8 foo9 | sed s/foo9/bar9/'", KERNEL=="sda5", SYMLINK+="%c{7}" - """), - + ''', + ), Rules.new( - "program arguments combined with apostrophes", + 'program arguments combined with apostrophes', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["foo7"], - not_exp_links = ["foo3", "foo4", "foo5", "foo6", "foo8"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['foo7'], + not_exp_links=['foo3', 'foo4', 'foo5', 'foo6', 'foo8'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n 'foo3 foo4' 'foo5 foo6 foo7 foo8'", KERNEL=="sda5", SYMLINK+="%c{5}" - """), - + ''', + ), Rules.new( - "program arguments combined with escaped double quotes, part 1", + 'program arguments combined with escaped double quotes, part 1', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["foo2"], - not_exp_links = ["foo1"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['foo2'], + not_exp_links=['foo1'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c 'printf %%s \"foo1 foo2\" | grep \"foo1 foo2\"'", KERNEL=="sda5", SYMLINK+="%c{2}" - """), - + ''', + ), Rules.new( - "program arguments combined with escaped double quotes, part 2", + 'program arguments combined with escaped double quotes, part 2', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["foo2"], - not_exp_links = ["foo1"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['foo2'], + not_exp_links=['foo1'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c \"printf %%s 'foo1 foo2' | grep 'foo1 foo2'\"", KERNEL=="sda5", SYMLINK+="%c{2}" - """), - + ''', + ), Rules.new( - "program arguments combined with escaped double quotes, part 3", + 'program arguments combined with escaped double quotes, part 3', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["foo2"], - not_exp_links = ["foo1", "foo3"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['foo2'], + not_exp_links=['foo1', 'foo3'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c 'printf \"%%s %%s\" \"foo1 foo2\" \"foo3\"| grep \"foo1 foo2\"'", KERNEL=="sda5", SYMLINK+="%c{2}" - """), - + ''', + ), Rules.new( - "characters before the %c{N} substitution", + 'characters before the %c{N} substitution', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["my-foo9"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['my-foo9'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{7}" - """), - + ''', + ), Rules.new( - "substitute the second to last argument", + 'substitute the second to last argument', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["my-foo8"], - not_exp_links = ["my-foo3", "my-foo4", "my-foo5", "my-foo6", "my-foo7", "my-foo9"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['my-foo8'], + not_exp_links=['my-foo3', 'my-foo4', 'my-foo5', 'my-foo6', 'my-foo7', 'my-foo9'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{6}" - """), - + ''', + ), Rules.new( - "test substitution by variable name", + 'test substitution by variable name', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['Major:8-minor:5-kernelnumber:5-id:0:0:0:0'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="Major:$major-minor:$minor-kernelnumber:$number-id:$id" - """), - + ''', + ), Rules.new( - "test substitution by variable name 2", + 'test substitution by variable name 2', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['Major:8-minor:5-kernelnumber:5-id:0:0:0:0'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="Major:$major-minor:%m-kernelnumber:$number-id:$id" - """), - + ''', + ), Rules.new( - "test substitution by variable name 3", + 'test substitution by variable name 3', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["850:0:0:05"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['850:0:0:05'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="%M%m%b%n" - """), - + ''', + ), Rules.new( - "test substitution by variable name 4", + 'test substitution by variable name 4', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["855"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['855'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="$major$minor$number" - """), - + ''', + ), Rules.new( - "test substitution by variable name 5", + 'test substitution by variable name 5', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["8550:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['8550:0:0:0'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="$major%m%n$id" - """), - + ''', + ), Rules.new( - "non matching SUBSYSTEMS for device with no parent", + 'non matching SUBSYSTEMS for device with no parent', Device( - "/devices/virtual/tty/console", - exp_links = ["TTY"], - not_exp_links = ["foo"], + '/devices/virtual/tty/console', + exp_links=['TTY'], + not_exp_links=['foo'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo", RESULT=="foo", SYMLINK+="foo" KERNEL=="console", SYMLINK+="TTY" - """), - + ''', + ), Rules.new( - "non matching SUBSYSTEMS", + 'non matching SUBSYSTEMS', Device( - "/devices/virtual/tty/console", - exp_links = ["TTY"], - not_exp_links = ["foo"], + '/devices/virtual/tty/console', + exp_links=['TTY'], + not_exp_links=['foo'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="foo", ATTRS{dev}=="5:1", SYMLINK+="foo" KERNEL=="console", SYMLINK+="TTY" - """), - + ''', + ), Rules.new( - "ATTRS match", + 'ATTRS match', Device( - "/devices/virtual/tty/console", - exp_links = ["foo", "TTY"], + '/devices/virtual/tty/console', + exp_links=['foo', 'TTY'], ), - rules = r""" + rules=r''' KERNEL=="console", SYMLINK+="TTY" ATTRS{dev}=="5:1", SYMLINK+="foo" - """), - + ''', + ), Rules.new( - "ATTR (empty file)", + 'ATTR (empty file)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["empty", "not-something"], - not_exp_links = ["something", "not-empty"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['empty', 'not-something'], + not_exp_links=['something', 'not-empty'], ), - rules = r""" + rules=r''' KERNEL=="sda", ATTR{test_empty_file}=="?*", SYMLINK+="something" KERNEL=="sda", ATTR{test_empty_file}!="", SYMLINK+="not-empty" KERNEL=="sda", ATTR{test_empty_file}=="", SYMLINK+="empty" KERNEL=="sda", ATTR{test_empty_file}!="?*", SYMLINK+="not-something" - """), - + ''', + ), Rules.new( - "ATTR (non-existent file)", + 'ATTR (non-existent file)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["non-existent", "wrong"], - not_exp_links = ["something", "empty", "not-empty", - "not-something", "something"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['non-existent', 'wrong'], + not_exp_links=['something', 'empty', 'not-empty', 'not-something', 'something'], ), - rules = r""" + rules=r''' KERNEL=="sda", ATTR{nofile}=="?*", SYMLINK+="something" KERNEL=="sda", ATTR{nofile}!="", SYMLINK+="not-empty" KERNEL=="sda", ATTR{nofile}=="", SYMLINK+="empty" KERNEL=="sda", ATTR{nofile}!="?*", SYMLINK+="not-something" KERNEL=="sda", TEST!="nofile", SYMLINK+="non-existent" KERNEL=="sda", SYMLINK+="wrong" - """), - + ''', + ), Rules.new( - "program and bus type match", + 'program and bus type match', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["scsi-0:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['scsi-0:0:0:0'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="usb", PROGRAM=="/bin/echo -n usb-%b", SYMLINK+="%c" SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n scsi-%b", SYMLINK+="%c" SUBSYSTEMS=="foo", PROGRAM=="/bin/echo -n foo-%b", SYMLINK+="%c" - """), - + ''', + ), Rules.new( - "sysfs parent hierarchy", + 'sysfs parent hierarchy', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem'], ), - rules = r""" + rules=r''' ATTRS{idProduct}=="007b", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "name test with ! in the name", + 'name test with ! in the name', Device( - "/devices/virtual/block/fake!blockdev0", - devnode = "fake/blockdev0", - exp_links = ["is/a/fake/blockdev0"], - not_exp_links = ["is/not/a/fake/blockdev0", "modem"], + '/devices/virtual/block/fake!blockdev0', + devnode='fake/blockdev0', + exp_links=['is/a/fake/blockdev0'], + not_exp_links=['is/not/a/fake/blockdev0', 'modem'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", SYMLINK+="is/not/a/%k" SUBSYSTEM=="block", SYMLINK+="is/a/%k" KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "name test with ! in the name, but no matching rule", + 'name test with ! in the name, but no matching rule', Device( - "/devices/virtual/block/fake!blockdev0", - devnode = "fake/blockdev0", - not_exp_links = ["modem"], + '/devices/virtual/block/fake!blockdev0', + devnode='fake/blockdev0', + not_exp_links=['modem'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM0", SYMLINK+="modem" - """), - + ''', + ), Rules.new( - "KERNELS rule", + 'KERNELS rule', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["scsi-0:0:0:0"], - not_exp_links = ["no-match", "short-id", "not-scsi"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['scsi-0:0:0:0'], + not_exp_links=['no-match', 'short-id', 'not-scsi'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="usb", KERNELS=="0:0:0:0", SYMLINK+="not-scsi" SUBSYSTEMS=="scsi", KERNELS=="0:0:0:1", SYMLINK+="no-match" SUBSYSTEMS=="scsi", KERNELS==":0", SYMLINK+="short-id" SUBSYSTEMS=="scsi", KERNELS=="/0:0:0:0", SYMLINK+="no-match" SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="scsi-0:0:0:0" - """), - + ''', + ), Rules.new( - "KERNELS wildcard all", + 'KERNELS wildcard all', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["scsi-0:0:0:0"], - not_exp_links = ["no-match", "before"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['scsi-0:0:0:0'], + not_exp_links=['no-match', 'before'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="*:1", SYMLINK+="no-match" SUBSYSTEMS=="scsi", KERNELS=="*:0:1", SYMLINK+="no-match" SUBSYSTEMS=="scsi", KERNELS=="*:0:0:1", SYMLINK+="no-match" SUBSYSTEMS=="scsi", KERNEL=="0:0:0:0", SYMLINK+="before" SUBSYSTEMS=="scsi", KERNELS=="*", SYMLINK+="scsi-0:0:0:0" - """), - + ''', + ), Rules.new( - "KERNELS wildcard partial", + 'KERNELS wildcard partial', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["scsi-0:0:0:0", "before"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['scsi-0:0:0:0', 'before'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="before" SUBSYSTEMS=="scsi", KERNELS=="*:0", SYMLINK+="scsi-0:0:0:0" - """), - + ''', + ), Rules.new( - "KERNELS wildcard partial 2", + 'KERNELS wildcard partial 2', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["scsi-0:0:0:0", "before"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['scsi-0:0:0:0', 'before'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="before" SUBSYSTEMS=="scsi", KERNELS=="*:0:0:0", SYMLINK+="scsi-0:0:0:0" - """), - + ''', + ), Rules.new( - "substitute attr with link target value (first match)", + 'substitute attr with link target value (first match)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["driver-is-sd"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['driver-is-sd'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", SYMLINK+="driver-is-$attr{driver}" - """), - + ''', + ), Rules.new( - "substitute attr with link target value (currently selected device)", + 'substitute attr with link target value (currently selected device)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["driver-is-ahci"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['driver-is-ahci'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="pci", SYMLINK+="driver-is-$attr{driver}" - """), - + ''', + ), Rules.new( - "ignore ATTRS attribute whitespace", + 'ignore ATTRS attribute whitespace', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["ignored"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['ignored'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE SPACE", SYMLINK+="ignored" - """), - + ''', + ), Rules.new( - "do not ignore ATTRS attribute whitespace", + 'do not ignore ATTRS attribute whitespace', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["matched-with-space"], - not_exp_links = ["wrong-to-ignore"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['matched-with-space'], + not_exp_links=['wrong-to-ignore'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE SPACE ", SYMLINK+="wrong-to-ignore" SUBSYSTEMS=="scsi", ATTRS{whitespace_test}=="WHITE SPACE ", SYMLINK+="matched-with-space" - """), - + ''', + ), Rules.new( - "permissions USER=bad GROUP=name", + 'permissions USER=bad GROUP=name', Device( - "/devices/virtual/tty/tty33", - exp_perms = "0:0:0600", + '/devices/virtual/tty/tty33', + exp_perms='0:0:0600', ), - rules = r""" + rules=r''' KERNEL=="tty33", OWNER="bad", GROUP="name" - """), - + ''', + ), Rules.new( - "permissions OWNER=1", + 'permissions OWNER=1', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_perms = "1::0600", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_perms='1::0600', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="1" - """), - + ''', + ), Rules.new( - "permissions GROUP=1", + 'permissions GROUP=1', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_perms = ":1:0660", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_perms=':1:0660', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", GROUP="1" - """), - + ''', + ), Rules.new( - "textual user id", + 'textual user id', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_perms = "daemon::0600", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_perms='daemon::0600', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="daemon" - """), - + ''', + ), Rules.new( - "textual group id", + 'textual group id', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_perms = ":daemon:0660", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_perms=':daemon:0660', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", GROUP="daemon" - """), - + ''', + ), Rules.new( - "textual user/group id", + 'textual user/group id', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_perms = "root:audio:0660", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_perms='root:audio:0660', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="root", GROUP="audio" - """), - + ''', + ), Rules.new( - "permissions MODE=0777", + 'permissions MODE=0777', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_perms = "::0777", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_perms='::0777', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", MODE="0777" - """), - + ''', + ), Rules.new( - "permissions OWNER=1 GROUP=1 MODE=0777", + 'permissions OWNER=1 GROUP=1 MODE=0777', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_perms = "1:1:0777", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_perms='1:1:0777', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="1", GROUP="1", MODE="0777" - """), - + ''', + ), Rules.new( - "permissions OWNER to 1", + 'permissions OWNER to 1', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_perms = "1::", + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_perms='1::', ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", OWNER="1" - """), - + ''', + ), Rules.new( - "permissions GROUP to 1", + 'permissions GROUP to 1', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_perms = ":1:0660", + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_perms=':1:0660', ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", GROUP="1" - """), - + ''', + ), Rules.new( - "permissions MODE to 0060", + 'permissions MODE to 0060', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_perms = "::0060", + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_perms='::0060', ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", MODE="0060" - """), - + ''', + ), Rules.new( - "permissions OWNER, GROUP, MODE", + 'permissions OWNER, GROUP, MODE', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_perms = "1:1:0777", + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_perms='1:1:0777', ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", OWNER="1", GROUP="1", MODE="0777" - """), - + ''', + ), Rules.new( - "permissions only rule", + 'permissions only rule', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_perms = "1:1:0777", + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_perms='1:1:0777', ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", OWNER="1", GROUP="1", MODE="0777" KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444" KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n" - """), - + ''', + ), Rules.new( - "multiple permissions only rule", + 'multiple permissions only rule', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_perms = "1:1:0777", + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_perms='1:1:0777', ), - rules = r""" + rules=r''' SUBSYSTEM=="tty", OWNER="1" SUBSYSTEM=="tty", GROUP="1" SUBSYSTEM=="tty", MODE="0777" KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444" KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n" - """), - + ''', + ), Rules.new( - "permissions only rule with override at SYMLINK+ rule", + 'permissions only rule with override at SYMLINK+ rule', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_perms = "1:2:0777", + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_perms='1:2:0777', ), - rules = r""" + rules=r''' SUBSYSTEM=="tty", OWNER="1" SUBSYSTEM=="tty", GROUP="1" SUBSYSTEM=="tty", MODE="0777" KERNEL=="ttyUSX[0-9]*", OWNER="2", GROUP="2", MODE="0444" KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", GROUP="2" - """), - + ''', + ), Rules.new( - "major/minor number test", + 'major/minor number test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - exp_major_minor = "8:0", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + exp_major_minor='8:0', ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node" - """), - + ''', + ), Rules.new( - "big major number test", + 'big major number test', Device( - "/devices/virtual/misc/misc-fake1", - exp_links = ["node"], - exp_major_minor = "4095:1", + '/devices/virtual/misc/misc-fake1', + exp_links=['node'], + exp_major_minor='4095:1', ), - rules = r""" + rules=r''' KERNEL=="misc-fake1", SYMLINK+="node" - """), - + ''', + ), Rules.new( - "big major and big minor number test", + 'big major and big minor number test', Device( - "/devices/virtual/misc/misc-fake89999", - exp_links = ["node"], - exp_major_minor = "4095:89999", + '/devices/virtual/misc/misc-fake89999', + exp_links=['node'], + exp_major_minor='4095:89999', ), - rules = r""" + rules=r''' KERNEL=="misc-fake89999", SYMLINK+="node" - """), - + ''', + ), Rules.new( - "multiple symlinks with format char", + 'multiple symlinks with format char', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["symlink1-0", "symlink2-ttyACM0", "symlink3-"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['symlink1-0', 'symlink2-ttyACM0', 'symlink3-'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK="symlink1-%n symlink2-%k symlink3-%b" - """), - + ''', + ), Rules.new( - "multiple symlinks with a lot of s p a c e s", + 'multiple symlinks with a lot of s p a c e s', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["one", "two"], - not_exp_links = [" "], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['one', 'two'], + not_exp_links=[' '], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK=" one two " - """), - + ''', + ), Rules.new( - "symlink with spaces in substituted variable", + 'symlink with spaces in substituted variable', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["name-one_two_three-end"], - not_exp_links = [" "], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['name-one_two_three-end'], + not_exp_links=[' '], ), - rules = r""" + rules=r''' ENV{WITH_WS}="one two three" SYMLINK="name-$env{WITH_WS}-end" - """), - + ''', + ), Rules.new( - "symlink with leading space in substituted variable", + 'symlink with leading space in substituted variable', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["name-one_two_three-end"], - not_exp_links = [" "], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['name-one_two_three-end'], + not_exp_links=[' '], ), - rules = r""" + rules=r''' ENV{WITH_WS}=" one two three" SYMLINK="name-$env{WITH_WS}-end" - """), - + ''', + ), Rules.new( - "symlink with trailing space in substituted variable", + 'symlink with trailing space in substituted variable', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["name-one_two_three-end"], - not_exp_links = [" "], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['name-one_two_three-end'], + not_exp_links=[' '], ), - rules = r""" + rules=r''' ENV{WITH_WS}="one two three " SYMLINK="name-$env{WITH_WS}-end" - """), - + ''', + ), Rules.new( - "symlink with lots of space in substituted variable", + 'symlink with lots of space in substituted variable', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["name-one_two_three-end"], - not_exp_links = [" "], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['name-one_two_three-end'], + not_exp_links=[' '], ), - rules = r""" + rules=r''' ENV{WITH_WS}=" one two three " SYMLINK="name-$env{WITH_WS}-end" - """), - + ''', + ), Rules.new( - "symlink with multiple spaces in substituted variable", + 'symlink with multiple spaces in substituted variable', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["name-one_two_three-end"], - not_exp_links = [" "], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['name-one_two_three-end'], + not_exp_links=[' '], ), - rules = r""" + rules=r''' ENV{WITH_WS}=" one two three " SYMLINK="name-$env{WITH_WS}-end" - """), - + ''', + ), Rules.new( - "symlink with space and var with space", + 'symlink with space and var with space', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["first", "name-one_two_three-end", - "another_symlink", "a", "b", "c"], - not_exp_links = [" "], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['first', 'name-one_two_three-end', 'another_symlink', 'a', 'b', 'c'], + not_exp_links=[' '], ), - rules = r""" + rules=r''' ENV{WITH_WS}=" one two three " SYMLINK=" first name-$env{WITH_WS}-end another_symlink a b c " - """), - + ''', + ), Rules.new( - "symlink with env which contain slash (see #19309)", + 'symlink with env which contain slash (see #19309)', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["first", "name-aaa_bbb_ccc-end", - "another_symlink", "a", "b", "c"], - not_exp_links = ["ame-aaa/bbb/ccc-end"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['first', 'name-aaa_bbb_ccc-end', 'another_symlink', 'a', 'b', 'c'], + not_exp_links=['ame-aaa/bbb/ccc-end'], ), - rules = r""" + rules=r''' ENV{WITH_SLASH}="aaa/bbb/ccc" OPTIONS="string_escape=replace", ENV{REPLACED}="$env{WITH_SLASH}" SYMLINK=" first name-$env{REPLACED}-end another_symlink a b c " - """), - + ''', + ), Rules.new( - "symlink creation (same directory)", + 'symlink creation (same directory)', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["modem0"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['modem0'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK="modem%n" - """), - + ''', + ), Rules.new( - "multiple symlinks", + 'multiple symlinks', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["first-0", "second-0", "third-0"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['first-0', 'second-0', 'third-0'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM0", SYMLINK="first-%n second-%n third-%n" - """), - + ''', + ), Rules.new( "symlink name '.'", Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', ), # we get a warning, but the process does not fail - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="." - """), - + ''', + ), Rules.new( - "symlink node to itself", + 'symlink node to itself', Device( - "/devices/virtual/tty/tty0", + '/devices/virtual/tty/tty0', ), # we get a warning, but the process does not fail - rules = r""" + rules=r''' KERNEL=="tty0", SYMLINK+="tty0" - """), - + ''', + ), Rules.new( - "symlink %n substitution", + 'symlink %n substitution', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["symlink0"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['symlink0'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="symlink%n" - """), - + ''', + ), Rules.new( - "symlink %k substitution", + 'symlink %k substitution', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["symlink-ttyACM0"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['symlink-ttyACM0'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="symlink-%k" - """), - + ''', + ), Rules.new( - "symlink %M:%m substitution", + 'symlink %M:%m substitution', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["major-166:0"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['major-166:0'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="ttyACM%n", SYMLINK+="major-%M:%m" - """), - + ''', + ), Rules.new( - "symlink %b substitution", + 'symlink %b substitution', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["symlink-0:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['symlink-0:0:0:0'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="symlink-%b" - """), - + ''', + ), Rules.new( - "symlink %c substitution", + 'symlink %c substitution', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["test"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['test'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo test", SYMLINK+="%c" - """), - + ''', + ), Rules.new( - "symlink %c{N} substitution", + 'symlink %c{N} substitution', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["test"], - not_exp_links = ["symlink", "this"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['test'], + not_exp_links=['symlink', 'this'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo symlink test this", SYMLINK+="%c{2}" - """), - + ''', + ), Rules.new( - "symlink %c{N+} substitution", + 'symlink %c{N+} substitution', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["test", "this"], - not_exp_links = ["symlink"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['test', 'this'], + not_exp_links=['symlink'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", PROGRAM=="/bin/echo symlink test this", SYMLINK+="%c{2+}" - """), - + ''', + ), Rules.new( - "symlink only rule with %c{N+}", + 'symlink only rule with %c{N+}', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["test", "this"], - not_exp_links = ["symlink"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['test', 'this'], + not_exp_links=['symlink'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", PROGRAM=="/bin/echo link test this" SYMLINK+="%c{2+}" - """), - + ''', + ), Rules.new( - "symlink %s{filename} substitution", + 'symlink %s{filename} substitution', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["166:0"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['166:0'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="%s{dev}" - """), - + ''', + ), Rules.new( - "program result substitution (numbered part of)", + 'program result substitution (numbered part of)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["link1", "link2"], - not_exp_links = ["node"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['link1', 'link2'], + not_exp_links=['node'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n node link1 link2", RESULT=="node *", SYMLINK+="%c{2} %c{3}" - """), - + ''', + ), Rules.new( - "program result substitution (numbered part of+)", + 'program result substitution (numbered part of+)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["link1", "link2", "link3", "link4"], - not_exp_links = ["node"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['link1', 'link2', 'link3', 'link4'], + not_exp_links=['node'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n node link1 link2 link3 link4", RESULT=="node *", SYMLINK+="%c{2+}" - """), - + ''', + ), Rules.new( - "SUBSYSTEM match test", + 'SUBSYSTEM match test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - not_exp_links = ["should_not_match", "should_not_match2"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + not_exp_links=['should_not_match', 'should_not_match2'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match", SUBSYSTEM=="vc" SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", SUBSYSTEM=="block" SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match2", SUBSYSTEM=="vc" - """), - + ''', + ), Rules.new( - "DRIVERS match test", + 'DRIVERS match test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], - not_exp_links = ["should_not_match"] + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], + not_exp_links=['should_not_match'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="should_not_match", DRIVERS=="sd-wrong" SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", DRIVERS=="sd" - """), - + ''', + ), Rules.new( - "devnode substitution test", + 'devnode substitution test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda", PROGRAM=="/usr/bin/test -b %N" SYMLINK+="node" - """), - + ''', + ), Rules.new( - "parent node name substitution test", + 'parent node name substitution test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["sda-part-1"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['sda-part-1'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="%P-part-%n" - """), - + ''', + ), Rules.new( - "udev_root substitution", + 'udev_root substitution', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["start-/dev-end"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['start-/dev-end'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="start-%r-end" - """), - + ''', + ), Rules.new( # This is not supported any more - "last_rule option", + 'last_rule option', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["last", "very-last"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['last', 'very-last'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="last", OPTIONS="last_rule" SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="very-last" - """), - + ''', + ), Rules.new( - "negation KERNEL!=", + 'negation KERNEL!=', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["match", "before"], - not_exp_links = ["matches-but-is-negated"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['match', 'before'], + not_exp_links=['matches-but-is-negated'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL!="sda1", SYMLINK+="matches-but-is-negated" SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before" SUBSYSTEMS=="scsi", KERNEL!="xsda1", SYMLINK+="match" - """), - + ''', + ), Rules.new( - "negation SUBSYSTEM!=", + 'negation SUBSYSTEM!=', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["before", "not-anything"], - not_exp_links = ["matches-but-is-negated"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['before', 'not-anything'], + not_exp_links=['matches-but-is-negated'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", SUBSYSTEM=="block", KERNEL!="sda1", SYMLINK+="matches-but-is-negated" SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before" SUBSYSTEMS=="scsi", SUBSYSTEM!="anything", SYMLINK+="not-anything" - """), - + ''', + ), Rules.new( - "negation PROGRAM!= exit code", + 'negation PROGRAM!= exit code', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["before", "nonzero-program"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['before', 'nonzero-program'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before" KERNEL=="sda1", PROGRAM!="/bin/false", SYMLINK+="nonzero-program" - """), - + ''', + ), Rules.new( - "ENV{} test", + 'ENV{} test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["true"], - not_exp_links = ["bad", "wrong"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['true'], + not_exp_links=['bad', 'wrong'], ), - rules = r""" + rules=r''' ENV{ENV_KEY_TEST}="test" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="go", SYMLINK+="wrong" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="test", SYMLINK+="true" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="bad", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "ENV{} test", + 'ENV{} test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["true"], - not_exp_links = ["bad", "wrong", "no"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['true'], + not_exp_links=['bad', 'wrong', 'no'], ), - rules = r""" + rules=r''' ENV{ENV_KEY_TEST}="test" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="go", SYMLINK+="wrong" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="yes", ENV{ACTION}=="add", ENV{DEVPATH}=="*/block/sda/sdax1", SYMLINK+="no" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="test", ENV{ACTION}=="add", ENV{DEVPATH}=="*/block/sda/sda1", SYMLINK+="true" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ENV_KEY_TEST}=="bad", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "ENV{} test (assign)", + 'ENV{} test (assign)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["true", "before"], - not_exp_links = ["no"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['true', 'before'], + not_exp_links=['no'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="true" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="yes", SYMLINK+="no" SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="true", SYMLINK+="true" - """), - + ''', + ), Rules.new( - "ENV{} test (assign 2 times)", + 'ENV{} test (assign 2 times)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["true", "before"], - not_exp_links = ["no", "bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['true', 'before'], + not_exp_links=['no', 'bad'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="true" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="absolutely-$env{ASSIGN}" SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="yes", SYMLINK+="no" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="true", SYMLINK+="bad" SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="absolutely-true", SYMLINK+="true" - """), - + ''', + ), Rules.new( - "ENV{} test (assign2)", + 'ENV{} test (assign2)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["part"], - not_exp_links = ["disk"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['part'], + not_exp_links=['disk'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["disk"], - not_exp_links = ["part"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['disk'], + not_exp_links=['part'], ), - rules = r""" + rules=r''' SUBSYSTEM=="block", KERNEL=="*[0-9]", ENV{PARTITION}="true", ENV{MAINDEVICE}="false" SUBSYSTEM=="block", KERNEL=="*[!0-9]", ENV{PARTITION}="false", ENV{MAINDEVICE}="true" ENV{MAINDEVICE}=="true", SYMLINK+="disk" SUBSYSTEM=="block", SYMLINK+="before" ENV{PARTITION}=="true", SYMLINK+="part" - """), - + ''', + ), Rules.new( - "untrusted string sanitize", + 'untrusted string sanitize', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["sane"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['sane'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e name; (/usr/bin/badprogram)", RESULT=="name_ _/usr/bin/badprogram_", SYMLINK+="sane" - """), - + ''', + ), Rules.new( "untrusted string sanitize (don't replace utf8)", Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["uber"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['uber'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e \xc3\xbcber" RESULT=="über", SYMLINK+="uber" - """), - + ''', + ), Rules.new( - "untrusted string sanitize (replace invalid utf8)", + 'untrusted string sanitize (replace invalid utf8)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["replaced"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['replaced'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", KERNEL=="sda1", PROGRAM=="/bin/echo -e \xef\xe8garbage", RESULT=="__garbage", SYMLINK+="replaced" - """), - + ''', + ), Rules.new( - "read sysfs value from parent device", + 'read sysfs value from parent device', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["serial-354172020305000"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['serial-354172020305000'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM*", ATTRS{serial}=="?*", SYMLINK+="serial-%s{serial}" - """), - + ''', + ), Rules.new( - "match against empty key string", + 'match against empty key string', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["ok"], - not_exp_links = ["not-1-ok", "not-2-ok", "not-3-ok"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['ok'], + not_exp_links=['not-1-ok', 'not-2-ok', 'not-3-ok'], ), - rules = r""" + rules=r''' KERNEL=="sda", ATTRS{nothing}!="", SYMLINK+="not-1-ok" KERNEL=="sda", ATTRS{nothing}=="", SYMLINK+="not-2-ok" KERNEL=="sda", ATTRS{vendor}!="", SYMLINK+="ok" KERNEL=="sda", ATTRS{vendor}=="", SYMLINK+="not-3-ok" - """), - + ''', + ), Rules.new( - "check ACTION value", + 'check ACTION value', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["ok"], - not_exp_links = ["unknown-not-ok"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['ok'], + not_exp_links=['unknown-not-ok'], ), - rules = r""" + rules=r''' ACTION=="unknown", KERNEL=="sda", SYMLINK+="unknown-not-ok" ACTION=="add", KERNEL=="sda", SYMLINK+="ok" - """), - + ''', + ), Rules.new( - "final assignment", + 'final assignment', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["ok"], - exp_perms = "root:tty:0640", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['ok'], + exp_perms='root:tty:0640', ), - rules = r""" + rules=r''' KERNEL=="sda", GROUP:="tty" KERNEL=="sda", GROUP="root", MODE="0640", SYMLINK+="ok" - """), - + ''', + ), Rules.new( - "final assignment 2", + 'final assignment 2', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["ok"], - exp_perms = "root:tty:0640", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['ok'], + exp_perms='root:tty:0640', ), - rules = r""" + rules=r''' KERNEL=="sda", GROUP:="tty" SUBSYSTEM=="block", MODE:="640" KERNEL=="sda", GROUP="root", MODE="0666", SYMLINK+="ok" - """), - + ''', + ), Rules.new( - "env substitution", + 'env substitution', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["node-add-me"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['node-add-me'], ), - rules = r""" + rules=r''' KERNEL=="sda", MODE="0666", SYMLINK+="node-$env{ACTION}-me" - """), - + ''', + ), Rules.new( - "reset list to current value", + 'reset list to current value', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["three"], - not_exp_links = ["two", "one"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['three'], + not_exp_links=['two', 'one'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="one" KERNEL=="ttyACM[0-9]*", SYMLINK+="two" KERNEL=="ttyACM[0-9]*", SYMLINK="three" - """), - + ''', + ), Rules.new( - "test empty SYMLINK+ (empty override)", + 'test empty SYMLINK+ (empty override)', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["right"], - not_exp_links = ["wrong"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['right'], + not_exp_links=['wrong'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM[0-9]*", SYMLINK+="wrong" KERNEL=="ttyACM[0-9]*", SYMLINK="" KERNEL=="ttyACM[0-9]*", SYMLINK+="right" - """), - + ''', + ), Rules.new( - "test multi matches", + 'test multi matches', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["right", "before"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['right', 'before'], ), - rules = r""" + rules=r''' KERNEL=="ttyACM*", SYMLINK+="before" KERNEL=="ttyACM*|nothing", SYMLINK+="right" - """), - + ''', + ), Rules.new( - "test multi matches 2", + 'test multi matches 2', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["right", "before"], - not_exp_links = ["nomatch"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['right', 'before'], + not_exp_links=['nomatch'], ), - rules = r""" + rules=r''' KERNEL=="dontknow*|*nothing", SYMLINK+="nomatch" KERNEL=="ttyACM*", SYMLINK+="before" KERNEL=="dontknow*|ttyACM*|nothing*", SYMLINK+="right" - """), - + ''', + ), Rules.new( - "test multi matches 3", + 'test multi matches 3', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["right"], - not_exp_links = ["nomatch", "wrong1", "wrong2"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['right'], + not_exp_links=['nomatch', 'wrong1', 'wrong2'], ), - rules = r""" + rules=r''' KERNEL=="dontknow|nothing", SYMLINK+="nomatch" KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1" KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2" KERNEL=="dontknow|ttyACM0|nothing", SYMLINK+="right" - """), - + ''', + ), Rules.new( - "test multi matches 4", + 'test multi matches 4', Device( - "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_links = ["right"], - not_exp_links = ["nomatch", "wrong1", "wrong2", "wrong3"], + '/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0', + exp_links=['right'], + not_exp_links=['nomatch', 'wrong1', 'wrong2', 'wrong3'], ), - rules = r""" + rules=r''' KERNEL=="dontknow|nothing", SYMLINK+="nomatch" KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1" KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2" KERNEL=="all|dontknow|ttyACM0", SYMLINK+="right" KERNEL=="ttyACM0a|nothing", SYMLINK+="wrong3" - """), - + ''', + ), Rules.new( - "test multi matches 5", + 'test multi matches 5', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="sda", TAG="foo" TAGS=="|foo", SYMLINK+="found" TAGS=="|aaa", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "test multi matches 6", + 'test multi matches 6', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="sda", ENV{HOGE}="" ENV{HOGE}=="|foo", SYMLINK+="found" ENV{HOGE}=="aaa|bbb", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "test multi matches 7", + 'test multi matches 7', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="sda", TAG="foo" TAGS=="foo||bar", SYMLINK+="found" TAGS=="aaa||bbb", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "test multi matches 8", + 'test multi matches 8', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="sda", ENV{HOGE}="" ENV{HOGE}=="foo||bar", SYMLINK+="found" ENV{HOGE}=="aaa|bbb", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "test multi matches 9", + 'test multi matches 9', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found", "found2"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found', 'found2'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="sda", TAG="foo" TAGS=="foo|", SYMLINK+="found" TAGS=="aaa|", SYMLINK+="bad" KERNEL=="sda", TAGS!="hoge", SYMLINK+="found2" KERNEL=="sda", TAGS!="foo", SYMLINK+="bad2" - """), - + ''', + ), Rules.new( - "test multi matches 10", + 'test multi matches 10', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="sda", ENV{HOGE}="" ENV{HOGE}=="foo|", SYMLINK+="found" ENV{HOGE}=="aaa|bbb", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "test multi matches 11", + 'test multi matches 11', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' KERNEL=="sda", TAG="c" TAGS=="foo||bar||c", SYMLINK+="found" TAGS=="aaa||bbb||ccc", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "TAG refuses invalid string", + 'TAG refuses invalid string', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["valid", "found"], - not_exp_links = ["empty", "invalid_char", "path", "bad", "bad2"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['valid', 'found'], + not_exp_links=['empty', 'invalid_char', 'path', 'bad', 'bad2'], ), - rules = r""" + rules=r''' KERNEL=="sda", TAG+="", TAG+="invalid.char", TAG+="path/is/also/invalid", TAG+="valid" TAGS=="", SYMLINK+="empty" TAGS=="invalid.char", SYMLINK+="invalid_char" @@ -1856,33 +1868,33 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c \"printf %%s 'foo1 foo2' | grep 'foo1 TAGS=="valid|", SYMLINK+="found" TAGS=="aaa|", SYMLINK+="bad" TAGS=="aaa|bbb", SYMLINK+="bad2" - """), - + ''', + ), Rules.new( - "IMPORT parent test", + 'IMPORT parent test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["parent"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['parent'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["parentenv-parent_right"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['parentenv-parent_right'], ), - delay = 500000, # Serialized! We need to sleep here after adding sda - rules = r""" + delay=500000, # Serialized! We need to sleep here after adding sda + rules=r''' KERNEL=="sda1", IMPORT{parent}="PARENT*", SYMLINK+="parentenv-$env{PARENT_KEY}$env{WRONG_PARENT_KEY}" KERNEL=="sda", IMPORT{program}="/bin/echo -e 'PARENT_KEY=parent_right\nWRONG_PARENT_KEY=parent_wrong'" KERNEL=="sda", SYMLINK+="parent" - """), - + ''', + ), Rules.new( - "GOTO test", + 'GOTO test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["right"], - not_exp_links = ["wrong", "wrong2"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['right'], + not_exp_links=['wrong', 'wrong2'], ), - rules = r""" + rules=r''' KERNEL=="sda1", GOTO="TEST" KERNEL=="sda1", SYMLINK+="wrong" KERNEL=="sda1", GOTO="BAD" @@ -1890,207 +1902,207 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c \"printf %%s 'foo1 foo2' | grep 'foo1 KERNEL=="sda1", SYMLINK+="right", LABEL="TEST", GOTO="end" KERNEL=="sda1", SYMLINK+="wrong2", LABEL="BAD" LABEL="end" - """), - + ''', + ), Rules.new( - "GOTO label does not exist", + 'GOTO label does not exist', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["right"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['right'], ), - rules = r""" + rules=r''' KERNEL=="sda1", GOTO="does-not-exist" KERNEL=="sda1", SYMLINK+="right", LABEL="exists" - """), - + ''', + ), Rules.new( - "SYMLINK+ compare test", + 'SYMLINK+ compare test', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["right", "link"], - not_exp_links = ["wrong"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['right', 'link'], + not_exp_links=['wrong'], ), - rules = r""" + rules=r''' KERNEL=="sda1", SYMLINK+="link" KERNEL=="sda1", SYMLINK=="link*", SYMLINK+="right" KERNEL=="sda1", SYMLINK=="nolink*", SYMLINK+="wrong" - """), - + ''', + ), Rules.new( - "invalid key operation", + 'invalid key operation', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["yes"], - not_exp_links = ["no"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['yes'], + not_exp_links=['no'], ), - rules = r""" + rules=r''' KERNEL="sda1", SYMLINK+="no" KERNEL=="sda1", SYMLINK+="yes" - """), - + ''', + ), Rules.new( - "operator chars in attribute", + 'operator chars in attribute', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["yes"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['yes'], ), - rules = r""" + rules=r''' KERNEL=="sda", ATTR{test:colon+plus}=="?*", SYMLINK+="yes" - """), - + ''', + ), Rules.new( - "overlong comment line", + 'overlong comment line', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["yes"], - not_exp_links = ["no"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['yes'], + not_exp_links=['no'], ), - rules = r""" + rules=r''' # 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 # 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 KERNEL=="sda1", SYMLINK+=="no" KERNEL=="sda1", SYMLINK+="yes" - """), - + ''', + ), Rules.new( - "magic subsys/kernel lookup", + 'magic subsys/kernel lookup', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["00:16:41:e2:8d:ff"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['00:16:41:e2:8d:ff'], ), - rules = r""" + rules=r''' KERNEL=="sda", SYMLINK+="$attr{[net/eth0]address}" - """), - + ''', + ), Rules.new( - "TEST absolute path", + 'TEST absolute path', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["there"], - not_exp_links = ["notthere"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['there'], + not_exp_links=['notthere'], ), - rules = r""" + rules=r''' TEST=="/etc/passwd", SYMLINK+="there" TEST!="/etc/passwd", SYMLINK+="notthere" - """), - + ''', + ), Rules.new( - "TEST subsys/kernel lookup", + 'TEST subsys/kernel lookup', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["yes"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['yes'], ), - rules = r""" + rules=r''' KERNEL=="sda", TEST=="[net/eth0]", SYMLINK+="yes" - """), - + ''', + ), Rules.new( - "TEST relative path", + 'TEST relative path', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["relative"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['relative'], ), - rules = r""" + rules=r''' KERNEL=="sda", TEST=="size", SYMLINK+="relative" - """), - + ''', + ), Rules.new( - "TEST wildcard substitution (find queue/nr_requests)", + 'TEST wildcard substitution (find queue/nr_requests)', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found-subdir"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found-subdir'], ), - rules = r""" + rules=r''' KERNEL=="sda", TEST=="*/nr_requests", SYMLINK+="found-subdir" - """), - + ''', + ), Rules.new( - "TEST MODE=0000", + 'TEST MODE=0000', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_perms = "0:0:0000", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_perms='0:0:0000', ), - rules = r""" + rules=r''' KERNEL=="sda", MODE="0000" - """), - + ''', + ), Rules.new( - "TEST PROGRAM feeds OWNER, GROUP, MODE", + 'TEST PROGRAM feeds OWNER, GROUP, MODE', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_perms = "1:1:0400", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_perms='1:1:0400', ), - rules = r""" + rules=r''' KERNEL=="sda", MODE="666" KERNEL=="sda", PROGRAM=="/bin/echo 1 1 0400", OWNER="%c{1}", GROUP="%c{2}", MODE="%c{3}" - """), - + ''', + ), Rules.new( - "TEST PROGRAM feeds MODE with overflow", + 'TEST PROGRAM feeds MODE with overflow', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_perms = "0:0:0440", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_perms='0:0:0440', ), - rules = r""" + rules=r''' KERNEL=="sda", MODE="440" KERNEL=="sda", PROGRAM=="/bin/echo 0 0 0400letsdoabuffferoverflow0123456789012345789012345678901234567890", OWNER="%c{1}", GROUP="%c{2}", MODE="%c{3}" - """), - + ''', + ), Rules.new( - "magic [subsys/sysname] attribute substitution", + 'magic [subsys/sysname] attribute substitution', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["sda-8741C4G-end"], - exp_perms = "0:0:0600", + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['sda-8741C4G-end'], + exp_perms='0:0:0600', ), - rules = r""" + rules=r''' KERNEL=="sda", SYMLINK+="%k-%s{[dmi/id]product_name}-end" - """), - + ''', + ), Rules.new( - "builtin path_id", + 'builtin path_id', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0'], ), - rules = r""" + rules=r''' KERNEL=="sda", IMPORT{builtin}="path_id" KERNEL=="sda", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" - """), - + ''', + ), Rules.new( - "add and match tag", + 'add and match tag', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", TAG+="green" TAGS=="green", SYMLINK+="found" TAGS=="blue", SYMLINK+="bad" - """), - + ''', + ), Rules.new( "don't crash with lots of tags", Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], ), - rules = f""" + rules=f''' {rules_10k_tags} TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="found" - """), - + ''', + ), Rules.new( - "continuations", + 'continuations', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = f""" + rules=f''' {rules_10k_tags_continuation} TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="bad" KERNEL=="sda",\\ @@ -2104,16 +2116,16 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c \"printf %%s 'foo1 foo2' | grep 'foo1 \\ TAG+="hoge4" TAGS=="hoge1", TAGS=="hoge2", TAGS=="hoge3", TAGS=="hoge4", SYMLINK+="found" - """), - + ''', + ), Rules.new( - "continuations with empty line", + 'continuations with empty line', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = r""" + rules=r''' # empty line finishes continuation KERNEL=="sda", TAG+="foo" \ @@ -2122,16 +2134,16 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c \"printf %%s 'foo1 foo2' | grep 'foo1 KERNEL=="sdb", TAG+="bbb" TAGS=="foo", SYMLINK+="found" TAGS=="aaa", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "continuations with space only line", + 'continuations with space only line', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_links = ["found"], - not_exp_links = ["bad"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda', + exp_links=['found'], + not_exp_links=['bad'], ), - rules = """ + rules=''' # space only line finishes continuation KERNEL=="sda", TAG+="foo" \\ \t @@ -2140,192 +2152,194 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/bash -c \"printf %%s 'foo1 foo2' | grep 'foo1 KERNEL=="sdb", TAG+="bbb" TAGS=="foo", SYMLINK+="found" TAGS=="aaa", SYMLINK+="bad" - """), - + ''', + ), Rules.new( - "multiple devices", + 'multiple devices', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["part-1"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['part-1'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["part-5"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['part-5'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", - exp_links = ["part-6"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6', + exp_links=['part-6'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", - exp_links = ["part-7"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7', + exp_links=['part-7'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", - exp_links = ["part-8"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8', + exp_links=['part-8'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", - exp_links = ["part-9"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9', + exp_links=['part-9'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", - exp_links = ["part-10"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10', + exp_links=['part-10'], ), - rules = r""" + rules=r''' SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n" - """), - + ''', + ), Rules.new( - "multiple devices, same link name, positive prio", + 'multiple devices, same link name, positive prio', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["part-1"], - not_exp_links = ["partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['part-1'], + not_exp_links=['partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["part-5"], - not_exp_links = ["partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['part-5'], + not_exp_links=['partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", - not_exp_links = ["partition"], - exp_links = ["part-6"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6', + not_exp_links=['partition'], + exp_links=['part-6'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", - exp_links = ["part-7", "partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7', + exp_links=['part-7', 'partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", - not_exp_links = ["partition"], - exp_links = ["part-8"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8', + not_exp_links=['partition'], + exp_links=['part-8'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", - not_exp_links = ["partition"], - exp_links = ["part-9"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9', + not_exp_links=['partition'], + exp_links=['part-9'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", - not_exp_links = ["partition"], - exp_links = ["part-10"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10', + not_exp_links=['partition'], + exp_links=['part-10'], ), - repeat = 100, - rules = r""" + repeat=100, + rules=r''' SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n" SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition" KERNEL=="*7", OPTIONS+="link_priority=10" - """), - + ''', + ), Rules.new( - "multiple devices, same link name, negative prio", + 'multiple devices, same link name, negative prio', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["part-1"], - not_exp_links = ["partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['part-1'], + not_exp_links=['partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["part-5"], - not_exp_links = ["partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['part-5'], + not_exp_links=['partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", - not_exp_links = ["partition"], - exp_links = ["part-6"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6', + not_exp_links=['partition'], + exp_links=['part-6'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", - exp_links = ["part-7", "partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7', + exp_links=['part-7', 'partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", - not_exp_links = ["partition"], - exp_links = ["part-8"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8', + not_exp_links=['partition'], + exp_links=['part-8'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", - not_exp_links = ["partition"], - exp_links = ["part-9"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9', + not_exp_links=['partition'], + exp_links=['part-9'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", - not_exp_links = ["partition"], - exp_links = ["part-10"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10', + not_exp_links=['partition'], + exp_links=['part-10'], ), - rules = r""" + rules=r''' SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n" SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition" KERNEL!="*7", OPTIONS+="link_priority=-10" - """), - + ''', + ), Rules.new( - "multiple devices, same link name, positive prio, sleep", + 'multiple devices, same link name, positive prio, sleep', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["part-1"], - not_exp_links = ["partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['part-1'], + not_exp_links=['partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_links = ["part-5"], - not_exp_links = ["partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5', + exp_links=['part-5'], + not_exp_links=['partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", - not_exp_links = ["partition"], - exp_links = ["part-6"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6', + not_exp_links=['partition'], + exp_links=['part-6'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", - exp_links = ["part-7", "partition"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7', + exp_links=['part-7', 'partition'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", - not_exp_links = ["partition"], - exp_links = ["part-8"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8', + not_exp_links=['partition'], + exp_links=['part-8'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", - not_exp_links = ["partition"], - exp_links = ["part-9"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9', + not_exp_links=['partition'], + exp_links=['part-9'], ), Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", - not_exp_links = ["partition"], - exp_links = ["part-10"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10', + not_exp_links=['partition'], + exp_links=['part-10'], ), - delay = 10000, - rules = r""" + delay=10000, + rules=r''' SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="part-%n" SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partition", SYMLINK+="partition" KERNEL=="*7", OPTIONS+="link_priority=10" - """), - + ''', + ), Rules.new( 'all_block_devs', - device_generator = lambda: \ - all_block_devs(lambda name: (["blockdev"], None) if name.endswith('/sda6') else (None, None)), - repeat = 10, - rules = r""" + device_generator=lambda: all_block_devs( + lambda name: (['blockdev'], None) if name.endswith('/sda6') else (None, None) + ), + repeat=10, + rules=r''' SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev" KERNEL=="sda6", OPTIONS+="link_priority=10" - """), - + ''', + ), Rules.new( - "case insensitive match", + 'case insensitive match', Device( - "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_links = ["ok"], + '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1', + exp_links=['ok'], ), - - rules = r""" + rules=r''' KERNEL==i"SDA1", SUBSYSTEMS==i"SCSI", ATTRS{vendor}==i"a?a", SYMLINK+="ok" - """), + ''', + ), ] + def fork_and_run_udev(action: str, rules: Rules) -> None: kinder = [] for k, device in enumerate(rules.devices): @@ -2351,13 +2365,11 @@ def environment_issue(): if os.getuid() != 0: return 'Must be root to run properly' - c = subprocess.run(['systemd-detect-virt', '-r', '-q'], - check=False) + c = subprocess.run(['systemd-detect-virt', '-r', '-q'], check=False) if c.returncode == 0: return 'Running in a chroot, skipping the test' - c = subprocess.run(['systemd-detect-virt', '-c', '-q'], - check=False) + c = subprocess.run(['systemd-detect-virt', '-c', '-q'], check=False) if c.returncode == 0: return 'Running in a container, skipping the test' @@ -2375,27 +2387,24 @@ def udev_setup(): _tmpdir = tempfile.TemporaryDirectory() tmpdir = Path(_tmpdir.name) - UDEV_RUN = tmpdir / 'run' + UDEV_RUN = tmpdir / 'run' UDEV_RULES = UDEV_RUN / 'udev-test.rules' udev_tmpfs = tmpdir / 'tmpfs' - UDEV_DEV = udev_tmpfs / 'dev' - UDEV_SYS = udev_tmpfs / 'sys' + UDEV_DEV = udev_tmpfs / 'dev' + UDEV_SYS = udev_tmpfs / 'sys' - subprocess.run(['umount', udev_tmpfs], - stderr=subprocess.DEVNULL, - check=False) + subprocess.run(['umount', udev_tmpfs], stderr=subprocess.DEVNULL, check=False) udev_tmpfs.mkdir(exist_ok=True, parents=True) - subprocess.check_call(['mount', '-v', - '-t', 'tmpfs', - '-o', 'rw,mode=0755,nosuid,noexec', - 'tmpfs', udev_tmpfs]) + subprocess.check_call( + ['mount', '-v', '-t', 'tmpfs', '-o', 'rw,mode=0755,nosuid,noexec', 'tmpfs', udev_tmpfs] + ) UDEV_DEV.mkdir(exist_ok=True) # setting group and mode of udev_dev ensures the tests work # even if the parent directory has setgid bit enabled. - os.chmod(UDEV_DEV,0o755) + os.chmod(UDEV_DEV, 0o755) os.chown(UDEV_DEV, 0, 0) os.mknod(UDEV_DEV / 'null', 0o600 | stat.S_IFCHR, os.makedev(1, 3)) @@ -2411,10 +2420,10 @@ def udev_setup(): os.chdir(tmpdir) - if subprocess.run([UDEV_BIN, 'check'], - check=False).returncode != 0: - pytest.skip(f'{UDEV_BIN} failed to set up the environment, skipping the test', - allow_module_level=True) + if subprocess.run([UDEV_BIN, 'check'], check=False).returncode != 0: + pytest.skip( + f'{UDEV_BIN} failed to set up the environment, skipping the test', allow_module_level=True + ) yield @@ -2423,7 +2432,7 @@ def udev_setup(): udev_tmpfs.rmdir() -@pytest.mark.parametrize("rules", RULES, ids=(rule.desc for rule in RULES)) +@pytest.mark.parametrize('rules', RULES, ids=(rule.desc for rule in RULES)) def test_udev(rules: Rules, udev_setup): assert udev_setup is None @@ -2441,6 +2450,7 @@ def test_udev(rules: Rules, udev_setup): for device in rules.devices: device.check_remove() + if __name__ == '__main__': issue = environment_issue() if issue: From b27bd1a39941e00ed1b6e24bbca9dc9d07faac46 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:03:17 +0900 Subject: [PATCH 34/62] TEST-69-SHUTDOWN: apply "ruff format" --- test/units/TEST-69-SHUTDOWN.py | 54 ++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/test/units/TEST-69-SHUTDOWN.py b/test/units/TEST-69-SHUTDOWN.py index 51569bf51e..a208e42f07 100755 --- a/test/units/TEST-69-SHUTDOWN.py +++ b/test/units/TEST-69-SHUTDOWN.py @@ -10,56 +10,58 @@ import pexpect def main(): # TODO: drop once https://bugs.debian.org/1075733 is fixed - with open("/usr/lib/os-release") as f: + with open('/usr/lib/os-release') as f: for line in f: - if line.startswith("ID="): - if "debian" in line or "ubuntu" in line: + if line.startswith('ID='): + if 'debian' in line or 'ubuntu' in line: sys.exit(77) - logger = logging.getLogger("test-shutdown") + logger = logging.getLogger('test-shutdown') consoles = [] for _ in range(2): # Use script to allocate a separate pseudo tty to run the login shell in. console = pexpect.spawn( - "script", ["--quiet", "--return", "--flush", "--command", "login -f root", "/dev/null"], + 'script', + ['--quiet', '--return', '--flush', '--command', 'login -f root', '/dev/null'], logfile=sys.stdout, - env={"TERM": "dumb"}, - encoding="utf-8", + env={'TERM': 'dumb'}, + encoding='utf-8', timeout=60, ) - logger.info("waiting for login prompt") - console.expect(".*# ", 10) + logger.info('waiting for login prompt') + console.expect('.*# ', 10) consoles += [console] - consoles[1].sendline("tty") - consoles[1].expect(r"/dev/(pts/\d+)") + consoles[1].sendline('tty') + consoles[1].expect(r'/dev/(pts/\d+)') pty = console.match.group(1) - logger.info("window 1 at tty %s", pty) + logger.info('window 1 at tty %s', pty) - logger.info("schedule reboot") - consoles[1].sendline("shutdown -r") + logger.info('schedule reboot') + consoles[1].sendline('shutdown -r') consoles[1].expect("Reboot scheduled for (?P.*), use 'shutdown -c' to cancel", 2) - date = consoles[1].match.group("date") - logger.info("reboot scheduled for %s", date) + date = consoles[1].match.group('date') + logger.info('reboot scheduled for %s', date) - logger.info("verify broadcast message") - consoles[0].expect(f"Broadcast message from root@H on {pty}", 2) - consoles[0].expect(f"The system will reboot at {date}!", 2) + logger.info('verify broadcast message') + consoles[0].expect(f'Broadcast message from root@H on {pty}', 2) + consoles[0].expect(f'The system will reboot at {date}!', 2) - logger.info("check show output") - consoles[1].sendline("shutdown --show") + logger.info('check show output') + consoles[1].sendline('shutdown --show') consoles[1].expect(f"Reboot scheduled for {date}, use 'shutdown -c' to cancel", 2) - logger.info("cancel shutdown") - consoles[1].sendline("shutdown -c") - consoles[0].expect("System shutdown has been cancelled", 2) + logger.info('cancel shutdown') + consoles[1].sendline('shutdown -c') + consoles[0].expect('System shutdown has been cancelled', 2) - consoles[0].sendline("> /testok") + consoles[0].sendline('> /testok') -if __name__ == "__main__": + +if __name__ == '__main__': main() # vim: sw=4 et From ce9b618e4675ca185ca68e89c164d3acd0a64864 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:03:44 +0900 Subject: [PATCH 35/62] analyze-dump-sort: apply "ruff format" --- tools/analyze-dump-sort.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/analyze-dump-sort.py b/tools/analyze-dump-sort.py index a464a14bd1..da6094f996 100755 --- a/tools/analyze-dump-sort.py +++ b/tools/analyze-dump-sort.py @@ -58,6 +58,7 @@ def sort_dump(sourcefile, destfile=None): destfile.flush() return destfile + def parse_args(): p = argparse.ArgumentParser(description=__doc__) p.add_argument('one') @@ -65,6 +66,7 @@ def parse_args(): p.add_argument('--user', action='store_true') return p.parse_args() + if __name__ == '__main__': opts = parse_args() @@ -73,8 +75,7 @@ if __name__ == '__main__': two = sort_dump(open(opts.two)) else: user = ['--user'] if opts.user else [] - two = subprocess.run(['systemd-analyze', 'dump', *user], - capture_output=True, text=True, check=True) + two = subprocess.run(['systemd-analyze', 'dump', *user], capture_output=True, text=True, check=True) two = sort_dump(two.stdout.splitlines()) with subprocess.Popen(['diff', '-U10', one.name, two.name], stdout=subprocess.PIPE) as diff: subprocess.Popen(['less'], stdin=diff.stdout) From 0965c5d71974ca2e0838ac44fbde62eaa115d617 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:03:56 +0900 Subject: [PATCH 36/62] catalog-report: apply "ruff format" --- tools/catalog-report.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/catalog-report.py b/tools/catalog-report.py index 060b1aae86..f7e67c2fb4 100755 --- a/tools/catalog-report.py +++ b/tools/catalog-report.py @@ -36,9 +36,13 @@ from systemd import id128, journal def log_entry(entry): if 'CODE_FILE' in entry: # some of our code was using 'CODE_FUNCTION' instead of 'CODE_FUNC' - print('{}:{} {}'.format(entry.get('CODE_FILE', '???'), - entry.get('CODE_LINE', '???'), - entry.get('CODE_FUNC', None) or entry.get('CODE_FUNCTION', '???'))) + print( + '{}:{} {}'.format( + entry.get('CODE_FILE'), + entry.get('CODE_LINE', '???'), + entry.get('CODE_FUNC', None) or entry.get('CODE_FUNCTION', '???'), + ) + ) print(' {}'.format(entry.get('MESSAGE', 'no message!'))) for k, v in entry.items(): if k.startswith('CODE_') or k in {'MESSAGE_ID', 'MESSAGE'}: @@ -46,12 +50,13 @@ def log_entry(entry): print(f' {k}={v}') print() + if __name__ == '__main__': j = journal.Reader() logged = set() pattern = re.compile('@[A-Z0-9_]+@') - mids = { v:k for k,v in id128.__dict__.items() if k.startswith('SD_MESSAGE') } + mids = {v: k for k, v in id128.__dict__.items() if k.startswith('SD_MESSAGE')} for i, x in enumerate(j): if i % 1000 == 0: From 53172028c8070c77607a6e2569e0c9aa985c5fcb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:04:20 +0900 Subject: [PATCH 37/62] check-efi-alignment: apply "ruff format" --- tools/check-efi-alignment.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/check-efi-alignment.py b/tools/check-efi-alignment.py index abdeb22fdb..b442a16f55 100755 --- a/tools/check-efi-alignment.py +++ b/tools/check-efi-alignment.py @@ -15,22 +15,23 @@ def main(): pe = pefile.PE(sys.argv[1], fast_load=True) for section in pe.sections: - name = section.Name.rstrip(b"\x00").decode() + name = section.Name.rstrip(b'\x00').decode() file_addr = section.PointerToRawData virt_addr = section.VirtualAddress - print(f"{name:10s} file=0x{file_addr:08x} virt=0x{virt_addr:08x}") + print(f'{name:10s} file=0x{file_addr:08x} virt=0x{virt_addr:08x}') if file_addr % 512 != 0: - print(f"File address of {name} section is not aligned to 512 bytes", file=sys.stderr) + print(f'File address of {name} section is not aligned to 512 bytes', file=sys.stderr) return 1 if virt_addr % 512 != 0: - print(f"Virt address of {name} section is not aligned to 512 bytes", file=sys.stderr) + print(f'Virt address of {name} section is not aligned to 512 bytes', file=sys.stderr) return 1 + if __name__ == '__main__': if len(sys.argv) != 2: - print(f"Usage: {sys.argv[0]} pe-image") + print(f'Usage: {sys.argv[0]} pe-image') sys.exit(1) sys.exit(main()) From a5d54f52d6d36711924c17cfe90e4d75ae30fcd7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:04:33 +0900 Subject: [PATCH 38/62] check-version-history: apply "ruff format" and "ruff check --fix" --- tools/check-version-history.py | 83 ++++++++++++++-------------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/tools/check-version-history.py b/tools/check-version-history.py index db32b6920a..39cf127b98 100755 --- a/tools/check-version-history.py +++ b/tools/check-version-history.py @@ -20,17 +20,14 @@ def find_undocumented_functions(pages, ignorelist): filename = os.path.basename(page) pagetree = tree.parse(page) - assert pagetree.getroot().tag == "refentry" + assert pagetree.getroot().tag == 'refentry' hist_section = pagetree.find("refsect1[title='History']") - for func in pagetree.findall(".//funcprototype/funcdef/function"): + for func in pagetree.findall('.//funcprototype/funcdef/function'): path = f"./refsynopsisdiv/funcsynopsis/funcprototype/funcdef/function[.='{func.text}']" assert pagetree.findall(path) == [func] - if ( - hist_section is None - or hist_section.find(f"para/function[.='{func.text}()']") is None - ): + if hist_section is None or hist_section.find(f"para/function[.='{func.text}()']") is None: if func.text not in ignorelist: undocumented.append((filename, func.text)) return undocumented @@ -39,22 +36,22 @@ def find_undocumented_functions(pages, ignorelist): def construct_path(element): tag = element.tag - if tag == "refentry": - return "." + if tag == 'refentry': + return '.' - predicate = "" - if tag == "varlistentry": - text = "".join(element.find("term").itertext()) + predicate = '' + if tag == 'varlistentry': + text = ''.join(element.find('term').itertext()) predicate = f'[term="{text}"]' - elif tag.startswith("refsect"): - text = "".join(element.find("title").itertext()) + elif tag.startswith('refsect'): + text = ''.join(element.find('title').itertext()) predicate = f'[title="{text}"]' - elif tag == "variablelist": + elif tag == 'variablelist': varlists = element.getparent().findall(tag) if len(varlists) > 1: - predicate = f"[{varlists.index(element)+1}]" + predicate = f'[{varlists.index(element) + 1}]' - return construct_path(element.getparent()) + "/" + tag + predicate + return construct_path(element.getparent()) + '/' + tag + predicate def find_undocumented_commands(pages, ignorelist): @@ -63,23 +60,21 @@ def find_undocumented_commands(pages, ignorelist): filename = os.path.basename(page) pagetree = tree.parse(page) - if pagetree.getroot().tag != "refentry": + if pagetree.getroot().tag != 'refentry': continue - for varlistentry in pagetree.findall("*//variablelist/varlistentry"): + for varlistentry in pagetree.findall('*//variablelist/varlistentry'): path = construct_path(varlistentry) assert pagetree.findall(path) == [varlistentry] - listitem = varlistentry.find("listitem") + listitem = varlistentry.find('listitem') parent = listitem if listitem is not None else varlistentry rev = parent.getchildren()[-1] - if ( - rev.get("href") != "version-info.xml" and - not path.startswith(tuple(entry[1] for entry in ignorelist if entry[0] == filename)) - ): - undocumented.append((filename, path)) + ignored_files = tuple(entry[1] for entry in ignorelist if entry[0] == filename) + if rev.get('href') != 'version-info.xml' and not path.startswith(ignored_files): + undocumented.append((filename, path)) return undocumented @@ -90,54 +85,44 @@ def process_pages(pages): for page in pages: filename = os.path.basename(page) - if filename.startswith("org.freedesktop."): # dbus + if filename.startswith('org.freedesktop.'): # dbus continue - if ( - filename.startswith("sd_") - or filename.startswith("sd-") - or filename.startswith("udev_") - ): + if filename.startswith('sd_') or filename.startswith('sd-') or filename.startswith('udev_'): function_pages.append(page) continue command_pages.append(page) - undocumented_commands = find_undocumented_commands( - command_pages, command_ignorelist - ) - undocumented_functions = find_undocumented_functions( - function_pages, function_ignorelist - ) + undocumented_commands = find_undocumented_commands(command_pages, command_ignorelist) + undocumented_functions = find_undocumented_functions(function_pages, function_ignorelist) return undocumented_commands, undocumented_functions -if __name__ == "__main__": - with open(os.path.join(os.path.dirname(__file__), "command_ignorelist")) as f: +if __name__ == '__main__': + with open(os.path.join(os.path.dirname(__file__), 'command_ignorelist')) as f: command_ignorelist = [] - for l in f.read().splitlines(): - if l.startswith("#"): + for line in f.read().splitlines(): + if line.startswith('#'): continue - fname, path = l.split(" ", 1) - path = path.replace("\\n", "\n") + fname, path = line.split(' ', 1) + path = path.replace('\\n', '\n') command_ignorelist.append((fname, path)) - with open(os.path.join(os.path.dirname(__file__), "function_ignorelist")) as f: + with open(os.path.join(os.path.dirname(__file__), 'function_ignorelist')) as f: function_ignorelist = f.read().splitlines() undocumented_commands, undocumented_functions = process_pages(sys.argv[1:]) if undocumented_commands or undocumented_functions: for filename, func in undocumented_functions: - print( - f"Function {func}() in {filename} isn't documented in the History section." - ) + print(f"Function {func}() in {filename} isn't documented in the History section.") for filename, path in undocumented_commands: - print(filename, path, "is undocumented") + print(filename, path, 'is undocumented') if undocumented_commands: print( - "Hint: if you reorganized this part of the documentation, " - "please update tools/commands_ignorelist." + 'Hint: if you reorganized this part of the documentation, ' + 'please update tools/commands_ignorelist.' ) sys.exit(1) From 5aa172e2635e176eae063693fbe73bd32bb70811 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:06:56 +0900 Subject: [PATCH 39/62] dbus-exporter: apply "ruff format" and "ruff check --fix" --- tools/dbus_exporter.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/dbus_exporter.py b/tools/dbus_exporter.py index 854c7a848b..ce6f8dbf78 100755 --- a/tools/dbus_exporter.py +++ b/tools/dbus_exporter.py @@ -11,14 +11,15 @@ def extract_interfaces_xml(output_dir, executable): # as glibc looks at /proc/self/exe when resolving RPATH env = os.environ.copy() if not os.path.exists('/proc/self'): - env["LD_ORIGIN_PATH"] = executable.parent.as_posix() + env['LD_ORIGIN_PATH'] = executable.parent.as_posix() proc = run( args=[executable.absolute(), '--bus-introspect', 'list'], stdout=PIPE, env=env, check=True, - universal_newlines=True) + text=True, + ) interface_names = (x.split()[1] for x in proc.stdout.splitlines()) @@ -28,19 +29,25 @@ def extract_interfaces_xml(output_dir, executable): stdout=PIPE, env=env, check=True, - universal_newlines=True) + text=True, + ) interface_file_name = output_dir / (interface_name + '.xml') interface_file_name.write_text(proc.stdout) interface_file_name.chmod(0o644) + def main(): parser = ArgumentParser() - parser.add_argument('output', - type=Path) - parser.add_argument('executables', - nargs='+', - type=Path) + parser.add_argument( + 'output', + type=Path, + ) + parser.add_argument( + 'executables', + nargs='+', + type=Path, + ) args = parser.parse_args() @@ -50,5 +57,6 @@ def main(): for exe in args.executables: extract_interfaces_xml(args.output, exe) + if __name__ == '__main__': main() From 7e62c9be6fa379d6b7613ef8f9fe3f157d50d263 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:07:28 +0900 Subject: [PATCH 40/62] dump-auxv: apply "ruff format" --- tools/dump-auxv.py | 54 +++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/tools/dump-auxv.py b/tools/dump-auxv.py index 1abacda9c1..1eb93b0125 100755 --- a/tools/dump-auxv.py +++ b/tools/dump-auxv.py @@ -85,24 +85,43 @@ AT_AUXV = { 'AT_L3_CACHEGEOMETRY' : 47, 'AT_MINSIGSTKSZ' : 51, # Stack needed for signal delivery -} -AT_AUXV_NAMES = {v:k for k,v in AT_AUXV.items()} +} # fmt: skip +AT_AUXV_NAMES = {v: k for k, v in AT_AUXV.items()} + @click.command(help=__doc__) -@click.option('-b', '--big-endian', 'endian', - flag_value='>', - help='Input is big-endian') -@click.option('-l', '--little-endian', 'endian', - flag_value='<', - help='Input is little-endian') -@click.option('-3', '--32', 'field_width', - flag_value=32, - help='Input is 32-bit') -@click.option('-6', '--64', 'field_width', - flag_value=64, - help='Input is 64-bit') -@click.argument('file', - type=click.File(mode='rb')) +@click.option( + '-b', + '--big-endian', + 'endian', + flag_value='>', + help='Input is big-endian', +) +@click.option( + '-l', + '--little-endian', + 'endian', + flag_value='<', + help='Input is little-endian', +) +@click.option( + '-3', + '--32', + 'field_width', + flag_value=32, + help='Input is 32-bit', +) +@click.option( + '-6', + '--64', + 'field_width', + flag_value=64, + help='Input is 64-bit', +) +@click.argument( + 'file', + type=click.File(mode='rb'), +) def dump(endian, field_width, file): data = file.read() @@ -111,7 +130,7 @@ def dump(endian, field_width, file): if endian is None: endian = '@' - width = {32:'II', 64:'QQ'}[field_width] + width = {32: 'II', 64: 'QQ'}[field_width] format_str = f'{endian}{width}' print(f'# {format_str=}') @@ -137,5 +156,6 @@ def dump(endian, field_width, file): if not seen_null: print('# array not terminated with AT_NULL') + if __name__ == '__main__': dump() From 0df17e665b4efdb1f73a154d477a20cf806d61d6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:08:46 +0900 Subject: [PATCH 41/62] elf2efi: apply "ruff format" and "ruff check --fix" --- tools/elf2efi.py | 471 +++++++++++++++++++++++------------------------ 1 file changed, 235 insertions(+), 236 deletions(-) diff --git a/tools/elf2efi.py b/tools/elf2efi.py index ed705beb31..1442d1a723 100755 --- a/tools/elf2efi.py +++ b/tools/elf2efi.py @@ -31,12 +31,12 @@ import sys import time import typing from ctypes import ( + LittleEndianStructure, c_char, c_uint8, c_uint16, c_uint32, c_uint64, - LittleEndianStructure, sizeof, ) @@ -46,28 +46,28 @@ from elftools.elf import elffile class PeCoffHeader(LittleEndianStructure): _fields_ = ( - ("Machine", c_uint16), - ("NumberOfSections", c_uint16), - ("TimeDateStamp", c_uint32), - ("PointerToSymbolTable", c_uint32), - ("NumberOfSymbols", c_uint32), - ("SizeOfOptionalHeader", c_uint16), - ("Characteristics", c_uint16), - ) + ('Machine', c_uint16), + ('NumberOfSections', c_uint16), + ('TimeDateStamp', c_uint32), + ('PointerToSymbolTable', c_uint32), + ('NumberOfSymbols', c_uint32), + ('SizeOfOptionalHeader', c_uint16), + ('Characteristics', c_uint16), + ) # fmt: skip class PeDataDirectory(LittleEndianStructure): _fields_ = ( - ("VirtualAddress", c_uint32), - ("Size", c_uint32), - ) + ('VirtualAddress', c_uint32), + ('Size', c_uint32), + ) # fmt: skip class PeRelocationBlock(LittleEndianStructure): _fields_ = ( - ("PageRVA", c_uint32), - ("BlockSize", c_uint32), - ) + ('PageRVA', c_uint32), + ('BlockSize', c_uint32), + ) # fmt: skip def __init__(self, PageRVA: int): super().__init__(PageRVA) @@ -76,64 +76,64 @@ class PeRelocationBlock(LittleEndianStructure): class PeRelocationEntry(LittleEndianStructure): _fields_ = ( - ("Offset", c_uint16, 12), - ("Type", c_uint16, 4), - ) + ('Offset', c_uint16, 12), + ('Type', c_uint16, 4), + ) # fmt: skip class PeOptionalHeaderStart(LittleEndianStructure): _fields_ = ( - ("Magic", c_uint16), - ("MajorLinkerVersion", c_uint8), - ("MinorLinkerVersion", c_uint8), - ("SizeOfCode", c_uint32), - ("SizeOfInitializedData", c_uint32), - ("SizeOfUninitializedData", c_uint32), - ("AddressOfEntryPoint", c_uint32), - ("BaseOfCode", c_uint32), - ) + ('Magic', c_uint16), + ('MajorLinkerVersion', c_uint8), + ('MinorLinkerVersion', c_uint8), + ('SizeOfCode', c_uint32), + ('SizeOfInitializedData', c_uint32), + ('SizeOfUninitializedData', c_uint32), + ('AddressOfEntryPoint', c_uint32), + ('BaseOfCode', c_uint32), + ) # fmt: skip class PeOptionalHeaderMiddle(LittleEndianStructure): _fields_ = ( - ("SectionAlignment", c_uint32), - ("FileAlignment", c_uint32), - ("MajorOperatingSystemVersion", c_uint16), - ("MinorOperatingSystemVersion", c_uint16), - ("MajorImageVersion", c_uint16), - ("MinorImageVersion", c_uint16), - ("MajorSubsystemVersion", c_uint16), - ("MinorSubsystemVersion", c_uint16), - ("Win32VersionValue", c_uint32), - ("SizeOfImage", c_uint32), - ("SizeOfHeaders", c_uint32), - ("CheckSum", c_uint32), - ("Subsystem", c_uint16), - ("DllCharacteristics", c_uint16), - ) + ('SectionAlignment', c_uint32), + ('FileAlignment', c_uint32), + ('MajorOperatingSystemVersion', c_uint16), + ('MinorOperatingSystemVersion', c_uint16), + ('MajorImageVersion', c_uint16), + ('MinorImageVersion', c_uint16), + ('MajorSubsystemVersion', c_uint16), + ('MinorSubsystemVersion', c_uint16), + ('Win32VersionValue', c_uint32), + ('SizeOfImage', c_uint32), + ('SizeOfHeaders', c_uint32), + ('CheckSum', c_uint32), + ('Subsystem', c_uint16), + ('DllCharacteristics', c_uint16), + ) # fmt: skip class PeOptionalHeaderEnd(LittleEndianStructure): _fields_ = ( - ("LoaderFlags", c_uint32), - ("NumberOfRvaAndSizes", c_uint32), - ("ExportTable", PeDataDirectory), - ("ImportTable", PeDataDirectory), - ("ResourceTable", PeDataDirectory), - ("ExceptionTable", PeDataDirectory), - ("CertificateTable", PeDataDirectory), - ("BaseRelocationTable", PeDataDirectory), - ("Debug", PeDataDirectory), - ("Architecture", PeDataDirectory), - ("GlobalPtr", PeDataDirectory), - ("TLSTable", PeDataDirectory), - ("LoadConfigTable", PeDataDirectory), - ("BoundImport", PeDataDirectory), - ("IAT", PeDataDirectory), - ("DelayImportDescriptor", PeDataDirectory), - ("CLRRuntimeHeader", PeDataDirectory), - ("Reserved", PeDataDirectory), - ) + ('LoaderFlags', c_uint32), + ('NumberOfRvaAndSizes', c_uint32), + ('ExportTable', PeDataDirectory), + ('ImportTable', PeDataDirectory), + ('ResourceTable', PeDataDirectory), + ('ExceptionTable', PeDataDirectory), + ('CertificateTable', PeDataDirectory), + ('BaseRelocationTable', PeDataDirectory), + ('Debug', PeDataDirectory), + ('Architecture', PeDataDirectory), + ('GlobalPtr', PeDataDirectory), + ('TLSTable', PeDataDirectory), + ('LoadConfigTable', PeDataDirectory), + ('BoundImport', PeDataDirectory), + ('IAT', PeDataDirectory), + ('DelayImportDescriptor', PeDataDirectory), + ('CLRRuntimeHeader', PeDataDirectory), + ('Reserved', PeDataDirectory), + ) # fmt: skip class PeOptionalHeader(LittleEndianStructure): @@ -141,47 +141,47 @@ class PeOptionalHeader(LittleEndianStructure): class PeOptionalHeader32(PeOptionalHeader): - _anonymous_ = ("Start", "Middle", "End") + _anonymous_ = ('Start', 'Middle', 'End') _fields_ = ( - ("Start", PeOptionalHeaderStart), - ("BaseOfData", c_uint32), - ("ImageBase", c_uint32), - ("Middle", PeOptionalHeaderMiddle), - ("SizeOfStackReserve", c_uint32), - ("SizeOfStackCommit", c_uint32), - ("SizeOfHeapReserve", c_uint32), - ("SizeOfHeapCommit", c_uint32), - ("End", PeOptionalHeaderEnd), - ) + ('Start', PeOptionalHeaderStart), + ('BaseOfData', c_uint32), + ('ImageBase', c_uint32), + ('Middle', PeOptionalHeaderMiddle), + ('SizeOfStackReserve', c_uint32), + ('SizeOfStackCommit', c_uint32), + ('SizeOfHeapReserve', c_uint32), + ('SizeOfHeapCommit', c_uint32), + ('End', PeOptionalHeaderEnd), + ) # fmt: skip class PeOptionalHeader32Plus(PeOptionalHeader): - _anonymous_ = ("Start", "Middle", "End") + _anonymous_ = ('Start', 'Middle', 'End') _fields_ = ( - ("Start", PeOptionalHeaderStart), - ("ImageBase", c_uint64), - ("Middle", PeOptionalHeaderMiddle), - ("SizeOfStackReserve", c_uint64), - ("SizeOfStackCommit", c_uint64), - ("SizeOfHeapReserve", c_uint64), - ("SizeOfHeapCommit", c_uint64), - ("End", PeOptionalHeaderEnd), - ) + ('Start', PeOptionalHeaderStart), + ('ImageBase', c_uint64), + ('Middle', PeOptionalHeaderMiddle), + ('SizeOfStackReserve', c_uint64), + ('SizeOfStackCommit', c_uint64), + ('SizeOfHeapReserve', c_uint64), + ('SizeOfHeapCommit', c_uint64), + ('End', PeOptionalHeaderEnd), + ) # fmt: skip class PeSection(LittleEndianStructure): _fields_ = ( - ("Name", c_char * 8), - ("VirtualSize", c_uint32), - ("VirtualAddress", c_uint32), - ("SizeOfRawData", c_uint32), - ("PointerToRawData", c_uint32), - ("PointerToRelocations", c_uint32), - ("PointerToLinenumbers", c_uint32), - ("NumberOfRelocations", c_uint16), - ("NumberOfLinenumbers", c_uint16), - ("Characteristics", c_uint32), - ) + ('Name', c_char * 8), + ('VirtualSize', c_uint32), + ('VirtualAddress', c_uint32), + ('SizeOfRawData', c_uint32), + ('PointerToRawData', c_uint32), + ('PointerToRelocations', c_uint32), + ('PointerToLinenumbers', c_uint32), + ('NumberOfRelocations', c_uint16), + ('NumberOfLinenumbers', c_uint16), + ('Characteristics', c_uint32), + ) # fmt: skip def __init__(self) -> None: super().__init__() @@ -195,30 +195,32 @@ assert sizeof(PeCoffHeader) == 20 assert sizeof(PeOptionalHeader32) == 224 assert sizeof(PeOptionalHeader32Plus) == 240 +# fmt: off PE_CHARACTERISTICS_RX = 0x60000020 # CNT_CODE|MEM_READ|MEM_EXECUTE PE_CHARACTERISTICS_RW = 0xC0000040 # CNT_INITIALIZED_DATA|MEM_READ|MEM_WRITE PE_CHARACTERISTICS_R = 0x40000040 # CNT_INITIALIZED_DATA|MEM_READ +# fmt: on IGNORE_SECTIONS = [ - ".eh_frame", - ".eh_frame_hdr", - ".ARM.exidx", - ".relro_padding", - ".sframe", + '.eh_frame', + '.eh_frame_hdr', + '.ARM.exidx', + '.relro_padding', + '.sframe', ] IGNORE_SECTION_TYPES = [ - "SHT_DYNAMIC", - "SHT_DYNSYM", - "SHT_GNU_ATTRIBUTES", - "SHT_GNU_HASH", - "SHT_HASH", - "SHT_NOTE", - "SHT_REL", - "SHT_RELA", - "SHT_RELR", - "SHT_STRTAB", - "SHT_SYMTAB", + 'SHT_DYNAMIC', + 'SHT_DYNSYM', + 'SHT_GNU_ATTRIBUTES', + 'SHT_GNU_HASH', + 'SHT_HASH', + 'SHT_NOTE', + 'SHT_REL', + 'SHT_RELA', + 'SHT_RELR', + 'SHT_STRTAB', + 'SHT_SYMTAB', ] # EFI mandates 4KiB memory pages. @@ -227,7 +229,7 @@ FILE_ALIGNMENT = 512 # Nobody cares about DOS headers, so put the PE header right after. PE_OFFSET = 64 -PE_MAGIC = b"PE\0\0" +PE_MAGIC = b'PE\0\0' def align_to(x: int, align: int) -> int: @@ -239,8 +241,7 @@ def align_down(x: int, align: int) -> int: def next_section_address(sections: list[PeSection]) -> int: - return align_to(sections[-1].VirtualAddress + sections[-1].VirtualSize, - SECTION_ALIGNMENT) + return align_to(sections[-1].VirtualAddress + sections[-1].VirtualSize, SECTION_ALIGNMENT) class BadSectionError(ValueError): @@ -256,29 +257,30 @@ def iter_copy_sections(file: elffile.ELFFile) -> typing.Iterator[PeSection]: relro = None for elf_seg in file.iter_segments(): - if elf_seg["p_type"] == "PT_LOAD" and elf_seg["p_align"] != SECTION_ALIGNMENT: - raise BadSectionError(f"ELF segment {elf_seg['p_type']} is not properly aligned" - f" ({elf_seg['p_align']} != {SECTION_ALIGNMENT})") - if elf_seg["p_type"] == "PT_GNU_RELRO": + if elf_seg['p_type'] == 'PT_LOAD' and elf_seg['p_align'] != SECTION_ALIGNMENT: + raise BadSectionError( + f'ELF segment {elf_seg["p_type"]} is not properly aligned ({elf_seg["p_align"]} != {SECTION_ALIGNMENT})' + ) + if elf_seg['p_type'] == 'PT_GNU_RELRO': relro = elf_seg for elf_s in file.iter_sections(): if ( - elf_s["sh_flags"] & elf.constants.SH_FLAGS.SHF_ALLOC == 0 - or elf_s["sh_type"] in IGNORE_SECTION_TYPES + elf_s['sh_flags'] & elf.constants.SH_FLAGS.SHF_ALLOC == 0 + or elf_s['sh_type'] in IGNORE_SECTION_TYPES or elf_s.name in IGNORE_SECTIONS - or elf_s["sh_size"] == 0 + or elf_s['sh_size'] == 0 ): continue - if elf_s["sh_type"] not in ["SHT_PROGBITS", "SHT_NOBITS"]: - raise BadSectionError(f"Unknown section {elf_s.name} with type {elf_s['sh_type']}") + if elf_s['sh_type'] not in ['SHT_PROGBITS', 'SHT_NOBITS']: + raise BadSectionError(f'Unknown section {elf_s.name} with type {elf_s["sh_type"]}') if elf_s.name == '.got': # FIXME: figure out why those sections are inserted - print("WARNING: Non-empty .got section", file=sys.stderr) + print('WARNING: Non-empty .got section', file=sys.stderr) - if elf_s["sh_flags"] & elf.constants.SH_FLAGS.SHF_EXECINSTR: + if elf_s['sh_flags'] & elf.constants.SH_FLAGS.SHF_EXECINSTR: rwx = PE_CHARACTERISTICS_RX - elif elf_s["sh_flags"] & elf.constants.SH_FLAGS.SHF_WRITE: + elif elf_s['sh_flags'] & elf.constants.SH_FLAGS.SHF_WRITE: rwx = PE_CHARACTERISTICS_RW else: rwx = PE_CHARACTERISTICS_R @@ -293,11 +295,11 @@ def iter_copy_sections(file: elffile.ELFFile) -> typing.Iterator[PeSection]: if pe_s: # Insert padding to properly align the section. - pad_len = elf_s["sh_addr"] - pe_s.VirtualAddress - len(pe_s.data) + pad_len = elf_s['sh_addr'] - pe_s.VirtualAddress - len(pe_s.data) pe_s.data += bytearray(pad_len) + elf_s.data() else: pe_s = PeSection() - pe_s.VirtualAddress = elf_s["sh_addr"] + pe_s.VirtualAddress = elf_s['sh_addr'] pe_s.Characteristics = rwx pe_s.data = elf_s.data() @@ -306,8 +308,8 @@ def iter_copy_sections(file: elffile.ELFFile) -> typing.Iterator[PeSection]: def convert_sections( - file: elffile.ELFFile, - opt: PeOptionalHeader, + file: elffile.ELFFile, + opt: PeOptionalHeader, ) -> list[PeSection]: last_vma = (0, 0) sections = [] @@ -324,24 +326,25 @@ def convert_sections( pe_s.VirtualSize = len(pe_s.data) pe_s.SizeOfRawData = align_to(len(pe_s.data), FILE_ALIGNMENT) pe_s.Name = { - PE_CHARACTERISTICS_RX: b".text", - PE_CHARACTERISTICS_RW: b".data", - PE_CHARACTERISTICS_R: b".rodata", + PE_CHARACTERISTICS_RX: b'.text', + PE_CHARACTERISTICS_RW: b'.data', + PE_CHARACTERISTICS_R: b'.rodata', }[pe_s.Characteristics] # This can happen if not building with '-z separate-code'. if pe_s.VirtualAddress < sum(last_vma): - raise BadSectionError(f"Section {pe_s.Name.decode()!r} @0x{pe_s.VirtualAddress:x} overlaps" - f" previous section @0x{last_vma[0]:x}+0x{last_vma[1]:x}=@0x{sum(last_vma):x}") + raise BadSectionError( + f'Section {pe_s.Name.decode()!r} @{pe_s.VirtualAddress:#x} overlaps previous section @{last_vma[0]:#x}+{last_vma[1]:#x}=@{sum(last_vma):#x}' + ) last_vma = (pe_s.VirtualAddress, pe_s.VirtualSize) - if pe_s.Name == b".text": + if pe_s.Name == b'.text': opt.BaseOfCode = pe_s.VirtualAddress opt.SizeOfCode += pe_s.VirtualSize else: opt.SizeOfInitializedData += pe_s.VirtualSize - if pe_s.Name == b".data" and isinstance(opt, PeOptionalHeader32): + if pe_s.Name == b'.data' and isinstance(opt, PeOptionalHeader32): opt.BaseOfData = pe_s.VirtualAddress sections.append(pe_s) @@ -355,14 +358,14 @@ def copy_sections( input_names: str, sections: list[PeSection], ) -> None: - for name in input_names.split(","): + for name in input_names.split(','): elf_s = file.get_section_by_name(name) if not elf_s: continue if elf_s.data_alignment > 1 and SECTION_ALIGNMENT % elf_s.data_alignment != 0: - raise BadSectionError(f"ELF section {name} is not aligned") - if elf_s["sh_flags"] & (elf.constants.SH_FLAGS.SHF_EXECINSTR | elf.constants.SH_FLAGS.SHF_WRITE) != 0: - raise BadSectionError(f"ELF section {name} is not read-only data") + raise BadSectionError(f'ELF section {name} is not aligned') + if elf_s['sh_flags'] & (elf.constants.SH_FLAGS.SHF_EXECINSTR | elf.constants.SH_FLAGS.SHF_WRITE) != 0: # fmt: skip + raise BadSectionError(f'ELF section {name} is not read-only data') pe_s = PeSection() pe_s.Name = name.encode() @@ -381,18 +384,21 @@ def apply_elf_relative_relocation( sections: list[PeSection], addend_size: int, ) -> None: - [target] = [pe_s for pe_s in sections - if pe_s.VirtualAddress <= reloc["r_offset"] < pe_s.VirtualAddress + len(pe_s.data)] + [target] = [ + pe_s + for pe_s in sections + if pe_s.VirtualAddress <= reloc['r_offset'] < pe_s.VirtualAddress + len(pe_s.data) + ] - addend_offset = reloc["r_offset"] - target.VirtualAddress + addend_offset = reloc['r_offset'] - target.VirtualAddress if reloc.is_RELA(): - addend = reloc["r_addend"] + addend = reloc['r_addend'] else: addend = target.data[addend_offset : addend_offset + addend_size] - addend = int.from_bytes(addend, byteorder="little") + addend = int.from_bytes(addend, byteorder='little') - value = (image_base + addend).to_bytes(addend_size, byteorder="little") + value = (image_base + addend).to_bytes(addend_size, byteorder='little') target.data[addend_offset : addend_offset + addend_size] = value @@ -404,47 +410,44 @@ def convert_elf_reloc_table( pe_reloc_blocks: dict[int, PeRelocationBlock], ) -> None: NONE_RELOC = { - "EM_386": elf.enums.ENUM_RELOC_TYPE_i386["R_386_NONE"], - "EM_AARCH64": elf.enums.ENUM_RELOC_TYPE_AARCH64["R_AARCH64_NONE"], - "EM_ARM": elf.enums.ENUM_RELOC_TYPE_ARM["R_ARM_NONE"], - "EM_LOONGARCH": 0, - "EM_RISCV": 0, - "EM_X86_64": elf.enums.ENUM_RELOC_TYPE_x64["R_X86_64_NONE"], - }[file["e_machine"]] + 'EM_386': elf.enums.ENUM_RELOC_TYPE_i386['R_386_NONE'], + 'EM_AARCH64': elf.enums.ENUM_RELOC_TYPE_AARCH64['R_AARCH64_NONE'], + 'EM_ARM': elf.enums.ENUM_RELOC_TYPE_ARM['R_ARM_NONE'], + 'EM_LOONGARCH': 0, + 'EM_RISCV': 0, + 'EM_X86_64': elf.enums.ENUM_RELOC_TYPE_x64['R_X86_64_NONE'], + }[file['e_machine']] # fmt: skip RELATIVE_RELOC = { - "EM_386": elf.enums.ENUM_RELOC_TYPE_i386["R_386_RELATIVE"], - "EM_AARCH64": elf.enums.ENUM_RELOC_TYPE_AARCH64["R_AARCH64_RELATIVE"], - "EM_ARM": elf.enums.ENUM_RELOC_TYPE_ARM["R_ARM_RELATIVE"], - "EM_LOONGARCH": 3, - "EM_RISCV": 3, - "EM_X86_64": elf.enums.ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"], - }[file["e_machine"]] + 'EM_386': elf.enums.ENUM_RELOC_TYPE_i386['R_386_RELATIVE'], + 'EM_AARCH64': elf.enums.ENUM_RELOC_TYPE_AARCH64['R_AARCH64_RELATIVE'], + 'EM_ARM': elf.enums.ENUM_RELOC_TYPE_ARM['R_ARM_RELATIVE'], + 'EM_LOONGARCH': 3, + 'EM_RISCV': 3, + 'EM_X86_64': elf.enums.ENUM_RELOC_TYPE_x64['R_X86_64_RELATIVE'], + }[file['e_machine']] # fmt: skip for reloc in elf_reloc_table.iter_relocations(): - if reloc["r_info_type"] == NONE_RELOC: + if reloc['r_info_type'] == NONE_RELOC: continue - if reloc["r_info_type"] == RELATIVE_RELOC: - apply_elf_relative_relocation(reloc, - elf_image_base, - sections, - file.elfclass // 8) + if reloc['r_info_type'] == RELATIVE_RELOC: + apply_elf_relative_relocation(reloc, elf_image_base, sections, file.elfclass // 8) # Now that the ELF relocation has been applied, we can create a PE relocation. - block_rva = reloc["r_offset"] & ~0xFFF + block_rva = reloc['r_offset'] & ~0xFFF if block_rva not in pe_reloc_blocks: pe_reloc_blocks[block_rva] = PeRelocationBlock(block_rva) entry = PeRelocationEntry() - entry.Offset = reloc["r_offset"] & 0xFFF + entry.Offset = reloc['r_offset'] & 0xFFF # REL_BASED_HIGHLOW or REL_BASED_DIR64 entry.Type = 3 if file.elfclass == 32 else 10 pe_reloc_blocks[block_rva].entries.append(entry) continue - raise BadSectionError(f"Unsupported relocation {reloc}") + raise BadSectionError(f'Unsupported relocation {reloc}') def convert_elf_relocations( @@ -453,27 +456,29 @@ def convert_elf_relocations( sections: list[PeSection], minimum_sections: int, ) -> typing.Optional[PeSection]: - dynamic = file.get_section_by_name(".dynamic") + dynamic = file.get_section_by_name('.dynamic') if dynamic is None: - raise BadSectionError("ELF .dynamic section is missing") + raise BadSectionError('ELF .dynamic section is missing') - [flags_tag] = dynamic.iter_tags("DT_FLAGS_1") - if not flags_tag["d_val"] & elf.enums.ENUM_DT_FLAGS_1["DF_1_PIE"]: - raise ValueError("ELF file is not a PIE") + [flags_tag] = dynamic.iter_tags('DT_FLAGS_1') + if not flags_tag['d_val'] & elf.enums.ENUM_DT_FLAGS_1['DF_1_PIE']: + raise ValueError('ELF file is not a PIE') # This checks that the ELF image base is 0. - symtab = file.get_section_by_name(".symtab") + symtab = file.get_section_by_name('.symtab') if symtab: - exe_start = symtab.get_symbol_by_name("__executable_start") - if exe_start and exe_start[0]["st_value"] != 0: - raise ValueError("Unexpected ELF image base") + exe_start = symtab.get_symbol_by_name('__executable_start') + if exe_start and exe_start[0]['st_value'] != 0: + raise ValueError('Unexpected ELF image base') - opt.SizeOfHeaders = align_to(PE_OFFSET - + len(PE_MAGIC) - + sizeof(PeCoffHeader) - + sizeof(opt) - + sizeof(PeSection) * max(len(sections) + 1, minimum_sections), - FILE_ALIGNMENT) + opt.SizeOfHeaders = align_to( + PE_OFFSET + + len(PE_MAGIC) + + sizeof(PeCoffHeader) + + sizeof(opt) + + sizeof(PeSection) * max(len(sections) + 1, minimum_sections), + FILE_ALIGNMENT, + ) # We use the basic VMA layout from the ELF image in the PE image. This could cause the first # section to overlap the PE image headers during runtime at VMA 0. We can simply apply a fixed @@ -482,23 +487,18 @@ def convert_elf_relocations( # the ELF portions of the image. segment_offset = 0 if sections[0].VirtualAddress < opt.SizeOfHeaders: - segment_offset = align_to(opt.SizeOfHeaders - sections[0].VirtualAddress, - SECTION_ALIGNMENT) + segment_offset = align_to(opt.SizeOfHeaders - sections[0].VirtualAddress, SECTION_ALIGNMENT) - opt.AddressOfEntryPoint = file["e_entry"] + segment_offset + opt.AddressOfEntryPoint = file['e_entry'] + segment_offset opt.BaseOfCode += segment_offset if isinstance(opt, PeOptionalHeader32): opt.BaseOfData += segment_offset pe_reloc_blocks: dict[int, PeRelocationBlock] = {} for reloc_type, reloc_table in dynamic.get_relocation_tables().items(): - if reloc_type not in ["REL", "RELA"]: - raise BadSectionError(f"Unsupported relocation type {reloc_type}") - convert_elf_reloc_table(file, - reloc_table, - opt.ImageBase + segment_offset, - sections, - pe_reloc_blocks) + if reloc_type not in ['REL', 'RELA']: + raise BadSectionError(f'Unsupported relocation type {reloc_type}') + convert_elf_reloc_table(file, reloc_table, opt.ImageBase + segment_offset, sections, pe_reloc_blocks) for pe_s in sections: pe_s.VirtualAddress += segment_offset @@ -524,7 +524,7 @@ def convert_elf_relocations( data += entry pe_reloc_s = PeSection() - pe_reloc_s.Name = b".reloc" + pe_reloc_s.Name = b'.reloc' pe_reloc_s.data = data pe_reloc_s.VirtualAddress = next_section_address(sections) pe_reloc_s.VirtualSize = len(data) @@ -543,9 +543,9 @@ def write_pe( opt: PeOptionalHeader, sections: list[PeSection], ) -> None: - file.write(b"MZ") + file.write(b'MZ') file.seek(0x3C, io.SEEK_SET) - file.write(PE_OFFSET.to_bytes(2, byteorder="little")) + file.write(PE_OFFSET.to_bytes(2, byteorder='little')) file.seek(PE_OFFSET, io.SEEK_SET) file.write(PE_MAGIC) file.write(coff) @@ -554,8 +554,9 @@ def write_pe( offset = opt.SizeOfHeaders for pe_s in sorted(sections, key=lambda s: s.VirtualAddress): if pe_s.VirtualAddress < opt.SizeOfHeaders: - raise BadSectionError(f"Section {pe_s.Name} @0x{pe_s.VirtualAddress:x} overlaps" - " PE headers ending at 0x{opt.SizeOfHeaders:x}") + raise BadSectionError( + f'Section {pe_s.Name} @{pe_s.VirtualAddress:#x} overlaps PE headers ending at {opt.SizeOfHeaders:#x}' + ) pe_s.PointerToRawData = offset file.write(pe_s) @@ -573,20 +574,20 @@ def write_pe( def elf2efi(args: argparse.Namespace) -> None: file = elffile.ELFFile(args.ELF) if not file.little_endian: - raise ValueError("ELF file is not little-endian") - if file["e_type"] not in ["ET_DYN", "ET_EXEC"]: - raise ValueError(f"Unsupported ELF type {file['e_type']}") + raise ValueError('ELF file is not little-endian') + if file['e_type'] not in ['ET_DYN', 'ET_EXEC']: + raise ValueError(f'Unsupported ELF type {file["e_type"]}') pe_arch = { - "EM_386": 0x014C, - "EM_AARCH64": 0xAA64, - "EM_ARM": 0x01C2, - "EM_LOONGARCH": 0x6232 if file.elfclass == 32 else 0x6264, - "EM_RISCV": 0x5032 if file.elfclass == 32 else 0x5064, - "EM_X86_64": 0x8664, - }.get(file["e_machine"]) + 'EM_386': 0x014C, + 'EM_AARCH64': 0xAA64, + 'EM_ARM': 0x01C2, + 'EM_LOONGARCH': 0x6232 if file.elfclass == 32 else 0x6264, + 'EM_RISCV': 0x5032 if file.elfclass == 32 else 0x5064, + 'EM_X86_64': 0x8664, + }.get(file['e_machine']) # fmt: skip if pe_arch is None: - raise ValueError(f"Unsupported ELF architecture {file['e_machine']}") + raise ValueError(f'Unsupported ELF architecture {file["e_machine"]}') coff = PeCoffHeader() opt = PeOptionalHeader32() if file.elfclass == 32 else PeOptionalHeader32Plus() @@ -605,7 +606,7 @@ def elf2efi(args: argparse.Namespace) -> None: coff.Machine = pe_arch coff.NumberOfSections = len(sections) - coff.TimeDateStamp = int(os.environ.get("SOURCE_DATE_EPOCH") or time.time()) + coff.TimeDateStamp = int(os.environ.get('SOURCE_DATE_EPOCH') or time.time()) coff.SizeOfOptionalHeader = sizeof(opt) # EXECUTABLE_IMAGE|LINE_NUMS_STRIPPED|LOCAL_SYMS_STRIPPED|DEBUG_STRIPPED # and (32BIT_MACHINE or LARGE_ADDRESS_AWARE) @@ -632,66 +633,64 @@ def elf2efi(args: argparse.Namespace) -> None: opt.NumberOfRvaAndSizes = N_DATA_DIRECTORY_ENTRIES if pe_reloc_s: - opt.BaseRelocationTable = PeDataDirectory( - pe_reloc_s.VirtualAddress, pe_reloc_s.VirtualSize - ) + opt.BaseRelocationTable = PeDataDirectory(pe_reloc_s.VirtualAddress, pe_reloc_s.VirtualSize) write_pe(args.PE, coff, opt, sections) def create_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description="Convert ELF binaries to PE/EFI") + parser = argparse.ArgumentParser(description='Convert ELF binaries to PE/EFI') parser.add_argument( - "--version-major", + '--version-major', type=int, default=0, - help="Major image version of EFI image", + help='Major image version of EFI image', ) parser.add_argument( - "--version-minor", + '--version-minor', type=int, default=0, - help="Minor image version of EFI image", + help='Minor image version of EFI image', ) parser.add_argument( - "--efi-major", + '--efi-major', type=int, default=0, - help="Minimum major EFI subsystem version", + help='Minimum major EFI subsystem version', ) parser.add_argument( - "--efi-minor", + '--efi-minor', type=int, default=0, - help="Minimum minor EFI subsystem version", + help='Minimum minor EFI subsystem version', ) parser.add_argument( - "--subsystem", + '--subsystem', type=int, default=10, - help="PE subsystem", + help='PE subsystem', ) parser.add_argument( - "ELF", - type=argparse.FileType("rb"), - help="Input ELF file", + 'ELF', + type=argparse.FileType('rb'), + help='Input ELF file', ) parser.add_argument( - "PE", - type=argparse.FileType("wb"), - help="Output PE/EFI file", + 'PE', + type=argparse.FileType('wb'), + help='Output PE/EFI file', ) parser.add_argument( - "--minimum-sections", + '--minimum-sections', type=int, default=0, - help="Minimum number of sections to leave space for", + help='Minimum number of sections to leave space for', ) parser.add_argument( - "--copy-sections", + '--copy-sections', type=str, - default="", - help="Copy these sections if found", + default='', + help='Copy these sections if found', ) return parser @@ -701,5 +700,5 @@ def main() -> None: elf2efi(parser.parse_args()) -if __name__ == "__main__": +if __name__ == '__main__': main() From 6c2bf5330b0966d219ac967b332fe557095f833c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:09:04 +0900 Subject: [PATCH 42/62] fetch-distro: apply "ruff format" --- tools/fetch-distro.py | 75 +++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/tools/fetch-distro.py b/tools/fetch-distro.py index 00b8eca4d1..a94b807186 100755 --- a/tools/fetch-distro.py +++ b/tools/fetch-distro.py @@ -12,6 +12,7 @@ import shlex import subprocess from pathlib import Path + def parse_args(): p = argparse.ArgumentParser( description=__doc__, @@ -27,34 +28,35 @@ def parse_args(): default=True, ) p.add_argument( - '--update', '-u', + '--update', + '-u', action='store_true', default=False, ) p.add_argument('--profile') return p.parse_args() + def read_config(distro: str): cmd = ['mkosi', '--json', '-d', distro, '-f', 'summary'] if args.profile: cmd += ['--profile', args.profile] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') text = subprocess.check_output(cmd, text=True) data = json.loads(text) - images = {image["Image"]: image for image in data["Images"]} - return images["build"] + images = {image['Image']: image for image in data['Images']} + return images['build'] + def commit_file(distro: str, files: list[Path], commit: str, changes: str): - message = '\n'.join(( - f'mkosi: update {distro} commit reference to {commit}', - '', - changes)) + message = '\n'.join((f'mkosi: update {distro} commit reference to {commit}', '', changes)) cmd = ['git', 'commit', '-m', message, *(str(file) for file in files)] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) + def checkout_distro(args, distro: str, config: dict): url = config['Environment']['GIT_URL'] branch = config['Environment']['GIT_BRANCH'] @@ -74,28 +76,30 @@ def checkout_distro(args, distro: str, config: dict): reference = ['--reference-if-able=.'] if distro == 'debian' else [] cmd = [ - 'git', 'clone', url, + 'git', + 'clone', + url, f'--branch={branch}', *sparse, dest.as_posix(), *reference, ] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) # Sparse checkout if the package is in a subdirectory if subdir is not None: - cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'sparse-checkout', 'set', - '--no-cone', f'{subdir}'] - print(f"+ {shlex.join(cmd)}") + cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'sparse-checkout', 'set', '--no-cone', f'{subdir}'] + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'checkout', 'HEAD'] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) args.fetch = False # no need to fetch if we just cloned + def update_distro(args, distro: str, config: dict): branch = config['Environment']['GIT_BRANCH'] subdir = config['Environment'].get('GIT_SUBDIR') @@ -103,32 +107,46 @@ def update_distro(args, distro: str, config: dict): pkg_subdir = config['Environment']['PKG_SUBDIR'] if args.fetch: - cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'fetch', 'origin', '-v', - f'{branch}:remotes/origin/{branch}'] - print(f"+ {shlex.join(cmd)}") + cmd = [ + 'git', + '-C', f'pkg/{pkg_subdir}', + 'fetch', + 'origin', + '-v', + f'{branch}:remotes/origin/{branch}', + ] # fmt: skip + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'switch', branch] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) - cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'log', '-n1', '--format=%H', - f'refs/remotes/origin/{branch}'] + cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'log', '-n1', '--format=%H', f'refs/remotes/origin/{branch}'] if subdir is not None: cmd += [f'{subdir}'] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') new_commit = subprocess.check_output(cmd, text=True).strip() if old_commit == new_commit: print(f'{pkg_subdir}: commit {new_commit!s} is still fresh') return - cmd = ['git', '-C', f'pkg/{pkg_subdir}', 'log', '--graph', '--no-merges', - '--pretty=oneline', '--no-decorate', '--abbrev-commit', '--abbrev=10', - f'{old_commit}..{new_commit}'] + cmd = [ + 'git', + '-C', f'pkg/{pkg_subdir}', + 'log', + '--graph', + '--no-merges', + '--pretty=oneline', + '--no-decorate', + '--abbrev-commit', + '--abbrev=10', + f'{old_commit}..{new_commit}', + ] # fmt: skip if subdir is not None: cmd += [f'{subdir}'] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') changes = subprocess.check_output(cmd, text=True).strip() conf_dir = Path('mkosi/mkosi.pkgenv/mkosi.conf.d') @@ -142,8 +160,8 @@ def update_distro(args, distro: str, config: dict): file.write_text(new) tocommit = [file] - if distro == "fedora": - packit = Path(".packit.yml") + if distro == 'fedora': + packit = Path('.packit.yml') s = packit.read_text() assert old_commit in s new = s.replace(old_commit, new_commit) @@ -155,6 +173,7 @@ def update_distro(args, distro: str, config: dict): else: raise ValueError(f'{distro}: hash {new_commit} not found under {conf_dir}') + if __name__ == '__main__': args = parse_args() From 2ab056cb7e27cc48cb94641d3bd1337a050ec52c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:09:19 +0900 Subject: [PATCH 43/62] fetch-mkosi: apply "ruff format" and "ruff check --fix" This also makes CONFIG is closed when not necessary. --- tools/fetch-mkosi.py | 51 ++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/tools/fetch-mkosi.py b/tools/fetch-mkosi.py index 6e6440d65e..481f607d40 100755 --- a/tools/fetch-mkosi.py +++ b/tools/fetch-mkosi.py @@ -7,9 +7,9 @@ With -u, if changed, commit the latest hash. """ import argparse +import re import shlex import subprocess -import re from pathlib import Path URL = 'https://github.com/systemd/mkosi' @@ -17,6 +17,7 @@ BRANCH = 'main' # We only want to ever use commits on upstream 'main' branch CONFIG = Path('mkosi/mkosi.conf') WORKFLOWS = [Path('.github/workflows') / f for f in ['mkosi.yml', 'coverage.yml', 'linter.yml']] + def parse_args(): p = argparse.ArgumentParser( description=__doc__, @@ -26,59 +27,72 @@ def parse_args(): type=Path, ) p.add_argument( - '--update', '-u', + '--update', + '-u', action='store_true', default=False, ) return p.parse_args() + def read_config(): print(f'Reading {CONFIG}…') - matches = [m.group(1) - for line in open(CONFIG) - if (m := re.match('^MinimumVersion=commit:([a-z0-9]{40})$', - line.strip()))] + c = CONFIG.read_text() + matches = [ + m.group(1) for m in re.finditer('^\s*MinimumVersion=commit:([a-z0-9]{40})\s*$', c, re.MULTILINE) + ] assert len(matches) == 1 return matches[0] + def commit_file(files: list[Path], commit: str, changes: str): - message = '\n'.join(( - f'mkosi: update mkosi ref to {commit}', - '', - changes)) + message = '\n'.join((f'mkosi: update mkosi ref to {commit}', '', changes)) cmd = ['git', 'commit', '-m', message, *(str(file) for file in files)] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) + def checkout_mkosi(args): if args.dir.exists(): print(f'{args.dir} already exists.') return cmd = [ - 'git', 'clone', URL, + 'git', + 'clone', + URL, f'--branch={BRANCH}', args.dir.as_posix(), ] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') subprocess.check_call(cmd) + def update_mkosi(args): old_commit = read_config() cmd = ['git', '-C', args.dir.as_posix(), 'rev-parse', f'refs/remotes/origin/{BRANCH}'] - print(f"+ {shlex.join(cmd)}") + print(f'+ {shlex.join(cmd)}') new_commit = subprocess.check_output(cmd, text=True).strip() if old_commit == new_commit: print(f'mkosi: commit {new_commit!s} is still fresh') return - cmd = ['git', '-C', args.dir.as_posix(), 'log', '--graph', '--no-merges', - '--pretty=oneline', '--no-decorate', '--abbrev-commit', '--abbrev=10', - f'{old_commit}..{new_commit}'] - print(f"+ {shlex.join(cmd)}") + cmd = [ + 'git', + '-C', args.dir.as_posix(), + 'log', + '--graph', + '--no-merges', + '--pretty=oneline', + '--no-decorate', + '--abbrev-commit', + '--abbrev=10', + f'{old_commit}..{new_commit}', + ] # fmt: skip + print(f'+ {shlex.join(cmd)}') changes = subprocess.check_output(cmd, text=True).strip() for f in [CONFIG, *WORKFLOWS]: @@ -91,6 +105,7 @@ def update_mkosi(args): commit_file([CONFIG, *WORKFLOWS], new_commit, changes) + if __name__ == '__main__': args = parse_args() checkout_mkosi(args) From 30c87278d44feccf0508e84e73e3443c396b68d4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:09:45 +0900 Subject: [PATCH 44/62] find-unused-library-symbols: apply "ruff format" and "ruff check --fix" --- tools/find-unused-library-symbols.py | 75 ++++++++++++++-------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/tools/find-unused-library-symbols.py b/tools/find-unused-library-symbols.py index 47f96df2ee..66083c0ea4 100755 --- a/tools/find-unused-library-symbols.py +++ b/tools/find-unused-library-symbols.py @@ -39,10 +39,10 @@ def get_exported_symbols(library_path): ['nm', '--dynamic', '--defined-only', '--extern-only', library_path], capture_output=True, text=True, - check=True + check=True, ) except subprocess.CalledProcessError as e: - print(f"Error: Failed to run nm on {library_path}: {e}", file=sys.stderr) + print(f'Error: Failed to run nm on {library_path}: {e}', file=sys.stderr) sys.exit(1) except FileNotFoundError: print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr) @@ -79,10 +79,10 @@ def get_undefined_symbols(executable_path): ['nm', '--dynamic', '--undefined-only', executable_path], capture_output=True, text=True, - check=True + check=True, ) except subprocess.CalledProcessError as e: - print(f"Warning: Failed to run nm on {executable_path}: {e}", file=sys.stderr) + print(f'Warning: Failed to run nm on {executable_path}: {e}', file=sys.stderr) return set() except FileNotFoundError: print("Error: 'nm' command not found. Please install binutils.", file=sys.stderr) @@ -112,7 +112,7 @@ def verify_executable_links_library(executable_path, library_name): ['ldd', executable_path], capture_output=True, text=True, - check=True + check=True, ) except (subprocess.CalledProcessError, FileNotFoundError): # If ldd fails or doesn't exist, we'll skip the verification @@ -137,14 +137,16 @@ def get_library_internal_references(library_path, exported_symbols): ['objdump', '-R', library_path], capture_output=True, text=True, - check=True + check=True, ) except subprocess.CalledProcessError as e: - print(f"Warning: Failed to run objdump on {library_path}: {e}", file=sys.stderr) + print(f'Warning: Failed to run objdump on {library_path}: {e}', file=sys.stderr) return set() except FileNotFoundError: - print("Warning: 'objdump' command not found. Internal references won't be detected.", - file=sys.stderr) + print( + "Warning: 'objdump' command not found. Internal references won't be detected.", + file=sys.stderr, + ) return set() internal_refs = set() @@ -181,7 +183,7 @@ def find_unused_symbols(library_path, executable_paths, verify_linkage=True): exported_symbols = get_exported_symbols(library_path) if not exported_symbols: - print(f"Warning: No exported symbols found in {library_path}", file=sys.stderr) + print(f'Warning: No exported symbols found in {library_path}', file=sys.stderr) return set(), set(), set() # Collect all symbols used by the executables @@ -194,8 +196,7 @@ def find_unused_symbols(library_path, executable_paths, verify_linkage=True): for exe_path in executable_paths: # Optionally verify linkage if verify_linkage and not verify_executable_links_library(exe_path, library_name): - print(f"Warning: {exe_path} does not appear to link against {library_name}", - file=sys.stderr) + print(f'Warning: {exe_path} does not appear to link against {library_name}', file=sys.stderr) undefined_symbols = get_undefined_symbols(exe_path) # Only count symbols that are actually exported by our library @@ -209,31 +210,31 @@ def find_unused_symbols(library_path, executable_paths, verify_linkage=True): def main(): parser = argparse.ArgumentParser( - description='Find unused exported symbols in a shared library' + description='Find unused exported symbols in a shared library', ) parser.add_argument( 'library', - help='Path to the shared library to analyze' + help='Path to the shared library to analyze', ) parser.add_argument( 'executables', nargs='+', - help='Paths to executables that link against the library' + help='Paths to executables that link against the library', ) parser.add_argument( '--no-verify-linkage', action='store_true', - help='Skip verification that executables actually link against the library' + help='Skip verification that executables actually link against the library', ) parser.add_argument( '--show-used', action='store_true', - help='Also show used symbols' + help='Also show used symbols', ) parser.add_argument( '--stats-only', action='store_true', - help='Only show statistics, not individual symbols' + help='Only show statistics, not individual symbols', ) args = parser.parse_args() @@ -241,7 +242,7 @@ def main(): # Verify library exists library_path = Path(args.library) if not library_path.exists(): - print(f"Error: Library not found: {library_path}", file=sys.stderr) + print(f'Error: Library not found: {library_path}', file=sys.stderr) sys.exit(1) # Verify executables exist @@ -249,47 +250,45 @@ def main(): for exe in args.executables: exe_path = Path(exe) if not exe_path.exists(): - print(f"Warning: Executable not found: {exe_path}", file=sys.stderr) + print(f'Warning: Executable not found: {exe_path}', file=sys.stderr) else: executable_paths.append(str(exe_path)) if not executable_paths: - print("Error: No valid executables provided", file=sys.stderr) + print('Error: No valid executables provided', file=sys.stderr) sys.exit(1) # Analyze symbols unused, exported, used = find_unused_symbols( - str(library_path), - executable_paths, - verify_linkage=not args.no_verify_linkage + str(library_path), executable_paths, verify_linkage=not args.no_verify_linkage ) # Print results - print(f"Analysis of {library_path.name}") - print("=" * 70) - print(f"Total exported symbols: {len(exported)}") - print(f" (excluding public API symbols starting with 'sd_')") - print(f"Used symbols: {len(used)}") - print(f"Unused symbols: {len(unused)}") - print(f"Usage rate: {len(used)/len(exported)*100:.1f}%" if exported else "N/A") + print(f'Analysis of {library_path.name}') + print('=' * 70) + print(f'Total exported symbols: {len(exported)}') + print(" (excluding public API symbols starting with 'sd_')") + print(f'Used symbols: {len(used)}') + print(f'Unused symbols: {len(unused)}') + print(f'Usage rate: {len(used) / len(exported) * 100:.1f}%' if exported else 'N/A') print() if not args.stats_only: if unused: - print("Unused symbols:") - print("-" * 70) + print('Unused symbols:') + print('-' * 70) for symbol in sorted(unused): - print(f" {symbol}") + print(f' {symbol}') print() else: - print("All exported symbols are used!") + print('All exported symbols are used!') print() if args.show_used and used: - print("Used symbols:") - print("-" * 70) + print('Used symbols:') + print('-' * 70) for symbol in sorted(used): - print(f" {symbol}") + print(f' {symbol}') print() # Exit with non-zero if there are unused symbols (useful for CI) From 86d3b07fbae1151311f3e83f3ef7af92a79e9468 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:10:00 +0900 Subject: [PATCH 45/62] gdb-sd_dump_hashmaps: apply "ruff format" and "ruff check --fix" --- tools/gdb-sd_dump_hashmaps.py | 56 ++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/tools/gdb-sd_dump_hashmaps.py b/tools/gdb-sd_dump_hashmaps.py index 596ee8d90c..d6fc2c4bb8 100755 --- a/tools/gdb-sd_dump_hashmaps.py +++ b/tools/gdb-sd_dump_hashmaps.py @@ -4,38 +4,39 @@ import gdb + class sd_dump_hashmaps(gdb.Command): "dump systemd's hashmaps" def __init__(self): - super().__init__("sd_dump_hashmaps", gdb.COMMAND_DATA, gdb.COMPLETE_NONE) + super().__init__('sd_dump_hashmaps', gdb.COMMAND_DATA, gdb.COMPLETE_NONE) def invoke(self, arg, _from_tty): - d = gdb.parse_and_eval("hashmap_debug_list") - hashmap_type_info = gdb.parse_and_eval("hashmap_type_info") - uchar_t = gdb.lookup_type("unsigned char") - ulong_t = gdb.lookup_type("unsigned long") - debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug") + d = gdb.parse_and_eval('hashmap_debug_list') + hashmap_type_info = gdb.parse_and_eval('hashmap_type_info') + uchar_t = gdb.lookup_type('unsigned char') + ulong_t = gdb.lookup_type('unsigned long') + debug_offset = gdb.parse_and_eval('(unsigned long)&((HashmapBase*)0)->debug') - print("type, hash, indirect, entries, max_entries, buckets, creator") + print('type, hash, indirect, entries, max_entries, buckets, creator') while d: - h = gdb.parse_and_eval(f"(HashmapBase*)((char*){int(d.cast(ulong_t))} - {debug_offset})") + h = gdb.parse_and_eval(f'(HashmapBase*)((char*){int(d.cast(ulong_t))} - {debug_offset})') - if h["has_indirect"]: - storage_ptr = h["indirect"]["storage"].cast(uchar_t.pointer()) - n_entries = h["indirect"]["n_entries"] - n_buckets = h["indirect"]["n_buckets"] + if h['has_indirect']: + storage_ptr = h['indirect']['storage'].cast(uchar_t.pointer()) + n_entries = h['indirect']['n_entries'] + n_buckets = h['indirect']['n_buckets'] else: - storage_ptr = h["direct"]["storage"].cast(uchar_t.pointer()) - n_entries = h["n_direct_entries"] - n_buckets = hashmap_type_info[h["type"]]["n_direct_buckets"] + storage_ptr = h['direct']['storage'].cast(uchar_t.pointer()) + n_entries = h['n_direct_entries'] + n_buckets = hashmap_type_info[h['type']]['n_direct_buckets'] - t = ["plain", "ordered", "set"][int(h["type"])] + t = ['plain', 'ordered', 'set'][int(h['type'])] - print(f'{t}, {h["hash_ops"]}, {bool(h["has_indirect"])}, {n_entries}, {d["max_entries"]}, {n_buckets}') + print(f'{t}, {h["hash_ops"]}, {bool(h["has_indirect"])}, {n_entries}, {d["max_entries"]}, {n_buckets}') # fmt: skip - if arg != "" and n_entries > 0: - dib_raw_addr = storage_ptr + hashmap_type_info[h["type"]]["entry_size"] * n_buckets + if arg != '' and n_entries > 0: + dib_raw_addr = storage_ptr + hashmap_type_info[h['type']]['entry_size'] * n_buckets histogram = {} for i in range(0, n_buckets): @@ -44,11 +45,11 @@ class sd_dump_hashmaps(gdb.Command): for dib in sorted(histogram): if dib != 255: - print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_entries):.0%} of entries") + print(f'{dib:>3} {histogram[dib]:>8} {float(histogram[dib] / n_entries):.0%} of entries') # fmt: skip else: - print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_buckets):.0%} of slots") - s = sum(dib*count for (dib, count) in histogram.items() if dib != 255) / n_entries - print(f"mean DIB of entries: {s}") + print(f'{dib:>3} {histogram[dib]:>8} {float(histogram[dib] / n_buckets):.0%} of slots') # fmt: skip + s = sum(dib * count for (dib, count) in histogram.items() if dib != 255) / n_entries + print(f'mean DIB of entries: {s}') blocks = [] current_len = 1 @@ -69,10 +70,11 @@ class sd_dump_hashmaps(gdb.Command): if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1: blocks[0][1] += blocks[-1][1] blocks = blocks[0:-1] - print("max block: {}".format(max(blocks, key=lambda a: a[1]))) - print("sum block lens: {}".format(sum(b[1] for b in blocks))) - print("mean block len: {}".format(sum(b[1] for b in blocks) / len(blocks))) + print(f'max block: {max(blocks, key=lambda a: a[1])}') + print(f'sum block lens: {sum(b[1] for b in blocks)}') + print(f'mean block len: {sum(b[1] for b in blocks) / len(blocks)}') + + d = d['debug_list_next'] - d = d["debug_list_next"] sd_dump_hashmaps() From cc1373700ba7eea6412627c582849f569851486a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:10:14 +0900 Subject: [PATCH 46/62] generate-gperfs: apply "ruff format" --- tools/generate-gperfs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/generate-gperfs.py b/tools/generate-gperfs.py index fc349b43fa..e9e520247e 100755 --- a/tools/generate-gperfs.py +++ b/tools/generate-gperfs.py @@ -13,23 +13,23 @@ if __name__ == '__main__': sys.exit(f'Usage: {sys.argv[0]} name prefix file [includes...]') name, prefix, file, *includes = sys.argv[1:] - includes = [f"#include {i}" for i in includes] + includes = [f'#include {i}' for i in includes] # Older versions of python don't allow backslashes # in f-strings so use chr(10) for newlines and chr(92) # for backslashes instead as a workaround. - print(f"""\ + print(f'''\ %{{ _Pragma("GCC diagnostic ignored {chr(92)}"-Wimplicit-fallthrough{chr(92)}"") #if __GNUC__ >= 15 _Pragma("GCC diagnostic ignored {chr(92)}"-Wzero-as-null-pointer-constant{chr(92)}"") #endif {chr(10).join(includes)} -%}}""") - print(f"""\ +%}}''') + print(f'''\ struct {name}_name {{ const char* name; int id; }}; %null-strings -%%""") +%%''') for line in open(file): - print("{0}, {1}{0}".format(line.rstrip(), prefix)) + print('{0}, {1}{0}'.format(line.rstrip(), prefix)) From e0ac581a8b8acf9e4bc8ff4efdef5ab1d5dcb36c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:10:48 +0900 Subject: [PATCH 47/62] list-discoverable-partitions: apply "ruff format" --- tools/list-discoverable-partitions.py | 71 +++++++++++++++++---------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/tools/list-discoverable-partitions.py b/tools/list-discoverable-partitions.py index a19bf1d6e2..437be68d72 100755 --- a/tools/list-discoverable-partitions.py +++ b/tools/list-discoverable-partitions.py @@ -30,16 +30,15 @@ ARCHITECTURES = { 'TILEGX': 'TILE-Gx', 'X86': 'x86', 'X86_64': 'amd64/x86_64', -} +} # fmt: skip TYPES = { - 'ROOT' : 'Root Partition', - 'ROOT_VERITY' : 'Root Verity Partition', - 'ROOT_VERITY_SIG' : 'Root Verity Signature Partition', - 'USR' : '`/usr/` Partition', - 'USR_VERITY' : '`/usr/` Verity Partition', - 'USR_VERITY_SIG' : '`/usr/` Verity Signature Partition', - + 'ROOT': 'Root Partition', + 'ROOT_VERITY': 'Root Verity Partition', + 'ROOT_VERITY_SIG': 'Root Verity Signature Partition', + 'USR': '`/usr/` Partition', + 'USR_VERITY': '`/usr/` Verity Partition', + 'USR_VERITY_SIG': '`/usr/` Verity Signature Partition', 'ESP': 'EFI System Partition', 'SRV': 'Server Data Partition', 'VAR': 'Variable Data Partition', @@ -49,7 +48,7 @@ TYPES = { 'USER_HOME': 'Per-user Home Partition', 'LINUX_GENERIC': 'Generic Linux Data Partition', 'XBOOTLDR': 'Extended Boot Loader Partition', -} +} # fmt: skip DESCRIPTIONS = { 'ROOT': ( @@ -57,56 +56,66 @@ DESCRIPTIONS = { 'On systems with matching architecture, the first partition with this type UUID on the disk ' 'containing the active EFI ESP is automatically mounted to the root directory `/`. ' 'If the partition is encrypted with LUKS or has dm-verity integrity data (see below), the ' - 'device mapper file will be named `/dev/mapper/root`.'), + 'device mapper file will be named `/dev/mapper/root`.', + ), 'USR': ( 'Any native, optionally in LUKS', - 'Similar semantics to root partition, but just the `/usr/` partition.'), + 'Similar semantics to root partition, but just the `/usr/` partition.', + ), 'ROOT_VERITY': ( 'A dm-verity superblock followed by hash data', 'Contains dm-verity integrity hash data for the matching root partition. If this feature is ' 'used the partition UUID of the root partition should be the first 128 bits of the root hash ' 'of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the ' 'final 128 bits of it, so that the root partition and its Verity partition can be discovered ' - 'easily, simply by specifying the root hash.'), + 'easily, simply by specifying the root hash.', + ), 'USR_VERITY': ( 'A dm-verity superblock followed by hash data', - 'Similar semantics to root Verity partition, but just for the `/usr/` partition.'), + 'Similar semantics to root Verity partition, but just for the `/usr/` partition.', + ), 'ROOT_VERITY_SIG': ( 'A serialized JSON object, see below', - 'Contains a root hash and a PKCS#7 signature for it, permitting signed dm-verity GPT images.'), + 'Contains a root hash and a PKCS#7 signature for it, permitting signed dm-verity GPT images.', + ), 'USR_VERITY_SIG': ( 'A serialized JSON object, see below', - 'Similar semantics to root Verity signature partition, but just for the `/usr/` partition.'), - + 'Similar semantics to root Verity signature partition, but just for the `/usr/` partition.', + ), 'ESP': ( 'VFAT', 'The ESP used for the current boot is automatically mounted to `/efi/` (or `/boot/` as ' 'fallback), unless a different partition is mounted there (possibly via `/etc/fstab`, or ' 'because the Extended Boot Loader Partition — see below — exists) or the directory is ' 'non-empty on the root disk. This partition type is defined by the ' - '[UEFI Specification](http://www.uefi.org/specifications).'), + '[UEFI Specification](http://www.uefi.org/specifications).', + ), 'XBOOTLDR': ( 'Typically VFAT', 'The Extended Boot Loader Partition (XBOOTLDR) used for the current boot is automatically ' 'mounted to `/boot/`, unless a different partition is mounted there (possibly via ' '`/etc/fstab`) or the directory is non-empty on the root disk. This partition type ' 'is defined by the [Boot Loader ' - 'Specification](https://uapi-group.org/specifications/specs/boot_loader_specification).'), + 'Specification](https://uapi-group.org/specifications/specs/boot_loader_specification).', + ), 'SWAP': ( 'Swap, optionally in LUKS', 'All swap partitions on the disk containing the root partition are automatically enabled. ' 'If the partition is encrypted with LUKS, the device mapper file will be named ' - '`/dev/mapper/swap`. This partition type predates the Discoverable Partitions Specification.'), + '`/dev/mapper/swap`. This partition type predates the Discoverable Partitions Specification.', + ), 'HOME': ( 'Any native, optionally in LUKS', 'The first partition with this type UUID on the disk containing the root partition is ' 'automatically mounted to `/home/`. If the partition is encrypted with LUKS, the device ' - 'mapper file will be named `/dev/mapper/home`.'), + 'mapper file will be named `/dev/mapper/home`.', + ), 'SRV': ( 'Any native, optionally in LUKS', 'The first partition with this type UUID on the disk containing the root partition is ' 'automatically mounted to `/srv/`. If the partition is encrypted with LUKS, the device ' - 'mapper file will be named `/dev/mapper/srv`.'), + 'mapper file will be named `/dev/mapper/srv`.', + ), 'VAR': ( 'Any native, optionally in LUKS', 'The first partition with this type UUID on the disk containing the root partition is ' @@ -118,7 +127,8 @@ DESCRIPTIONS = { 'listed here) is inherently private to a specific installation and cannot possibly be ' 'shared between multiple OS installations on the same disk, and thus should be bound to ' 'a specific instance of the OS, identified by its machine ID. If the partition is ' - 'encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`.'), + 'encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`.', + ), 'TMP': ( 'Any native, optionally in LUKS', 'The first partition with this type UUID on the disk containing the root partition is ' @@ -127,19 +137,23 @@ DESCRIPTIONS = { 'is indeed `/var/tmp/`, not `/tmp/`. The latter is typically maintained in memory via ' '`tmpfs` and does not require a partition on disk. In some cases it might be ' 'desirable to make `/tmp/` persistent too, in which case it is recommended to make it ' - 'a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID.'), + 'a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID.', + ), 'USER_HOME': ( 'Any native, optionally in LUKS', 'A home partition of a user, managed by ' - '[`systemd-homed`](https://www.freedesktop.org/software/systemd/man/systemd-homed.html).'), + '[`systemd-homed`](https://www.freedesktop.org/software/systemd/man/systemd-homed.html).', + ), 'LINUX_GENERIC': ( 'Any native, optionally in LUKS', 'No automatic mounting takes place for other Linux data partitions. This partition type ' 'should be used for all partitions that carry Linux file systems. The installer needs ' 'to mount them explicitly via entries in `/etc/fstab`. Optionally, these partitions may ' - 'be encrypted with LUKS. This partition type predates the Discoverable Partitions Specification.'), + 'be encrypted with LUKS. This partition type predates the Discoverable Partitions Specification.', + ), } + def extract(file): for line in file: # print(line) @@ -148,7 +162,10 @@ def extract(file): continue name = line.split()[1] - if m2 := re.match(r'^(ROOT|USR)_([A-Z0-9]+|X86_64|PPC64_LE|MIPS_LE|MIPS64_LE)(|_VERITY|_VERITY_SIG)\s+SD_ID128_MAKE\((.*)\)', m.group(1)): + if m2 := re.match( + r'^(ROOT|USR)_([A-Z0-9]+|X86_64|PPC64_LE|MIPS_LE|MIPS64_LE)(|_VERITY|_VERITY_SIG)\s+SD_ID128_MAKE\((.*)\)', + m.group(1), + ): ptype, arch, suffix, u = m2.groups() u = uuid.UUID(u.replace(',', '')) assert arch in ARCHITECTURES, f'{arch} not in f{ARCHITECTURES}' @@ -165,6 +182,7 @@ def extract(file): else: raise ValueError(f'Failed to match: {m.group(1)}') + def generate(defines): prevtype = None @@ -188,6 +206,7 @@ def generate(defines): print(f'| _{tdesc}{adesc}_ | `{puuid}` `{name}` | {morea} | {moreb} |') + if __name__ == '__main__': known = extract(sys.stdin) generate(known) From 8b24db05d42cd62642a5d3f19cd88a2d96eca24c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 20 Feb 2026 02:11:03 +0900 Subject: [PATCH 48/62] make-directive-index: apply "ruff format" --- tools/make-directive-index.py | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tools/make-directive-index.py b/tools/make-directive-index.py index 5398b452ef..ddf07534ef 100755 --- a/tools/make-directive-index.py +++ b/tools/make-directive-index.py @@ -13,6 +13,7 @@ This index contains {count} entries in {sections} sections, referring to {pages} individual manual pages. ''' + def _extract_directives(directive_groups, formatting, page): t = xml_parse(page) section = t.find('./refmeta/manvolnum').text @@ -21,12 +22,13 @@ def _extract_directives(directive_groups, formatting, page): storopt = directive_groups['options'] for variablelist in t.iterfind('.//variablelist'): klass = variablelist.attrib.get('class') - searchpath = variablelist.attrib.get('xpath','./varlistentry/term/varname') + searchpath = variablelist.attrib.get('xpath', './varlistentry/term/varname') storvar = directive_groups[klass or 'miscellaneous'] #