Skip to content
Snippets Groups Projects
lvm.py 11.7 KiB
Newer Older
# -*- 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
        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
        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
        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]
        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')
        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,
                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())