Merge pull request #1250 from mwhudson/offset-pedantry
keep partition offsets up to date as they are deleted and resized
This commit is contained in:
commit
c384d5b617
|
@ -23,6 +23,7 @@ from subiquity.models.filesystem import (
|
|||
Disk,
|
||||
LVM_CHUNK_SIZE,
|
||||
LVM_VolGroup,
|
||||
Partition,
|
||||
Raid,
|
||||
)
|
||||
|
||||
|
@ -165,3 +166,23 @@ def largest_gap_size(device):
|
|||
if largest is not None:
|
||||
return largest.size
|
||||
return 0
|
||||
|
||||
|
||||
def movable_trailing_partitions_and_gap_size(partition):
|
||||
pgs = parts_and_gaps(partition.device)
|
||||
part_idx = pgs.index(partition)
|
||||
trailing_partitions = []
|
||||
in_extended = partition.flag == "logical"
|
||||
for pg in pgs[part_idx + 1:]:
|
||||
if isinstance(pg, Partition):
|
||||
if pg.preserve:
|
||||
break
|
||||
if in_extended and pg.flag != "logical":
|
||||
break
|
||||
trailing_partitions.append(pg)
|
||||
else:
|
||||
if pg.in_extended == in_extended:
|
||||
return (trailing_partitions, pg.size)
|
||||
else:
|
||||
return (trailing_partitions, 0)
|
||||
return (trailing_partitions, 0)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from functools import partial
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from subiquity.models.filesystem import (
|
||||
PartitionAlignmentData,
|
||||
|
@ -165,3 +167,153 @@ class TestDiskGaps(unittest.TestCase):
|
|||
gaps.Gap(d, 35, 15, True),
|
||||
gaps.Gap(d, 50, 50, False),
|
||||
])
|
||||
|
||||
|
||||
class TestMovableTrailingPartitionsAndGapSize(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(
|
||||
([], 0),
|
||||
gaps.movable_trailing_partitions_and_gap_size(p))
|
||||
|
||||
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=20)
|
||||
self.assertEqual(
|
||||
([], 20),
|
||||
gaps.movable_trailing_partitions_and_gap_size(p1))
|
||||
self.assertEqual(
|
||||
([], 20),
|
||||
gaps.movable_trailing_partitions_and_gap_size(p2))
|
||||
|
||||
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)
|
||||
self.assertEqual(
|
||||
([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(
|
||||
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)
|
||||
self.assertEqual(
|
||||
([p2], 0),
|
||||
gaps.movable_trailing_partitions_and_gap_size(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(
|
||||
([], 0),
|
||||
gaps.movable_trailing_partitions_and_gap_size(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(
|
||||
([], 0),
|
||||
gaps.movable_trailing_partitions_and_gap_size(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(
|
||||
([], 6),
|
||||
gaps.movable_trailing_partitions_and_gap_size(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], 14),
|
||||
gaps.movable_trailing_partitions_and_gap_size(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')
|
||||
self.assertEqual(
|
||||
([p6], 0),
|
||||
gaps.movable_trailing_partitions_and_gap_size(p5))
|
||||
|
||||
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(
|
||||
([], 0),
|
||||
gaps.movable_trailing_partitions_and_gap_size(p1))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()))
|
||||
|
|
Loading…
Reference in New Issue