[Kimchi-devel] [PATCH 1/2] Implement the backend to use existing VG as storage pool
Aline Manera
alinefm at linux.vnet.ibm.com
Tue Oct 27 17:13:36 UTC 2015
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 at 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:
More information about the Kimchi-devel
mailing list