
- This commit implements the backend for users to create a storage pool using an existing backend in the system. Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- src/wok/plugins/kimchi/control/host.py | 22 +++++ src/wok/plugins/kimchi/disks.py | 123 +++++++++++++++++++++++++++ src/wok/plugins/kimchi/i18n.py | 3 + src/wok/plugins/kimchi/model/host.py | 30 +++++++ src/wok/plugins/kimchi/model/storagepools.py | 7 +- 5 files changed, 184 insertions(+), 1 deletion(-) diff --git a/src/wok/plugins/kimchi/control/host.py b/src/wok/plugins/kimchi/control/host.py index 9fe4c0a..1fa236e 100644 --- a/src/wok/plugins/kimchi/control/host.py +++ b/src/wok/plugins/kimchi/control/host.py @@ -42,6 +42,28 @@ class Host(Resource): self.swupdate = self.generate_action_handler_task('swupdate') self.swupdateprogress = SoftwareUpdateProgress(self.model) self.cpuinfo = CPUInfo(self.model) + self.vgs = VolumeGroups(self.model) + + @property + def data(self): + return self.info + + +class VolumeGroups(Collection): + def __init__(self, model): + super(VolumeGroups, self).__init__(model) + self.role_key = 'host' + self.uri_fmt = "/host/vgs" + self.admin_methods = ['GET'] + self.resource = VolumeGroup + + +class VolumeGroup(Resource): + def __init__(self, model, id=None): + super(VolumeGroup, self).__init__(model, id) + self.role_key = 'host' + self.uri_fmt = "/host/vgs/%s" + self.admin_methods = ['GET'] @property def data(self): diff --git a/src/wok/plugins/kimchi/disks.py b/src/wok/plugins/kimchi/disks.py index eb40e3a..30a52b9 100644 --- a/src/wok/plugins/kimchi/disks.py +++ b/src/wok/plugins/kimchi/disks.py @@ -194,3 +194,126 @@ def get_partition_details(name): dev['path'] = dev_path dev['name'] = name return dev + + +def vgs(): + """ + lists all volume groups in the system. All size units are in bytes. + + [{'vgname': 'vgtest', 'size': 999653638144L, 'free': 0}] + """ + cmd = ['vgs', + '--units', + 'b', + '--nosuffix', + '--noheading', + '--unbuffered', + '--options', + 'vg_name,vg_size,vg_free'] + + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + out, err = proc.communicate() + + if proc.returncode != 0: + raise OperationFailed("KCHDISKS0003E", {'err': err}) + + if not out: + return [] + + # remove blank spaces and create a list of VGs + vgs = map(lambda v: v.strip(), out.strip('\n').split('\n')) + + # create a dict based on data retrieved from vgs + return map(lambda l: {'vgname': l[0], + 'size': long(l[1]), + 'free': long(l[2])}, + [fields.split() for fields in vgs]) + + +def lvs(vgname=None): + """ + lists all logical volumes found in the system. It can be filtered by + the volume group. All size units are in bytes. + + [{'lvname': 'lva', 'path': '/dev/vgtest/lva', 'size': 12345L}, + {'lvname': 'lvb', 'path': '/dev/vgtest/lvb', 'size': 12345L}] + """ + cmd = ['lvs', + '--units', + 'b', + '--nosuffix', + '--noheading', + '--unbuffered', + '--options', + 'lv_name,lv_path,lv_size,vg_name'] + + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + out, err = proc.communicate() + + if proc.returncode != 0: + raise OperationFailed("KCHDISKS0003E", {'err': err}) + + if not out: + return [] + + # remove blank spaces and create a list of LVs filtered by vgname, if + # provided + lvs = filter(lambda f: vgname is None or vgname in f, + map(lambda v: v.strip(), out.strip('\n').split('\n'))) + + # create a dict based on data retrieved from lvs + return map(lambda l: {'lvname': l[0], + 'path': l[1], + 'size': long(l[2])}, + [fields.split() for fields in lvs]) + + +def pvs(vgname=None): + """ + lists all physical volumes in the system. It can be filtered by the + volume group. All size units are in bytes. + + [{'pvname': '/dev/sda3', + 'size': 469502001152L, + 'uuid': 'kkon5B-vnFI-eKHn-I5cG-Hj0C-uGx0-xqZrXI'}, + {'pvname': '/dev/sda2', + 'size': 21470642176L, + 'uuid': 'CyBzhK-cQFl-gWqr-fyWC-A50Y-LMxu-iHiJq4'}] + """ + cmd = ['pvs', + '--units', + 'b', + '--nosuffix', + '--noheading', + '--unbuffered', + '--options', + 'pv_name,pv_size,pv_uuid,vg_name'] + + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + out, err = proc.communicate() + + if proc.returncode != 0: + raise OperationFailed("KCHDISKS0003E", {'err': err}) + + if not out: + return [] + + # remove blank spaces and create a list of PVs filtered by vgname, if + # provided + pvs = filter(lambda f: vgname is None or vgname in f, + map(lambda v: v.strip(), out.strip('\n').split('\n'))) + + # create a dict based on data retrieved from pvs + return map(lambda l: {'pvname': l[0], + 'size': long(l[1]), + 'uuid': l[2]}, + [fields.split() for fields in pvs]) diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py index e9be6af..cc55a3a 100644 --- a/src/wok/plugins/kimchi/i18n.py +++ b/src/wok/plugins/kimchi/i18n.py @@ -34,6 +34,7 @@ messages = { "KCHDISKS0001E": _("Error while getting block devices. Details: %(err)s"), "KCHDISKS0002E": _("Error while getting block device information for %(device)s."), + "KCHDISKS0003E": _("Unable to retrieve LVM information. Details: %(err)s"), "KCHDL0001E": _("Unable to find distro file: %(filename)s"), "KCHDL0002E": _("Unable to parse distro file: %(filename)s. Make sure, it is a JSON file."), @@ -334,4 +335,6 @@ messages = { "KCHCPUINF0002E": _("Invalid vCPU/topology combination."), "KCHCPUINF0003E": _("This host (or current configuration) does not allow CPU topology."), + "KCHLVMS0001E": _("Invalid volume group name parameter: %(name)s."), + } diff --git a/src/wok/plugins/kimchi/model/host.py b/src/wok/plugins/kimchi/model/host.py index 9b1fc32..6705ae7 100644 --- a/src/wok/plugins/kimchi/model/host.py +++ b/src/wok/plugins/kimchi/model/host.py @@ -293,6 +293,36 @@ class HostStatsModel(object): self.host_stats['net_sent_bytes'].append(sent_bytes) +class VolumeGroupsModel(object): + def __init__(self, **kargs): + pass + + def get_list(self): + if not disks.vgs(): + return [] + #return [vg['vgname'] for vg in disks.vgs() if vg['free'] > 0L] + return [vg['vgname'] for vg in disks.vgs()] + + +class VolumeGroupModel(object): + def __init__(self, **kargs): + pass + + def lookup(self, name): + def _format(vg): + return {'vgname': vg['vgname'], + 'size': vg['size'], + 'free': vg['free'], + 'pvs': [pv['pvname'] for pv in disks.pvs(vg['vgname'])], + 'lvs': [lv['lvname'] for lv in disks.lvs(vg['vgname'])]} + + vgs = [_format(vg) for vg in disks.vgs() if vg['vgname'] == name] + if not vgs: + raise InvalidParameter("KCHLVMS0001E", {'name': name}) + + return vgs[0] + + class HostStatsHistoryModel(object): def __init__(self, **kargs): self.history = HostStatsModel(**kargs) diff --git a/src/wok/plugins/kimchi/model/storagepools.py b/src/wok/plugins/kimchi/model/storagepools.py index db68252..07a9524 100644 --- a/src/wok/plugins/kimchi/model/storagepools.py +++ b/src/wok/plugins/kimchi/model/storagepools.py @@ -144,6 +144,11 @@ class StoragePoolsModel(object): def create(self, params): task_id = None conn = self.conn.get() + + use_existing_vg = False + if 'vgselect' in params and params['vgselect'] == 'existingvg': + use_existing_vg = True + try: name = params['name'] if name == ISO_POOL_NAME: @@ -152,7 +157,7 @@ class StoragePoolsModel(object): # The user may want to create a logical pool with the same name # used before but a volume group will already exist with this name # So check the volume group does not exist to create the pool - if params['type'] == 'logical': + if params['type'] == 'logical' and not use_existing_vg: vgdisplay_cmd = ['vgdisplay', name.encode('utf-8')] output, error, returncode = run_command(vgdisplay_cmd) # From vgdisplay error codes: -- 1.9.1