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 providers.ec2.volume import EBSVolume
from providers.virtualbox.volume import VirtualBoxVolume
from partitionmap import PartitionMap
from nopartitions import NoPartitions
from partitionmaps.gpt import GPTPartitionMap
from partitionmaps.mbr import MBRPartitionMap
from partitionmaps.none import NoPartitions
partition_maps = {'none': NoPartitions,
'gpt': PartitionMap,
'gpt': GPTPartitionMap,
'mbr': MBRPartitionMap,
}
partition_map = partition_maps.get(data['partitions']['type'])(data['partitions'])
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 partitions.partition import Partition
from partitions.swap import Swap
from exceptions import PartitionError
from ..exceptions import PartitionError
class PartitionMap(object):
class AbstractPartitionMap(object):
__metaclass__ = ABCMeta
@abstractmethod
def __init__(self, data):
self.boot = None
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])
pass
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):
log_check_call(['/sbin/parted', '--script', '--align', 'optimal', 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'])
pass
def map(self, volume):
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):

View file

@ -1,5 +1,6 @@
from common.tools import log_check_call
from abc import ABCMeta
from abc import abstractmethod
from common.tools import log_check_call
from fysom import Fysom
@ -17,9 +18,9 @@ class AbstractPartition(object):
self.size = size
self.filesystem = filesystem
self.device_path = None
self.initial_state = 'nonexistent'
callbacks.update({'onbeforeformat': self._format,
callbacks.update({'onbeforecreate': self._create,
'onbeforeformat': self._format,
'onbeforemount': self._mount,
'onbeforeunmount': self._unmount,
})
@ -40,6 +41,13 @@ class AbstractPartition(object):
[uuid] = log_check_call(['/sbin/blkid', '-s', 'UUID', '-o', 'value', self.device_path])
return uuid
def create(self, volume):
self.fsm.create(volume=volume)
@abstractmethod
def _create(self, e):
pass
def _format(self, e):
mkfs = '/sbin/mkfs.{fs}'.format(fs=self.filesystem)
log_check_call([mkfs, self.device_path])

View file

@ -1,8 +1,7 @@
from common.tools import log_check_call
from abstractpartition import AbstractPartition
from abstract import AbstractPartition
class Partition(AbstractPartition):
class BasePartition(AbstractPartition):
events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'unmapped'},
{'name': 'map', 'src': 'unmapped', 'dst': 'mapped'},
@ -16,11 +15,10 @@ class Partition(AbstractPartition):
def __init__(self, size, filesystem, previous, callbacks={}):
self.previous = previous
callbacks.update({'onbeforecreate': self._create,
'onbeforemap': self._map,
callbacks.update({'onbeforemap': self._map,
'onbeforeunmap': self._unmap,
})
super(Partition, self).__init__(size, filesystem, callbacks=callbacks)
super(BasePartition, self).__init__(size, filesystem, callbacks=callbacks)
def get_index(self):
if self.previous is None:
@ -30,19 +28,10 @@ class Partition(AbstractPartition):
def get_start(self):
if self.previous is None:
return 0
return 1
else:
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):
self.fsm.map(device_path=device_path)
@ -51,10 +40,3 @@ class Partition(AbstractPartition):
def _unmap(self, e):
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 partition import Partition
from mbr import MBRPartition
class Swap(Partition):
class MBRSwapPartition(MBRPartition):
def __init__(self, size, previous):
super(Swap, self).__init__(size, 'swap', previous)
super(MBRSwapPartition, self).__init__(size, 'swap', previous)
def _format(self, e):
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):
@ -9,12 +9,5 @@ class SinglePartition(AbstractPartition):
{'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):
self.device_path = e.volume.device_path

View file

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

View file

@ -1,33 +1,33 @@
{
"provider": "virtualbox",
"provider" : "virtualbox",
"bootstrapper": {
"workspace": "/target",
"tarball": true
},
"image": {
"name": "debian-{release}-{architecture}-{%y}{%m}{%d}",
"name" : "debian-{release}-{architecture}-{%y}{%m}{%d}",
"description": "Debian {release} {architecture}"
},
"system": {
"release": "wheezy",
"release" : "wheezy",
"architecture": "amd64",
"timezone": "UTC",
"locale": "en_US",
"charmap": "UTF-8"
"timezone" : "UTC",
"locale" : "en_US",
"charmap" : "UTF-8"
},
"volume": {
"backing": "vdi",
"backing": "vdi",
"partitions": {
"type": "gpt",
"type": "mbr",
"boot": {
"size": 64,
"size": 32,
"filesystem": "ext2"
},
"root": {
"size": 832,
"size": 991,
"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')
grub_dir = os.path.join(boot_dir, 'grub')
# if type(info.volume) is LoopbackVolume:
if isinstance(info.volume, LoopbackVolume):
# GRUB cannot deal with installing to loopback devices
# so we fake a real harddisk with dmsetup.
@ -27,18 +28,32 @@ class ConfigureGrub(Task):
info.volume.mount_root(info.root)
info.volume.mount_boot()
info.volume.mount_specials()
[device_path] = log_check_call(['readlink', '-f', info.volume.device_path])
device_map_path = os.path.join(grub_dir, 'device.map')
with open(device_map_path, 'w') as device_map:
device_map.write('(hd0) {device_path}\n'.format(device_path=device_path))
try:
[device_path] = log_check_call(['readlink', '-f', info.volume.device_path])
device_map_path = os.path.join(grub_dir, 'device.map')
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
log_check_call(['/usr/sbin/grub-install',
'--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-initramfs', '-u'])
# Install grub
log_check_call(['/usr/sbin/chroot', info.root,
'/usr/sbin/grub-install',
# '--root-directory=' + info.root,
# '--boot-directory=' + boot_dir,
device_path])
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):
info.volume.unmount()
@ -48,8 +63,3 @@ class ConfigureGrub(Task):
info.volume.mount_root(info.root)
info.volume.mount_boot()
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')