mirror of
https://github.com/flatcar/scripts.git
synced 2025-09-23 14:41:31 +02:00
fix(disk_util): Rework config parsing.
Merge GetPartitionTable and partition alignment from WritePartitionTable into LoadPartitionConfig so that all this config manipulation code is in one place and inheritance from the 'base' layout is more predictable.
This commit is contained in:
parent
0f4b52134a
commit
ab23353448
@ -36,6 +36,9 @@ def LoadPartitionConfig(options):
|
|||||||
valid_layout_keys = set((
|
valid_layout_keys = set((
|
||||||
'_comment', 'type', 'num', 'label', 'blocks', 'block_size', 'fs_blocks',
|
'_comment', 'type', 'num', 'label', 'blocks', 'block_size', 'fs_blocks',
|
||||||
'fs_block_size', 'features', 'uuid', 'alignment'))
|
'fs_block_size', 'features', 'uuid', 'alignment'))
|
||||||
|
integer_layout_keys = set((
|
||||||
|
'blocks', 'block_size', 'fs_blocks', 'fs_block_size', 'alignment'))
|
||||||
|
required_layout_keys = set(('type', 'num', 'label', 'blocks'))
|
||||||
|
|
||||||
filename = options.disk_layout_file
|
filename = options.disk_layout_file
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
@ -43,82 +46,116 @@ def LoadPartitionConfig(options):
|
|||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
config = json.load(f)
|
config = json.load(f)
|
||||||
|
|
||||||
|
unknown_keys = set(config.keys()) - valid_keys
|
||||||
|
if unknown_keys:
|
||||||
|
raise InvalidLayout('Unknown items: %s' % ' '.join(unknown_keys))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
metadata = config['metadata']
|
metadata = config['metadata']
|
||||||
|
base = config['layouts']['base']
|
||||||
for key in ('alignment', 'block_size', 'fs_block_size'):
|
for key in ('alignment', 'block_size', 'fs_block_size'):
|
||||||
metadata[key] = int(metadata[key])
|
metadata[key] = int(metadata[key])
|
||||||
|
|
||||||
# Sometimes qemu-img expects disks sizes aligned to 64k
|
|
||||||
align_bytes = metadata['alignment'] * metadata['block_size']
|
|
||||||
if align_bytes < 65536 or align_bytes % 65536 != 0:
|
|
||||||
raise InvalidLayout('Invalid alignment, 64KB or better required')
|
|
||||||
|
|
||||||
unknown_keys = set(config.keys()) - valid_keys
|
|
||||||
if unknown_keys:
|
|
||||||
raise InvalidLayout('Unknown items: %r' % unknown_keys)
|
|
||||||
|
|
||||||
if len(config['layouts']) <= 0:
|
|
||||||
raise InvalidLayout('Missing "layouts" entries')
|
|
||||||
|
|
||||||
for layout_name, layout in config['layouts'].iteritems():
|
|
||||||
for part_num, part in layout.iteritems():
|
|
||||||
unknown_keys = set(part.keys()) - valid_layout_keys
|
|
||||||
if unknown_keys:
|
|
||||||
raise InvalidLayout('Unknown items in layout %s: %r' %
|
|
||||||
(layout_name, unknown_keys))
|
|
||||||
|
|
||||||
part['num'] = int(part_num)
|
|
||||||
if part['type'] == 'blank':
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not part.get('label', None):
|
|
||||||
raise InvalidLayout('Layout "%s" missing "label" in partition %s' %
|
|
||||||
(layout_name, part_num))
|
|
||||||
|
|
||||||
part['alignment'] = int(part.get('alignment', metadata['alignment']))
|
|
||||||
part['blocks'] = int(part['blocks'])
|
|
||||||
part['bytes'] = part['blocks'] * metadata['block_size']
|
|
||||||
|
|
||||||
if 'fs_blocks' in part:
|
|
||||||
part['fs_blocks'] = int(part['fs_blocks'])
|
|
||||||
part['fs_bytes'] = part['fs_blocks'] * metadata['fs_block_size']
|
|
||||||
|
|
||||||
if part['fs_bytes'] > part['bytes']:
|
|
||||||
raise InvalidLayout(
|
|
||||||
'Filesystem may not be larger than partition: %s %s: %d > %d' %
|
|
||||||
(layout_name, part['label'], part['fs_bytes'], part['bytes']))
|
|
||||||
|
|
||||||
if 'uuid' in part:
|
|
||||||
try:
|
|
||||||
# double check the string formatting
|
|
||||||
part['uuid'] = str(uuid.UUID(part['uuid']))
|
|
||||||
except ValueError as e:
|
|
||||||
raise InvalidLayout('Invalid uuid %r: %s' % (part['uuid'], e))
|
|
||||||
else:
|
|
||||||
part['uuid'] = str(uuid.uuid4())
|
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise InvalidLayout('Layout is missing required entries: %s' % e)
|
raise InvalidLayout('Metadata is missing required entries: %s' % e)
|
||||||
|
|
||||||
return config
|
# Sometimes qemu-img expects disks sizes aligned to 64k
|
||||||
|
align_bytes = metadata['alignment'] * metadata['block_size']
|
||||||
|
if align_bytes < 65536 or align_bytes % 65536 != 0:
|
||||||
|
raise InvalidLayout('Invalid alignment, 64KB or better required')
|
||||||
|
|
||||||
|
|
||||||
def GetPartitionTable(options, config):
|
def VerifyLayout(layout_name, layout, base=None):
|
||||||
"""Generates requested image_type layout from a layout configuration.
|
for part_num, part in layout.iteritems():
|
||||||
This loads the base table and then overlays the requested layout over
|
part['num'] = int(part_num)
|
||||||
the base layout.
|
part_keys = set(part.iterkeys())
|
||||||
|
unknown_keys = part_keys - valid_layout_keys
|
||||||
|
if unknown_keys:
|
||||||
|
raise InvalidLayout('Unknown items in partition %s %s: %r' %
|
||||||
|
(layout_name, part_num, ' '.join(unknown_keys)))
|
||||||
|
|
||||||
Args:
|
for int_key in integer_layout_keys.intersection(part_keys):
|
||||||
options: Flags passed to the script
|
part[int_key] = int(part[int_key])
|
||||||
config: Partition configuration file object
|
|
||||||
Returns:
|
|
||||||
Object representing a selected partition table
|
|
||||||
"""
|
|
||||||
|
|
||||||
partitions = config['layouts']['base'].copy()
|
if part.get('type', None) == 'blank':
|
||||||
for part_num, part in config['layouts'][options.disk_layout].iteritems():
|
continue
|
||||||
partitions[part_num].update(part)
|
|
||||||
|
|
||||||
return partitions
|
if base:
|
||||||
|
part_keys.update(base.iterkeys())
|
||||||
|
|
||||||
|
missing_keys = required_layout_keys - part_keys
|
||||||
|
if missing_keys:
|
||||||
|
raise InvalidLayout('Missing items in partition %s %s: %s' %
|
||||||
|
(layout_name, part_num, ' '.join(missing_keys)))
|
||||||
|
|
||||||
|
if 'uuid' in part:
|
||||||
|
try:
|
||||||
|
# double check the string formatting
|
||||||
|
part['uuid'] = str(uuid.UUID(part['uuid']))
|
||||||
|
except ValueError as e:
|
||||||
|
raise InvalidLayout('Invalid uuid %r: %s' % (part['uuid'], e))
|
||||||
|
|
||||||
|
|
||||||
|
def Align(count, alignment):
|
||||||
|
offset = count % alignment
|
||||||
|
if offset:
|
||||||
|
count += alignment - offset
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def FillExtraValues(layout_name, layout, base=None):
|
||||||
|
# Reserved size for first GPT
|
||||||
|
disk_block_count = GPT_RESERVED_SECTORS
|
||||||
|
|
||||||
|
# Fill in default values from base,
|
||||||
|
# dict doesn't have a update()+setdefault() method so this looks tedious
|
||||||
|
if base:
|
||||||
|
for part_num, base_part in base.iteritems():
|
||||||
|
part = layout.setdefault(part_num, {})
|
||||||
|
for base_key, base_value in base_part.iteritems():
|
||||||
|
part.setdefault(base_key, base_value)
|
||||||
|
|
||||||
|
for part_num, part in layout.iteritems():
|
||||||
|
if part['type'] == 'blank':
|
||||||
|
continue
|
||||||
|
|
||||||
|
part.setdefault('alignment', metadata['alignment'])
|
||||||
|
part['bytes'] = part['blocks'] * metadata['block_size']
|
||||||
|
part.setdefault('fs_block_size', metadata['fs_block_size'])
|
||||||
|
part.setdefault('fs_blocks', part['bytes'] // part['fs_block_size'])
|
||||||
|
part['fs_bytes'] = part['fs_blocks'] * part['fs_block_size']
|
||||||
|
|
||||||
|
if part['fs_bytes'] > part['bytes']:
|
||||||
|
raise InvalidLayout(
|
||||||
|
'Filesystem may not be larger than partition: %s %s: %d > %d' %
|
||||||
|
(layout_name, part_num, part['fs_bytes'], part['bytes']))
|
||||||
|
|
||||||
|
disk_block_count = Align(disk_block_count, part['alignment'])
|
||||||
|
part['first_block'] = disk_block_count
|
||||||
|
disk_block_count += part['blocks']
|
||||||
|
|
||||||
|
part.setdefault('uuid', str(uuid.uuid4()))
|
||||||
|
|
||||||
|
# Reserved size for second GPT plus align disk image size
|
||||||
|
disk_block_count += GPT_RESERVED_SECTORS
|
||||||
|
disk_block_count = Align(disk_block_count, metadata['alignment'])
|
||||||
|
|
||||||
|
# If this is the requested layout stash the disk size into the global
|
||||||
|
# metadata. Kinda odd but the best place I've got with this data structure.
|
||||||
|
if layout_name == options.disk_layout:
|
||||||
|
metadata['blocks'] = disk_block_count
|
||||||
|
|
||||||
|
|
||||||
|
# Verify 'base' before other layouts because it is inherited by the others
|
||||||
|
# Fill in extra/default values in base last so they aren't inherited
|
||||||
|
VerifyLayout('base', base)
|
||||||
|
for layout_name, layout in config['layouts'].iteritems():
|
||||||
|
if layout_name == 'base':
|
||||||
|
continue
|
||||||
|
VerifyLayout(layout_name, layout, base)
|
||||||
|
FillExtraValues(layout_name, layout, base)
|
||||||
|
FillExtraValues('base', base)
|
||||||
|
|
||||||
|
return config, config['layouts'][options.disk_layout]
|
||||||
|
|
||||||
|
|
||||||
def GetPartitionTableFromConfig(options):
|
def GetPartitionTableFromConfig(options):
|
||||||
@ -130,9 +167,7 @@ def GetPartitionTableFromConfig(options):
|
|||||||
A list defining all known partitions.
|
A list defining all known partitions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config = LoadPartitionConfig(options)
|
config, partitions = LoadPartitionConfig(options)
|
||||||
partitions = GetPartitionTable(options, config)
|
|
||||||
|
|
||||||
return partitions
|
return partitions
|
||||||
|
|
||||||
|
|
||||||
@ -146,33 +181,15 @@ def WritePartitionTable(options):
|
|||||||
def Cgpt(*args):
|
def Cgpt(*args):
|
||||||
subprocess.check_call(['cgpt'] + [str(a) for a in args])
|
subprocess.check_call(['cgpt'] + [str(a) for a in args])
|
||||||
|
|
||||||
def Align(count, alignment):
|
config, partitions = LoadPartitionConfig(options)
|
||||||
offset = count % alignment
|
|
||||||
if offset:
|
|
||||||
count += alignment - offset
|
|
||||||
return count
|
|
||||||
|
|
||||||
config = LoadPartitionConfig(options)
|
Cgpt('create', '-c', '-s', config['metadata']['blocks'], options.disk_image)
|
||||||
partitions = GetPartitionTable(options, config)
|
|
||||||
disk_block_count = GPT_RESERVED_SECTORS
|
|
||||||
|
|
||||||
for partition in partitions:
|
|
||||||
disk_block_count = Align(disk_block_count, partition['alignment'])
|
|
||||||
disk_block_count += partition['blocks']
|
|
||||||
|
|
||||||
disk_block_count += GPT_RESERVED_SECTORS
|
|
||||||
# Sometimes qemu-img expects disks sizes aligned to 64k
|
|
||||||
disk_block_count = Align(disk_block_count, config['metadata']['alignment'])
|
|
||||||
|
|
||||||
Cgpt('create', '-c', '-s', disk_block_count, options.disk_image)
|
|
||||||
|
|
||||||
sector = GPT_RESERVED_SECTORS
|
|
||||||
esp_number = None
|
esp_number = None
|
||||||
for partition in partitions.itervalues():
|
for partition in partitions.itervalues():
|
||||||
sector = Align(sector, partition['alignment'])
|
|
||||||
if partition['type'] != 'blank':
|
if partition['type'] != 'blank':
|
||||||
Cgpt('add', '-i', partition['num'],
|
Cgpt('add', '-i', partition['num'],
|
||||||
'-b', sector,
|
'-b', partition['first_block'],
|
||||||
'-s', partition['blocks'],
|
'-s', partition['blocks'],
|
||||||
'-t', partition['type'],
|
'-t', partition['type'],
|
||||||
'-l', partition['label'],
|
'-l', partition['label'],
|
||||||
@ -182,8 +199,6 @@ def WritePartitionTable(options):
|
|||||||
if partition['type'] == 'efi':
|
if partition['type'] == 'efi':
|
||||||
esp_number = partition['num']
|
esp_number = partition['num']
|
||||||
|
|
||||||
sector += partition['blocks']
|
|
||||||
|
|
||||||
if esp_number is None:
|
if esp_number is None:
|
||||||
raise InvalidLayout('Table does not include an EFI partition.')
|
raise InvalidLayout('Table does not include an EFI partition.')
|
||||||
|
|
||||||
@ -240,7 +255,7 @@ def GetBlockSize(options):
|
|||||||
Block size of all partitions in the layout
|
Block size of all partitions in the layout
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config = LoadPartitionConfig(options)
|
config, partitions = LoadPartitionConfig(options)
|
||||||
print config['metadata']['block_size']
|
print config['metadata']['block_size']
|
||||||
|
|
||||||
|
|
||||||
@ -255,7 +270,7 @@ def GetFilesystemBlockSize(options):
|
|||||||
Block size of all filesystems in the layout
|
Block size of all filesystems in the layout
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config = LoadPartitionConfig(options)
|
config, partitions = LoadPartitionConfig(options)
|
||||||
print config['metadata']['fs_block_size']
|
print config['metadata']['fs_block_size']
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user