VDI images working now

*Helpful error messages included
This commit is contained in:
Anders Ingemann 2013-09-15 18:26:22 +02:00
parent 6fb60a33f0
commit efecc15382
6 changed files with 81 additions and 15 deletions

View file

@ -6,6 +6,7 @@ class NoPartitions(object):
def __init__(self, data): def __init__(self, data):
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.mount_points = [('/', self.root)] self.mount_points = [('/', self.root)]
def get_total_size(self): def get_total_size(self):

View file

@ -1,11 +1,16 @@
def get_major_minor_dev_num(device_name): def get_partitions():
import re import re
regexp = re.compile('^ *(?P<major>\d+) *(?P<minor>\d+) *(?P<num_blks>\d+) {device_name}$' regexp = re.compile('^ *(?P<major>\d+) *(?P<minor>\d+) *(?P<num_blks>\d+) (?P<dev_name>\S+)$')
.format(device_name=device_name)) matches = {}
with open('/proc/partitions') as partitions: path = '/proc/partitions'
with open(path) as partitions:
next(partitions)
next(partitions)
for line in partitions: for line in partitions:
match = regexp.match(line) match = regexp.match(line)
if match is not None: if match is None:
return match.group('major'), match.group('minor') raise RuntimeError('Unable to parse {line} in {path}'.format(line=line, path=path))
matches[match.group('dev_name')] = match.groupdict()
return matches

View file

@ -49,15 +49,17 @@ class LoopbackVolume(Volume):
def _link_dm_node(self, e): def _link_dm_node(self, e):
import os.path import os.path
from . import get_partitions
proc_partitions = get_partitions()
loop_device_name = os.path.basename(self.loop_device_path) loop_device_name = os.path.basename(self.loop_device_path)
from . import get_major_minor_dev_num loop_device_partition = proc_partitions[loop_device_name]
major, minor = get_major_minor_dev_num(loop_device_name)
sectors = self.size*1024*1024/512 sectors = self.size*1024*1024/512
table = ('{log_start_sec} {sectors} linear {major}:{minor} {start_sec}' table = ('{log_start_sec} {sectors} linear {major}:{minor} {start_sec}'
.format(log_start_sec=0, .format(log_start_sec=0,
sectors=sectors, sectors=sectors,
major=major, major=loop_device_partition['major'],
minor=minor, minor=loop_device_partition['minor'],
start_sec=0)) start_sec=0))
import string import string
import os.path import os.path

View file

@ -64,15 +64,15 @@ class MountBoot(Task):
class CreateBootMountDir(Task): class CreateBootMountDir(Task):
description = 'Creating mountpoint boot partition' description = 'Creating mountpoint for the boot partition'
phase = phases.volume_mounting phase = phases.volume_mounting
after = [MountRoot] after = [MountRoot]
before = [MountBoot] before = [MountBoot]
def run(self, info): def run(self, info):
import os import os
info.boot = os.path.join(info.root, 'boot') boot_dir = os.path.join(info.root, 'boot')
os.makedirs() os.makedirs(boot_dir)
class MountSpecials(Task): class MountSpecials(Task):

View file

@ -91,7 +91,9 @@ def tasks(tasklist, manifest):
break break
if 'boot' in manifest.volume['partitions']: if 'boot' in manifest.volume['partitions']:
tasklist.add(filesystem.MountBoot(), filesystem.UnmountBoot()) tasklist.add(filesystem.CreateBootMountDir(),
filesystem.MountBoot(),
filesystem.UnmountBoot())
def rollback_tasks(tasklist, tasks_completed, manifest): def rollback_tasks(tasklist, tasks_completed, manifest):
@ -105,7 +107,7 @@ def rollback_tasks(tasklist, tasks_completed, manifest):
counter_task(filesystem.CreateMountDir, filesystem.DeleteMountDir) counter_task(filesystem.CreateMountDir, filesystem.DeleteMountDir)
counter_task(partitioning.MapPartitions, partitioning.UnmapPartitions) counter_task(partitioning.MapPartitions, partitioning.UnmapPartitions)
counter_task(filesystem.MountRoot, filesystem.UnmountRoot) counter_task(filesystem.MountRoot, filesystem.UnmountRoot)
counter_task(filesystem.MountSpecials, filesystem.UnmountSpecials)
counter_task(filesystem.MountBoot, filesystem.UnmountBoot) counter_task(filesystem.MountBoot, filesystem.UnmountBoot)
counter_task(filesystem.MountSpecials, filesystem.UnmountSpecials)
counter_task(volume_tasks.Attach, volume_tasks.Detach) counter_task(volume_tasks.Attach, volume_tasks.Detach)
counter_task(workspace.CreateWorkspace, workspace.DeleteWorkspace) counter_task(workspace.CreateWorkspace, workspace.DeleteWorkspace)

View file

@ -1,5 +1,7 @@
from common.fs.loopbackvolume import LoopbackVolume from common.fs.loopbackvolume import LoopbackVolume
from base.fs.exceptions import VolumeError
from common.tools import log_check_call from common.tools import log_check_call
from common.fs import get_partitions
class VirtualBoxVolume(LoopbackVolume): class VirtualBoxVolume(LoopbackVolume):
@ -9,3 +11,57 @@ class VirtualBoxVolume(LoopbackVolume):
def _create(self, e): def _create(self, e):
self.image_path = e.image_path self.image_path = e.image_path
log_check_call(['/usr/bin/qemu-img', 'create', '-f', 'vdi', self.image_path, str(self.size) + 'M']) log_check_call(['/usr/bin/qemu-img', 'create', '-f', 'vdi', self.image_path, str(self.size) + 'M'])
def _attach(self, e):
num_partitions = len(self.partition_map.partitions)
if not self._module_loaded('nbd'):
msg = ('The kernel module `nbd\' must be loaded '
'(`modprobe nbd max_part={num_partitions}\') to attach .vdi images'
.format(num_partitions=num_partitions))
raise VolumeError(msg)
nbd_max_part = int(self._module_param('nbd', 'max_part'))
if nbd_max_part < num_partitions:
msg = ('The kernel module `nbd\' was loaded with the max_part '
'parameter set to {max_part}, which is below '
'the amount of partitions for this volume ({num_partitions}). '
'Reload the nbd kernel module with max_part set to at least {num_partitions} '
'(`rmmod nbd; modprobe nbd max_part={num_partitions}\').'
.format(max_part=nbd_max_part, num_partitions=num_partitions))
raise VolumeError(msg)
self.loop_device_path = self._find_free_nbd_device()
log_check_call(['/usr/bin/qemu-nbd', '--connect', self.loop_device_path, self.image_path])
self.device_path = self.loop_device_path
def _detach(self, e):
log_check_call(['/usr/bin/qemu-nbd', '--disconnect', self.loop_device_path])
del self.loop_device_path
del self.device_path
def _module_loaded(self, module):
import re
regexp = re.compile('^{module} +'.format(module=module))
with open('/proc/modules') as loaded_modules:
for line in loaded_modules:
match = regexp.match(line)
if match is not None:
return True
return False
def _module_param(self, module, param):
import os.path
param_path = os.path.join('/sys/module', module, 'parameters', param)
with open(param_path) as param:
return param.read().strip()
# From http://lists.gnu.org/archive/html/qemu-devel/2011-11/msg02201.html
# Apparently it's not in the current qemu-nbd shipped with wheezy
def _is_nbd_used(self, device_name):
return device_name in get_partitions()
def _find_free_nbd_device(self):
import os.path
for i in xrange(0, 15):
device_name = 'nbd' + str(i)
if not self._is_nbd_used(device_name):
return os.path.join('/dev', device_name)
raise VolumeError('Unable to find free nbd device.')