Skip to content

Commit 6a32159

Browse files
author
mhecko
committed
feature: add possibility to use net.naming-scheme
Leapp writes .link files to prevent interfaces being renamed after booting to post-upgrade system. This patch adds a less error-prone approach that uses net.naming-scheme kernel param. The naming-scheme tells udev what hardware properties to use when composing a device name. Moreover, possible values of this parameter are coarse-grained "profiles", that tell udev to behave as if it did on RHEL8.0. The functionality is enabled by setting LEAPP_USE_NET_NAMING_SCHEME environmental variable to 1. If the feature is enabled, the .link file generation is disabled. A kernel parameter `net.naming-scheme=` is added to the upgrade boot entry and the post-upgrade entry. The value of the parameter will be `rhel-<source_major>.0`. Note that the minor source version is *not used*. Using also source major version instead of 0 causes the device names to change slightly, so we use 0. Moreover, an extra RPM named `rhel-net-naming-sysattrs` is installed to the target system and target userspace container. The RPM provides definitions of the "profiles" for net.naming-scheme. The feature is available only for 8>9 and higher. Attempting to upgrade 7>8 with LEAPP_USE_NET_NAMING_SCHEME=1 will ignore the value of LEAPP_USE_NET_NAMING_SCHEME. Add a possibility to use the net.naming-scheme cmdline argument to make immutable network interface names during the upgrade. The feature can be used only for 8>9 upgrades and higher. To enable the feature, use LEAPP_USE_NET_NAMING_SCHEME=1. Jira-ref: RHEL-23473
1 parent c2c96af commit 6a32159

File tree

10 files changed

+317
-57
lines changed

10 files changed

+317
-57
lines changed

repos/system_upgrade/common/actors/addupgradebootentry/actor.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
FirmwareFacts,
99
GrubConfigError,
1010
KernelCmdline,
11+
LateTargetKernelCmdlineArgTasks,
1112
LiveImagePreparationInfo,
1213
LiveModeArtifacts,
1314
LiveModeConfig,
1415
TargetKernelCmdlineArgTasks,
15-
TransactionDryRun
16+
TransactionDryRun,
17+
UpgradeKernelCmdlineArgTasks
1618
)
1719
from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag
1820

@@ -33,9 +35,11 @@ class AddUpgradeBootEntry(Actor):
3335
LiveModeArtifacts,
3436
LiveModeConfig,
3537
KernelCmdline,
36-
TransactionDryRun
38+
TransactionDryRun,
39+
TargetKernelCmdlineArgTasks,
40+
UpgradeKernelCmdlineArgTasks
3741
)
38-
produces = (TargetKernelCmdlineArgTasks,)
42+
produces = (LateTargetKernelCmdlineArgTasks,)
3943
tags = (IPUWorkflowTag, InterimPreparationPhaseTag)
4044

4145
def process(self):

repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py

+56-22
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99
BootContent,
1010
KernelCmdline,
1111
KernelCmdlineArg,
12+
LateTargetKernelCmdlineArgTasks,
1213
LiveImagePreparationInfo,
1314
LiveModeArtifacts,
1415
LiveModeConfig,
15-
TargetKernelCmdlineArgTasks
16+
TargetKernelCmdlineArgTasks,
17+
UpgradeKernelCmdlineArgTasks
1618
)
1719

1820

19-
def collect_boot_args(livemode_enabled):
21+
def collect_upgrade_kernel_args(livemode_enabled):
2022
args = {
2123
'enforcing': '0',
2224
'rd.plymouth': '0',
@@ -34,7 +36,10 @@ def collect_boot_args(livemode_enabled):
3436
livemode_args = construct_cmdline_args_for_livemode()
3537
args.update(livemode_args)
3638

37-
return args
39+
upgrade_kernel_args = collect_set_of_kernel_args_from_msgs(UpgradeKernelCmdlineArgTasks, 'to_add')
40+
args.update(upgrade_kernel_args)
41+
42+
return set(args.items())
3843

3944

4045
def collect_undesired_args(livemode_enabled):
@@ -43,11 +48,11 @@ def collect_undesired_args(livemode_enabled):
4348
args = dict(zip(('ro', 'rhgb', 'quiet'), itertools.repeat(None)))
4449
args['rd.lvm.lv'] = _get_rdlvm_arg_values()
4550

46-
return args
51+
return set(args.items())
4752

4853

49-
def format_grubby_args_from_args_dict(args_dict):
50-
""" Format the given args dictionary in a form required by grubby's --args. """
54+
def format_grubby_args_from_args_set(args_dict):
55+
""" Format the given args set in a form required by grubby's --args. """
5156

5257
def fmt_single_arg(arg_pair):
5358
key, value = arg_pair
@@ -65,7 +70,7 @@ def flatten_arguments(arg_pair):
6570
else:
6671
yield (key, value) # Just a single (key, value) pair
6772

68-
arg_sequence = itertools.chain(*(flatten_arguments(arg_pair) for arg_pair in args_dict.items()))
73+
arg_sequence = itertools.chain(*(flatten_arguments(arg_pair) for arg_pair in args_dict))
6974

7075
# Sorting should be fine as only values can be None, but we cannot have a (key, None) and (key, value) in
7176
# the dictionary at the same time.
@@ -78,7 +83,7 @@ def flatten_arguments(arg_pair):
7883
def figure_out_commands_needed_to_add_entry(kernel_path, initramfs_path, args_to_add, args_to_remove):
7984
boot_entry_modification_commands = []
8085

81-
args_to_add_str = format_grubby_args_from_args_dict(args_to_add)
86+
args_to_add_str = format_grubby_args_from_args_set(args_to_add)
8287

8388
create_entry_cmd = [
8489
'/usr/sbin/grubby',
@@ -93,7 +98,7 @@ def figure_out_commands_needed_to_add_entry(kernel_path, initramfs_path, args_to
9398

9499
# We need to update root= param separately, since we cannot do it during --add-kernel with --copy-default.
95100
# This is likely a bug in grubby.
96-
root_param_value = args_to_add.get('root', None)
101+
root_param_value = dict(args_to_add).get('root', None)
97102
if root_param_value:
98103
enforce_root_param_for_the_entry_cmd = [
99104
'/usr/sbin/grubby',
@@ -103,7 +108,7 @@ def figure_out_commands_needed_to_add_entry(kernel_path, initramfs_path, args_to
103108
boot_entry_modification_commands.append(enforce_root_param_for_the_entry_cmd)
104109

105110
if args_to_remove:
106-
args_to_remove_str = format_grubby_args_from_args_dict(args_to_remove)
111+
args_to_remove_str = format_grubby_args_from_args_set(args_to_remove)
107112
remove_undesired_args_cmd = [
108113
'/usr/sbin/grubby',
109114
'--update-kernel', kernel_path,
@@ -113,18 +118,55 @@ def figure_out_commands_needed_to_add_entry(kernel_path, initramfs_path, args_to
113118
return boot_entry_modification_commands
114119

115120

121+
def collect_set_of_kernel_args_from_msgs(msg_type, arg_list_field_name):
122+
cmdline_modification_msgs = api.consume(msg_type)
123+
lists_of_args_to_add = (getattr(msg, arg_list_field_name, []) for msg in cmdline_modification_msgs)
124+
args = itertools.chain(*lists_of_args_to_add)
125+
return set((arg.key, arg.value) for arg in args)
126+
127+
128+
def emit_removal_of_args_meant_only_for_upgrade_kernel(added_upgrade_kernel_args):
129+
"""
130+
Emit message requesting removal of upgrade kernel args that should not be on the target kernel.
131+
132+
Target kernel args are created by copying the args of the booted (upgrade) kernel. Therefore,
133+
we need to explicitly modify the target kernel cmdline, removing what should not have been copied.
134+
"""
135+
target_args_to_add = collect_set_of_kernel_args_from_msgs(TargetKernelCmdlineArgTasks, 'to_add')
136+
actual_kernel_args = collect_set_of_kernel_args_from_msgs(KernelCmdline, 'parameters')
137+
138+
# actual_kernel_args should not be changed during upgrade, unless explicitly removed by
139+
# TargetKernelCmdlineArgTasks.to_remove, but that is handled by some other upgrade component. We just want
140+
# to make sure we remove what was not on the source system and that we don't overwrite args to be added to target.
141+
args_not_present_on_target_kernel = added_upgrade_kernel_args - actual_kernel_args - target_args_to_add
142+
143+
# We remove only what we've added and what will not be already removed by someone else.
144+
args_to_remove = [KernelCmdlineArg(key=arg[0], value=arg[1]) for arg in args_not_present_on_target_kernel]
145+
146+
if args_to_remove:
147+
msg = ('Following upgrade kernel args were added, but they should not be present '
148+
'on target cmdline: `%s`, requesting removal.')
149+
api.current_logger().info(msg, args_not_present_on_target_kernel)
150+
args_sorted = sorted(args_to_remove, key=lambda arg: arg.key)
151+
api.produce(LateTargetKernelCmdlineArgTasks(to_remove=args_sorted))
152+
153+
116154
def add_boot_entry(configs=None):
117155
kernel_dst_path, initram_dst_path = get_boot_file_paths()
156+
118157
_remove_old_upgrade_boot_entry(kernel_dst_path, configs=configs)
119158

120159
livemode_enabled = next(api.consume(LiveImagePreparationInfo), None) is not None
121160

122-
cmdline_args = collect_boot_args(livemode_enabled)
161+
# We have to keep the desired and unwanted args separate and modify cmline in two separate grubby calls. Merging
162+
# these sets and trying to execute only a single command would leave the unwanted cmdline args present if they
163+
# are present on the original system.
164+
added_cmdline_args = collect_upgrade_kernel_args(livemode_enabled)
123165
undesired_cmdline_args = collect_undesired_args(livemode_enabled)
124166

125167
commands_to_run = figure_out_commands_needed_to_add_entry(kernel_dst_path,
126168
initram_dst_path,
127-
args_to_add=cmdline_args,
169+
args_to_add=added_cmdline_args,
128170
args_to_remove=undesired_cmdline_args)
129171

130172
def run_commands_adding_entry(extra_command_suffix=None):
@@ -146,16 +188,8 @@ def run_commands_adding_entry(extra_command_suffix=None):
146188
# See https://bugzilla.redhat.com/show_bug.cgi?id=1764306
147189
run(['/usr/sbin/zipl'])
148190

149-
if 'debug' in cmdline_args:
150-
# The kernelopts for target kernel are generated based on the cmdline used in the upgrade initramfs,
151-
# therefore, if we enabled debug above, and the original system did not have the debug kernelopt, we
152-
# need to explicitly remove it from the target os boot entry.
153-
# NOTE(mhecko): This will also unconditionally remove debug kernelopt if the source system used it.
154-
api.produce(TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='debug')]))
155-
156-
# NOTE(mmatuska): This will remove the option even if the source system had it set.
157-
# However enforcing=0 shouldn't be set persistently anyway.
158-
api.produce(TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='enforcing', value='0')]))
191+
effective_upgrade_kernel_args = added_cmdline_args - undesired_cmdline_args
192+
emit_removal_of_args_meant_only_for_upgrade_kernel(effective_upgrade_kernel_args)
159193

160194
except CalledProcessError as e:
161195
raise StopActorExecutionError(

repos/system_upgrade/common/actors/addupgradebootentry/tests/unit_test_addupgradebootentry.py

+20-27
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
BootContent,
1313
KernelCmdline,
1414
KernelCmdlineArg,
15+
LateTargetKernelCmdlineArgTasks,
1516
LiveModeArtifacts,
1617
LiveModeConfig,
1718
TargetKernelCmdlineArgTasks
@@ -82,8 +83,10 @@ def get_boot_file_paths_mocked():
8283
assert addupgradebootentry.run.args[0] == run_args.args_remove
8384
assert addupgradebootentry.run.args[1] == run_args.args_add
8485
assert api.produce.model_instances == [
85-
TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='debug')]),
86-
TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='enforcing', value='0')])
86+
LateTargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='debug'),
87+
KernelCmdlineArg(key='enforcing', value='0'),
88+
KernelCmdlineArg(key='plymouth.enable', value='0'),
89+
KernelCmdlineArg(key='rd.plymouth', value='0')])
8790
]
8891

8992
if run_args.args_zipl:
@@ -103,16 +106,16 @@ def get_boot_file_paths_mocked():
103106
CurrentActorMocked(envars={'LEAPP_DEBUG': str(int(is_leapp_invoked_with_debug))}))
104107

105108
addupgradebootentry.add_boot_entry()
109+
assert len(api.produce.model_instances) == 1
106110

107-
expected_produced_messages = []
108-
if is_leapp_invoked_with_debug:
109-
expected_produced_messages = [TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='debug')])]
110-
111-
expected_produced_messages.append(
112-
TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='enforcing', value='0')])
113-
)
111+
produced_msg = api.produce.model_instances[0]
112+
assert isinstance(produced_msg, LateTargetKernelCmdlineArgTasks)
114113

115-
assert api.produce.model_instances == expected_produced_messages
114+
debug_kernel_cmline_arg = KernelCmdlineArg(key='debug')
115+
if is_leapp_invoked_with_debug:
116+
assert debug_kernel_cmline_arg in produced_msg.to_remove
117+
else:
118+
assert debug_kernel_cmline_arg not in produced_msg.to_remove
116119

117120

118121
def test_add_boot_entry_configs(monkeypatch):
@@ -132,8 +135,10 @@ def get_boot_file_paths_mocked():
132135
assert addupgradebootentry.run.args[2] == run_args_add + ['-c', CONFIGS[0]]
133136
assert addupgradebootentry.run.args[3] == run_args_add + ['-c', CONFIGS[1]]
134137
assert api.produce.model_instances == [
135-
TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='debug')]),
136-
TargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='enforcing', value='0')]),
138+
LateTargetKernelCmdlineArgTasks(to_remove=[KernelCmdlineArg(key='debug'),
139+
KernelCmdlineArg(key='enforcing', value='0'),
140+
KernelCmdlineArg(key='plymouth.enable', value='0'),
141+
KernelCmdlineArg(key='rd.plymouth', value='0')])
137142
]
138143

139144

@@ -183,7 +188,7 @@ def test_fix_grub_config_error(monkeypatch, error_type, test_file_name):
183188
(False, False),
184189
)
185190
)
186-
def test_collect_boot_args(monkeypatch, is_debug_enabled, network_enablement_type):
191+
def test_collect_upgrade_kernel_args(monkeypatch, is_debug_enabled, network_enablement_type):
187192
env_vars = {'LEAPP_DEBUG': str(int(is_debug_enabled))}
188193
if network_enablement_type:
189194
env_vars['LEAPP_DEVEL_INITRAM_NETWORK'] = network_enablement_type
@@ -192,7 +197,8 @@ def test_collect_boot_args(monkeypatch, is_debug_enabled, network_enablement_typ
192197
monkeypatch.setattr(addupgradebootentry, 'construct_cmdline_args_for_livemode',
193198
lambda *args: {'livemodearg': 'value'})
194199

195-
args = addupgradebootentry.collect_boot_args(livemode_enabled=True)
200+
arg_set = addupgradebootentry.collect_upgrade_kernel_args(livemode_enabled=True)
201+
args = dict(arg_set)
196202

197203
assert args['enforcing'] == '0'
198204
assert args['rd.plymouth'] == '0'
@@ -320,16 +326,3 @@ def readlink_mock(path):
320326
uuid = addupgradebootentry._get_device_uuid(path)
321327

322328
assert uuid == 'MY_UUID1'
323-
324-
325-
@pytest.mark.parametrize(
326-
('args', 'expected_result'),
327-
(
328-
([('argA', 'val'), ('argB', 'valB'), ('argC', None), ], 'argA=val argB=valB argC'),
329-
([('argA', ('val1', 'val2'))], 'argA=val1 argA=val2')
330-
)
331-
)
332-
def test_format_grubby_args_from_args_dict(args, expected_result):
333-
actual_result = addupgradebootentry.format_grubby_args_from_args_dict(dict(args))
334-
335-
assert actual_result == expected_result

repos/system_upgrade/common/actors/kernelcmdlineconfig/actor.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
from leapp.actors import Actor
44
from leapp.exceptions import StopActorExecutionError
55
from leapp.libraries.actor import kernelcmdlineconfig
6-
from leapp.models import FirmwareFacts, InstalledTargetKernelInfo, KernelCmdlineArg, TargetKernelCmdlineArgTasks
6+
from leapp.models import (
7+
FirmwareFacts,
8+
InstalledTargetKernelInfo,
9+
KernelCmdlineArg,
10+
LateTargetKernelCmdlineArgTasks,
11+
TargetKernelCmdlineArgTasks
12+
)
713
from leapp.reporting import Report
814
from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag
915

@@ -14,7 +20,13 @@ class KernelCmdlineConfig(Actor):
1420
"""
1521

1622
name = 'kernelcmdlineconfig'
17-
consumes = (KernelCmdlineArg, InstalledTargetKernelInfo, FirmwareFacts, TargetKernelCmdlineArgTasks)
23+
consumes = (
24+
KernelCmdlineArg,
25+
InstalledTargetKernelInfo,
26+
FirmwareFacts,
27+
LateTargetKernelCmdlineArgTasks,
28+
TargetKernelCmdlineArgTasks
29+
)
1830
produces = (Report,)
1931
tags = (FinalizationPhaseTag, IPUWorkflowTag)
2032

repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import itertools
12
import re
23

34
from leapp import reporting
45
from leapp.exceptions import StopActorExecutionError
56
from leapp.libraries import stdlib
67
from leapp.libraries.common.config import architecture, version
78
from leapp.libraries.stdlib import api
8-
from leapp.models import InstalledTargetKernelInfo, KernelCmdlineArg, TargetKernelCmdlineArgTasks
9+
from leapp.models import (
10+
InstalledTargetKernelInfo,
11+
KernelCmdlineArg,
12+
LateTargetKernelCmdlineArgTasks,
13+
TargetKernelCmdlineArgTasks
14+
)
915

1016
KERNEL_CMDLINE_FILE = "/etc/kernel/cmdline"
1117

@@ -71,7 +77,9 @@ def retrieve_arguments_to_modify():
7177
kernelargs_msgs_to_add = list(api.consume(KernelCmdlineArg))
7278
kernelargs_msgs_to_remove = []
7379

74-
for target_kernel_arg_task in api.consume(TargetKernelCmdlineArgTasks):
80+
modification_msgs = itertools.chain(api.consume(TargetKernelCmdlineArgTasks),
81+
api.consume(LateTargetKernelCmdlineArgTasks))
82+
for target_kernel_arg_task in modification_msgs:
7583
kernelargs_msgs_to_add.extend(target_kernel_arg_task.to_add)
7684
kernelargs_msgs_to_remove.extend(target_kernel_arg_task.to_remove)
7785

repos/system_upgrade/common/actors/persistentnetnamesconfig/libraries/persistentnetnamesconfig.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import re
44

5-
from leapp.libraries.common.config import get_env
5+
from leapp.libraries.common.config import get_env, version
66
from leapp.libraries.stdlib import api
77
from leapp.models import (
88
InitrdIncludes,
@@ -39,6 +39,9 @@ def generate_link_file(interface):
3939

4040
@suppress_deprecation(InitrdIncludes)
4141
def process():
42+
if get_env('LEAPP_USE_NET_NAMING_SCHEMES', '0') == '1' and version.get_target_major_version() != '8':
43+
api.current_logger().info('Skipping generation of .link files renaming NICs as LEAPP_USE_NET_NAMING_SCHEMES=1')
44+
return
4245

4346
if get_env('LEAPP_NO_NETWORK_RENAMING', '0') == '1':
4447
api.current_logger().info(

repos/system_upgrade/common/models/kernelcmdlineargs.py

+21
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@ class TargetKernelCmdlineArgTasks(Model):
2424
to_remove = fields.List(fields.Model(KernelCmdlineArg), default=[])
2525

2626

27+
class LateTargetKernelCmdlineArgTasks(Model):
28+
"""
29+
Desired modifications of the target kernel args produced later in the upgrade process.
30+
31+
Defined to prevent loops in the actor dependency graph.
32+
"""
33+
topic = SystemInfoTopic
34+
35+
to_add = fields.List(fields.Model(KernelCmdlineArg), default=[])
36+
to_remove = fields.List(fields.Model(KernelCmdlineArg), default=[])
37+
38+
39+
class UpgradeKernelCmdlineArgTasks(Model):
40+
"""
41+
Modifications of the upgrade kernel cmdline.
42+
"""
43+
topic = SystemInfoTopic
44+
45+
to_add = fields.List(fields.Model(KernelCmdlineArg), default=[])
46+
47+
2748
class KernelCmdline(Model):
2849
"""
2950
Kernel command line parameters the system was booted with

0 commit comments

Comments
 (0)