-
Notifications
You must be signed in to change notification settings - Fork 86
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
WIP: Allow setting file system UUIDs #537
Conversation
I'm heading for a hybrid approach (using UUIDs and partition layout holes) in nixpart for achieving storage tree determinism, so we need to have support for setting UUIDs. Blivet currently doesn't yet support this, so I've implemented it. Upstream pull request: storaged-project/blivet#537 Signed-off-by: aszlig <[email protected]>
This is currently lacking support for setting UUIDs on BTRFS, but I'm working on that tomorrow. So please review but don't merge yet :-) |
s/tomorrow/weekend/ |
Jenkins, ok to test. |
Jenkins, test this, please. |
These are the failures the CI tests report: |
The results are not yet published anywhere, sorry. This should change next week. |
Reported by @vpodzine at: storaged-project#537 (comment) These are the failures the CI tests report: *** Running pylint *** PYTHONPATH=.:tests/: tests/pylint/runpylint.py ************* Module /home/jenkins/workspace/blivet-2.x-PR/blivet/formats/fs.py W1201(logging-not-lazy):399,12: FS._create: Specify string format arguments as logging function parameters ************* Module /home/jenkins/workspace/blivet-2.x-PR/blivet/tasks/fsuuid.py W0402(deprecated-module): 3,0: : Uses of a deprecated module 'string' ************* Module /home/jenkins/workspace/blivet-2.x-PR/blivet/tasks/fsmkfs.py E0102(function-redefined): 57,4: FSMkfs.get_uuid_args: method already defined line 51 ************* Module /home/jenkins/workspace/blivet-2.x-PR/tests/formats_test/methods_test.py E1101(no-member):303,12: FSMethodsTestCase._test_create_backend: Instance of 'DeviceFormat' has no '_mkfs' member E1101(no-member):305,26: FSMethodsTestCase._test_create_backend: Instance of 'DeviceFormat' has no 'relabels' member E1101(no-member):306,25: FSMethodsTestCase._test_create_backend: Instance of 'DeviceFormat' has no 'can_assign_uuid' member Makefile:75: recipe for target 'pylint' failed The warning about deprecated-module seems to be a bug in pylint: https://www.logilab.org/ticket/2481 Instead of silencing the warning, I'm using the hex digit characters directly, because string.hexdigits contains both lower-case a-f and upper-case A-F hex characters. In our case however, we only want to match case-sensitive. Signed-off-by: aszlig <[email protected]>
Reported by @vpodzime at: storaged-project#537 (comment) These are the failures the CI tests report: *** Running pylint *** PYTHONPATH=.:tests/: tests/pylint/runpylint.py ************* Module /home/jenkins/workspace/blivet-2.x-PR/blivet/formats/fs.py W1201(logging-not-lazy):399,12: FS._create: Specify string format arguments as logging function parameters ************* Module /home/jenkins/workspace/blivet-2.x-PR/blivet/tasks/fsuuid.py W0402(deprecated-module): 3,0: : Uses of a deprecated module 'string' ************* Module /home/jenkins/workspace/blivet-2.x-PR/blivet/tasks/fsmkfs.py E0102(function-redefined): 57,4: FSMkfs.get_uuid_args: method already defined line 51 ************* Module /home/jenkins/workspace/blivet-2.x-PR/tests/formats_test/methods_test.py E1101(no-member):303,12: FSMethodsTestCase._test_create_backend: Instance of 'DeviceFormat' has no '_mkfs' member E1101(no-member):305,26: FSMethodsTestCase._test_create_backend: Instance of 'DeviceFormat' has no 'relabels' member E1101(no-member):306,25: FSMethodsTestCase._test_create_backend: Instance of 'DeviceFormat' has no 'can_assign_uuid' member Makefile:75: recipe for target 'pylint' failed The warning about deprecated-module seems to be a bug in pylint: https://www.logilab.org/ticket/2481 Instead of silencing the warning, I'm using the hex digit characters directly, because string.hexdigits contains both lower-case a-f and upper-case A-F hex characters. In our case however, we only want to match case-sensitive. Signed-off-by: aszlig <[email protected]>
@vpodzime: Thanks :-) The pylint warnings should be resolved now. |
So far we had disabled the tests while referring to the NixOS VM test instead. However, it's desirable to run as much tests as we can, so let's run the pocketlint tests in checkPhase instead of skipping it altogether. In my case this is very useful because it would have caught a few errors during development of the UUIDs pull request: storaged-project/blivet#537 But even if we're not directly developing for the upstream project, this also catches Nix-related errors, such as references against pyanaconda which might still exist or exist again after an update. I'm using a list to accumulate find arguments because I wanted to avoid endless repetitions of -o -path xyz -prune. Signed-off-by: aszlig <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than the comments below, could you please squash the changes a bit? I'm okay with the final pylint fixes, but for example the commits that fix/change the label restrictions should be squashed to the commits that set the restrictions in the first place.
@@ -298,6 +298,7 @@ def available_resource(name): | |||
MKFS_XFS_APP = application("mkfs.xfs") | |||
MKNTFS_APP = application("mkntfs") | |||
MKREISERFS_APP = application("mkreiserfs") | |||
MLABEL_APP = application("mlabel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the fatlabel
utility that's part of the dosfstools
package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fatlabel
doesn't have the ability to set UUIDs yet, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=563746 and its source at https://github.com/dosfstools/dosfstools/blob/820c2f90726db0468e0a684a5dc500fbde66466f/src/fatlabel.c.
return False | ||
chunklens = [len(chunk) for chunk in chunks | ||
if all(char in hexdigits for char in chunk)] | ||
return chunklens == [8, 4, 4, 4, 12] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these the only restrictions specified by the RFC? Or should this be using some regexp to limit the character sets as well instead?
@@ -86,6 +86,7 @@ def __init__(self, **kwargs): | |||
:keyword mountpoint: the filesystem's planned mountpoint | |||
:keyword label: the filesystem label | |||
:keyword uuid: the filesystem UUID | |||
:keyword new_uuid: new UUID to set for this filesystem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this. We know if the file system exists (in the system) or not (just in the model). If the UUID is set before it even exists, it means that it is requested to be set on creation, doesn't it? And other parts of blivet shouldn't care about these things, they just need to know the UUID of the file system when everything is created (for /etc/fstab
,...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mhm, you're right, it makes sense to actually use the uuid
keyword here.
@@ -251,6 +253,14 @@ def label_format_ok(self, label): | |||
label = property(lambda s: s._get_label(), lambda s, l: s._set_label(l), | |||
doc="this filesystem's label") | |||
|
|||
def can_assign_uuid(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about can_set_uuid
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would have sounded odd for can_reassign_uuid
: can_reset_uuid
sounds a bit like "use the autogenerated UUID".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about can_set_uuid
and can_modify_uuid
? (or change
or edit
for that matter).
@@ -261,6 +264,14 @@ def can_assign_uuid(self): | |||
""" | |||
return self._mkfs.can_set_uuid and self._mkfs.available | |||
|
|||
def can_reassign_uuid(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And can_reset_uuid
here?
@@ -21,8 +21,10 @@ | |||
# | |||
|
|||
from parted import PARTITION_SWAP, fileSystemType | |||
from ..errors import FSWriteUUIDError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note on the commit message -- swap is not a file system. It's just a device format, similarly to LVM PV, LUKS or these things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know, the commit message says "other file systems" and I should have just used "file systems".
else: | ||
if not self.uuid_format_ok(self.new_uuid): | ||
raise FSWriteUUIDError("bad UUID format for swap filesystem") | ||
extra = [blockdev.ExtraArg("-U", self.new_uuid)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to just pass the {"-U": self.uuid}
dictionary as extra
making things look more pythonic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, will do that.
try: | ||
self.write_uuid() | ||
except FSError as e: | ||
log.warning("Failed to write UUID (%s) for filesystem %s: %s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually needs to (re)raise the exception. Otherwise it would go unnoticed. Nobody reads the logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it makes even more sense to not log this in the first place but actually directly throw an exception.
@@ -37,13 +40,21 @@ class SetUUIDWithMkFs(SetUUID): | |||
native mkfs tool can set the UUID. | |||
""" | |||
|
|||
def test_set_uuid(self): | |||
@skipIf(sys.version_info < (3, 4), "assertLogs is not supported") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed if the method actually raises an exception.
There's also one more thing -- could you please check the 3.0-devel branch which contains changes from the #434 -- we will need a special action type for (re)setting UUID there. |
These arguments are only relevant if we want to set a UUID during creation of file systems. Right now this doesn't implement any real functionality and only implements the interface and the options for various file systems. The reason I'm using get_uuid_args instead of just uuid_options is that for XFS, we need to use ["-m", "uuid=something"], so we don't have a single flag with a plain argument. Signed-off-by: aszlig <[email protected]>
This is more or less modelled the same way as tasks.fslabeling, consisting of classes that currently only contain a method for verifying whether an UUID is correct for a given file system or not. I'm using _uuidfs within the FS class (and subclasses) even though it sounds a bit odd, mainly because it is consistent with _labelfs and also because it avoids clashes with future _uuid attributes. For the _check_rfc4122_uuid() class method, I'm using string processing rather than uuid.UUID(), because first of all uuid.UUID() is much more complicated and it also allows to omit the dashes while the file system utilities may not allow this. Signed-off-by: aszlig <[email protected]>
We already have a uuid keyword argument, which might get passed if the format already exists, but if it exists on a new format, we can safely assume that the user wants the format to have a specific UUID and we use that instead of letting the mkfs utility generate a random UUID. The implementation for _create() deviates a bit from the scheme we had with labels so far, where we have a labeling() method which returns True if we can set a label either during or after the creation of a file system. In our case we now have a can_assign_uuid(), which *only* returns True if we can assign an UUID during creation. We will have another method soon which checks whether we can assign an UUID *after* creation. Also, using a similar naming scheme to labeling(), we would have ended up with something like uuiding() or any other weird name. IMHO setting this to can_assign_uuid() also has the advantage that the name in itself is more descriptive about what this method does. Signed-off-by: aszlig <[email protected]>
Some file systems don't have utilities for setting the UUID during creation, so we need to fall back to doing it afterwards in _post_create(). This introduces mlabel (for FAT) and tune2fs (for ext2/3/4) as new applications required in tasks/availability. The "mlabel" tool is required to set serials for FAT file systems, because dosfstools seems to only allow setting the volume ID during creation and doesn't have a command to change it for existing file systems. "fatlabel" only can set labels, but not UUIDs but "mlabel" can. Unfortunately, I haven't found a way to set HFS+ UUIDs after creation using the hfsprogs package, so I'm omitting HFS+ here for now. Signed-off-by: aszlig <[email protected]>
These are the more simple tests, because they only rely on pure functions. We're only possing a range of invalid and valid UUIDs and assert that the checkers (uuid_format_ok) return correctly. Signed-off-by: aszlig <[email protected]>
While we already had pure tests for just verifying whether an UUID is correct, let's now add the impure stuff that actually uses the various file system tools to write the UUIDs for real. Note that the fsuuid.py file does not use the same scheme than fslabeling.py, even though it might look similar. For UUIDs we assume here, that we can _always_ assign a new UUID after the file system has been created. Of course, this is not yet true for HFS+, because I haven't yet found a way to set the UUID afterwards. So the distinction between SetUUIDWithMkFs and SetUUIDAfterMkFs is that with the former we assume that the mkfs utility has a way to set the UUID, even though we have a _post_create() method that sets the UUID if that's not the case. Signed-off-by: aszlig <[email protected]>
This is quite similar to what we have for file systems, but doesn't rely on the fsuuid/fswriteuuid tasks except for the _check_rfc4122_uuid() method from fsuuid. Other than that the implementation is the same. We can't assign the UUID for a swap file system at a later point, so we only implement this during creation. Signed-off-by: aszlig <[email protected]>
So far we only have tested valid UUIDs properly, while we had a test using invalid UUIDs the test was more or less a no-op, because during creation no error is thrown if we use an invalid UUID. Rather than throwing an error the create() method logs a warning, which we now verify using assertLogs test_set_invalid_uuid(). Signed-off-by: aszlig <[email protected]>
@vpodzime: I've addressed most of the points you've mentioned here:
The following point is not yet addressed:
Also, even though I have squashed a few of the commits I personally think that squashing commits during review is a bad idea, because it's difficult for the reviewer to keep track on what's already been reviewed and what's left to review. As for addressing |
This has been requested by @vpodzime in storaged-project/blivet#537 and it obviously didn't make sense to introduce a new keyword argument. IIRC the reason for introducing it in the first place was that it might conflict with the device tree populator. But when creating a new file system it doesn't matter because we don't go through the populator anyway and we also don't run _create on a pre-existing filesystem unless we really want to format it, but then it's fine to set the UUID. Signed-off-by: aszlig <[email protected]>
Regarding setting UUIDs for BTRFS (well, not only BTRFS but
On the other hand creating a new file in |
Makes sense. I'm okay with the assign versions. |
If it's possible, I'd like to have this PR finished, reviewed and merged to 2.1-devel and then to 3.0-devel by the merge of 2.1-devel into 3.0-devel and then have a new PR for 3.0-devel adding the extra features. |
Sounds good to me. |
#573 contains changes from here and is merged. |
I'm heading for a hybrid approach (using UUIDs and partition layout holes) in nixpart for achieving storage tree determinism, so we need to have support for setting UUIDs. Blivet currently doesn't yet support this, so I've implemented it. Upstream pull request: storaged-project/blivet#537 Signed-off-by: aszlig <[email protected]>
So far we had disabled the tests while referring to the NixOS VM test instead. However, it's desirable to run as much tests as we can, so let's run the pocketlint tests in checkPhase instead of skipping it altogether. In my case this is very useful because it would have caught a few errors during development of the UUIDs pull request: storaged-project/blivet#537 But even if we're not directly developing for the upstream project, this also catches Nix-related errors, such as references against pyanaconda which might still exist or exist again after an update. I'm using a list to accumulate find arguments because I wanted to avoid endless repetitions of -o -path xyz -prune. Signed-off-by: aszlig <[email protected]>
Implements setting UUIDs via passing
new_uuid="..."
to the format.This is similar to setting labels, but while labels are set by just passing the
label=something
argument, this usesnew_uuid=something
because first of all, an UUID is not optional and we also have anuuid
argument that's already used by the device tree populator to pass the UUID from the udev attributes.File systems that support setting UUIDs are now ext2/3/4, FAT, NTFS, XFS, JFS, ReiserFS, HFS+ and swap. Not supported is HFS, because it doesn't seem to have a notion for setting serial or any kind of volume ID.