
Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
This patch implements the mockmodel class to simulate lookup, add, remove, update of devices in a guest vm. Also, it adds test cases to test the rest API.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com>
Minor changes/improvements based on ML feedback Removing hard disk tests from tests/tests_rest.py
Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 70 ++++++++++++++++++++++++++++++++++++++++++- tests/test_rest.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 988683f..329e17c 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -28,6 +28,7 @@ import ipaddr import os import psutil import random +import string import time import uuid
@@ -164,8 +165,23 @@ class MockModel(object): for vol in params['volumes']: vm.disk_paths.append({'pool': pool.name, 'volume': vol}) + else: vm.disk_paths = t.fork_vm_storage(vm_uuid) + + index = 0 + for disk in vm.disk_paths: + storagepath = self._mock_storagepools[disk['pool']].info['path'] + fullpath = os.path.join(storagepath, disk['volume']) + dev_name = "hd" + string.ascii_lowercase[index] + params = {'dev': dev_name, 'path': fullpath, 'type': 'disk'} + vm.storagedevices[dev_name] = MockVMStorageDevice(params) + index += 1 + + cdrom = "hd" + string.ascii_lowercase[index + 1] + cdrom_params = {'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} + vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) + self._mock_vms[name] = vm return name
@@ -570,6 +586,50 @@ class MockModel(object): def networks_get_list(self): return sorted(self._mock_networks.keys())
+ def vmstorages_create(self, vm_name, params): + path = params.get('path') + if path.startswith('/') and not os.path.exists(path): + raise InvalidParameter("KCHCDROM0003E", {'value': path}) + + dom = self._get_vm(vm_name) + dev = params.get('dev', None) + if dev and dev in self.vmstorages_get_list(vm_name): + return OperationFailed("KCHCDROM0004E", {'dev_name': dev, + 'vm_name': vm_name}) + if not dev: + index = len(dom.storagedevices.keys()) + 1 + params['dev'] = "hd" + string.ascii_lowercase[index] + + vmdev = MockVMStorageDevice(params) + dom.storagedevices[params['dev']] = vmdev + return params['dev'] + + def vmstorages_get_list(self, vm_name): + dom = self._get_vm(vm_name) + return dom.storagedevices.keys() + + def vmstorage_lookup(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + return dom.storagedevices.get(dev_name).info + + def vmstorage_delete(self, vm_name, dev_name): + dom = self._get_vm(vm_name) + if dev_name not in self.vmstorages_get_list(vm_name): + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name, + 'vm_name': vm_name}) + dom.storagedevices.pop(dev_name) + + def vmstorage_update(self, vm_name, dev_name, params): + try: + dom = self._get_vm(vm_name) + dom.storagedevices[dev_name].info.update(params) + except Exception as e: + raise OperationFailed("KCHCDROM0009E", {'error': e.message}) + return dev_name + def vmifaces_create(self, vm, params): if (params["type"] == "network" and params["network"] not in self.networks_get_list()): @@ -752,6 +812,13 @@ class MockVMTemplate(VMTemplate): return disk_paths
+class MockVMStorageDevice(object): + def __init__(self, params): + self.info = {'dev': params.get('dev'), + 'type': params.get('type'), + 'path': params.get('path')} + + class MockVMIface(object): counter = 0
@@ -777,6 +844,7 @@ class MockVM(object): self.disk_paths = [] self.networks = template_info['networks'] ifaces = [MockVMIface(net) for net in self.networks] + self.storagedevices = {} self.ifaces = dict([(iface.info['mac'], iface) for iface in ifaces]) self.info = {'state': 'shutoff', 'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \ @@ -898,7 +966,7 @@ def get_mock_environment(): model = MockModel() for i in xrange(5): name = 'test-template-%i' % i - params = {'name': name} + params = {'name': name, 'cdrom': '/file.iso'} t = MockVMTemplate(params, model) model._mock_templates[name] = t
diff --git a/tests/test_rest.py b/tests/test_rest.py index deb6fe8..37bfccf 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -354,6 +354,81 @@ class RestTests(unittest.TestCase): resp = self.request('/templates/test', '{}', 'DELETE') self.assertEquals(204, resp.status)
+ def test_vm_storage_devices(self): + + with RollbackContext() as rollback: + # Create a template as a base for our VMs + req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the template + rollback.prependDefer(self.request, + '/templates/test', '{}', 'DELETE') + + # Create a VM with default args + req = json.dumps({'name': 'test-vm', + 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the VM + rollback.prependDefer(self.request, + '/vms/test-vm', '{}', 'DELETE') + + # Check storage devices + resp = self.request('/vms/test-vm/storages', '{}', 'GET') + devices = json.loads(resp.read()) + self.assertEquals(2, len(devices)) + dev_types = [] + for d in devices: + self.assertIn(u'type', d.keys()) + self.assertIn(u'dev', d.keys()) + self.assertIn(u'path', d.keys()) + dev_types.append(d['type']) + + self.assertEquals(['cdrom', 'disk'], sorted(dev_types)) + + # Attach cdrom with nonexistent iso + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/nonexistent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(400, resp.status) + + # Attach a cdrom with existent dev name + open('/tmp/existent.iso', 'w').close() + req = json.dumps({'dev': 'hdx', + 'type': 'cdrom', + 'path': '/tmp/existent.iso'}) + resp = self.request('/vms/test-vm/storages', req, 'POST') + self.assertEquals(201, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('hdx', cd_info['dev']) + self.assertEquals('cdrom', cd_info['type']) + self.assertEquals('/tmp/existent.iso', cd_info['path']) + # Delete the file and cdrom + rollback.prependDefer(self.request, + '/vms/test-vm/storages/hdx', '{}', 'DELETE') + os.remove('/tmp/existent.iso') + + # Change path of storage cdrom + req = json.dumps({'path': 'http://myserver.com/myiso.iso'}) + resp = self.request('/vms/test-vm/storages/hdx', req, 'PUT') + self.assertEquals(200, resp.status) + cd_info = json.loads(resp.read()) + self.assertEquals('http://myserver.com/myiso.iso', cd_info['path']) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(3, len(devs)) + + # Detach storage cdrom + resp = self.request('/vms/test-vm/storages/hdx', '{}', 'DELETE') + self.assertEquals(204, resp.status) + + # Test GET + devs = json.loads(self.request('/vms/test-vm/storages').read()) + self.assertEquals(2, len(devs)) + def test_vm_iface(self):
with RollbackContext() as rollback: