[PATCHv3 0/8] Support img based template

From: Royce Lv <lvroyce@linux.vnet.ibm.com> v2>v3, Clear unused iso link, Adding mockmodel and tests How to test: create a image using: POST /templates {'name':'mytemp', 'disks':[{'base':'a_base_img_path'}]} create a vm using: POST /vms {'template': '/templates/mytemp', 'pool'....} Known issues: 1.This is only available for NFS and dir pool, logical pool in kimchi now adopted raw format which does not suitable for qcow2 img(will fix later). iscsi, scsi pool volume size need to be checked and handled later. So in this version just support dir based pool. 2.Due to template integrity check need be refactored, this check will be added later. Royce Lv (8): Add image probe function Change doc and api specification Change 'cdrom' to a optional param Fix: Prevent iso links filling in osinfo.py Integrate image os probe in vm template create Create volume based on backing store image Update mockmodel of base img vm Add tests for image based template Makefile.am | 1 + contrib/DEBIAN/control.in | 4 +- contrib/kimchi.spec.fedora.in | 2 + contrib/kimchi.spec.suse.in | 2 + docs/API.md | 3 +- docs/README.md | 9 +++-- src/kimchi/API.json | 8 +++- src/kimchi/control/storagevolumes.py | 2 +- src/kimchi/control/templates.py | 2 +- src/kimchi/exception.py | 4 ++ src/kimchi/i18n.py | 8 +++- src/kimchi/imageinfo.py | 66 +++++++++++++++++++++++++++++++ src/kimchi/mockmodel.py | 31 ++++++++++++--- src/kimchi/model/templates.py | 13 +++--- src/kimchi/model/vms.py | 1 + src/kimchi/osinfo.py | 24 +----------- src/kimchi/vmtemplate.py | 76 ++++++++++++++++++++++++------------ tests/test_model.py | 25 ++++++++++++ tests/test_osinfo.py | 8 ---- tests/test_rest.py | 32 ++++++++++++++- tests/test_vmtemplate.py | 2 +- 21 files changed, 244 insertions(+), 79 deletions(-) create mode 100644 src/kimchi/imageinfo.py -- 1.8.3.2

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Image file probe will be used in identify image file os info and generate reasonable configuration for it. This will be useful when import image and create a vm from it. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- Makefile.am | 1 + contrib/DEBIAN/control.in | 4 +++- contrib/kimchi.spec.fedora.in | 2 ++ contrib/kimchi.spec.suse.in | 2 ++ docs/README.md | 9 +++++--- src/kimchi/exception.py | 4 ++++ src/kimchi/i18n.py | 4 ++++ src/kimchi/imageinfo.py | 49 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 src/kimchi/imageinfo.py diff --git a/Makefile.am b/Makefile.am index 44c2515..3293d9e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,6 +55,7 @@ PEP8_WHITELIST = \ src/kimchi/distroloader.py \ src/kimchi/exception.py \ src/kimchi/featuretests.py \ + src/kimchi/imageinfo.py \ src/kimchi/iscsi.py \ src/kimchi/isoinfo.py \ src/kimchi/kvmusertests.py \ diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index aac1a24..3754fa2 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -23,7 +23,9 @@ Depends: python-cherrypy3 (>= 3.2.0), python-lxml, open-iscsi, firewalld, - nginx + nginx, + python-guestfs, + libguestfs-tools Build-Depends: libxslt, python-libxml2 Maintainer: Aline Manera <alinefm@br.ibm.com> diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index ca3f7e1..5fa93f7 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -27,6 +27,8 @@ Requires: nfs-utils Requires: nginx Requires: iscsi-initiator-utils Requires: policycoreutils-python +Requires: python-libguestfs +Requires: libguestfs-tools BuildRequires: libxslt BuildRequires: libxml2-python diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 73304c2..9e87ada 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -26,6 +26,8 @@ Requires: python-xml Requires: nfs-client Requires: nginx Requires: open-iscsi +Requires: python-libguestfs +Requires: guestfs-tools BuildRequires: libxslt-tools BuildRequires: python-libxml2 diff --git a/docs/README.md b/docs/README.md index ab03918..24537e1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -54,7 +54,8 @@ Install Dependencies qemu-kvm python-psutil python-ethtool sos \ python-ipaddr python-lxml nfs-utils \ iscsi-initiator-utils libxslt pyparted nginx \ - policycoreutils-python + policycoreutils-python python-libguestfs \ + libguestfs-tools # If using RHEL6, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict # Restart libvirt to allow configuration changes to take effect @@ -76,7 +77,8 @@ for more information on how to configure your system to access this repository. python-pam python-m2crypto python-jsonschema \ qemu-kvm libtool python-psutil python-ethtool \ sosreport python-ipaddr python-lxml nfs-common \ - open-iscsi lvm2 xsltproc python-parted nginx firewalld + open-iscsi lvm2 xsltproc python-parted nginx \ + firewalld python-guestfs libguestfs-tools Packages version requirement: python-jsonschema >= 1.3.0 @@ -90,7 +92,8 @@ for more information on how to configure your system to access this repository. python-pam python-M2Crypto python-jsonschema \ rpm-build kvm python-psutil python-ethtool \ python-ipaddr python-lxml nfs-client open-iscsi \ - libxslt-tools python-xml python-parted + libxslt-tools python-xml python-parted \ + python-libguestfs guestfs-tools Packages version requirement: python-psutil >= 0.6.0 diff --git a/src/kimchi/exception.py b/src/kimchi/exception.py index d84ddb9..f8f5fb3 100644 --- a/src/kimchi/exception.py +++ b/src/kimchi/exception.py @@ -90,5 +90,9 @@ class IsoFormatError(KimchiException): pass +class ImageFormatError(KimchiException): + pass + + class TimeoutExpired(KimchiException): pass diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 0c76145..14b057f 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -61,6 +61,10 @@ messages = { "'%(user)s' to the ISO path group, or (not recommended) 'chmod -R o+x 'path_to_iso'." "Details: %(err)s" ), + "KCHIMG0001E": _("Error occurs when probing image os information."), + "KCHIMG0002E": _("No OS information found in given image."), + "KCHIMG0003E": _("Unable to find/read image file %(filename)s"), + "KCHVM0001E": _("Virtual machine %(name)s already exists"), "KCHVM0002E": _("Virtual machine %(name)s does not exist"), "KCHVM0003E": _("Unable to rename virtual machine %(name)s. The name %(new_name)s already exists or it is not powered off."), diff --git a/src/kimchi/imageinfo.py b/src/kimchi/imageinfo.py new file mode 100644 index 0000000..f874ece --- /dev/null +++ b/src/kimchi/imageinfo.py @@ -0,0 +1,49 @@ +# +# Kimchi +# +# Copyright IBM Corp, 2014 +# +# 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 os +import sys +import guestfs + +from kimchi.exception import ImageFormatError + + +def probe_image(image_path): + g = guestfs.GuestFS(python_return_dict=True) + g.add_drive_opts(image_path, readonly=1) + g.launch() + if not os.access(image_path, os.R_OK): + raise ImageFormatError("KCHIMG0003E", {'filename': image_path}) + try: + roots = g.inspect_os() + except: + raise ImageFormatError("KCHIMG0001E") + if len(roots) == 0: + raise ImageFormatError("KCHIMG0002E") + + for root in roots: + version = "%d.%d" % (g.inspect_get_major_version(root), + g.inspect_get_minor_version(root)) + distro = "%s" % (g.inspect_get_distro(root)) + + return (distro, version) + + +if __name__ == '__main__': + print probe_image(sys.argv[1]) -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 07/20/2014 12:07 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Image file probe will be used in identify image file os info and generate reasonable configuration for it. This will be useful when import image and create a vm from it.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- Makefile.am | 1 + contrib/DEBIAN/control.in | 4 +++- contrib/kimchi.spec.fedora.in | 2 ++ contrib/kimchi.spec.suse.in | 2 ++ docs/README.md | 9 +++++--- src/kimchi/exception.py | 4 ++++ src/kimchi/i18n.py | 4 ++++ src/kimchi/imageinfo.py | 49 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 src/kimchi/imageinfo.py
diff --git a/Makefile.am b/Makefile.am index 44c2515..3293d9e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,6 +55,7 @@ PEP8_WHITELIST = \ src/kimchi/distroloader.py \ src/kimchi/exception.py \ src/kimchi/featuretests.py \ + src/kimchi/imageinfo.py \ src/kimchi/iscsi.py \ src/kimchi/isoinfo.py \ src/kimchi/kvmusertests.py \ diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index aac1a24..3754fa2 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -23,7 +23,9 @@ Depends: python-cherrypy3 (>= 3.2.0), python-lxml, open-iscsi, firewalld, - nginx + nginx, + python-guestfs, + libguestfs-tools Build-Depends: libxslt, python-libxml2 Maintainer: Aline Manera <alinefm@br.ibm.com> diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index ca3f7e1..5fa93f7 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -27,6 +27,8 @@ Requires: nfs-utils Requires: nginx Requires: iscsi-initiator-utils Requires: policycoreutils-python +Requires: python-libguestfs +Requires: libguestfs-tools BuildRequires: libxslt BuildRequires: libxml2-python
diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 73304c2..9e87ada 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -26,6 +26,8 @@ Requires: python-xml Requires: nfs-client Requires: nginx Requires: open-iscsi +Requires: python-libguestfs +Requires: guestfs-tools BuildRequires: libxslt-tools BuildRequires: python-libxml2
diff --git a/docs/README.md b/docs/README.md index ab03918..24537e1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -54,7 +54,8 @@ Install Dependencies qemu-kvm python-psutil python-ethtool sos \ python-ipaddr python-lxml nfs-utils \ iscsi-initiator-utils libxslt pyparted nginx \ - policycoreutils-python + policycoreutils-python python-libguestfs \ + libguestfs-tools # If using RHEL6, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict # Restart libvirt to allow configuration changes to take effect @@ -76,7 +77,8 @@ for more information on how to configure your system to access this repository. python-pam python-m2crypto python-jsonschema \ qemu-kvm libtool python-psutil python-ethtool \ sosreport python-ipaddr python-lxml nfs-common \ - open-iscsi lvm2 xsltproc python-parted nginx firewalld + open-iscsi lvm2 xsltproc python-parted nginx \ + firewalld python-guestfs libguestfs-tools
Packages version requirement: python-jsonschema >= 1.3.0 @@ -90,7 +92,8 @@ for more information on how to configure your system to access this repository. python-pam python-M2Crypto python-jsonschema \ rpm-build kvm python-psutil python-ethtool \ python-ipaddr python-lxml nfs-client open-iscsi \ - libxslt-tools python-xml python-parted + libxslt-tools python-xml python-parted \ + python-libguestfs guestfs-tools
Packages version requirement: python-psutil >= 0.6.0 diff --git a/src/kimchi/exception.py b/src/kimchi/exception.py index d84ddb9..f8f5fb3 100644 --- a/src/kimchi/exception.py +++ b/src/kimchi/exception.py @@ -90,5 +90,9 @@ class IsoFormatError(KimchiException): pass
+class ImageFormatError(KimchiException): + pass + + class TimeoutExpired(KimchiException): pass diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 0c76145..14b057f 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -61,6 +61,10 @@ messages = { "'%(user)s' to the ISO path group, or (not recommended) 'chmod -R o+x 'path_to_iso'." "Details: %(err)s" ),
+ "KCHIMG0001E": _("Error occurs when probing image os information."), + "KCHIMG0002E": _("No OS information found in given image."), + "KCHIMG0003E": _("Unable to find/read image file %(filename)s"), + "KCHVM0001E": _("Virtual machine %(name)s already exists"), "KCHVM0002E": _("Virtual machine %(name)s does not exist"), "KCHVM0003E": _("Unable to rename virtual machine %(name)s. The name %(new_name)s already exists or it is not powered off."), diff --git a/src/kimchi/imageinfo.py b/src/kimchi/imageinfo.py new file mode 100644 index 0000000..f874ece --- /dev/null +++ b/src/kimchi/imageinfo.py @@ -0,0 +1,49 @@ +# +# Kimchi +# +# Copyright IBM Corp, 2014 +# +# 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 os +import sys +import guestfs + +from kimchi.exception import ImageFormatError + + +def probe_image(image_path): + g = guestfs.GuestFS(python_return_dict=True) + g.add_drive_opts(image_path, readonly=1) + g.launch() + if not os.access(image_path, os.R_OK): + raise ImageFormatError("KCHIMG0003E", {'filename': image_path}) + try: + roots = g.inspect_os() + except: + raise ImageFormatError("KCHIMG0001E") + if len(roots) == 0: + raise ImageFormatError("KCHIMG0002E") + + for root in roots: + version = "%d.%d" % (g.inspect_get_major_version(root), + g.inspect_get_minor_version(root)) + distro = "%s" % (g.inspect_get_distro(root)) + + return (distro, version) + + +if __name__ == '__main__': + print probe_image(sys.argv[1])

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Add 'base' to 'disks' param to create template, so that we can support create template from image. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 3 ++- src/kimchi/API.json | 8 +++++++- src/kimchi/i18n.py | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index 1a228df..d66254a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -174,7 +174,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. * cpus *(optional)*: The number of CPUs assigned to the VM. Default is 1. * memory *(optional)*: The amount of memory assigned to the VM. Default is 1024M. - * cdrom *(required)*: A volume name or URI to an ISO image. + * cdrom *(optional)*: A volume name or URI to an ISO image. * storagepool *(optional)*: URI of the storagepool. Default is '/storagepools/default' * networks *(optional)*: list of networks will be assigned to the new VM. @@ -183,6 +183,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. (either *size* or *volume* must be specified): * index: The device index * size: The device size in GB + * base: Base image of this disk * 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 3616509..f441f37 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -373,7 +373,6 @@ "description": "Path for cdrom", "type": "string", "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", - "required": true, "error": "KCHTMPL0014E" }, "disks": { @@ -392,7 +391,14 @@ "type": "number", "minimum": 1, "error": "KCHTMPL0022E" + }, + "base": { + "description": "Base image of the disk", + "type": "string", + "pattern": "^/.+$", + "error": "KCHTMPL0023E" } + } }, "minItems": 1, diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 14b057f..0c3aae5 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -125,6 +125,7 @@ messages = { "KCHTMPL0020E": _("Unable to create template due error: %(err)s"), "KCHTMPL0021E": _("Unable to delete template due error: %(err)s"), "KCHTMPL0022E": _("Disk size must be greater than 1GB."), + "KCHTMPL0023E": _("Template base image must be a valid local image file"), "KCHPOOL0001E": _("Storage pool %(name)s already exists"), "KCHPOOL0002E": _("Storage pool %(name)s does not exist"), -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 07/20/2014 12:07 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add 'base' to 'disks' param to create template, so that we can support create template from image.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- docs/API.md | 3 ++- src/kimchi/API.json | 8 +++++++- src/kimchi/i18n.py | 1 + 3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/docs/API.md b/docs/API.md index 1a228df..d66254a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -174,7 +174,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. * cpus *(optional)*: The number of CPUs assigned to the VM. Default is 1. * memory *(optional)*: The amount of memory assigned to the VM. Default is 1024M. - * cdrom *(required)*: A volume name or URI to an ISO image. + * cdrom *(optional)*: A volume name or URI to an ISO image. * storagepool *(optional)*: URI of the storagepool. Default is '/storagepools/default' * networks *(optional)*: list of networks will be assigned to the new VM. @@ -183,6 +183,7 @@ Represents a snapshot of the Virtual Machine's primary monitor. (either *size* or *volume* must be specified): * index: The device index * size: The device size in GB + * base: Base image of this disk
* 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 3616509..f441f37 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -373,7 +373,6 @@ "description": "Path for cdrom", "type": "string", "pattern": "^((/)|(http)[s]?:|[t]?(ftp)[s]?:)+.*$", - "required": true, "error": "KCHTMPL0014E" }, "disks": { @@ -392,7 +391,14 @@ "type": "number", "minimum": 1, "error": "KCHTMPL0022E" + }, + "base": { + "description": "Base image of the disk", + "type": "string", + "pattern": "^/.+$", + "error": "KCHTMPL0023E" } + } }, "minItems": 1, diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 14b057f..0c3aae5 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -125,6 +125,7 @@ messages = { "KCHTMPL0020E": _("Unable to create template due error: %(err)s"), "KCHTMPL0021E": _("Unable to delete template due error: %(err)s"), "KCHTMPL0022E": _("Disk size must be greater than 1GB."), + "KCHTMPL0023E": _("Template base image must be a valid local image file"),
"KCHPOOL0001E": _("Storage pool %(name)s already exists"), "KCHPOOL0002E": _("Storage pool %(name)s does not exist"),

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Multiple files modification for change 'cdrom' to optional param. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/templates.py | 2 +- src/kimchi/i18n.py | 2 +- src/kimchi/imageinfo.py | 19 ++++++++++++++++++- src/kimchi/mockmodel.py | 27 +++++++++++++++++++++------ src/kimchi/model/templates.py | 13 ++++++++----- src/kimchi/vmtemplate.py | 37 +++++++++++++++++++++++++++---------- tests/test_rest.py | 2 +- tests/test_vmtemplate.py | 2 +- 8 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py index 907929f..65b6805 100644 --- a/src/kimchi/control/templates.py +++ b/src/kimchi/control/templates.py @@ -47,7 +47,7 @@ class Template(Resource): 'os_version': self.info['os_version'], 'cpus': self.info['cpus'], 'memory': self.info['memory'], - 'cdrom': self.info['cdrom'], + 'cdrom': self.info.get('cdrom', None), 'disks': self.info['disks'], 'storagepool': self.info['storagepool'], 'networks': self.info['networks'], diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 0c3aae5..f872bee 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -118,7 +118,7 @@ messages = { "KCHTMPL0013E": _("Amount of memory (MB) must be an integer greater than 512"), "KCHTMPL0014E": _("Template CDROM must be a local or remote ISO file"), "KCHTMPL0015E": _("Invalid storage pool URI %(value)s specified for template"), - "KCHTMPL0016E": _("Specify an ISO image as CDROM to create a template"), + "KCHTMPL0016E": _("Specify an ISO image as CDROM or a base image to create a template"), "KCHTMPL0017E": _("All networks for the template must be specified in a list."), "KCHTMPL0018E": _("Must specify a volume to a template, when storage pool is iscsi or scsi"), "KCHTMPL0019E": _("The volume: %(volume)s in not in storage pool %(pool)s"), diff --git a/src/kimchi/imageinfo.py b/src/kimchi/imageinfo.py index f874ece..f4c6356 100644 --- a/src/kimchi/imageinfo.py +++ b/src/kimchi/imageinfo.py @@ -17,11 +17,28 @@ # 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 sys import guestfs -from kimchi.exception import ImageFormatError +from kimchi.exception import ImageFormatError, TimeoutExpired +from kimchi.utils import run_command, kimchi_log + + +def probe_img_info(path): + cmd = ["qemu-img", "info", "--output=json", path] + info = dict() + try: + out = run_command(cmd, 10)[0] + except TimeoutExpired: + kimchi_log.warning("Cannot decide format of base img %s", path) + return None + + info = json.loads(out) + info['virtual-size'] = info['virtual-size'] >> 30 + info['actual-size'] = info['actual-size'] >> 30 + return info def probe_image(image_path): diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 0e45d1e..ccaa4d8 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -201,8 +201,10 @@ class MockModel(object): index += 1 cdrom = "hd" + string.ascii_lowercase[index + 1] - cdrom_params = {'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} - vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) + if t_info.get('cdrom'): + cdrom_params = { + 'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} + vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) self._mock_vms[name] = vm return name @@ -238,11 +240,24 @@ class MockModel(object): def templates_create(self, params): name = params.get('name', '').strip() + iso = params.get('cdrom') if not name: - iso = params['cdrom'] - iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] - name = iso_name + str(int(time.time() * 1000)) - params['name'] = name + if iso: + iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] + name = iso_name + str(int(time.time() * 1000)) + params['name'] = name + else: + raise InvalidParameter("KCHTMPL0008E") + + base_tmpl = False + if not iso: + if 'disks' in params: + for d in params['disks']: + if 'base' in d: + base_tmpl = True + break + if not base_tmpl: + raise InvalidParameter("KCHTMPL0016E") if name in self._mock_templates: raise InvalidOperation("KCHTMPL0001E", {'name': name}) diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py index 60f4de5..0e07a78 100644 --- a/src/kimchi/model/templates.py +++ b/src/kimchi/model/templates.py @@ -40,9 +40,9 @@ class TemplatesModel(object): def create(self, params): name = params.get('name', '').strip() - iso = params['cdrom'] + iso = params.get('cdrom') # check search permission - if iso.startswith('/') and os.path.isfile(iso): + if iso and iso.startswith('/') and os.path.isfile(iso): user = UserTests().probe_user() ret, excp = probe_file_permission_as_user(iso, user) if ret is False: @@ -51,9 +51,12 @@ class TemplatesModel(object): 'err': excp}) if not name: - iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] - name = iso_name + str(int(time.time() * 1000)) - params['name'] = name + if iso: + iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] + name = iso_name + str(int(time.time() * 1000)) + params['name'] = name + else: + raise InvalidParameter('KCHTMPL0008E') conn = self.conn.get() pool_uri = params.get(u'storagepool', '') diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 8d5217a..0903b7f 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -24,7 +24,8 @@ import urlparse from kimchi import osinfo -from kimchi.exception import InvalidParameter, IsoFormatError +from kimchi.exception import InvalidParameter, IsoFormatError, ImageFormatError +from kimchi.imageinfo import probe_image, probe_img_info from kimchi.isoinfo import IsoImage from kimchi.utils import check_url_path, pool_name_from_uri from lxml import etree @@ -54,6 +55,19 @@ class VMTemplate(object): iso_distro = iso_version = 'unknown' iso = args.get('cdrom', '') + # if ISO not specified and base disk image specified, + # prevent cdrom from filling automatically + if len(iso) == 0 and 'disks' in args: + for d in args['disks']: + if 'base' in d: + try: + distro, version = probe_image(d['base']) + except ImageFormatError: + pass + if 'size' not in d: + d['size'] = probe_img_info(d['base'])['virtual-size'] + break + if scan and len(iso) > 0: iso_distro, iso_version = self.get_iso_info(iso) if not iso.startswith('/'): @@ -84,6 +98,8 @@ class VMTemplate(object): raise InvalidParameter("KCHISO0001E", {'filename': iso}) def _get_cdrom_xml(self, libvirt_stream_protocols, qemu_stream_dns): + if 'cdrom' not in self.info: + return None bus = self.info['cdrom_bus'] dev = "%s%s" % (self._bus_to_dev[bus], string.lowercase[self.info['cdrom_index']]) @@ -325,13 +341,14 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/> libvirt_stream_protocols = kwargs.get('libvirt_stream_protocols', []) cdrom_xml = self._get_cdrom_xml(libvirt_stream_protocols, qemu_stream_dns) - - if not urlparse.urlparse(self.info['cdrom']).scheme in \ - libvirt_stream_protocols and params.get('iso_stream', False): - params['qemu-namespace'] = QEMU_NAMESPACE - params['qemu-stream-cmdline'] = cdrom_xml - else: - params['cdroms'] = cdrom_xml + if cdrom_xml: + if not urlparse.urlparse(self.info['cdrom']).scheme in \ + libvirt_stream_protocols and \ + params.get('iso_stream', False): + params['qemu-namespace'] = QEMU_NAMESPACE + params['qemu-stream-cmdline'] = cdrom_xml + else: + params['cdroms'] = cdrom_xml xml = """ <domain type='%(domain)s' %(qemu-namespace)s> @@ -414,8 +431,8 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/> # validate iso integrity # FIXME when we support multiples cdrom devices - iso = self.info['cdrom'] - if not (os.path.isfile(iso) or check_url_path(iso)): + iso = self.info.get('cdrom') + if iso and not (os.path.isfile(iso) or check_url_path(iso)): invalid['cdrom'] = [iso] self.info['invalid'] = invalid diff --git a/tests/test_rest.py b/tests/test_rest.py index 54209ef..97668e7 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1068,7 +1068,7 @@ class RestTests(unittest.TestCase): self.assertEquals(200, resp.status) self.assertEquals(0, len(json.loads(resp.read()))) - # Create a template without cdrom fails with 400 + # 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'} diff --git a/tests/test_vmtemplate.py b/tests/test_vmtemplate.py index 821ca24..319f250 100644 --- a/tests/test_vmtemplate.py +++ b/tests/test_vmtemplate.py @@ -29,7 +29,7 @@ class VMTemplateTests(unittest.TestCase): def test_minimal_construct(self): fields = (('name', 'test'), ('os_distro', 'unknown'), ('os_version', 'unknown'), ('cpus', 1), - ('memory', 1024), ('cdrom', ''), ('networks', ['default']), + ('memory', 1024), ('networks', ['default']), ('disk_bus', 'ide'), ('nic_model', 'e1000'), ('graphics', {'type': 'vnc', 'listen': '0.0.0.0'})) -- 1.8.3.2

On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Multiple files modification for change 'cdrom' to optional param.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/templates.py | 2 +- src/kimchi/i18n.py | 2 +- src/kimchi/imageinfo.py | 19 ++++++++++++++++++- src/kimchi/mockmodel.py | 27 +++++++++++++++++++++------ src/kimchi/model/templates.py | 13 ++++++++----- src/kimchi/vmtemplate.py | 37 +++++++++++++++++++++++++++---------- tests/test_rest.py | 2 +- tests/test_vmtemplate.py | 2 +- 8 files changed, 78 insertions(+), 26 deletions(-)
diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py index 907929f..65b6805 100644 --- a/src/kimchi/control/templates.py +++ b/src/kimchi/control/templates.py @@ -47,7 +47,7 @@ class Template(Resource): 'os_version': self.info['os_version'], 'cpus': self.info['cpus'], 'memory': self.info['memory'], - 'cdrom': self.info['cdrom'], + 'cdrom': self.info.get('cdrom', None), 'disks': self.info['disks'], 'storagepool': self.info['storagepool'], 'networks': self.info['networks'], diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 0c3aae5..f872bee 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -118,7 +118,7 @@ messages = { "KCHTMPL0013E": _("Amount of memory (MB) must be an integer greater than 512"), "KCHTMPL0014E": _("Template CDROM must be a local or remote ISO file"), "KCHTMPL0015E": _("Invalid storage pool URI %(value)s specified for template"), - "KCHTMPL0016E": _("Specify an ISO image as CDROM to create a template"), + "KCHTMPL0016E": _("Specify an ISO image as CDROM or a base image to create a template"), "KCHTMPL0017E": _("All networks for the template must be specified in a list."), "KCHTMPL0018E": _("Must specify a volume to a template, when storage pool is iscsi or scsi"), "KCHTMPL0019E": _("The volume: %(volume)s in not in storage pool %(pool)s"), diff --git a/src/kimchi/imageinfo.py b/src/kimchi/imageinfo.py index f874ece..f4c6356 100644 --- a/src/kimchi/imageinfo.py +++ b/src/kimchi/imageinfo.py @@ -17,11 +17,28 @@ # 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 sys import guestfs
-from kimchi.exception import ImageFormatError +from kimchi.exception import ImageFormatError, TimeoutExpired +from kimchi.utils import run_command, kimchi_log + + +def probe_img_info(path): + cmd = ["qemu-img", "info", "--output=json", path] + info = dict() + try: + out = run_command(cmd, 10)[0] + except TimeoutExpired: + kimchi_log.warning("Cannot decide format of base img %s", path) + return None + + info = json.loads(out) + info['virtual-size'] = info['virtual-size'] >> 30 + info['actual-size'] = info['actual-size'] >> 30 + return info
def probe_image(image_path): diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 0e45d1e..ccaa4d8 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -201,8 +201,10 @@ class MockModel(object): index += 1
cdrom = "hd" + string.ascii_lowercase[index + 1] - cdrom_params = {'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} - vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) + if t_info.get('cdrom'): + cdrom_params = { + 'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} + vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params)
self._mock_vms[name] = vm return name @@ -238,11 +240,24 @@ class MockModel(object):
def templates_create(self, params): name = params.get('name', '').strip()
+ iso = params.get('cdrom') if not name: - iso = params['cdrom'] - iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] - name = iso_name + str(int(time.time() * 1000)) - params['name'] = name + if iso: + iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] + name = iso_name + str(int(time.time() * 1000)) + params['name'] = name + else: + raise InvalidParameter("KCHTMPL0008E") +
If no ISO is specified you should get the template name based on "base" img I suggest change those 2 if's order base_tmpl = None if not iso: if 'disks' in params: for d in params['disks']: if 'base' in d: base_tmpl = d['base'] break if base_tmpl is None: raise MissingParameter() if not name: # get the name according to base_tmpl or iso
+ base_tmpl = False + if not iso: + if 'disks' in params: + for d in params['disks']: + if 'base' in d: + base_tmpl = True + break + if not base_tmpl: + raise InvalidParameter("KCHTMPL0016E")
MissingParameter() is better than InvalidParameter()
if name in self._mock_templates: raise InvalidOperation("KCHTMPL0001E", {'name': name}) diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py index 60f4de5..0e07a78 100644 --- a/src/kimchi/model/templates.py +++ b/src/kimchi/model/templates.py @@ -40,9 +40,9 @@ class TemplatesModel(object):
def create(self, params): name = params.get('name', '').strip() - iso = params['cdrom'] + iso = params.get('cdrom') # check search permission - if iso.startswith('/') and os.path.isfile(iso): + if iso and iso.startswith('/') and os.path.isfile(iso): user = UserTests().probe_user() ret, excp = probe_file_permission_as_user(iso, user) if ret is False: @@ -51,9 +51,12 @@ class TemplatesModel(object): 'err': excp})
if not name: - iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] - name = iso_name + str(int(time.time() * 1000)) - params['name'] = name
+ if iso: + iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] + name = iso_name + str(int(time.time() * 1000)) + params['name'] = name + else: + raise InvalidParameter('KCHTMPL0008E')
The same I commented above. If no ISO is specified get the name from "base"
conn = self.conn.get() pool_uri = params.get(u'storagepool', '') diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 8d5217a..0903b7f 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -24,7 +24,8 @@ import urlparse
from kimchi import osinfo -from kimchi.exception import InvalidParameter, IsoFormatError +from kimchi.exception import InvalidParameter, IsoFormatError, ImageFormatError +from kimchi.imageinfo import probe_image, probe_img_info from kimchi.isoinfo import IsoImage from kimchi.utils import check_url_path, pool_name_from_uri from lxml import etree @@ -54,6 +55,19 @@ class VMTemplate(object): iso_distro = iso_version = 'unknown' iso = args.get('cdrom', '')
+ # if ISO not specified and base disk image specified, + # prevent cdrom from filling automatically + if len(iso) == 0 and 'disks' in args: + for d in args['disks']: + if 'base' in d: + try: + distro, version = probe_image(d['base']) + except ImageFormatError: + pass + if 'size' not in d: + d['size'] = probe_img_info(d['base'])['virtual-size'] + break + if scan and len(iso) > 0: iso_distro, iso_version = self.get_iso_info(iso) if not iso.startswith('/'): @@ -84,6 +98,8 @@ class VMTemplate(object): raise InvalidParameter("KCHISO0001E", {'filename': iso})
def _get_cdrom_xml(self, libvirt_stream_protocols, qemu_stream_dns): + if 'cdrom' not in self.info: + return None
As this function should return a xml string, it is better to return "" instead of None Otherwise the xml will be filled wrong
bus = self.info['cdrom_bus'] dev = "%s%s" % (self._bus_to_dev[bus], string.lowercase[self.info['cdrom_index']]) @@ -325,13 +341,14 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/> libvirt_stream_protocols = kwargs.get('libvirt_stream_protocols', []) cdrom_xml = self._get_cdrom_xml(libvirt_stream_protocols, qemu_stream_dns) -
- if not urlparse.urlparse(self.info['cdrom']).scheme in \ - libvirt_stream_protocols and params.get('iso_stream', False): - params['qemu-namespace'] = QEMU_NAMESPACE - params['qemu-stream-cmdline'] = cdrom_xml - else: - params['cdroms'] = cdrom_xml + if cdrom_xml: + if not urlparse.urlparse(self.info['cdrom']).scheme in \ + libvirt_stream_protocols and \ + params.get('iso_stream', False): + params['qemu-namespace'] = QEMU_NAMESPACE + params['qemu-stream-cmdline'] = cdrom_xml + else: + params['cdroms'] = cdrom_xml
If you return "" in _get_cdrom_xml() as I suggested you just need to change self.info['cdrom'] to self.info.get('cdrom', "")
xml = """ <domain type='%(domain)s' %(qemu-namespace)s> @@ -414,8 +431,8 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/>
# validate iso integrity # FIXME when we support multiples cdrom devices - iso = self.info['cdrom'] - if not (os.path.isfile(iso) or check_url_path(iso)): + iso = self.info.get('cdrom') + if iso and not (os.path.isfile(iso) or check_url_path(iso)): invalid['cdrom'] = [iso]
self.info['invalid'] = invalid diff --git a/tests/test_rest.py b/tests/test_rest.py index 54209ef..97668e7 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1068,7 +1068,7 @@ class RestTests(unittest.TestCase): self.assertEquals(200, resp.status) self.assertEquals(0, len(json.loads(resp.read())))
- # Create a template without cdrom fails with 400 + # 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'} diff --git a/tests/test_vmtemplate.py b/tests/test_vmtemplate.py index 821ca24..319f250 100644 --- a/tests/test_vmtemplate.py +++ b/tests/test_vmtemplate.py @@ -29,7 +29,7 @@ class VMTemplateTests(unittest.TestCase): def test_minimal_construct(self): fields = (('name', 'test'), ('os_distro', 'unknown'), ('os_version', 'unknown'), ('cpus', 1), - ('memory', 1024), ('cdrom', ''), ('networks', ['default']), + ('memory', 1024), ('networks', ['default']), ('disk_bus', 'ide'), ('nic_model', 'e1000'), ('graphics', {'type': 'vnc', 'listen': '0.0.0.0'}))

Will fix all in next release On 2014年07月22日 02:10, Aline Manera wrote:
On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Multiple files modification for change 'cdrom' to optional param.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/templates.py | 2 +- src/kimchi/i18n.py | 2 +- src/kimchi/imageinfo.py | 19 ++++++++++++++++++- src/kimchi/mockmodel.py | 27 +++++++++++++++++++++------ src/kimchi/model/templates.py | 13 ++++++++----- src/kimchi/vmtemplate.py | 37 +++++++++++++++++++++++++++---------- tests/test_rest.py | 2 +- tests/test_vmtemplate.py | 2 +- 8 files changed, 78 insertions(+), 26 deletions(-)
diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py index 907929f..65b6805 100644 --- a/src/kimchi/control/templates.py +++ b/src/kimchi/control/templates.py @@ -47,7 +47,7 @@ class Template(Resource): 'os_version': self.info['os_version'], 'cpus': self.info['cpus'], 'memory': self.info['memory'], - 'cdrom': self.info['cdrom'], + 'cdrom': self.info.get('cdrom', None), 'disks': self.info['disks'], 'storagepool': self.info['storagepool'], 'networks': self.info['networks'], diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 0c3aae5..f872bee 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -118,7 +118,7 @@ messages = { "KCHTMPL0013E": _("Amount of memory (MB) must be an integer greater than 512"), "KCHTMPL0014E": _("Template CDROM must be a local or remote ISO file"), "KCHTMPL0015E": _("Invalid storage pool URI %(value)s specified for template"), - "KCHTMPL0016E": _("Specify an ISO image as CDROM to create a template"), + "KCHTMPL0016E": _("Specify an ISO image as CDROM or a base image to create a template"), "KCHTMPL0017E": _("All networks for the template must be specified in a list."), "KCHTMPL0018E": _("Must specify a volume to a template, when storage pool is iscsi or scsi"), "KCHTMPL0019E": _("The volume: %(volume)s in not in storage pool %(pool)s"), diff --git a/src/kimchi/imageinfo.py b/src/kimchi/imageinfo.py index f874ece..f4c6356 100644 --- a/src/kimchi/imageinfo.py +++ b/src/kimchi/imageinfo.py @@ -17,11 +17,28 @@ # 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 sys import guestfs
-from kimchi.exception import ImageFormatError +from kimchi.exception import ImageFormatError, TimeoutExpired +from kimchi.utils import run_command, kimchi_log + + +def probe_img_info(path): + cmd = ["qemu-img", "info", "--output=json", path] + info = dict() + try: + out = run_command(cmd, 10)[0] + except TimeoutExpired: + kimchi_log.warning("Cannot decide format of base img %s", path) + return None + + info = json.loads(out) + info['virtual-size'] = info['virtual-size'] >> 30 + info['actual-size'] = info['actual-size'] >> 30 + return info
def probe_image(image_path): diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 0e45d1e..ccaa4d8 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -201,8 +201,10 @@ class MockModel(object): index += 1
cdrom = "hd" + string.ascii_lowercase[index + 1] - cdrom_params = {'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} - vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params) + if t_info.get('cdrom'): + cdrom_params = { + 'dev': cdrom, 'path': t_info['cdrom'], 'type': 'cdrom'} + vm.storagedevices[cdrom] = MockVMStorageDevice(cdrom_params)
self._mock_vms[name] = vm return name @@ -238,11 +240,24 @@ class MockModel(object):
def templates_create(self, params): name = params.get('name', '').strip()
+ iso = params.get('cdrom') if not name: - iso = params['cdrom'] - iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] - name = iso_name + str(int(time.time() * 1000)) - params['name'] = name + if iso: + iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] + name = iso_name + str(int(time.time() * 1000)) + params['name'] = name + else: + raise InvalidParameter("KCHTMPL0008E") +
If no ISO is specified you should get the template name based on "base" img
I suggest change those 2 if's order
base_tmpl = None if not iso: if 'disks' in params: for d in params['disks']: if 'base' in d: base_tmpl = d['base'] break
if base_tmpl is None: raise MissingParameter()
if not name: # get the name according to base_tmpl or iso
+ base_tmpl = False + if not iso: + if 'disks' in params: + for d in params['disks']: + if 'base' in d: + base_tmpl = True + break + if not base_tmpl: + raise InvalidParameter("KCHTMPL0016E")
MissingParameter() is better than InvalidParameter()
if name in self._mock_templates: raise InvalidOperation("KCHTMPL0001E", {'name': name}) diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py index 60f4de5..0e07a78 100644 --- a/src/kimchi/model/templates.py +++ b/src/kimchi/model/templates.py @@ -40,9 +40,9 @@ class TemplatesModel(object):
def create(self, params): name = params.get('name', '').strip() - iso = params['cdrom'] + iso = params.get('cdrom') # check search permission - if iso.startswith('/') and os.path.isfile(iso): + if iso and iso.startswith('/') and os.path.isfile(iso): user = UserTests().probe_user() ret, excp = probe_file_permission_as_user(iso, user) if ret is False: @@ -51,9 +51,12 @@ class TemplatesModel(object): 'err': excp})
if not name: - iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] - name = iso_name + str(int(time.time() * 1000)) - params['name'] = name
+ if iso: + iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] + name = iso_name + str(int(time.time() * 1000)) + params['name'] = name + else: + raise InvalidParameter('KCHTMPL0008E')
The same I commented above. If no ISO is specified get the name from "base"
conn = self.conn.get() pool_uri = params.get(u'storagepool', '') diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 8d5217a..0903b7f 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -24,7 +24,8 @@ import urlparse
from kimchi import osinfo -from kimchi.exception import InvalidParameter, IsoFormatError +from kimchi.exception import InvalidParameter, IsoFormatError, ImageFormatError +from kimchi.imageinfo import probe_image, probe_img_info from kimchi.isoinfo import IsoImage from kimchi.utils import check_url_path, pool_name_from_uri from lxml import etree @@ -54,6 +55,19 @@ class VMTemplate(object): iso_distro = iso_version = 'unknown' iso = args.get('cdrom', '')
+ # if ISO not specified and base disk image specified, + # prevent cdrom from filling automatically + if len(iso) == 0 and 'disks' in args: + for d in args['disks']: + if 'base' in d: + try: + distro, version = probe_image(d['base']) + except ImageFormatError: + pass + if 'size' not in d: + d['size'] = probe_img_info(d['base'])['virtual-size'] + break + if scan and len(iso) > 0: iso_distro, iso_version = self.get_iso_info(iso) if not iso.startswith('/'): @@ -84,6 +98,8 @@ class VMTemplate(object): raise InvalidParameter("KCHISO0001E", {'filename': iso})
def _get_cdrom_xml(self, libvirt_stream_protocols, qemu_stream_dns): + if 'cdrom' not in self.info: + return None
As this function should return a xml string, it is better to return "" instead of None Otherwise the xml will be filled wrong
bus = self.info['cdrom_bus'] dev = "%s%s" % (self._bus_to_dev[bus], string.lowercase[self.info['cdrom_index']]) @@ -325,13 +341,14 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/> libvirt_stream_protocols = kwargs.get('libvirt_stream_protocols', []) cdrom_xml = self._get_cdrom_xml(libvirt_stream_protocols, qemu_stream_dns) -
- if not urlparse.urlparse(self.info['cdrom']).scheme in \ - libvirt_stream_protocols and params.get('iso_stream', False): - params['qemu-namespace'] = QEMU_NAMESPACE - params['qemu-stream-cmdline'] = cdrom_xml - else: - params['cdroms'] = cdrom_xml + if cdrom_xml: + if not urlparse.urlparse(self.info['cdrom']).scheme in \ + libvirt_stream_protocols and \ + params.get('iso_stream', False): + params['qemu-namespace'] = QEMU_NAMESPACE + params['qemu-stream-cmdline'] = cdrom_xml + else: + params['cdroms'] = cdrom_xml
If you return "" in _get_cdrom_xml() as I suggested you just need to change self.info['cdrom'] to self.info.get('cdrom', "")
xml = """ <domain type='%(domain)s' %(qemu-namespace)s> @@ -414,8 +431,8 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/>
# validate iso integrity # FIXME when we support multiples cdrom devices - iso = self.info['cdrom'] - if not (os.path.isfile(iso) or check_url_path(iso)): + iso = self.info.get('cdrom') + if iso and not (os.path.isfile(iso) or check_url_path(iso)): invalid['cdrom'] = [iso]
self.info['invalid'] = invalid diff --git a/tests/test_rest.py b/tests/test_rest.py index 54209ef..97668e7 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1068,7 +1068,7 @@ class RestTests(unittest.TestCase): self.assertEquals(200, resp.status) self.assertEquals(0, len(json.loads(resp.read())))
- # Create a template without cdrom fails with 400 + # 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'} diff --git a/tests/test_vmtemplate.py b/tests/test_vmtemplate.py index 821ca24..319f250 100644 --- a/tests/test_vmtemplate.py +++ b/tests/test_vmtemplate.py @@ -29,7 +29,7 @@ class VMTemplateTests(unittest.TestCase): def test_minimal_construct(self): fields = (('name', 'test'), ('os_distro', 'unknown'), ('os_version', 'unknown'), ('cpus', 1), - ('memory', 1024), ('cdrom', ''), ('networks', ['default']), + ('memory', 1024), ('networks', ['default']), ('disk_bus', 'ide'), ('nic_model', 'e1000'), ('graphics', {'type': 'vnc', 'listen': '0.0.0.0'}))
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

From: Royce Lv <lvroyce@linux.vnet.ibm.com> As we already has distro json schema based template creation, we need to stop filling iso links filling in osinfo.py. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/osinfo.py | 24 +----------------------- tests/test_osinfo.py | 8 -------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/kimchi/osinfo.py b/src/kimchi/osinfo.py index 39c9163..97f1836 100644 --- a/src/kimchi/osinfo.py +++ b/src/kimchi/osinfo.py @@ -65,31 +65,10 @@ modern_version_bases = {'x86': {'debian': '6.0', 'ubuntu': '7.10', 'opensuse': '13.1', 'sles': '12'}} + icon_available_distros = [icon[5:-4] for icon in glob.glob1('%s/images/' % paths.ui_dir, 'icon-*.png')] -isolinks = { - 'debian': { - 'squeeze': 'http://cdimage.debian.org/debian-cd/6.0.7-live/amd64/' - 'iso-hybrid/debian-live-6.0.7-amd64-gnome-desktop.iso', - }, - 'ubuntu': { - 'raring': 'http://ubuntu-releases.cs.umn.edu/13.04/' - 'ubuntu-13.04-desktop-amd64.iso', - }, - 'opensuse': { - '12.3': 'http://suse.mirrors.tds.net/pub/opensuse/distribution/12.3/' - 'iso/openSUSE-12.3-DVD-x86_64.iso', - }, - 'fedora': { - '16': 'http://fedora.mirrors.tds.net/pub/fedora/releases/16/Live/' - 'x86_64/Fedora-16-x86_64-Live-Desktop.iso', - '17': 'http://fedora.mirrors.tds.net/pub/fedora/releases/17/Live/' - 'x86_64/Fedora-17-x86_64-Live-Desktop.iso', - '18': 'http://fedora.mirrors.tds.net/pub/fedora/releases/18/Live/' - 'x86_64/Fedora-18-x86_64-Live-Desktop.iso', - }, -} defaults = {'networks': ['default'], 'storagepool': '/storagepools/default', @@ -113,7 +92,6 @@ def lookup(distro, version): params = copy.deepcopy(defaults) params['os_distro'] = distro params['os_version'] = version - params['cdrom'] = isolinks.get(distro, {}).get(version, '') arch = _get_arch() if distro in modern_version_bases[arch]: diff --git a/tests/test_osinfo.py b/tests/test_osinfo.py index 104e7b8..816f88b 100644 --- a/tests/test_osinfo.py +++ b/tests/test_osinfo.py @@ -30,14 +30,6 @@ class OSInfoTests(unittest.TestCase): self.assertEquals('unknown', entry['os_version']) self.assertEquals(['default'], entry['networks']) - def test_fedora_lookup(self): - cd = ('http://fedora.mirrors.tds.net/pub/fedora/releases/17/Live/' - 'x86_64/Fedora-17-x86_64-Live-Desktop.iso') - entry = lookup('fedora', '17') - self.assertEquals(10, entry['disks'][0]['size']) - self.assertEquals(cd, entry['cdrom']) - self.assertEquals('/storagepools/default', entry['storagepool']) - def test_old_distros(self): old_versions = {'debian': '5.0', 'ubuntu': '7.04', 'opensuse': '10.1', 'centos': '5.1', 'rhel': '5.1', 'fedora': '15'} -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
As we already has distro json schema based template creation, we need to stop filling iso links filling in osinfo.py.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/osinfo.py | 24 +----------------------- tests/test_osinfo.py | 8 -------- 2 files changed, 1 insertion(+), 31 deletions(-)
diff --git a/src/kimchi/osinfo.py b/src/kimchi/osinfo.py index 39c9163..97f1836 100644 --- a/src/kimchi/osinfo.py +++ b/src/kimchi/osinfo.py @@ -65,31 +65,10 @@ modern_version_bases = {'x86': {'debian': '6.0', 'ubuntu': '7.10', 'opensuse': '13.1', 'sles': '12'}}
+ icon_available_distros = [icon[5:-4] for icon in glob.glob1('%s/images/' % paths.ui_dir, 'icon-*.png')]
-isolinks = { - 'debian': { - 'squeeze': 'http://cdimage.debian.org/debian-cd/6.0.7-live/amd64/' - 'iso-hybrid/debian-live-6.0.7-amd64-gnome-desktop.iso', - }, - 'ubuntu': { - 'raring': 'http://ubuntu-releases.cs.umn.edu/13.04/' - 'ubuntu-13.04-desktop-amd64.iso', - }, - 'opensuse': { - '12.3': 'http://suse.mirrors.tds.net/pub/opensuse/distribution/12.3/' - 'iso/openSUSE-12.3-DVD-x86_64.iso', - }, - 'fedora': { - '16': 'http://fedora.mirrors.tds.net/pub/fedora/releases/16/Live/' - 'x86_64/Fedora-16-x86_64-Live-Desktop.iso', - '17': 'http://fedora.mirrors.tds.net/pub/fedora/releases/17/Live/' - 'x86_64/Fedora-17-x86_64-Live-Desktop.iso', - '18': 'http://fedora.mirrors.tds.net/pub/fedora/releases/18/Live/' - 'x86_64/Fedora-18-x86_64-Live-Desktop.iso', - }, -}
defaults = {'networks': ['default'], 'storagepool': '/storagepools/default', @@ -113,7 +92,6 @@ def lookup(distro, version): params = copy.deepcopy(defaults) params['os_distro'] = distro params['os_version'] = version - params['cdrom'] = isolinks.get(distro, {}).get(version, '') arch = _get_arch()
if distro in modern_version_bases[arch]: diff --git a/tests/test_osinfo.py b/tests/test_osinfo.py index 104e7b8..816f88b 100644 --- a/tests/test_osinfo.py +++ b/tests/test_osinfo.py @@ -30,14 +30,6 @@ class OSInfoTests(unittest.TestCase): self.assertEquals('unknown', entry['os_version']) self.assertEquals(['default'], entry['networks'])
- def test_fedora_lookup(self): - cd = ('http://fedora.mirrors.tds.net/pub/fedora/releases/17/Live/' - 'x86_64/Fedora-17-x86_64-Live-Desktop.iso') - entry = lookup('fedora', '17') - self.assertEquals(10, entry['disks'][0]['size']) - self.assertEquals(cd, entry['cdrom']) - self.assertEquals('/storagepools/default', entry['storagepool']) - def test_old_distros(self): old_versions = {'debian': '5.0', 'ubuntu': '7.04', 'opensuse': '10.1', 'centos': '5.1', 'rhel': '5.1', 'fedora': '15'}

From: Royce Lv <lvroyce@linux.vnet.ibm.com> When we supply base image, we need to probe its os to choose a proper system configuration for it. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/vmtemplate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 0903b7f..9e8ed3d 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -52,7 +52,7 @@ class VMTemplate(object): self.fc_host_support = args.get('fc_host_support') # Identify the cdrom if present - iso_distro = iso_version = 'unknown' + distro = version = 'unknown' iso = args.get('cdrom', '') # if ISO not specified and base disk image specified, @@ -69,13 +69,13 @@ class VMTemplate(object): break if scan and len(iso) > 0: - iso_distro, iso_version = self.get_iso_info(iso) + distro, version = self.get_iso_info(iso) if not iso.startswith('/'): self.info.update({'iso_stream': True}) # Fetch defaults based on the os distro and version - os_distro = args.get('os_distro', iso_distro) - os_version = args.get('os_version', iso_version) + os_distro = args.get('os_distro', distro) + os_version = args.get('os_version', version) entry = osinfo.lookup(os_distro, os_version) self.info.update(entry) -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
When we supply base image, we need to probe its os to choose a proper system configuration for it.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/vmtemplate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 0903b7f..9e8ed3d 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -52,7 +52,7 @@ class VMTemplate(object): self.fc_host_support = args.get('fc_host_support')
# Identify the cdrom if present - iso_distro = iso_version = 'unknown' + distro = version = 'unknown' iso = args.get('cdrom', '')
# if ISO not specified and base disk image specified, @@ -69,13 +69,13 @@ class VMTemplate(object): break
if scan and len(iso) > 0: - iso_distro, iso_version = self.get_iso_info(iso) + distro, version = self.get_iso_info(iso) if not iso.startswith('/'): self.info.update({'iso_stream': True})
# Fetch defaults based on the os distro and version - os_distro = args.get('os_distro', iso_distro) - os_version = args.get('os_version', iso_version) + os_distro = args.get('os_distro', distro) + os_version = args.get('os_version', version) entry = osinfo.lookup(os_distro, os_version) self.info.update(entry)

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Creating volume base on backing store so that we can create vm from this cow volume. Also change volume xml generation method to lxml. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/i18n.py | 1 + src/kimchi/model/vms.py | 1 + src/kimchi/vmtemplate.py | 31 +++++++++++++++++++------------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index f872bee..1f70767 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -126,6 +126,7 @@ messages = { "KCHTMPL0021E": _("Unable to delete template due error: %(err)s"), "KCHTMPL0022E": _("Disk size must be greater than 1GB."), "KCHTMPL0023E": _("Template base image must be a valid local image file"), + "KCHTMPL0024E": _("Cannot identify base image %(path)s format"), "KCHPOOL0001E": _("Storage pool %(name)s already exists"), "KCHPOOL0002E": _("Storage pool %(name)s does not exist"), diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 8c0dcb1..7d975b2 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -201,6 +201,7 @@ class VMsModel(object): # the user from UI or manually. vol_list = [] if t._get_storage_type() in ["iscsi", "scsi"]: + # FIXME: iscsi and scsi storage work with base image needs to be fixed. vol_list = [] else: vol_list = t.fork_vm_storage(vm_uuid) diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 9e8ed3d..b17e3ff 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -265,19 +265,26 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/> 'type': 'disk', 'format': fmt, 'path': '%s/%s' % (storage_path, volume)} - info['allocation'] = 0 if fmt == 'qcow2' else info['capacity'] - info['xml'] = """ - <volume> - <name>%(name)s</name> - <allocation unit="G">%(allocation)s</allocation> - <capacity unit="G">%(capacity)s</capacity> - <target> - <format type='%(format)s'/> - <path>%(path)s</path> - </target> - </volume> - """ % info + + if 'base' in d: + info['base'] = dict() + base_fmt = probe_img_info(d['base'])['format'] + if base_fmt is None: + raise InvalidParameter("KCHTMPL0024E", {'path': d['base']}) + info['base']['path'] = d['base'] + info['base']['format'] = base_fmt + + v_tree = E.volume(E.name(info['name'])) + v_tree.append(E.allocation(str(info['allocation']), unit='G')) + v_tree.append(E.capacity(str(info['capacity']), unit='G')) + target = E.target( + E.format(type=info['format']), E.path(info['path'])) + if 'base' in d: + v_tree.append(E.backingStore( + E.path(info['base']['path']), E.format(type=info['base']['format']))) + v_tree.append(target) + info['xml'] = etree.tostring(v_tree) ret.append(info) return ret -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> Question: Do you know what is needed to have it working for iscsi and scsi pools? On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Creating volume base on backing store so that we can create vm from this cow volume. Also change volume xml generation method to lxml.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/i18n.py | 1 + src/kimchi/model/vms.py | 1 + src/kimchi/vmtemplate.py | 31 +++++++++++++++++++------------ 3 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index f872bee..1f70767 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -126,6 +126,7 @@ messages = { "KCHTMPL0021E": _("Unable to delete template due error: %(err)s"), "KCHTMPL0022E": _("Disk size must be greater than 1GB."), "KCHTMPL0023E": _("Template base image must be a valid local image file"), + "KCHTMPL0024E": _("Cannot identify base image %(path)s format"),
"KCHPOOL0001E": _("Storage pool %(name)s already exists"), "KCHPOOL0002E": _("Storage pool %(name)s does not exist"), diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 8c0dcb1..7d975b2 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -201,6 +201,7 @@ class VMsModel(object): # the user from UI or manually. vol_list = [] if t._get_storage_type() in ["iscsi", "scsi"]: + # FIXME: iscsi and scsi storage work with base image needs to be fixed. vol_list = [] else: vol_list = t.fork_vm_storage(vm_uuid) diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 9e8ed3d..b17e3ff 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -265,19 +265,26 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/> 'type': 'disk', 'format': fmt, 'path': '%s/%s' % (storage_path, volume)} - info['allocation'] = 0 if fmt == 'qcow2' else info['capacity'] - info['xml'] = """ - <volume> - <name>%(name)s</name> - <allocation unit="G">%(allocation)s</allocation> - <capacity unit="G">%(capacity)s</capacity> - <target> - <format type='%(format)s'/> - <path>%(path)s</path> - </target> - </volume> - """ % info + + if 'base' in d: + info['base'] = dict() + base_fmt = probe_img_info(d['base'])['format'] + if base_fmt is None: + raise InvalidParameter("KCHTMPL0024E", {'path': d['base']}) + info['base']['path'] = d['base'] + info['base']['format'] = base_fmt + + v_tree = E.volume(E.name(info['name'])) + v_tree.append(E.allocation(str(info['allocation']), unit='G')) + v_tree.append(E.capacity(str(info['capacity']), unit='G')) + target = E.target( + E.format(type=info['format']), E.path(info['path'])) + if 'base' in d: + v_tree.append(E.backingStore( + E.path(info['base']['path']), E.format(type=info['base']['format']))) + v_tree.append(target) + info['xml'] = etree.tostring(v_tree) ret.append(info) return ret

On 2014年07月22日 02:16, Aline Manera wrote:
Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com>
Question: Do you know what is needed to have it working for iscsi and scsi pools?
We can refer to vdsm of how to work with iscsi/scsi pool, but larger volume of 'iscsi' or 'scsi' need to be handled by cloud init as openstack did. Just leave 'size' and work with volume will be easy to finish I suppose.
On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Creating volume base on backing store so that we can create vm from this cow volume. Also change volume xml generation method to lxml.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/i18n.py | 1 + src/kimchi/model/vms.py | 1 + src/kimchi/vmtemplate.py | 31 +++++++++++++++++++------------ 3 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index f872bee..1f70767 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -126,6 +126,7 @@ messages = { "KCHTMPL0021E": _("Unable to delete template due error: %(err)s"), "KCHTMPL0022E": _("Disk size must be greater than 1GB."), "KCHTMPL0023E": _("Template base image must be a valid local image file"), + "KCHTMPL0024E": _("Cannot identify base image %(path)s format"),
"KCHPOOL0001E": _("Storage pool %(name)s already exists"), "KCHPOOL0002E": _("Storage pool %(name)s does not exist"), diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 8c0dcb1..7d975b2 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -201,6 +201,7 @@ class VMsModel(object): # the user from UI or manually. vol_list = [] if t._get_storage_type() in ["iscsi", "scsi"]: + # FIXME: iscsi and scsi storage work with base image needs to be fixed. vol_list = [] else: vol_list = t.fork_vm_storage(vm_uuid) diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py index 9e8ed3d..b17e3ff 100644 --- a/src/kimchi/vmtemplate.py +++ b/src/kimchi/vmtemplate.py @@ -265,19 +265,26 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/> 'type': 'disk', 'format': fmt, 'path': '%s/%s' % (storage_path, volume)} - info['allocation'] = 0 if fmt == 'qcow2' else info['capacity'] - info['xml'] = """ - <volume> - <name>%(name)s</name> - <allocation unit="G">%(allocation)s</allocation> - <capacity unit="G">%(capacity)s</capacity> - <target> - <format type='%(format)s'/> - <path>%(path)s</path> - </target> - </volume> - """ % info + + if 'base' in d: + info['base'] = dict() + base_fmt = probe_img_info(d['base'])['format'] + if base_fmt is None: + raise InvalidParameter("KCHTMPL0024E", {'path': d['base']}) + info['base']['path'] = d['base'] + info['base']['format'] = base_fmt + + v_tree = E.volume(E.name(info['name'])) + v_tree.append(E.allocation(str(info['allocation']), unit='G')) + v_tree.append(E.capacity(str(info['capacity']), unit='G')) + target = E.target( + E.format(type=info['format']), E.path(info['path'])) + if 'base' in d: + v_tree.append(E.backingStore( + E.path(info['base']['path']), E.format(type=info['base']['format']))) + v_tree.append(target) + info['xml'] = etree.tostring(v_tree) ret.append(info) return ret
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Adding base img report in mockmodel Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/storagevolumes.py | 2 +- src/kimchi/mockmodel.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py index c4d6c41..983c52c 100644 --- a/src/kimchi/control/storagevolumes.py +++ b/src/kimchi/control/storagevolumes.py @@ -52,7 +52,7 @@ class StorageVolume(Resource): 'ref_cnt': self.info['ref_cnt'], 'format': self.info['format']} - for key in ('os_version', 'os_distro', 'bootable'): + for key in ('os_version', 'os_distro', 'bootable', 'base'): val = self.info.get(key) if val: res[key] = val diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index ccaa4d8..ec8730d 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -487,6 +487,8 @@ class MockModel(object): volume.info['format'] = params['format'] volume.info['path'] = os.path.join( pool.info['path'], name) + if 'base' in params: + volume.info['base'] = copy.deepcopy(params['base']) except KeyError, item: raise MissingParameter("KCHVOL0004E", {'item': str(item), 'volume': name}) @@ -1004,6 +1006,8 @@ class MockVMTemplate(VMTemplate): for vol_info in volumes: vol_info['capacity'] = vol_info['capacity'] << 10 vol_info['ref_cnt'] = 1 + if 'base' in self.info: + vol_info['base'] = copy.deepcopy(self.info['base']) self.model.storagevolumes_create(pool.name, vol_info) disk_paths.append({'pool': pool.name, 'volume': vol_info['name']}) return disk_paths -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Adding base img report in mockmodel
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/control/storagevolumes.py | 2 +- src/kimchi/mockmodel.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py index c4d6c41..983c52c 100644 --- a/src/kimchi/control/storagevolumes.py +++ b/src/kimchi/control/storagevolumes.py @@ -52,7 +52,7 @@ class StorageVolume(Resource): 'ref_cnt': self.info['ref_cnt'], 'format': self.info['format']}
- for key in ('os_version', 'os_distro', 'bootable'): + for key in ('os_version', 'os_distro', 'bootable', 'base'): val = self.info.get(key) if val: res[key] = val diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index ccaa4d8..ec8730d 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -487,6 +487,8 @@ class MockModel(object): volume.info['format'] = params['format'] volume.info['path'] = os.path.join( pool.info['path'], name) + if 'base' in params: + volume.info['base'] = copy.deepcopy(params['base']) except KeyError, item: raise MissingParameter("KCHVOL0004E", {'item': str(item), 'volume': name}) @@ -1004,6 +1006,8 @@ class MockVMTemplate(VMTemplate): for vol_info in volumes: vol_info['capacity'] = vol_info['capacity'] << 10 vol_info['ref_cnt'] = 1 + if 'base' in self.info: + vol_info['base'] = copy.deepcopy(self.info['base']) self.model.storagevolumes_create(pool.name, vol_info) disk_paths.append({'pool': pool.name, 'volume': vol_info['name']}) return disk_paths

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/test_model.py | 25 +++++++++++++++++++++++++ tests/test_rest.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/tests/test_model.py b/tests/test_model.py index 30daafa..3bc0f7f 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -107,6 +107,31 @@ class ModelTests(unittest.TestCase): self.assertFalse('kimchi-vm' in vms) @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_image_based_template(self): + disk_path = '/tmp/existent2.img' + open(disk_path, 'w').close() + + inst = model.Model(objstore_loc=self.tmp_store) + + with RollbackContext() as rollback: + params = {'name': 'test', 'disks': [{'base': disk_path}]} + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + + params = {'name': 'kimchi-vm', 'template': '/templates/test'} + inst.vms_create(params) + rollback.prependDefer(inst.vm_delete, 'kimchi-vm') + + vms = inst.vms_get_list() + self.assertTrue('kimchi-vm' in vms) + + inst.vm_start('kimchi-vm') + rollback.prependDefer(inst.vm_poweroff, 'kimchi-vm') + + info = inst.vm_lookup('kimchi-vm') + self.assertEquals('running', info['state']) + + @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', 'disks': [], 'cdrom': self.kimchi_iso} diff --git a/tests/test_rest.py b/tests/test_rest.py index 97668e7..b58fc43 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -854,6 +854,27 @@ class RestTests(unittest.TestCase): resp = json.loads(resp.read()) self.assertIn(u"KCHVM0012E", resp['reason']) + def test_create_vm_with_img_based_template(self): + resp = json.loads( + self.request('/storagepools/default/storagevolumes').read()) + self.assertEquals(0, len(resp)) + + # Create a Template + mock_base = '/tmp/mock.img' + open(mock_base, 'w').close() + req = json.dumps({'name': 'test', 'disks': [{'base':mock_base}]}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + + req = json.dumps({'template': '/templates/test'}) + json.loads(self.request('/vms', req, 'POST').read()) + + # Test storage volume created with backing store of base file + resp = json.loads( + self.request('/storagepools/default/storagevolumes').read()) + self.assertEquals(1, len(resp)) + self.assertEquals(mock_base, resp[0]['base']['path']) + def test_get_storagepools(self): storagepools = json.loads(self.request('/storagepools').read()) self.assertEquals(2, len(storagepools)) @@ -1076,6 +1097,15 @@ class RestTests(unittest.TestCase): 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/alt', 'disks':[{'base':'/tmp/mock.img'}]} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + # Create a template graphics = {'type': 'spice', 'listen': '127.0.0.1'} t = {'name': 'test', 'os_distro': 'ImagineOS', -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 07/20/2014 12:08 PM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/test_model.py | 25 +++++++++++++++++++++++++ tests/test_rest.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+)
diff --git a/tests/test_model.py b/tests/test_model.py index 30daafa..3bc0f7f 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -107,6 +107,31 @@ class ModelTests(unittest.TestCase): self.assertFalse('kimchi-vm' in vms)
@unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_image_based_template(self): + disk_path = '/tmp/existent2.img' + open(disk_path, 'w').close() + + inst = model.Model(objstore_loc=self.tmp_store) + + with RollbackContext() as rollback: + params = {'name': 'test', 'disks': [{'base': disk_path}]} + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + + params = {'name': 'kimchi-vm', 'template': '/templates/test'} + inst.vms_create(params) + rollback.prependDefer(inst.vm_delete, 'kimchi-vm') + + vms = inst.vms_get_list() + self.assertTrue('kimchi-vm' in vms) + + inst.vm_start('kimchi-vm') + rollback.prependDefer(inst.vm_poweroff, 'kimchi-vm') + + info = inst.vm_lookup('kimchi-vm') + self.assertEquals('running', info['state']) + + @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', 'disks': [], 'cdrom': self.kimchi_iso} diff --git a/tests/test_rest.py b/tests/test_rest.py index 97668e7..b58fc43 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -854,6 +854,27 @@ class RestTests(unittest.TestCase): resp = json.loads(resp.read()) self.assertIn(u"KCHVM0012E", resp['reason'])
+ def test_create_vm_with_img_based_template(self): + resp = json.loads( + self.request('/storagepools/default/storagevolumes').read()) + self.assertEquals(0, len(resp)) + + # Create a Template + mock_base = '/tmp/mock.img' + open(mock_base, 'w').close() + req = json.dumps({'name': 'test', 'disks': [{'base':mock_base}]}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + + req = json.dumps({'template': '/templates/test'}) + json.loads(self.request('/vms', req, 'POST').read()) + + # Test storage volume created with backing store of base file + resp = json.loads( + self.request('/storagepools/default/storagevolumes').read()) + self.assertEquals(1, len(resp)) + self.assertEquals(mock_base, resp[0]['base']['path']) + def test_get_storagepools(self): storagepools = json.loads(self.request('/storagepools').read()) self.assertEquals(2, len(storagepools)) @@ -1076,6 +1097,15 @@ class RestTests(unittest.TestCase): 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/alt', 'disks':[{'base':'/tmp/mock.img'}]} + req = json.dumps(t) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + # Create a template graphics = {'type': 'spice', 'listen': '127.0.0.1'} t = {'name': 'test', 'os_distro': 'ImagineOS',
participants (3)
-
Aline Manera
-
lvroyce0210@gmail.com
-
Royce Lv