From 0d1f1e2d3c86443b0c6085723d087a241ce2b656 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 1 Apr 2022 15:10:41 +1300 Subject: [PATCH 1/8] very bare bones version of gaps.movable_trailing_partitions_and_gap --- subiquity/common/filesystem/gaps.py | 14 +++++ .../common/filesystem/tests/test_gaps.py | 61 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index 1e53c3ee..6380ee02 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -23,6 +23,7 @@ from subiquity.models.filesystem import ( Disk, LVM_CHUNK_SIZE, LVM_VolGroup, + Partition, Raid, ) @@ -165,3 +166,16 @@ def largest_gap_size(device): if largest is not None: return largest.size return 0 + + +def movable_trailing_partitions_and_gap(partition): + pg = parts_and_gaps(partition.device) + part_idx = pg.index(partition) + trailing_partitions = [] + for idx in range(part_idx + 1, len(pg)): + p = pg[idx] + if isinstance(p, Partition): + trailing_partitions.append(p) + else: + return (trailing_partitions, p) + return (trailing_partitions, None) diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index 1c028596..d143ba82 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -13,7 +13,9 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from functools import partial import unittest +from unittest import mock from subiquity.models.filesystem import ( PartitionAlignmentData, @@ -165,3 +167,62 @@ class TestDiskGaps(unittest.TestCase): gaps.Gap(d, 35, 15, True), gaps.Gap(d, 50, 50, False), ]) + + +class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): + + def use_alignment_data(self, alignment_data): + m = mock.patch('subiquity.common.filesystem.gaps.parts_and_gaps') + p = m.start() + self.addCleanup(m.stop) + p.side_effect = partial( + gaps.find_disk_gaps_v2, info=alignment_data) + + def test_no_next_gap(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=10, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 ]##### + m, d = make_model_and_disk(size=100) + p = make_partition(m, d, offset=10, size=80) + self.assertEqual( + gaps.movable_trailing_partitions_and_gap(p), ([], None)) + + def test_immediately_trailing_gap(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=10, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 ] [ p2 ] ##### + m, d = make_model_and_disk(size=100) + p1 = make_partition(m, d, offset=10, size=20) + p2 = make_partition(m, d, offset=50, size=10) + mtpg1 = gaps.movable_trailing_partitions_and_gap(p1) + self.assertEqual(([], gaps.Gap(device=d, offset=30, size=20)), mtpg1) + mtpg2 = gaps.movable_trailing_partitions_and_gap(p2) + self.assertEqual(([], gaps.Gap(device=d, offset=60, size=30)), mtpg2) + + def test_one_trailing_movable_partition_and_gap(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=10, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 ][ p2 ] ##### + m, d = make_model_and_disk(size=100) + p1 = make_partition(m, d, offset=10, size=40) + p2 = make_partition(m, d, offset=50, size=10) + mtpg = gaps.movable_trailing_partitions_and_gap(p1) + self.assertEqual(([p2], gaps.Gap(device=d, offset=60, size=30)), mtpg) + + def test_one_trailing_movable_partition_and_no_gap(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=10, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 ][ p2 ]##### + m, d = make_model_and_disk(size=100) + p1 = make_partition(m, d, offset=10, size=40) + p2 = make_partition(m, d, offset=50, size=40) + mtpg = gaps.movable_trailing_partitions_and_gap(p1) + self.assertEqual(([p2], None), mtpg) From f8e30f7125d7814bbd631213a3cde4291d6a438b Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 1 Apr 2022 15:33:55 +1300 Subject: [PATCH 2/8] teach gaps.movable_trailing_partitions_and_gap about logical/extended partitions --- subiquity/common/filesystem/gaps.py | 20 ++-- .../common/filesystem/tests/test_gaps.py | 97 +++++++++++++++++-- 2 files changed, 101 insertions(+), 16 deletions(-) diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index 6380ee02..b7b4dd44 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -169,13 +169,19 @@ def largest_gap_size(device): def movable_trailing_partitions_and_gap(partition): - pg = parts_and_gaps(partition.device) - part_idx = pg.index(partition) + pgs = parts_and_gaps(partition.device) + part_idx = pgs.index(partition) trailing_partitions = [] - for idx in range(part_idx + 1, len(pg)): - p = pg[idx] - if isinstance(p, Partition): - trailing_partitions.append(p) + in_extended = partition.flag == "logical" + for idx in range(part_idx + 1, len(pgs)): + pg = pgs[idx] + if isinstance(pg, Partition): + if in_extended and pg.flag != "logical": + break + trailing_partitions.append(pg) else: - return (trailing_partitions, p) + if pg.in_extended == in_extended: + return (trailing_partitions, pg) + else: + return (trailing_partitions, None) return (trailing_partitions, None) diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index d143ba82..f3b2efc0 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -187,7 +187,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): m, d = make_model_and_disk(size=100) p = make_partition(m, d, offset=10, size=80) self.assertEqual( - gaps.movable_trailing_partitions_and_gap(p), ([], None)) + ([], None), + gaps.movable_trailing_partitions_and_gap(p)) def test_immediately_trailing_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -198,10 +199,12 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): m, d = make_model_and_disk(size=100) p1 = make_partition(m, d, offset=10, size=20) p2 = make_partition(m, d, offset=50, size=10) - mtpg1 = gaps.movable_trailing_partitions_and_gap(p1) - self.assertEqual(([], gaps.Gap(device=d, offset=30, size=20)), mtpg1) - mtpg2 = gaps.movable_trailing_partitions_and_gap(p2) - self.assertEqual(([], gaps.Gap(device=d, offset=60, size=30)), mtpg2) + self.assertEqual( + ([], gaps.Gap(device=d, offset=30, size=20)), + gaps.movable_trailing_partitions_and_gap(p1)) + self.assertEqual( + ([], gaps.Gap(device=d, offset=60, size=30)), + gaps.movable_trailing_partitions_and_gap(p2)) def test_one_trailing_movable_partition_and_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -212,8 +215,9 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): m, d = make_model_and_disk(size=100) p1 = make_partition(m, d, offset=10, size=40) p2 = make_partition(m, d, offset=50, size=10) - mtpg = gaps.movable_trailing_partitions_and_gap(p1) - self.assertEqual(([p2], gaps.Gap(device=d, offset=60, size=30)), mtpg) + self.assertEqual( + ([p2], gaps.Gap(device=d, offset=60, size=30)), + gaps.movable_trailing_partitions_and_gap(p1)) def test_one_trailing_movable_partition_and_no_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -224,5 +228,80 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): m, d = make_model_and_disk(size=100) p1 = make_partition(m, d, offset=10, size=40) p2 = make_partition(m, d, offset=50, size=40) - mtpg = gaps.movable_trailing_partitions_and_gap(p1) - self.assertEqual(([p2], None), mtpg) + self.assertEqual( + ([p2], None), + gaps.movable_trailing_partitions_and_gap(p1)) + + def test_full_extended_partition_then_gap(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=1, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10, ebr_space=2)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 (extended) ] ##### + # ######[ p5 (logical) ] ##### + m, d = make_model_and_disk(size=100) + make_partition(m, d, offset=10, size=40, flag='extended') + p5 = make_partition(m, d, offset=12, size=38, flag='logical') + self.assertEqual( + ([], None), + gaps.movable_trailing_partitions_and_gap(p5)) + + def test_full_extended_partition_then_part(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=1, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10, ebr_space=2)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 (extended) ][ p2 ]##### + # ######[ p5 (logical) ] ##### + m, d = make_model_and_disk(size=100) + make_partition(m, d, offset=10, size=40, flag='extended') + make_partition(m, d, offset=50, size=40) + p5 = make_partition(m, d, offset=12, size=38, flag='logical') + self.assertEqual( + ([], None), + gaps.movable_trailing_partitions_and_gap(p5)) + + def test_gap_in_extended_partition(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=1, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10, ebr_space=2)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 (extended) ] ##### + # ######[ p5 (logical)] ##### + m, d = make_model_and_disk(size=100) + make_partition(m, d, offset=10, size=40, flag='extended') + p5 = make_partition(m, d, offset=12, size=30, flag='logical') + self.assertEqual( + ([], gaps.Gap(device=d, offset=44, size=6, in_extended=True)), + gaps.movable_trailing_partitions_and_gap(p5)) + + def test_trailing_logical_partition_then_gap(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=1, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10, ebr_space=2)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 (extended) ]##### + # ######[ p5 (logical)] [ p6 (logical)] ##### + m, d = make_model_and_disk(size=100) + make_partition(m, d, offset=10, size=80, flag='extended') + p5 = make_partition(m, d, offset=12, size=30, flag='logical') + p6 = make_partition(m, d, offset=44, size=30, flag='logical') + self.assertEqual( + ([p6], gaps.Gap(device=d, offset=76, size=14, in_extended=True)), + gaps.movable_trailing_partitions_and_gap(p5)) + + def test_trailing_logical_partition_then_no_gap(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=1, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10, ebr_space=2)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 (extended) ]##### + # ######[ p5 (logical)] [ p6 (logical) ] ##### + m, d = make_model_and_disk(size=100) + make_partition(m, d, offset=10, size=80, flag='extended') + p5 = make_partition(m, d, offset=12, size=30, flag='logical') + p6 = make_partition(m, d, offset=44, size=44, flag='logical') + mtpg = gaps.movable_trailing_partitions_and_gap(p5) + self.assertEqual( + ([p6], None), + mtpg) From ebeda433ada42d18a9ddea77612c20b811c43ee3 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 1 Apr 2022 15:44:36 +1300 Subject: [PATCH 3/8] teach gaps.movable_trailing_partitions_and_gap about preserved partitions --- subiquity/common/filesystem/gaps.py | 2 ++ subiquity/common/filesystem/tests/test_gaps.py | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index b7b4dd44..9f9ae4c9 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -176,6 +176,8 @@ def movable_trailing_partitions_and_gap(partition): for idx in range(part_idx + 1, len(pgs)): pg = pgs[idx] if isinstance(pg, Partition): + if pg.preserve: + break if in_extended and pg.flag != "logical": break trailing_partitions.append(pg) diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index f3b2efc0..60110173 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -305,3 +305,16 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): self.assertEqual( ([p6], None), mtpg) + + def test_trailing_preserved_partition(self): + self.use_alignment_data(PartitionAlignmentData( + part_align=10, min_gap_size=1, min_start_offset=10, + min_end_offset=10, primary_part_limit=10)) + # 0----10---20---30---40---50---60---70---80---90---100 + # #####[ p1 ][ p2 p ] ##### + m, d = make_model_and_disk(size=100) + p1 = make_partition(m, d, offset=10, size=40) + make_partition(m, d, offset=50, size=20, preserve=True) + self.assertEqual( + ([], None), + gaps.movable_trailing_partitions_and_gap(p1)) From f4cd20446abc259784f94199c67b8be52a142776 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 1 Apr 2022 15:55:00 +1300 Subject: [PATCH 4/8] change api to return size, not gap --- subiquity/common/filesystem/gaps.py | 8 ++-- .../common/filesystem/tests/test_gaps.py | 47 +++++++++---------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index 9f9ae4c9..1d93717b 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -168,7 +168,7 @@ def largest_gap_size(device): return 0 -def movable_trailing_partitions_and_gap(partition): +def movable_trailing_partitions_and_gap_size(partition): pgs = parts_and_gaps(partition.device) part_idx = pgs.index(partition) trailing_partitions = [] @@ -183,7 +183,7 @@ def movable_trailing_partitions_and_gap(partition): trailing_partitions.append(pg) else: if pg.in_extended == in_extended: - return (trailing_partitions, pg) + return (trailing_partitions, pg.size) else: - return (trailing_partitions, None) - return (trailing_partitions, None) + return (trailing_partitions, 0) + return (trailing_partitions, 0) diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index 60110173..4b5d5f9d 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -169,7 +169,7 @@ class TestDiskGaps(unittest.TestCase): ]) -class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): +class TestMovableTrailingPartitionsAndGapSize(unittest.TestCase): def use_alignment_data(self, alignment_data): m = mock.patch('subiquity.common.filesystem.gaps.parts_and_gaps') @@ -187,8 +187,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): m, d = make_model_and_disk(size=100) p = make_partition(m, d, offset=10, size=80) self.assertEqual( - ([], None), - gaps.movable_trailing_partitions_and_gap(p)) + ([], 0), + gaps.movable_trailing_partitions_and_gap_size(p)) def test_immediately_trailing_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -200,11 +200,11 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): p1 = make_partition(m, d, offset=10, size=20) p2 = make_partition(m, d, offset=50, size=10) self.assertEqual( - ([], gaps.Gap(device=d, offset=30, size=20)), - gaps.movable_trailing_partitions_and_gap(p1)) + ([], 20), + gaps.movable_trailing_partitions_and_gap_size(p1)) self.assertEqual( - ([], gaps.Gap(device=d, offset=60, size=30)), - gaps.movable_trailing_partitions_and_gap(p2)) + ([], 30), + gaps.movable_trailing_partitions_and_gap_size(p2)) def test_one_trailing_movable_partition_and_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -216,8 +216,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): p1 = make_partition(m, d, offset=10, size=40) p2 = make_partition(m, d, offset=50, size=10) self.assertEqual( - ([p2], gaps.Gap(device=d, offset=60, size=30)), - gaps.movable_trailing_partitions_and_gap(p1)) + ([p2], 30), + gaps.movable_trailing_partitions_and_gap_size(p1)) def test_one_trailing_movable_partition_and_no_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -229,8 +229,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): p1 = make_partition(m, d, offset=10, size=40) p2 = make_partition(m, d, offset=50, size=40) self.assertEqual( - ([p2], None), - gaps.movable_trailing_partitions_and_gap(p1)) + ([p2], 0), + gaps.movable_trailing_partitions_and_gap_size(p1)) def test_full_extended_partition_then_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -243,8 +243,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): make_partition(m, d, offset=10, size=40, flag='extended') p5 = make_partition(m, d, offset=12, size=38, flag='logical') self.assertEqual( - ([], None), - gaps.movable_trailing_partitions_and_gap(p5)) + ([], 0), + gaps.movable_trailing_partitions_and_gap_size(p5)) def test_full_extended_partition_then_part(self): self.use_alignment_data(PartitionAlignmentData( @@ -258,8 +258,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): make_partition(m, d, offset=50, size=40) p5 = make_partition(m, d, offset=12, size=38, flag='logical') self.assertEqual( - ([], None), - gaps.movable_trailing_partitions_and_gap(p5)) + ([], 0), + gaps.movable_trailing_partitions_and_gap_size(p5)) def test_gap_in_extended_partition(self): self.use_alignment_data(PartitionAlignmentData( @@ -272,8 +272,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): make_partition(m, d, offset=10, size=40, flag='extended') p5 = make_partition(m, d, offset=12, size=30, flag='logical') self.assertEqual( - ([], gaps.Gap(device=d, offset=44, size=6, in_extended=True)), - gaps.movable_trailing_partitions_and_gap(p5)) + ([], 6), + gaps.movable_trailing_partitions_and_gap_size(p5)) def test_trailing_logical_partition_then_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -287,8 +287,8 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): p5 = make_partition(m, d, offset=12, size=30, flag='logical') p6 = make_partition(m, d, offset=44, size=30, flag='logical') self.assertEqual( - ([p6], gaps.Gap(device=d, offset=76, size=14, in_extended=True)), - gaps.movable_trailing_partitions_and_gap(p5)) + ([p6], 14), + gaps.movable_trailing_partitions_and_gap_size(p5)) def test_trailing_logical_partition_then_no_gap(self): self.use_alignment_data(PartitionAlignmentData( @@ -301,10 +301,9 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): make_partition(m, d, offset=10, size=80, flag='extended') p5 = make_partition(m, d, offset=12, size=30, flag='logical') p6 = make_partition(m, d, offset=44, size=44, flag='logical') - mtpg = gaps.movable_trailing_partitions_and_gap(p5) self.assertEqual( - ([p6], None), - mtpg) + ([p6], 0), + gaps.movable_trailing_partitions_and_gap_size(p5)) def test_trailing_preserved_partition(self): self.use_alignment_data(PartitionAlignmentData( @@ -316,5 +315,5 @@ class TestMovableTrailingPartitionsAndGaps(unittest.TestCase): p1 = make_partition(m, d, offset=10, size=40) make_partition(m, d, offset=50, size=20, preserve=True) self.assertEqual( - ([], None), - gaps.movable_trailing_partitions_and_gap(p1)) + ([], 0), + gaps.movable_trailing_partitions_and_gap_size(p1)) From f24f50879623b7f54713551347ae621c7d4cd9a2 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 1 Apr 2022 15:56:43 +1300 Subject: [PATCH 5/8] use movable_trailing_partitions_and_gap_size to compute max_size in PartitionStretchy --- subiquity/ui/views/filesystem/partition.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subiquity/ui/views/filesystem/partition.py b/subiquity/ui/views/filesystem/partition.py index 6a05a4f5..cc43ef51 100644 --- a/subiquity/ui/views/filesystem/partition.py +++ b/subiquity/ui/views/filesystem/partition.py @@ -373,7 +373,6 @@ class PartitionStretchy(Stretchy): self.model = parent.model self.controller = parent.controller self.parent = parent - max_size = gaps.largest_gap_size(disk) if partition is None and gap is None: raise Exception('bad PartitionStretchy - needs partition or gap') @@ -395,7 +394,8 @@ class PartitionStretchy(Stretchy): else: label = _("Save") initial['size'] = humanize_size(self.partition.size) - max_size += self.partition.size + max_size = partition.size + \ + gaps.movable_trailing_partitions_and_gap_size(partition)[1] if not boot.is_esp(partition): initial.update(initial_data_for_fs(self.partition.fs())) From ce4316fc23b38d2760762b5af9b886b1f1e6ea5e Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 1 Apr 2022 16:04:26 +1300 Subject: [PATCH 6/8] move partitions around on deletion and resize --- subiquity/common/filesystem/manipulator.py | 10 ++++++++-- subiquity/models/filesystem.py | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/subiquity/common/filesystem/manipulator.py b/subiquity/common/filesystem/manipulator.py index 9b88ea39..aabbada1 100644 --- a/subiquity/common/filesystem/manipulator.py +++ b/subiquity/common/filesystem/manipulator.py @@ -168,9 +168,15 @@ class FilesystemManipulator: if partition is not None: if 'size' in spec: - partition.size = align_up(spec['size']) - if gaps.largest_gap_size(disk) < 0: + trailing, gap_size = \ + gaps.movable_trailing_partitions_and_gap_size(partition) + new_size = align_up(spec['size']) + size_change = new_size - partition.size + if size_change > gap_size: raise Exception("partition size too large") + partition.size = new_size + for part in trailing: + part.offset += size_change self.delete_filesystem(partition.fs()) self.create_filesystem(partition, spec) return diff --git a/subiquity/models/filesystem.py b/subiquity/models/filesystem.py index 4ac29387..6de612b1 100644 --- a/subiquity/models/filesystem.py +++ b/subiquity/models/filesystem.py @@ -1404,6 +1404,11 @@ class FilesystemModel(object): def remove_partition(self, part): if part._fs or part._constructed_device: raise Exception("can only remove empty partition") + from subiquity.common.filesystem.gaps import ( + movable_trailing_partitions_and_gap_size, + ) + for p2 in movable_trailing_partitions_and_gap_size(part)[0]: + p2.offset -= part.size self._remove(part) if len(part.device._partitions) == 0: part.device.ptable = None From 134ce89159e976d1dd3974befb79599271536ac6 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 11 Apr 2022 14:39:37 +1200 Subject: [PATCH 7/8] remove unnecessary range() --- subiquity/common/filesystem/gaps.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index 1d93717b..d212ae65 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -173,8 +173,7 @@ def movable_trailing_partitions_and_gap_size(partition): part_idx = pgs.index(partition) trailing_partitions = [] in_extended = partition.flag == "logical" - for idx in range(part_idx + 1, len(pgs)): - pg = pgs[idx] + for pg in pgs[part_idx + 1:]: if isinstance(pg, Partition): if pg.preserve: break From 63f32caac7a9dc07ce3e17bdfe848788ca931f7b Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 11 Apr 2022 14:48:29 +1200 Subject: [PATCH 8/8] fix test/diagram discrepancy (although maybe this test should just go) --- subiquity/common/filesystem/tests/test_gaps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index 4b5d5f9d..2b3c0264 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -195,15 +195,15 @@ class TestMovableTrailingPartitionsAndGapSize(unittest.TestCase): part_align=10, min_gap_size=1, min_start_offset=10, min_end_offset=10, primary_part_limit=10)) # 0----10---20---30---40---50---60---70---80---90---100 - # #####[ p1 ] [ p2 ] ##### + # #####[ p1 ] [ p2 ] ##### m, d = make_model_and_disk(size=100) p1 = make_partition(m, d, offset=10, size=20) - p2 = make_partition(m, d, offset=50, size=10) + p2 = make_partition(m, d, offset=50, size=20) self.assertEqual( ([], 20), gaps.movable_trailing_partitions_and_gap_size(p1)) self.assertEqual( - ([], 30), + ([], 20), gaps.movable_trailing_partitions_and_gap_size(p2)) def test_one_trailing_movable_partition_and_gap(self):