[PATCH 0/2 v3] Edit disk format in vm template - backend

Changes in v3: - changed the name of the new parameter to 'format' instead of 'type' - added all the supported disk formats in i18n.py messages - using 'GET' to retrieve the updated value in unit tests Changes in v2: - added unit tests - added support to all default disk types These patches implement the backend support to change the disk format when editing a template. Example of usage: 1- create a test template: $ curl -u user -H "Content-Type: application/json -H "Accept: application/json" http://localhost:8010/templates -X POST -d'{"name": "test_template", "cdrom": "/valid/iso/path.iso"}' 2- change the disk format to 'cloop': $ curl -u user -H "Content-Type: application/json -H "Accept: application/json" http://localhost:8010/templates/test_template -X PUT -d'{"disks":[{"index":0,"size":10, "format": "cloop"}]}' 3- retrieve the template to see the changes: $ curl -u user -H "Content-Type: application/json -H "Accept: application/json" http://localhost:8010/templates/test_template -X GET Enter host password for user 'user': { "cpus":1, "cpu_info":{}, "graphics":{ "type":"vnc", "listen":"127.0.0.1" }, "cdrom":"/valid/iso/path.iso", "networks":[ "default" ], "icon":"images/icon-opensuse.png", "os_distro":"opensuse", "name":"test_template", "disks":[ { "index":0, "size":10, "format":"cloop" } ], "invalid":{}, "os_version":"13.1", "storagepool":"/storagepools/default", "memory":1024, "folder":[] } Daniel Henrique Barboza (2): Choose disk image format in vm template - backend Unit tests for the new disk image format docs/API.md | 1 + src/kimchi/API.json | 8 +++++- src/kimchi/i18n.py | 3 +- src/kimchi/vmtemplate.py | 5 +++- tests/test_mockmodel.py | 75 ++++++++++++++++++++++++++++++++++++++++-------- tests/test_model.py | 14 +++++++++ 6 files changed, 91 insertions(+), 15 deletions(-) -- 1.8.3.1

These changes implements the backend support for setting the disk image format in an existing VM template, by using a new parameter called "format" in the 'disk' object of the template_update schema. Supported formats: 'bochs', 'cloop', 'cow', 'dmg', 'qcow', 'qcow2', 'qed', 'raw', 'vmdk', 'vpc'. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- docs/API.md | 1 + src/kimchi/API.json | 8 +++++++- src/kimchi/i18n.py | 3 ++- src/kimchi/vmtemplate.py | 5 ++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index 1338e26..8d78864 100644 --- a/docs/API.md +++ b/docs/API.md @@ -208,6 +208,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. * index: The device index * size: The device size in GB * base: Base image of this disk + * format: Format of the image. Valid formats: bochs, cloop, cow, dmg, qcow, qcow2, qed, raw, vmdk, vpc. * graphics *(optional)*: The graphics paramenters of this template * type: The type of graphics. It can be VNC or spice or None. diff --git a/src/kimchi/API.json b/src/kimchi/API.json index a1156d5..0ad36ab 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -208,7 +208,7 @@ "format": { "description": "The format of the volume", "type": "string", - "pattern": "^qcow2|raw$", + "pattern": "^(bochs|cloop|cow|dmg|qcow|qcow2|qed|raw|vmdk|vpc)$", "error": "KCHVOL0015E" }, "url": { @@ -619,6 +619,12 @@ "type": "integer", "minimum": 1, "error": "KCHTMPL0022E" + }, + "format": { + "description": "Type of the image of the disk", + "type": "string", + "pattern": "^(bochs|cloop|cow|dmg|qcow|qcow2|qed|raw|vmdk|vpc)$", + "error": "KCHTMPL0027E" } } }, diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 74ea98e..f789259 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -145,6 +145,7 @@ messages = { "KCHTMPL0024E": _("Cannot identify base image %(path)s format"), "KCHTMPL0025E": _("When specifying CPU topology, VCPUs must be a product of sockets, cores, and threads."), "KCHTMPL0026E": _("When specifying CPU topology, each element must be an integer greater than zero."), + "KCHTMPL0027E": _("Invalid disk image format. Valid formats: bochs, cloop, cow, dmg, qcow, qcow2, qed, raw, vmdk, vpc."), "KCHPOOL0001E": _("Storage pool %(name)s already exists"), "KCHPOOL0002E": _("Storage pool %(name)s does not exist"), @@ -197,7 +198,7 @@ messages = { "KCHVOL0012E": _("Storage type %(type)s does not support volume create and delete"), "KCHVOL0013E": _("Storage volume name must be a string"), "KCHVOL0014E": _("Storage volume allocation must be an integer number"), - "KCHVOL0015E": _("Storage volume format not supported"), + "KCHVOL0015E": _("Storage volume format not supported. Valid formats: bochs, cloop, cow, dmg, qcow, qcow2, qed, raw, vmdk, vpc."), "KCHVOL0016E": _("Storage volume requires a volume name"), "KCHVOL0017E": _("Unable to update database with storage volume information due error: %(err)s"), "KCHVOL0018E": _("Only one of parameter %(param)s can be specified"), diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 0541106..ff7ddb4 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -162,7 +162,10 @@ class VMTemplate(object): src = os.path.join(storage_path, volume) dev = "%s%s" % (self._bus_to_dev[self.info['disk_bus']], string.lowercase[index]) - fmt = 'raw' if self._get_storage_type() in ['logical'] else 'qcow2' + if self._get_storage_type() in ['logical']: + fmt = 'raw' + else: + fmt = disk.get('format', 'qcow2') params = {'src': src, 'dev': dev, 'bus': self.info['disk_bus'], 'type': fmt} ret += """ -- 1.8.3.1

The tests covered the case where the template is created and the format of the disk is changed. Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com> --- tests/test_mockmodel.py | 75 +++++++++++++++++++++++++++++++++++++++++-------- tests/test_model.py | 14 +++++++++ 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/tests/test_mockmodel.py b/tests/test_mockmodel.py index eeb6715..db0915e 100644 --- a/tests/test_mockmodel.py +++ b/tests/test_mockmodel.py @@ -91,29 +91,80 @@ class MockModelTests(unittest.TestCase): else: self.fail("Expected exception not raised") - def test_template_cpu_info(self): + def _create_default_template(self): # Create default template req = json.dumps({'name': 'test', 'cdrom': fake_iso}) resp = request(host, ssl_port, '/templates', req, 'POST') rsp_body = resp.read() - template_data = json.loads(rsp_body) + return json.loads(rsp_body) + + def test_template_cpu_info(self): + template = self._create_default_template() # GET of cpu_info will be {} - cpu_info = template_data['cpu_info'] + 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' - req = json.dumps({'cpu_info': {'topology': {'sockets': 1, - 'cores': 1, - 'threads': 1}}}) - resp = request(host, ssl_port, '/templates/test', req, 'PUT') + 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() - template_data = json.loads(rsp_body) - cpu_info = template_data['cpu_info'] - self.assertEquals(cpu_info, {'topology': {'sockets': 1, - 'cores': 1, - 'threads': 1}}) + return json.loads(rsp_body), resp.status def test_screenshot_refresh(self): # Create a VM diff --git a/tests/test_model.py b/tests/test_model.py index 563e80a..dab5890 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -639,6 +639,20 @@ class ModelTests(unittest.TestCase): 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'} -- 1.8.3.1
participants (1)
-
Daniel Henrique Barboza