# Copyright (c) 2014 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.

"""
 Volume api for Huawei SDSHypervisor systems.
"""

import uuid


from oslo.utils import units
from oslo_config import cfg
import six

from cinder import context
from cinder import exception
from cinder.i18n import _, _LE
from cinder.openstack.common import log as logging
from cinder.openstack.common import loopingcall
from cinder import utils
from cinder.volume import driver
from cinder.volume.drivers.huaweistorhyper import utils as storhyper_utils
from cinder.volume.drivers.huaweistorhyper import vbs_client
from cinder.volume import volume_types

CONF = cfg.CONF
LOG = logging.getLogger(__name__)

QOS_KEY = ["Qos-high", "Qos-normal", "Qos-low"]

LINKED_CLONE_TYPE = 'linked'
FULL_CLONE_TYPE = 'full'

CHECK_VOLUME_DATA_FINISHED_INTERVAL = 10
CHECK_VOLUME_DELETE_FINISHED_INTERVAL = 2
CHECK_SNAPSHOT_DELETE_FINISHED_INTERVAL = 2

huawei_storhyper_opts = [
    cfg.StrOpt('cinder_huawei_sds_conf_file',
               default='/etc/cinder/cinder_huawei_storac_conf.xml',
               help='huawei storagehyper driver config file path'),
]

CONF.register_opts(huawei_storhyper_opts)


class StorACDriver(driver.VolumeDriver):

    VERSION = '1.0.0'
    del_complete_code = '-900079'

    def __init__(self, *args, **kwargs):
        super(StorACDriver, self).__init__(*args, **kwargs)
        self.configuration.append_config_values(huawei_storhyper_opts)
        self._conf_file = self.configuration.cinder_huawei_sds_conf_file
        LOG.debug('Conf_file is: ' + self._conf_file)
        self._vbs_client = vbs_client.VbsClient(self._conf_file)
        self._volume_stats = self._get_default_volume_stats()

    def check_for_setup_error(self):
        pass

    def initialize_connection(self, volume, connector):
        LOG.debug('Initialize connection.')
        properties = {}
        properties['volume_id'] = volume['name']
        return {'driver_volume_type': 'HUAWEISDSHYPERVISOR',
                'data': properties}

    def terminate_connection(self, volume, connector, **kwargs):
        """Terminate the map."""
        pass

    def create_volume(self, volume):
        """Create a new volume."""
        volume_name = volume['name']
        LOG.debug('Create volume, volume name: %s.' % volume_name)
        volume_size = self._size_translate(volume['size'])

        volume_info = self._create_storage_info('volume_info')
        volume_info['vol_name'] = volume_name
        volume_info['vol_size'] = volume_size
        volume_info['pool_id'] = self._get_volume_pool_id(volume['host'])
        self._update_volume_info_from_volume(volume_info, volume)
        self._send_request('CREATE_VOLUME_REQ',
                           volume_info,
                           'create volume error.')
        return {'provider_location': volume['name']}

    def create_volume_from_snapshot(self, volume, snapshot):
        """Create a volume from a snapshot."""
        tgt_vol_name = volume['name']
        src_snapshot_name = snapshot['name']
        LOG.debug('Create volume from snapshot: '
                  'tgt_vol_name: %(tgt_vol_name)s, '
                  'src_snapshot_name: %(src_snapshot_name)s, '
                  'vol_size: %(vol_size)s.'
                  % {'tgt_vol_name': tgt_vol_name,
                     'src_snapshot_name': src_snapshot_name,
                     'vol_size': volume['size']})
        self._create_linked_volume_from_snap(src_snapshot_name,
                                             tgt_vol_name,
                                             volume['size'])

        return {'provider_location': volume['name']}

    def create_cloned_volume(self, tgt_volume, src_volume):
        """Create a clone volume."""
        src_vol_name = src_volume['name']
        tgt_vol_name = tgt_volume['name']
        LOG.debug('Create cloned volume: src volume: %(src)s, '
                  'tgt volume: %(tgt)s.' % {'src': src_vol_name,
                                            'tgt': tgt_vol_name})

        src_vol_id = src_volume.get('provider_location')
        if not src_vol_id:
            err_msg = (_LE('Source volume %(name)s does not exist.')
                       % {'name': src_vol_name})
            LOG.error(err_msg)
            raise exception.VolumeNotFound(volume_id=src_vol_name)

        volume_info = self._create_target_volume(src_volume,
                                                 tgt_vol_name,
                                                 tgt_volume)
        tgt_vol_id = volume_info['vol_name']

        self.copy_volume_data(context.get_admin_context(), src_volume,
                              tgt_volume, remote=None)

        return {'provider_location': tgt_vol_id}

    def delete_volume(self, volume):
        """Delete a volume."""
        req_paras = {}
        req_paras['vol_name'] = volume['name']
        self._send_request('DELETE_VOLUME_REQ',
                           req_paras,
                           'Delete volume failed.')
        self._wait_for_volume_delete(volume['name'])

    def extend_volume(self, volume, new_size):
        """Extend the size of an existing volume."""
        LOG.debug('Extend volume: %s.' % volume['name'])
        volume_name = volume['name']
        new_volume_size = self._size_translate(new_size)
        volume_info = {"vol_name": volume_name,
                       "vol_size": new_volume_size}

        self._send_request('EXTEND_VOLUME_REQ',
                           volume_info,
                           'extend volume failed.')

    def get_volume_stats(self, refresh=False):
        """Get volume stats."""
        if refresh:
            try:
                self._get_volume_stats()
            except Exception as ex:
                self._volume_stats = self._get_default_volume_stats()
                msg = (_LE('Error from get volume stats: '
                           '%s, using default stats.') % ex)
                LOG.error(msg)
        return self._volume_stats

    def create_snapshot(self, snapshot):
        create_snapshot_req = {}
        create_snapshot_req['snap_name'] = snapshot['name']
        create_snapshot_req['vol_name'] = snapshot['volume_name']
        create_snapshot_req['smartflag'] = '1'

        self._send_request('CREATE_SNAPSHOT_REQ',
                           create_snapshot_req,
                           'create snapshot failed.')

        return {'provider_location': snapshot['name']}

    def delete_snapshot(self, snapshot):
        """Delete a snapshot."""
        """Delete SDS snapshot,ensure source volume is attached """
        source_volume_id = snapshot['volume_id']
        if not source_volume_id:
            self._delete_snapshot(snapshot)
            return

        is_volume_attached = self._is_volume_attached(source_volume_id)
        if is_volume_attached:
            LOG.debug('Volume is attached')
            self._delete_snapshot(snapshot)
        else:
            LOG.debug('Volume is not attached')
            source_volume = {'name': 'volume-' + source_volume_id,
                             'id': source_volume_id}
            properties = utils.brick_get_connector_properties()
            source_volume_attach_info = self._attach_volume(
                None, source_volume, properties, False)
            try:
                self._delete_snapshot(snapshot)
            except Exception as ex:
                err_msg = (_LE('Delete snapshot failed: '
                           '%s.') % ex)
                LOG.error(err_msg)
            self._detach_volume(
                None, source_volume_attach_info, source_volume,
                properties, False, False)

    def create_export(self, context, volume):
        """Export the volume."""
        pass

    def ensure_export(self, context, volume):
        """Synchronously recreate an export for a volume."""
        pass

    def remove_export(self, context, volume):
        """Remove an export for a volume."""
        pass

    def copy_volume_to_image(self, context, volume, image_service, image_meta):
        err_msg = ''
        temp_snapshot, temp_volume = self._create_temp_snap_and_volume(volume)
        try:
            self.create_snapshot(temp_snapshot)
            self._create_linked_volume_from_snap(temp_snapshot['name'],
                                                 temp_volume['name'],
                                                 temp_volume['size'])
            temp_volume['status'] = volume['status']
            super(StorACDriver, self).copy_volume_to_image(context,
                                                           temp_volume,
                                                           image_service,
                                                           image_meta)
        except Exception as ex:
            err_msg = (_LE('Copy volume to image failed: %s.') % ex)
            LOG.error(err_msg)
            raise exception.VolumeBackendAPIException(data=err_msg)
        finally:
            self._clean_copy_volume_data(temp_volume,
                                         temp_snapshot,
                                         'copy_volume_to_image')

    def copy_volume_data(self, context, src_vol, dest_vol, remote=None):
        err_msg = ''
        temp_snapshot, temp_volume = self._create_temp_snap_and_volume(src_vol)
        try:
            self.create_snapshot(temp_snapshot)
            self._create_linked_volume_from_snap(temp_snapshot['name'],
                                                 temp_volume['name'],
                                                 temp_volume['size'])
            temp_volume['status'] = src_vol['status']
            super(StorACDriver, self).copy_volume_data(context,
                                                       temp_volume,
                                                       dest_vol,
                                                       remote)
        except Exception as ex:
            err_msg = (_LE('Copy volume data failed: %s.') % ex)
            LOG.error(err_msg)
            raise exception.VolumeBackendAPIException(data=err_msg)
        finally:
            self._clean_copy_volume_data(temp_volume,
                                         temp_snapshot,
                                         'copy_volume_data')

    def _create_temp_snap_and_volume(self, src_vol):
        temp_snapshot = {'name': 'snapshot-' + six.text_type(uuid.uuid1()),
                         'volume_name': src_vol['name'],
                         'smartflag': '1',
                         'volume_id': src_vol['id']}
        temp_volume_id = six.text_type(uuid.uuid1())
        temp_volume = {'id': temp_volume_id,
                       'name': 'volume-' + temp_volume_id,
                       'size': src_vol['size']}
        return temp_snapshot, temp_volume

    def _clean_copy_volume_data(self, temp_volume, temp_snapshot, method):
        try:
            self.delete_volume(temp_volume)
        except Exception as ex:
            err_msg = (_LE('Delete temp volume failed '
                       'after %(method)s: %(ex)s.')
                       % {'ex': ex, 'method': method})
            LOG.error(err_msg)
        try:
            self.delete_snapshot(temp_snapshot)
        except Exception as ex:
            err_msg = (_LE('Delete temp snapshot failed '
                       'after %(method)s: %(ex)s.')
                       % {'ex': ex, 'method': method})
            LOG.error(err_msg)

    def _is_volume_attached(self, volume_id):
        if not volume_id:
            return False
        conn = {'driver_volume_type': 'HUAWEISDSHYPERVISOR',
                'data': {'volume_id': 'volume-' + volume_id}}
        use_multipath = self.configuration.use_multipath_for_image_xfer
        device_scan_attempts = self.configuration.num_volume_device_scan_tries
        protocol = conn['driver_volume_type']
        connector = utils.brick_get_connector(protocol,
                                              use_multipath=use_multipath,
                                              device_scan_attempts=
                                              device_scan_attempts,
                                              conn=conn)
        is_volume_attached = connector.is_volume_connected(
            conn['data']['volume_id'])
        return is_volume_attached

    def _create_target_volume(self, src_volume, tgt_vol_name, tgt_volume):
        if int(tgt_volume['size']) == 0:
            tgt_vol_size = self._size_translate(src_volume['size'])
        else:
            tgt_vol_size = self._size_translate(tgt_volume['size'])

        volume_info = self._create_storage_info('volume_info')
        volume_info['vol_name'] = tgt_vol_name
        volume_info['vol_size'] = tgt_vol_size
        volume_info['pool_id'] = self._get_volume_pool_id(tgt_volume['host'])

        self._update_volume_info_from_volume_type(volume_info,
                                                  tgt_volume['volume_type_id'])
        self._send_request('CREATE_VOLUME_REQ',
                           volume_info,
                           'create volume failed.')
        return volume_info

    def _create_linked_volume_from_snap(self, src_snapshot_name,
                                        tgt_vol_name, volume_size):
        vol_size = self._size_translate(volume_size)
        req_paras = {'vol_name': tgt_vol_name,
                     'vol_size': vol_size,
                     'snap_name_src': src_snapshot_name,
                     'vol_num': '1'}

        self._send_request('CREATE_VOLUME_FROM_SNAPSHOT_REQ',
                           req_paras,
                           'Create volume from snapshot failed.')

    def _get_volume_stats(self):
        """Retrieve stats info from volume group."""
        capacity = self._get_capacity()
        self._volume_stats['pools'] = capacity

        if len(capacity) == 1:
            for key, value in capacity[0].items():
                self._volume_stats[key] = value

    def _get_all_pool_capacity(self):
        pool_info = {}
        poolnum = len(self._volume_stats['pools_id'])
        pool_info['pool_num'] = six.text_type(poolnum)
        pool_info['pool_id'] = self._volume_stats['pools_id']
        result = self._send_request('QUERY_POOLS_CAPABILITY_REQ',
                                    pool_info,
                                    'Get storage capacity failed')
        return self._extract_pool_capacity_mapping_from_result(result)

    def _get_capacity(self):
        storage_capacity = []
        try:
            all_pool_policy = self._extract_pool_policy_mapping_from_config(
                self._conf_file)
            all_pool_capacity = self._get_all_pool_capacity()
            self._update_all_pool_capacity_from_policy(all_pool_capacity,
                                                       all_pool_policy)
            storage_capacity = all_pool_capacity.values()
        except exception.VolumeBackendAPIException as ex:
            msg = (_LE('Error from get block storage capacity: '
                   '%s.') % ex)
            LOG.error(msg)
            raise exception.VolumeBackendAPIException(msg)
        return storage_capacity

    def _delete_snapshot(self, snapshot):
        req_paras = {}
        req_paras['snap_name'] = snapshot['name']
        self._send_request('DELETE_SNAPSHOT_REQ',
                           req_paras,
                           'Delete snapshot error.')
        self._wait_for_snapshot_delete(snapshot['name'])

    def _create_default_volume_stats(self):
        default_volume_stats = {'tolerance_disk_failure': ['1', '2', '3'],
                                'tolerance_cache_failure': ['0', '1'],
                                'free_capacity_gb': 0,
                                'total_capacity_gb': 0,
                                'reserved_percentage': 0,
                                'vendor_name': 'Huawei',
                                'driver_version': self.VERSION,
                                'storage_protocol': 'StorageHypervisor',
                                'pools_id': []}
        backend_name = self.configuration.safe_get('volume_backend_name')
        default_volume_stats['volume_backend_name'] = (
            backend_name or self.__class__.__name__)
        return default_volume_stats

    def _get_default_volume_stats(self):
        default_volume_stats = self._create_default_volume_stats()
        self._update_default_volume_stats_from_config(default_volume_stats,
                                                      self._conf_file)
        return default_volume_stats

    def _wait_for_volume_delete(self, volume_name):
        """Wait for volume delete to complete."""
        timer = loopingcall.FixedIntervalLoopingCall(
            self._check_volume_delete_finished, volume_name)
        LOG.debug('Calling _wait_for_volume_delete: volume_name %s.'
                  % volume_name)
        ret = timer.start(
            interval=CHECK_VOLUME_DELETE_FINISHED_INTERVAL).wait()
        timer.stop()
        if not ret:
            msg = (_LE('Delete volume failed,volume_name: %s.')
                   % volume_name)
            LOG.error(msg)
            raise exception.VolumeBackendAPIException(message=msg)

        LOG.debug('Finish _wait_for_volume_delete: volume_name %s.'
                  % volume_name)

    def _wait_for_snapshot_delete(self, snapshot_name):
        """Wait for snapshot delete to complete."""
        timer = loopingcall.FixedIntervalLoopingCall(
            self._check_snapshot_delete_finished, snapshot_name)
        LOG.debug('Calling _wait_for_snapshot_delete: snapshot_name %s.'
                  % snapshot_name)
        ret = timer.start(
            interval=CHECK_SNAPSHOT_DELETE_FINISHED_INTERVAL).wait()
        timer.stop()
        if not ret:
            msg = (_LE('Delete snapshot failed,snapshot_name: %s.')
                   % snapshot_name)
            LOG.error(msg)
            raise exception.VolumeBackendAPIException(message=msg)

        LOG.debug('Finish _wait_for_snapshot_delete: snapshot_name %s.'
                  % snapshot_name)

    def _check_volume_delete_finished(self, volume_name):
        try:
            is_volume_exist = self._is_volume_exist(volume_name)
        except Exception as ex:
            msg = (_LE('Check volume_name delete finished failed: '
                   '%s.') % ex)
            LOG.error(msg)
            raise loopingcall.LoopingCallDone(retvalue=False)
        if not is_volume_exist:
            raise loopingcall.LoopingCallDone(retvalue=True)

    def _check_snapshot_delete_finished(self, snapshot_name):
        try:
            is_snapshot_exist = self._is_snapshot_exist(snapshot_name)
        except Exception as ex:
            msg = (_LE('Check snapshot delete finished failed: '
                   '%s.') % ex)
            LOG.error(msg)
            raise loopingcall.LoopingCallDone(retvalue=False)
        if not is_snapshot_exist:
            raise loopingcall.LoopingCallDone(retvalue=True)

    def _query_volume(self, volume_name):
        request_info = {'vol_name': volume_name}
        request_type = 'QUERY_VOLUME_REQ'
        rsp_str = self._vbs_client.send_message(
            storhyper_utils.serialize(request_type,
                                      request_info)
        )
        LOG.debug('%s received:%s.' % (request_type, repr(rsp_str)))
        result = storhyper_utils.deserialize(six.text_type(rsp_str),
                                             delimiter='\n')
        storhyper_utils.log_dict(result)
        return result

    def _is_volume_exist(self, volume_name):
        query_volume_result = self._query_volume(volume_name)
        if ((not query_volume_result) or
                ('retcode' not in query_volume_result) or
                (query_volume_result['retcode']
                 not in ('0', self.del_complete_code))):
            msg = _('%(err)s\n') % {'err': 'Query volume failed!'
                                           ' Invalid result code'}
            raise exception.VolumeBackendAPIException(data=msg)
        if query_volume_result['retcode'] == self.del_complete_code:
            return False
        if query_volume_result['retcode'] == '0':
            if 'volume0' not in query_volume_result:
                msg = _('%(err)s\n') % {'err': 'Query volume failed! '
                                               'Volume0 not exist!'}
                raise exception.VolumeBackendAPIException(data=msg)
            query_volume_result['volume0'] = \
                storhyper_utils.generate_dict_from_result(
                    query_volume_result['volume0'])
            if (('status' not in query_volume_result['volume0']) or
                    (query_volume_result['volume0']['status'] not in
                        ('1', '2', '10'))):
                msg = _('%(err)s\n') % {'err': 'Query volume failed!'
                                               ' Invalid volume status'}
                raise exception.VolumeBackendAPIException(data=msg)
            return True

    def _query_snapshot(self, snapshot_name):
        request_info = {'snap_name': snapshot_name}
        request_type = 'QUERY_SNAPSHOT_REQ'
        rsp_str = self._vbs_client.send_message(
            storhyper_utils.serialize(request_type,
                                      request_info)
        )
        LOG.debug('%s received:%s.' % (request_type, repr(rsp_str)))
        result = storhyper_utils.deserialize(six.text_type(rsp_str),
                                             delimiter='\n')
        storhyper_utils.log_dict(result)
        return result

    def _is_snapshot_exist(self, snapshot_name):
        query_snapshot_result = self._query_snapshot(snapshot_name)
        if ((not query_snapshot_result) or
                ('retcode' not in query_snapshot_result) or
                (query_snapshot_result['retcode']
                 not in ('0', self.del_complete_code))):
            msg = _('%(err)s\n') % {'err': 'Query snapshot failed!'}
            raise exception.VolumeBackendAPIException(data=msg)
        if query_snapshot_result['retcode'] == self.del_complete_code:
            return False
        if query_snapshot_result['retcode'] == '0':
            if 'snapshot0' not in query_snapshot_result:
                msg = _('%(err)s\n') % {'err': 'Query snapshot failed!'}
                raise exception.VolumeBackendAPIException(data=msg)
            query_snapshot_result['snapshot0'] =\
                storhyper_utils.generate_dict_from_result(
                    query_snapshot_result['snapshot0'])
            if (('status' not in query_snapshot_result['snapshot0']) or
                    (query_snapshot_result['snapshot0']['status'] not in
                        ('1', '2'))):
                msg = _('%(err)s\n') % {'err': 'Query snapshot failed!'}
                raise exception.VolumeBackendAPIException(data=msg)
            return True

    def _get_volume_pool_id(self, volume_host):
        if volume_host:
            if len(volume_host.split('#', 1)) == 2:
                return volume_host.split('#')[1]

        if len(self._volume_stats['pools_id']) == 1:
            return self._volume_stats['pools_id'][0]
        else:
            msg = (_LE("Get pool id failed, invalid pool id."))
            LOG.error(msg)
            raise exception.VolumeBackendAPIException(data=msg)

    def _send_request(self, request_type, request_info, error_message):
        rsp_str = self._vbs_client.send_message(
            storhyper_utils.serialize(request_type, request_info))
        LOG.debug('%s received:%s.' % (request_type, repr(rsp_str)))
        result = storhyper_utils.deserialize(six.text_type(rsp_str),
                                             delimiter='\n')
        storhyper_utils.log_dict(result)
        if (len(result) < 0 or 'retcode' not in result
                or result['retcode'] != '0'):
            msg = _('%(err)s\n') % {'err': error_message}
            raise exception.VolumeBackendAPIException(data=msg)
        return result

    def _update_default_volume_stats_from_config(self,
                                                 default_volume_stats,
                                                 config_file):
            root = storhyper_utils.parse_xml_file(config_file)
            for child in root.find('policy').findall('*'):
                if child.tag == 'QoS_support':
                    if child.text.strip() == '0':
                        default_volume_stats[child.tag] = False
                    else:
                        default_volume_stats[child.tag] = True
                else:
                    default_volume_stats[child.tag] = child.text.strip()
            for child in root.find('capability').findall('*'):
                default_volume_stats[child.tag] = child.text.strip()
            pools = root.find('pools').findall('*')
            for pool in pools:
                for child in pool.findall('*'):
                    childtext = child.text.strip()
                    if child.tag == 'pool_id' and len(childtext) > 0:
                        default_volume_stats['pools_id'].append(childtext)

    def _update_all_pool_capacity_from_policy(self,
                                              all_pool_capacity,
                                              all_pool_policy):
        for pool_name in all_pool_capacity.keys():
            if pool_name in all_pool_policy:
                for pool_key, pool_value in all_pool_policy[pool_name].items():
                    all_pool_capacity[pool_name][pool_key] = pool_value

    def _extract_pool_policy_mapping_from_config(self, conf_file):
        pools_policy_mapping = {}
        root = storhyper_utils.parse_xml_file(conf_file)
        pools = root.find('pools').findall('*')
        for pool in pools:
            policy = {}
            pool_id = ''
            for child in pool.findall('*'):
                if child.tag == 'pool_id':
                    pool_id = child.text.strip()
                else:
                    policy[child.tag] = child.text.strip()
            pools_policy_mapping[pool_id] = policy
        return pools_policy_mapping

    def _extract_pool_capacity_mapping_from_result(self, result):
        pool_capacity_mapping = {}
        for key, value in result.items():
            if 'pool' in key and value:
                pool_capacity = {}
                pool_name = ''
                pool_str = value.replace('[', '').replace(']', '')
                paras = pool_str.split(',')
                for para in paras:
                    key = para.split('=')[0]
                    value = para.split('=')[1]
                    if key == 'stor_id':
                        pool_capacity['pool_name'] = six.text_type(value)
                        pool_name = six.text_type(value)
                    elif key == 'total_capacity':
                        pool_capacity['total_capacity_gb'] = int(value)
                    elif key == 'usable_capacity':
                        pool_capacity['free_capacity_gb'] = int(value)
                    elif key == 'raid_level':
                        pool_capacity['raid_level'] = int(value)
                    elif key == 'iops':
                        pool_capacity['iops'] = int(value)

                pool_capacity['allocated_capacity_gb'] = \
                    pool_capacity['total_capacity_gb'] \
                    - pool_capacity['free_capacity_gb']
                pool_capacity['reserved_percentage'] = 0
                pool_capacity_mapping[pool_name] = pool_capacity

        return pool_capacity_mapping

    def _size_translate(self, size):
            volume_size = '%s' % (size * units.Ki)
            return volume_size

    def _update_volume_info_from_volume_extra_specs(self, volume_info,
                                                    extra_specs):
        if not extra_specs:
            return

        for x in extra_specs:
            key = x['key']
            value = x['value']
            LOG.debug('Volume type: key=%(key)s  value=%(value)s.'
                      % {'key': key, 'value': value})
            if key in volume_info.keys():
                words = value.strip().split()
                volume_info[key] = words.pop()

    def _update_volume_info_from_volume(self, volume_info, volume):
        if not volume['volume_type_id']:
            return
        else:
            spec = volume['volume_type']['extra_specs']
            self._update_volume_info_from_volume_extra_specs(volume_info,
                                                             spec)
            self._update_volume_info_from_qos_specs(volume_info,
                                                    volume['volume_type'])

    def _update_volume_info_from_extra_specs(self,
                                             volume_info,
                                             extra_specs):
        if not extra_specs:
            return
        for key, value in extra_specs.items():
            LOG.debug('key=%(key)s  value=%(value)s.'
                      % {'key': key, 'value': value})
            if key in volume_info.keys():
                words = value.strip().split()
                volume_info[key] = words.pop()

    def _update_volume_info_from_qos_specs(self,
                                           volume_info,
                                           qos_specs):
        if not qos_specs:
            return
        if qos_specs.get('qos_specs'):
            if qos_specs['qos_specs'].get('specs'):
                qos_spec = qos_specs['qos_specs'].get('specs')
                for key, value in qos_spec.items():
                    LOG.debug('key=%(key)s  value=%(value)s.'
                              % {'key': key, 'value': value})
                    if key in QOS_KEY:
                        volume_info['IOClASSID'] = value.strip()
                        qos_level = key
                        if qos_level == 'Qos-high':
                            volume_info['IOPRIORITY'] = "3"
                        elif qos_level == 'Qos-normal':
                            volume_info['IOPRIORITY'] = "2"
                        elif qos_level == 'Qos-low':
                            volume_info['IOPRIORITY'] = "1"
                        else:
                            volume_info['IOPRIORITY'] = "2"

    def _update_volume_info_from_volume_type(self,
                                             volume_info,
                                             volume_type_id):
        if not volume_type_id:
            return
        else:
            volume_type = volume_types.get_volume_type(
                context.get_admin_context(), volume_type_id)
            extra_specs = volume_type.get('extra_specs')
            self._update_volume_info_from_extra_specs(volume_info, extra_specs)
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            self._update_volume_info_from_qos_specs(volume_info, qos_specs)

    def _create_storage_info(self, info_type):
        if info_type == 'volume_info':
            volume_info = {'vol_name': '',
                           'vol_size': '',
                           'pool_id': '0',
                           'thin_flag': '0',
                           'reserved': '0',
                           'volume_space_reserved': '0',
                           'force_provision_size': '0',
                           'iops': '100',
                           'max_iops': '100',
                           'min_iops': '0',
                           'cache_size': '0',
                           'repicate_num': '1',
                           'repicate_tolerant_num': '1',
                           'encrypt_algorithm': '0',
                           'consistency': '0',
                           'stor_space_level': '1',
                           'compress_algorithm': '0',
                           'deduplication': '0',
                           'snapshot': '0',
                           'backup_cycle': '0',
                           'tolerance_disk_failure': '0',
                           'tolerance_cache_failure': '1'}
            return volume_info
        else:
            LOG.error(_LE('Invalid info type.'))
            return None
