Skip to content

Commit f446a57

Browse files
authored
Merge pull request #62 from prilr/CLOS-3465-block-system-upgrade-when-netdev-found-i
CLOS-3465: Inhibit upgrade when _netdev found in fstab
2 parents 173aefc + 8ccb119 commit f446a57

3 files changed

Lines changed: 92 additions & 1 deletion

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class CheckMountOptions(Actor):
1111
1212
Checks performed:
1313
- /var is mounted with the noexec option
14+
- any fstab entry uses the _netdev mount option
1415
"""
1516
name = "check_mount_options"
1617
consumes = (StorageInfo,)

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from leapp import reporting
2+
from leapp.libraries.common.config import get_env
23
from leapp.libraries.stdlib import api
34
from leapp.models import StorageInfo
45

@@ -70,6 +71,56 @@ def check_noexec_on_var(storage_info):
7071
return
7172

7273

74+
def check_netdev_mounts(storage_info):
75+
"""Check for fstab entries with the _netdev mount option without nofail.
76+
77+
Entries combining _netdev with nofail are skipped: nofail tells systemd not
78+
to fail the boot if the mount fails, so the upgrade can proceed even though
79+
the network mount itself will not come up before the first reboot.
80+
"""
81+
if get_env('LEAPP_DEVEL_INITRAM_NETWORK', None):
82+
return
83+
84+
netdev_entries = [
85+
entry for entry in storage_info.fstab
86+
if '_netdev' in entry.fs_mntops.split(',')
87+
and 'nofail' not in entry.fs_mntops.split(',')
88+
]
89+
90+
if not netdev_entries:
91+
return
92+
93+
entries_str = '\n'.join(
94+
'- {} (mounted at {})'.format(entry.fs_spec, entry.fs_file)
95+
for entry in netdev_entries
96+
)
97+
98+
reporting.create_report([
99+
reporting.Title(
100+
'Detected _netdev mount option in /etc/fstab, preventing a successful in-place upgrade.'
101+
),
102+
reporting.Summary(
103+
'Leapp detected one or more entries in /etc/fstab using the _netdev mount option '
104+
'without nofail:\n{}\n\n'
105+
'During the in-place upgrade, the system is disconnected from the network before '
106+
'the first reboot. Entries with the _netdev option cannot be mounted at that point, '
107+
'which causes the upgrade to fail.'.format(entries_str)
108+
),
109+
reporting.Remediation(
110+
hint=(
111+
'Either remove the _netdev option from the affected /etc/fstab entries before '
112+
'proceeding with the upgrade (and add it back afterwards if needed), or add the '
113+
'nofail option so the boot does not fail when the network mount is unavailable.'
114+
)
115+
),
116+
reporting.RelatedResource('file', '/etc/fstab'),
117+
reporting.Severity(reporting.Severity.HIGH),
118+
reporting.Groups([reporting.Groups.FILESYSTEM, reporting.Groups.NETWORK]),
119+
reporting.Groups([reporting.Groups.INHIBITOR]),
120+
])
121+
122+
73123
def check_mount_options():
74124
for storage_info in api.consume(StorageInfo):
75125
check_noexec_on_var(storage_info)
126+
check_netdev_mounts(storage_info)

repos/system_upgrade/common/actors/checkmountoptions/tests/test_checkmountoptions.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pytest
55

66
from leapp import reporting
7-
from leapp.libraries.actor.checkmountoptions import check_mount_options
7+
from leapp.libraries.actor.checkmountoptions import check_mount_options, check_netdev_mounts
88
from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked
99
from leapp.libraries.stdlib import api
1010
from leapp.models import FstabEntry, MountEntry, StorageInfo
@@ -59,3 +59,42 @@ def test_var_mounted_with_noexec_is_detected(monkeypatch, fstab_entries, mounts,
5959
check_mount_options()
6060

6161
assert bool(created_reports.called) == should_inhibit
62+
63+
64+
def _make_fstab_entry(fs_spec, fs_file, fs_mntops, fs_vfstype='ext4'):
65+
return FstabEntry(fs_spec=fs_spec, fs_file=fs_file, fs_vfstype=fs_vfstype,
66+
fs_mntops=fs_mntops, fs_freq='0', fs_passno='0')
67+
68+
69+
@pytest.mark.parametrize(
70+
('fstab_mntops', 'initram_network_envar', 'should_inhibit'),
71+
[
72+
('_netdev,defaults', None, True),
73+
('defaults,_netdev', None, True),
74+
('_netdev', None, True),
75+
('defaults', None, False),
76+
('netdev,defaults', None, False),
77+
('_netdev,defaults', '1', False),
78+
('_netdev,nofail', None, False),
79+
('nofail,_netdev,defaults', None, False),
80+
('_netdev,nofail,defaults', None, False),
81+
]
82+
)
83+
def test_netdev_in_fstab_is_detected(monkeypatch, fstab_mntops, initram_network_envar, should_inhibit):
84+
fstab_entries = [
85+
_make_fstab_entry('UUID=abc123', '/var/lib/psa/dumps', fstab_mntops),
86+
_make_fstab_entry('/dev/sda1', '/', 'defaults'),
87+
]
88+
storage_info = StorageInfo(fstab=fstab_entries)
89+
90+
envars = {'LEAPP_DEVEL_INITRAM_NETWORK': initram_network_envar} if initram_network_envar else {}
91+
created_reports = create_report_mocked()
92+
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(envars=envars))
93+
monkeypatch.setattr(reporting, 'create_report', created_reports)
94+
95+
check_netdev_mounts(storage_info)
96+
97+
assert bool(created_reports.called) == should_inhibit
98+
if should_inhibit:
99+
assert '_netdev' in created_reports.report_fields['title']
100+
assert 'UUID=abc123' in created_reports.report_fields['summary']

0 commit comments

Comments
 (0)