2013-09-15 13:19:45 +02:00
|
|
|
from abc import ABCMeta
|
2014-01-19 01:02:29 +01:00
|
|
|
from abc import abstractmethod
|
2015-01-19 23:11:19 +01:00
|
|
|
from bootstrapvz.common.sectors import Sectors
|
2014-03-23 23:12:07 +01:00
|
|
|
from bootstrapvz.common.tools import log_check_call
|
|
|
|
from bootstrapvz.common.fsm_proxy import FSMProxy
|
2013-09-15 13:19:45 +02:00
|
|
|
|
|
|
|
|
2013-09-22 21:20:09 +02:00
|
|
|
class AbstractPartition(FSMProxy):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Abstract representation of a partiton
|
|
|
|
This class is a finite state machine and represents the state of the real partition
|
|
|
|
"""
|
2013-09-15 13:19:45 +02:00
|
|
|
|
|
|
|
__metaclass__ = ABCMeta
|
|
|
|
|
2014-03-23 16:04:03 +01:00
|
|
|
# Our states
|
2013-09-15 13:19:45 +02:00
|
|
|
events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'created'},
|
|
|
|
{'name': 'format', 'src': 'created', 'dst': 'formatted'},
|
|
|
|
{'name': 'mount', 'src': 'formatted', 'dst': 'mounted'},
|
|
|
|
{'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'},
|
|
|
|
]
|
|
|
|
|
2014-02-23 17:52:05 +01:00
|
|
|
def __init__(self, size, filesystem, format_command):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""
|
2014-05-04 19:31:53 +02:00
|
|
|
:param Bytes size: Size of the partition
|
|
|
|
:param str filesystem: Filesystem the partition should be formatted with
|
|
|
|
:param list format_command: Optional format command, valid variables are fs, device_path and size
|
2014-03-23 16:04:03 +01:00
|
|
|
"""
|
2014-02-23 17:52:05 +01:00
|
|
|
self.size = size
|
|
|
|
self.filesystem = filesystem
|
|
|
|
self.format_command = format_command
|
2015-01-19 23:11:19 +01:00
|
|
|
# Initialize the start & end padding to 0 sectors, may be changed later
|
|
|
|
self.pad_start = Sectors(0, size.sector_size)
|
|
|
|
self.pad_end = Sectors(0, size.sector_size)
|
2014-03-23 16:04:03 +01:00
|
|
|
# Path to the partition
|
2014-02-23 17:52:05 +01:00
|
|
|
self.device_path = None
|
2014-03-23 16:04:03 +01:00
|
|
|
# Dictionary with mount points as keys and Mount objects as values
|
2014-02-23 17:52:05 +01:00
|
|
|
self.mounts = {}
|
2013-09-15 13:19:45 +02:00
|
|
|
|
2014-03-23 16:04:03 +01:00
|
|
|
# Create the configuration for our state machine
|
2013-09-22 21:20:09 +02:00
|
|
|
cfg = {'initial': 'nonexistent', 'events': self.events, 'callbacks': {}}
|
|
|
|
super(AbstractPartition, self).__init__(cfg)
|
2013-09-15 13:19:45 +02:00
|
|
|
|
|
|
|
def get_uuid(self):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Gets the UUID of the partition
|
|
|
|
|
2014-05-04 19:31:53 +02:00
|
|
|
:return: The UUID of the partition
|
|
|
|
:rtype: str
|
2014-03-23 16:04:03 +01:00
|
|
|
"""
|
2014-02-23 22:16:10 +01:00
|
|
|
[uuid] = log_check_call(['blkid', '-s', 'UUID', '-o', 'value', self.device_path])
|
2013-09-15 13:19:45 +02:00
|
|
|
return uuid
|
|
|
|
|
2014-01-19 01:02:29 +01:00
|
|
|
@abstractmethod
|
|
|
|
def get_start(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get_end(self):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Gets the end of the partition
|
|
|
|
|
2014-05-04 19:31:53 +02:00
|
|
|
:return: The end of the partition
|
2015-01-03 12:44:57 +01:00
|
|
|
:rtype: Sectors
|
2014-03-23 16:04:03 +01:00
|
|
|
"""
|
2015-01-19 23:11:19 +01:00
|
|
|
return self.get_start() + self.pad_start + self.size + self.pad_end
|
2014-01-19 01:02:29 +01:00
|
|
|
|
2013-09-22 21:20:09 +02:00
|
|
|
def _before_format(self, e):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Formats the partition
|
|
|
|
"""
|
|
|
|
# If there is no explicit format_command define we simply call mkfs.fstype
|
2014-02-23 17:52:05 +01:00
|
|
|
if self.format_command is None:
|
2014-02-23 22:16:10 +01:00
|
|
|
format_command = ['mkfs.{fs}', '{device_path}']
|
2014-02-23 17:52:05 +01:00
|
|
|
else:
|
|
|
|
format_command = self.format_command
|
|
|
|
variables = {'fs': self.filesystem,
|
|
|
|
'device_path': self.device_path,
|
|
|
|
'size': self.size,
|
|
|
|
}
|
|
|
|
command = map(lambda part: part.format(**variables), format_command)
|
2014-03-23 16:04:03 +01:00
|
|
|
# Format the partition
|
2014-02-23 17:52:05 +01:00
|
|
|
log_check_call(command)
|
2013-09-15 13:19:45 +02:00
|
|
|
|
2013-09-22 21:20:09 +02:00
|
|
|
def _before_mount(self, e):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Mount the partition
|
|
|
|
"""
|
2014-02-23 22:16:10 +01:00
|
|
|
log_check_call(['mount', '--types', self.filesystem, self.device_path, e.destination])
|
2013-09-15 13:19:45 +02:00
|
|
|
self.mount_dir = e.destination
|
|
|
|
|
2013-11-27 18:06:45 +01:00
|
|
|
def _after_mount(self, e):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Mount any mounts associated with this partition
|
|
|
|
"""
|
|
|
|
# Make sure we mount in ascending order of mountpoint path length
|
|
|
|
# This ensures that we don't mount /dev/pts before we mount /dev
|
2013-11-27 18:06:45 +01:00
|
|
|
for destination in sorted(self.mounts.iterkeys(), key=len):
|
|
|
|
self.mounts[destination].mount(self.mount_dir)
|
|
|
|
|
2013-09-22 21:20:09 +02:00
|
|
|
def _before_unmount(self, e):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Unmount any mounts associated with this partition
|
|
|
|
"""
|
|
|
|
# Unmount the mounts in descending order of mounpoint path length
|
|
|
|
# You cannot unmount /dev before you have unmounted /dev/pts
|
2013-11-27 18:06:45 +01:00
|
|
|
for destination in sorted(self.mounts.iterkeys(), key=len, reverse=True):
|
|
|
|
self.mounts[destination].unmount()
|
2014-02-23 22:16:10 +01:00
|
|
|
log_check_call(['umount', self.mount_dir])
|
2013-09-15 13:19:45 +02:00
|
|
|
del self.mount_dir
|
2013-11-27 18:06:45 +01:00
|
|
|
|
|
|
|
def add_mount(self, source, destination, opts=[]):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Associate a mount with this partition
|
|
|
|
Automatically mounts it
|
|
|
|
|
2014-05-04 19:31:53 +02:00
|
|
|
:param str,AbstractPartition source: The source of the mount
|
|
|
|
:param str destination: The path to the mountpoint
|
|
|
|
:param list opts: Any options that should be passed to the mount command
|
2014-03-23 16:04:03 +01:00
|
|
|
"""
|
|
|
|
# Create a new mount object, mount it if the partition is mounted and put it in the mounts dict
|
2014-12-19 01:43:10 +01:00
|
|
|
from mount import Mount
|
|
|
|
mount = Mount(source, destination, opts)
|
2013-11-27 18:06:45 +01:00
|
|
|
if self.fsm.current == 'mounted':
|
|
|
|
mount.mount(self.mount_dir)
|
|
|
|
self.mounts[destination] = mount
|
|
|
|
|
|
|
|
def remove_mount(self, destination):
|
2014-03-23 16:04:03 +01:00
|
|
|
"""Remove a mount from this partition
|
|
|
|
Automatically unmounts it
|
|
|
|
|
2014-05-04 19:31:53 +02:00
|
|
|
:param str destination: The mountpoint path of the mount that should be removed
|
2014-03-23 16:04:03 +01:00
|
|
|
"""
|
|
|
|
# Unmount the mount if the partition is mounted and delete it from the mounts dict
|
|
|
|
# If the mount is already unmounted and the source is a partition, this will raise an exception
|
2013-11-27 18:06:45 +01:00
|
|
|
if self.fsm.current == 'mounted':
|
|
|
|
self.mounts[destination].unmount()
|
|
|
|
del self.mounts[destination]
|