Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
On 02/14/2014 03:26 PM, Aline Manera wrote:
From: Rodrigo Trujillo <rodrigo.trujillo(a)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(a)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(a)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: