diff --git a/base/fs/__init__.py b/base/fs/__init__.py index 6180894..6b4763b 100644 --- a/base/fs/__init__.py +++ b/base/fs/__init__.py @@ -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, diff --git a/base/fs/partitionmaps/__init__.py b/base/fs/partitionmaps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/base/fs/partitionmap.py b/base/fs/partitionmaps/abstract.py similarity index 61% rename from base/fs/partitionmap.py rename to base/fs/partitionmaps/abstract.py index 49a75ab..167ddea 100644 --- a/base/fs/partitionmap.py +++ b/base/fs/partitionmaps/abstract.py @@ -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: diff --git a/base/fs/partitionmaps/gpt.py b/base/fs/partitionmaps/gpt.py new file mode 100644 index 0000000..76bbd0a --- /dev/null +++ b/base/fs/partitionmaps/gpt.py @@ -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']) diff --git a/base/fs/partitionmaps/mbr.py b/base/fs/partitionmaps/mbr.py new file mode 100644 index 0000000..2e635c4 --- /dev/null +++ b/base/fs/partitionmaps/mbr.py @@ -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']) diff --git a/base/fs/nopartitions.py b/base/fs/partitionmaps/none.py similarity index 90% rename from base/fs/nopartitions.py rename to base/fs/partitionmaps/none.py index 0133ffc..a04f742 100644 --- a/base/fs/nopartitions.py +++ b/base/fs/partitionmaps/none.py @@ -1,4 +1,4 @@ -from partitions.singlepartition import SinglePartition +from ..partitions.single import SinglePartition class NoPartitions(object): diff --git a/base/fs/partitions/abstractpartition.py b/base/fs/partitions/abstract.py similarity index 86% rename from base/fs/partitions/abstractpartition.py rename to base/fs/partitions/abstract.py index 161e134..15b7f0d 100644 --- a/base/fs/partitions/abstractpartition.py +++ b/base/fs/partitions/abstract.py @@ -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]) diff --git a/base/fs/partitions/partition.py b/base/fs/partitions/base.py similarity index 58% rename from base/fs/partitions/partition.py rename to base/fs/partitions/base.py index a62ac0e..f79ebb3 100644 --- a/base/fs/partitions/partition.py +++ b/base/fs/partitions/base.py @@ -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 diff --git a/base/fs/partitions/gpt.py b/base/fs/partitions/gpt.py new file mode 100644 index 0000000..01987ad --- /dev/null +++ b/base/fs/partitions/gpt.py @@ -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]) diff --git a/base/fs/partitions/gpt_swap.py b/base/fs/partitions/gpt_swap.py new file mode 100644 index 0000000..030e58b --- /dev/null +++ b/base/fs/partitions/gpt_swap.py @@ -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]) diff --git a/base/fs/partitions/mbr.py b/base/fs/partitions/mbr.py new file mode 100644 index 0000000..9d4fbbd --- /dev/null +++ b/base/fs/partitions/mbr.py @@ -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]) diff --git a/base/fs/partitions/swap.py b/base/fs/partitions/mbr_swap.py similarity index 54% rename from base/fs/partitions/swap.py rename to base/fs/partitions/mbr_swap.py index 80b7531..0c5c1a1 100644 --- a/base/fs/partitions/swap.py +++ b/base/fs/partitions/mbr_swap.py @@ -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]) diff --git a/base/fs/partitions/singlepartition.py b/base/fs/partitions/single.py similarity index 58% rename from base/fs/partitions/singlepartition.py rename to base/fs/partitions/single.py index 10bd702..c43236a 100644 --- a/base/fs/partitions/singlepartition.py +++ b/base/fs/partitions/single.py @@ -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 diff --git a/common/tasks/host.py b/common/tasks/host.py index c377ecc..aa76df0 100644 --- a/common/tasks/host.py +++ b/common/tasks/host.py @@ -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) diff --git a/manifests/virtualbox.manifest.json b/manifests/virtualbox.manifest.json index 3b33999..d3ce81f 100644 --- a/manifests/virtualbox.manifest.json +++ b/manifests/virtualbox.manifest.json @@ -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} } } } diff --git a/providers/virtualbox/tasks/boot.py b/providers/virtualbox/tasks/boot.py index bb9a0c5..516b6a6 100644 --- a/providers/virtualbox/tasks/boot.py +++ b/providers/virtualbox/tasks/boot.py @@ -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')