diff options
Diffstat (limited to 'anate/devices.py')
-rw-r--r-- | anate/devices.py | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/anate/devices.py b/anate/devices.py new file mode 100644 index 0000000..cdc845e --- /dev/null +++ b/anate/devices.py @@ -0,0 +1,234 @@ +import logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger('anate.devices') + +import os +import stat +import pwd +import grp +import tempfile +from baseutils import Subprocess + + +def getDeviceNumbers(path): + try: + statinfo = os.stat(path) + except OSError: + return (None, None) + + return (os.major(statinfo.st_rdev), os.minor(statinfo.st_rdev)) + + +class BlockDeviceNode(object): + def __init__(self, path, major, minor): + self._path = path + self._major = major + self._minor = minor + + def create(self, mode=0660, owner='root', group='disk'): + try: + os.mknod(self.path, mode | stat.S_IFBLK, os.makedev(self.major, self.minor)) + except OSError: + logger.error('unable to create node %s', self.path) + return + + try: + uid = pwd.getpwnam(owner)[2] + except KeyError: + logger.warning('user %s does not exist, not changing the owner', owner) + uid = -1 + + try: + gid = grp.getgrnam(group)[2] + except KeyError: + logger.warning('group %s does not exist, not changing the group', group) + gid = -1 + + try: + os.chown(self.path, uid, gid) + except OSError: + logger.warning('unable to change the owner and group of node %s', self.path) + + def unlink(self): + try: + os.unlink(self.path) + except OSError: + logger.error('unable to unlink node %s', self.path) + + @property + def path(self): + return self._path + + @property + def major(self): + return self._major + + @major.setter + def major(self, value): + self._major = value + + @property + def minor(self): + return self._minor + + @minor.setter + def minor(self, value): + self._minor = value + + @property + def available(self): + return os.access(self.path, os.R_OK | os.W_OK) + + +class BlockDevice(object): + def __init__(self, name, node): + if not isinstance(node, BlockDeviceNode): + raise TypeError, 'node must be an instance of BlockDeviceNode, got %s' % type(node) + + self._name = name + self._node = node + self._active = False + + def initialize(self): + self._active = True + + def destroy(self): + self._active = False + + @property + def name(self): + return self._name + + @property + def path(self): + return self._node.path + + @property + def major(self): + return self._node.major + + @property + def minor(self): + return self._node.minor + + @property + def active(self): + return self._active + + +class StorageDevice(BlockDevice): + def __init__(self, name, node, size=0): + BlockDevice.__init__(self, name, node) + self._size = size + + @property + def size(self): + return self._size + + +class LoopDevice(StorageDevice): + def __init__(self, name, node, destination_file, size): + StorageDevice.__init__(self, name, node, size) + self._file = destination_file + + def initialize(self): + proc = Subprocess('dd', 'if=/dev/zero', 'of=%s' % self._file, 'bs=1k', 'seek=%d' % (self.size * 1024 - 1), 'count=1') + proc.run() + if proc.rc: + logger.error('%s', proc.stderr) + raise OSError, 'dd failed when creating file %s' % self._file + + proc = Subprocess('losetup', self.path, self._file) + proc.run() + if proc.rc: + logger.error('%s', proc.stderr) + raise OSError, 'losetup failed when setting up loop device %s' % self.path + + self._active = True + + def destroy(self): + proc = Subprocess('losetup', '-d', self.path) + proc.run() + if proc.rc: + logger.error('%s', proc.stderr) + raise OSError, 'losetup failed when removing loop device %s' % self.path + + os.unlink(self._file) + + self._active = False + + +class DMDevice(StorageDevice): + + _DM_DEVICES_ROOT_PATH = '/dev/mapper/' + _SECTORS_PER_MB = 2048 + + def __init__(self, name, destination_device, size=None): + if not isinstance(destination_device, StorageDevice): + raise TypeError, 'destination_device must be an instance of StorageDevice, got %s' % type(destination_device) + if not destination_device.active: + destination_device.initialize() + + usable_size = destination_device.size + if not size: + size = usable_size + elif size > usable_size: + logger.warning('creating dm device which is bigger than the destination device') + else: + usable_size = size + + node = BlockDeviceNode(os.path.join(self._DM_DEVICES_ROOT_PATH, name), major=None, minor=None) + + StorageDevice.__init__(self, name, node, size) + self._destination_device = destination_device + self._usable_size = usable_size + + def initialize(self): + table_file = tempfile.NamedTemporaryFile() + + map = {'logical_start_sector': 0, + 'num_sectors': self.usable_size * self._SECTORS_PER_MB, + 'target_type': 'linear', + 'destination_device': self._destination_device.path, + 'destination_device_start_sector': 0} + string = '%(logical_start_sector)d %(num_sectors)d %(target_type)s %(destination_device)s %(destination_device_start_sector)d\n' % map + table_file.write(string) + table_file.flush() + logger.info('table_file: %s', string.strip()) + + if self.size > self.usable_size: + map = {'logical_start_sector': self.usable_size * self._SECTORS_PER_MB, + 'num_sectors': (self.size - self.usable_size) * self._SECTORS_PER_MB, + 'target_type': 'zero'} + string = '%(logical_start_sector)d %(num_sectors)d %(target_type)s\n' % map + table_file.write(string) + table_file.flush() + logger.info('table_file: %s', string.strip()) + + proc = Subprocess('dmsetup', 'create', self.name, table_file.name) + proc.run() + + table_file.close() + + if proc.rc: + logger.error('%s', proc.stderr) + raise OSError, 'dmsetup failed when setting up dm device %s' % self.name + + major, minor = getDeviceNumbers(self._node.path) + self._node.major = major + self._node.minor = minor + + self._active = True + + def destroy(self): + proc = Subprocess('dmsetup', 'remove', '--force', self.name) + proc.run() + if proc.rc: + logger.error('%s', proc.stderr) + raise OSError, 'dmsetup failed when removing dm device %s' % self.name + + self._active = False + + @property + def usable_size(self): + return self._usable_size |