[PATCH V4] [Kimchi 0/3] Issue #372: Add support to netboot installation.

From: Paulo Vital <pvital@linux.vnet.ibm.com> V4: * Changed API of source_media to be a dictionary with type and path * Adapted source code to the new API V3: * set network as third default option to boot order V2: * Adapted patch to new template API (automatic detection of cdrom) V1: This patchset adds support in backend to create templates and guests to netboot without setting a cdrom path or URL as image to install. Once created a guest to boot via network, the guest will request DHCP/TFTP/(NFS/HTTP/FTP) network installation servers to download the configured images and start the install. Reference: http://miud.in/1Ht3 To test, use the curl commands (look the parameters used): $ curl -k -u test -H "Content-Type: application/json" -H \ "Accept: application/json" 'https://localhost:8001/plugins/kimchi/templates' \ -X POST -d '{"name": "test-netboot", "source_media": {"type": "netboot", "path": "/var/lib/libvirt/images/netboot"}}' Enter host password for user 'test': { "cpu_info":{ "maxvcpus":1, "vcpus":1 }, "graphics":{ "type":"vnc", "listen":"127.0.0.1" }, "cdrom":null, "networks":[ "default" ], "icon":"plugins/kimchi/images/icon-vm.png", "os_distro":"unknown", "name":"test-netboot", "disks":[ { "index":0, "format":"qcow2", "pool":{ "type":"dir", "name":"/plugins/kimchi/storagepools/default" }, "size":10 } ], "invalid":{}, "os_version":"unknown", "memory":{ "current":1024, "maxmemory":1024 }, "folder":[] } $ curl -k -u test -H "Content-Type: application/json" -H \ "Accept: application/json" 'https://localhost:8001/plugins/kimchi/vms' -X POST \ -d '{"name":"1netboot-test","template":"/plugins/kimchi/templates/test-netboot"}' Enter host password for user 'test': { "status":"running", "message":"Provisioning storages for new VM", "id":"1", "target_uri":"/plugins/kimchi/vms/1netboot-test" } $ sudo virsh dumpxml 1netboot-test <domain type='kvm'> <name>1netboot-test</name> <uuid>5c9fa5b3-3203-4c93-92d6-2b4103fe7b40</uuid> <metadata> <kimchi:metadata xmlns:kimchi="https://github.com/kimchi-project/kimchi"> <kimchi:os version="unknown" distro="unknown"/> </kimchi:metadata> </metadata> <memory unit='KiB'>1048576</memory> <currentMemory unit='KiB'>1048576</currentMemory> <memtune> <hard_limit unit='KiB'>2097152</hard_limit> </memtune> <vcpu placement='static'>1</vcpu> <os> <type arch='x86_64' machine='pc-i440fx-2.4'>hvm</type> <boot dev='hd'/> <boot dev='cdrom'/> <boot dev='network'/> </os> <acpi/> <apic/> <pae/> </features> <cpu> <numa> <cell id='0' cpus='0' memory='1048576' unit='KiB'/> </numa> </cpu> <clock offset='utc'/> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> <on_crash>restart</on_crash> <devices> <emulator>/usr/bin/qemu-kvm</emulator> <disk type='file' device='disk'> <driver name='qemu' type='qcow2' cache='none'/> <source file='/var/lib/libvirt/images/5c9fa5b3-3203-4c93-92d6-2b4103fe7b40-0.img'/> <target dev='hda' bus='ide'/> <address type='drive' controller='0' bus='0' target='0' unit='0'/> </disk> <controller type='usb' index='0'> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> </controller> <controller type='pci' index='0' model='pci-root'/> <controller type='ide' index='0'> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> </controller> <interface type='network'> <mac address='52:54:00:ea:7e:ad'/> <source network='default'/> <model type='e1000'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> <serial type='pty'> <target port='0'/> </serial> <console type='pty'> <target type='serial' port='0'/> </console> <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'> <listen type='address' address='127.0.0.1'/> </graphics> <sound model='ich6'> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </sound> <video> <model type='cirrus' vram='16384' heads='1'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> </video> <memballoon model='virtio'> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> </memballoon> </devices> </domain> Paulo Vital (3): Add support to create netboot templates. Add support to create guests to netboot. Update test cases to support netboot. API.json | 18 +++++++++++---- i18n.py | 1 + model/templates.py | 27 ++++++++++++++++++++--- tests/test_authorization.py | 4 +++- tests/test_mockmodel.py | 15 ++++++++----- tests/test_model.py | 43 +++++++++++++++++++++--------------- tests/test_rest.py | 37 +++++++++++++++++++++---------- tests/test_template.py | 53 ++++++++++++++++++++++++++++++++++----------- tests/test_vmtemplate.py | 18 +++++++++++++++ vmtemplate.py | 28 ++++++++++++++++-------- xmlutils/bootorder.py | 39 +++++++++++++++++++++++++++++++++ 11 files changed, 219 insertions(+), 64 deletions(-) create mode 100644 xmlutils/bootorder.py -- 2.5.5

From: Paulo Vital <pvital@linux.vnet.ibm.com> Changed API.json and model to accept 'netboot' as source media parameter while creating a new template. Now, when creating a new template and specifying 'netboot' as source media parameter, it is assumed the template will use netboot process - PXE/DHCP/TFTP/(NFS/HTTP/FTP). This is part of solution to Issue #372. Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- API.json | 18 ++++++++++++++---- i18n.py | 1 + model/templates.py | 27 ++++++++++++++++++++++++--- vmtemplate.py | 4 ++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/API.json b/API.json index 5ef4f55..285b650 100644 --- a/API.json +++ b/API.json @@ -476,7 +476,6 @@ }, "templates_create": { "type": "object", - "error": "KCHTMPL0016E", "properties": { "name": { "description": "The name of the template", @@ -505,9 +504,20 @@ }, "memory": { "$ref": "#/kimchitype/memory" }, "source_media": { - "description": "Path for installation media (ISO, disk, remote ISO)", - "type" : "string", - "pattern" : "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "type" : "object", + "properties" : { + "type": { + "description": "Type of source media: disk or netboot", + "type": "string", + "pattern": "^disk|netboot$", + "required": true + }, + "path": { + "description": "Path for installation media (ISO, disk, remote ISO)", + "type": "string", + "pattern" : "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$" + } + }, "required": true }, "disks": { diff --git a/i18n.py b/i18n.py index 6f88fae..0e1de77 100644 --- a/i18n.py +++ b/i18n.py @@ -157,6 +157,7 @@ messages = { "KCHTMPL0001E": _("Template %(name)s already exists"), "KCHTMPL0003E": _("Network '%(network)s' specified for template %(template)s does not exist"), "KCHTMPL0004E": _("Storage pool %(pool)s specified for template %(template)s does not exist"), + "KCHTMPL0005E": _("Invalid parameter specified for source media: '%(param)s'"), "KCHTMPL0006E": _("Invalid parameter '%(param)s' specified for CDROM."), "KCHTMPL0007E": _("Network %(network)s specified for template %(template)s is not active"), "KCHTMPL0008E": _("Template name must be a string"), diff --git a/model/templates.py b/model/templates.py index a02099c..61314f1 100644 --- a/model/templates.py +++ b/model/templates.py @@ -62,10 +62,28 @@ class TemplatesModel(object): raise InvalidParameter("KCHTMPL0003E", {'network': net_name, 'template': name}) - # get source_media - path = params.pop("source_media") + # Get source_media + source_media = params.get("source_media", None) + + if source_media is None: + raise InvalidParameter("KCHTMPL0005E", {'param': 'type = None'}) + + if source_media['type'] == 'netboot': + params['os_distro'] = 'unknown' + params['os_version'] = 'unknown' + return self.save_template(params) + elif source_media['type'] == 'disk': + # Get path of source media if it's based on disk type. + path = source_media.get('path', None) + else: + raise InvalidParameter("KCHTMPL0005E", {'param': 'type = %s' % + source_media['type']}) + + if path is None: + raise InvalidParameter("KCHTMPL0016E") # not local image: set as remote ISO + path = path.encode('utf-8') if urlparse.urlparse(path).scheme in ["http", "https", "tftp", "ftp", "ftps"]: params["cdrom"] = path @@ -104,7 +122,10 @@ class TemplatesModel(object): def save_template(self, params): # Creates the template class with necessary information - t = LibvirtVMTemplate(params, scan=True, conn=self.conn) + if 'cdrom' not in params.keys(): + t = LibvirtVMTemplate(params, conn=self.conn) + else: + t = LibvirtVMTemplate(params, scan=True, conn=self.conn) # Validate cpu info t.cpuinfo_validate() diff --git a/vmtemplate.py b/vmtemplate.py index a223beb..3e4418f 100644 --- a/vmtemplate.py +++ b/vmtemplate.py @@ -123,6 +123,10 @@ class VMTemplate(object): def _get_os_info(self, args, scan): distro = version = 'unknown' + if 'source_media' in args.keys(): + if args['source_media']['type'] == 'netboot': + return distro, version + # Identify the cdrom if present iso = args.get('cdrom', '') if len(iso) > 0: -- 2.5.5

On 04/20/2016 05:22 PM, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
Changed API.json and model to accept 'netboot' as source media parameter while creating a new template.
Now, when creating a new template and specifying 'netboot' as source media parameter, it is assumed the template will use netboot process - PXE/DHCP/TFTP/(NFS/HTTP/FTP).
This is part of solution to Issue #372.
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- API.json | 18 ++++++++++++++---- i18n.py | 1 + model/templates.py | 27 ++++++++++++++++++++++++--- vmtemplate.py | 4 ++++ 4 files changed, 43 insertions(+), 7 deletions(-)
diff --git a/API.json b/API.json index 5ef4f55..285b650 100644 --- a/API.json +++ b/API.json @@ -476,7 +476,6 @@ }, "templates_create": { "type": "object",
- "error": "KCHTMPL0016E",
This error is raised when no source_media object is passed. So you need to keep it here otherwise jsonschema will raise a default error that will not have i18n support.
"properties": { "name": { "description": "The name of the template", @@ -505,9 +504,20 @@ }, "memory": { "$ref": "#/kimchitype/memory" }, "source_media": {
Add additionalProperties:false, to ensure no other parameters will be accepted in the source_media object.
- "description": "Path for installation media (ISO, disk, remote ISO)", - "type" : "string", - "pattern" : "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", + "type" : "object", + "properties" : { + "type": { + "description": "Type of source media: disk or netboot", + "type": "string", + "pattern": "^disk|netboot$", + "required": true + }, + "path": { + "description": "Path for installation media (ISO, disk, remote ISO)", + "type": "string", + "pattern" : "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$" + } + }, "required": true }, "disks": { diff --git a/i18n.py b/i18n.py index 6f88fae..0e1de77 100644 --- a/i18n.py +++ b/i18n.py @@ -157,6 +157,7 @@ messages = { "KCHTMPL0001E": _("Template %(name)s already exists"), "KCHTMPL0003E": _("Network '%(network)s' specified for template %(template)s does not exist"), "KCHTMPL0004E": _("Storage pool %(pool)s specified for template %(template)s does not exist"), + "KCHTMPL0005E": _("Invalid parameter specified for source media: '%(param)s'"), "KCHTMPL0006E": _("Invalid parameter '%(param)s' specified for CDROM."), "KCHTMPL0007E": _("Network %(network)s specified for template %(template)s is not active"), "KCHTMPL0008E": _("Template name must be a string"), diff --git a/model/templates.py b/model/templates.py index a02099c..61314f1 100644 --- a/model/templates.py +++ b/model/templates.py @@ -62,10 +62,28 @@ class TemplatesModel(object): raise InvalidParameter("KCHTMPL0003E", {'network': net_name, 'template': name})
- # get source_media - path = params.pop("source_media") + # Get source_media + source_media = params.get("source_media", None) +
+ if source_media is None: + raise InvalidParameter("KCHTMPL0005E", {'param': 'type = None'}) +
As you marked source_media and type as required parameters in jsonschema you don't need to do that validation here as at this time source_media will be a valid input.
+ if source_media['type'] == 'netboot': + params['os_distro'] = 'unknown' + params['os_version'] = 'unknown' + return self.save_template(params) + elif source_media['type'] == 'disk': + # Get path of source media if it's based on disk type. + path = source_media.get('path', None)
+ else: + raise InvalidParameter("KCHTMPL0005E", {'param': 'type = %s' % + source_media['type']}) +
If you add "additionalProperties:false" to jsonschema, you can remove the else statement above and source_media will be always a valid parameter.
+ if path is None: + raise InvalidParameter("KCHTMPL0016E")
# not local image: set as remote ISO + path = path.encode('utf-8') if urlparse.urlparse(path).scheme in ["http", "https", "tftp", "ftp", "ftps"]: params["cdrom"] = path @@ -104,7 +122,10 @@ class TemplatesModel(object): def save_template(self, params):
# Creates the template class with necessary information - t = LibvirtVMTemplate(params, scan=True, conn=self.conn)
+ if 'cdrom' not in params.keys(): + t = LibvirtVMTemplate(params, conn=self.conn) + else: + t = LibvirtVMTemplate(params, scan=True, conn=self.conn)
Why did you change it? I don't think it is related to this netboot feature. Is there any bug you are trying to fix? or... ?
# Validate cpu info t.cpuinfo_validate() diff --git a/vmtemplate.py b/vmtemplate.py index a223beb..3e4418f 100644 --- a/vmtemplate.py +++ b/vmtemplate.py @@ -123,6 +123,10 @@ class VMTemplate(object): def _get_os_info(self, args, scan): distro = version = 'unknown'
+ if 'source_media' in args.keys(): + if args['source_media']['type'] == 'netboot': + return distro, version +
# Identify the cdrom if present iso = args.get('cdrom', '') if len(iso) > 0:

From: Paulo Vital <pvital@linux.vnet.ibm.com> Add support to create guests without CD-ROM device information and setting the second boot order option as 'netboot'. This is part of solution to Issue #372. Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- vmtemplate.py | 24 +++++++++++++++--------- xmlutils/bootorder.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 xmlutils/bootorder.py diff --git a/vmtemplate.py b/vmtemplate.py index 3e4418f..0611a07 100644 --- a/vmtemplate.py +++ b/vmtemplate.py @@ -33,6 +33,7 @@ from wok.plugins.kimchi import imageinfo from wok.plugins.kimchi import osinfo from wok.plugins.kimchi.isoinfo import IsoImage from wok.plugins.kimchi.utils import check_url_path, pool_name_from_uri +from wok.plugins.kimchi.xmlutils.bootorder import get_bootorder_xml from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml from wok.plugins.kimchi.xmlutils.disk import get_disk_xml from wok.plugins.kimchi.xmlutils.graphics import get_graphics_xml @@ -174,7 +175,7 @@ class VMTemplate(object): def _get_cdrom_xml(self, libvirt_stream_protocols): if 'cdrom' not in self.info: - return '' + return None params = {} params['type'] = 'cdrom' @@ -354,12 +355,18 @@ class VMTemplate(object): libvirt_stream_protocols = kwargs.get('libvirt_stream_protocols', []) cdrom_xml = self._get_cdrom_xml(libvirt_stream_protocols) - if not urlparse.urlparse(self.info.get('cdrom', "")).scheme in \ - libvirt_stream_protocols and \ - params.get('iso_stream', False): - params['qemu-stream-cmdline'] = cdrom_xml - else: - params['cdroms'] = cdrom_xml + # Add information of CD-ROM device only if template have info about it. + if cdrom_xml is not None: + if not urlparse.urlparse(self.info.get('cdrom', "")).scheme in \ + libvirt_stream_protocols and \ + params.get('iso_stream', False): + params['qemu-stream-cmdline'] = cdrom_xml + else: + params['cdroms'] = cdrom_xml + + # Set the boot order of VM + # TODO: need modify this when boot order edition feature came upstream. + params['boot_order'] = get_bootorder_xml() # Setting maximum number of slots to avoid errors when hotplug memory # Number of slots are the numbers of chunks of 1GB that fit inside @@ -413,8 +420,7 @@ class VMTemplate(object): %(cpu_info_xml)s <os> <type arch='%(arch)s'>hvm</type> - <boot dev='hd'/> - <boot dev='cdrom'/> + %(boot_order)s </os> <features> <acpi/> diff --git a/xmlutils/bootorder.py b/xmlutils/bootorder.py new file mode 100644 index 0000000..28457e7 --- /dev/null +++ b/xmlutils/bootorder.py @@ -0,0 +1,39 @@ +# +# Project Kimchi +# +# Copyright IBM Corp, 2016 +# +# 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 lxml.etree as ET +from lxml.builder import E + + +def get_bootorder_xml(boot_order=['hd', 'cdrom', 'network']): + """ + Returns the XML for boot order. The default return includes the following: + + <boot dev='hd'/> + <boot dev='cdrom'/> + <boot dev='network'/> + + To a different boot order, specify the order by a list as argument. + """ + boot_xml = '' + for device in boot_order: + boot = E.boot(dev=device) + boot_xml += ET.tostring(boot, encoding='utf-8', pretty_print=True) + + return boot_xml -- 2.5.5

From: Paulo Vital <pvital@linux.vnet.ibm.com> Update test cases to support the creation of netboot templates and creation of guests without cdrom. This is part of solution to Issue #372. Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- tests/test_authorization.py | 4 +++- tests/test_mockmodel.py | 15 ++++++++----- tests/test_model.py | 43 +++++++++++++++++++++--------------- tests/test_rest.py | 37 +++++++++++++++++++++---------- tests/test_template.py | 53 ++++++++++++++++++++++++++++++++++----------- tests/test_vmtemplate.py | 18 +++++++++++++++ 6 files changed, 122 insertions(+), 48 deletions(-) diff --git a/tests/test_authorization.py b/tests/test_authorization.py index 7cf8da2..cbeca04 100644 --- a/tests/test_authorization.py +++ b/tests/test_authorization.py @@ -100,7 +100,9 @@ class AuthorizationTests(unittest.TestCase): self.assertEquals(403, resp.status) # Non-root users can only get vms authorized to them - model.templates_create({'name': u'test', 'source_media': fake_iso}) + model.templates_create({'name': u'test', + 'source_media': {'type': 'disk', + 'path': fake_iso}}) task_info = model.vms_create({ 'name': u'test-me', diff --git a/tests/test_mockmodel.py b/tests/test_mockmodel.py index 41156f9..d5fffb4 100644 --- a/tests/test_mockmodel.py +++ b/tests/test_mockmodel.py @@ -66,7 +66,8 @@ class MockModelTests(unittest.TestCase): def test_screenshot_refresh(self): # Create a VM - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) request(host, ssl_port, '/plugins/kimchi/templates', req, 'POST') req = json.dumps({'name': 'test-vm', 'template': '/plugins/kimchi/templates/test'}) @@ -96,7 +97,8 @@ class MockModelTests(unittest.TestCase): resp.getheader('last-modified')) def test_vm_list_sorted(self): - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) request(host, ssl_port, '/plugins/kimchi/templates', req, 'POST') def add_vm(name): @@ -116,7 +118,8 @@ class MockModelTests(unittest.TestCase): def test_memory_window_changes(self): model.templates_create({'name': u'test', - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', + 'path': fake_iso}}) task = model.vms_create({'name': u'test-vm', 'template': '/plugins/kimchi/templates/test'}) wait_task(model.task_lookup, task['id']) @@ -128,7 +131,8 @@ class MockModelTests(unittest.TestCase): def test_hotplug_3D_card(self): model.templates_create({'name': u'test', - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', + 'path': fake_iso}}) task = model.vms_create({'name': u'test-vm', 'template': '/plugins/kimchi/templates/test'}) wait_task(model.task_lookup, task['id']) @@ -148,7 +152,8 @@ class MockModelTests(unittest.TestCase): def test_vm_info(self): model.templates_create({'name': u'test', - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', + 'path': fake_iso}}) task = model.vms_create({'name': u'test-vm', 'template': '/plugins/kimchi/templates/test'}) wait_task(model.task_lookup, task['id']) diff --git a/tests/test_model.py b/tests/test_model.py index 0804228..edab447 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -136,7 +136,8 @@ class ModelTests(unittest.TestCase): task = inst.task_lookup(task['id']) self.assertEquals('finished', task['status']) - params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -269,7 +270,7 @@ class ModelTests(unittest.TestCase): "networks": ["default"], "memory": {'current': 1024}, "folder": [], "icon": "images/icon-vm.png", "os_distro": "unknown", "os_version": "unknown", - "source_media": vol_path} + "source_media": {'type': 'disk', 'path': vol_path}} inst.templates_create(tmpl_info) rollback.prependDefer(inst.template_delete, tmpl_name) @@ -296,7 +297,8 @@ class ModelTests(unittest.TestCase): @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_graphics(self): inst = model.Model(objstore_loc=self.tmp_store) - params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) with RollbackContext() as rollback: params = {'name': 'kimchi-vnc', @@ -326,7 +328,8 @@ class ModelTests(unittest.TestCase): @unittest.skipUnless(utils.running_as_root(), "Must be run as root") def test_vm_serial(self): inst = model.Model(objstore_loc=self.tmp_store) - params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) with RollbackContext() as rollback: params = {'name': 'kimchi-serial', @@ -347,7 +350,8 @@ class ModelTests(unittest.TestCase): def test_vm_ifaces(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: - params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -463,7 +467,8 @@ class ModelTests(unittest.TestCase): inst.task_wait(task_id) vm_name = 'kimchi-cdrom' - params = {'name': 'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': 'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': vm_name, @@ -507,7 +512,7 @@ class ModelTests(unittest.TestCase): vm_name = 'kimchi-ide-bus-vm' params = {'name': 'old_distro_template', 'disks': [], - 'source_media': old_distro_iso} + 'source_media': {'type': 'disk', 'path': old_distro_iso}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'old_distro_template') params = { @@ -532,7 +537,8 @@ class ModelTests(unittest.TestCase): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: vm_name = 'kimchi-cdrom' - params = {'name': 'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': 'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': vm_name, @@ -623,7 +629,7 @@ class ModelTests(unittest.TestCase): with RollbackContext() as rollback: params = {'name': 'test', 'disks': [{'size': 1, 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -683,7 +689,7 @@ class ModelTests(unittest.TestCase): params = {'name': 'test', 'disks': [{'size': 1, 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -711,7 +717,7 @@ class ModelTests(unittest.TestCase): params = {'name': 'test', 'disks': [{ 'size': 1, 'format': user_vol, 'pool': {'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -737,7 +743,7 @@ class ModelTests(unittest.TestCase): params = {'name': 'test', 'disks': [{'size': 1, 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -757,7 +763,7 @@ class ModelTests(unittest.TestCase): inst = model.Model(None, objstore_loc=self.tmp_store) orig_params = {'name': 'test', 'memory': {'current': 1024, 'maxmemory': 3072}, - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(orig_params) with RollbackContext() as rollback: @@ -794,7 +800,8 @@ class ModelTests(unittest.TestCase): # only supports this format orig_params = { 'name': 'test', 'memory': {'current': 1024}, - 'cpu_info': {'vcpus': 1}, 'source_media': UBUNTU_ISO, + 'cpu_info': {'vcpus': 1}, + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}, 'disks': [{'size': 1, 'format': 'qcow2', 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}]} inst.templates_create(orig_params) @@ -1033,7 +1040,8 @@ class ModelTests(unittest.TestCase): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: - params = {'name': u'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': u'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -1059,7 +1067,8 @@ class ModelTests(unittest.TestCase): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: - params = {'name': 'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': 'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -1130,7 +1139,7 @@ class ModelTests(unittest.TestCase): with RollbackContext() as rollback: params = { 'name': 'test', - 'source_media': UBUNTU_ISO, + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}, 'domain': 'test', 'arch': 'i686', 'disks': [] diff --git a/tests/test_rest.py b/tests/test_rest.py index a009ed3..cf86926 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -106,7 +106,8 @@ class RestTests(unittest.TestCase): self.assertEquals(1, len(vms)) # Create a template as a base for our VMs - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -133,7 +134,8 @@ class RestTests(unittest.TestCase): self.assertEquals([], vm['groups']) def test_edit_vm(self): - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -290,7 +292,8 @@ class RestTests(unittest.TestCase): def test_vm_lifecycle(self): # Create a Template - req = json.dumps({'name': 'test', 'source_media': fake_iso, + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}, 'disks': DISKS, 'icon': 'plugins/kimchi/images/icon-debian.png'}) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -517,7 +520,8 @@ class RestTests(unittest.TestCase): def test_vm_graphics(self): # Create a Template - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -611,7 +615,9 @@ class RestTests(unittest.TestCase): with RollbackContext() as rollback: # Create a template as a base for our VMs - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', + 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) # Delete the template @@ -784,7 +790,9 @@ class RestTests(unittest.TestCase): with RollbackContext() as rollback: # Create a template as a base for our VMs - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', + 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) # Delete the template @@ -891,7 +899,7 @@ class RestTests(unittest.TestCase): def test_vm_customise_storage(self): # Create a Template req = json.dumps({'name': 'test', 'disks': DISKS, - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -953,7 +961,8 @@ class RestTests(unittest.TestCase): # Create template fails because SCSI volume is missing tmpl_params = { - 'name': 'test_fc_pool', 'source_media': fake_iso, + 'name': 'test_fc_pool', + 'source_media': {'type': 'disk', 'path': fake_iso}, 'disks': [{'pool': {'name': '/plugins/kimchi/storagepools/scsi_fc_pool'}}]} @@ -998,7 +1007,8 @@ class RestTests(unittest.TestCase): def test_unnamed_vms(self): # Create a Template - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -1040,7 +1050,8 @@ class RestTests(unittest.TestCase): # Create a Template mock_base = '/tmp/mock.img' os.system("qemu-img create -f qcow2 %s 10M" % mock_base) - req = json.dumps({'name': 'test', 'source_media': mock_base}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': mock_base}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -1110,7 +1121,8 @@ class RestTests(unittest.TestCase): # In real model os distro/version can be omitted # as we will scan the iso req = json.dumps({'name': 'test', - 'source_media': storagevolume['path'], + 'source_media': {'type': 'disk', + 'path': storagevolume['path']}, 'os_distro': storagevolume['os_distro'], 'os_version': storagevolume['os_version']}) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -1148,7 +1160,8 @@ class RestTests(unittest.TestCase): def test_screenshot_refresh(self): # Create a VM - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') req = json.dumps({'name': 'test-vm', 'template': '/plugins/kimchi/templates/test'}) diff --git a/tests/test_template.py b/tests/test_template.py index 158bbeb..71835ae 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -80,8 +80,26 @@ class TemplateTests(unittest.TestCase): resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(400, resp.status) + # Create a netboot template + t = {'name': 'test-netboot', 'source_media': {'type': 'netboot'}} + req = json.dumps(t) + resp = self.request('/plugins/kimchi/templates', req, 'POST') + self.assertEquals(201, resp.status) + + # Verify the netboot template + tmpl = json.loads( + self.request('/plugins/kimchi/templates/test-netboot').read() + ) + self.assertIsNone(tmpl['cdrom']) + + # Delete the netboot template + resp = self.request('/plugins/kimchi/templates/test-netboot', '{}', + 'DELETE') + self.assertEquals(204, resp.status) + # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -93,7 +111,7 @@ class TemplateTests(unittest.TestCase): self.request('/plugins/kimchi/templates/test').read() ) self.assertEquals(sorted(tmpl.keys()), sorted(keys)) - self.assertEquals(t['source_media'], tmpl["cdrom"]) + self.assertEquals(t['source_media']['path'], tmpl["cdrom"]) disk_keys = ['index', 'pool', 'size', 'format'] disk_pool_keys = ['name', 'type'] self.assertEquals(sorted(tmpl['disks'][0].keys()), sorted(disk_keys)) @@ -119,22 +137,25 @@ class TemplateTests(unittest.TestCase): self.assertEquals(204, resp.status) # Create a template with same name fails with 400 - req = json.dumps({'name': 'test', 'source_media': MOCK_ISO}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(400, resp.status) # Create an image based template os.system("qemu-img create -f qcow2 %s 10G" % '/tmp/mock.img') - t = {'name': 'test_img_template', 'source_media': '/tmp/mock.img'} + t = {'name': 'test_img_template', + 'source_media': {'type': 'disk', 'path': '/tmp/mock.img'}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) os.remove('/tmp/mock.img') # Test disk format - t = {'name': 'test-format', 'source_media': MOCK_ISO, 'disks': [{ - 'size': 10, 'format': 'vmdk', 'pool': { - 'name': DEFAULT_POOL}}]} + t = {'name': 'test-format', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}, + 'disks': [{'size': 10, 'format': 'vmdk', + 'pool': {'name': DEFAULT_POOL}}]} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -150,7 +171,8 @@ class TemplateTests(unittest.TestCase): else: max_mem = (psutil.TOTAL_PHYMEM >> 10 >> 10) memory = max_mem + 1024 - t = {'name': 'test-maxmem', 'source_media': MOCK_ISO, + t = {'name': 'test-maxmem', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}, 'memory': {'current': memory}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -159,7 +181,8 @@ class TemplateTests(unittest.TestCase): def test_customized_tmpl(self): # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -329,7 +352,8 @@ class TemplateTests(unittest.TestCase): def test_customized_network(self): # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -365,7 +389,8 @@ class TemplateTests(unittest.TestCase): def test_customized_storagepool(self): # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -456,8 +481,10 @@ class TemplateTests(unittest.TestCase): self.request(pool_uri + '/activate', '{}', 'POST') # Create a template using the custom network and pool - t = {'name': 'test', 'source_media': mock_iso2, - 'networks': ['nat-network'], 'disks': [{'pool': { + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': mock_iso2}, + 'networks': ['nat-network'], + 'disks': [{'pool': { 'name': '/plugins/kimchi/storagepools/dir-pool'}, 'size': 2, 'format': 'qcow2'}]} diff --git a/tests/test_vmtemplate.py b/tests/test_vmtemplate.py index 503d34a..772093e 100644 --- a/tests/test_vmtemplate.py +++ b/tests/test_vmtemplate.py @@ -135,3 +135,21 @@ class VMTemplateTests(unittest.TestCase): self.assertEquals(['foo'], t.info.get('networks')) self.assertEquals(self.iso, t.info.get('cdrom')) self.assertEquals(graphics, t.info.get('graphics')) + + def test_netboot_vmtemplate(self): + disk_bus = get_template_default('old', 'disk_bus') + memory = get_template_default('old', 'memory') + nic_model = get_template_default('old', 'nic_model') + fields = (('name', 'test'), ('os_distro', 'unknown'), + ('os_version', 'unknown'), + ('cpu_info', {'vcpus': 1, 'maxvcpus': 1}), + ('memory', memory), ('networks', ['default']), + ('disk_bus', disk_bus), ('nic_model', nic_model), + ('graphics', {'type': 'vnc', 'listen': '127.0.0.1'})) + + args = {'name': 'test', 'source_media': {'type': 'netboot'}} + t = VMTemplate(args) + for name, val in fields: + self.assertEquals(val, t.info.get(name)) + + self.assertNotIn('cdrom', t.info.keys()) -- 2.5.5

On 04/20/2016 05:22 PM, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
Update test cases to support the creation of netboot templates and creation of guests without cdrom.
This is part of solution to Issue #372.
Signed-off-by: Paulo Vital <pvital@linux.vnet.ibm.com> --- tests/test_authorization.py | 4 +++- tests/test_mockmodel.py | 15 ++++++++----- tests/test_model.py | 43 +++++++++++++++++++++--------------- tests/test_rest.py | 37 +++++++++++++++++++++---------- tests/test_template.py | 53 ++++++++++++++++++++++++++++++++++----------- tests/test_vmtemplate.py | 18 +++++++++++++++ 6 files changed, 122 insertions(+), 48 deletions(-)
diff --git a/tests/test_authorization.py b/tests/test_authorization.py index 7cf8da2..cbeca04 100644 --- a/tests/test_authorization.py +++ b/tests/test_authorization.py @@ -100,7 +100,9 @@ class AuthorizationTests(unittest.TestCase): self.assertEquals(403, resp.status)
# Non-root users can only get vms authorized to them - model.templates_create({'name': u'test', 'source_media': fake_iso}) + model.templates_create({'name': u'test', + 'source_media': {'type': 'disk', + 'path': fake_iso}})
task_info = model.vms_create({ 'name': u'test-me', diff --git a/tests/test_mockmodel.py b/tests/test_mockmodel.py index 41156f9..d5fffb4 100644 --- a/tests/test_mockmodel.py +++ b/tests/test_mockmodel.py @@ -66,7 +66,8 @@ class MockModelTests(unittest.TestCase):
def test_screenshot_refresh(self): # Create a VM - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) request(host, ssl_port, '/plugins/kimchi/templates', req, 'POST') req = json.dumps({'name': 'test-vm', 'template': '/plugins/kimchi/templates/test'}) @@ -96,7 +97,8 @@ class MockModelTests(unittest.TestCase): resp.getheader('last-modified'))
def test_vm_list_sorted(self): - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) request(host, ssl_port, '/plugins/kimchi/templates', req, 'POST')
def add_vm(name): @@ -116,7 +118,8 @@ class MockModelTests(unittest.TestCase):
def test_memory_window_changes(self): model.templates_create({'name': u'test', - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', + 'path': fake_iso}}) task = model.vms_create({'name': u'test-vm', 'template': '/plugins/kimchi/templates/test'}) wait_task(model.task_lookup, task['id']) @@ -128,7 +131,8 @@ class MockModelTests(unittest.TestCase):
def test_hotplug_3D_card(self): model.templates_create({'name': u'test', - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', + 'path': fake_iso}}) task = model.vms_create({'name': u'test-vm', 'template': '/plugins/kimchi/templates/test'}) wait_task(model.task_lookup, task['id']) @@ -148,7 +152,8 @@ class MockModelTests(unittest.TestCase):
def test_vm_info(self): model.templates_create({'name': u'test', - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', + 'path': fake_iso}}) task = model.vms_create({'name': u'test-vm', 'template': '/plugins/kimchi/templates/test'}) wait_task(model.task_lookup, task['id']) diff --git a/tests/test_model.py b/tests/test_model.py index 0804228..edab447 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -136,7 +136,8 @@ class ModelTests(unittest.TestCase): task = inst.task_lookup(task['id']) self.assertEquals('finished', task['status'])
- params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}}
inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -269,7 +270,7 @@ class ModelTests(unittest.TestCase): "networks": ["default"], "memory": {'current': 1024}, "folder": [], "icon": "images/icon-vm.png", "os_distro": "unknown", "os_version": "unknown", - "source_media": vol_path} + "source_media": {'type': 'disk', 'path': vol_path}}
inst.templates_create(tmpl_info) rollback.prependDefer(inst.template_delete, tmpl_name) @@ -296,7 +297,8 @@ class ModelTests(unittest.TestCase): @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_graphics(self): inst = model.Model(objstore_loc=self.tmp_store) - params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) with RollbackContext() as rollback: params = {'name': 'kimchi-vnc', @@ -326,7 +328,8 @@ class ModelTests(unittest.TestCase): @unittest.skipUnless(utils.running_as_root(), "Must be run as root") def test_vm_serial(self): inst = model.Model(objstore_loc=self.tmp_store) - params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) with RollbackContext() as rollback: params = {'name': 'kimchi-serial', @@ -347,7 +350,8 @@ class ModelTests(unittest.TestCase): def test_vm_ifaces(self): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: - params = {'name': 'test', 'source_media': UBUNTU_ISO} + params = {'name': 'test', + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test')
@@ -463,7 +467,8 @@ class ModelTests(unittest.TestCase): inst.task_wait(task_id)
vm_name = 'kimchi-cdrom' - params = {'name': 'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': 'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': vm_name, @@ -507,7 +512,7 @@ class ModelTests(unittest.TestCase):
vm_name = 'kimchi-ide-bus-vm' params = {'name': 'old_distro_template', 'disks': [], - 'source_media': old_distro_iso} + 'source_media': {'type': 'disk', 'path': old_distro_iso}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'old_distro_template') params = { @@ -532,7 +537,8 @@ class ModelTests(unittest.TestCase): inst = model.Model(objstore_loc=self.tmp_store) with RollbackContext() as rollback: vm_name = 'kimchi-cdrom' - params = {'name': 'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': 'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') params = {'name': vm_name, @@ -623,7 +629,7 @@ class ModelTests(unittest.TestCase): with RollbackContext() as rollback: params = {'name': 'test', 'disks': [{'size': 1, 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}}
inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -683,7 +689,7 @@ class ModelTests(unittest.TestCase):
params = {'name': 'test', 'disks': [{'size': 1, 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test')
@@ -711,7 +717,7 @@ class ModelTests(unittest.TestCase): params = {'name': 'test', 'disks': [{ 'size': 1, 'format': user_vol, 'pool': {'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}}
inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test') @@ -737,7 +743,7 @@ class ModelTests(unittest.TestCase):
params = {'name': 'test', 'disks': [{'size': 1, 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}], - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test')
@@ -757,7 +763,7 @@ class ModelTests(unittest.TestCase): inst = model.Model(None, objstore_loc=self.tmp_store) orig_params = {'name': 'test', 'memory': {'current': 1024, 'maxmemory': 3072}, - 'source_media': UBUNTU_ISO} + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(orig_params)
with RollbackContext() as rollback: @@ -794,7 +800,8 @@ class ModelTests(unittest.TestCase): # only supports this format orig_params = { 'name': 'test', 'memory': {'current': 1024}, - 'cpu_info': {'vcpus': 1}, 'source_media': UBUNTU_ISO, + 'cpu_info': {'vcpus': 1}, + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}, 'disks': [{'size': 1, 'format': 'qcow2', 'pool': { 'name': '/plugins/kimchi/storagepools/default'}}]} inst.templates_create(orig_params) @@ -1033,7 +1040,8 @@ class ModelTests(unittest.TestCase): inst = model.Model(objstore_loc=self.tmp_store)
with RollbackContext() as rollback: - params = {'name': u'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': u'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test')
@@ -1059,7 +1067,8 @@ class ModelTests(unittest.TestCase): inst = model.Model(objstore_loc=self.tmp_store)
with RollbackContext() as rollback: - params = {'name': 'test', 'disks': [], 'source_media': UBUNTU_ISO} + params = {'name': 'test', 'disks': [], + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}} inst.templates_create(params) rollback.prependDefer(inst.template_delete, 'test')
@@ -1130,7 +1139,7 @@ class ModelTests(unittest.TestCase): with RollbackContext() as rollback: params = { 'name': 'test', - 'source_media': UBUNTU_ISO, + 'source_media': {'type': 'disk', 'path': UBUNTU_ISO}, 'domain': 'test', 'arch': 'i686', 'disks': [] diff --git a/tests/test_rest.py b/tests/test_rest.py index a009ed3..cf86926 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -106,7 +106,8 @@ class RestTests(unittest.TestCase): self.assertEquals(1, len(vms))
# Create a template as a base for our VMs - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status)
@@ -133,7 +134,8 @@ class RestTests(unittest.TestCase): self.assertEquals([], vm['groups'])
def test_edit_vm(self): - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status)
@@ -290,7 +292,8 @@ class RestTests(unittest.TestCase):
def test_vm_lifecycle(self): # Create a Template - req = json.dumps({'name': 'test', 'source_media': fake_iso, + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}, 'disks': DISKS, 'icon': 'plugins/kimchi/images/icon-debian.png'}) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -517,7 +520,8 @@ class RestTests(unittest.TestCase):
def test_vm_graphics(self): # Create a Template - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status)
@@ -611,7 +615,9 @@ class RestTests(unittest.TestCase):
with RollbackContext() as rollback: # Create a template as a base for our VMs - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', + 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) # Delete the template @@ -784,7 +790,9 @@ class RestTests(unittest.TestCase):
with RollbackContext() as rollback: # Create a template as a base for our VMs - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', + 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) # Delete the template @@ -891,7 +899,7 @@ class RestTests(unittest.TestCase): def test_vm_customise_storage(self): # Create a Template req = json.dumps({'name': 'test', 'disks': DISKS, - 'source_media': fake_iso}) + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status)
@@ -953,7 +961,8 @@ class RestTests(unittest.TestCase):
# Create template fails because SCSI volume is missing tmpl_params = { - 'name': 'test_fc_pool', 'source_media': fake_iso, + 'name': 'test_fc_pool', + 'source_media': {'type': 'disk', 'path': fake_iso}, 'disks': [{'pool': {'name': '/plugins/kimchi/storagepools/scsi_fc_pool'}}]}
@@ -998,7 +1007,8 @@ class RestTests(unittest.TestCase):
def test_unnamed_vms(self): # Create a Template - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status)
@@ -1040,7 +1050,8 @@ class RestTests(unittest.TestCase): # Create a Template mock_base = '/tmp/mock.img' os.system("qemu-img create -f qcow2 %s 10M" % mock_base) - req = json.dumps({'name': 'test', 'source_media': mock_base}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': mock_base}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status)
@@ -1110,7 +1121,8 @@ class RestTests(unittest.TestCase): # In real model os distro/version can be omitted # as we will scan the iso req = json.dumps({'name': 'test', - 'source_media': storagevolume['path'], + 'source_media': {'type': 'disk', + 'path': storagevolume['path']}, 'os_distro': storagevolume['os_distro'], 'os_version': storagevolume['os_version']}) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -1148,7 +1160,8 @@ class RestTests(unittest.TestCase):
def test_screenshot_refresh(self): # Create a VM - req = json.dumps({'name': 'test', 'source_media': fake_iso}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': fake_iso}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') req = json.dumps({'name': 'test-vm', 'template': '/plugins/kimchi/templates/test'}) diff --git a/tests/test_template.py b/tests/test_template.py index 158bbeb..71835ae 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -80,8 +80,26 @@ class TemplateTests(unittest.TestCase): resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(400, resp.status)
+ # Create a netboot template + t = {'name': 'test-netboot', 'source_media': {'type': 'netboot'}} + req = json.dumps(t) + resp = self.request('/plugins/kimchi/templates', req, 'POST') + self.assertEquals(201, resp.status) + + # Verify the netboot template + tmpl = json.loads( + self.request('/plugins/kimchi/templates/test-netboot').read() + ) + self.assertIsNone(tmpl['cdrom']) +
Please, create a vm using the netboot template and start/stop it just to make sure the vm created has a valid XML configuration for netboot.
+ # Delete the netboot template + resp = self.request('/plugins/kimchi/templates/test-netboot', '{}', + 'DELETE') + self.assertEquals(204, resp.status) + # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -93,7 +111,7 @@ class TemplateTests(unittest.TestCase): self.request('/plugins/kimchi/templates/test').read() ) self.assertEquals(sorted(tmpl.keys()), sorted(keys)) - self.assertEquals(t['source_media'], tmpl["cdrom"]) + self.assertEquals(t['source_media']['path'], tmpl["cdrom"]) disk_keys = ['index', 'pool', 'size', 'format'] disk_pool_keys = ['name', 'type'] self.assertEquals(sorted(tmpl['disks'][0].keys()), sorted(disk_keys)) @@ -119,22 +137,25 @@ class TemplateTests(unittest.TestCase): self.assertEquals(204, resp.status)
# Create a template with same name fails with 400 - req = json.dumps({'name': 'test', 'source_media': MOCK_ISO}) + req = json.dumps({'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}}) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(400, resp.status)
# Create an image based template os.system("qemu-img create -f qcow2 %s 10G" % '/tmp/mock.img') - t = {'name': 'test_img_template', 'source_media': '/tmp/mock.img'} + t = {'name': 'test_img_template', + 'source_media': {'type': 'disk', 'path': '/tmp/mock.img'}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) os.remove('/tmp/mock.img')
# Test disk format - t = {'name': 'test-format', 'source_media': MOCK_ISO, 'disks': [{ - 'size': 10, 'format': 'vmdk', 'pool': { - 'name': DEFAULT_POOL}}]} + t = {'name': 'test-format', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}, + 'disks': [{'size': 10, 'format': 'vmdk', + 'pool': {'name': DEFAULT_POOL}}]}
req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -150,7 +171,8 @@ class TemplateTests(unittest.TestCase): else: max_mem = (psutil.TOTAL_PHYMEM >> 10 >> 10) memory = max_mem + 1024 - t = {'name': 'test-maxmem', 'source_media': MOCK_ISO, + t = {'name': 'test-maxmem', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}, 'memory': {'current': memory}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') @@ -159,7 +181,8 @@ class TemplateTests(unittest.TestCase):
def test_customized_tmpl(self): # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -329,7 +352,8 @@ class TemplateTests(unittest.TestCase):
def test_customized_network(self): # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -365,7 +389,8 @@ class TemplateTests(unittest.TestCase):
def test_customized_storagepool(self): # Create a template - t = {'name': 'test', 'source_media': MOCK_ISO} + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': MOCK_ISO}} req = json.dumps(t) resp = self.request('/plugins/kimchi/templates', req, 'POST') self.assertEquals(201, resp.status) @@ -456,8 +481,10 @@ class TemplateTests(unittest.TestCase): self.request(pool_uri + '/activate', '{}', 'POST')
# Create a template using the custom network and pool - t = {'name': 'test', 'source_media': mock_iso2, - 'networks': ['nat-network'], 'disks': [{'pool': { + t = {'name': 'test', + 'source_media': {'type': 'disk', 'path': mock_iso2}, + 'networks': ['nat-network'], + 'disks': [{'pool': { 'name': '/plugins/kimchi/storagepools/dir-pool'}, 'size': 2, 'format': 'qcow2'}]} diff --git a/tests/test_vmtemplate.py b/tests/test_vmtemplate.py index 503d34a..772093e 100644 --- a/tests/test_vmtemplate.py +++ b/tests/test_vmtemplate.py @@ -135,3 +135,21 @@ class VMTemplateTests(unittest.TestCase): self.assertEquals(['foo'], t.info.get('networks')) self.assertEquals(self.iso, t.info.get('cdrom')) self.assertEquals(graphics, t.info.get('graphics')) + + def test_netboot_vmtemplate(self): + disk_bus = get_template_default('old', 'disk_bus') + memory = get_template_default('old', 'memory') + nic_model = get_template_default('old', 'nic_model') + fields = (('name', 'test'), ('os_distro', 'unknown'), + ('os_version', 'unknown'), + ('cpu_info', {'vcpus': 1, 'maxvcpus': 1}), + ('memory', memory), ('networks', ['default']), + ('disk_bus', disk_bus), ('nic_model', nic_model), + ('graphics', {'type': 'vnc', 'listen': '127.0.0.1'})) + + args = {'name': 'test', 'source_media': {'type': 'netboot'}} + t = VMTemplate(args) + for name, val in fields: + self.assertEquals(val, t.info.get(name)) + + self.assertNotIn('cdrom', t.info.keys())

Reviewed by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> On 20-04-2016 17:22, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
V4: * Changed API of source_media to be a dictionary with type and path * Adapted source code to the new API
V3: * set network as third default option to boot order
V2: * Adapted patch to new template API (automatic detection of cdrom)
V1:
This patchset adds support in backend to create templates and guests to netboot without setting a cdrom path or URL as image to install. Once created a guest to boot via network, the guest will request DHCP/TFTP/(NFS/HTTP/FTP) network installation servers to download the configured images and start the install.
Reference: http://miud.in/1Ht3
To test, use the curl commands (look the parameters used):
$ curl -k -u test -H "Content-Type: application/json" -H \ "Accept: application/json" 'https://localhost:8001/plugins/kimchi/templates' \ -X POST -d '{"name": "test-netboot", "source_media": {"type": "netboot", "path": "/var/lib/libvirt/images/netboot"}}' Enter host password for user 'test':
{ "cpu_info":{ "maxvcpus":1, "vcpus":1 }, "graphics":{ "type":"vnc", "listen":"127.0.0.1" }, "cdrom":null, "networks":[ "default" ], "icon":"plugins/kimchi/images/icon-vm.png", "os_distro":"unknown", "name":"test-netboot", "disks":[ { "index":0, "format":"qcow2", "pool":{ "type":"dir", "name":"/plugins/kimchi/storagepools/default" }, "size":10 } ], "invalid":{}, "os_version":"unknown", "memory":{ "current":1024, "maxmemory":1024 }, "folder":[] }
$ curl -k -u test -H "Content-Type: application/json" -H \ "Accept: application/json" 'https://localhost:8001/plugins/kimchi/vms' -X POST \ -d '{"name":"1netboot-test","template":"/plugins/kimchi/templates/test-netboot"}' Enter host password for user 'test':
{ "status":"running", "message":"Provisioning storages for new VM", "id":"1", "target_uri":"/plugins/kimchi/vms/1netboot-test" }
$ sudo virsh dumpxml 1netboot-test
<domain type='kvm'> <name>1netboot-test</name> <uuid>5c9fa5b3-3203-4c93-92d6-2b4103fe7b40</uuid> <metadata> <kimchi:metadata xmlns:kimchi="https://github.com/kimchi-project/kimchi"> <kimchi:os version="unknown" distro="unknown"/> </kimchi:metadata> </metadata> <memory unit='KiB'>1048576</memory> <currentMemory unit='KiB'>1048576</currentMemory> <memtune> <hard_limit unit='KiB'>2097152</hard_limit> </memtune> <vcpu placement='static'>1</vcpu> <os> <type arch='x86_64' machine='pc-i440fx-2.4'>hvm</type> <boot dev='hd'/> <boot dev='cdrom'/> <boot dev='network'/> </os> <acpi/> <apic/> <pae/> </features> <cpu> <numa> <cell id='0' cpus='0' memory='1048576' unit='KiB'/> </numa> </cpu> <clock offset='utc'/> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> <on_crash>restart</on_crash> <devices> <emulator>/usr/bin/qemu-kvm</emulator> <disk type='file' device='disk'> <driver name='qemu' type='qcow2' cache='none'/> <source file='/var/lib/libvirt/images/5c9fa5b3-3203-4c93-92d6-2b4103fe7b40-0.img'/> <target dev='hda' bus='ide'/> <address type='drive' controller='0' bus='0' target='0' unit='0'/> </disk> <controller type='usb' index='0'> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> </controller> <controller type='pci' index='0' model='pci-root'/> <controller type='ide' index='0'> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> </controller> <interface type='network'> <mac address='52:54:00:ea:7e:ad'/> <source network='default'/> <model type='e1000'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> <serial type='pty'> <target port='0'/> </serial> <console type='pty'> <target type='serial' port='0'/> </console> <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'> <listen type='address' address='127.0.0.1'/> </graphics> <sound model='ich6'> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </sound> <video> <model type='cirrus' vram='16384' heads='1'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> </video> <memballoon model='virtio'> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> </memballoon> </devices> </domain>
Paulo Vital (3): Add support to create netboot templates. Add support to create guests to netboot. Update test cases to support netboot.
API.json | 18 +++++++++++---- i18n.py | 1 + model/templates.py | 27 ++++++++++++++++++++--- tests/test_authorization.py | 4 +++- tests/test_mockmodel.py | 15 ++++++++----- tests/test_model.py | 43 +++++++++++++++++++++--------------- tests/test_rest.py | 37 +++++++++++++++++++++---------- tests/test_template.py | 53 ++++++++++++++++++++++++++++++++++----------- tests/test_vmtemplate.py | 18 +++++++++++++++ vmtemplate.py | 28 ++++++++++++++++-------- xmlutils/bootorder.py | 39 +++++++++++++++++++++++++++++++++ 11 files changed, 219 insertions(+), 64 deletions(-) create mode 100644 xmlutils/bootorder.py
-- 2.5.5
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
-- Jose Ricardo Ziviani ----------------------------- Software Engineer Linux Technology Center - IBM

Hi Paulo, As you are changing the source_media object, you need to update the current API requests from UI to do not break anything there. Otherwise, I will need to wait to the UI patch to merge this one to do not break the current Template creation via UI. Regards, Aline Manera On 04/20/2016 05:22 PM, pvital@linux.vnet.ibm.com wrote:
From: Paulo Vital <pvital@linux.vnet.ibm.com>
V4: * Changed API of source_media to be a dictionary with type and path * Adapted source code to the new API
V3: * set network as third default option to boot order
V2: * Adapted patch to new template API (automatic detection of cdrom)
V1:
This patchset adds support in backend to create templates and guests to netboot without setting a cdrom path or URL as image to install. Once created a guest to boot via network, the guest will request DHCP/TFTP/(NFS/HTTP/FTP) network installation servers to download the configured images and start the install.
Reference: http://miud.in/1Ht3
To test, use the curl commands (look the parameters used):
$ curl -k -u test -H "Content-Type: application/json" -H \ "Accept: application/json" 'https://localhost:8001/plugins/kimchi/templates' \ -X POST -d '{"name": "test-netboot", "source_media": {"type": "netboot", "path": "/var/lib/libvirt/images/netboot"}}' Enter host password for user 'test':
{ "cpu_info":{ "maxvcpus":1, "vcpus":1 }, "graphics":{ "type":"vnc", "listen":"127.0.0.1" }, "cdrom":null, "networks":[ "default" ], "icon":"plugins/kimchi/images/icon-vm.png", "os_distro":"unknown", "name":"test-netboot", "disks":[ { "index":0, "format":"qcow2", "pool":{ "type":"dir", "name":"/plugins/kimchi/storagepools/default" }, "size":10 } ], "invalid":{}, "os_version":"unknown", "memory":{ "current":1024, "maxmemory":1024 }, "folder":[] }
$ curl -k -u test -H "Content-Type: application/json" -H \ "Accept: application/json" 'https://localhost:8001/plugins/kimchi/vms' -X POST \ -d '{"name":"1netboot-test","template":"/plugins/kimchi/templates/test-netboot"}' Enter host password for user 'test':
{ "status":"running", "message":"Provisioning storages for new VM", "id":"1", "target_uri":"/plugins/kimchi/vms/1netboot-test" }
$ sudo virsh dumpxml 1netboot-test
<domain type='kvm'> <name>1netboot-test</name> <uuid>5c9fa5b3-3203-4c93-92d6-2b4103fe7b40</uuid> <metadata> <kimchi:metadata xmlns:kimchi="https://github.com/kimchi-project/kimchi"> <kimchi:os version="unknown" distro="unknown"/> </kimchi:metadata> </metadata> <memory unit='KiB'>1048576</memory> <currentMemory unit='KiB'>1048576</currentMemory> <memtune> <hard_limit unit='KiB'>2097152</hard_limit> </memtune> <vcpu placement='static'>1</vcpu> <os> <type arch='x86_64' machine='pc-i440fx-2.4'>hvm</type> <boot dev='hd'/> <boot dev='cdrom'/> <boot dev='network'/> </os> <acpi/> <apic/> <pae/> </features> <cpu> <numa> <cell id='0' cpus='0' memory='1048576' unit='KiB'/> </numa> </cpu> <clock offset='utc'/> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> <on_crash>restart</on_crash> <devices> <emulator>/usr/bin/qemu-kvm</emulator> <disk type='file' device='disk'> <driver name='qemu' type='qcow2' cache='none'/> <source file='/var/lib/libvirt/images/5c9fa5b3-3203-4c93-92d6-2b4103fe7b40-0.img'/> <target dev='hda' bus='ide'/> <address type='drive' controller='0' bus='0' target='0' unit='0'/> </disk> <controller type='usb' index='0'> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> </controller> <controller type='pci' index='0' model='pci-root'/> <controller type='ide' index='0'> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> </controller> <interface type='network'> <mac address='52:54:00:ea:7e:ad'/> <source network='default'/> <model type='e1000'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> <serial type='pty'> <target port='0'/> </serial> <console type='pty'> <target type='serial' port='0'/> </console> <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'> <listen type='address' address='127.0.0.1'/> </graphics> <sound model='ich6'> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </sound> <video> <model type='cirrus' vram='16384' heads='1'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> </video> <memballoon model='virtio'> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> </memballoon> </devices> </domain>
Paulo Vital (3): Add support to create netboot templates. Add support to create guests to netboot. Update test cases to support netboot.
API.json | 18 +++++++++++---- i18n.py | 1 + model/templates.py | 27 ++++++++++++++++++++--- tests/test_authorization.py | 4 +++- tests/test_mockmodel.py | 15 ++++++++----- tests/test_model.py | 43 +++++++++++++++++++++--------------- tests/test_rest.py | 37 +++++++++++++++++++++---------- tests/test_template.py | 53 ++++++++++++++++++++++++++++++++++----------- tests/test_vmtemplate.py | 18 +++++++++++++++ vmtemplate.py | 28 ++++++++++++++++-------- xmlutils/bootorder.py | 39 +++++++++++++++++++++++++++++++++ 11 files changed, 219 insertions(+), 64 deletions(-) create mode 100644 xmlutils/bootorder.py
-- 2.5.5
participants (3)
-
Aline Manera
-
Jose Ricardo Ziviani
-
pvital@linux.vnet.ibm.com