Skip to content

Commit

Permalink
filesystem: store the actual size in bytes alongside the human readab…
Browse files Browse the repository at this point in the history
…le size

Currently, the partition form stores the size as a human readable value.
(e.g., 123456K, 1.1G, 1.876G, 100G). When we exit the size field (i.e., upon
losing focus), we convert the value to a number of bytes and then align
it up to the nearest MiB (or whatever the alignment requirement is).

Unfortunately, after computing the aligned value, we turn it back into a
human-readable string and store it as is. It is not okay because the
conversion does not ensure that the alignment requirement is still
honored.

For instance, if the user types in 1.1G, we do the following:

 * convert it to a number of bytes -> 1181116006.4 (surprise, it is not
   even an integer).

 * round it up to the nearest MiB -> 1181745152 (this is the correct
   value)

 * transform it into a human readable string and store it as is -> 1.1G
   - which actually corresponds to the original value.

This leads to an exception later when creating the partition:

    File "subiquity/models/filesystem.py", line 1841, in add_partition
      raise Exception(
   Exception: ('size %s or offset %s not aligned to %s', 1181116006, 1048576, 1048576)

Fixed by storing the actual size as a number of bytes - alongside the
human readable size.

Signed-off-by: Olivier Gayot <[email protected]>
  • Loading branch information
ogayot committed Aug 9, 2023
1 parent b0f1030 commit cda6c54
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 2 deletions.
6 changes: 6 additions & 0 deletions subiquity/ui/views/filesystem/partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"""
import logging
import re
from typing import Optional

from urwid import Text, connect_signal

Expand Down Expand Up @@ -89,6 +90,7 @@ def _make_widget(self, form):
class SizeWidget(StringEditor):
def __init__(self, form):
self.form = form
self.accurate_value: Optional[int] = None
super().__init__()

def lost_focus(self):
Expand All @@ -114,6 +116,9 @@ def lost_focus(self):
),
)
)
# This will invoke self.form.clean_size() and it is expected that
# size_str (and therefore self.value) are propertly aligned.
self.accurate_value = self.form.size
else:
aligned_sz = align_up(sz, self.form.alignment)
aligned_sz_str = humanize_size(aligned_sz)
Expand All @@ -125,6 +130,7 @@ def lost_focus(self):
_("Rounded size up to {size}").format(size=aligned_sz_str),
)
)
self.accurate_value = aligned_sz


class SizeField(FormField):
Expand Down
29 changes: 28 additions & 1 deletion subiquity/ui/views/filesystem/tests/test_partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from subiquity.client.controllers.filesystem import FilesystemController
from subiquity.common.filesystem import gaps
from subiquity.models.filesystem import dehumanize_size
from subiquity.models.filesystem import MiB, dehumanize_size
from subiquity.models.tests.test_filesystem import make_model_and_disk
from subiquity.ui.views.filesystem.partition import (
FormatEntireStretchy,
Expand Down Expand Up @@ -58,6 +58,7 @@ def test_create_partition(self):
gap = gaps.Gap(device=disk, offset=1 << 20, size=99 << 30)
view, stretchy = make_partition_view(model, disk, gap=gap)
view_helpers.enter_data(stretchy.form, valid_data)
stretchy.form.size.widget.lost_focus()
view_helpers.click(stretchy.form.done_btn.base_widget)
valid_data["mount"] = "/"
valid_data["size"] = dehumanize_size(valid_data["size"])
Expand All @@ -77,6 +78,7 @@ def test_edit_partition(self):
view, stretchy = make_partition_view(model, disk, partition=partition)
self.assertTrue(stretchy.form.done_btn.enabled)
view_helpers.enter_data(stretchy.form, form_data)
stretchy.form.size.widget.lost_focus()
view_helpers.click(stretchy.form.done_btn.base_widget)
expected_data = {
"size": dehumanize_size(form_data["size"]),
Expand Down Expand Up @@ -111,6 +113,7 @@ def test_edit_existing_partition(self):
self.assertFalse(stretchy.form.size.enabled)
self.assertTrue(stretchy.form.done_btn.enabled)
view_helpers.enter_data(stretchy.form, form_data)
stretchy.form.size.widget.lost_focus()
view_helpers.click(stretchy.form.done_btn.base_widget)
expected_data = {
"fstype": "xfs",
Expand Down Expand Up @@ -177,6 +180,7 @@ def test_edit_boot_partition(self):
self.assertEqual(stretchy.form.mount.value, "/boot/efi")

view_helpers.enter_data(stretchy.form, form_data)
stretchy.form.size.widget.lost_focus()
view_helpers.click(stretchy.form.done_btn.base_widget)
expected_data = {
"size": dehumanize_size(form_data["size"]),
Expand Down Expand Up @@ -246,3 +250,26 @@ def test_format_entire_unusual_filesystem(self):
model._orig_config = model._render_actions()
view, stretchy = make_format_entire_view(model, disk)
self.assertEqual(stretchy.form.fstype.value, None)

def test_create_partition_unaligned_size(self):
# In LP: #2013201, the user would type in 1.1G and the partition
# created would not be aligned to a MiB boundary.
unaligned_data = {
"size": "1.1G", # Corresponds to 1181116006.4 bytes (not an int)
"fstype": "ext4",
}
valid_data = {
"mount": "/",
"size": 1127 * MiB, # ~1.10058 GiB
"use_swap": False,
"fstype": "ext4",
}
model, disk = make_model_and_disk()
gap = gaps.Gap(device=disk, offset=1 << 20, size=99 << 30)
view, stretchy = make_partition_view(model, disk, gap=gap)
view_helpers.enter_data(stretchy.form, unaligned_data)
stretchy.form.size.widget.lost_focus()
view_helpers.click(stretchy.form.done_btn.base_widget)
view.controller.partition_disk_handler.assert_called_once_with(
stretchy.disk, valid_data, partition=None, gap=gap
)
6 changes: 5 additions & 1 deletion subiquitycore/ui/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,11 @@ def as_data(self):
data = {}
for field in self._fields:
if field.enabled:
data[field.field.name] = field.value
accurate_value = getattr(field.widget, "accurate_value", None)
if accurate_value is not None:
data[field.field.name] = accurate_value
else:
data[field.field.name] = field.value
return data


Expand Down

0 comments on commit cda6c54

Please sign in to comment.