GPT support for both extlinux and grub

This commit is contained in:
Anders Ingemann 2014-01-19 01:02:29 +01:00
parent 81a4ec78eb
commit 328b971289
17 changed files with 98 additions and 71 deletions

View file

@ -12,7 +12,7 @@ class BootstrapInformation(object):
self.workspace = os.path.join(manifest.bootstrapper['workspace'], self.run_id) self.workspace = os.path.join(manifest.bootstrapper['workspace'], self.run_id)
from fs import load_volume from fs import load_volume
self.volume = load_volume(self.manifest.volume) self.volume = load_volume(self.manifest.volume, manifest.system['bootloader'])
self.apt_mirror = self.manifest.packages.get('mirror', 'http://http.debian.net/debian') self.apt_mirror = self.manifest.packages.get('mirror', 'http://http.debian.net/debian')

View file

@ -1,6 +1,6 @@
def load_volume(data): def load_volume(data, bootloader):
from common.fs.loopbackvolume import LoopbackVolume from common.fs.loopbackvolume import LoopbackVolume
from providers.ec2.ebsvolume import EBSVolume from providers.ec2.ebsvolume import EBSVolume
from common.fs.virtualdiskimage import VirtualDiskImage from common.fs.virtualdiskimage import VirtualDiskImage
@ -12,7 +12,7 @@ def load_volume(data):
'gpt': GPTPartitionMap, 'gpt': GPTPartitionMap,
'msdos': MSDOSPartitionMap, 'msdos': MSDOSPartitionMap,
} }
partition_map = partition_maps.get(data['partitions']['type'])(data['partitions']) partition_map = partition_maps.get(data['partitions']['type'])(data['partitions'], bootloader)
volume_backings = {'raw': LoopbackVolume, volume_backings = {'raw': LoopbackVolume,
's3': LoopbackVolume, 's3': LoopbackVolume,
'vdi': VirtualDiskImage, 'vdi': VirtualDiskImage,

View file

@ -14,7 +14,7 @@ class AbstractPartitionMap(FSMProxy):
{'name': 'unmap', 'src': 'mapped', 'dst': 'unmapped'}, {'name': 'unmap', 'src': 'mapped', 'dst': 'unmapped'},
] ]
def __init__(self): def __init__(self, bootloader):
cfg = {'initial': 'nonexistent', 'events': self.events, 'callbacks': {}} cfg = {'initial': 'nonexistent', 'events': self.events, 'callbacks': {}}
super(AbstractPartitionMap, self).__init__(cfg) super(AbstractPartitionMap, self).__init__(cfg)
@ -22,7 +22,7 @@ class AbstractPartitionMap(FSMProxy):
return self.fsm.current == 'mapped' return self.fsm.current == 'mapped'
def get_total_size(self): def get_total_size(self):
return sum(p.size for p in self.partitions) return self.partitions[-1].get_end()
def create(self, volume): def create(self, volume):
self.fsm.create(volume=volume) self.fsm.create(volume=volume)

View file

@ -6,13 +6,20 @@ from common.tools import log_check_call
class GPTPartitionMap(AbstractPartitionMap): class GPTPartitionMap(AbstractPartitionMap):
def __init__(self, data): def __init__(self, data, bootloader):
self.partitions = [] self.partitions = []
def last_partition(): def last_partition():
return self.partitions[-1] if len(self.partitions) > 0 else None return self.partitions[-1] if len(self.partitions) > 0 else None
if bootloader == 'grub':
from ..partitions.unformatted import UnformattedPartition
self.grub_boot = UnformattedPartition(2, last_partition())
self.grub_boot.flags.append('bios_grub')
self.partitions.append(self.grub_boot)
if 'boot' in data: if 'boot' in data:
self.boot = GPTPartition(data['boot']['size'], data['boot']['filesystem'], 'boot', None) self.boot = GPTPartition(data['boot']['size'], data['boot']['filesystem'], 'boot', last_partition())
self.partitions.append(self.boot) self.partitions.append(self.boot)
if 'swap' in data: if 'swap' in data:
self.swap = GPTSwapPartition(data['swap']['size'], last_partition()) self.swap = GPTSwapPartition(data['swap']['size'], last_partition())
@ -20,7 +27,11 @@ class GPTPartitionMap(AbstractPartitionMap):
self.root = GPTPartition(data['root']['size'], data['root']['filesystem'], 'root', last_partition()) self.root = GPTPartition(data['root']['size'], data['root']['filesystem'], 'root', last_partition())
self.partitions.append(self.root) self.partitions.append(self.root)
super(GPTPartitionMap, self).__init__() # getattr(self, 'boot', self.root).flags.append('boot')
if bootloader == 'extlinux':
getattr(self, 'boot', self.root).flags.append('legacy_boot')
super(GPTPartitionMap, self).__init__(bootloader)
def _before_create(self, event): def _before_create(self, event):
volume = event.volume volume = event.volume
@ -28,9 +39,3 @@ class GPTPartitionMap(AbstractPartitionMap):
'--', 'mklabel', 'gpt']) '--', 'mklabel', 'gpt'])
for partition in self.partitions: for partition in self.partitions:
partition.create(volume) partition.create(volume)
boot_idx = getattr(self, 'boot', self.root).get_index()
log_check_call(['/sbin/parted', '--script', volume.device_path,
'--', 'set ' + str(boot_idx) + ' boot on'])
log_check_call(['/sbin/parted', '--script', volume.device_path,
'--', 'set ' + str(boot_idx) + ' bios_grub on'])

View file

@ -6,7 +6,7 @@ from common.tools import log_check_call
class MSDOSPartitionMap(AbstractPartitionMap): class MSDOSPartitionMap(AbstractPartitionMap):
def __init__(self, data): def __init__(self, data, bootloader):
self.partitions = [] self.partitions = []
def last_partition(): def last_partition():
@ -20,7 +20,11 @@ class MSDOSPartitionMap(AbstractPartitionMap):
self.root = MSDOSPartition(data['root']['size'], data['root']['filesystem'], last_partition()) self.root = MSDOSPartition(data['root']['size'], data['root']['filesystem'], last_partition())
self.partitions.append(self.root) self.partitions.append(self.root)
super(MSDOSPartitionMap, self).__init__() getattr(self, 'boot', self.root).flags.append('boot')
if bootloader == 'grub':
self.partitions[0].offset = 2
super(MSDOSPartitionMap, self).__init__(bootloader)
def _before_create(self, event): def _before_create(self, event):
volume = event.volume volume = event.volume
@ -28,7 +32,3 @@ class MSDOSPartitionMap(AbstractPartitionMap):
'--', 'mklabel', 'msdos']) '--', 'mklabel', 'msdos'])
for partition in self.partitions: for partition in self.partitions:
partition.create(volume) partition.create(volume)
boot_idx = getattr(self, 'boot', self.root).get_index()
log_check_call(['/sbin/parted', '--script', volume.device_path,
'--', 'set ' + str(boot_idx) + ' boot on'])

View file

@ -3,7 +3,7 @@ from ..partitions.single import SinglePartition
class NoPartitions(object): class NoPartitions(object):
def __init__(self, data): def __init__(self, data, bootloader):
root = data['root'] root = data['root']
self.root = SinglePartition(root['size'], root['filesystem']) self.root = SinglePartition(root['size'], root['filesystem'])
self.partitions = [self.root] self.partitions = [self.root]

View file

@ -1,4 +1,5 @@
from abc import ABCMeta from abc import ABCMeta
from abc import abstractmethod
import os.path import os.path
from common.tools import log_check_call from common.tools import log_check_call
from common.fsm_proxy import FSMProxy from common.fsm_proxy import FSMProxy
@ -23,7 +24,7 @@ class AbstractPartition(FSMProxy):
def mount(self, prefix): def mount(self, prefix):
mount_dir = os.path.join(prefix, self.destination) mount_dir = os.path.join(prefix, self.destination)
if isinstance(self.source, AbstractPartition): if isinstance(self.source, AbstractPartition):
self.source.mount(mount_dir) self.source.mount(destination=mount_dir)
else: else:
log_check_call(['/bin/mount'] + self.opts + [self.source, mount_dir]) log_check_call(['/bin/mount'] + self.opts + [self.source, mount_dir])
self.mount_dir = mount_dir self.mount_dir = mount_dir
@ -48,13 +49,17 @@ class AbstractPartition(FSMProxy):
[uuid] = log_check_call(['/sbin/blkid', '-s', 'UUID', '-o', 'value', self.device_path]) [uuid] = log_check_call(['/sbin/blkid', '-s', 'UUID', '-o', 'value', self.device_path])
return uuid return uuid
@abstractmethod
def get_start(self):
pass
def get_end(self):
return self.get_start() + self.size
def _before_format(self, e): def _before_format(self, e):
mkfs = '/sbin/mkfs.{fs}'.format(fs=self.filesystem) mkfs = '/sbin/mkfs.{fs}'.format(fs=self.filesystem)
log_check_call([mkfs, self.device_path]) log_check_call([mkfs, self.device_path])
def mount(self, destination):
self.fsm.mount(destination=destination)
def _before_mount(self, e): def _before_mount(self, e):
log_check_call(['/bin/mount', '--types', self.filesystem, self.device_path, e.destination]) log_check_call(['/bin/mount', '--types', self.filesystem, self.device_path, e.destination])
self.mount_dir = e.destination self.mount_dir = e.destination

View file

@ -16,6 +16,8 @@ class BasePartition(AbstractPartition):
def __init__(self, size, filesystem, previous): def __init__(self, size, filesystem, previous):
self.previous = previous self.previous = previous
self.offset = 0
self.flags = []
super(BasePartition, self).__init__(size, filesystem) super(BasePartition, self).__init__(size, filesystem)
def create(self, volume): def create(self, volume):
@ -29,13 +31,26 @@ class BasePartition(AbstractPartition):
def get_start(self): def get_start(self):
if self.previous is None: if self.previous is None:
return 0 return self.offset
else: else:
return self.previous.get_start() + self.previous.size return self.previous.get_end() + self.offset
def map(self, device_path): def map(self, device_path):
self.fsm.map(device_path=device_path) self.fsm.map(device_path=device_path)
def _before_create(self, e):
from common.tools import log_check_call
create_command = ('mkpart primary {start}MiB {end}MiB'
.format(start=str(self.get_start()),
end=str(self.get_end())))
log_check_call(['/sbin/parted', '--script', '--align', 'none', e.volume.device_path,
'--', create_command])
for flag in self.flags:
log_check_call(['/sbin/parted', '--script', e.volume.device_path,
'--', ('set {idx} {flag} on'
.format(idx=str(self.get_index()), flag=flag))])
def _before_map(self, e): def _before_map(self, e):
self.device_path = e.device_path self.device_path = e.device_path

View file

@ -9,16 +9,10 @@ class GPTPartition(BasePartition):
super(GPTPartition, self).__init__(size, filesystem, previous) super(GPTPartition, self).__init__(size, filesystem, previous)
def _before_create(self, e): def _before_create(self, e):
start = self.get_start() super(GPTPartition, self)._before_create(e)
create_command = ('mkpart primary {start}MiB {end}MiB'
.format(start=str(start),
end=str(start + self.size)))
log_check_call(['/sbin/parted', '--script', '--align', 'none', e.volume.device_path,
'--', create_command])
# partition name only works for gpt, for msdos that becomes the part-type (primary, extended, logical) # partition name only works for gpt, for msdos that becomes the part-type (primary, extended, logical)
name_command = ('name {idx} {name}' name_command = ('name {idx} {name}'
.format(idx=self.get_index(), .format(idx=self.get_index(),
name=self.name)) name=self.name))
log_check_call(['/sbin/parted', '--script', '--align', 'none', e.volume.device_path, log_check_call(['/sbin/parted', '--script', e.volume.device_path,
'--', name_command]) '--', name_command])

View file

@ -1,19 +1,5 @@
from common.tools import log_check_call
from base import BasePartition from base import BasePartition
class MSDOSPartition(BasePartition): class MSDOSPartition(BasePartition):
pass
def get_start(self):
if self.previous is None:
return 2 # Post-MBR gap for embedding grub
else:
return self.previous.get_start() + self.previous.size
def _before_create(self, e):
start = self.get_start()
parted_command = ('mkpart primary {start}MiB {end}MiB'
.format(start=str(start),
end=str(start + self.size)))
log_check_call(['/sbin/parted', '--script', '--align', 'none', e.volume.device_path,
'--', parted_command])

View file

@ -2,4 +2,6 @@ from abstract import AbstractPartition
class SinglePartition(AbstractPartition): class SinglePartition(AbstractPartition):
pass
def get_start(self):
return 0

View file

@ -0,0 +1,12 @@
from base import BasePartition
class UnformattedPartition(BasePartition):
events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'unmapped'},
{'name': 'map', 'src': 'unmapped', 'dst': 'mapped'},
{'name': 'unmap', 'src': 'mapped', 'dst': 'unmapped'},
]
def __init__(self, size, previous):
super(UnformattedPartition, self).__init__(size, None, previous)

View file

@ -3,7 +3,6 @@ from common.fsm_proxy import FSMProxy
from common.tools import log_check_call from common.tools import log_check_call
from exceptions import VolumeError from exceptions import VolumeError
from partitionmaps.none import NoPartitions from partitionmaps.none import NoPartitions
from partitionmaps.mbr import MSDOSPartitionMap
class Volume(FSMProxy): class Volume(FSMProxy):
@ -23,8 +22,6 @@ class Volume(FSMProxy):
self.real_device_path = None self.real_device_path = None
self.partition_map = partition_map self.partition_map = partition_map
self.size = self.partition_map.get_total_size() self.size = self.partition_map.get_total_size()
if isinstance(self.partition_map, MSDOSPartitionMap):
self.size += 2 # Post-MBR gap for embedding bootloader
callbacks = {'onbeforedetach': self._check_blocking} callbacks = {'onbeforedetach': self._check_blocking}
if isinstance(self.partition_map, NoPartitions): if isinstance(self.partition_map, NoPartitions):

View file

@ -28,5 +28,5 @@ def remount(volume, fn):
p_map.map(volume) p_map.map(volume)
else: else:
result = fn() result = fn()
p_map.root.mount(root_dir) p_map.root.mount(destination=root_dir)
return result return result

View file

@ -2,6 +2,7 @@ from base import Task
from common import phases from common import phases
from common.tasks import apt from common.tasks import apt
from common.tasks import filesystem from common.tasks import filesystem
from base.fs import partitionmaps
import os.path import os.path
@ -56,19 +57,17 @@ class InstallGrub(Task):
boot_dir = os.path.join(info.root, 'boot') boot_dir = os.path.join(info.root, 'boot')
grub_dir = os.path.join(boot_dir, 'grub') grub_dir = os.path.join(boot_dir, 'grub')
from base.fs.partitionmaps.none import NoPartitions
from base.fs.partitionmaps.gpt import GPTPartitionMap
from common.fs import remount from common.fs import remount
p_map = info.volume.partition_map p_map = info.volume.partition_map
def link_fn(): def link_fn():
info.volume.link_dm_node() info.volume.link_dm_node()
if isinstance(p_map, NoPartitions): if isinstance(p_map, partitionmaps.none.NoPartitions):
p_map.root.device_path = info.volume.device_path p_map.root.device_path = info.volume.device_path
def unlink_fn(): def unlink_fn():
info.volume.unlink_dm_node() info.volume.unlink_dm_node()
if isinstance(p_map, NoPartitions): if isinstance(p_map, partitionmaps.none.NoPartitions):
p_map.root.device_path = info.volume.device_path p_map.root.device_path = info.volume.device_path
# GRUB cannot deal with installing to loopback devices # GRUB cannot deal with installing to loopback devices
@ -80,11 +79,11 @@ class InstallGrub(Task):
[device_path] = log_check_call(['readlink', '-f', info.volume.device_path]) [device_path] = log_check_call(['readlink', '-f', info.volume.device_path])
device_map_path = os.path.join(grub_dir, 'device.map') device_map_path = os.path.join(grub_dir, 'device.map')
partition_prefix = 'msdos' partition_prefix = 'msdos'
if isinstance(p_map, GPTPartitionMap): if isinstance(p_map, partitionmaps.gpt.GPTPartitionMap):
partition_prefix = 'gpt' partition_prefix = 'gpt'
with open(device_map_path, 'w') as device_map: with open(device_map_path, 'w') as device_map:
device_map.write('(hd0) {device_path}\n'.format(device_path=device_path)) device_map.write('(hd0) {device_path}\n'.format(device_path=device_path))
if not isinstance(p_map, NoPartitions): if not isinstance(p_map, partitionmaps.none.NoPartitions):
for idx, partition in enumerate(info.volume.partition_map.partitions): for idx, partition in enumerate(info.volume.partition_map.partitions):
device_map.write('(hd0,{prefix}{idx}) {device_path}\n' device_map.write('(hd0,{prefix}{idx}) {device_path}\n'
.format(device_path=partition.device_path, .format(device_path=partition.device_path,
@ -93,10 +92,7 @@ class InstallGrub(Task):
# Install grub # Install grub
log_check_call(['/usr/sbin/chroot', info.root, log_check_call(['/usr/sbin/chroot', info.root,
'/usr/sbin/grub-install', '/usr/sbin/grub-install', device_path])
# '--root-directory=' + info.root,
# '--boot-directory=' + boot_dir,
device_path])
log_check_call(['/usr/sbin/chroot', info.root, '/usr/sbin/update-grub']) log_check_call(['/usr/sbin/chroot', info.root, '/usr/sbin/update-grub'])
except Exception as e: except Exception as e:
if isinstance(info.volume, LoopbackVolume): if isinstance(info.volume, LoopbackVolume):
@ -115,6 +111,8 @@ class AddExtlinuxPackage(Task):
@classmethod @classmethod
def run(cls, info): def run(cls, info):
info.packages.add('extlinux') info.packages.add('extlinux')
if isinstance(info.volume.partition_map, partitionmaps.gpt.GPTPartitionMap):
info.packages.add('syslinux-common')
class InstallExtLinux(Task): class InstallExtLinux(Task):
@ -125,7 +123,16 @@ class InstallExtLinux(Task):
@classmethod @classmethod
def run(cls, info): def run(cls, info):
from common.tools import log_check_call from common.tools import log_check_call
if isinstance(info.volume.partition_map, partitionmaps.gpt.GPTPartitionMap):
bootloader = '/usr/lib/syslinux/gptmbr.bin'
else:
bootloader = '/usr/lib/extlinux/mbr.bin'
log_check_call(['/usr/sbin/chroot', info.root, log_check_call(['/usr/sbin/chroot', info.root,
'/usr/sbin/extlinux-install', info.volume.device_path]) '/bin/dd', 'bs=440', 'count=1',
'if=' + bootloader,
'of=' + info.volume.device_path])
log_check_call(['/usr/sbin/chroot', info.root,
'/usr/bin/extlinux',
'--install', '/boot/extlinux'])
log_check_call(['/usr/sbin/chroot', info.root, log_check_call(['/usr/sbin/chroot', info.root,
'/usr/sbin/extlinux-update']) '/usr/sbin/extlinux-update'])

View file

@ -12,7 +12,9 @@ class Format(Task):
@classmethod @classmethod
def run(cls, info): def run(cls, info):
from base.fs.partitions.unformatted import UnformattedPartition
for partition in info.volume.partition_map.partitions: for partition in info.volume.partition_map.partitions:
if not isinstance(partition, UnformattedPartition):
partition.format() partition.format()
@ -23,9 +25,11 @@ class TuneVolumeFS(Task):
@classmethod @classmethod
def run(cls, info): def run(cls, info):
from base.fs.partitions.unformatted import UnformattedPartition
import re import re
# Disable the time based filesystem check # Disable the time based filesystem check
for partition in info.volume.partition_map.partitions: for partition in info.volume.partition_map.partitions:
if not isinstance(partition, UnformattedPartition):
if re.match('^ext[2-4]$', partition.filesystem) is not None: if re.match('^ext[2-4]$', partition.filesystem) is not None:
log_check_call(['/sbin/tune2fs', '-i', '0', partition.device_path]) log_check_call(['/sbin/tune2fs', '-i', '0', partition.device_path])
@ -58,7 +62,7 @@ class MountRoot(Task):
@classmethod @classmethod
def run(cls, info): def run(cls, info):
info.volume.partition_map.root.mount(info.root) info.volume.partition_map.root.mount(destination=info.root)
class CreateBootMountDir(Task): class CreateBootMountDir(Task):

View file

@ -30,7 +30,7 @@
"partitions": { "partitions": {
"type": "object", "type": "object",
"properties": { "properties": {
"type": { "enum": ["none", "msdos"] } "type": { "enum": ["none", "msdos", "gpt"] }
} }
} }
}, },