mirror of
https://github.com/kevingruesser/bootstrap-vz.git
synced 2025-08-24 15:36:27 +00:00
Implement partition mounts.
This solves quite a few challenges with mounting directories into root etc.
This commit is contained in:
parent
970cbfccf2
commit
2af0968156
11 changed files with 65 additions and 97 deletions
|
@ -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]
|
||||||
|
|
|
@ -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')
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
]
|
]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Reference in a new issue