-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
storage: Reimplement btrfs polling with benefits #20893
base: main
Are you sure you want to change the base?
storage: Reimplement btrfs polling with benefits #20893
Conversation
a178c2f
to
11232c7
Compare
03b3bbf
to
f4fd732
Compare
Some notes:
|
f4fd732
to
98e9cae
Compare
98e9cae
to
804c443
Compare
2062f54
to
37ba9ce
Compare
91f7300
to
1a21f04
Compare
0d8374b
to
58c912f
Compare
pkg/storaged/btrfs/btrfs-tool.py
Outdated
if b['fstype'] == "btrfs": | ||
uuid = b['uuid'] | ||
mps = list(filter(lambda x: x is not None and not x.startswith(TMP_MP_DIR), b['mountpoints'])) | ||
has_tmp_mp = len(list(filter(lambda x: x is not None and x.startswith(TMP_MP_DIR), b['mountpoints']))) > 0 |
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.
Small nit, could be len(mps)
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.
hmm, `len(mps) < len(b['mountpoints']), yeah this code is sh*t.
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.
Do you still want to rewrite this? :)
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.
Done!
return filesystems | ||
|
||
|
||
tmp_mountpoints = set() |
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.
Not looking too deep, we have this global state and the db
why do we not save the tmp_mountpoints in the DB?
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.
tmp_mountpoints is per process state, just so that we remember which ones this very process has added, so that they can all be removed when it exits. The db is global state for all processes.
58c912f
to
a20f003
Compare
a20f003
to
aa8de77
Compare
aa8de77
to
d6d4ab5
Compare
pkg/storaged/btrfs/btrfs-tool.py
Outdated
if b['fstype'] == "btrfs": | ||
uuid = b['uuid'] | ||
mps = list(filter(lambda x: x is not None and not x.startswith(TMP_MP_DIR), b['mountpoints'])) | ||
has_tmp_mp = len(list(filter(lambda x: x is not None and x.startswith(TMP_MP_DIR), b['mountpoints']))) > 0 |
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.
Do you still want to rewrite this? :)
|
||
|
||
@contextlib.contextmanager | ||
def context_chdir(path): |
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.
Note, this now exists in 3.11 https://docs.python.org/3/library/contextlib.html#contextlib.chdir
We can't use it yet, but adding a note might be nice.
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, I originally had contextlib.chdir
here but had to stop using it bc not all our test images have it. I add a comment.
pkg/storaged/btrfs/btrfs-tool.py
Outdated
os.makedirs(TMP_MP_DIR, mode=0o700, exist_ok=True) | ||
fd = os.open(path, os.O_RDWR | os.O_CREAT) | ||
fcntl.flock(fd, fcntl.LOCK_EX) | ||
data = os.read(fd, 100000) |
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.
Not the most robust :)
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.
Ah, forgot about that... thanks!
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.
Actually... this is pretty ok, no? Files will never have a short read, and the 100000 gives us a safety limit.
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 made a read_all function anyway.
@@ -1245,6 +1245,8 @@ export const TeardownMessage = (usage, expect_single_unmount) => { | |||
if (use.block) { | |||
const name = teardown_block_name(use); | |||
let location = use.location; | |||
if (location && location.startsWith("/var/lib/cockpit/btrfs/")) | |||
return; |
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.
Aha, so we hide it here! Feels a bit random :)
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 introducing a new mount option x-cockpit-tmp-mount
and then filtering based on that mount option.
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, this is a bit random and also not totally trivial. We do want Cockpit to teardown this mount point because otherwise we couldn't format a block device with btrfs on it. The btrfs-tool can tolerate that. We just don't want to show it in the UI.
As to how to identify this mountpoint... I think just the path is enough and we don't need to identify more machinery. Also, we don't really have access to run-time mount options in Cockpit, only to the ones in fstab. Except for btrfs filesystems from the findmnt poll....
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.
Right, having mount options available... if that is btrfs specific then a general x-cockpit-tmp-mount
which only works in btrfs case because of findmnt is not ideal. So we scratch the idea but might suggest adding a constant for it.
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.
but might suggest adding a constant for it.
Yes, good idea.
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.
Done.
path = TMP_MP_DIR + "/db" | ||
os.makedirs(TMP_MP_DIR, mode=0o700, exist_ok=True) | ||
fd = os.open(path, os.O_RDWR | os.O_CREAT) | ||
fcntl.flock(fd, fcntl.LOCK_EX) |
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.
If I open two browsers with the same user this can race, and flock() would throw so do we need a retry mechanism here?
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.
flock would block, no?
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.
Read the man page... https://manpages.debian.org/bookworm/manpages-dev/flock.2.en.html#DESCRIPTION
It's only non-blocking if you include LOCK_NB
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 want it to block. I am afraid I don't understand what failure scenario you are after here.
d6d4ab5
to
24b9224
Compare
We used to ignore all other mounts when the first one is for the root filesystem. But filesystems can have more than one mount and we should look at each one individually. This also prepares the code for more cases.
4e8fc61
to
b1b434b
Compare
The subvolume polling is now done by a long-running monitor program that only reports changes back to Cockpit. This reduces traffic in general and allows us to do additional clever things. The monitor program can optionally keep all btrfs filesystems mounted in a "secret" location, so that we can list the subvolumes of all btrfs filesystems, even those that are not officially mounted otherwise. This mode is only enabled in Anaconda mode: It is important there since filesystems are normally not mounted when preparing storage for Anaconda, and outside of Anaconda mode, keeping long standing secret mounts is deemed to invasive. The program can also carry out btrfs operations (such as creating subvolumes) while temporarily mounting it. This allows Cockpit to always create and delete subvolumes. The UI still only does it when monitoring subvolumes works as well, however.
b1b434b
to
4fba463
Compare
@@ -397,15 +290,91 @@ function btrfs_findmnt_poll() { | |||
}); | |||
} | |||
|
|||
function btrfs_update(data) { | |||
if (!client.uuids_btrfs_subvols) | |||
client.uuids_btrfs_subvols = { }; |
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 added line is not executed by any test.
if (!client.uuids_btrfs_subvols) | ||
client.uuids_btrfs_subvols = { }; | ||
if (!client.uuids_btrfs_usage) | ||
client.uuids_btrfs_usage = { }; |
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 added line is not executed by any test.
if (!client.uuids_btrfs_usage) | ||
client.uuids_btrfs_usage = { }; | ||
if (!client.uuids_btrfs_default_subvol) | ||
client.uuids_btrfs_default_subvol = { }; |
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 added line is not executed by any test.
if (data[uuid].error) { | ||
console.warn("Error polling btrfs", uuid, data[uuid].error); |
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.
These 2 added lines are not executed by any test.
channel.catch(err => { | ||
throw new Error(err.toString()); |
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.
These 2 added lines are not executed by any test.
if (usage.Blocking) { | ||
dialog_open({ | ||
Title: cockpit.format(_("$0 is in use"), name), | ||
Body: BlockingMessage(usage) |
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.
These 4 added lines are not executed by any test.
Title: cockpit.format(_("$0 is in use"), name), | ||
Body: BlockingMessage(usage) | ||
}); | ||
return; |
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 added line is not executed by any test.
// We don't complain about the rootfs, it's probably | ||
// configured somewhere else, like in the bootloader. | ||
if (m == "/") | ||
return true; |
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 added line is not executed by any test.
|
||
// This is the mount point used for monitoring btrfs filesystems. | ||
if (m.startsWith(BTRFS_TOOL_MOUNT_PATH)) | ||
return true; |
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 added line is not executed by any test.
This reduces the roundtrips and allows us to maintain "temporary" mount points for btrfs filesystems that are not otherwise mounted.
The process will be shutdown when the cockpit session is closed by cockpit-ws, so we get reliable cleanup.
Fixes #20855
Demo: https://youtu.be/aNsdXtHnUUM
Anaconda demo: https://youtu.be/EM-qqogOt6g