
On 12/05/2015 11:58, CrÃstian Deives wrote:
Instead of storing the number of VMs which use each storage volume ('ref_cnt'), we should store the names of the VMs which use each volume ('used_by'). That new variable can provide the same information to the user while also providing more details.
Use the variable 'used_by' to store the names of the VMs which use a storage volume instead of 'ref_cnt' which stores the number of VMs which use a volume.
Signed-off-by: CrÃstian Deives <cristiandeives@gmail.com> --- docs/API.md | 4 +-- src/kimchi/control/storagevolumes.py | 2 +- src/kimchi/mockmodel.py | 4 +-- src/kimchi/model/diskutils.py | 18 +++++------ src/kimchi/model/storagevolumes.py | 22 +++++-------- src/kimchi/model/vms.py | 14 ++++++--- src/kimchi/model/vmstorages.py | 50 ++++++++++++++++-------------- tests/test_model_storagevolume.py | 2 +- tests/test_rest.py | 2 +- ui/js/src/kimchi.guest_storage_add.main.js | 2 +- ui/js/src/kimchi.storage_main.js | 2 +- 11 files changed, 60 insertions(+), 62 deletions(-)
diff --git a/docs/API.md b/docs/API.md index 62e4063..b5d34d7 100644 --- a/docs/API.md +++ b/docs/API.md @@ -509,9 +509,7 @@ A interface represents available network interface on VM. * os_distro *(optional)*: os distribution of the volume, for iso volume only. * os_version *(optional)*: os version of the volume, for iso volume only. * bootable *(optional)*: True if iso image is bootable and not corrupted. - * ref_cnt: Number of vms which used this volume, - 0 for volumes which are available for attachment. - >1 indicate number of vms used this volume. + * used_by: Name of vms which use this volume.
* **DELETE**: Remove the Storage Volume * **POST**: *See Storage Volume Actions* diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py index 9f5fcea..54fa263 100644 --- a/src/kimchi/control/storagevolumes.py +++ b/src/kimchi/control/storagevolumes.py @@ -56,7 +56,7 @@ class StorageVolume(Resource): 'capacity': self.info['capacity'], 'allocation': self.info['allocation'], 'path': self.info['path'], - 'ref_cnt': self.info['ref_cnt'], + 'used_by': self.info['used_by'], 'format': self.info['format']}
for key in ('os_version', 'os_distro', 'bootable', 'base'): diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index ae97354..ecfd5bc 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -455,13 +455,13 @@ class MockStorageVolumes(object): 'allocation': 512, 'type': 'block', 'path': base_path + '1', - 'ref_cnt': 0}, + 'used_by': []}, 'unit:0:0:2': {'capacity': 2048, 'format': 'unknown', 'allocation': 512, 'type': 'block', 'path': base_path + '2', - 'ref_cnt': 0}} + 'used_by': []}}
class MockPartitions(object): diff --git a/src/kimchi/model/diskutils.py b/src/kimchi/model/diskutils.py index 97fc9d9..566cc3a 100644 --- a/src/kimchi/model/diskutils.py +++ b/src/kimchi/model/diskutils.py @@ -29,14 +29,14 @@ from kimchi.xmlutils.disk import get_vm_disk_info, get_vm_disks """
-def get_disk_ref_cnt(objstore, conn, path): +def get_disk_used_by(objstore, conn, path): try: with objstore as session: try: - ref_cnt = session.get('storagevolume', path)['ref_cnt'] + used_by = session.get('storagevolume', path)['used_by'] except (KeyError, NotFoundError): kimchi_log.info('Volume %s not found in obj store.' % path) - ref_cnt = 0 + used_by = [] # try to find this volume in existing vm vms_list = VMsModel.get_vms(conn) for vm in vms_list: @@ -45,13 +45,13 @@ def get_disk_ref_cnt(objstore, conn, path): for disk in storages.keys(): d_info = get_vm_disk_info(dom, disk) if path == d_info['path']: - ref_cnt = ref_cnt + 1 + used_by.append(vm) try: session.store('storagevolume', path, - {'ref_cnt': ref_cnt}) + {'used_by': used_by}) except Exception as e: # Let the exception be raised. If we allow disks' - # ref_cnts to be out of sync, data corruption could + # used_by to be out of sync, data corruption could # occour if a disk is added to two guests # unknowingly. kimchi_log.error('Unable to store storage volume id in' @@ -64,12 +64,12 @@ def get_disk_ref_cnt(objstore, conn, path): # specially ones generated by 'session.store'. It is outside # to avoid conflict with the __exit__ function of 'with' raise OperationFailed('KCHVOL0017E', {'err': e.message}) - return ref_cnt + return used_by
-def set_disk_ref_cnt(objstore, path, new_count): +def set_disk_used_by(objstore, path, new_used_by): try: with objstore as session: - session.store('storagevolume', path, {'ref_cnt': new_count}) + session.store('storagevolume', path, {'used_by': new_used_by}) except Exception as e: raise OperationFailed('KCHVOL0017E', {'err': e.message}) diff --git a/src/kimchi/model/storagevolumes.py b/src/kimchi/model/storagevolumes.py index cf4611d..56525d1 100644 --- a/src/kimchi/model/storagevolumes.py +++ b/src/kimchi/model/storagevolumes.py @@ -31,7 +31,7 @@ from kimchi.config import READONLY_POOL_TYPE from kimchi.exception import InvalidOperation, InvalidParameter, IsoFormatError from kimchi.exception import MissingParameter, NotFoundError, OperationFailed from kimchi.isoinfo import IsoImage -from kimchi.model.diskutils import get_disk_ref_cnt +from kimchi.model.diskutils import get_disk_used_by, set_disk_used_by from kimchi.model.storagepools import StoragePoolModel from kimchi.model.tasks import TaskModel from kimchi.utils import add_task, get_next_clone_name, get_unique_file_name @@ -148,6 +148,7 @@ class StorageVolumesModel(object):
# Refresh to make sure volume can be found in following lookup StoragePoolModel.get_storagepool(pool_name, self.conn).refresh(0) + cb('OK', True)
def _create_volume_with_capacity(self, cb, params): @@ -186,15 +187,7 @@ class StorageVolumesModel(object): objstore=self.objstore).lookup(pool_name, name)
- try: - with self.objstore as session: - session.store('storagevolume', vol_info['path'], - {'ref_cnt': 0}) - except Exception as e: - # If the storage volume was created flawlessly, then lets hide this - # error to avoid more error in the VM creation process - kimchi_log.warning('Unable to store storage volume id in ' - 'objectstore due error: %s', e.message) + set_disk_used_by(self.objstore, vol_info['path'], [])
cb('', True)
@@ -339,12 +332,12 @@ class StorageVolumeModel(object): else: fmt = 'iso'
- ref_cnt = get_disk_ref_cnt(self.objstore, self.conn, path) + used_by = get_disk_used_by(self.objstore, self.conn, path) res = dict(type=VOLUME_TYPE_MAP[info[0]], capacity=info[1], allocation=info[2], path=path, - ref_cnt=ref_cnt, + used_by=used_by, format=fmt) if fmt == 'iso': if os.path.islink(path): @@ -484,10 +477,9 @@ class StorageVolumeModel(object): 'pool': orig_pool_name, 'err': e.get_error_message()})
+ new_vol = self.lookup(new_pool_name, new_vol_name) cb('adding volume to the object store') - new_vol_id = '%s:%s' % (new_pool_name, new_vol_name) - with self.objstore as session: - session.store('storagevolume', new_vol_id, {'ref_cnt': 0}) + set_disk_used_by(self.objstore, new_vol['path'], [])
cb('OK', True)
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index f0182b9..ed1500e 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -368,6 +368,8 @@ class VMModel(object): with self.objstore as session: session.delete('storagevolume', path)
+ domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0] + for i, path in enumerate(all_paths): try: vir_orig_vol = vir_conn.storageVolLookupByPath(path) @@ -376,7 +378,6 @@ class VMModel(object): orig_pool_name = vir_pool.name().decode('utf-8') orig_vol_name = vir_orig_vol.name().decode('utf-8') except libvirt.libvirtError, e: - domain_name = xpath_get_text(xml, XPATH_DOMAIN_NAME)[0] raise OperationFailed('KCHVM0035E', {'name': domain_name, 'err': e.message})
@@ -437,9 +438,10 @@ class VMModel(object): xml = xml_item_update(xml, XPATH_DOMAIN_DISK_BY_FILE % path, new_vol['path'], 'file')
- # set the new disk's ref_cnt + # set the new disk's used_by with self.objstore as session: - session.store('storagevolume', new_vol['path'], {'ref_cnt': 1}) + session.store('storagevolume', new_vol['path'], + {'used_by': [domain_name]}) rollback.prependDefer(_delete_disk_from_objstore, new_vol['path'])
# remove the new volume should an error occur later @@ -946,8 +948,10 @@ class VMModel(object): try: with self.objstore as session: if path in session.get_list('storagevolume'): - n = session.get('storagevolume', path)['ref_cnt'] - session.store('storagevolume', path, {'ref_cnt': n-1}) + used_by = session.get('storagevolume', path)['used_by'] + used_by.remove(name) + session.store('storagevolume', path, + {'used_by': used_by}) except Exception as e: raise OperationFailed('KCHVOL0017E', {'err': e.message})
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py index 87c6b3d..d3d4a8b 100644 --- a/src/kimchi/model/vmstorages.py +++ b/src/kimchi/model/vmstorages.py @@ -28,7 +28,7 @@ from kimchi.model.vms import DOM_STATE_MAP, VMModel from kimchi.model.storagevolumes import StorageVolumeModel from kimchi.model.utils import get_vm_config_flag from kimchi.osinfo import lookup -from kimchi.model.diskutils import get_disk_ref_cnt, set_disk_ref_cnt +from kimchi.model.diskutils import get_disk_used_by, set_disk_used_by from kimchi.utils import kimchi_log from kimchi.xmlutils.disk import get_device_node, get_disk_xml from kimchi.xmlutils.disk import get_vm_disk_info, get_vm_disks @@ -111,7 +111,7 @@ class VMStoragesModel(object): raise InvalidParameter("KCHVMSTOR0012E") except Exception as e: raise InvalidParameter("KCHVMSTOR0015E", {'error': e}) - if vol_info['ref_cnt'] != 0: + if len(vol_info['used_by']) != 0: raise InvalidParameter("KCHVMSTOR0016E")
valid_format = { @@ -142,11 +142,12 @@ class VMStoragesModel(object):
diff --git a/ui/js/src/kimchi.guest_storage_add.main.js b/ui/js/src/kimchi.guest_storage_add.main.js index 5df5625..768f1a5 100644 --- a/ui/js/src/kimchi.guest_storage_add.main.js +++ b/ui/js/src/kimchi.guest_storage_add.main.js @@ -81,7 +81,7 @@ kimchi.guest_storage_add_main = function() { if (result.length) { $.each(result, function(index, value) { // Only unused volume can be attached - if ((value.ref_cnt == 0) && (value.type != 'file' || validVolType[selectType].test(value.format))) { + if (value.type != 'file' || validVolType[selectType].test(value.format)) { options.push({ label: value.name, value: value.name
The UI code does not comply the backend code. I'd suggest to change the UI to use the used_by info instead of ref_cnt and in a separated patch allow user to attach a disk independent of the used_by value. But again, we need to warn the user when a volume in use is selected.