Sent V5 having fix for failed test cases also.
On 09/06/2016 09:40 PM, Aline Manera wrote:
Hi Archana,
There are some tests failing on x86:
======================================================================
FAIL: test_tmpl_lifecycle (test_template.TemplateTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_template.py", line 151, in test_tmpl_lifecycle
self.assertEquals(201, resp.status)
AssertionError: 201 != 400
----------------------------------------------------------------------
Ran 5 tests in 19.531s
FAILED (failures=1)
[06/Sep/2016:13:05:18] ENGINE Waiting for child threads to terminate...
***** Running unit test: test_model_libvirtevents... PASSED - Ran
1 test in 43.125s
***** Running unit test: test_rest... FAILED
======================================================================
FAIL: test_create_vm_with_img_based_template (test_rest.HttpsRestTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_rest.py", line 1168, in
test_create_vm_with_img_based_template
self.assertEquals(201, resp.status)
AssertionError: 201 != 400
======================================================================
FAIL: test_create_vm_with_img_based_template (test_rest.RestTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_rest.py", line 1168, in
test_create_vm_with_img_based_template
self.assertEquals(201, resp.status)
AssertionError: 201 != 400
----------------------------------------------------------------------
Ran 42 tests in 153.490s
FAILED (failures=2)
Could you fix that and resend?
Ya, ent V5 having fix for failed test cases also.
Thanks,
Aline Manera
On 09/06/2016 12:44 PM, archus(a)linux.vnet.ibm.com wrote:
> From: Harshal Patil <harshalp(a)linux.vnet.ibm.com>
>
> V1:
> This patch adds support for creating templates on s390x arch
> without using libvirt related storage calls
>
> V2:
> Review comments
>
> V3:
> Review comments and refactoring
>
> V4:
> Rebase patch to latest upstream.
>
> Signed-off-by: Harshal Patil <harshalp(a)linux.vnet.ibm.com>
> ---
> docs/API.md | 2 +
> i18n.py | 3 ++
> model/storagepools.py | 13 ++++--
> model/storagevolumes.py | 1 -
> model/templates.py | 27 +++++++++---
> model/vms.py | 9 +++-
> osinfo.py | 35 +++++++++++++--
> utils.py | 34 +++++++++++++++
> vmtemplate.py | 111
> +++++++++++++++++++++++++++++++-----------------
> 9 files changed, 179 insertions(+), 56 deletions(-)
>
> diff --git a/docs/API.md b/docs/API.md
> index 1c20466..cff623e 100644
> --- a/docs/API.md
> +++ b/docs/API.md
> @@ -438,6 +438,7 @@ A interface represents available network
> interface on VM.
> over current will be used exclusively for memory hotplug
> * cdrom: A volume name or URI to an ISO image
> * storagepool: URI of the storagepool where template allocates
> vm storage.
> + * path *(optional and only valid for s390x architecture)*:
> Storage path to store virtual disks without libvirt.
> * networks *(optional)*: list of networks will be assigned to
> the new VM.
> * interfaces *(optional)*: list of host network interfaces will
> be assigned to the new VM. Only applicable for s390x or s390
> architecture.
> * type: Type of host network interface. Type should be
> 'macvtap' for host network interface (Ethernet, Bond, VLAN) to be
> connected as direct MacVTap; or 'ovs' for openvswitch host network
> interface to be connected as virtual switch to a VM.
> @@ -504,6 +505,7 @@ A interface represents available network
> interface on VM.
> * format: Format of the image. Valid formats: qcow, qcow2,
> qed, raw, vmdk, vpc.
> * pool: Storage pool information
> * name: URI of the storagepool where template allocates
> vm disk.
> + * path *(optional and only valid for s390x architecture)*:
> Either pool or path to store the virtual disks should be specified.
> * graphics *(optional)*: A dict of graphics paramenters of this
> template
> * type: The type of graphics. It can be VNC or spice or None.
> * vnc: Graphical display using the Virtual Network
> diff --git a/i18n.py b/i18n.py
> index 47c829e..b6533d4 100644
> --- a/i18n.py
> +++ b/i18n.py
> @@ -203,6 +203,9 @@ messages = {
> "KCHTMPL0037E": _("Interfaces should be list of interfaces.
> Each interface should have name, type and mode(optional, only
> applicable for interfcae type 'macvtap'."),
> "KCHTMPL0038E": _("Interface expects an object with parameters:
> 'name', 'type' and 'mode'. Name should be name of host
network
> interface (Ethernet, Bond, VLAN) for type 'macvtap' or the name of
> host openvswitch bridge interface for type 'ovs'. Mode (optional) is
> only applicable for interface type 'macvtap' to indicates whether
> packets will be delivered directly to target device (bridge) or to
> the external bridge (vepa-capable bridge)."),
> "KCHTMPL0039E": _("Interfaces parameter only supported on s390x
> or s390 architecture."),
> + "KCHTMPL0040E": _("Storage without libvirt pool is not supported
> on this architecture"),
> + "KCHTMPL0041E": _("Error while creating the virtual disk for the
> guest. Details: %(err)s"),
> + "KCHTMPL0042E": _("When setting template disks without libvirt,
> following parameters are required: 'index', 'format', 'path',
'size'"),
>
> "KCHPOOL0001E": _("Storage pool %(name)s already exists"),
> "KCHPOOL0002E": _("Storage pool %(name)s does not exist"),
> diff --git a/model/storagepools.py b/model/storagepools.py
> index af92ec9..5942b31 100644
> --- a/model/storagepools.py
> +++ b/model/storagepools.py
> @@ -33,7 +33,7 @@ from wok.plugins.kimchi.model.host import DeviceModel
> from wok.plugins.kimchi.model.libvirtstoragepool import StoragePoolDef
> from wok.plugins.kimchi.osinfo import defaults as tmpl_defaults
> from wok.plugins.kimchi.scan import Scanner
> -from wok.plugins.kimchi.utils import pool_name_from_uri
> +from wok.plugins.kimchi.utils import pool_name_from_uri, is_s390x
>
>
> ISO_POOL_NAME = u'kimchi_isos'
> @@ -57,6 +57,7 @@ STORAGE_SOURCES = {'netfs': {'addr':
> '/pool/source/host/@name',
>
>
> class StoragePoolsModel(object):
> +
> def __init__(self, **kargs):
> self.conn = kargs['conn']
> self.objstore = kargs['objstore']
> @@ -72,6 +73,9 @@ class StoragePoolsModel(object):
> def _check_default_pools(self):
> pools = {}
>
> + if is_s390x():
> + return
> +
> default_pool =
tmpl_defaults['disks'][0]['pool']['name']
> default_pool = default_pool.split('/')[-1]
>
> @@ -437,9 +441,10 @@ class StoragePoolModel(object):
> for tmpl in templates:
> t_info = session.get('template', tmpl)
> for disk in t_info['disks']:
> - t_pool = disk['pool']['name']
> - if pool_name_from_uri(t_pool) == pool_name:
> - return True
> + if 'pool' in disk:
> + t_pool = disk['pool']['name']
> + if pool_name_from_uri(t_pool) == pool_name:
> + return True
> return False
>
> def deactivate(self, name):
> diff --git a/model/storagevolumes.py b/model/storagevolumes.py
> index a6ce97b..2d68027 100644
> --- a/model/storagevolumes.py
> +++ b/model/storagevolumes.py
> @@ -43,7 +43,6 @@ from wok.plugins.kimchi.model.diskutils import
> get_disk_used_by
> from wok.plugins.kimchi.model.storagepools import StoragePoolModel
> from wok.plugins.kimchi.utils import get_next_clone_name
>
> -
> VOLUME_TYPE_MAP = {0: 'file',
> 1: 'block',
> 2: 'directory',
> diff --git a/model/templates.py b/model/templates.py
> index 04e6626..a5d17af 100644
> --- a/model/templates.py
> +++ b/model/templates.py
> @@ -28,13 +28,15 @@ import urlparse
>
> from wok.exception import InvalidOperation, InvalidParameter
> from wok.exception import NotFoundError, OperationFailed
> -from wok.utils import probe_file_permission_as_user,
> run_setfacl_set_attr
> +from wok.utils import probe_file_permission_as_user
> +from wok.utils import run_setfacl_set_attr
> from wok.xmlutils.utils import xpath_get_text
>
> from wok.plugins.kimchi.config import get_kimchi_version
> from wok.plugins.kimchi.kvmusertests import UserTests
> from wok.plugins.kimchi.model.cpuinfo import CPUInfoModel
> from wok.plugins.kimchi.utils import is_libvirtd_up,
> pool_name_from_uri
> +from wok.plugins.kimchi.utils import create_disk_image
> from wok.plugins.kimchi.vmtemplate import VMTemplate
>
> ISO_TYPE = "ISO 9660 CD-ROM"
> @@ -417,15 +419,26 @@ class LibvirtVMTemplate(VMTemplate):
>
> def fork_vm_storage(self, vm_uuid):
> # Provision storages:
> - vol_list = self.to_volume_list(vm_uuid)
> + disk_and_vol_list = self.to_volume_list(vm_uuid)
> try:
> - for v in vol_list:
> - pool = self._get_storage_pool(v['pool'])
> - # outgoing text to libvirt, encode('utf-8')
> - pool.createXML(v['xml'].encode('utf-8'), 0)
> + for v in disk_and_vol_list:
> + if v['pool'] is not None:
> + pool = self._get_storage_pool(v['pool'])
> + # outgoing text to libvirt, encode('utf-8')
> + pool.createXML(v['xml'].encode('utf-8'), 0)
> + else:
> + capacity = v['capacity']
> + format_type = v['format']
> + path = v['path']
> + create_disk_image(
> + format_type=format_type,
> + path=path,
> + capacity=capacity)
> +
> except libvirt.libvirtError as e:
> raise OperationFailed("KCHVMSTOR0008E", {'error':
> e.message})
> - return vol_list
> +
> + return disk_and_vol_list
>
> def set_cpu_info(self):
> # undefined topology: consider these values to calculate
> maxvcpus
> diff --git a/model/vms.py b/model/vms.py
> index b889166..6a9b1d1 100644
> --- a/model/vms.py
> +++ b/model/vms.py
> @@ -61,7 +61,7 @@ from wok.plugins.kimchi.model.utils import
> remove_metadata_node
> from wok.plugins.kimchi.model.utils import set_metadata_node
> from wok.plugins.kimchi.osinfo import defaults, MEM_DEV_SLOTS
> from wok.plugins.kimchi.screenshot import VMScreenshot
> -from wok.plugins.kimchi.utils import get_next_clone_name
> +from wok.plugins.kimchi.utils import get_next_clone_name, is_s390x
> from wok.plugins.kimchi.utils import template_name_from_uri
> from wok.plugins.kimchi.xmlutils.bootorder import get_bootorder_node
> from wok.plugins.kimchi.xmlutils.bootorder import get_bootmenu_node
> @@ -1405,6 +1405,13 @@ class VMModel(object):
> except libvirt.libvirtError as e:
> wok_log.error('Unable to get storage volume by
> path: %s' %
> e.message)
> + try:
> + if is_s390x() and os.path.exists(path):
> + os.remove(path)
> + except Exception as e:
> + wok_log.error('Unable to delete storage path: %s' %
> + e.message)
> +
> except Exception as e:
> raise OperationFailed('KCHVOL0017E', {'err':
> e.message})
>
> diff --git a/osinfo.py b/osinfo.py
> index 3e56d97..8ec9480 100644
> --- a/osinfo.py
> +++ b/osinfo.py
> @@ -159,6 +159,12 @@ def _get_tmpl_defaults():
> 'maxmemory':
> _get_default_template_mem()}
> tmpl_defaults['storage']['disk.0'] = {'size': 10,
'format':
> 'qcow2',
> 'pool': 'default'}
> + is_on_s390x = True if _get_arch() == 's390x' else False
> +
> + if is_on_s390x:
> + tmpl_defaults['storage']['disk.0']['path'] =
> '/var/lib/libvirt/images/'
> + del tmpl_defaults['storage']['disk.0']['pool']
> +
> tmpl_defaults['processor']['vcpus'] = 1
> tmpl_defaults['processor']['maxvcpus'] = 1
> tmpl_defaults['graphics'] = {'type': 'vnc',
'listen': '127.0.0.1'}
> @@ -166,7 +172,12 @@ def _get_tmpl_defaults():
> default_config = ConfigObj(tmpl_defaults)
>
> # Load template configuration file
> - config_file = os.path.join(kimchiPaths.sysconf_dir,
> 'template.conf')
> + if is_on_s390x:
> + config_file = os.path.join(
> + kimchiPaths.sysconf_dir,
> + 'template_s390x.conf')
> + else:
> + config_file = os.path.join(kimchiPaths.sysconf_dir,
> 'template.conf')
> config = ConfigObj(config_file)
>
> # Merge default configuration with file configuration
> @@ -187,11 +198,27 @@ def _get_tmpl_defaults():
> # Parse storage section to get disks values
> storage_section = default_config.pop('storage')
> defaults['disks'] = []
> - for disk in storage_section.keys():
> +
> + for index, disk in enumerate(storage_section.keys()):
> data = storage_section[disk]
> data['index'] = int(disk.split('.')[1])
> - data['pool'] = {"name":
'/plugins/kimchi/storagepools/' +
> - storage_section[disk].pop('pool')}
> + # Right now 'Path' is only supported on s390x
> + if storage_section[disk].get('path') and is_on_s390x:
> + data['path'] = storage_section[disk].pop('path')
> + if 'size' not in storage_section[disk]:
> + data['size'] =
> tmpl_defaults['storage']['disk.0']['size']
> + else:
> + data['size'] = storage_section[disk].pop('size')
> +
> + if 'format' not in storage_section[disk]:
> + data['format'] =
> tmpl_defaults['storage']['disk.0']['format']
> + else:
> + data['format'] =
storage_section[disk].pop('format')
> + else:
> + storage_section[disk].get('pool')
> + data['pool'] = {"name":
'/plugins/kimchi/storagepools/' +
> + storage_section[disk].pop('pool')}
> +
> defaults['disks'].append(data)
>
> # Parse processor section to get vcpus and cpu_topology values
> diff --git a/utils.py b/utils.py
> index 26d3cf6..7129bfe 100644
> --- a/utils.py
> +++ b/utils.py
> @@ -24,6 +24,7 @@ import platform
> import re
> import sqlite3
> import time
> +import os
> import urllib2
> from httplib import HTTPConnection, HTTPException
> from urlparse import urlparse
> @@ -31,6 +32,7 @@ from urlparse import urlparse
> from wok.exception import InvalidParameter, OperationFailed
> from wok.plugins.kimchi import config
> from wok.plugins.kimchi.osinfo import get_template_default
> +from wok.stringutils import encode_value
> from wok.utils import run_command, wok_log
> from wok.xmlutils.utils import xpath_get_text
>
> @@ -272,3 +274,35 @@ def is_libvirtd_up():
>
> output, error, rc = run_command(cmd, silent=True)
> return True if output == 'active\n' else False
> +
> +
> +def is_s390x():
> + """
> + Check if current arch is 's390x'
> + Returns:
> + """
> + if os.uname()[4] == 's390x':
> + return True
> +
> + return False
> +
> +
> +def create_disk_image(format_type, path, capacity):
> + """
> + Create a disk image for the Guest
> + Args:
> + format: Format of the storage. e.g. qcow2
> + path: Path where the virtual disk will be created
> + capacity: Capacity of the virtual disk in GBs
> +
> + Returns:
> +
> + """
> + out, err, rc = run_command(
> + ["/usr/bin/qemu-img", "create", "-f",
format_type, "-o",
> + "preallocation=metadata", path, encode_value(capacity) +
"G"])
> +
> + if rc != 0:
> + raise OperationFailed("KCHTMPL0041E", {'err': err})
> +
> + return
> diff --git a/vmtemplate.py b/vmtemplate.py
> index 07cebb9..20edbfb 100644
> --- a/vmtemplate.py
> +++ b/vmtemplate.py
> @@ -31,7 +31,8 @@ from wok.exception import MissingParameter,
> OperationFailed
> 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.utils import check_url_path, is_s390x
> +from wok.plugins.kimchi.utils import 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
> @@ -42,6 +43,7 @@ from wok.plugins.kimchi.xmlutils.serial import
> get_serial_xml
>
>
> class VMTemplate(object):
> +
> def __init__(self, args, scan=False, netboot=False):
> """
> Construct a VM Template from a widely variable amount of
> information.
> @@ -95,35 +97,55 @@ class VMTemplate(object):
> disks = self.info.get('disks')
>
> basic_disk = ['index', 'format', 'pool',
'size']
> + basic_path_disk = ['index', 'format', 'path',
'size']
> ro_disk = ['index', 'format', 'pool',
'volume']
> base_disk = ['index', 'base', 'pool',
'size', 'format']
>
> for index, disk in enumerate(disks):
> disk_info = dict(default_disk)
> -
> - pool = disk.get('pool', default_disk['pool'])
> - pool_type = self._get_storage_type(pool['name'])
> -
> - if pool_type in ['iscsi', 'scsi']:
> - disk_info = {'index': 0, 'format': 'raw',
'volume':
> None}
> -
> - disk_info.update(disk)
> - pool_name = disk_info.get('pool', {}).get('name')
> - if pool_name is None:
> - raise MissingParameter('KCHTMPL0028E')
> -
> - keys = sorted(disk_info.keys())
> - if ((keys != sorted(basic_disk)) and (keys !=
> sorted(ro_disk)) and
> - (keys != sorted(base_disk))):
> - raise MissingParameter('KCHTMPL0028E')
> -
> - if pool_type in ['logical', 'iscsi', 'scsi']:
> - if disk_info['format'] != 'raw':
> - raise InvalidParameter('KCHTMPL0029E')
> -
> - disk_info['pool']['type'] = pool_type
> - disk_info['index'] = disk_info.get('index', index)
> - self.info['disks'][index] = disk_info
> + if disk.get('pool'):
> + pool = disk.get('pool', default_disk.get('pool'))
> + pool_type = self._get_storage_type(pool['name'])
> + if pool_type in ['iscsi', 'scsi']:
> + disk_info = {'index': 0, 'format':
'raw',
> 'volume': None}
> +
> + # This check is required where 'path' disk
> + # exists and is replaced by 'pool' disk during
> + # template update
> + if 'path' in disk_info:
> + del disk_info['path']
> +
> + disk_info.update(disk)
> + pool_name = disk_info.get('pool', {}).get('name')
> + if pool_name is None:
> + raise MissingParameter('KCHTMPL0028E')
> +
> + keys = sorted(disk_info.keys())
> + if ((keys != sorted(basic_disk)) and
> + (keys != sorted(ro_disk)) and
> + (keys != sorted(base_disk)) and
> + (keys != sorted(basic_path_disk))):
> + raise MissingParameter('KCHTMPL0028E')
> +
> + if pool_type in ['logical', 'iscsi',
'scsi']:
> + if disk_info['format'] != 'raw':
> + raise InvalidParameter('KCHTMPL0029E')
> +
> + disk_info['pool']['type'] = pool_type
> + disk_info['index'] = disk_info.get('index', index)
> + self.info['disks'][index] = disk_info
> + elif is_s390x():
> + # For now support 'path' only on s390x
> + path = disk.get('path', default_disk['path'])
> + disk_info.update(disk)
> + keys = sorted(disk_info.keys())
> + if keys != sorted(basic_path_disk):
> + raise MissingParameter('KCHTMPL0042E')
> + disk_info['path'] = path
> + disk_info['index'] = disk_info.get('index', index)
> + self.info['disks'][index] = disk_info
> + else:
> + raise InvalidParameter('KCHTMPL0040E')
>
> def _get_os_info(self, args, scan):
> distro = version = 'unknown'
> @@ -217,8 +239,9 @@ class VMTemplate(object):
> params = dict(base_disk_params)
> params['format'] = disk['format']
> params['index'] = index
> - params.update(locals().get('%s_disk_params' %
> - disk['pool']['type'], {}))
> + if disk.get('pool'):
> + params.update(locals().get('%s_disk_params' %
> + disk['pool']['type'], {}))
>
> volume = disk.get('volume')
> if volume is not None:
> @@ -226,9 +249,13 @@ class VMTemplate(object):
> volume)
> else:
> img = "%s-%s.img" % (vm_uuid, params['index'])
> - storage_path =
> self._get_storage_path(disk['pool']['name'])
> + if disk.get('pool'):
> + storage_path =
> self._get_storage_path(disk['pool']['name'])
> + params['pool_type'] =
disk['pool']['type']
> + elif disk.get('path'):
> + storage_path = disk.get('path')
> + params['pool_type'] = None
> params['path'] = os.path.join(storage_path, img)
> - params['pool_type'] = disk['pool']['type']
> disks_xml += get_disk_xml(params)[1]
>
> return unicode(disks_xml, 'utf-8')
> @@ -237,20 +264,24 @@ class VMTemplate(object):
> ret = []
> for i, d in enumerate(self.info['disks']):
> # Create only .img. If storagepool is (i)SCSI, volumes
> will be LUNs
> - if d['pool']['type'] in ["iscsi",
"scsi"]:
> + if 'pool' in d and d['pool']['type'] in
["iscsi", "scsi"]:
> continue
>
> index = d.get('index', i)
> volume = "%s-%s.img" % (vm_uuid, index)
>
> - storage_path =
self._get_storage_path(d['pool']['name'])
> + if 'path' in d:
> + storage_path = d['path']
> + else:
> + storage_path =
> self._get_storage_path(d['pool']['name'])
> +
> info = {'name': volume,
> 'capacity': d['size'],
> 'format': d['format'],
> 'path': '%s/%s' % (storage_path, volume),
> - 'pool': d['pool']['name']}
> + 'pool': d['pool']['name'] if
'pool' in d else None}
>
> - if 'logical' == d['pool']['type'] or \
> + if ('pool' in d and 'logical' ==
d['pool']['type']) or \
> info['format'] not in ['qcow2', 'raw']:
> info['allocation'] = info['capacity']
> else:
> @@ -467,8 +498,9 @@ class VMTemplate(object):
>
> def validate(self):
> for disk in self.info.get('disks'):
> - pool_uri = disk.get('pool', {}).get('name')
> - self._get_storage_pool(pool_uri)
> + if 'pool' in disk:
> + pool_uri = disk.get('pool', {}).get('name')
> + self._get_storage_pool(pool_uri)
> self._network_validate()
> self._iso_validate()
> self.cpuinfo_validate()
> @@ -519,10 +551,11 @@ class VMTemplate(object):
>
> # validate storagepools and image-based templates integrity
> for disk in self.info['disks']:
> - pool_uri = disk['pool']['name']
> - pool_name = pool_name_from_uri(pool_uri)
> - if pool_name not in self._get_active_storagepools_name():
> - invalid['storagepools'] = [pool_name]
> + if 'pool' in disk:
> + pool_uri = disk['pool']['name']
> + pool_name = pool_name_from_uri(pool_uri)
> + if pool_name not in
> self._get_active_storagepools_name():
> + invalid['storagepools'] = [pool_name]
>
> if disk.get("base") is None:
> continue