Hi Harshal,

Some inline comments below. Please feel free to reply back if it does not make sense to you.

Thanks,

Archana Singh

On 08/30/2016 06:39 PM, harshalp@linux.vnet.ibm.com wrote:
From: Harshal Patil <harshalp@linux.vnet.ibm.com>

This patch adds support for creating templates on s390x arch 
without using libvirt related storage calls

Signed-off-by: Harshal Patil <harshalp@linux.vnet.ibm.com>
---
 docs/API.md             |   2 +
 i18n.py                 |   2 +
 model/storagepools.py   |  15 +++++--
 model/storagevolumes.py |  10 +++--
 model/templates.py      |  56 +++++++++++++++++++++-----
 model/vms.py            |  38 ++++++++++++------
 osinfo.py               |  47 ++++++++++++++++++++--
 utils.py                |  13 ++++++
 vmtemplate.py           | 105 ++++++++++++++++++++++++++++++------------------
 9 files changed, 213 insertions(+), 75 deletions(-)

diff --git a/docs/API.md b/docs/API.md
index 7bd677f..d8d191a 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -427,6 +427,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 : Storage path to store virtual disks without libvirt
     * networks *(optional)*: list of networks will be assigned to the new VM.
     * disks: An array of requested disks with the following optional fields
       (either *size* or *volume* must be specified):
@@ -481,6 +482,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): 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 e8d9c05..8e55e01 100644
--- a/i18n.py
+++ b/i18n.py
@@ -190,6 +190,8 @@ messages = {
     "KCHTMPL0031E": _("Memory value (%(mem)sMiB) must be equal or lesser than maximum memory value (%(maxmem)sMiB)"),
     "KCHTMPL0032E": _("Unable to update template due error: %(err)s"),
     "KCHTMPL0033E": _("Parameter 'disks' requires at least one disk object"),
+    "KCHTMPL0034E": _("Storage without libvirt pool is not supported on this architecture"),
+    "KCHTMPL0035E": _("Error while creating the virtual disk for the guest. Details: %(err)s"),

     "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 a2dbaec..cbd756b 100644
--- a/model/storagepools.py
+++ b/model/storagepools.py
@@ -32,7 +32,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'
@@ -56,6 +56,7 @@ STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',


 class StoragePoolsModel(object):
+
     def __init__(self, **kargs):
         self.conn = kargs['conn']
         self.objstore = kargs['objstore']
@@ -71,6 +72,11 @@ class StoragePoolsModel(object):
     def _check_default_pools(self):
         pools = {}

+        if 'disks' not in tmpl_defaults or len(tmpl_defaults['disks']) == 0 \
+                or (not tmpl_defaults.get('disks')[0].get(
+                'pool') and is_s390x()):  # s390x specific handling
Can we have some more details in comments on s390x checks?
+            return
+        tmpl_defaults.get('disks')
         default_pool = tmpl_defaults['disks'][0]['pool']['name']
         default_pool = default_pool.split('/')[-1]

@@ -436,9 +442,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 7b2272b..1e4a2af 100644
--- a/model/storagevolumes.py
+++ b/model/storagevolumes.py
@@ -41,8 +41,7 @@ from wok.plugins.kimchi.kvmusertests import UserTests
 from wok.plugins.kimchi.model.diskutils import get_disk_used_by
 from wok.plugins.kimchi.model.diskutils import set_disk_used_by
 from wok.plugins.kimchi.model.storagepools import StoragePoolModel
-from wok.plugins.kimchi.utils import get_next_clone_name
-
+from wok.plugins.kimchi.utils import get_next_clone_name, is_s390x

 VOLUME_TYPE_MAP = {0: 'file',
                    1: 'block',
@@ -275,8 +274,11 @@ class StorageVolumeModel(object):
         self.task = TaskModel(**kargs)
         self.storagevolumes = StorageVolumesModel(**kargs)
         self.storagepool = StoragePoolModel(**kargs)
-        if self.conn.get() is not None:
-            self.libvirt_user = UserTests().probe_user()
+        if is_s390x():  # s390x specific handling
+            if self.conn.get() is not None:
+                self.libvirt_user = UserTests().probe_user()
+            else:
+                self.libvirt_user = None
         else:
             self.libvirt_user = None
In case of architecture other than s390x also UserTests().probe_user() is called incase self.conn.get() is not None, otherwise self.libvirt_user is assigned to None. Any specific reason of having architecture check and doing the same thing?

diff --git a/model/templates.py b/model/templates.py
index a299c85..4edd98e 100644
--- a/model/templates.py
+++ b/model/templates.py
@@ -28,7 +28,8 @@ 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, run_command
+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
@@ -47,6 +48,7 @@ if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:


 class TemplatesModel(object):
+
     def __init__(self, **kargs):
         self.objstore = kargs['objstore']
         self.conn = kargs['conn']
@@ -142,7 +144,7 @@ class TemplatesModel(object):
                               get_kimchi_version())
         except InvalidOperation:
             raise
-        except Exception, e:
+        except Exception as e:
             raise OperationFailed('KCHTMPL0020E', {'err': e.message})

         return name
@@ -171,6 +173,7 @@ class TemplatesModel(object):


 class TemplateModel(object):
+
     def __init__(self, **kargs):
         self.objstore = kargs['objstore']
         self.conn = kargs['conn']
@@ -198,10 +201,9 @@ class TemplateModel(object):
         # set default name
         subfixs = [v[len(name):] for v in self.templates.get_list()
                    if v.startswith(name)]
-        indexs = [int(v.lstrip("-clone")) for v in subfixs
-                  if v.startswith("-clone") and
-                  v.lstrip("-clone").isdigit()]
-        indexs.sort()
+        indexs = sorted([int(v.lstrip("-clone")) for v in subfixs
+                         if v.startswith("-clone") and
+                         v.lstrip("-clone").isdigit()])
         index = "1" if not indexs else str(indexs[-1] + 1)
         clone_name = name + "-clone" + index

@@ -267,7 +269,7 @@ class TemplateModel(object):

         except InvalidOperation:
             raise
-        except Exception, e:
+        except Exception as e:
             raise OperationFailed('KCHTMPL0032E', {'err': e.message})

         return params['name']
@@ -290,7 +292,7 @@ def validate_memory(memory):
     # memory limit
     if (current > (MAX_MEM_LIM >> 10)) or (maxmem > (MAX_MEM_LIM >> 10)):
         raise InvalidParameter("KCHVM0079E",
-                               {'value': str(MAX_MEM_LIM / (1024**3))})
+                               {'value': str(MAX_MEM_LIM / (1024 ** 3))})
     if (current > host_memory) or (maxmem > host_memory):
         raise InvalidParameter("KCHVM0078E", {'memHost': host_memory})

@@ -316,6 +318,7 @@ def validate_memory(memory):


 class LibvirtVMTemplate(VMTemplate):
+
     def __init__(self, args, scan=False, conn=None):
         self.conn = conn
         netboot = True if 'netboot' in args.keys() else False
@@ -398,16 +401,47 @@ class LibvirtVMTemplate(VMTemplate):
             raise NotFoundError("KCHVOL0002E", {'name': vol,
                                                 'pool': pool})

+    def _create_disk_image(self, format_type, path, capacity):
This should not be part of libvirtVMTemplate class as name says I guess we should not have any non libvirt related handling here. What do you think?
+        """
+        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, str(capacity) + "G"])
str(capacity) will throw error in case of unicode characters. I will suggest to ensure that its integer and use encode or decode methods to do str conversion.
Does wok_log is taken care inside run_command, if not we need to take care of that also.

+
+        if rc != 0:
+            raise OperationFailed("KCHTMPL0035E", {'err': err})
+
+        return
+
     def fork_vm_storage(self, vm_uuid):
         # Provision storages:
         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)
+                if v['pool'] is not None:
If 'pool' key will not present, it will throw key error. Is this expected to be the behaviour?
Are we saying that key should be there but value can be 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']
+                    self._create_disk_image(
+                        format_type=format_type,
+                        path=path,
+                        capacity=capacity)
I think as name suggested to_volume_list, it should look correct from naming point of view as iterating to volume and handing non volume related stuff. What do you think?
+
         except libvirt.libvirtError as e:
             raise OperationFailed("KCHVMSTOR0008E", {'error': e.message})
+
         return vol_list

     def set_cpu_info(self):
diff --git a/model/vms.py b/model/vms.py
index 3380278..5d1eebb 100644
--- a/model/vms.py
+++ b/model/vms.py
@@ -60,13 +60,13 @@ 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
 from wok.plugins.kimchi.xmlutils.cpu import get_topology_xml
 from wok.plugins.kimchi.xmlutils.disk import get_vm_disk_info, get_vm_disks
-from utils import has_cpu_numa, set_numa_memory
+from .utils import has_cpu_numa, set_numa_memory


 DOM_STATE_MAP = {0: 'nostate',
@@ -114,6 +114,7 @@ vm_locks = {}


 class VMsModel(object):
+
     def __init__(self, **kargs):
         self.conn = kargs['conn']
         self.objstore = kargs['objstore']
@@ -231,6 +232,7 @@ class VMsModel(object):


 class VMModel(object):
+
     def __init__(self, **kargs):
         self.conn = kargs['conn']
         self.objstore = kargs['objstore']
@@ -373,7 +375,7 @@ class VMModel(object):
             vir_dom = self.get_vm(name, self.conn)
             flags = libvirt.VIR_DOMAIN_XML_SECURE
             xml = vir_dom.XMLDesc(flags).decode('utf-8')
-        except libvirt.libvirtError, e:
+        except libvirt.libvirtError as e:
             raise OperationFailed('KCHVM0035E', {'name': name,
                                                  'err': e.message})

@@ -407,7 +409,7 @@ class VMModel(object):
                 vir_conn = self.conn.get()
                 dom = vir_conn.defineXML(xml)
                 self._update_metadata_name(dom, nonascii_name)
-            except libvirt.libvirtError, e:
+            except libvirt.libvirtError as e:
                 raise OperationFailed('KCHVM0035E', {'name': name,
                                                      'err': e.message})

@@ -480,7 +482,7 @@ class VMModel(object):

                 orig_pool_name = vir_pool.name().decode('utf-8')
                 orig_vol_name = vir_orig_vol.name().decode('utf-8')
-            except libvirt.libvirtError, e:
+            except libvirt.libvirtError as e:
                 raise OperationFailed('KCHVM0035E', {'name': domain_name,
                                                      'err': e.message})

@@ -1366,7 +1368,7 @@ class VMModel(object):
         # "OperationFailed" in that case.
         try:
             snapshot_names = self.vmsnapshots.get_list(name)
-        except OperationFailed, e:
+        except OperationFailed as e:
             wok_log.error('cannot list snapshots: %s; '
                           'skipping snapshot deleting...' % e.message)
         else:
@@ -1394,6 +1396,14 @@ class VMModel(object):
             except libvirt.libvirtError as e:
                 wok_log.error('Unable to get storage volume by path: %s' %
                               e.message)
Below changes will not get called if libvirtError will be thrown for the path given. I guess we need to ensure that even if libvirt pool and volume does not exit for path provided below gets executed for s390x.
+                try:
+                    if is_s390x():  # s390x specific handling
+                        if 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})

@@ -1538,7 +1548,7 @@ class VMModel(object):
                 raise OperationFailed("KCHVM0081E",
                                       {'dir': serialconsole.BASE_DIRECTORY})

-        websocket.add_proxy_token(name.encode('utf-8')+'-console',
+        websocket.add_proxy_token(name.encode('utf-8') + '-console',
                                   os.path.join(serialconsole.BASE_DIRECTORY,
                                                name.encode('utf-8')), True)

@@ -1596,7 +1606,7 @@ class VMModel(object):

         try:
             vir_dom.suspend()
-        except libvirt.libvirtError, e:
+        except libvirt.libvirtError as e:
             raise OperationFailed('KCHVM0038E', {'name': name,
                                                  'err': e.message})

@@ -1617,7 +1627,7 @@ class VMModel(object):

         try:
             vir_dom.resume()
-        except libvirt.libvirtError, e:
+        except libvirt.libvirtError as e:
             raise OperationFailed('KCHVM0040E', {'name': name,
                                                  'err': e.message})

@@ -1657,7 +1667,7 @@ class VMModel(object):
                         'destarch': dest_arch
                     }
                 )
-        except Exception, e:
+        except Exception as e:
             raise OperationFailed("KCHVM0066E", {'error': e.message})

         finally:
@@ -1768,7 +1778,7 @@ class VMModel(object):
                 ssh_client,
                 id_rsa_data
             )
-        except Exception, e:
+        except Exception as e:
             raise OperationFailed(
                 "KCHVM0068E",
                 {'host': remote_host, 'user': user, 'error': e.message}
@@ -1833,7 +1843,7 @@ class VMModel(object):
             conn = self.conn.get()
             vol_obj = conn.storageVolLookupByPath(disk_path)
             return vol_obj.info()[1]
-        except Exception, e:
+        except Exception as e:
             raise OperationFailed(
                 "KCHVM0062E",
                 {'path': disk_path, 'error': e.message}
@@ -1949,6 +1959,7 @@ class VMModel(object):


 class VMScreenshotModel(object):
+
     def __init__(self, **kargs):
         self.objstore = kargs['objstore']
         self.conn = kargs['conn']
@@ -1995,6 +2006,7 @@ class VMScreenshotModel(object):


 class LibvirtVMScreenshot(VMScreenshot):
+
     def __init__(self, vm_uuid, conn):
         VMScreenshot.__init__(self, vm_uuid)
         self.conn = conn
@@ -2004,7 +2016,7 @@ class LibvirtVMScreenshot(VMScreenshot):
             fd = opaque
             os.write(fd, buf)

-        fd = os.open(thumbnail, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0644)
+        fd = os.open(thumbnail, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0o644)
         try:
             conn = self.conn.get()
             dom = conn.lookupByUUIDString(self.vm_uuid)
diff --git a/osinfo.py b/osinfo.py
index 2c59312..524b3bd 100644
--- a/osinfo.py
+++ b/osinfo.py
@@ -146,6 +146,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'}
@@ -153,7 +159,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')
Not sure, but should we handle scenario where in case s390x specific arch conf file is not present just go with template.conf?
+    else:
+        config_file = os.path.join(kimchiPaths.sysconf_dir, 'template.conf')
     config = ConfigObj(config_file)

     # Merge default configuration with file configuration
@@ -174,11 +185,39 @@ 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():
+
+    pool_exists = False
+    path_exists = False
+    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:
+            path_exists = True
+            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')
+
+        if storage_section[disk].get('pool'):
+            pool_exists = True
+            data['pool'] = {"name": '/plugins/kimchi/storagepools/' +
+                                    storage_section[disk].pop('pool')}
For one disk either it can be pool or path, both should not be added. Do you agree?
I guess getting pool should be in else of first if.
+
+        # If both pool and path don't exist, pick the defaults
+        if index == len(storage_section.keys()) - 1 and \
+                (not pool_exists and not path_exists):
+            if is_on_s390x:  # Special handling for s390x
+                data['path'] = tmpl_defaults['storage']['disk.0']['path']
+            else:
+                data['pool'] = {"name": '/plugins/kimchi/storagepools/default'}
+
         defaults['disks'].append(data)

     # Parse processor section to get vcpus and cpu_topology values
diff --git a/utils.py b/utils.py
index 26d3cf6..b8f81ca 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
@@ -272,3 +273,15 @@ 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:
+
+    """
do we need new line in comment?
+    if os.uname()[4] == 's390x':
+        return True
+
+    return False
diff --git a/vmtemplate.py b/vmtemplate.py
index 7ac0541..c0d51a5 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,49 @@ 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}
+
+                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 != 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
+            else:
+                if is_s390x():  # For now support 'path' only on s390x
any specific reason of keeping if in side else and not just elif?
+                    path = disk.get('path', default_disk['path'])
+                    disk_info.update(disk)
+                    keys = sorted(disk_info.keys())
+                    if keys != sorted(basic_path_disk):
+                        raise MissingParameter('KCHTMPL0028E')
i18n message has to be changed to convey that either pool or path has to be given in a disk, as current is "KCHTMPL0028E": _("When setting template disks, following parameters are required: 'index', 'pool name', 'format', 'size' or 'volume' (for scsi/iscsi pools)")

+                    disk_info['path'] = path
+                    disk_info['index'] = disk_info.get('index', index)
+                    self.info['disks'][index] = disk_info
+                else:
+                    raise InvalidParameter('KCHTMPL0034E')

     def _get_os_info(self, args, scan):
         distro = version = 'unknown'
@@ -217,8 +233,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 +243,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 +258,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 not is_s390x() and d['pool']['type'] in ["iscsi", "scsi"]:
                 continue
in case of s390x also if pool is present and type is iscsi or scsi we should continue.
so the check should be:- in case of arch is 390x if pool is not present or if pool of type iscsi or scsi just continue. Or can have for all arch also that if pool not present or if pool type is scsi or iscsi just continue.
@Aline: What do you think, pool not present just continue can be true for all arch. Or you think we should get key error in case of non s390x arch?

             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'])
+
I guess as this method is meant for libvirt volume listing, I guess we should restructure.
May be one method to deal with pool and vol, and another to deal with path, and use both while iterating through list of disks.
             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:
@@ -444,8 +469,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 not is_s390x():  # s390x specific handling
+                pool_uri = disk.get('pool', {}).get('name')
+                self._get_storage_pool(pool_uri)
Again in case of s390x also if pool is present, it should validate it.
         self._network_validate()
         self._iso_validate()
         self.cpuinfo_validate()
@@ -494,10 +520,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