
Create a test_template.py file to handle all the template tests. There is no need to differ model and mockmodel in this case as the Template code is the same in both models. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- tests/test_mockmodel.py | 70 +--------- tests/test_model.py | 238 --------------------------------- tests/test_rest.py | 337 ---------------------------------------------- tests/test_template.py | 348 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+), 644 deletions(-) create mode 100644 tests/test_template.py diff --git a/tests/test_mockmodel.py b/tests/test_mockmodel.py index 29354aa..542f845 100644 --- a/tests/test_mockmodel.py +++ b/tests/test_mockmodel.py @@ -1,7 +1,7 @@ # # Project Kimchi # -# Copyright IBM, Corp. 2013-2014 +# Copyright IBM, Corp. 2013-2015 # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -96,74 +96,6 @@ class MockModelTests(unittest.TestCase): else: self.fail("Expected exception not raised") - def test_template_cpu_info(self): - template = self._create_default_template() - # GET of cpu_info will be {} - cpu_info = template['cpu_info'] - self.assertEquals(cpu_info, {}) - self.assertEquals(cpu_info.get('topology'), None) - - # Update topology - # GET of cpu_info will contain 'topology' - cpu_info_data = {'cpu_info': {'topology': {'sockets': 1, - 'cores': 1, - 'threads': 1}}} - _, resp_code = self._send_url_request('PUT', '/templates/test', - cpu_info_data) - self.assertEquals(200, resp_code) - - updated_template, resp_code = \ - self._send_url_request('GET', '/templates/test') - self.assertEquals(200, resp_code) - self.assertEquals(updated_template['cpu_info'], - cpu_info_data['cpu_info']) - - def test_template_update_disk_type(self): - def _get_default_disk_data(disk_type): - return {'disks': [{'index': 0, 'format': disk_type, 'size': 10}]} - - template = self._create_default_template() - # Default template is created with 1 disk without any declared - # type. - disk_data = template['disks'] - self.assertEquals(disk_data, [{'index': 0, 'size': 10}]) - - # For all supported types, edit the template and check if - # the change was made. - disk_types = ['bochs', 'cloop', 'cow', 'dmg', 'qcow', 'qcow2', - 'qed', 'raw', 'vmdk', 'vpc'] - for disk_type in disk_types: - disk_data = _get_default_disk_data(disk_type) - _, resp_code = self._send_url_request('PUT', '/templates/test', - disk_data) - self.assertEquals(200, resp_code) - - updated_template, resp_code = \ - self._send_url_request('GET', '/templates/test') - self.assertEquals(200, resp_code) - self.assertEquals(updated_template['disks'], disk_data['disks']) - - # Check Bad Request when type is invalid - bad_disk_data = _get_default_disk_data('invalid_disk_type') - _, resp_code = self._send_url_request('PUT', '/templates/test', - bad_disk_data) - self.assertEquals(400, resp_code) - - def _create_default_template(self): - params = {'name': 'test', 'cdrom': fake_iso} - template, resp_code = self._send_url_request('POST', '/templates', - params) - self.assertEquals(201, resp_code) - return template - - def _send_url_request(self, method, url, data=None): - req = None - if data: - req = json.dumps(data) - resp = request(host, ssl_port, url, req, method) - rsp_body = resp.read() - return json.loads(rsp_body), resp.status - def test_screenshot_refresh(self): # Create a VM req = json.dumps({'name': 'test', 'cdrom': fake_iso}) diff --git a/tests/test_model.py b/tests/test_model.py index f80f1c9..f37a33c 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -558,244 +558,6 @@ class ModelTests(unittest.TestCase): self.assertTrue(os.access(disk_path, os.F_OK)) self.assertFalse(os.access(disk_path, os.F_OK)) - @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') - def test_template_storage_customise(self): - inst = model.Model(objstore_loc=self.tmp_store) - - with RollbackContext() as rollback: - path = '/tmp/kimchi-images' - pool = 'test-pool' - if not os.path.exists(path): - os.mkdir(path) - - params = {'name': 'test', 'disks': [{'size': 1}], - 'cdrom': self.kimchi_iso} - inst.templates_create(params) - rollback.prependDefer(inst.template_delete, 'test') - - params = {'storagepool': '/storagepools/test-pool'} - self.assertRaises(InvalidParameter, inst.template_update, - 'test', params) - - # For all supported formats, edit the template and check if - # the change was made. - disk_formats = ['bochs', 'cloop', 'cow', 'dmg', 'qcow', 'qcow2', - 'qed', 'raw', 'vmdk', 'vpc'] - for disk_format in disk_formats: - disk_data = {'disks': [{'index': 0, 'format': disk_format, - 'size': 1}]} - inst.template_update('test', disk_data) - updated_template = inst.template_lookup('test') - self.assertEquals(updated_template['disks'], - disk_data['disks']) - # Restore disk data to default value - inst.template_update('test', {'disks': [{'index': 0, 'size': 1}]}) - - args = {'name': pool, - 'path': path, - 'type': 'dir'} - inst.storagepools_create(args) - rollback.prependDefer(shutil.rmtree, args['path']) - rollback.prependDefer(inst.storagepool_delete, pool) - - inst.template_update('test', params) - - params = {'name': 'test-vm-1', 'template': '/templates/test'} - self.assertRaises(InvalidParameter, inst.vms_create, params) - - inst.storagepool_activate(pool) - rollback.prependDefer(inst.storagepool_deactivate, pool) - - inst.vms_create(params) - rollback.prependDefer(inst.vm_delete, 'test-vm-1') - vm_info = inst.vm_lookup(params['name']) - disk_path = '/tmp/kimchi-images/%s-0.img' % vm_info['uuid'] - self.assertTrue(os.access(disk_path, os.F_OK)) - vol = '%s-0.img' % vm_info['uuid'] - volinfo = inst.storagevolume_lookup(pool, vol) - self.assertEquals(1, volinfo['ref_cnt']) - - # reset template to default storage pool - # so we can remove the storage pool created 'test-pool' - params = {'storagepool': '/storagepools/default'} - inst.template_update('test', params) - - @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') - def test_template_create(self): - inst = model.Model('test:///default', - objstore_loc=self.tmp_store) - # Test non-exist path raises InvalidParameter - params = {'name': 'test', - 'cdrom': '/non-exsitent.iso'} - self.assertRaises(InvalidParameter, inst.templates_create, params) - - # Test non-iso path raises InvalidParameter - params['cdrom'] = os.path.abspath(__file__) - self.assertRaises(InvalidParameter, inst.templates_create, params) - - with RollbackContext() as rollback: - net_name = 'test-network' - net_args = {'name': net_name, - 'connection': 'nat', - 'subnet': '127.0.100.0/24'} - inst.networks_create(net_args) - rollback.prependDefer(inst.network_delete, net_name) - - params = {'name': 'test', 'memory': 1024, 'cpus': 1, - 'cdrom': self.kimchi_iso} - inst.templates_create(params) - rollback.prependDefer(inst.template_delete, 'test') - info = inst.template_lookup('test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) - self.assertEquals("default", info["networks"][0]) - - # create template with non-existent network - params['name'] = 'new-test' - params['networks'] = ["no-exist"] - self.assertRaises(InvalidParameter, inst.templates_create, params) - - params['networks'] = ['default', 'test-network'] - inst.templates_create(params) - rollback.prependDefer(inst.template_delete, params['name']) - info = inst.template_lookup(params['name']) - for key in params.keys(): - self.assertEquals(params[key], info[key]) - - @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') - def test_template_integrity(self): - inst = model.Model('test:///default', - objstore_loc=self.tmp_store) - - with RollbackContext() as rollback: - net_name = 'test-network' - net_args = {'name': net_name, - 'connection': 'nat', - 'subnet': '127.0.100.0/24'} - inst.networks_create(net_args) - - path = '/tmp/kimchi-iso/' - if not os.path.exists(path): - os.makedirs(path) - iso = path + 'ubuntu12.04.iso' - iso_gen.construct_fake_iso(iso, True, '12.04', 'ubuntu') - - args = {'name': 'test-pool', - 'path': '/tmp/kimchi-images', - 'type': 'dir'} - inst.storagepools_create(args) - rollback.prependDefer(inst.storagepool_delete, 'test-pool') - - params = {'name': 'test', 'memory': 1024, 'cpus': 1, - 'networks': ['test-network'], 'cdrom': iso, - 'storagepool': '/storagepools/test-pool'} - inst.templates_create(params) - rollback.prependDefer(inst.template_delete, 'test') - - # Try to delete network - # It should fail as it is associated to a template - self.assertRaises(InvalidOperation, inst.network_delete, net_name) - # Update template to release network and then delete it - params = {'networks': []} - inst.template_update('test', params) - inst.network_delete(net_name) - - shutil.rmtree(path) - info = inst.template_lookup('test') - self.assertEquals(info['invalid']['cdrom'], [iso]) - - @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') - def test_template_clone(self): - inst = model.Model(None, - objstore_loc=self.tmp_store) - with RollbackContext() as rollback: - orig_params = {'name': 'test-template', 'memory': 1024, - 'cpus': 1, 'cdrom': self.kimchi_iso} - inst.templates_create(orig_params) - rollback.prependDefer(inst.template_delete, 'test-template') - orig_temp = inst.template_lookup(orig_params['name']) - - ident = inst.template_clone('test-template') - rollback.prependDefer(inst.template_delete, ident) - clone_temp = inst.template_lookup(ident) - - clone_temp['name'] = orig_temp['name'] - for key in clone_temp.keys(): - self.assertEquals(clone_temp[key], orig_temp[key]) - - @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') - def test_template_update(self): - inst = model.Model(None, - objstore_loc=self.tmp_store) - with RollbackContext() as rollback: - net_name = 'test-network' - net_args = {'name': net_name, - 'connection': 'nat', - 'subnet': '127.0.100.0/24'} - inst.networks_create(net_args) - rollback.prependDefer(inst.network_delete, net_name) - inst.network_activate(net_name) - rollback.prependDefer(inst.network_deactivate, net_name) - - net_name = u'kīмсhī-пet' - net_args = {'name': net_name, - 'connection': 'nat', - 'subnet': '127.0.20.0/24'} - inst.networks_create(net_args) - rollback.prependDefer(inst.network_delete, net_name) - inst.network_activate(net_name) - rollback.prependDefer(inst.network_deactivate, net_name) - - orig_params = {'name': 'test', 'memory': 1024, 'cpus': 1, - 'cdrom': self.kimchi_iso} - inst.templates_create(orig_params) - - params = {'name': 'new-test'} - self.assertEquals('new-test', inst.template_update('test', params)) - self.assertRaises(NotFoundError, inst.template_delete, 'test') - - params = {'name': 'new-test', 'memory': 512, 'cpus': 2} - inst.template_update('new-test', params) - rollback.prependDefer(inst.template_delete, 'new-test') - - info = inst.template_lookup('new-test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) - self.assertEquals("default", info["networks"][0]) - - params = {'name': 'new-test', 'memory': 1024, 'cpus': 1, - 'networks': ['default', 'test-network', u'kīмсhī-пet']} - inst.template_update('new-test', params) - info = inst.template_lookup('new-test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) - - # test cpu_info - # new-test has 1 cpu, so this should fail: - params['cpu_info'] = {"topology": - {"sockets": 1, "cores": 1, "threads": 2}} - self.assertRaises(InvalidParameter, inst.template_update, - 'new-test', params) - - params['cpus'] = 2 - inst.template_update('new-test', params) - info = inst.template_lookup('new-test') - for key in params.keys(): - self.assertEquals(params[key], info[key]) - - # test update with non-existent network - params = {'networks': ["no-exist"]} - self.assertRaises(InvalidParameter, inst.template_update, - 'new-test', params) - - params = {'name': 'some-vm', 'template': '/templates/new-test'} - self.assertEquals('some-vm', inst.vms_create(params)) - rollback.prependDefer(inst.vm_delete, 'some-vm') - - iface_args = {'type': 'network', 'network': u'kīмсhī-пet'} - mac = inst.vmifaces_create('some-vm', iface_args) - self.assertEquals(17, len(mac)) - def test_vm_edit(self): config.set("authentication", "method", "pam") inst = model.Model(None, diff --git a/tests/test_rest.py b/tests/test_rest.py index 953cff7..a54e98e 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -23,7 +23,6 @@ import json import os import re import requests -import shutil import time import unittest import urllib2 @@ -851,115 +850,6 @@ class RestTests(unittest.TestCase): resp = self.request('/vms/test-vm', '{}', 'DELETE') self.assertEquals(204, resp.status) - def test_template_customise_storage(self): - req = json.dumps({'name': 'test', 'cdrom': fake_iso, - 'disks': [{'size': 1}]}) - resp = self.request('/templates', req, 'POST') - self.assertEquals(201, resp.status) - - # Update a Template with non-existent pool fails with 400 - req = json.dumps({'storagepool': '/storagepools/alt'}) - resp = self.request('/templates/test', req, 'PUT') - self.assertEquals(400, resp.status) - - # Create alternate storage - req = json.dumps({'name': 'alt', - 'capacity': 1024, - 'allocated': 512, - 'path': '/tmp', - 'type': 'dir'}) - resp = self.request('/storagepools', req, 'POST') - self.assertEquals(201, resp.status) - - req = json.dumps({'storagepool': '/storagepools/alt'}) - resp = self.request('/templates/test', req, 'PUT') - self.assertEquals(200, resp.status) - - # Create a VM on inactive pool fails with 400 - req = json.dumps({'name': 'test-vm', 'template': '/templates/test'}) - resp = self.request('/vms', req, 'POST') - self.assertEquals(400, resp.status) - - resp = self.request('/storagepools/alt/activate', req, 'POST') - self.assertEquals(200, resp.status) - - # Create a VM - req = json.dumps({'name': 'test-vm', 'template': '/templates/test'}) - resp = self.request('/vms', req, 'POST') - vm = json.loads(resp.read()) - self.assertEquals(201, resp.status) - - # Verify the volume was created - vol_uri = '/storagepools/alt/storagevolumes/%s-0.img' % vm['uuid'] - resp = self.request(vol_uri) - vol = json.loads(resp.read()) - self.assertEquals(1073741824, vol['capacity']) - - # Delete the VM - resp = self.request('/vms/test-vm', '{}', 'DELETE') - self.assertEquals(204, resp.status) - - # Verify the volume was deleted - self.assertHTTPStatus(404, vol_uri) - - def test_template_customise_network(self): - with RollbackContext() as rollback: - tmpl = {'name': 'test', 'cdrom': fake_iso, - 'disks': [{'size': 1}]} - req = json.dumps(tmpl) - resp = self.request('/templates', req, 'POST') - self.assertEquals(201, resp.status) - # Delete the template - rollback.prependDefer(self.request, - '/templates/test', '{}', 'DELETE') - tmpl_res = json.loads(resp.read()) - self.assertTrue(type(tmpl_res['networks']) is list) - self.assertEquals("default", tmpl_res['networks'][0]) - - tmpl['name'] = "failed_tmpl" - # Create a Template with non-array network fails with 400 - tmpl['networks'] = "test-network" - req = json.dumps(tmpl) - resp = self.request('/templates', req, 'POST') - self.assertEquals(400, resp.status) - - # Create a Template with non-existent network fails with 400 - tmpl['networks'] = ["test-network"] - req = json.dumps(tmpl) - resp = self.request('/templates', req, 'POST') - self.assertEquals(400, resp.status) - - # Create a network - req = json.dumps({'name': 'test-network', - 'connection': 'nat', - 'net': '127.0.1.0/24'}) - resp = self.request('/networks', req, 'POST') - self.assertEquals(201, resp.status) - # Delete the network - rollback.prependDefer(self.request, - '/networks/test-network', '{}', 'DELETE') - - tmpl['name'] = "test" - # Update a Template with non-array network fails with 400 - tmpl['networks'] = "bad-network" - req = json.dumps(tmpl) - resp = self.request('/templates/test', req, 'PUT') - self.assertEquals(400, resp.status) - # Update a Template with non-existent network fails with 400 - tmpl['networks'] = ["bad-network"] - req = json.dumps(tmpl) - resp = self.request('/templates/test', req, 'PUT') - self.assertEquals(400, resp.status) - - # Update a Template with existent network, successful - tmpl['networks'] = ["default", "test-network"] - req = json.dumps(tmpl) - resp = self.request('/templates/test', req, 'PUT') - self.assertEquals(200, resp.status) - tmpl_res = json.loads(resp.read()) - self.assertTrue(type(tmpl_res['networks']) is list) - self.assertEquals(tmpl['networks'], tmpl_res['networks']) - def test_unnamed_vms(self): # Create a Template req = json.dumps({'name': 'test', 'cdrom': fake_iso}) @@ -1029,233 +919,6 @@ class RestTests(unittest.TestCase): resp = self.request('/storagepools/%s' % name, '{}', 'DELETE') self.assertEquals(204, resp.status) - def test_templates(self): - def verify_template(t, res): - for field in ('name', 'os_distro', 'os_version', 'memory', - 'cpus', 'storagepool', 'graphics'): - if field in t: - self.assertEquals(t[field], res[field]) - - resp = self.request('/templates') - self.assertEquals(200, resp.status) - self.assertEquals(0, len(json.loads(resp.read()))) - - # Create a template without cdrom and disk specified fails with 400 - t = {'name': 'test', 'os_distro': 'ImagineOS', - 'os_version': '1.0', 'memory': 1024, 'cpus': 1, - 'storagepool': '/storagepools/alt'} - req = json.dumps(t) - resp = self.request('/templates', req, 'POST') - self.assertEquals(400, resp.status) - - # Create an image based template - open('/tmp/mock.img', 'w').close() - t = {'name': 'test_img_template', 'os_distro': 'ImagineOS', - 'os_version': '1.0', 'memory': 1024, 'cpus': 1, - 'storagepool': '/storagepools/default-pool', - 'disks': [{'base': '/tmp/mock.img'}]} - req = json.dumps(t) - resp = self.request('/templates', req, 'POST') - self.assertEquals(201, resp.status) - os.remove('/tmp/mock.img') - - # Create a template - open('/tmp/mock.iso', 'w').close() - graphics = {'type': 'spice', 'listen': '127.0.0.1'} - t = {'name': 'test', 'os_distro': 'ImagineOS', - 'os_version': '1.0', 'memory': 1024, 'cpus': 1, - 'storagepool': '/storagepools/default-pool', - 'cdrom': '/tmp/mock.iso', 'graphics': graphics} - req = json.dumps(t) - resp = self.request('/templates', req, 'POST') - self.assertEquals(201, resp.status) - os.remove('/tmp/mock.iso') - - # Verify the template - res = json.loads(self.request('/templates/test').read()) - verify_template(t, res) - - # clone a template - resp = self.request('/templates/%s/clone' % t['name'], '{}', 'POST') - self.assertEquals(303, resp.status) - - # Verify the clone template - res = json.loads(self.request('/templates/%s-clone1' % - t['name']).read()) - old_temp = t['name'] - t['name'] = res['name'] - verify_template(t, res) - # Delete the clone template - resp = self.request('/templates/%s' % t['name'], '{}', 'DELETE') - self.assertEquals(204, resp.status) - t['name'] = old_temp - - # Create a template with same name fails with 400 - t = {'name': 'test', 'os_distro': 'ImagineOS', - 'os_version': '1.0', 'memory': 1024, 'cpus': 1, - 'storagepool': '/storagepools/default-pool', - 'cdrom': fake_iso} - req = json.dumps(t) - resp = self.request('/templates', req, 'POST') - self.assertEquals(400, resp.status) - - # Update the template - t['os_distro'] = 'Linux.ISO' - t['os_version'] = '1.1' - t['graphics'] = {'type': 'vnc', 'listen': '127.0.0.1'} - req = json.dumps(t) - resp = self.request('/templates/%s' % t['name'], req, 'PUT') - self.assertEquals(200, resp.status) - - # Verify the template - res = json.loads(self.request('/templates/test').read()) - verify_template(t, res) - - # Update the template with ipv6 address as listen - t['graphics'] = {'type': 'vnc', 'listen': 'fe00::0'} - req = json.dumps(t) - resp = self.request('/templates/%s' % t['name'], req, 'PUT') - self.assertEquals(200, resp.status) - - # Verify the template - res = json.loads(self.request('/templates/test').read()) - verify_template(t, res) - - # Update the template with integer values - t['memory'] = 512 - t['cpus'] = 2 - req = json.dumps(t) - resp = self.request('/templates/%s' % t['name'], req, 'PUT') - self.assertEquals(200, resp.status) - - # Verify the template - res = json.loads(self.request('/templates/%s' % t['name']).read()) - verify_template(t, res) - - # Update the template name - oldname = t['name'] - t['name'] = "test1" - req = json.dumps(t) - resp = self.request('/templates/%s' % oldname, req, 'PUT') - self.assertEquals(303, resp.status) - - # Verify the template - res = json.loads(self.request('/templates/%s' % t['name']).read()) - verify_template(t, res) - - # Try to change template name to empty string - t = {"name": "test1"} - tmpl_name = t['name'] - t['name'] = ' ' - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - # Get the right template name back. - t['name'] = tmpl_name - - # Try to change template memory to a non-number value - t['memory'] = 'invalid-value' - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - - # Try to clean up template memory value - t['memory'] = ' ' - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - - # Try to change template cpus to a non-number value - t['cpus'] = 'invalid-value' - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - - # Try to change template graphics type to invalid value - t['graphics'] = {'type': 'invalid'} - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - - # Try to change template graphics type to invalid listen - t['graphics'] = {'type': 'vnc', 'listen': 'invalid'} - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - - # Try to clean up template cpus value - t['cpus'] = ' ' - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - - # Test nonexistent fields, specify a field 'foo' isn't in the Template - t['foo'] = "bar" - req = json.dumps(t) - resp = self.request('/templates/%s' % tmpl_name, req, 'PUT') - self.assertEquals(400, resp.status) - - # Delete the template - resp = self.request('/templates/%s' % tmpl_name, '{}', 'DELETE') - self.assertEquals(204, resp.status) - - def test_template_integrity(self): - - path = '/tmp/kimchi-iso/' - if not os.path.exists(path): - os.makedirs(path) - iso = path + 'ubuntu12.04.iso' - iso_gen.construct_fake_iso(iso, True, '12.04', 'ubuntu') - - req = json.dumps({'name': 'test-network', - 'connection': 'nat', - 'net': '127.0.1.0/24'}) - resp = request(host, ssl_port, '/networks', req, 'POST') - self.assertEquals(201, resp.status) - - req = json.dumps({'name': 'test-storagepool', - 'path': '/tmp/kimchi-images', - 'type': 'dir'}) - resp = request(host, ssl_port, '/storagepools', req, 'POST') - self.assertEquals(201, resp.status) - - t = {'name': 'test', 'memory': 1024, 'cpus': 1, - 'networks': ['test-network'], 'cdrom': iso, - 'storagepool': '/storagepools/test-storagepool'} - - req = json.dumps(t) - resp = self.request('/templates', req, 'POST') - self.assertEquals(201, resp.status) - - shutil.rmtree(path) - # Try to delete network - # It should fail as it is associated to a template - resp = json.loads(request(host, ssl_port, '/networks/test-network', - '{}', 'DELETE').read()) - self.assertIn("KCHNET0017E", resp["reason"]) - - # Update template to release network and then delete it - params = {'networks': []} - req = json.dumps(params) - resp = request(host, ssl_port, '/templates/test', req, 'PUT') - resp = request(host, ssl_port, '/networks/test-network', '{}', - 'DELETE') - self.assertEquals(204, resp.status) - - # Try to delete the storagepool - # It should fail as it is associated to a template - resp = request(host, ssl_port, '/storagepools/test-storagepool', - '{}', 'DELETE') - self.assertEquals(400, resp.status) - - # Verify the template - res = json.loads(self.request('/templates/test').read()) - self.assertEquals(res['invalid']['cdrom'], [iso]) - - # Delete the template - resp = request(host, ssl_port, '/templates/test', '{}', 'DELETE') - self.assertEquals(204, resp.status) - def test_iso_scan_shallow(self): # fake environment preparation self._create_pool('pool-3') diff --git a/tests/test_template.py b/tests/test_template.py new file mode 100644 index 0000000..4446025 --- /dev/null +++ b/tests/test_template.py @@ -0,0 +1,348 @@ +# -*- coding: utf-8 -*- +# +# Project Kimchi +# +# Copyright IBM, Corp. 2015 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import json +import os +import unittest + +from functools import partial + +from kimchi.config import READONLY_POOL_TYPE +from kimchi.mockmodel import MockModel +from utils import get_free_port, patch_auth, request, run_server + + +model = None +test_server = None +host = None +port = None +ssl_port = None +cherrypy_port = None + + +def setUpModule(): + global test_server, model, host, port, ssl_port, cherrypy_port + + patch_auth() + model = MockModel('/tmp/obj-store-test') + host = '127.0.0.1' + port = get_free_port('http') + ssl_port = get_free_port('https') + cherrypy_port = get_free_port('cherrypy_port') + test_server = run_server(host, port, ssl_port, test_mode=True, + cherrypy_port=cherrypy_port, model=model) + + +def tearDownModule(): + test_server.stop() + os.unlink('/tmp/obj-store-test') + + +class TemplateTests(unittest.TestCase): + def setUp(self): + self.request = partial(request, host, ssl_port) + model.reset() + + def test_tmpl_lifecycle(self): + resp = self.request('/templates') + self.assertEquals(200, resp.status) + self.assertEquals(0, len(json.loads(resp.read()))) + + # Create a template without cdrom and disk specified fails with 400 + t = {'name': 'test', 'os_distro': 'ImagineOS', + 'os_version': '1.0', 'memory': 1024, 'cpus': 1, + 'storagepool': '/storagepools/alt'} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(400, resp.status) + + # Create a template + t = {'name': 'test', 'cdrom': '/tmp/mock.iso'} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + + # Verify the template + keys = ['name', 'icon', 'invalid', 'os_distro', 'os_version', 'cpus', + 'memory', 'cdrom', 'disks', 'storagepool', 'networks', + 'folder', 'graphics', 'cpu_info'] + tmpl = json.loads(self.request('/templates/test').read()) + self.assertEquals(sorted(tmpl.keys()), sorted(keys)) + + # Clone a template + resp = self.request('/templates/test/clone', '{}', 'POST') + self.assertEquals(303, resp.status) + + # Verify the cloned template + tmpl_cloned = json.loads(self.request('/templates/test-clone1').read()) + del tmpl['name'] + del tmpl_cloned['name'] + self.assertEquals(tmpl, tmpl_cloned) + + # Delete the cloned template + resp = self.request('/templates/test-clone1', '{}', 'DELETE') + self.assertEquals(204, resp.status) + + # Create a template with same name fails with 400 + req = json.dumps({'name': 'test', 'cdrom': '/tmp/mock.iso'}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(400, resp.status) + + # Create an image based template + open('/tmp/mock.img', 'w').close() + t = {'name': 'test_img_template', 'disks': [{'base': '/tmp/mock.img'}]} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + os.remove('/tmp/mock.img') + + def test_customized_tmpl(self): + # Create a template + t = {'name': 'test', 'cdrom': '/tmp/mock.iso'} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + tmpl = json.loads(self.request('/templates/test').read()) + + # Update name + new_name = u'kīмсhīTmpl' + new_tmpl_uri = '/templates/%s' % new_name.encode('utf-8') + req = json.dumps({'name': new_name}) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(303, resp.status) + resp = self.request(new_tmpl_uri) + update_tmpl = json.loads(resp.read()) + self.assertEquals(new_name, update_tmpl['name']) + del tmpl['name'] + del update_tmpl['name'] + self.assertEquals(tmpl, update_tmpl) + + # Update icon + req = json.dumps({'icon': 'images/icon-fedora.png'}) + resp = self.request(new_tmpl_uri, req, 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals('images/icon-fedora.png', update_tmpl['icon']) + + # Update os_distro and os_version + req = json.dumps({'os_distro': 'fedora', 'os_version': '21'}) + resp = self.request(new_tmpl_uri, req, 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals('fedora', update_tmpl['os_distro']) + self.assertEquals('21', update_tmpl['os_version']) + + # Update cpus + req = json.dumps({'cpus': 2}) + resp = self.request(new_tmpl_uri, req, 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals(2, update_tmpl['cpus']) + + # Update memory + req = json.dumps({'memory': 2048}) + resp = self.request(new_tmpl_uri, req, 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals(2048, update_tmpl['memory']) + + # Update cpu_info + resp = self.request(new_tmpl_uri) + cpu_info = json.loads(resp.read())['cpu_info'] + self.assertEquals(cpu_info, {}) + self.assertEquals(cpu_info.get('topology'), None) + + cpu_info_data = {'cpu_info': {'topology': {'sockets': 1, + 'cores': 2, + 'threads': 1}}} + resp = self.request(new_tmpl_uri, json.dumps(cpu_info_data), 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals(update_tmpl['cpu_info'], cpu_info_data['cpu_info']) + + # Update cdrom + cdrom_data = {'cdrom': '/tmp/mock2.iso'} + resp = self.request(new_tmpl_uri, json.dumps(cdrom_data), 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals(update_tmpl['cdrom'], cdrom_data['cdrom']) + + # Update disks + disk_data = {'disks': [{'index': 0, 'size': 10}, + {'index': 1, 'size': 20}]} + resp = self.request(new_tmpl_uri, json.dumps(disk_data), 'PUT') + self.assertEquals(200, resp.status) + resp = self.request(new_tmpl_uri) + self.assertEquals(200, resp.status) + updated_tmpl = json.loads(resp.read()) + self.assertEquals(updated_tmpl['disks'], disk_data['disks']) + + # For all supported types, edit the template and check if + # the change was made. + disk_types = ['bochs', 'cloop', 'cow', 'dmg', 'qcow', 'qcow2', + 'qed', 'raw', 'vmdk', 'vpc'] + for disk_type in disk_types: + disk_data = {'disks': [{'index': 0, 'format': disk_type, + 'size': 10}]} + resp = self.request(new_tmpl_uri, json.dumps(disk_data), 'PUT') + self.assertEquals(200, resp.status) + + resp = self.request(new_tmpl_uri) + self.assertEquals(200, resp.status) + updated_tmpl = json.loads(resp.read()) + self.assertEquals(updated_tmpl['disks'], disk_data['disks']) + + # Update folder + folder_data = {'folder': ['mock', 'isos']} + resp = self.request(new_tmpl_uri, json.dumps(folder_data), 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals(update_tmpl['folder'], folder_data['folder']) + + # Update graphics + req = json.dumps({'graphics': {'type': 'spice'}}) + resp = self.request(new_tmpl_uri, req, 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals('spice', update_tmpl['graphics']['type']) + + req = json.dumps({'graphics': {'type': 'vnc', 'listen': 'fe00::0'}}) + resp = self.request(new_tmpl_uri, req, 'PUT') + self.assertEquals(200, resp.status) + update_tmpl = json.loads(resp.read()) + self.assertEquals('vnc', update_tmpl['graphics']['type']) + self.assertEquals('fe00::0', update_tmpl['graphics']['listen']) + + def test_customized_network(self): + # Create a template + t = {'name': 'test', 'cdrom': '/tmp/mock.iso'} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + + # Create networks to be used for testing + networks = [{'name': u'kīмсhī-пet', 'connection': 'isolated'}, + {'name': u'nat-network', 'connection': 'nat'}, + {'name': u'subnet-network', 'connection': 'nat', + 'subnet': '127.0.100.0/24'}] + + # Verify the current system has at least one interface to create a + # bridged network + interfaces = json.loads(self.request('/interfaces?type=nic').read()) + if len(interfaces) > 0: + iface = interfaces[0]['name'] + networks.append({'name': u'bridge-network', 'connection': 'bridge', + 'interface': iface}) + networks.append({'name': u'bridge-network', 'connection': 'bridge', + 'interface': iface, 'vlan_id': 987}) + + tmpl_nets = [] + for net in networks: + self.request('/networks', json.dumps(net), 'POST') + tmpl_nets.append(net['name']) + req = json.dumps({'networks': tmpl_nets}) + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(200, resp.status) + + def test_customized_storagepool(self): + # Create a template + t = {'name': 'test', 'cdrom': '/tmp/mock.iso'} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + + # MockModel always returns 2 partitions (vdx, vdz) + partitions = json.loads(self.request('/host/partitions').read()) + devs = [dev['path'] for dev in partitions] + + # MockModel always returns 3 FC devices + fc_devs = json.loads(self.request('/host/devices?_cap=fc_host').read()) + fc_devs = [dev['name'] for dev in fc_devs] + + poolDefs = [ + {'type': 'dir', 'name': u'kīмсhīUnitTestDirPool', + 'path': '/tmp/kimchi-images'}, + {'type': 'netfs', 'name': u'kīмсhīUnitTestNSFPool', + 'source': {'host': 'localhost', + 'path': '/var/lib/kimchi/nfs-pool'}}, + {'type': 'scsi', 'name': u'kīмсhīUnitTestSCSIFCPool', + 'source': {'adapter_name': fc_devs[0]}}, + {'type': 'iscsi', 'name': u'kīмсhīUnitTestISCSIPool', + 'source': {'host': '127.0.0.1', + 'target': 'iqn.2015-01.localhost.kimchiUnitTest'}}, + {'type': 'logical', 'name': u'kīмсhīUnitTestLogicalPool', + 'source': {'devices': [devs[0]]}}] + + for pool in poolDefs: + self.request('/storagepools', json.dumps(pool), 'POST') + pool_uri = '/storagepools/%s' % pool['name'].encode('utf-8') + self.request(pool_uri + '/activate', '{}', 'POST') + + req = None + if pool['type'] in READONLY_POOL_TYPE: + resp = self.request(pool_uri + '/storagevolumes') + vols = json.loads(resp.read()) + if len(vols) > 0: + vol = vols[0]['name'] + req = json.dumps({'storagepool': pool_uri, + 'disks': [{'volume': vol}]}) + else: + req = json.dumps({'storagepool': pool_uri}) + + if req is not None: + resp = self.request('/templates/test', req, 'PUT') + self.assertEquals(200, resp.status) + + def test_tmpl_integrity(self): + # Create a network and a pool for testing template integrity + net = {'name': u'nat-network', 'connection': 'nat'} + self.request('/networks', json.dumps(net), 'POST') + + pool = {'type': 'dir', 'name': 'dir-pool', 'path': '/tmp/dir-pool'} + self.request('/storagepools', json.dumps(pool), 'POST') + + # Create a template using the custom network and pool + t = {'name': 'test', 'cdrom': '/tmp/mock.iso', + 'networks': ['nat-network'], + 'storagepool': '/storagepools/dir-pool'} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + + # Try to delete network + # It should fail as it is associated to a template + resp = self.request('/networks/nat-network', '{}', 'DELETE') + self.assertIn("KCHNET0017E", json.loads(resp.read())["reason"]) + + # Update template to release network and then delete it + params = {'networks': []} + req = json.dumps(params) + self.request('/templates/test', req, 'PUT') + resp = self.request('/networks/nat-network', '{}', 'DELETE') + self.assertEquals(204, resp.status) + + # Try to delete the storagepool + # It should fail as it is associated to a template + resp = self.request('/storagepools/dir-pool', '{}', 'DELETE') + self.assertEquals(400, resp.status) + + # Verify the template + res = json.loads(self.request('/templates/test').read()) + self.assertEquals(res['invalid']['cdrom'], ['/tmp/mock.iso']) -- 2.1.0