Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[revived] Leapp initram networking poc #960

Merged
merged 3 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

def add_boot_entry(configs=None):
debug = 'debug' if os.getenv('LEAPP_DEBUG', '0') == '1' else ''

enable_network = os.getenv('LEAPP_DEVEL_INITRAM_NETWORK') in ('network-manager', 'scripts')
ip_arg = ' ip=dhcp rd.neednet=1' if enable_network else ''
kernel_dst_path, initram_dst_path = get_boot_file_paths()
_remove_old_upgrade_boot_entry(kernel_dst_path, configs=configs)
try:
Expand All @@ -20,7 +21,7 @@ def add_boot_entry(configs=None):
'--title', 'RHEL-Upgrade-Initramfs',
'--copy-default',
'--make-default',
'--args', '{DEBUG} enforcing=0 rd.plymouth=0 plymouth.enable=0'.format(DEBUG=debug)
'--args', '{DEBUG}{NET} enforcing=0 rd.plymouth=0 plymouth.enable=0'.format(DEBUG=debug, NET=ip_arg)
]
if configs:
for config in configs:
Expand Down
4 changes: 4 additions & 0 deletions repos/system_upgrade/common/actors/checknfs/actor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from leapp import reporting
from leapp.actors import Actor
from leapp.libraries.common.config import get_env
from leapp.models import StorageInfo
from leapp.reporting import create_report, Report
from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
Expand All @@ -18,6 +19,9 @@ class CheckNfs(Actor):
tags = (ChecksPhaseTag, IPUWorkflowTag,)

def process(self):
# if network in initramfs is enabled NFS inhibitors are redundant
if get_env('LEAPP_DEVEL_INITRAM_NETWORK', None):
fernflower marked this conversation as resolved.
Show resolved Hide resolved
return
details = "NFS is currently not supported by the inplace upgrade.\n" \
"We have found NFS usage at the following locations:\n"

Expand Down
61 changes: 54 additions & 7 deletions repos/system_upgrade/common/actors/checknfs/tests/test_checknfs.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import pytest

from leapp.libraries.common import config
from leapp.models import FstabEntry, MountEntry, StorageInfo, SystemdMountEntry
from leapp.reporting import Report
from leapp.snactor.fixture import current_actor_context
from leapp.utils.report import is_inhibitor


@pytest.mark.parametrize('nfs_fstype', ('nfs', 'nfs4'))
def test_actor_with_systemdmount_entry(current_actor_context, nfs_fstype):
def test_actor_with_systemdmount_entry(current_actor_context, nfs_fstype, monkeypatch):
monkeypatch.setattr(config, 'get_env', lambda x, y: y)
with_systemdmount_entry = [SystemdMountEntry(node="nfs", path="n/a", model="n/a",
wwn="n/a", fs_type=nfs_fstype, label="n/a",
uuid="n/a")]
Expand All @@ -17,7 +19,8 @@ def test_actor_with_systemdmount_entry(current_actor_context, nfs_fstype):
assert is_inhibitor(report_fields)


def test_actor_without_systemdmount_entry(current_actor_context):
def test_actor_without_systemdmount_entry(current_actor_context, monkeypatch):
monkeypatch.setattr(config, 'get_env', lambda x, y: y)
without_systemdmount_entry = [SystemdMountEntry(node="/dev/sda1",
path="pci-0000:00:17.0-ata-2",
model="TOSHIBA_THNSNJ512GDNU_A",
Expand All @@ -30,7 +33,8 @@ def test_actor_without_systemdmount_entry(current_actor_context):


@pytest.mark.parametrize('nfs_fstype', ('nfs', 'nfs4'))
def test_actor_with_fstab_entry(current_actor_context, nfs_fstype):
def test_actor_with_fstab_entry(current_actor_context, nfs_fstype, monkeypatch):
monkeypatch.setattr(config, 'get_env', lambda x, y: y)
with_fstab_entry = [FstabEntry(fs_spec="lithium:/mnt/data", fs_file="/mnt/data",
fs_vfstype=nfs_fstype,
fs_mntops="noauto,noatime,rsize=32768,wsize=32768",
Expand All @@ -41,7 +45,8 @@ def test_actor_with_fstab_entry(current_actor_context, nfs_fstype):
assert is_inhibitor(report_fields)


def test_actor_without_fstab_entry(current_actor_context):
def test_actor_without_fstab_entry(current_actor_context, monkeypatch):
monkeypatch.setattr(config, 'get_env', lambda x, y: y)
without_fstab_entry = [FstabEntry(fs_spec="/dev/mapper/fedora-home", fs_file="/home",
fs_vfstype="ext4",
fs_mntops="defaults,x-systemd.device-timeout=0",
Expand All @@ -51,15 +56,17 @@ def test_actor_without_fstab_entry(current_actor_context):
assert not current_actor_context.consume(Report)


def test_actor_with_nfsd(current_actor_context):
def test_actor_with_nfsd(current_actor_context, monkeypatch):
monkeypatch.setattr(config, 'get_env', lambda x, y: y)
with_nfsd = [MountEntry(name="nfsd", mount="/proc/fs/nfsd", tp="nfsd", options="rw,relatime")]
current_actor_context.feed(StorageInfo(mount=with_nfsd))
current_actor_context.run()
assert not current_actor_context.consume(Report)


@pytest.mark.parametrize('nfs_fstype', ('nfs', 'nfs4'))
def test_actor_with_mount_share(current_actor_context, nfs_fstype):
def test_actor_with_mount_share(current_actor_context, nfs_fstype, monkeypatch):
monkeypatch.setattr(config, 'get_env', lambda x, y: y)
with_mount_share = [MountEntry(name="nfs", mount="/mnt/data", tp=nfs_fstype,
options="rw,nosuid,nodev,relatime,user_id=1000,group_id=1000")]
current_actor_context.feed(StorageInfo(mount=with_mount_share))
Expand All @@ -68,9 +75,49 @@ def test_actor_with_mount_share(current_actor_context, nfs_fstype):
assert is_inhibitor(report_fields)


def test_actor_without_mount_share(current_actor_context):
def test_actor_without_mount_share(current_actor_context, monkeypatch):
monkeypatch.setattr(config, 'get_env', lambda x, y: y)
without_mount_share = [MountEntry(name="tmpfs", mount="/run/snapd/ns", tp="tmpfs",
options="rw,nosuid,nodev,seclabel,mode=755")]
current_actor_context.feed(StorageInfo(mount=without_mount_share))
current_actor_context.run()
assert not current_actor_context.consume(Report)


def test_actor_skipped_if_initram_network_enabled(current_actor_context, monkeypatch):
"""Check that previous inhibitors are not stopping the upgrade in case env var is set"""
monkeypatch.setattr(config, 'get_env', lambda x, y: 'network-manager' if x == 'LEAPP_DEVEL_INITRAM_NETWORK' else y)
with_mount_share = [MountEntry(name="nfs", mount="/mnt/data", tp='nfs',
options="rw,nosuid,nodev,relatime,user_id=1000,group_id=1000")]
with_systemdmount_entry = [SystemdMountEntry(node="nfs", path="n/a", model="n/a",
wwn="n/a", fs_type='nfs', label="n/a",
uuid="n/a")]
with_fstab_entry = [FstabEntry(fs_spec="lithium:/mnt/data", fs_file="/mnt/data",
fs_vfstype='nfs',
fs_mntops="noauto,noatime,rsize=32768,wsize=32768",
fs_freq="0", fs_passno="0")]
current_actor_context.feed(StorageInfo(mount=with_mount_share,
systemdmount=with_systemdmount_entry,
fstab=with_fstab_entry))
current_actor_context.run()
assert not current_actor_context.consume(Report)


def test_actor_not_skipped_if_initram_network_empty(current_actor_context, monkeypatch):
"""Check that previous inhibitors are not stopping the upgrade in case env var is set"""
monkeypatch.setattr(config, 'get_env', lambda x, y: '' if x == 'LEAPP_DEVEL_INITRAM_NETWORK' else y)
with_mount_share = [MountEntry(name="nfs", mount="/mnt/data", tp='nfs',
options="rw,nosuid,nodev,relatime,user_id=1000,group_id=1000")]
with_systemdmount_entry = [SystemdMountEntry(node="nfs", path="n/a", model="n/a",
wwn="n/a", fs_type='nfs', label="n/a",
uuid="n/a")]
with_fstab_entry = [FstabEntry(fs_spec="lithium:/mnt/data", fs_file="/mnt/data",
fs_vfstype='nfs',
fs_mntops="noauto,noatime,rsize=32768,wsize=32768",
fs_freq="0", fs_passno="0")]
current_actor_context.feed(StorageInfo(mount=with_mount_share,
systemdmount=with_systemdmount_entry,
fstab=with_fstab_entry))
current_actor_context.run()
report_fields = current_actor_context.consume(Report)[0].report
assert is_inhibitor(report_fields)
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,19 @@ ibdmp() {
done
}

bring_up_network() {
if [ -f /etc/leapp-initram-network-manager ]; then
# NOTE(ivasilev) Reverting the change to see if it caused the crash
. /lib/dracut/hooks/cmdline/99-nm-config.sh
. /lib/dracut/hooks/initqueue/settled/99-nm-run.sh
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that in general, you'd want these regenerated&executed with updated dracut, which also runs them - otherwise I'd rather use the ones from the updated package:

/lib/dracut/modules.d/35network-manager/nm-config.sh
/lib/dracut/modules.d/35network-manager/nm-run.sh

They also expect /lib/nm-lib.sh to exist (should be updated from the same directory).

But you should be able to bring up the system with either.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you'd prefer that we substitute L199-L200 with

/lib/dracut/modules.d/35network-manager/nm-config.sh
/lib/dracut/modules.d/35network-manager/nm-run.sh

, right?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's what I assume, I've not tried it exists in the initrd... but I suppose it does. Let me know if you encounter issues, I'll investigate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pvalena hmmm I have tried that on rhel8 vagrant box and got an early fail in initram stage http://pastebin.test.redhat.com/1077800

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, let me check where the files live, when in the initrd.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the locations are indeed where the files are installed in the initrd.

IIUC:

  • This initrd is built specially for the upgrade (the dracut version/build is corresponding to the system you're running)
  • you don't have access to the rootfs (or don't have it upgraded yet)
  • dracut+initqueue was invoked already, and you've reached some target
  • the network is still not running (or not properly configured) and you need to start the interfaces (NetworkManager) manually
  • Initqueue settled means:
    This hook (initqueue/settled) gets executed every time udev has settled.

(I'm sorry If I've missed something, I still need to take a look at the reproducer.)

In that case it would make sense to run those paths you had originally.

Copy link
Member

@pirat89 pirat89 Oct 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pvalena not sure I understand it right, but, will it work on systems where the network manager is not used?
Update: ignore the question. I see the whole code now :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the ifup should do that.... TBH I'm not all that familiar with legacy networking, so I hope it's sufficient :).

fi
if [ -f /etc/leapp-initram-network-scripts ]; then
for interface in /sys/class/net/*;
do
ifup ${interface##*/};
done;
fi
}

do_upgrade() {
local args="" rv=0
Expand All @@ -202,6 +215,8 @@ do_upgrade() {
#getargbool 0 rd.upgrade.verbose && args="$args --verbose"
getargbool 0 rd.upgrade.debug && args="$args --debug"

bring_up_network

# Force selinux into permissive mode unless booted with 'enforcing=1'.
# FIXME: THIS IS A BIG STUPID HAMMER AND WE SHOULD ACTUALLY SOLVE THE ROOT
# PROBLEMS RATHER THAN JUST PAPERING OVER THE WHOLE THING. But this is what
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ install() {
# Q: Would we hack that in way of copy whole initramfs into the root, mount
# mount it and set envars

# Install network configuration triggers
if [ -f /etc/leapp-initram-network-manager ]; then
dracut_install /etc/leapp-initram-network-manager
fi

if [ -f /etc/leapp-initram-network-scripts ]; then
dracut_install /etc/leapp-initram-network-scripts
fi

# install this one to ensure we are able to sync write
inst_binary sync
# install in-band debugging utilities
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from leapp.utils.deprecation import suppress_deprecation

from leapp.models import ( # isort:skip
CopyFile,
RequiredUpgradeInitramPackages, # deprecated
UpgradeDracutModule, # deprecated
DracutModule,
Expand Down Expand Up @@ -42,6 +43,18 @@
]


def _create_initram_networking_tasks():
# include networking-related dracut modules
modules_map = {'network-manager': ('network-manager', '/etc/leapp-initram-network-manager'),
'scripts': ('network', '/etc/leapp-initram-network-scripts')}
initram_network_chosen = os.getenv('LEAPP_DEVEL_INITRAM_NETWORK', None)
if initram_network_chosen in modules_map:
module, touch_file = modules_map[initram_network_chosen]
yield UpgradeInitramfsTasks(include_dracut_modules=[DracutModule(name=module)])
# touch expected file
yield TargetUserSpaceUpgradeTasks(copy_files=[CopyFile(src='/dev/null', dst=touch_file)])


# The decorator is not effective for generators, it has to be used one level
# above
# @suppress_deprecation(UpgradeDracutModule)
Expand All @@ -68,6 +81,8 @@ def _create_initram_packages():
required_pkgs = _REQUIRED_PACKAGES[:]
if architecture.matches_architecture(architecture.ARCH_X86_64):
required_pkgs.append('biosdevname')
if os.getenv('LEAPP_DEVEL_INITRAM_NETWORK', None) == 'network-manager':
required_pkgs.append('NetworkManager')
if version.get_target_major_version() == '9':
required_pkgs += ['policycoreutils', 'rng-tools']
return (
Expand All @@ -79,3 +94,4 @@ def _create_initram_packages():
def process():
api.produce(*tuple(_create_dracut_modules()))
api.produce(*_create_initram_packages())
api.produce(*tuple(_create_initram_networking_tasks()))