mirror of
https://github.com/kevingruesser/bootstrap-vz.git
synced 2025-08-24 15:36:27 +00:00

String concatenation can at times be easier to read that format(). One should choose whichever approach is more readable.
128 lines
3.8 KiB
Python
128 lines
3.8 KiB
Python
from abc import ABCMeta
|
|
from abc import abstractmethod
|
|
from bootstrapvz.common.tools import log_check_call
|
|
from bootstrapvz.common.fsm_proxy import FSMProxy
|
|
from ..exceptions import PartitionError
|
|
|
|
|
|
class AbstractPartitionMap(FSMProxy):
|
|
"""Abstract representation of a partiton map
|
|
This class is a finite state machine and represents the state of the real partition map
|
|
"""
|
|
|
|
__metaclass__ = ABCMeta
|
|
|
|
# States the partition map can be in
|
|
events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'unmapped'},
|
|
{'name': 'map', 'src': 'unmapped', 'dst': 'mapped'},
|
|
{'name': 'unmap', 'src': 'mapped', 'dst': 'unmapped'},
|
|
]
|
|
|
|
def __init__(self, bootloader):
|
|
"""
|
|
Args:
|
|
bootloader (str): Name of the bootloader we will use for bootstrapping
|
|
"""
|
|
# Create the configuration for the state machine
|
|
cfg = {'initial': 'nonexistent', 'events': self.events, 'callbacks': {}}
|
|
super(AbstractPartitionMap, self).__init__(cfg)
|
|
|
|
def is_blocking(self):
|
|
"""Returns whether the partition map is blocking volume detach operations
|
|
|
|
Returns:
|
|
bool.
|
|
"""
|
|
return self.fsm.current == 'mapped'
|
|
|
|
def get_total_size(self):
|
|
"""Returns the total size the partitions occupy
|
|
|
|
Returns:
|
|
Bytes. The size of all the partitions
|
|
"""
|
|
# We just need the endpoint of the last partition
|
|
return self.partitions[-1].get_end()
|
|
|
|
def create(self, volume):
|
|
"""Creates the partition map
|
|
|
|
Args:
|
|
volume (Volume): The volume to create the partition map on
|
|
"""
|
|
self.fsm.create(volume=volume)
|
|
|
|
@abstractmethod
|
|
def _before_create(self, event):
|
|
pass
|
|
|
|
def map(self, volume):
|
|
"""Maps the partition map to device nodes
|
|
|
|
Args:
|
|
volume (Volume): The volume the partition map resides on
|
|
"""
|
|
self.fsm.map(volume=volume)
|
|
|
|
def _before_map(self, event):
|
|
"""
|
|
Raises:
|
|
PartitionError
|
|
"""
|
|
volume = event.volume
|
|
try:
|
|
# Ask kpartx how the partitions will be mapped before actually attaching them.
|
|
mappings = log_check_call(['kpartx', '-l', volume.device_path])
|
|
import re
|
|
regexp = re.compile('^(?P<name>.+[^\d](?P<p_idx>\d+)) : '
|
|
'(?P<start_blk>\d) (?P<num_blks>\d+) '
|
|
'{device_path} (?P<blk_offset>\d+)$'
|
|
.format(device_path=volume.device_path))
|
|
log_check_call(['kpartx', '-as', volume.device_path])
|
|
import os.path
|
|
# Run through the kpartx output and map the paths to the partitions
|
|
for mapping in mappings:
|
|
match = regexp.match(mapping)
|
|
if match is None:
|
|
raise PartitionError('Unable to parse kpartx output: ' + mapping)
|
|
partition_path = os.path.join('/dev/mapper', match.group('name'))
|
|
p_idx = int(match.group('p_idx')) - 1
|
|
self.partitions[p_idx].map(partition_path)
|
|
|
|
# Check if any partition was not mapped
|
|
for idx, partition in enumerate(self.partitions):
|
|
if partition.fsm.current not in ['mapped', 'formatted']:
|
|
raise PartitionError('kpartx did not map partition #' + str(idx + 1))
|
|
|
|
except PartitionError as e:
|
|
# Revert any mapping and reraise the error
|
|
for partition in self.partitions:
|
|
if not partition.fsm.can('unmap'):
|
|
partition.unmap()
|
|
log_check_call(['kpartx', '-ds', volume.device_path])
|
|
raise e
|
|
|
|
def unmap(self, volume):
|
|
"""Unmaps the partition
|
|
|
|
Args:
|
|
volume (Volume): The volume to unmap the partition map from
|
|
"""
|
|
self.fsm.unmap(volume=volume)
|
|
|
|
def _before_unmap(self, event):
|
|
"""
|
|
Raises:
|
|
PartitionError
|
|
"""
|
|
volume = event.volume
|
|
# Run through all partitions before unmapping and make sure they can all be unmapped
|
|
for partition in self.partitions:
|
|
if partition.fsm.cannot('unmap'):
|
|
msg = 'The partition {partition} prevents the unmap procedure'.format(partition=partition)
|
|
raise PartitionError(msg)
|
|
# Actually unmap the partitions
|
|
log_check_call(['kpartx', '-ds', volume.device_path])
|
|
# Call unmap on all partitions
|
|
for partition in self.partitions:
|
|
partition.unmap()
|