On 22/10/2015 22:52, Jose Ricardo Ziviani wrote:
- 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(a)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]
You can remove this line
+ 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
+
I'd suggest to use a boolean parameter, for example, 'from_vg' to
represent that.
Also, from the libvirt documentation
(
https://libvirt.org/storage.html#StorageBackendLogical), the user just
needs to specify the VG name to create the pool from it.
So does it mean we can not create a logical pool from an existing VG
with a different name?
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: