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(a)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 +-
9 files changed, 58 insertions(+), 60 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index 922ded1..bbd5eb3 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -502,9 +502,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 1d575cb..3734633 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 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):
raise OperationFailed("KCHVMSTOR0008E", {'error':
e.message})
# Don't put a try-block here. Let the exception be raised. If we
- # allow disks ref_cnts to be out of sync, data corruption could
+ # allow disks used_by to be out of sync, data corruption could
# occour if a disk is added to two guests unknowingly.
if params.get('vol'):
- set_disk_ref_cnt(self.objstore, params['path'],
- vol_info['ref_cnt'] + 1)
+ used_by = vol_info['used_by']
+ used_by.append(vm_name)
+ set_disk_used_by(self.objstore, params['path'], used_by)
return dev
@@ -186,26 +187,27 @@ class VMStorageModel(object):
path = self.lookup(vm_name, dev_name)['path']
# This has to be done before it's detached. If it wasn't
# in the obj store, its ref count would have been updated
- # by get_disk_ref_cnt()
+ # by get_disk_used_by()
if path is not None:
- ref_cnt = get_disk_ref_cnt(self.objstore, self.conn, path)
+ used_by = get_disk_used_by(self.objstore, self.conn, path)
else:
- kimchi_log.error("Unable to decrement volume ref_cnt on"
+ kimchi_log.error("Unable to decrement volume used_by on"
" delete because no path could be found.")
dom.detachDeviceFlags(etree.tostring(disk),
get_vm_config_flag(dom, 'all'))
except Exception as e:
raise OperationFailed("KCHVMSTOR0010E", {'error':
e.message})
- if ref_cnt is not None and ref_cnt > 0:
- set_disk_ref_cnt(self.objstore, path, ref_cnt - 1)
+ if used_by is not None and vm_name in used_by:
+ used_by.remove(vm_name)
+ set_disk_used_by(self.objstore, path, used_by)
else:
- kimchi_log.error("Unable to decrement %s:%s ref_cnt on delete."
+ kimchi_log.error("Unable to update %s:%s used_by on delete."
% (vm_name, dev_name))
def update(self, vm_name, dev_name, params):
- old_disk_ref_cnt = None
- new_disk_ref_cnt = None
+ old_disk_used_by = None
+ new_disk_used_by = None
dom = VMModel.get_vm(vm_name, self.conn)
@@ -219,10 +221,10 @@ class VMStorageModel(object):
if new_disk_path != old_disk_path:
# An empty path means a CD-ROM was empty or ejected:
if old_disk_path is not '':
- old_disk_ref_cnt = get_disk_ref_cnt(
+ old_disk_used_by = get_disk_used_by(
self.objstore, self.conn, old_disk_path)
if new_disk_path is not '':
- new_disk_ref_cnt = get_disk_ref_cnt(
+ new_disk_used_by = get_disk_used_by(
self.objstore, self.conn, new_disk_path)
dev_info.update(params)
@@ -234,14 +236,16 @@ class VMStorageModel(object):
raise OperationFailed("KCHVMSTOR0009E", {'error':
e.message})
try:
- if old_disk_ref_cnt is not None and \
- old_disk_ref_cnt > 0:
- set_disk_ref_cnt(self.objstore, old_disk_path,
- old_disk_ref_cnt - 1)
- if new_disk_ref_cnt is not None:
- set_disk_ref_cnt(self.objstore, new_disk_path,
- new_disk_ref_cnt + 1)
+ if old_disk_used_by is not None and \
+ vm_name in old_disk_used_by:
+ old_disk_used_by.remove(vm_name)
+ set_disk_used_by(self.objstore, old_disk_path,
+ old_disk_used_by)
+ if new_disk_used_by is not None:
+ new_disk_used_by.append(vm_name)
+ set_disk_used_by(self.objstore, new_disk_path,
+ new_disk_used_by)
except Exception as e:
- kimchi_log.error("Unable to update dev ref_cnt on update due to"
+ kimchi_log.error("Unable to update dev used_by on update due to"
" %s:" % e.message)
return dev
diff --git a/tests/test_model_storagevolume.py b/tests/test_model_storagevolume.py
index a3c3ce3..ca4ebc1 100644
--- a/tests/test_model_storagevolume.py
+++ b/tests/test_model_storagevolume.py
@@ -195,7 +195,7 @@ class StorageVolumeTests(unittest.TestCase):
self.assertEquals(200, resp.status)
keys = [u'name', u'type', u'capacity',
u'allocation', u'path',
- u'ref_cnt', u'format']
+ u'used_by', u'format']
for vol in json.loads(resp.read()):
resp = self.request(uri + '/' + vol['name'])
self.assertEquals(200, resp.status)
diff --git a/tests/test_rest.py b/tests/test_rest.py
index c2ebd88..e43b327 100644
--- a/tests/test_rest.py
+++ b/tests/test_rest.py
@@ -265,7 +265,7 @@ class RestTests(unittest.TestCase):
resp = self.request(vol_uri % vm['uuid'])
vol = json.loads(resp.read())
self.assertEquals(1 << 30, vol['capacity'])
- self.assertEquals(1, vol['ref_cnt'])
+ self.assertEquals(['test-vm'], vol['used_by'])
# Start the VM
resp = self.request('/vms/test-vm/start', '{}', 'POST')
--
2.1.0