[Kimchi-devel] [PATCH 8/8] Add tests and mockmodel for the cloning feature
Aline Manera
alinefm at linux.vnet.ibm.com
Mon Nov 3 16:50:24 UTC 2014
Reviewed-by: Aline Manera <alinefm at linux.vnet.ibm.com>
On 11/02/2014 11:05 PM, Crístian Viana wrote:
> The function "vm_clone" is now implemented on the mockmodel with a
> similar implementation as the real model; the biggest difference is that
> it doesn't deal with the different storage pool details.
> Also, new tests were added.
>
> Signed-off-by: Crístian Viana <vianac at linux.vnet.ibm.com>
> ---
> src/kimchi/mockmodel.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
> tests/test_model.py | 38 ++++++++++++++++++++++++++++++++
> tests/test_rest.py | 31 ++++++++++++++++++++++++++
> 3 files changed, 127 insertions(+)
>
> diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
> index baee0b6..2265298 100644
> --- a/src/kimchi/mockmodel.py
> +++ b/src/kimchi/mockmodel.py
> @@ -28,6 +28,7 @@ import os
> import shutil
> import psutil
> import random
> +import re
> import string
> import time
> import uuid
> @@ -181,6 +182,63 @@ class MockModel(object):
> def vm_connect(self, name):
> pass
>
> + def _clone_get_next_name(self, basename):
> + re_group_num = 'num'
> +
> + max_num = 0
> + re_cloned_vm = re.compile(u'%s-clone-(?P<%s>\d+)' %
> + (basename, re_group_num))
> +
> + vm_names = self.vms_get_list()
> +
> + for v in vm_names:
> + match = re_cloned_vm.match(v)
> + if match is not None:
> + max_num = max(max_num, int(match.group(re_group_num)))
> +
> + return u'%s-clone-%d' % (basename, max_num + 1)
> +
> + def vm_clone(self, name):
> + vm = self._mock_vms[name]
> + if vm.info['state'] != u'shutoff':
> + raise InvalidParameter('KCHVM0033E', {'name': name})
> +
> + new_name = self._clone_get_next_name(name)
> +
> + taskid = self.add_task(u'/vms/%s' % new_name, self._do_clone,
> + {'name': name, 'new_name': new_name})
> + return self.task_lookup(taskid)
> +
> + def _do_clone(self, cb, params):
> + name = params['name']
> + new_name = params['new_name']
> +
> + vm = self._mock_vms[name]
> + new_vm = copy.deepcopy(vm)
> +
> + new_uuid = unicode(uuid.uuid4())
> +
> + new_vm.name = new_name
> + new_vm.info['name'] = new_name
> + new_vm.uuid = new_uuid
> + new_vm.info['uuid'] = new_uuid
> +
> + for mac, iface in new_vm.ifaces.items():
> + new_mac = MockVMIface.get_mac()
> + iface.info['mac'] = new_mac
> + new_vm.ifaces[new_mac] = iface
> +
> + storage_names = new_vm.storagedevices.keys()
> + for i, storage_name in enumerate(storage_names):
> + storage = new_vm.storagedevices[storage_name]
> + basename, ext = os.path.splitext(storage.info['path'])
> + new_path = u'%s-%d%s' % (basename, i, ext)
> + new_vm.storagedevices[storage_name].path = new_path
> +
> + self._mock_vms[new_name] = new_vm
> +
> + cb('OK', True)
> +
> def vms_create(self, params):
> t_name = template_name_from_uri(params['template'])
> name = get_vm_name(params.get('name'), t_name, self._mock_vms.keys())
> diff --git a/tests/test_model.py b/tests/test_model.py
> index b165731..b05d71d 100644
> --- a/tests/test_model.py
> +++ b/tests/test_model.py
> @@ -1256,6 +1256,44 @@ class ModelTests(unittest.TestCase):
>
> self.assertEquals(vms, sorted(vms, key=unicode.lower))
>
> + def test_vm_clone(self):
> + inst = model.Model('test:///default', objstore_loc=self.tmp_store)
> +
> + all_vm_names = inst.vms_get_list()
> + name = all_vm_names[0]
> +
> + original_vm = inst.vm_lookup(name)
> +
> + # the VM 'test' should be running by now, so we can't clone it yet
> + self.assertRaises(InvalidParameter, inst.vm_clone, name)
> +
> + with RollbackContext() as rollback:
> + inst.vm_poweroff(name)
> + rollback.prependDefer(inst.vm_start, name)
> +
> + task = inst.vm_clone(name)
> + clone_name = task['target_uri'].split('/')[-1]
> + rollback.prependDefer(inst.vm_delete, clone_name)
> + inst.task_wait(task['id'])
> +
> + # update the original VM info because its state has changed
> + original_vm = inst.vm_lookup(name)
> + clone_vm = inst.vm_lookup(clone_name)
> +
> + self.assertNotEqual(original_vm['name'], clone_vm['name'])
> + self.assertTrue(re.match(u'%s-clone-\d+' % original_vm['name'],
> + clone_vm['name']))
> + del original_vm['name']
> + del clone_vm['name']
> +
> + self.assertNotEqual(original_vm['uuid'], clone_vm['uuid'])
> + del original_vm['uuid']
> + del clone_vm['uuid']
> +
> + # compare all VM settings except the ones already compared
> + # (and removed) above (i.e. 'name' and 'uuid')
> + self.assertEquals(original_vm, clone_vm)
> +
> def test_use_test_host(self):
> inst = model.Model('test:///default',
> objstore_loc=self.tmp_store)
> diff --git a/tests/test_rest.py b/tests/test_rest.py
> index 66072bc..09cf52b 100644
> --- a/tests/test_rest.py
> +++ b/tests/test_rest.py
> @@ -22,6 +22,7 @@ import base64
> import json
> import os
> import random
> +import re
> import requests
> import shutil
> import time
> @@ -341,6 +342,10 @@ class RestTests(unittest.TestCase):
> self.assertEquals(200, resp.status)
> self.assertTrue(resp.getheader('Content-type').startswith('image'))
>
> + # Clone a running VM
> + resp = self.request('/vms/test-vm/clone', '{}', 'POST')
> + self.assertEquals(400, resp.status)
> +
> # Force poweroff the VM
> resp = self.request('/vms/test-vm/poweroff', '{}', 'POST')
> vm = json.loads(self.request('/vms/test-vm').read())
> @@ -351,6 +356,32 @@ class RestTests(unittest.TestCase):
> resp = self.request('/vms', req, 'POST')
> self.assertEquals(400, resp.status)
>
> + # Clone a VM
> + resp = self.request('/vms/test-vm/clone', '{}', 'POST')
> + self.assertEquals(202, resp.status)
> + task = json.loads(resp.read())
> + wait_task(self._task_lookup, task['id'])
> + task = json.loads(self.request('/tasks/%s' % task['id'], '{}').read())
> + self.assertEquals('finished', task['status'])
> + clone_vm_name = task['target_uri'].split('/')[-1]
> + self.assertTrue(re.match(u'test-vm-clone-\d+', clone_vm_name))
> +
> + resp = self.request('/vms/test-vm', '{}')
> + original_vm_info = json.loads(resp.read())
> + resp = self.request('/vms/%s' % clone_vm_name, '{}')
> + self.assertEquals(200, resp.status)
> + clone_vm_info = json.loads(resp.read())
> +
> + self.assertNotEqual(original_vm_info['name'], clone_vm_info['name'])
> + del original_vm_info['name']
> + del clone_vm_info['name']
> +
> + self.assertNotEqual(original_vm_info['uuid'], clone_vm_info['uuid'])
> + del original_vm_info['uuid']
> + del clone_vm_info['uuid']
> +
> + self.assertEquals(original_vm_info, clone_vm_info)
> +
> # Delete the VM
> resp = self.request('/vms/test-vm', '{}', 'DELETE')
> self.assertEquals(204, resp.status)
More information about the Kimchi-devel
mailing list