# Copyright 2016 Huawei Technologies Co.,LTD.
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import re

from nova.i18n import _
from oslo_serialization import jsonutils

from nova.virt.fusioncomputeapi import constant
from nova.virt.fusioncomputeapi import exception as fc_exc
from nova.virt.fusioncomputeapi.fcinstance import FC_INSTANCE_MANAGER as FC_MGR
from nova.virt.fusioncomputeapi import ops_task_base
from nova.virt.fusioncomputeapi import utils
from nova.virt.fusioncomputeapi.utils import LOG


class VmCreateBase(ops_task_base.OpsTaskBase):
    """vm controller class"""

    def __init__(self, fc_client, task_ops, instance):
        super(VmCreateBase, self).__init__(fc_client, task_ops)
        self._instance = instance
        self._key_data = self._instance.get('key_data')
        self._metadata = self._instance.get('metadata')

        self._vm_create_body = {}
        self._volume_ops = None
        self._location = None
        self._vifs = []
        self._block_device_info = {}
        self._root_device_name = None
        self._image_meta = {}
        self._injected_files = []
        self._admin_password = None
        self._extra_specs = {}
        self._context = {}
        self._customization = {}
        self._is_support_virtual_io = False

    def __call__(
            self,
            context,
            volume_ops,
            location,
            vifs,
            block_device_info,
            image_meta,
            injected_files,
            admin_password,
            extra_specs,
            customization,
            resource_group_urn,
            compute_ops):
        self._volume_ops = volume_ops
        self._compute_ops = compute_ops
        self._location = location
        self._vifs = vifs
        self._block_device_info = block_device_info
        self._root_device_name = block_device_info.get('root_device_name')
        self._image_meta = image_meta
        self._injected_files = injected_files
        self._admin_password = admin_password
        self._extra_specs = extra_specs
        self._context = context
        self._customization = customization
        self._resource_group_urn = resource_group_urn

    @property
    def image_properties(self):
        """image mate properties

        :return:
        """
        if self._image_meta:
            return self._image_meta.properties
        else:
            return {}

    def check_input(self):
        """check function input params

        :return:
        """
        os_option = self.get_os_options()
        LOG.debug(_('os option: %s .'), jsonutils.dumps(os_option))
        if not (os_option['osType'] and os_option['osVersion']):
            LOG.error('Invalid os option for vm %s!', self._instance['name'])
            raise fc_exc.InvalidOsOption()

    def get_body_ext(self):
        """if body not enough, child class can extend

        :return:
        """
        raise NotImplementedError()

    def build_para(self):
        """build create body"""
        if constant.CONF.fusioncompute.instance_initial_mode == 'cloud_init':
            self._key_data = None
        self._vm_create_body = {
            'name': self._instance['display_name'],
            'description': self._instance['name'],
            'group': constant.VM_GROUP_FLAG,
            'uuid': self._instance['uuid'],
            'externalUuid': self._instance['uuid'],
            'location': self._location,
            'autoBoot': self.is_auto_boot(),
            'osOptions': self.get_os_options(),
            'vmConfig': self.get_vm_config(),
            'vmCustomization': self.get_vm_customization(),
            'publickey': self._key_data
        }
        self.get_body_ext()

    def extend_ops_before_start(self):
        """vm is created in stopped state, do something before start

        :return:
        """
        pass

    def create_and_boot_vm(self):
        """create vm interface func

        :return:
        """
        self.check_input()
        self.build_para()
        self.create_vm()

        # VM is created in stopped state in some cases,
        # do the extended ops in subclass and start it at last
        if not self.is_auto_boot():
            self.inject_files()

            # Other opeation when vm stoped
            self.extend_ops_before_start()
            self._compute_ops.start_vm(self._instance, self._block_device_info)

    def get_cpu_info(self):
        """get vm cpu info"""
        cpu_info = {'quantity': self._instance['vcpus']}
        cpu_qos = utils.fc_qos_convert(self._extra_specs,
                                       constant.CPU_QOS_NOVA_KEY,
                                       constant.CPU_QOS_FC_KEY,
                                       cpu_info.get('quantity'))
        cpu_info = utils.dict_add(cpu_info, cpu_qos)

        numa_nodes = self._extra_specs.get('hw:numa_nodes', None)
        if numa_nodes is not None:
            LOG.debug(_('numa_nodes %s'), numa_nodes)
            _core_per_socket = int(self._instance['vcpus']) / int(numa_nodes)
            cpu_info['coresPerSocket'] = _core_per_socket
            LOG.debug(_('_core_per_socket %d'), _core_per_socket)

        return cpu_info

    def get_memory_info(self):
        """get vm memory info"""
        return {
            'quantityMB': self._instance['memory_mb']
        }

    def get_disks_info(self):
        """get vm disk specific info"""
        raise NotImplementedError()

    def get_nic_info(self):
        """get vm nic info"""
        return [
            {
                'name': vif['network_info']['id'],
                'portId': vif['network_info']['id'],
                'mac': vif['network_info']['address'],
                'portGroupUrn': vif['pg_urn'],
                'sequenceNum': vif['sequence_num'],
                'virtIo': 1 if self._is_support_virtual_io else 0
            }
            for vif in self._vifs
        ]

    def get_fc_os_options(self, os_type, os_version):
        """get fc options

        :param os_type:
        :param os_version:
        :return:
        """
        if os_type is None:
            os_type = ''
        if os_version is None:
            os_version = ''

        fc_os_type = constant.HUAWEI_OS_TYPE_MAP.\
            get(os_type.lower(), constant.DEFAULT_HUAWEI_OS_TYPE)

        # 201=Other_Windows(32_bit),301=Other_Linux(32_bit),401=Other(32_bit)
        # using hard code for default os_version value.
        # if huawei-os-config.conf has been changed,
        # those code should be modified also.
        if fc_os_type == 'Windows':
            fc_os_version = \
                constant.HUAWEI_OS_VERSION_INT[fc_os_type].\
                get(os_version.lower(), 201)
        elif fc_os_type == 'Linux':
            fc_os_version = \
                constant.HUAWEI_OS_VERSION_INT[fc_os_type].\
                get(os_version.lower(), 301)
        else:
            fc_os_version = \
                constant.HUAWEI_OS_VERSION_INT[fc_os_type].\
                get(os_version.lower(), 401)

        if fc_os_version in constant.VIRTUAL_IO_OS_LIST:
            self._is_support_virtual_io = True

        return {
            'osType': fc_os_type,
            'osVersion': fc_os_version
        }

    def get_os_options(self):
        """get vm os info

        get os Type from mata
        :return:
        """
        os_type = "other"
        os_version = "other"
        return self.get_fc_os_options(os_type, os_version)

    def get_properties(self):
        """get vm property"""
        vm_properties = {
            'bootOption': utils.get_boot_option_from_metadata(
                self._metadata),
            'vmVncKeymapSetting': utils.get_vnc_key_map_setting_from_metadata(
                self._metadata)}
        hpet_support = self._extra_specs.get('extra_spec:bios:hpet')
        if hpet_support is not None:
            LOG.debug(_('hpet_support %s'), hpet_support)
            if str(hpet_support).lower() == 'enabled':
                vm_properties['isHpet'] = True
        secure_vm_type = self._extra_specs.get('secuirty:instance_type')
        if secure_vm_type and str(secure_vm_type).upper() == 'GVM':
            vm_properties['secureVmType'] = 'GVM'
        elif secure_vm_type and str(secure_vm_type).upper() == 'SVM':
            vm_properties['secureVmType'] = 'SVM'

        return vm_properties

    def get_gpu_info(self):
        gpu_info = []
        enable_gpu = self._extra_specs.get('pci_passthrough:enable_gpu')
        gpu_number = self._extra_specs.get('pci_passthrough:gpu_number')
        gpu_specs = self._extra_specs.get('pci_passthrough:gpu_specs')

        if enable_gpu and str(enable_gpu).upper() == 'TRUE':
            if gpu_specs:
                gpu_specs = gpu_specs.split(':')
                if gpu_specs is None or len(gpu_specs) != 3:
                    reason = 'Invalid flavor extra spec info: ' \
                             'gpu_specs is %s' % gpu_specs
                    LOG.error(reason)
                    raise fc_exc.InvalidFlavorExtraSpecInfo(reason=reason)
                else:
                    # gpu_alias = gpu_specs[0]  # reserve property
                    gpu_mode = gpu_specs[1]
                    gpu_number = gpu_specs[2]
                    for i in range(int(gpu_number)):
                        gpu_info.append({'gpuUrn': 'auto', 'mode': gpu_mode})
                    return True, gpu_info
            elif gpu_number and int(gpu_number) > 0:
                for i in range(int(gpu_number)):
                    gpu_info.append({'gpuUrn': 'auto'})
                return True, gpu_info
            else:
                reason = 'Invalid flavor extra spec info:gpu_number is %s,' \
                         ' gpu_specs is %s' % (gpu_number, gpu_specs)
                LOG.error(reason)
                raise fc_exc.InvalidFlavorExtraSpecInfo(reason=reason)
        return False, gpu_info

    def get_vm_config(self):
        """get vm config info"""
        config = {
            'cpu': self.get_cpu_info(),
            'memory': self.get_memory_info(),
            'disks': self.get_disks_info(),
            'nics': self.get_nic_info(),
            'properties': self.get_properties()
        }

        (ret, gpu_info) = self.get_gpu_info()
        if ret:
            config['gpu'] = gpu_info
            config['memory']['reservation'] = config['memory']['quantityMB']

        # reserve cdrom mount device for vm.
        # The value None represent not reserve,
        # default is None for Uxy
        # default is xvdd for private cloud
        if constant.CONF.fusioncompute.reserve_disk_symbol is not None and str(
                constant.CONF.fusioncompute.reserve_disk_symbol).\
                upper() == 'FALSE':
            config['cdromSequenceNum'] = constant.CONF.fusioncompute. \
                cdrom_sequence_num

        return config

    def _get_inject_ip_flag(self, port_id):
        """vnic_info:<port_uuid>":"enable_ip_inject:true|false"

        :param port_id:
        :return:
        """
        inject_ip_flag = False
        vnic_info = self._metadata.get("vnic_info:%s" % port_id)
        try:
            if isinstance(vnic_info, unicode):
                for t in vnic_info.strip().split(','):
                    if t.startswith('enable_ip_inject'):
                        flag_str = t.strip().split(':')[1]
                        flag_str = flag_str.lower()
                        inject_ip_flag = (flag_str == 'true')
        except Exception as e:
            LOG.error("network param error: %s", vnic_info)
            LOG.error("exception: %s", e)
        return inject_ip_flag

    def _get_vm_customization_nics(self):
        """get vm customization nics"""
        cus_nics = []
        for vif in self._vifs:
            if self._get_inject_ip_flag(vif['network_info']['id']) is False:
                cus_nic = {
                    'sequenceNum': vif['sequence_num'] + 1
                }
                cus_nics.append(cus_nic)
                continue

            network = vif['network_info']['network']
            subnet_ipv4_list = [s for s in network['subnets']
                                if s['version'] == constant.IPV4_VERSION]
            if len(subnet_ipv4_list) > 0:
                ip_ipv4 = None

                dns = [None, None]
                if len(subnet_ipv4_list[0]['ips']) > 0:
                    ip_ipv4 = subnet_ipv4_list[0]['ips'][0]

                dns_len = len(subnet_ipv4_list[0]['dns'])
                for index in range(0, min(2, dns_len)):
                    dns[index] = subnet_ipv4_list[0]['dns'][index]['address']

                netmask_ipv4 = str(subnet_ipv4_list[0].as_netaddr().netmask)
                gateway_ipv4 = subnet_ipv4_list[0]['gateway']['address']

                cus_nic = {'sequenceNum': vif['sequence_num'] + 1,
                           'ip': ip_ipv4 and ip_ipv4['address'] or '',
                           'gateway': gateway_ipv4,
                           'netmask': netmask_ipv4,
                           'ipVersion': constant.IPV4_VERSION,
                           'setdns': dns[0],
                           'adddns': dns[1]}
                cus_nics.append(cus_nic)

        LOG.debug(_('cus_nic: %s.'), jsonutils.dumps(cus_nics))
        return cus_nics

    def _validate_customization(self, customization):
        """_validate_customization

        :return:
        """

        valid_customizations = [
            'hostname',
            'workgroup',
            'domain',
            'domainName',
            'domainPassword',
            'ouName'
        ]

        for key in customization.keys():
            if key not in valid_customizations:
                msg = _("Invalid key: %s") % key
                raise fc_exc.InvalidCustomizationInfo(reason=msg)

    def get_vm_customization(self):
        """get vm custom info"""

        vm_custom_body = {}

        if constant.CONF.fusioncompute.instance_initial_mode == 'cloud_init':
            vm_custom_body['isUpdateVmPassword'] = False
            vm_custom_body['osType'] = self.get_os_options()['osType']
            return vm_custom_body

        inject_pwd_flag = self._metadata.get('__inject_pwd')
        if inject_pwd_flag is False or inject_pwd_flag == 'False':
            vm_custom_body['isUpdateVmPassword'] = False

        if self.get_os_options()['osType'] == 'Other':
            if len(vm_custom_body):
                return vm_custom_body
            return None

        vm_custom_body['osType'] = self.get_os_options()['osType']
        vm_custom_body['password'] = self._admin_password
        vm_custom_body['nicSpecification'] = self._get_vm_customization_nics()

        self._validate_customization(self._customization)
        for key in self._customization.keys():
            vm_custom_body[key] = self._customization[key]

        return vm_custom_body

    def is_auto_boot(self):
        """get auto boot"""
        if len(self._injected_files):
            return False
        else:
            return True

    def inject_files(self):
        """inject_files

        :return:
        """
        if constant.CONF.fusioncompute.fusioncompute_file_inject == 'disabled':
            LOG.debug(_('inject files use fusioncompute is disabled.'))
            return
        fc_vm = FC_MGR.get_vm_by_uuid(self._instance)
        for (path, contents) in self._injected_files:
            body = {
                'fileName': path,
                'vmData': contents
            }
            self.post(fc_vm.get_vm_action_uri('set_vm_data'), data=body)
            LOG.debug(_('inject file %s succeed.') % path)

    def create_vm(self):
        """create vm interface

        :return:
        """
        raise NotImplementedError()


class VmCreateByImport(VmCreateBase):
    """create vm use import vm interface

    """

    def get_protocol(self):
        """get nfs or null"""
        raise NotImplementedError()

    def create_vm(self):
        """create vm by import interface

        :return:
        """
        self.post(self.site.import_vm_uri, data=self._vm_create_body,
                  excp=fc_exc.FusionComputeReturnException, fixedInterval=1)

    def is_auto_boot(self):
        """get auto boot"""
        if len(self._injected_files):
            return False
        if self._compute_ops.get_local_disk_property(self._instance):
            return False
        else:
            return True

    def get_body_ext(self):
        """import vm extend params

        :return:
        """
        self._vm_create_body['protocol'] = self.get_protocol()
        if self._resource_group_urn:
            self._vm_create_body['resourceGroup'] = self._resource_group_urn
        if self._extra_specs:
            instance_vnic_type = self._extra_specs.get('instance_vnic:type')
            if instance_vnic_type and instance_vnic_type.lower() == 'enhanced':
                instance_vnic_bandwidth = self._extra_specs.get(
                    'instance_vnic:instance_bandwidth')
                instance_vnic_max_count = self._extra_specs.get(
                    'instance_vnic:max_count')
                if instance_vnic_bandwidth is not None \
                        and instance_vnic_max_count is not None:
                    self._vm_create_body['bandwidth'] = int(
                        instance_vnic_bandwidth)
                    self._vm_create_body['maxVnic'] = int(
                        instance_vnic_max_count)

            is_multi_disk_speedup = self._extra_specs.get(
                'extra_spec:io:persistent_grant')
            if is_multi_disk_speedup \
                    and is_multi_disk_speedup.lower() == 'true':
                self._vm_create_body[
                    'isMultiDiskSpeedup'] = is_multi_disk_speedup

    def extend_ops_before_start(self):
        """create with local disk, local disk should attach when vm stoped

        :return:
        """
        self._compute_ops.create_and_attach_local_disk_before_start(
            self._instance, self._block_device_info)


class VmCreateWithVolume(VmCreateByImport):
    """create vm with volume"""

    def get_protocol(self):
        """get null"""
        return "null"

    def get_disks_info(self):
        """override get vm disk specific info"""

        LOG.debug(_('prepare volume'))

        disks_info = []
        for disk in self._volume_ops.ensure_volume(self._block_device_info):
            disk_info = {
                'volumeUrn': disk['urn'],
                'isThin': constant.FC_DRIVER_JOINT_CFG['volume_is_thin']
            }

            if disk['mount_device'] == self._root_device_name:
                disk_info['sequenceNum'] = 1
            else:
                disk_info['sequenceNum'] = self._compute_ops.get_sequence_num(
                    disk['urn'], disk['mount_device'])

            disks_info.append(disk_info)
        return disks_info

    def get_os_options(self):
        """get vm os info"""
        if self._instance._task_state == 'rebuild_spawning':
            # os_type = self.image_properties.get(constant.HUAWEI_OS_TYPE)
            # os_version =
            # self.image_properties.get(constant.HUAWEI_OS_VERSION)
            # if os_type:
            #    return self.get_fc_os_options(os_type, os_version)
            # else:
            return super(VmCreateWithVolume, self).get_os_options()

        # get os Type from mata
        meta_data = self._volume_ops.\
            get_block_device_meta_data(self._context, self._block_device_info)
        if meta_data:
            volume_meta_data = meta_data.get('volume_image_metadata')
            if volume_meta_data:
                os_type = volume_meta_data.get(constant.HUAWEI_OS_TYPE)
                os_version = volume_meta_data.get(constant.HUAWEI_OS_VERSION)
                if os_type:
                    return self.get_fc_os_options(os_type, os_version)

        return super(VmCreateWithVolume, self).get_os_options()


class VmCreateWithImage(VmCreateByImport):
    """create vm with image"""

    def get_protocol(self):
        """default protocol is glance"""
        return "glance"

    def get_os_options(self):
        """get vm os info"""

        if self.image_properties.obj_attr_is_set(constant.HUAWEI_OS_TYPE):
            os_type = self.image_properties.get(constant.HUAWEI_OS_TYPE)
        else:
            os_type = "other"

        if self.image_properties.obj_attr_is_set(constant.HUAWEI_OS_VERSION):
            os_version = self.image_properties.get(constant.HUAWEI_OS_VERSION)
        else:
            os_version = "other"

        if os_type:
            return self.get_fc_os_options(os_type, os_version)
        else:
            return super(VmCreateWithImage, self).get_os_options()

    def _get_image_size(self):
        """get image size info"""
        image_size = self._image_meta.size
        if image_size:
            return utils.image_size_to_gb(image_size)
        else:
            return 0

    def check_input(self):
        """create vm image detail check

        :return:
        """
        super(VmCreateWithImage, self).check_input()

        disk_quantity_gb = self._instance['root_gb']
        image_size = self._get_image_size()
        if image_size > disk_quantity_gb:
            LOG.error(_("image is larger than sys-vol."))
            raise fc_exc.ImageTooLarge

    def get_disks_info(self):
        """get image disk detail info"""

        LOG.debug(_('prepare volume'))

        disks_info = []

        # sys vol info
        sys_disk_info = {
            'sequenceNum': 1,
            'quantityGB': self._instance['root_gb'],
            'isThin': constant.FC_DRIVER_JOINT_CFG['volume_is_thin']
        }
        disks_info.append(sys_disk_info)

        # user vol info
        for disk in self._volume_ops.ensure_volume(self._block_device_info):
            user_disk_info = {
                'volumeUrn': disk['urn'],
                'sequenceNum': self._compute_ops.get_sequence_num(
                    disk['urn'],
                    disk['mount_device']),
                'isThin': constant.FC_DRIVER_JOINT_CFG['volume_is_thin']}
            disks_info.append(user_disk_info)

        return disks_info


class VmCreateWithNfsImage(VmCreateWithImage):
    """create vm with nfs image"""

    def get_protocol(self):
        """get nfs protocol"""
        return "nfs"

    def _get_template_url(self):
        """get nfs location"""
        return self.image_properties[constant.HUAWEI_IMAGE_LOCATION]

    def get_body_ext(self):
        """create vm with image, extend url info

        :return:
        """
        super(VmCreateWithNfsImage, self).get_body_ext()
        self._vm_create_body['url'] = self._get_template_url()


class VmCreateWithUdsImage(VmCreateWithImage):
    """create vm with uds image"""

    """create vm use import vm interface"""

    def __init__(self, fc_client, task_ops, instance):
        super(
            VmCreateWithUdsImage,
            self).__init__(
            fc_client,
            task_ops,
            instance)
        self.usd_image_server_ip = None
        self.usd_image_port = None
        self.usd_image_bucket_name = None
        self.usd_image_key = None

    def _get_uds_image_info(self, image_location):
        """_get_uds_image_info

        :param image_location: {ip}:{port}:{buket name}:{key}
        192.168.0.1:5443:region1.glance:001
        """

        if image_location:
            uds_image_info = image_location.strip()
            str_array = re.split(":", uds_image_info)
            if len(str_array) == 4:
                return str_array[0], \
                    str_array[1], \
                    str_array[2], \
                    str_array[3]
        reason = _("Invalid uds image info,invalid image_location!")
        raise fc_exc.InvalidUdsImageInfo(reason=reason)

    def check_input(self):
        super(VmCreateWithUdsImage, self).check_input()

        properties = self._image_meta.properties
        if properties:
            try:
                self.usd_image_server_ip,  \
                    self.usd_image_port, \
                    self.usd_image_bucket_name, \
                    self.usd_image_key = \
                    self._get_uds_image_info(
                        properties.get(constant.HUAWEI_IMAGE_LOCATION))
            except Exception:
                reason = _("Invalid uds image info,invalid loaction!")
                raise fc_exc.InvalidUdsImageInfo(reason=reason)

        if constant.CONF.fusioncompute.uds_access_key is '' \
                or constant.CONF.fusioncompute.uds_secret_key is '':
            reason = _("Invalid uds image info,invalid AK SK!")
            raise fc_exc.InvalidUdsImageInfo(reason=reason)

    def get_protocol(self):
        """get uds protocol"""
        return "uds"

    def get_body_ext(self):
        """get_body_ext

        create vm with image, extend uds info
        :return:
        """
        super(VmCreateWithUdsImage, self).get_body_ext()
        self._vm_create_body['s3Config'] = {
            'serverIp': self.usd_image_server_ip,
            'port': self.usd_image_port,
            'accessKey': constant.CONF.fusioncompute.uds_access_key,
            'secretKey': constant.CONF.fusioncompute.uds_secret_key,
            'bucketName': self.usd_image_bucket_name,
            'key': self.usd_image_key
        }


class VmCreateWithGlanceImage(VmCreateWithImage):
    """create vm with glance image"""

    def check_input(self):
        super(VmCreateWithGlanceImage, self).check_input()

        if constant.CONF.fusioncompute.glance_server_ip is '':
            reason = _("Invalid glance image info,invalid server ip!")
            raise fc_exc.InvalidGlanceImageInfo(reason=reason)

    def get_body_ext(self):
        """get_body_ext

        create vm with image, extend glance info
        :return:
        """
        super(VmCreateWithGlanceImage, self).get_body_ext()
        self._vm_create_body['glanceConfig'] = {
            'endPoint': ':'.join([str(constant.CONF.fusioncompute.host),
                                  str(constant.CONF.fusioncompute.port)]),
            'serverIp': constant.CONF.fusioncompute.glance_server_ip,
            'token': self._context.auth_token,
            'imageID': self._image_meta.id
        }


class VmCreateByClone(VmCreateBase):
    """create vm use import vm interface

    """

    def __init__(self, fc_client, task_ops, instance):
        super(VmCreateByClone, self).__init__(fc_client, task_ops, instance)
        self._need_attach_user_vols = False
        self._cloned_source_vm_or_tpl = None

    def is_auto_boot(self):
        """is_auto_boot

        :return:
        """
        if len(self._block_device_info.get('block_device_mapping')):
            self._need_attach_user_vols = True
            return False
        else:
            return super(VmCreateByClone, self).is_auto_boot()

    def get_os_options(self):
        """get vm os info"""

        # get os Type from mata
        # os_type = self.image_properties.get(constant.HUAWEI_OS_TYPE)
        # os_version = self.image_properties.get(constant.HUAWEI_OS_VERSION)
        # if os_type:
        #    return self.get_fc_os_options(os_type, os_version)
        # else:
        return super(VmCreateByClone, self).get_os_options()

    def get_disks_info(self):
        """get_disks_info

        FC itself will clone disks belonging to this tpl/vm(it should and
        must has only one sys volume).
        """
        LOG.debug(_('prepare volume'))
        disks_info = []
        disk_sequence = 1

        # sys vol info
        sys_disk_info = {
            'sequenceNum': disk_sequence,
            'quantityGB': self._instance['root_gb'],
            'isThin': constant.FC_DRIVER_JOINT_CFG['volume_is_thin']
        }
        disks_info.append(sys_disk_info)

        return disks_info

    def get_body_ext(self):
        """if body not enough, child class can extend

        :return:
        """
        if "uuid" in self._vm_create_body:
            self._vm_create_body.pop("uuid")
        self._vm_create_body["clonedVmUUID"] = self._instance['uuid']

    def extend_ops_before_start(self):
        """create by clone, user vol should attach when vm stoped

        :return:
        """
        if self._need_attach_user_vols:
            self._attach_user_vols()

    def _attach_user_vols(self):
        """_attach_user_vols

        :return:
        """
        fc_vm = FC_MGR.get_vm_by_uuid(self._instance)
        for disk in self._volume_ops.ensure_volume(self._block_device_info):
            body = {
                'volUrn': disk['urn'],
                'sequenceNum': self._compute_ops.get_sequence_num(
                    disk['urn'],
                    disk['mount_device'])}
            LOG.debug(_("begin attach user vol: %s"), disk['urn'])
            self._volume_ops.attach_volume(fc_vm, vol_config=body)

    def create_vm(self):
        self.post(self._cloned_source_vm_or_tpl.get_vm_action_uri('clone'),
                  data=self._vm_create_body,
                  excp=fc_exc.InstanceCloneFailure)


class VmCreateWithTemplate(VmCreateByClone):
    """create vm with image"""

    def check_input(self):
        super(VmCreateWithTemplate, self).check_input()

        properties = self._image_meta.properties
        if properties:
            try:
                self._cloned_source_vm_or_tpl = \
                    self._get_vm_by_template_url(
                        properties.get(constant.HUAWEI_IMAGE_LOCATION))
                self._validate_template(self._cloned_source_vm_or_tpl)
            except Exception:
                LOG.error(_("Invalid FusionCompute template !"))
                raise fc_exc.InstanceCloneFailure

    def get_body_ext(self):
        """if body not enough, child class can extend

        :return:
        """
        super(VmCreateWithTemplate, self).get_body_ext()
        self._vm_create_body['isTemplate'] = False

        is_link_clone = self._metadata.get(constant.HUAWEI_IS_LINK_CLONE)
        if is_link_clone:
            self._vm_create_body['isLinkClone'] = is_link_clone

    def _get_vm_by_template_url(self, template_url):
        """_get_vm_by_template_url

        :param template_url: {vrm site id}:{vm id}
        239d8a8e:i-00000061
        """

        vm_id = None
        if template_url:
            url = template_url.strip()
            str_array = re.split(":", url)
            if len(str_array) == 2:
                vm_id = str_array[1]

        if vm_id is not None:
            return FC_MGR.get_vm_by_id(vm_id)
        return None

    def _validate_template(self, instance):
        """_validate_template

        :param instance: fc vm
        :return:
        """
        if instance is not None and instance.isTemplate is not True:
            raise fc_exc.InstanceCloneFailure

        for disk in instance['vmConfig']['disks']:
            if disk['sequenceNum'] not in [0, 1]:
                raise fc_exc.InstanceCloneFailure


def get_vm_create(fc_client, task_ops, instance, image_meta=None):
    """get create vm object"""
    vm_class = VmCreateWithGlanceImage

    return vm_class(fc_client, task_ops, instance)
