bootstrap-vz/common/fs/loopbackvolume.py
Anders Ingemann ff7c04c120 Support for partitions
MAJOR refactor. The volume is now abstracted into a model along with a partitionmap and partitions.
Volumes and partitions are now controlled via an FSM to ensure that commands are called in the proper sequence.
GRUB can now be installed properly onto loop devices by using dmsetup to fake a proper harddisk.
2013-10-27 18:11:12 +01:00

90 lines
3.5 KiB
Python

from base.fs.volume import Volume
from common.tools import log_check_call
from base.fs.exceptions import VolumeError
# QEMU formats:
# raw, host_device, qcow2, qcow, cow, vdi, vmdk, vpc, cloop
class LoopbackVolume(Volume):
link_dm_events = [{'name': 'link_dm_node', 'src': 'partitioned', 'dst': 'linked'},
{'name': 'map', 'src': 'linked', 'dst': 'mapped_lnk'},
{'name': 'format', 'src': 'mapped_lnk', 'dst': 'formatted_lnk'},
{'name': 'mount', 'src': ['formatted_lnk', 'mounted_lnk'], 'dst': 'mounted_lnk'},
{'name': 'unmount', 'src': 'mounted_lnk', 'dst': 'formatted_lnk'},
{'name': 'unmap', 'src': 'formatted_lnk', 'dst': 'partitioned_fmt_lnk'},
{'name': 'unlink_dm_node', 'src': 'partitioned_fmt_lnk', 'dst': 'partitioned_fmt'},
{'name': 'link_dm_node', 'src': 'partitioned_fmt', 'dst': 'partitioned_fmt_lnk'},
{'name': 'map', 'src': 'partitioned_fmt_lnk', 'dst': 'formatted_lnk'},
{'name': 'unmap', 'src': 'mapped_lnk', 'dst': 'linked'},
{'name': 'unlink_dm_node', 'src': 'linked', 'dst': 'partitioned'},
]
def __init__(self, partition_map, callbacks={}):
callbacks.update({'onbeforecreate': self._create,
'onbeforeattach': self._attach,
'onbeforedetach': self._detach,
'onbeforedelete': self._delete,
'onbeforelink_dm_node': self._link_dm_node,
'onbeforeunlink_dm_node': self._unlink_dm_node,
})
self.events.extend(self.link_dm_events)
super(LoopbackVolume, self).__init__(partition_map, callbacks=callbacks)
def create(self, image_path):
self.fsm.create(image_path=image_path)
def _create(self, e):
self.image_path = e.image_path
log_check_call(['/usr/bin/qemu-img', 'create', '-f', 'raw', self.image_path, str(self.size) + 'M'])
def _attach(self, e):
[self.loop_device_path] = log_check_call(['/sbin/losetup', '--show', '--find', self.image_path])
self.device_path = self.loop_device_path
def _link_dm_node(self, e):
import re
loop_device_name = re.match('^/dev/(?P<name>.*)$', self.loop_device_path).group('name')
from . import get_major_minor_dev_num
major, minor = get_major_minor_dev_num(loop_device_name)
sectors = self.size*1024*1024/512
table = ('{log_start_sec} {sectors} linear {major}:{minor} {start_sec}'
.format(log_start_sec=0,
sectors=sectors,
major=major,
minor=minor,
start_sec=0))
import string
import os.path
for letter in string.ascii_lowercase:
dev_name = 'vd' + letter
dev_path = os.path.join('/dev/mapper', dev_name)
if not os.path.exists(dev_path):
self.dm_node_name = dev_name
self.dm_node_path = dev_path
break
if not hasattr(self, 'dm_node_name'):
raise VolumeError('Unable to find a free block device path for mounting the bootstrap volume')
log_check_call(['/sbin/dmsetup', 'create', self.dm_node_name], table)
self.device_path = self.dm_node_path
def _unlink_dm_node(self, e):
log_check_call(['/sbin/dmsetup', 'remove', self.dm_node_name])
del self.dm_node_name
del self.dm_node_path
self.device_path = self.loop_device_path
def _detach(self, e):
log_check_call(['/sbin/losetup', '--detach', self.loop_device_path])
del self.loop_device_path
del self.device_path
def _delete(self, e):
from os import remove
remove(self.image_path)
del self.image_path