# -*- coding: utf-8 -*- import os from errors import LVMError from utils import Exec, RWLock class LVM(object): ''' ''' BIN_PATH = '/sbin' LVM = os.path.join(BIN_PATH, 'lvm') LVMDISKSCAN = os.path.join(BIN_PATH, 'lvmdiskscan') LVDISPLAY = os.path.join(BIN_PATH, 'lvdisplay') LVCREATE = os.path.join(BIN_PATH, 'lvcreate') LVCHANGE = os.path.join(BIN_PATH, 'lvchange') LVCONVERT = os.path.join(BIN_PATH, 'lvconvert') LVRENAME = os.path.join(BIN_PATH, 'lvrename') LVEXTEND = os.path.join(BIN_PATH, 'lvextend') LVREDUCE = os.path.join(BIN_PATH, 'lvreduce') LVREMOVE = os.path.join(BIN_PATH, 'lvremove') PVCREATE = os.path.join(BIN_PATH, 'pvcreate') PVREMOVE = os.path.join(BIN_PATH, 'pvremove') PVMOVE = os.path.join(BIN_PATH, 'pvmove') VGCREATE = os.path.join(BIN_PATH, 'vgcreate') VGCHANGE = os.path.join(BIN_PATH, 'vgchange') VGEXTEND = os.path.join(BIN_PATH, 'vgextend') VGREDUCE = os.path.join(BIN_PATH, 'vgreduce') VGREMOVE = os.path.join(BIN_PATH, 'vgremove') DMSETUP = os.path.join(BIN_PATH, 'dmsetup') # global class lock : do not abuse LVM/DM too much _mutex = RWLock() def __init__(self): ''' ''' self.reload() """ FIXME not working anymore def __str__(self): ''' ''' s = '' for vgname, vg in self._vgs.iteritems(): s += 'VG %s (total: %s, allocated: %s, free: %s)\n' % (vgname, vg.total, vg.allocated, vg.free) s += ' * PV(s):\n' for pv in vg.pvs: s += (' %s (total: %s, allocated: %s, free: %s, format: %s)' '\n' % (pv.name, pv.total, pv.allocated, pv.free, pv.format)) s += ' * LV(s):\n' for lv in vg.lvs: s += ' %s (path: %s, size: %s)' % (lv.name, lv.path, lv.total) return s """ def _reload_pvs(self): ''' ''' with self._mutex.write: self._pvs = {} # execute query command (rc, output) = Exec.call([self.LVM, 'pvs', '--nosuffix', '--noheadings', '--units', 'b', '--separator', '~', '-o', ('pv_uuid,pv_name,vg_name,pv_fmt,' 'pv_attr,dev_size,pv_size,pv_free,' 'pv_used,pv_pe_count,' 'pv_pe_alloc_count')], merge_output = False) if rc != 0: raise LVMError('error getting list of PV') # parse command output for line in output[0].splitlines(): # extract tokens ( p_uuid, p_name, p_vg, p_format, p_attr, p_devsize, p_size, p_free, p_used, p_extent, p_ext_alloc, ) = line.strip().split('~') # if the PV is not used, set VG to None if not len(p_vg): p_vg = None else: p_vg_obj = self._vgs[p_vg] # create PV object pv = self._PV(self, p_vg_obj, p_uuid, p_name, p_format, p_attr, int(p_devsize), int(p_size), int(p_free), int(p_used), int(p_extent), int(p_ext_alloc)) # register obj self._pvs[p_name] = pv if p_vg: self._vgs[p_vg]._pvs[p_name] = pv def _reload_vgs(self): ''' ''' with self._mutex.write: self._vgs = {} # execute query command (rc, output) = Exec.call([self.LVM, 'vgs', '--nosuffix', '--noheadings', '--units', 'b', '--separator', '~', '-o', ('vg_uuid,vg_name,vg_fmt,vg_attr,' 'vg_size,vg_free,vg_extent_size,' 'vg_extent_count,vg_free_count')], merge_output = False) if rc != 0: raise LVMError('error getting list of VG') # parse command output for line in output[0].splitlines(): # extract tokens ( v_uuid, v_name, v_format, v_attr, v_size, v_free, v_ext_size, v_extent, v_ext_free, ) = line.strip().split('~') # create VG object vg = self._VG(self, v_uuid, v_name, v_format, v_attr, int(v_size), int(v_free), int(v_ext_size), int(v_extent), int(v_ext_free)) # register object self._vgs[v_name] = vg def _reload_lvs(self): ''' ''' with self._mutex.write: # execute query command (rc, output) = Exec.call([self.LVM, 'lvs', '--nosuffix', '--noheadings', '--units', 'b', '--separator', '~', '-o', ('lv_uuid,lv_name,vg_name,lv_attr,' 'lv_size')], merge_output = False) if rc != 0: raise LVMError('error getting list of LV') # parse command output for line in output[0].splitlines(): # extract tokens ( l_uuid, l_name, l_vg, l_attr, l_size, ) = line.strip().split('~') # create LV object lv = self._LV(self, self._vgs[l_vg], l_uuid, l_name, l_attr, l_size) # register object self._vgs[l_vg]._lvs[l_name] = lv def reload(self): # VG must be reloaded before PV self._reload_vgs() self._reload_pvs() self._reload_lvs() def get_pv(self, path): ''' ''' with self._mutex.read: return self._pvs[path] def get_vg(self, name): ''' ''' with self._mutex.read: return self._vgs[name] def get_lv(self, vgname, name): ''' ''' with self._mutex.read: return self._vgs[vgname]._lvs[name] def dm_create(self, name, table): ''' ''' with self._mutex.write: rc = Exec.silent([self.DMSETUP, 'create', name], input=table) if rc: raise LVMError('failed to create DM device') def dm_remove(self, name, force=True): ''' ''' with self._mutex.write: # build command arguments argv = [self.DMSETUP, 'remove'] if force is True: argv.append('--force') argv.append(name) # execute command if Exec.silent(argv): raise LVMError('failed to remove DM') def dm_get_table(self, name): ''' ''' with self._mutex.read: rc, output = Exec.call([self.DMSETUP, 'table', '--showkeys', name], merge_output=False) table = str(output[0]).strip() if rc or not len(table): raise LVMError('failed to get DM table') return table def dm_set_table(self, name, table): ''' ''' with self._mutex.write: rc = Exec.silent([self.DMSETUP, 'load', name], input=table) if rc: raise LVMError('failed to change DM table') class _Vol(object): ''' ''' def __init__(self, mngr, vg, uuid, name, attrs): self._mngr = mngr self.vg = vg self.uuid = str(uuid) self.name = str(name) self.attrs = str(attrs) class _PV(_Vol): ''' ''' def __init__(self, mngr, vg, uuid, name, format, attrs, device_size, size, free, used, extent_total, extent_used): super(LVM._PV, self).__init__(mngr, vg, uuid, name, attrs) self.format = str(format) self.device_size = int(device_size) self.size = int(size) self.free = int(free) self.used = int(used) self.extent_total = int(extent_total) self.extent_used = int(extent_used) class _VG(_Vol): ''' ''' def __init__(self, mngr, uuid, name, format, attrs, size, free, extent_size, extent_total, extent_free): super(LVM._VG, self).__init__(mngr, self, uuid, name, attrs) self._pvs = {} self._lvs = {} self.format = str(format) self.size = int(size) self.free = int(free) self.extent_size = int(extent_size) self.extent_total = int(extent_total) self.extent_free = int(extent_free) def create(self, name, size): ''' ''' # execute create command if Exec.silent([LVM.LVCREATE, '-L', '%iB' % size, '-n', name, self.name]) != 0: raise LVMError('error creating LV') def remove(self, name): ''' ''' # execute create command if Exec.silent([LVM.LVREMOVE, '-f', '%s/%s' % (self.name, name)]) != 0: raise LVMError('error removing LV') def get_lv(self, name): ''' ''' with self._mngr._mutex.read: return self._lvs[name] class _LV(_Vol): ''' ''' def __init__(self, mngr, vg, uuid, name, attrs, size): super(LVM._LV, self).__init__(mngr, vg, uuid, name, attrs) self.size = int(size) #fixme self.path = '/dev/%s/%s' % (self.vg.name, self.name) def dm_name(self): ''' ''' return '%s-%s' % (self.vg.name.replace('-', '--'), self.name.replace('-', '--')) def dm_table(self): ''' ''' return self._mngr.dm_get_table(self.dm_name())