Implemented both MBR and GPT partitioning.

VirtualBox seems to not like GPT
This commit is contained in:
Anders Ingemann 2013-09-18 00:46:58 +02:00
parent 8073edc902
commit d6502089e2
16 changed files with 187 additions and 96 deletions

View file

@ -4,10 +4,12 @@ def load_volume(data):
from common.fs.loopbackvolume import LoopbackVolume from common.fs.loopbackvolume import LoopbackVolume
from providers.ec2.volume import EBSVolume from providers.ec2.volume import EBSVolume
from providers.virtualbox.volume import VirtualBoxVolume from providers.virtualbox.volume import VirtualBoxVolume
from partitionmap import PartitionMap from partitionmaps.gpt import GPTPartitionMap
from nopartitions import NoPartitions from partitionmaps.mbr import MBRPartitionMap
from partitionmaps.none import NoPartitions
partition_maps = {'none': NoPartitions, partition_maps = {'none': NoPartitions,
'gpt': PartitionMap, 'gpt': GPTPartitionMap,
'mbr': MBRPartitionMap,
} }
partition_map = partition_maps.get(data['partitions']['type'])(data['partitions']) partition_map = partition_maps.get(data['partitions']['type'])(data['partitions'])
volume_backings = {'raw': LoopbackVolume, volume_backings = {'raw': LoopbackVolume,

View file

View file

@ -1,39 +1,23 @@
from abc import ABCMeta
from abc import abstractmethod
from common.tools import log_check_call from common.tools import log_check_call
from partitions.partition import Partition from ..exceptions import PartitionError
from partitions.swap import Swap
from exceptions import PartitionError
class PartitionMap(object): class AbstractPartitionMap(object):
__metaclass__ = ABCMeta
@abstractmethod
def __init__(self, data): def __init__(self, data):
self.boot = None pass
self.swap = None
self.mount_points = []
if 'boot' in data:
self.boot = Partition(data['boot']['size'], data['boot']['filesystem'], None)
self.mount_points.append(('/boot', self.boot))
self.root = Partition(data['root']['size'], data['root']['filesystem'], self.boot)
self.mount_points.append(('/', self.root))
if 'swap' in data:
self.swap = Swap(data['swap']['size'], self.root)
self.mount_points.append(('none', self.root))
self.partitions = filter(lambda p: p is not None, [self.boot, self.root, self.swap])
def get_total_size(self): def get_total_size(self):
return sum(p.size for p in self.partitions) return sum(p.size for p in self.partitions) + 1
@abstractmethod
def create(self, volume): def create(self, volume):
log_check_call(['/sbin/parted', '--script', '--align', 'optimal', volume.device_path, pass
'--', 'mklabel', 'gpt'])
for partition in self.partitions:
partition.create(volume)
boot_idx = self.root.get_index()
if self.boot is not None:
boot_idx = self.boot.get_index()
log_check_call(['/sbin/parted', '--script', volume.device_path,
'--', 'set', str(boot_idx), 'bios_grub', 'on'])
def map(self, volume): def map(self, volume):
try: try:

View file

@ -0,0 +1,35 @@
from abstract import AbstractPartitionMap
from ..partitions.gpt import GPTPartition
from ..partitions.gpt_swap import GPTSwapPartition
from common.tools import log_check_call
class GPTPartitionMap(AbstractPartitionMap):
def __init__(self, data):
self.boot = None
self.swap = None
self.mount_points = []
if 'boot' in data:
self.boot = GPTPartition(data['boot']['size'], data['boot']['filesystem'], 'boot', None)
self.mount_points.append(('/boot', self.boot))
self.root = GPTPartition(data['root']['size'], data['root']['filesystem'], 'root', self.boot)
self.mount_points.append(('/', self.root))
if 'swap' in data:
self.swap = GPTSwapPartition(data['swap']['size'], self.root)
self.mount_points.append(('none', self.root))
self.partitions = filter(lambda p: p is not None, [self.boot, self.root, self.swap])
def create(self, volume):
log_check_call(['/sbin/parted', '--script', '--align', 'none', volume.device_path,
'--', 'mklabel', 'gpt'])
for partition in self.partitions:
partition.create(volume)
boot_idx = self.root.get_index()
if self.boot is not None:
boot_idx = self.boot.get_index()
log_check_call(['/sbin/parted', '--script', volume.device_path,
'--', 'set ' + str(boot_idx) + ' bios_grub on'])
log_check_call(['/sbin/parted', '--script', volume.device_path,
'--', 'set ' + str(boot_idx) + ' boot on'])

View file

@ -0,0 +1,33 @@
from abstract import AbstractPartitionMap
from ..partitions.mbr import MBRPartition
from ..partitions.mbr_swap import MBRSwapPartition
from common.tools import log_check_call
class MBRPartitionMap(AbstractPartitionMap):
def __init__(self, data):
self.boot = None
self.swap = None
self.mount_points = []
if 'boot' in data:
self.boot = MBRPartition(data['boot']['size'], data['boot']['filesystem'], None)
self.mount_points.append(('/boot', self.boot))
self.root = MBRPartition(data['root']['size'], data['root']['filesystem'], self.boot)
self.mount_points.append(('/', self.root))
if 'swap' in data:
self.swap = MBRSwapPartition(data['swap']['size'], self.root)
self.mount_points.append(('none', self.root))
self.partitions = filter(lambda p: p is not None, [self.boot, self.root, self.swap])
def create(self, volume):
log_check_call(['/sbin/parted', '--script', '--align', 'none', volume.device_path,
'--', 'mklabel', 'msdos'])
for partition in self.partitions:
partition.create(volume)
boot_idx = self.root.get_index()
if self.boot is not None:
boot_idx = self.boot.get_index()
log_check_call(['/sbin/parted', '--script', volume.device_path,
'--', 'set ' + str(boot_idx) + ' boot on'])

View file

@ -1,4 +1,4 @@
from partitions.singlepartition import SinglePartition from ..partitions.single import SinglePartition
class NoPartitions(object): class NoPartitions(object):

View file

@ -1,5 +1,6 @@
from common.tools import log_check_call
from abc import ABCMeta from abc import ABCMeta
from abc import abstractmethod
from common.tools import log_check_call
from fysom import Fysom from fysom import Fysom
@ -17,9 +18,9 @@ class AbstractPartition(object):
self.size = size self.size = size
self.filesystem = filesystem self.filesystem = filesystem
self.device_path = None self.device_path = None
self.initial_state = 'nonexistent'
callbacks.update({'onbeforeformat': self._format, callbacks.update({'onbeforecreate': self._create,
'onbeforeformat': self._format,
'onbeforemount': self._mount, 'onbeforemount': self._mount,
'onbeforeunmount': self._unmount, 'onbeforeunmount': self._unmount,
}) })
@ -40,6 +41,13 @@ class AbstractPartition(object):
[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
def create(self, volume):
self.fsm.create(volume=volume)
@abstractmethod
def _create(self, e):
pass
def _format(self, e): def _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])

View file

@ -1,8 +1,7 @@
from common.tools import log_check_call from abstract import AbstractPartition
from abstractpartition import AbstractPartition
class Partition(AbstractPartition): class BasePartition(AbstractPartition):
events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'unmapped'}, events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'unmapped'},
{'name': 'map', 'src': 'unmapped', 'dst': 'mapped'}, {'name': 'map', 'src': 'unmapped', 'dst': 'mapped'},
@ -16,11 +15,10 @@ class Partition(AbstractPartition):
def __init__(self, size, filesystem, previous, callbacks={}): def __init__(self, size, filesystem, previous, callbacks={}):
self.previous = previous self.previous = previous
callbacks.update({'onbeforecreate': self._create, callbacks.update({'onbeforemap': self._map,
'onbeforemap': self._map,
'onbeforeunmap': self._unmap, 'onbeforeunmap': self._unmap,
}) })
super(Partition, self).__init__(size, filesystem, callbacks=callbacks) super(BasePartition, self).__init__(size, filesystem, callbacks=callbacks)
def get_index(self): def get_index(self):
if self.previous is None: if self.previous is None:
@ -30,19 +28,10 @@ class Partition(AbstractPartition):
def get_start(self): def get_start(self):
if self.previous is None: if self.previous is None:
return 0 return 1
else: else:
return self.previous.get_start() + self.previous.size return self.previous.get_start() + self.previous.size
def create(self, volume):
self.fsm.create(volume=volume)
def _create(self, e):
start = self.get_start()
# maybe use `parted -- name` to set partition name
log_check_call(['/sbin/parted', '--script', '--align', 'optimal', e.volume.device_path,
'--', 'mkpart', 'primary', str(start), str(start + self.size)])
def map(self, device_path): def map(self, device_path):
self.fsm.map(device_path=device_path) self.fsm.map(device_path=device_path)
@ -51,10 +40,3 @@ class Partition(AbstractPartition):
def _unmap(self, e): def _unmap(self, e):
self.device_path = None self.device_path = None
# Partition flags:
# boot, root, swap, hidden, raid, lvm, lba, legacy_boot, palo
# Partition tables
# bsd, dvh, gpt, loop, mac, msdos, pc98, sun

19
base/fs/partitions/gpt.py Normal file
View file

@ -0,0 +1,19 @@
from common.tools import log_check_call
from base import BasePartition
class GPTPartition(BasePartition):
def __init__(self, size, filesystem, name, previous, callbacks={}):
self.name = name
super(GPTPartition, self).__init__(size, filesystem, previous, callbacks=callbacks)
def _create(self, e):
start = self.get_start()
# {name} only works for gpt, for msdos that becomes the part-type (primary, extended, logical)
parted_command = ('mkpart primary {start}MiB {end}MiB'
.format(name=self.name,
start=str(start),
end=str(start + self.size)))
log_check_call(['/sbin/parted', '--script', '--align', 'none', e.volume.device_path,
'--', parted_command])

View file

@ -0,0 +1,11 @@
from common.tools import log_check_call
from gpt import GPTPartition
class GPTSwapPartition(GPTPartition):
def __init__(self, size, previous):
super(GPTSwapPartition, self).__init__(size, 'swap', 'swap', previous)
def _format(self, e):
log_check_call(['/sbin/mkswap', self.device_path])

13
base/fs/partitions/mbr.py Normal file
View file

@ -0,0 +1,13 @@
from common.tools import log_check_call
from base import BasePartition
class MBRPartition(BasePartition):
def _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

@ -1,11 +1,11 @@
from common.tools import log_check_call from common.tools import log_check_call
from partition import Partition from mbr import MBRPartition
class Swap(Partition): class MBRSwapPartition(MBRPartition):
def __init__(self, size, previous): def __init__(self, size, previous):
super(Swap, self).__init__(size, 'swap', previous) super(MBRSwapPartition, self).__init__(size, 'swap', previous)
def _format(self, e): def _format(self, e):
log_check_call(['/sbin/mkswap', self.device_path]) log_check_call(['/sbin/mkswap', self.device_path])

View file

@ -1,4 +1,4 @@
from abstractpartition import AbstractPartition from abstract import AbstractPartition
class SinglePartition(AbstractPartition): class SinglePartition(AbstractPartition):
@ -9,12 +9,5 @@ class SinglePartition(AbstractPartition):
{'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'}, {'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'},
] ]
def __init__(self, size, filesystem, callbacks={}):
callbacks['oncreate'] = self._create
super(SinglePartition, self).__init__(size, filesystem, callbacks=callbacks)
def create(self, volume):
self.fsm.create(volume=volume)
def _create(self, e): def _create(self, e):
self.device_path = e.volume.device_path self.device_path = e.volume.device_path

View file

@ -14,6 +14,7 @@ class CheckPackages(Task):
from subprocess import CalledProcessError from subprocess import CalledProcessError
for package in info.host_packages: for package in info.host_packages:
try: try:
# Use "dpkg-query -W -f='${Status} ${Version}\n' package" instead
log_check_call(['/usr/bin/dpkg', '--status', package]) log_check_call(['/usr/bin/dpkg', '--status', package])
except CalledProcessError: except CalledProcessError:
msg = "The package ``{0}\'\' is not installed".format(package) msg = "The package ``{0}\'\' is not installed".format(package)

View file

@ -1,33 +1,33 @@
{ {
"provider": "virtualbox", "provider" : "virtualbox",
"bootstrapper": { "bootstrapper": {
"workspace": "/target", "workspace": "/target",
"tarball": true "tarball": true
}, },
"image": { "image": {
"name": "debian-{release}-{architecture}-{%y}{%m}{%d}", "name" : "debian-{release}-{architecture}-{%y}{%m}{%d}",
"description": "Debian {release} {architecture}" "description": "Debian {release} {architecture}"
}, },
"system": { "system": {
"release": "wheezy", "release" : "wheezy",
"architecture": "amd64", "architecture": "amd64",
"timezone": "UTC", "timezone" : "UTC",
"locale": "en_US", "locale" : "en_US",
"charmap": "UTF-8" "charmap" : "UTF-8"
}, },
"volume": { "volume": {
"backing": "vdi", "backing": "vdi",
"partitions": { "partitions": {
"type": "gpt", "type": "mbr",
"boot": { "boot": {
"size": 64, "size": 32,
"filesystem": "ext2" "filesystem": "ext2"
}, },
"root": { "root": {
"size": 832, "size": 991,
"filesystem": "ext4" "filesystem": "ext4"
}, }
"swap": {"size": 128} // ,"swap": {"size": 128}
} }
} }
} }

View file

@ -16,6 +16,7 @@ class ConfigureGrub(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')
# if type(info.volume) is LoopbackVolume:
if isinstance(info.volume, LoopbackVolume): if isinstance(info.volume, LoopbackVolume):
# GRUB cannot deal with installing to loopback devices # GRUB cannot deal with installing to loopback devices
# so we fake a real harddisk with dmsetup. # so we fake a real harddisk with dmsetup.
@ -27,18 +28,32 @@ class ConfigureGrub(Task):
info.volume.mount_root(info.root) info.volume.mount_root(info.root)
info.volume.mount_boot() info.volume.mount_boot()
info.volume.mount_specials() info.volume.mount_specials()
[device_path] = log_check_call(['readlink', '-f', info.volume.device_path]) try:
device_map_path = os.path.join(grub_dir, 'device.map') [device_path] = log_check_call(['readlink', '-f', info.volume.device_path])
with open(device_map_path, 'w') as device_map: device_map_path = os.path.join(grub_dir, 'device.map')
device_map.write('(hd0) {device_path}\n'.format(device_path=device_path)) with open(device_map_path, 'w') as device_map:
device_map.write('(hd0) {device_path}\n'.format(device_path=device_path))
for idx, partition in enumerate(info.volume.partition_map.partitions):
[partition_path] = log_check_call(['readlink', '-f', partition.device_path])
device_map.write('(hd0,gpt{idx}) {device_path}\n'.format(device_path=partition_path, idx=idx+1))
# Install grub # Install grub
log_check_call(['/usr/sbin/grub-install', log_check_call(['/usr/sbin/chroot', info.root,
'--root-directory=' + info.root, '/usr/sbin/grub-install',
'--boot-directory=' + boot_dir, # '--root-directory=' + info.root,
device_path]) # '--boot-directory=' + boot_dir,
log_check_call(['/usr/sbin/chroot', info.root, '/usr/sbin/update-grub']) device_path])
# log_check_call(['/usr/sbin/chroot', info.root, '/usr/sbin/update-initramfs', '-u']) log_check_call(['/usr/sbin/chroot', info.root, '/usr/sbin/update-grub'])
except Exception as e:
if isinstance(info.volume, LoopbackVolume):
info.volume.unmount()
info.volume.unmap()
info.volume.unlink_dm_node()
info.volume.map()
info.volume.mount_root(info.root)
info.volume.mount_boot()
info.volume.mount_specials()
raise e
if isinstance(info.volume, LoopbackVolume): if isinstance(info.volume, LoopbackVolume):
info.volume.unmount() info.volume.unmount()
@ -48,8 +63,3 @@ class ConfigureGrub(Task):
info.volume.mount_root(info.root) info.volume.mount_root(info.root)
info.volume.mount_boot() info.volume.mount_boot()
info.volume.mount_specials() info.volume.mount_specials()
# Best guess right now...
device_map_path = os.path.join(grub_dir, 'device.map')
with open(device_map_path, 'w') as device_map:
device_map.write('(hd0) /dev/mapper/vda\n')