From dc6778ee8ede52966f46491b09bc30ae3f9c1be4 Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Wed, 12 Feb 2025 12:39:58 +0100 Subject: [PATCH 1/5] Do not mark non-existing btrfs subvolumes format as immutable The volume format is not marked as immutable so there is no reason to mark the subvolumes format as such. --- blivet/devices/btrfs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/blivet/devices/btrfs.py b/blivet/devices/btrfs.py index b9ed2fafb..4b27896d8 100644 --- a/blivet/devices/btrfs.py +++ b/blivet/devices/btrfs.py @@ -543,7 +543,6 @@ class BTRFSSubVolumeDevice(BTRFSDevice): """ A btrfs subvolume pseudo-device. """ _type = "btrfs subvolume" - _format_immutable = True def __init__(self, *args, **kwargs): """ @@ -567,6 +566,10 @@ def __init__(self, *args, **kwargs): self.volume._add_subvolume(self) + @property + def format_immutable(self): + return super(BTRFSSubVolumeDevice, self).format_immutable or self.exists + def _set_mountopts(self): # propagate mount options specified for members via kickstart opts = "subvol=%s" % self.name From 2b0813ebb9fa2214c3d3d8f8b23943edc1d0ad8e Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Wed, 12 Feb 2025 13:17:05 +0100 Subject: [PATCH 2/5] fstab: Remove the special FSTabOption attribute We don't need a special property for fstab options, especially not for mount options which are already covered by the existing "options" property. --- blivet/formats/fs.py | 8 +++--- blivet/formats/swap.py | 7 ++--- blivet/fstab.py | 45 ++++++++++--------------------- tests/storage_tests/fstab_test.py | 8 +++--- 4 files changed, 27 insertions(+), 41 deletions(-) diff --git a/blivet/formats/fs.py b/blivet/formats/fs.py index dfe5eadb5..2ae80fb5b 100644 --- a/blivet/formats/fs.py +++ b/blivet/formats/fs.py @@ -55,7 +55,7 @@ from . import DeviceFormat, register_device_format from .. import util from ..flags import flags -from ..fstab import FSTabOptions, HAVE_LIBMOUNT +from ..fstab import HAVE_LIBMOUNT from ..storage_log import log_exception_info, log_method_call from .. import arch from ..size import Size, ROUND_UP @@ -132,8 +132,6 @@ def __init__(self, **kwargs): DeviceFormat.__init__(self, **kwargs) - self.fstab = FSTabOptions() - # Create task objects self._fsck = self._fsck_class(self) self._mkfs = self._mkfs_class(self) @@ -148,6 +146,10 @@ def __init__(self, **kwargs): self.mountpoint = kwargs.get("mountpoint") self.mountopts = kwargs.get("mountopts", "") + self.freq = kwargs.get("freq", 0) + self.passno = kwargs.get("passno", 0) + self.fstab_spec_type = kwargs.get("fstab_spec_type", None) + self.label = kwargs.get("label") self.fsprofile = kwargs.get("fsprofile") self._mkfs_nodiscard = kwargs.get("nodiscard", False) diff --git a/blivet/formats/swap.py b/blivet/formats/swap.py index 03be883cc..ca105a976 100644 --- a/blivet/formats/swap.py +++ b/blivet/formats/swap.py @@ -24,7 +24,6 @@ from parted import PARTITION_SWAP, fileSystemType from ..errors import FSWriteUUIDError, SwapSpaceError -from ..fstab import FSTabOptions from ..storage_log import log_method_call from ..tasks import availability from . import DeviceFormat, register_device_format @@ -78,11 +77,13 @@ def __init__(self, **kwargs): log_method_call(self, **kwargs) DeviceFormat.__init__(self, **kwargs) - self.fstab = FSTabOptions() - self.priority = kwargs.get("priority", -1) self.label = kwargs.get("label") + self.freq = kwargs.get("freq", 0) + self.passno = kwargs.get("passno", 0) + self.fstab_spec_type = kwargs.get("fstab_spec_type", None) + def __repr__(self): s = DeviceFormat.__repr__(self) s += (" priority = %(priority)s label = %(label)s" % diff --git a/blivet/fstab.py b/blivet/fstab.py index 6249f34a9..d8085753a 100644 --- a/blivet/fstab.py +++ b/blivet/fstab.py @@ -34,23 +34,6 @@ log = logging.getLogger("blivet") -class FSTabOptions(object): - """ User preferred fstab settings object intended to be attached to device.format. - Set variables override otherwise automatically obtained values put into fstab. - """ - - def __init__(self): - self.freq = None - self.passno = None - - # preferred spec identification type; default "UUID" - # possible values: None, "UUID", "LABEL", "PARTLABEL", "PARTUUID", "PATH" - self.spec_type = None - - # list of fstab options to be used - self.mntops = [] - - class FSTabEntry(object): """ One processed line of fstab """ @@ -762,8 +745,8 @@ def _get_spec(self, device): spec = None - if hasattr(device.format, 'fstab') and device.format.fstab.spec_type: - spec_type = device.format.fstab.spec_type + if hasattr(device.format, "fstab_spec_type") and device.format.fstab_spec_type: + spec_type = device.format.fstab_spec_type else: spec_type = self.spec_type @@ -825,25 +808,25 @@ def update(self, action, bae_entry): # device is not present in fstab and has a defined mountpoint => add it self.add_entry(spec=spec, file=action.device.format.mountpoint, - mntops=action.device.format.fstab.mntops, - freq=action.device.format.fstab.freq, - passno=action.device.format.fstab.passno, + mntops=action.device.format.options, + freq=action.device.format.freq, + passno=action.device.format.passno, entry=entry) elif found and found.spec != spec and action.device.format.mountpoint is not None: # allow change of spec of existing devices self.remove_entry(entry=found) self.add_entry(spec=spec, - mntops=action.device.format.fstab.mntops, - freq=action.device.format.fstab.freq, - passno=action.device.format.fstab.passno, + mntops=action.device.format.options, + freq=action.device.format.freq, + passno=action.device.format.passno, entry=found) elif found and found.file != action.device.format.mountpoint and action.device.format.mountpoint is not None: # device already exists in fstab but with a different mountpoint => add it self.add_entry(spec=spec, file=action.device.format.mountpoint, - mntops=action.device.format.fstab.mntops, - freq=action.device.format.fstab.freq, - passno=action.device.format.fstab.passno, + mntops=action.device.format.options, + freq=action.device.format.freq, + passno=action.device.format.passno, entry=found) return @@ -858,9 +841,9 @@ def update(self, action, bae_entry): self.remove_entry(entry=bae_entry) self.add_entry(spec=spec, file=action.device.format.mountpoint, - mntops=action.device.format.fstab.mntops, - freq=action.device.format.fstab.freq, - passno=action.device.format.fstab.passno, + mntops=action.device.format.options, + freq=action.device.format.freq, + passno=action.device.format.passno, entry=bae_entry) elif action.device.format.mountpoint is None: self.remove_entry(entry=bae_entry) diff --git a/tests/storage_tests/fstab_test.py b/tests/storage_tests/fstab_test.py index 1cbd91caf..94cee0cc3 100644 --- a/tests/storage_tests/fstab_test.py +++ b/tests/storage_tests/fstab_test.py @@ -71,10 +71,10 @@ def test_fstab(self): self.storage.create_device(lv) # specify device spec representation in fstab - lv.format.fstab.spec_type = "PATH" - lv.format.fstab.freq = 54321 - lv.format.fstab.passno = 2 - lv.format.fstab.mntops = ['optionA', 'optionB'] + lv.format.fstab_spec_type = "PATH" + lv.format.freq = 54321 + lv.format.passno = 2 + lv.format.options = "optionA,optionB" # Change the mountpoint, make sure the change will make it into the fstab ac = blivet.deviceaction.ActionConfigureFormat(device=lv, attr="mountpoint", new_value="/mnt/test2") From 992c4980acd6ca188e55474538e9905a115bd03e Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Wed, 12 Feb 2025 13:28:23 +0100 Subject: [PATCH 3/5] tests: Add some more tests for fstab management --- tests/storage_tests/fstab_test.py | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/storage_tests/fstab_test.py b/tests/storage_tests/fstab_test.py index 94cee0cc3..ce18825c1 100644 --- a/tests/storage_tests/fstab_test.py +++ b/tests/storage_tests/fstab_test.py @@ -147,6 +147,57 @@ def test_luks_creation(self): self.assertTrue("/mnt/test_fstab_luks_correct" in contents) self.assertFalse("/mnt/test_fstab_luks_wrong" in contents) + def test_btrfs_creation(self): + unavailable_deps = blivet.devices.BTRFSVolumeDevice.unavailable_type_dependencies() + if unavailable_deps: + dep_str = ", ".join([d.name for d in unavailable_deps]) + raise unittest.SkipTest("some unavailable dependencies required for this test: %s" % dep_str) + + disk = self.storage.devicetree.get_device_by_path(self.vdevs[0]) + self.assertIsNotNone(disk) + + fstab_path = '/tmp/myfstab' + + with tempfile.TemporaryDirectory() as tmpdirname: + fstab_path = os.path.join(tmpdirname, 'fstab') + + # change write path of blivet.fstab + self.storage.fstab.dest_file = fstab_path + + self.storage.initialize_disk(disk) + + part = self.storage.new_partition(size=blivet.size.Size("1 GiB"), fmt_type="btrfs", + parents=[disk]) + self.storage.create_device(part) + + blivet.partitioning.do_partitioning(self.storage) + + vol = self.storage.new_btrfs(name="blivetTestVol", parents=[part]) + self.storage.create_device(vol) + + sub = self.storage.new_btrfs_sub_volume(parents=[vol], name="blivetTestSubVol1", + mountpoint="/home") + self.storage.create_device(sub) + + # create second subvolume with some more settings + sub = self.storage.new_btrfs_sub_volume(parents=[vol], name="blivetTestSubVol2", + mountpoint="/var", + fmt_args={"fstab_spec_type": "PATH", + "freq": 1, "passno": 1}) + self.storage.create_device(sub) + + self.storage.do_it() + self.storage.reset() + + with open(fstab_path, "r") as f: + contents = f.read() + + # mountpoints are set and fstab management enabled, fstab entries should be + # automatically added for both subvolumes + self.assertEqual(contents, + "%s1 /var btrfs subvol=blivetTestSubVol2 1 1\n" + "UUID=%s /home btrfs subvol=blivetTestSubVol1 0 0\n" % (self.vdevs[0], vol.uuid)) + def test_swap_creation(self): # test swap creation for presence of FSTabOptions object disk = self.storage.devicetree.get_device_by_path(self.vdevs[0]) From 68da0a96151250b1f5f14f015d5d329357c305dc Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 13 Feb 2025 07:05:58 +0100 Subject: [PATCH 4/5] fstab: Rename "mntops" to "mntopts" We use "opts" for "options" everywhere, calling it "ops" here is confusing. --- blivet/fstab.py | 76 +++++++++++++++++----------------- tests/unit_tests/fstab_test.py | 18 ++++---- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/blivet/fstab.py b/blivet/fstab.py index d8085753a..78eb787f7 100644 --- a/blivet/fstab.py +++ b/blivet/fstab.py @@ -38,7 +38,7 @@ class FSTabEntry(object): """ One processed line of fstab """ - def __init__(self, spec=None, file=None, vfstype=None, mntops=None, + def __init__(self, spec=None, file=None, vfstype=None, mntopts=None, freq=None, passno=None, comment=None, *, entry=None): # Note: "*" in arguments means that every following parameter can be used @@ -55,8 +55,8 @@ def __init__(self, spec=None, file=None, vfstype=None, mntops=None, self.file = file if vfstype is not None: self.vfstype = vfstype - if mntops is not None: - self.mntops = mntops + if mntopts is not None: + self.mntopts = mntopts if freq is not None: self.freq = freq if passno is not None: @@ -152,7 +152,7 @@ def vfstype(self, value): self._entry.fstype = value if value is not None else "" @property - def mntops(self): + def mntopts(self): """ Return mount options :returns: list of mount options or None when not set @@ -164,7 +164,7 @@ def mntops(self): return self._entry.options.split(',') - def get_raw_mntops(self): + def get_raw_mntopts(self): """ Return mount options :returns: comma separated string of mount options or None when not set @@ -173,11 +173,11 @@ def get_raw_mntops(self): return self._entry.options if self._entry.options != "" else None - @mntops.setter - def mntops(self, values): + @mntopts.setter + def mntopts(self, values): """ Set new mount options from the list of strings - :param values: mount options (see man fstab(5) fs_mntops) + :param values: mount options (see man fstab(5) fs_mntopts) :type values: list of str """ @@ -189,10 +189,10 @@ def mntops(self, values): else: self._entry.options = ','.join([x for x in values if x != ""]) - def mntops_add(self, values): + def mntopts_add(self, values): """ Append new mount options to already existing ones - :param values: mount options (see man fstab(5) fs_mntops) + :param values: mount options (see man fstab(5) fs_mntopts) :type values: list of str """ @@ -238,7 +238,7 @@ def is_valid(self): :returns: False if any of the listed values is not set; otherwise True :rtype: bool """ - items = [self.spec, self.file, self.vfstype, self.mntops, self.freq, self.passno] + items = [self.spec, self.file, self.vfstype, self.mntopts, self.freq, self.passno] # (Property getters replace empty strings with None) return not any(x is None for x in items) @@ -392,7 +392,7 @@ def entry_from_device(self, device): else: entry.vfstype = device.format.type - entry.mntops = device.format.options + entry.mntopts = device.format.options if device.format.check and entry.file == "/": entry.passno = 1 @@ -443,7 +443,7 @@ def entry_from_action(self, action): else: entry.vfstype = action.device.format.type - entry.mntops = action.device.format.options + entry.mntopts = action.device.format.options if action.device.format.check and entry.file == "/": entry.passno = 1 @@ -474,34 +474,34 @@ def read(self): self._table.parse_fstab(self.src_file) - def find_device(self, devicetree, spec=None, mntops=None, blkid_tab=None, crypt_tab=None, *, entry=None): + def find_device(self, devicetree, spec=None, mntopts=None, blkid_tab=None, crypt_tab=None, *, entry=None): """ Find a blivet device, based on spec or entry. Mount options can be used to refine the search. - If both entry and spec/mntops are given, spec/mntops are prioritized over entry values. + If both entry and spec/mntopts are given, spec/mntopts are prioritized over entry values. :param devicetree: populated blivet.Devicetree instance :type devicetree: :class: `blivet.Devicetree` :keyword spec: searched device specs (see man fstab(5) fs_spec) :type spec: str - :keyword mntops: list of mount option strings (see man fstab(5) fs_mntops) + :keyword mntopts: list of mount option strings (see man fstab(5) fs_mntopts) :type mnops: list :keyword blkid_tab: Blkidtab object :type blkid_tab: :class: `BlkidTab` :keyword crypt_tab: Crypttab object :type crypt_tab: :class: `CryptTab` - :keyword entry: fstab entry with its spec (and mntops) filled as an alternative input type + :keyword entry: fstab entry with its spec (and mntopts) filled as an alternative input type :type: :class: `FSTabEntry` :returns: found device or None :rtype: :class: `~.devices.StorageDevice` or None """ _spec = spec or (entry.spec if entry is not None else None) - _mntops = mntops or (entry.mntops if entry is not None else None) - _mntops_str = ",".join(_mntops) if mntops is not None else None + _mntopts = mntopts or (entry.mntopts if entry is not None else None) + _mntopts_str = ",".join(_mntopts) if mntopts is not None else None - return devicetree.resolve_device(_spec, options=_mntops_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab) + return devicetree.resolve_device(_spec, options=_mntopts_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab) def get_device(self, devicetree, spec=None, file=None, vfstype=None, - mntops=None, blkid_tab=None, crypt_tab=None, *, entry=None): + mntopts=None, blkid_tab=None, crypt_tab=None, *, entry=None): """ Parse an fstab entry for a device and return the corresponding device from the devicetree. If not found, try to create a new device based on given values. Raises UnrecognizedFSTabError in case of invalid or incomplete data. @@ -510,7 +510,7 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None, :type devicetree: :class: `blivet.Devicetree` :keyword spec: searched device specs (see man fstab(5) fs_spec) :type spec: str - :keyword mntops: list of mount option strings (see man fstab(5) fs_mntops) + :keyword mntopts: list of mount option strings (see man fstab(5) fs_mntopts) :type mnops: list :keyword blkid_tab: Blkidtab object :type blkid_tab: :class: `BlkidTab` @@ -527,11 +527,11 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None, from blivet.errors import UnrecognizedFSTabEntryError _spec = spec or (entry.spec if entry is not None else None) - _mntops = mntops or (entry.mntops if entry is not None else None) - _mntops_str = ",".join(_mntops) if mntops is not None else None + _mntopts = mntopts or (entry.mntopts if entry is not None else None) + _mntopts_str = ",".join(_mntopts) if mntopts is not None else None # find device in the tree - device = devicetree.resolve_device(_spec, options=_mntops_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab) + device = devicetree.resolve_device(_spec, options=_mntopts_str, blkid_tab=blkid_tab, crypt_tab=crypt_tab) if device is None: if vfstype == "swap": @@ -540,7 +540,7 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None, parents=devicetree.resolve_device(_spec), fmt=get_format(vfstype, device=_spec, exists=True), exists=True) - elif vfstype == "bind" or (_mntops is not None and "bind" in _mntops): + elif vfstype == "bind" or (_mntopts is not None and "bind" in _mntopts): # bind mount... set vfstype so later comparison won't # turn up false positives vfstype = "bind" @@ -559,15 +559,15 @@ def get_device(self, devicetree, spec=None, file=None, vfstype=None, if hasattr(device.format, "mountpoint"): device.format.mountpoint = file - device.format.options = _mntops + device.format.options = _mntopts return device - def add_entry(self, spec=None, file=None, vfstype=None, mntops=None, + def add_entry(self, spec=None, file=None, vfstype=None, mntopts=None, freq=None, passno=None, comment=None, *, entry=None): """ Add a new entry into the table If both entry and other values are given, these values are prioritized over entry values. - If mntops/freq/passno is not set uses their respective default values. + If mntopts/freq/passno is not set uses their respective default values. :keyword spec: device specs (see man fstab(5) fs_spec) :type spec: str @@ -575,7 +575,7 @@ def add_entry(self, spec=None, file=None, vfstype=None, mntops=None, :type file: str :keyword vfstype: device file system type (see man fstab(5) fs_vfstype) :type vfstype: str - :keyword mntops: list of mount option strings (see man fstab(5) fs_mntops) + :keyword mntopts: list of mount option strings (see man fstab(5) fs_mntopts) :type mnops: list :keyword freq: whether to dump the filesystem (see man fstab(5) fs_freq) :type freq: int @@ -598,10 +598,10 @@ def add_entry(self, spec=None, file=None, vfstype=None, mntops=None, if vfstype is not None: _entry.vfstype = vfstype - if mntops is not None: - _entry.mntops = mntops - if _entry.mntops is None: - _entry.mntops = ['defaults'] + if mntopts is not None: + _entry.mntopts = mntopts + if _entry.mntopts is None: + _entry.mntopts = ['defaults'] if freq is not None: _entry.freq = freq @@ -808,7 +808,7 @@ def update(self, action, bae_entry): # device is not present in fstab and has a defined mountpoint => add it self.add_entry(spec=spec, file=action.device.format.mountpoint, - mntops=action.device.format.options, + mntopts=action.device.format.options, freq=action.device.format.freq, passno=action.device.format.passno, entry=entry) @@ -816,7 +816,7 @@ def update(self, action, bae_entry): # allow change of spec of existing devices self.remove_entry(entry=found) self.add_entry(spec=spec, - mntops=action.device.format.options, + mntopts=action.device.format.options, freq=action.device.format.freq, passno=action.device.format.passno, entry=found) @@ -824,7 +824,7 @@ def update(self, action, bae_entry): # device already exists in fstab but with a different mountpoint => add it self.add_entry(spec=spec, file=action.device.format.mountpoint, - mntops=action.device.format.options, + mntopts=action.device.format.options, freq=action.device.format.freq, passno=action.device.format.passno, entry=found) @@ -841,7 +841,7 @@ def update(self, action, bae_entry): self.remove_entry(entry=bae_entry) self.add_entry(spec=spec, file=action.device.format.mountpoint, - mntops=action.device.format.options, + mntopts=action.device.format.options, freq=action.device.format.freq, passno=action.device.format.passno, entry=bae_entry) diff --git a/tests/unit_tests/fstab_test.py b/tests/unit_tests/fstab_test.py index 512e362f9..920a81be6 100644 --- a/tests/unit_tests/fstab_test.py +++ b/tests/unit_tests/fstab_test.py @@ -55,7 +55,7 @@ def test_fstab(self): self.assertIsNotNone(entry, self.fstab) self.assertEqual(entry.file, "/mnt/mountpath") self.assertEqual(entry.vfstype, "xfs") - self.assertEqual(entry.mntops, ["ro", "noatime"]) + self.assertEqual(entry.mntopts, ["ro", "noatime"]) self.assertEqual(entry.freq, 0) self.assertEqual(entry.passno, 0) @@ -89,14 +89,14 @@ def test_fstab(self): self.assertEqual(entry.passno, 2) # check options update - self.assertEqual(entry.mntops, ["defaults"]) - entry.mntops_add("ro") - self.assertEqual(entry.mntops, ["defaults", "ro"]) - self.assertEqual(entry.get_raw_mntops(), "defaults,ro") - - entry.mntops_add(["noatime", "auto"]) - self.assertEqual(entry.mntops, ["defaults", "ro", "noatime", "auto"]) - self.assertEqual(entry.get_raw_mntops(), "defaults,ro,noatime,auto") + self.assertEqual(entry.mntopts, ["defaults"]) + entry.mntopts_add("ro") + self.assertEqual(entry.mntopts, ["defaults", "ro"]) + self.assertEqual(entry.get_raw_mntopts(), "defaults,ro") + + entry.mntopts_add(["noatime", "auto"]) + self.assertEqual(entry.mntopts, ["defaults", "ro", "noatime", "auto"]) + self.assertEqual(entry.get_raw_mntopts(), "defaults,ro,noatime,auto") def test_deepcopy(self): fstab1 = FSTabManager(None, 'dest') From 04986a6944686301cfb330e685a20dd7ad6388ae Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 13 Feb 2025 07:21:34 +0100 Subject: [PATCH 5/5] fstab: Add a simple test to read fstab using our code --- tests/storage_tests/fstab_test.py | 7 +++++ tests/unit_tests/fstab_test.py | 44 +++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/tests/storage_tests/fstab_test.py b/tests/storage_tests/fstab_test.py index ce18825c1..dd16b1d5a 100644 --- a/tests/storage_tests/fstab_test.py +++ b/tests/storage_tests/fstab_test.py @@ -31,6 +31,7 @@ def setUp(self): def _clean_up(self): self.storage.fstab.dest_file = None + self.storage.fstab.src_file = None self.storage.reset() for disk in self.storage.disks: @@ -91,6 +92,12 @@ def test_fstab(self): self.assertTrue("54321 2" in contents) self.assertTrue("optionA,optionB" in contents) + # check that we can read and parse the fstab written above + self.storage.fstab.src_file = fstab_path + self.storage.reset() + self.assertEqual(str(self.storage.fstab), + "/dev/mapper/blivetTestVG-blivetTestLVMine\t/mnt/test2\text4\toptionA,optionB\t54321\t2\t\n") + dev = self.storage.devicetree.get_device_by_name("blivetTestVG-blivetTestLVMine") self.storage.recursive_remove(dev) diff --git a/tests/unit_tests/fstab_test.py b/tests/unit_tests/fstab_test.py index 920a81be6..1836765a5 100644 --- a/tests/unit_tests/fstab_test.py +++ b/tests/unit_tests/fstab_test.py @@ -1,5 +1,6 @@ import copy import os +import tempfile import unittest from unittest.mock import Mock @@ -9,8 +10,6 @@ from blivet.size import Size from blivet import Blivet -FSTAB_WRITE_FILE = "/tmp/test-blivet-fstab2" - class FSTabTestCase(unittest.TestCase): @@ -20,19 +19,17 @@ def setUpClass(cls): raise unittest.SkipTest("Missing libmount support required for this test") def setUp(self): + self._temp_dir = tempfile.TemporaryDirectory() self.fstab = FSTabManager() self.addCleanup(self._clean_up) def _clean_up(self): - try: - os.remove(FSTAB_WRITE_FILE) - except FileNotFoundError: - pass + self._temp_dir.cleanup() def test_fstab(self): self.fstab.src_file = None - self.fstab.dest_file = FSTAB_WRITE_FILE + self.fstab.dest_file = os.path.join(self._temp_dir.name, "fstab") entry = FSTabEntry("/dev/sda_dummy", "/media/wrongpath", "xfs", ["ro", "noatime"]) @@ -71,7 +68,7 @@ def test_fstab(self): self.fstab.write() # read the file and verify its contents - with open(FSTAB_WRITE_FILE, "r") as f: + with open(os.path.join(self._temp_dir.name, "fstab"), "r") as f: contents = f.read() self.assertEqual(contents, "/dev/sdb_dummy /media/newpath ext4 defaults 0 0\n") @@ -233,3 +230,34 @@ def test_get_device(self): dev2 = self.fstab.get_device(b.devicetree, "/dev/sda_dummy", "/mnt/mountpath", "xfs", ["defaults"]) self.assertEqual(dev1, dev2) + + def test_read_fstab(self): + with open(os.path.join(self._temp_dir.name, "fstab"), "w+") as f: + f.write("#\n" + "# /etc/fstab\n" + "# Created by anaconda on Mon Feb 10 11:39:42 2025\n" + "#\n" + "# Accessible filesystems, by reference, are maintained under '/dev/disk/'.\n" + "# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.\n" + "#\n" + "# After editing this file, run 'systemctl daemon-reload' to update systemd\n" + "# units generated from this file.\n" + "#\n" + "UUID=bc08e185-280e-46d3-9ae3-c0cc62c486ff\t/\t\text4\tdefaults\t1 1\n" + "UUID=51d9dc40-d1a9-4fd9-b538-61d5bd020fda\t/boot\t\text4\tdefaults\t1 2\n" + "/dev/sda1\t/mnt/test\tntfs\tro,nosuid,users,nofail,noauto\t0 2\n") + + self.fstab.src_file = os.path.join(self._temp_dir.name, "fstab") + self.fstab.read() + + entry = self.fstab.find_entry(file="/") + self.assertIsNotNone(entry) + self.assertEqual(entry.spec, "UUID=bc08e185-280e-46d3-9ae3-c0cc62c486ff") + self.assertEqual(entry.vfstype, "ext4") + self.assertEqual(entry.mntopts, ["defaults"]) + + entry = self.fstab.find_entry("/dev/sda1") + self.assertIsNotNone(entry) + self.assertEqual(entry.file, "/mnt/test") + self.assertEqual(entry.vfstype, "ntfs") + self.assertEqual(entry.mntopts, ['ro', 'nosuid', 'users', 'nofail', 'noauto'])