Implement partition mounts.

This solves quite a few challenges with mounting directories into root etc.
This commit is contained in:
Anders Ingemann 2013-11-27 18:06:45 +01:00 committed by Anders Ingemann
parent 970cbfccf2
commit 2af0968156
11 changed files with 65 additions and 97 deletions

View file

@ -1,4 +1,5 @@
from abc import ABCMeta from abc import ABCMeta
import os.path
from common.tools import log_check_call from common.tools import log_check_call
from common.fsm_proxy import FSMProxy from common.fsm_proxy import FSMProxy
@ -13,10 +14,32 @@ class AbstractPartition(FSMProxy):
{'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'}, {'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'},
] ]
class Mount(object):
def __init__(self, source, destination, opts):
self.source = source
self.destination = destination
self.opts = opts
def mount(self, prefix):
mount_dir = os.path.join(prefix, self.destination)
if isinstance(self.source, AbstractPartition):
self.source.mount(mount_dir)
else:
log_check_call(['/bin/mount'] + self.opts + [self.source, mount_dir])
self.mount_dir = mount_dir
def unmount(self):
if isinstance(self.source, AbstractPartition):
self.source.unmount()
else:
log_check_call(['/bin/umount', self.mount_dir])
del self.mount_dir
def __init__(self, size, filesystem): def __init__(self, size, filesystem):
self.size = size self.size = size
self.filesystem = filesystem self.filesystem = filesystem
self.device_path = None self.device_path = None
self.mounts = {}
cfg = {'initial': 'nonexistent', 'events': self.events, 'callbacks': {}} cfg = {'initial': 'nonexistent', 'events': self.events, 'callbacks': {}}
super(AbstractPartition, self).__init__(cfg) super(AbstractPartition, self).__init__(cfg)
@ -36,6 +59,23 @@ class AbstractPartition(FSMProxy):
log_check_call(['/bin/mount', '--types', self.filesystem, self.device_path, e.destination]) log_check_call(['/bin/mount', '--types', self.filesystem, self.device_path, e.destination])
self.mount_dir = e.destination self.mount_dir = e.destination
def _after_mount(self, e):
for destination in sorted(self.mounts.iterkeys(), key=len):
self.mounts[destination].mount(self.mount_dir)
def _before_unmount(self, e): def _before_unmount(self, e):
for destination in sorted(self.mounts.iterkeys(), key=len, reverse=True):
self.mounts[destination].unmount()
log_check_call(['/bin/umount', self.mount_dir]) log_check_call(['/bin/umount', self.mount_dir])
del self.mount_dir del self.mount_dir
def add_mount(self, source, destination, opts=[]):
mount = self.Mount(source, destination, opts)
if self.fsm.current == 'mounted':
mount.mount(self.mount_dir)
self.mounts[destination] = mount
def remove_mount(self, destination):
if self.fsm.current == 'mounted':
self.mounts[destination].unmount()
del self.mounts[destination]

View file

@ -17,7 +17,6 @@ class Volume(FSMProxy):
def __init__(self, partition_map): def __init__(self, partition_map):
self.device_path = None self.device_path = None
self.specials_mounted = False
self.partition_map = partition_map self.partition_map = partition_map
self.size = self.partition_map.get_total_size() self.size = self.partition_map.get_total_size()
@ -34,31 +33,6 @@ class Volume(FSMProxy):
if isinstance(self.partition_map, NoPartitions): if isinstance(self.partition_map, NoPartitions):
self.partition_map.root.create() self.partition_map.root.create()
def can_mount_specials(self):
return self.fsm.current == 'attached'
def mount_specials(self):
if self.specials_mounted:
raise VolumeError('The special devices are already mounted')
root = self.partition_map.root.mount_dir
log_check_call(['/bin/mount', '--bind', '/dev', '{root}/dev'.format(root=root)])
log_check_call(['/usr/sbin/chroot', root, '/bin/mount', '--types', 'proc', 'none', '/proc'])
log_check_call(['/usr/sbin/chroot', root, '/bin/mount', '--types', 'sysfs', 'none', '/sys'])
log_check_call(['/usr/sbin/chroot', root, '/bin/mount', '--types', 'devpts', 'none', '/dev/pts'])
self.specials_mounted = True
def unmount_specials(self):
if not self.specials_mounted:
raise VolumeError('The special devices are not mounted')
root = self.partition_map.root.mount_dir
log_check_call(['/usr/sbin/chroot', root, '/bin/umount', '/dev/pts'])
log_check_call(['/usr/sbin/chroot', root, '/bin/umount', '/sys'])
log_check_call(['/usr/sbin/chroot', root, '/bin/umount', '/proc'])
log_check_call(['/bin/umount', '{root}/dev'.format(root=root)])
self.specials_mounted = False
def _check_blocking(self, e): def _check_blocking(self, e):
if self.partition_map.is_blocking(): if self.partition_map.is_blocking():
raise VolumeError('The partitionmap prevents the detach procedure') raise VolumeError('The partitionmap prevents the detach procedure')
if self.specials_mounted:
raise VolumeError('The special devices are mounted and prevent the detaching procedure')

View file

@ -20,10 +20,6 @@ def remount(volume, fn):
from base.fs.partitionmaps.none import NoPartitions from base.fs.partitionmaps.none import NoPartitions
p_map = volume.partition_map p_map = volume.partition_map
volume.unmount_specials()
if hasattr(p_map, 'boot'):
boot_dir = p_map.boot.mount_dir
p_map.boot.unmount()
root_dir = p_map.root.mount_dir root_dir = p_map.root.mount_dir
p_map.root.unmount() p_map.root.unmount()
if not isinstance(p_map, NoPartitions): if not isinstance(p_map, NoPartitions):
@ -33,7 +29,4 @@ def remount(volume, fn):
else: else:
result = fn() result = fn()
p_map.root.mount(root_dir) p_map.root.mount(root_dir)
if hasattr(p_map, 'boot'):
p_map.boot.mount(boot_dir)
volume.mount_specials()
return result return result

View file

@ -19,9 +19,6 @@ class LoopbackVolume(Volume):
extension = 'raw' extension = 'raw'
def can_mount_specials(self):
return self.fsm.current in ['attached', 'linked']
def create(self, image_path): def create(self, image_path):
self.fsm.create(image_path=image_path) self.fsm.create(image_path=image_path)

View file

@ -31,13 +31,11 @@ partitioning_set = [partitioning.PartitionVolume,
boot_partition_set = [filesystem.CreateBootMountDir, boot_partition_set = [filesystem.CreateBootMountDir,
filesystem.MountBoot, filesystem.MountBoot,
filesystem.UnmountBoot,
] ]
mounting_set = [filesystem.CreateMountDir, mounting_set = [filesystem.CreateMountDir,
filesystem.MountRoot, filesystem.MountRoot,
filesystem.MountSpecials, filesystem.MountSpecials,
filesystem.UnmountSpecials,
filesystem.UnmountRoot, filesystem.UnmountRoot,
filesystem.DeleteMountDir, filesystem.DeleteMountDir,
] ]

View file

@ -55,25 +55,24 @@ class MountRoot(Task):
info.volume.partition_map.root.mount(info.root) info.volume.partition_map.root.mount(info.root)
class MountBoot(Task):
description = 'Mounting the boot partition'
phase = phases.volume_mounting
predecessors = [MountRoot]
def run(self, info):
info.volume.partition_map.boot.mount(info.boot_dir)
class CreateBootMountDir(Task): class CreateBootMountDir(Task):
description = 'Creating mountpoint for the boot partition' description = 'Creating mountpoint for the boot partition'
phase = phases.volume_mounting phase = phases.volume_mounting
successors = [MountBoot]
predecessors = [MountRoot] predecessors = [MountRoot]
def run(self, info): def run(self, info):
import os import os.path
info.boot_dir = os.path.join(info.root, 'boot') os.makedirs(os.path.join(info.root, 'boot'))
os.makedirs(info.boot_dir)
class MountBoot(Task):
description = 'Mounting the boot partition'
phase = phases.volume_mounting
predecessors = [CreateBootMountDir]
def run(self, info):
p_map = info.volume.partition_map
p_map.root.add_mount(p_map.boot, 'boot')
class MountSpecials(Task): class MountSpecials(Task):
@ -82,7 +81,11 @@ class MountSpecials(Task):
predecessors = [Bootstrap] predecessors = [Bootstrap]
def run(self, info): def run(self, info):
info.volume.mount_specials() root = info.volume.partition_map.root
root.add_mount('/dev', 'dev', ['--bind'])
root.add_mount('none', 'proc', ['--types', 'proc'])
root.add_mount('none', 'sys', ['--types', 'sysfs'])
root.add_mount('none', 'dev/pts', ['--types', 'devpts'])
class UnmountRoot(Task): class UnmountRoot(Task):
@ -94,24 +97,6 @@ class UnmountRoot(Task):
info.volume.partition_map.root.unmount() info.volume.partition_map.root.unmount()
class UnmountBoot(Task):
description = 'Unmounting the boot partition'
phase = phases.volume_unmounting
successors = [UnmountRoot]
def run(self, info):
info.volume.partition_map.boot.unmount()
class UnmountSpecials(Task):
description = 'Unmunting special block devices'
phase = phases.volume_unmounting
successors = [UnmountRoot]
def run(self, info):
info.volume.unmount_specials()
class DeleteMountDir(Task): class DeleteMountDir(Task):
description = 'Deleting mountpoint for the bootstrap volume' description = 'Deleting mountpoint for the bootstrap volume'
phase = phases.volume_unmounting phase = phases.volume_unmounting

View file

@ -1,18 +1,18 @@
{ {
"provider" : "virtualbox", "provider": "virtualbox",
"bootstrapper": { "bootstrapper": {
"workspace": "/target" "workspace": "/target"
}, },
"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",

View file

@ -2,7 +2,6 @@ from tasks import Snapshot
from tasks import CopyImage from tasks import CopyImage
from tasks import CreateFromSnapshot from tasks import CreateFromSnapshot
from tasks import CreateFromImage from tasks import CreateFromImage
from tasks import SetBootMountDir
from providers.ec2.tasks import ebs from providers.ec2.tasks import ebs
from common.tasks import loopback from common.tasks import loopback
from common.tasks import volume from common.tasks import volume
@ -27,16 +26,12 @@ def tasks(tasklist, manifest):
if 'snapshot' in settings and settings['snapshot'] is not None: if 'snapshot' in settings and settings['snapshot'] is not None:
tasklist.add(CreateFromSnapshot) tasklist.add(CreateFromSnapshot)
tasklist.remove(*skip_tasks) tasklist.remove(*skip_tasks)
if 'boot' in manifest.volume['partitions']:
tasklist.add(SetBootMountDir)
else: else:
tasklist.add(Snapshot) tasklist.add(Snapshot)
else: else:
if 'image' in settings and settings['image'] is not None: if 'image' in settings and settings['image'] is not None:
tasklist.add(CreateFromImage) tasklist.add(CreateFromImage)
tasklist.remove(*skip_tasks) tasklist.remove(*skip_tasks)
if 'boot' in manifest.volume['partitions']:
tasklist.add(SetBootMountDir)
else: else:
tasklist.add(CopyImage) tasklist.add(CopyImage)

View file

@ -14,7 +14,7 @@ log = logging.getLogger(__name__)
class Snapshot(Task): class Snapshot(Task):
description = 'Creating a snapshot of the bootstrapped volume' description = 'Creating a snapshot of the bootstrapped volume'
phase = phases.os_installation phase = phases.os_installation
predecessors = [bootstrap.Bootstrap, filesystem.MountSpecials] predecessors = [bootstrap.Bootstrap]
def run(self, info): def run(self, info):
def mk_snapshot(): def mk_snapshot():
@ -45,7 +45,7 @@ class CreateFromSnapshot(Task):
class CopyImage(Task): class CopyImage(Task):
description = 'Creating a snapshot of the bootstrapped volume' description = 'Creating a snapshot of the bootstrapped volume'
phase = phases.os_installation phase = phases.os_installation
predecessors = [bootstrap.Bootstrap, filesystem.MountSpecials] predecessors = [bootstrap.Bootstrap]
def run(self, info): def run(self, info):
loopback_backup_name = 'volume-{id:x}.{ext}.backup'.format(id=info.run_id, ext=info.volume.extension) loopback_backup_name = 'volume-{id:x}.{ext}.backup'.format(id=info.run_id, ext=info.volume.extension)
@ -71,16 +71,6 @@ class CreateFromImage(Task):
set_fs_states(info.volume) set_fs_states(info.volume)
class SetBootMountDir(Task):
description = 'Setting mountpoint for the boot partition'
phase = phases.volume_mounting
predecessors = [filesystem.MountRoot]
successors = [filesystem.MountBoot]
def run(self, info):
info.boot_dir = os.path.join(info.root, 'boot')
def set_fs_states(volume): def set_fs_states(volume):
volume.fsm.current = 'detached' volume.fsm.current = 'detached'

View file

@ -107,10 +107,8 @@ def rollback_tasks(tasklist, tasks_completed, manifest):
counter_task(partitioning.MapPartitions, partitioning.UnmapPartitions) counter_task(partitioning.MapPartitions, partitioning.UnmapPartitions)
counter_task(common_filesystem.CreateMountDir, common_filesystem.DeleteMountDir) counter_task(common_filesystem.CreateMountDir, common_filesystem.DeleteMountDir)
counter_task(common_filesystem.MountSpecials, common_filesystem.UnmountSpecials)
counter_task(common_filesystem.MountRoot, common_filesystem.UnmountRoot) counter_task(common_filesystem.MountRoot, common_filesystem.UnmountRoot)
counter_task(common_filesystem.MountBoot, common_filesystem.UnmountBoot)
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)
counter_task(ami.BundleImage, ami.RemoveBundle) counter_task(ami.BundleImage, ami.RemoveBundle)

View file

@ -74,7 +74,5 @@ 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.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)