Exemplo n.º 1
0
 def _spawn_hyper_vm(self, context, instance, image_meta, injected_files,
           admin_password, network_info=None, block_device_info=None):
     LOG.debug('start to _spawn_hyper_vm')
     bdms = block_device_info.get('block_device_mapping', [])
     self._binding_host(context, network_info, instance.uuid)
     if not instance.image_ref and len(bdms) > 0:
         volume_ids, bootable_volume_id = self._get_volume_ids_from_bdms(bdms)
         if bootable_volume_id:
             db_manager = DatabaseManager()
             cascaded_volume_id = db_manager.get_cascaded_volume_id(bootable_volume_id)
             # if cascaded volume already been created, then the data maybe changed, so cann't be created from image.
             if cascaded_volume_id:
                 LOG.info('Cascaded volume exist, need to transfer to image then create server from new image')
                 cascaded_backup_id = self.get_cascaded_volume_backup(bootable_volume_id)
                 # if cascaded_backup_id exist, means last time execute may fail, need to go on with last time job.
                 if cascaded_backup_id:
                     self.create_server_from_backup(cascaded_backup_id, bootable_volume_id, instance)
                 else:
                     cascaded_backup_id = self.create_backup_from_volume(self.project, bootable_volume_id, cascaded_volume_id)
                     self.create_server_from_backup(cascaded_backup_id, bootable_volume_id, instance)
             # if cascaded volume not exist, the data is the same between image and volume,
             # so we can create server from image.
             else:
                 LOG.info('Cascaded volume not exist, create server from image directly')
                 image_id = self._get_volume_source_image_id(context, bootable_volume_id)
                 instance.image_ref = image_id
                 self._spawn_from_image(context, instance, image_meta, injected_files,
                                         admin_password, network_info, block_device_info)
         else:
             raise Exception('No bootable volume for created server')
     else:
         self._spawn_from_image_for_hyper_vm(context, instance, image_meta, injected_files,
           admin_password, network_info, block_device_info)
     self._binding_host(context, network_info, instance.uuid)
Exemplo n.º 2
0
    def __init__(self, *args, **kwargs):
        super(HWSDriver, self).__init__(*args, **kwargs)
        gong_yao = CONF.hws.gong_yao
        si_yao = CONF.hws.si_yao
        region = CONF.hws.service_region
        protocol = CONF.hws.service_protocol
        port = CONF.hws.service_port
        self.hws_client = HWSClient(gong_yao, si_yao, region, protocol, port)
        self.db_manager = DatabaseManager()
        self.project_id = CONF.hws.project_id
        self.availability_zone = CONF.hws.resource_region
        self.volume_type_default = CONF.hws.volume_type

        self.hws_vgw_user = CONF.hws_vgw.user_name
        self.hws_vgw_password = CONF.hws_vgw.password
        self.hws_wgw_ip = CONF.hws_vgw.host_ip
        self.hws_vgw_ip = CONF.hws_vgw.hws_vgw_ip
Exemplo n.º 3
0
 def _wait_for_boot():
     """Called at an interval until the VM is running."""
     job_current_info = self.hws_client.ecs.get_job_detail(project_id, job_id)
     if job_current_info and job_current_info['status'] == 200:
         job_status_ac = job_current_info['body']['status']
         if job_status_ac == 'SUCCESS':
             server_id = job_current_info['body']['entities']['sub_jobs'][0]["entities"]['server_id']
             LOG.info('Add hws server id: %s' % server_id)
             if server_id:
                 LOG.info('HWS add server id mapping, cascading id: %s, cascaded id: %s' %
                          (instance.uuid, server_id))
                 db_manager = DatabaseManager()
                 db_manager.add_server_id_mapping(instance.uuid, server_id)
                 db_manager.add_server_id_name_mapping(instance.uuid, server_name)
             else:
                 error_info = 'No server id found for cascading id: %s, server: %s' % (instance.uuid, server_name)
                 LOG.error(error_info)
                 raise Exception('HWS Create Server Error, EXCEPTION: %s' % error_info)
             raise loopingcall.LoopingCallDone()
         elif job_status_ac == 'FAIL':
             error_info = json.dumps(job_current_info)
             LOG.error('HWS Create Server Error, EXCEPTION: %s' % error_info)
             raise Exception(error_info)
         elif job_status_ac == "RUNNING":
             LOG.debug('Job for creating server: %s is still RUNNING.' % server_name)
             pass
         elif job_status_ac == "INIT":
             LOG.debug('JOB for creating server: %s is INIT' % server_name)
             pass
         else:
             LOG.debug('JOB status is %s' % job_status_ac)
             pass
     elif job_current_info and job_current_info['status'] == 'error':
         try:
             self._deal_java_error(job_current_info)
         except Exception, e:
             pass
Exemplo n.º 4
0
    def __init__(self, *args, **kwargs):
        super(HWSDriver, self).__init__( *args, **kwargs)
        gong_yao = CONF.hws.gong_yao
        si_yao = CONF.hws.si_yao
        region = CONF.hws.service_region
        protocol = CONF.hws.service_protocol
        port = CONF.hws.service_port
        self.hws_client = HWSClient(gong_yao, si_yao, region, protocol, port)
        self.db_manager = DatabaseManager()
        self.project_id = CONF.hws.project_id
        self.availability_zone = CONF.hws.resource_region
        self.volume_type_default = CONF.hws.volume_type

        self.hws_vgw_user = CONF.hws_vgw.user_name
        self.hws_vgw_password = CONF.hws_vgw.password
        self.hws_wgw_ip = CONF.hws_vgw.host_ip
        self.hws_vgw_ip = CONF.hws_vgw.hws_vgw_ip
Exemplo n.º 5
0
                    pass
            elif job_current_info and job_current_info['status'] == 'error':
                try:
                    self._deal_java_error(job_current_info)
                except Exception, e:
                    pass
            elif not job_current_info:
                pass
            else:
                error_info = json.dumps(job_current_info)
                # log.error('HWS Create Server Error, EXCEPTION: %s' % error_info)
                raise Exception(error_info)

        timer = loopingcall.FixedIntervalLoopingCall(_wait_for_boot)
        timer.start(interval=5).wait()
        db_m = DatabaseManager()
        provider_server_id = db_m.get_cascaded_server_id(instance.uuid)
        provider_server = self.hws_client.ecs.get_detail(self.project, provider_server_id)
        self._create_hyper_service_container(context, instance, provider_server,
                                        network_info, block_device_info,
                                        image_meta['name'], injected_files, image_meta,admin_password)

    def _create_hyper_service_container(self, context, instance, provider_server,
                                        network_info, block_device_info,
                                        image_name, injected_files, image_meta, admin_password):
        LOG.debug('instance: %s' % instance)
        LOG.debug('provider_server: %s' % provider_server)
        LOG.debug('network_info: %s' % network_info)
        LOG.debug('block_device_info: %s' % block_device_info)
        LOG.debug('image_name: %s' % image_name)
        LOG.debug('injected_files: %s' % injected_files)
Exemplo n.º 6
0
class HWSDriver(driver.VolumeDriver):
    VERSION = "1.0"

    def __init__(self, *args, **kwargs):
        super(HWSDriver, self).__init__(*args, **kwargs)
        gong_yao = CONF.hws.gong_yao
        si_yao = CONF.hws.si_yao
        region = CONF.hws.service_region
        protocol = CONF.hws.service_protocol
        port = CONF.hws.service_port
        self.hws_client = HWSClient(gong_yao, si_yao, region, protocol, port)
        self.db_manager = DatabaseManager()
        self.project_id = CONF.hws.project_id
        self.availability_zone = CONF.hws.resource_region
        self.volume_type_default = CONF.hws.volume_type

        self.hws_vgw_user = CONF.hws_vgw.user_name
        self.hws_vgw_password = CONF.hws_vgw.password
        self.hws_wgw_ip = CONF.hws_vgw.host_ip
        self.hws_vgw_ip = CONF.hws_vgw.hws_vgw_ip

    def create_volume(self, volume):
        """Create a volume.
        """
        LOG.info('VOLUME: %s' % dir(volume))
        LOG.info('IMAGE ID: %s' % volume.get('image_id'))
        if not volume.get('image_id'):
            volume_name = volume.display_name
            project_id = self.project_id
            size = volume.size
            volume_type = self.volume_type_default

            job_info = self.hws_client.evs.create_volume(
                project_id,
                self.availability_zone,
                size,
                volume_type,
                name=volume_name)
            self._deal_with_job(job_info, project_id,
                                self._add_volume_mapping_to_db, None, volume)
        else:
            return {'provider_location': 'HWS CLOUD'}

    def _get_instance_volume_list(self, instance_id):
        """

        :param project_id: string, hws project id
        :param volume_id: string, hws volume id
        :return volume_list_rsp:
        """
        volume_list_rsp = self.hws_client.ecs.get_volume_list(
            self.project_id, instance_id)
        if volume_list_rsp['status'] != 200:
            error_info = 'hws_v2v: get hws v2v gateway host volume list error, Exception: %s' \
                         % json.dumps(volume_list_rsp)
            LOG.error(error_info)
            raise Exception(error_info)
        return volume_list_rsp

    def _get_volume_detail(self, volume_id):
        """

        :param project_id: string, hws project id
        :param volume_id: string, hws volume id
        :return volume_detail_rsp:
        """
        volume_detail_rsp = self.hws_client.evs.get_volume_detail(
            self.project_id, volume_id)
        if volume_detail_rsp['status'] != 200:
            error_info = 'hws_v2v: get hws volume detail error, Exception: %s' \
                         % json.dumps(volume_detail_rsp)
            LOG.error(error_info)
            raise Exception(error_info)
        return volume_detail_rsp

    def _attach_volume(self, instance_id, volume_id, device_name):
        """

        :param project: string, hws project id
        :param instance_id: string, hws server id
        :param volume_id: string, hws volume id
        :param device_name: device name, e.g. '/dev/sdb'
        :param cascading_volume_id:  string, cascading volume id
        :return:
        """
        job_attach_volume = self.hws_client.ecs.attach_volume(
            self.project_id, instance_id, volume_id, device_name)
        self._deal_with_job(job_attach_volume, self.project_id)

    def _deal_java_error(self, java_response):
        """
        {
          'status': 'error',
          'body': {
            'message': '<MESSAGE>',
            'exception': '<EXCEPTION>'
          }
        }
        :param java_response: dict
        :return:
        """
        if 'error' == java_response['status']:
            error_message = java_response['body']['message']
            exception = java_response['body']['exception']
            LOG.error('Java error message: %s, exception: %s' %
                      (error_message, exception))
            raise exception.NovaException(exception)
        if 200 == java_response['status']:
            return
        elif 202 == java_response['status']:
            return
        else:
            error_info = json.dumps(java_response)
            LOG.error(error_info)
            raise Exception(error_info)

    def _power_on(self, instance_id):
        start_result = self.hws_client.ecs.start_server(
            self.project_id, instance_id)
        self._deal_java_error(start_result)

    def _power_off(self, instance_id):
        stop_result = self.hws_client.ecs.stop_server(self.project_id,
                                                      instance_id)
        self._deal_java_error(stop_result)

    def _get_server_status(self, instance_id):
        try:
            server = self.hws_client.ecs.get_detail(self.project_id,
                                                    instance_id)
            if server and server['status'] == 200:
                status = server['body']['server']['status']
        except Exception:
            msg = traceback.format_exc()
            raise Exception(msg)
        return status

    def _stop_server(self, instance_id):
        status = self._get_server_status(instance_id)
        if HWS_SERVER_STATUS['active'] == status:
            self._power_off(instance_id)
            time.sleep(20)
            retry_times = 10
            # query server status until server status is SHUTOFF
            while retry_times > 0:
                time.sleep(5)
                status = self._get_server_status(instance_id)
                LOG.error('status: %s' % status)
                if HWS_SERVER_STATUS['shutoff'] == status:
                    break
                retry_times -= 1
        if HWS_SERVER_STATUS['shutoff'] != status:
            msg = "hws_v2v: stop server failed, hws_instance_id: %s, status: %s " %\
                  (instance_id, status)
            raise Exception(msg)

    def _detach_volume(self, instance_id, volume_id):
        """
        Detach the disk attached to the instance.

        :param connection_info:
        {
            u'driver_volume_type': u'vcloud_volume',
            u'serial': u'824d397e-4138-48e4-b00b-064cf9ef4ed8',
            u'data': {
                u'backend': u'vcloud',
                u'qos_specs': None,
                u'access_mode': u'rw',
                u'display_name': u'volume_02',
                u'volume_id': u'824d397e-4138-48e4-b00b-064cf9ef4ed8'
            }
        }
        :param instance:
        :param mountpoint: string, e.g. '/dev/sdb'
        :param encryption:
        :return:
        """
        job_detach_volume = self.hws_client.ecs.detach_volume(
            self.project_id, instance_id, volume_id)
        self._deal_with_job(job_detach_volume, self.project_id)

    def _get_instance_next_devname(self, instance_id):
        volume_list_rsp = self._get_instance_volume_list(instance_id)
        volume_list = volume_list_rsp['body']['volumeAttachments']
        used_device_letter = set()
        all_letters = set(string.ascii_lowercase)
        for volume in volume_list:
            used_device_letter.add(volume.get('device')[-1])
        unused_device_letter = list(all_letters - used_device_letter)
        LOG.error(used_device_letter)
        LOG.error(all_letters)
        next_dev_name = volume.get('device')[:-1] + unused_device_letter[0]
        return next_dev_name

    def _get_management_url(self, kc, image_name, **kwargs):
        endpoint_info = kc.service_catalog.get_endpoints(**kwargs)
        endpoint_list = endpoint_info.get(kwargs.get('service_type'), None)
        region_name = image_name.split('_')[-1]
        if endpoint_list:
            for endpoint in endpoint_list:
                if region_name == endpoint.get('region'):
                    return endpoint.get('publicURL')

    def _copy_volume_to_file(self, image_meta, dev_name):
        image_id = image_meta.get('id')
        dest_file_path = os.path.join('/tmp', image_id)
        real_devname = HWS_REAL_DEVNAME[dev_name]
        try:
            ssh_client = sshclient.SSH(user=self.hws_vgw_user,
                                       host=self.hws_vgw_ip,
                                       password=self.hws_vgw_password)
            # convert volume to image
            cmd = 'qemu-img convert -c -O qcow2 %s %s' % \
                  (real_devname, dest_file_path)
            LOG.error(
                'begin time of %s is %s' %
                (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
            ssh_client.run(cmd)
            LOG.debug("Finished running cmd : %s" % cmd)
            LOG.error(
                'end time of %s is %s' %
                (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.error('Failed to copy volume to image by vgw.',
                          traceback.format_exc())
        finally:
            if ssh_client:
                # delete the temp file which is used for convert volume to image
                ssh_client.close()

    @sshclient.RetryDecorator(max_retry_count=CONF.hws_vgw.ssh_retry_times,
                              exceptions=(sshclient.SSHError,
                                          sshclient.SSHTimeout))
    def _copy_file_to_remote_vgw(self, image_meta):
        image_id = image_meta.get('id')
        image_name = image_meta.get('name')
        dest_file_path = os.path.join('/tmp', image_id)
        kwargs = {
            'auth_url': CONF.keystone_authtoken.keystone_auth_url,
            'tenant_name': CONF.keystone_authtoken.tenant_name,
            'user_name': CONF.keystone_authtoken.user_name,
            'password': CONF.keystone_authtoken.password,
            'insecure': True
        }
        keystone_client = kc.Client(**kwargs)
        # get remote v2v gateway
        vgw_url = self._get_management_url(keystone_client,
                                           image_name,
                                           service_type='v2v')
        try:
            ssh_client = sshclient.SSH(user=self.hws_vgw_user,
                                       host=self.hws_vgw_ip,
                                       password=self.hws_vgw_password)
            LOG.debug('The remote vgw url is %(vgw_url)s',
                      {'vgw_url': vgw_url})
            # eg: curl -X POST --http1.0 -T
            # /tmp/467bd6e1-5a6e-4daa-b8bc-356b718834f2
            # http://172.27.12.245:8090/467bd6e1-5a6e-4daa-b8bc-356b718834f2
            cmd = 'curl -X POST --http1.0 -T %s ' % dest_file_path
            cmd += vgw_url
            if cmd.endswith('/'):
                cmd += image_id
            else:
                cmd += '/' + image_id
            LOG.error(
                'begin time of %s is %s' %
                (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
            ssh_client.run(cmd)
            LOG.error(
                'end time of %s is %s' %
                (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))

            LOG.debug("Finished running cmd : %s" % cmd)
            ssh_client.run('rm -f %s' % dest_file_path)
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.error('Failed to copy volume to image by vgw.',
                          traceback.format_exc())
        finally:
            if ssh_client:
                ssh_client.close()

    def copy_volume_to_image(self, context, volume, image_service, image_meta):
        container_format = image_meta.get('container_format')
        #if container_format == 'vgw_url':
        if container_format == 'bare':
            try:
                # 1.get the hws volume id
                cascaded_volume_id = volume['id']
                hws_volume_id = self.db_manager.get_cascaded_volume_id(
                    cascaded_volume_id)
                if not hws_volume_id:
                    msg = 'get hws volume id error, cascaded id: %s' % cascaded_volume_id
                    LOG.error(msg)
                    raise Exception('get hws volume id error')
                # 2. get the hws_volume status
                volume_detail_rsp = self._get_volume_detail(hws_volume_id)
                status = volume_detail_rsp['body']['volume']['status']
                # attachments = volume_detail_rsp['body']['volume']['attachments']
                # attach_num = len(attachments)
                # origin_instance_id = None
                # attach_back = False
                # 3. detach volume from origin instance
                # if status == 'in-use':
                #     if attach_num != 1:
                #         msg = 'hws_v2v: get attachments info error, num: %s' % attach_num
                #         LOG.error(msg)
                #         raise Exception(msg)
                #     origin_instance_id = attachments[0]['server_id']
                #     # volume can only be detached when sever stop
                #     self._stop_server(origin_instance_id)
                #     self._detach_volume(origin_instance_id, hws_volume_id)
                #     attach_back = True
                #     volume_detail_rsp = self._get_volume_detail(hws_volume_id)
                #     status = volume_detail_rsp['body']['status']

                # 4. attach volume to hws v2v gateway host
                if status != 'available':
                    msg = 'attach volume to local v2v gateway host error, status : %s, cascaded_volume_id: %s, ' \
                          'hws_volume_id %s' % (status, cascaded_volume_id, hws_volume_id)
                    LOG.error(msg)
                    raise Exception(
                        'attach volume to local v2v gateway failed')
                hws_vgw_instance_id = CONF.hws_vgw.hws_instance_id
                # if not hws_vgw_instance_id:
                #     LOG.error(
                #         'hws_v2v: get cascaded v2v gateway instance id error: %s' % CONF.hws_vgw.cascaded_instance_id)
                #     raise Exception('hws_v2v: get cascaded v2v gateway instance error.')
                dev_name = self._get_instance_next_devname(hws_vgw_instance_id)
                self._attach_volume(hws_vgw_instance_id, hws_volume_id,
                                    dev_name)
                # 5. copy volume to file
                self._copy_volume_to_file(image_meta, dev_name)

                # 6. copy file to remote v2v gateway
                # self._copy_file_to_remote_vgw(image_meta)
                # 7. create a empty file to glance
                with image_utils.temporary_file() as tmp:
                    image_utils.upload_volume(context, image_service,
                                              image_meta, tmp)
                fileutils.delete_if_exists(tmp)
                # 8. detach volume from hws v2v gateway
                self._stop_server(hws_vgw_instance_id)
                self._detach_volume(hws_vgw_instance_id, hws_volume_id)
                self._power_on(hws_vgw_instance_id)
            finally:
                attach_back = True
                # if attach_back is True:
                #     origin_dev_name = attachments[0]['device']
                #     self._attach_volume(origin_instance_id, hws_volume_id, origin_dev_name)
                #     self._power_on(origin_instance_id)

    @sshclient.RetryDecorator(max_retry_count=CONF.hws_vgw.ssh_retry_times,
                              exceptions=(sshclient.SSHError,
                                          sshclient.SSHTimeout))
    def _copy_file_to_volume(self, image_id, dev_name):
        try:
            real_devname = HWS_REAL_DEVNAME[dev_name]
            dest_file_path = os.path.join('/tmp', image_id)
            ssh_client = sshclient.SSH(user=self.hws_vgw_user,
                                       host=self.hws_vgw_ip,
                                       password=self.hws_vgw_password)
            # copy data to volume
            cmd = 'qemu-img convert %s %s' % \
                  (dest_file_path, real_devname)
            ssh_client.run(cmd)
            LOG.debug("Finished running cmd : %s" % cmd)

            # cmd = 'rm -rf %s' % dest_file_path
            # ssh_client.run(cmd)

        except Exception as e:
            LOG.error(
                'Failed to copy data to volume from vgw. '
                'traceback: %s', traceback.format_exc())
            raise e
        finally:
            if ssh_client:
                ssh_client.close()

    def copy_image_to_volume(self, context, volume, image_service, image_id):
        image_meta = image_service.show(context, image_id)
        container_format = image_meta.get('container_format')
        # if container_format == 'vgw_url':
        if container_format == 'bare':
            # 1.get the hws_volume_id
            cascaded_volume_id = volume['id']
            self.create_volume(volume)
            hws_volume_id = self.db_manager.get_cascaded_volume_id(
                cascaded_volume_id)
            if not cascaded_volume_id:
                LOG.error('get cascaded volume id error: %s' %
                          cascaded_volume_id)
                raise Exception('get cascaded volume id error.')
            # 2. get the hws_volume status
            time.sleep(30)
            retry_times = 10
            while retry_times > 0:
                volume_detail_rsp = self._get_volume_detail(hws_volume_id)
                status = volume_detail_rsp['body']['volume']['status']
                if status == 'available':
                    break
                else:
                    time.sleep(5)
                    retry_times -= 1
            if status != 'available':
                LOG.error(
                    'create hws volume failed, status: %s, cascaded_volume_id: %s, hws_volume_id: %s'
                    % (status, cascaded_volume_id, hws_volume_id))
                raise Exception('create hws volume failed.')
            # 2. attach volume to hws v2v gateway host
            hws_vgw_instance_id = CONF.hws_vgw.hws_instance_id
            # if not hws_vgw_instance_id:
            #     LOG.error('hws_v2v: get cascaded v2v gateway instance id error.' % CONF.hws_vgw.cascaded_instance_id)
            #     raise Exception('get cascaded v2v gateway instance id error.')
            dev_name = self._get_instance_next_devname(hws_vgw_instance_id)
            self._attach_volume(hws_vgw_instance_id, hws_volume_id, dev_name)
            # 3. copy image's file to volume
            self._copy_file_to_volume(image_id, dev_name)

            # 4. detach volume from hws v2v gateway
            self._stop_server(hws_vgw_instance_id)
            self._detach_volume(hws_vgw_instance_id, hws_volume_id)
            self._power_on(hws_vgw_instance_id)

        # Not to create volume when call cinder create volume API
        # Only when attache or dettach, or create server by volume, then create volume.
        elif not image_id:
            volume_name = volume.display_name
            project_id = self.project_id
            size = volume.size
            volume_type = self.volume_type_default
            image_hws_id = self._get_cascaded_image_id(image_id)

            job_info = self.hws_client.evs.create_volume(
                project_id,
                self.availability_zone,
                size,
                volume_type,
                name=volume_name,
                imageRef=image_hws_id)
            self._deal_with_job(job_info, project_id,
                                self._add_volume_mapping_to_db, None, volume)

    def _get_volume_type(self, volume_type):
        if volume_type not in SUPPORT_VOLUME_TYPE:
            LOG.info(
                'VOLUME TYPE: %s is not support in HWS Clouds, support type is: [%s]. Use SATA as default'
                % (volume_type, SUPPORT_VOLUME_TYPE))
            volume_type = SATA

        return volume_type

    def _get_cascaded_image_id(self, cascading_image_id):
        cascaded_image_id = self.db_manager.get_cascaded_image_id(
            cascading_image_id)
        if not cascaded_image_id:
            LOG.error('No image mapping in HWS Cloud.')
            raise Exception('No image mapping in HWS Cloud.')

        return cascaded_image_id

    def _add_volume_mapping_to_db(self, job_detail_of_create_volume, volume):
        """

        :param job_detail_of_create_volume:
        :return:
        """
        hws_volume_id = job_detail_of_create_volume['body']['entities'][
            'volume_id']
        volume_id = volume.id
        self.db_manager.add_volume_mapping(volume_id, hws_volume_id)
        LOG.info('Success to add volume mapping: {%s: %s}' %
                 (volume_id, hws_volume_id))

    def _deal_with_job(self,
                       job_info,
                       project_id,
                       function_deal_with_success=None,
                       function_deal_with_fail=None,
                       object=None):
        if job_info['status'] == 200:
            job_id = job_info['body']['job_id']
            while True:
                time.sleep(5)
                job_detail_info = self.hws_client.evs.get_job_detail(
                    project_id, job_id)
                if job_detail_info:
                    if job_detail_info['status'] == 200:
                        job_status = job_detail_info['body']['status']
                        if job_status == 'RUNNING':
                            LOG.debug('job<%s> is still RUNNING.' % job_id)
                            continue
                        elif job_status == 'FAIL':
                            if function_deal_with_fail:
                                function_deal_with_fail(
                                    job_detail_info, object)
                            error_info = 'job<%s> FAIL, ERROR INFO: %s' % (
                                job_id, json.dumps(job_detail_info))
                            raise Exception(error_info)
                        elif job_status == 'SUCCESS':
                            if function_deal_with_success:
                                function_deal_with_success(
                                    job_detail_info, object)
                            success_info = 'job<%s> SUCCESS.' % job_id
                            LOG.info(success_info)
                            break
                    elif job_detail_info['status'] == 'error':
                        error_message = job_detail_info['body']['message']
                        exception = job_detail_info['body']['exception']
                        LOG.error('Java error message: %s, exception: %s' %
                                  (error_message, exception))
                        continue
                    else:
                        info = json.dumps(job_detail_info)
                        LOG.info(
                            'Job info get has some issue: %s, will retry to get again.'
                            % info)
                        continue
                else:
                    retry_info = 'job detail info is empty, will retry to get. JOB DETAIL: %s' % job_detail_info
                    LOG.info(retry_info)
                    continue
        else:
            error_info = json.dumps(job_info)
            LOG.error('Job init FAIL, error info: %s' % error_info)
            raise Exception(error_info)

    def _deal_with_create_volume_fail(self, job_detail_info, volume):
        """
        deal with create volume fail.
        If hws volume is created, but fail, then save id mapping in db. then raise exception.
        if hws volume id is not created, raise exception directly.
        {
            "body": {
                "status": "FAIL",
                "entities": {
                    "volume_id": "1be7a768-59b6-4ef6-b4c0-a4f8039fa626"
                },
                "job_id": "8aace0c751b0a3bd01523529e4f70d35",
                "job_type": "createVolume",
                "begin_time": "2016-01-12T09:28:04.086Z",
                "end_time": "2016-01-12T09:28:32.252Z",
                "error_code": "EVS.2024",
                "fail_reason": "EbsCreateVolumeTask-fail:volume is error!"
            },
            "status": 200
        }
        :param job_detail_info:
        :param volume:
        :return:
        """
        job_id = job_detail_info.get('body').get('job_id')
        error_info = 'job<%s> FAIL, ERROR INFO: %s' % (
            job_id, json.dumps(job_detail_info))
        if job_detail_info.get('body').get('entities'):
            hws_volume_id = job_detail_info.get('body').get('entities').get(
                'volume_id')
            if hws_volume_id:
                LOG.info('HWS volume is created, id is: %s' % hws_volume_id)
                volume_id = volume.id
                self.db_manager.add_volume_mapping(volume_id, hws_volume_id)
                LOG.debug('Success to add volume mapping: {%s: %s}' %
                          (volume_id, hws_volume_id))
                raise Exception(error_info)

        raise Exception(error_info)

    def delete_volume(self, volume):
        cascading_volume_id = volume.id
        project_id = self.project_id
        cascaded_volume_id = self.db_manager.get_cascaded_volume_id(
            cascading_volume_id)
        LOG.info('VOLUME_ID: %s' % cascaded_volume_id)

        if cascaded_volume_id:
            volume_get = self.hws_client.evs.get_volume_detail(
                project_id, cascaded_volume_id)
            if volume_get['status'] == 200:
                job_info = self.hws_client.evs.delete_volume(
                    project_id, cascaded_volume_id)
                self._deal_with_job(job_info, project_id,
                                    self._delete_volume_mapping, None, volume)
            elif volume_get['status'] == 404 and volume_get.get('body').get(
                    'itemNotFound'):
                LOG.info(
                    'cascaded volume is not exist, so directly return delete success'
                )
                return
            else:
                error_info = 'Delete volume fail, Exception: %s' % json.dumps(
                    volume_get)
                LOG.error(error_info)
                raise Exception(error_info)
        else:
            LOG.info(
                'cascaded volume is not exist, so directly return delete success'
            )
            return

    def _delete_volume_mapping(self, job_detail_info, volume):
        cascading_volume_id = volume.id
        self.db_manager.delete_volume_mapping(cascading_volume_id)
        LOG.info('Delete volume mapping for cascading volume id: %s' %
                 cascading_volume_id)

    def get_volume_stats(self, refresh=False):
        """Get volume stats."""
        # pdb.set_trace()
        if not self._stats:
            backend_name = self.configuration.safe_get('volume_backend_name')
            LOG.debug('*******backend_name is %s' % backend_name)
            if not backend_name:
                backend_name = 'HC_HWS'
            data = {
                'volume_backend_name': backend_name,
                'vendor_name': 'Huawei',
                'driver_version': self.VERSION,
                'storage_protocol': 'LSI Logic SCSI',
                'reserved_percentage': 0,
                'total_capacity_gb': 1000,
                'free_capacity_gb': 1000
            }
            self._stats = data
        return self._stats

    def initialize_connection(self, volume, connector):
        """Allow connection to connector and return connection info."""
        LOG.debug('vCloud Driver: initialize_connection')

        driver_volume_type = 'hwclouds_volume'
        data = {}
        data['backend'] = 'hwclouds'
        data['volume_id'] = volume['id']
        data['display_name'] = volume['display_name']

        return {'driver_volume_type': driver_volume_type, 'data': data}

    def check_for_setup_error(self):
        """Check configuration file."""
        pass

    def create_cloned_volume(self, volume, src_vref):
        """Create a clone of the specified volume."""
        pass

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

    def create_snapshot(self, snapshot):
        pass

    def create_volume_from_snapshot(self, volume, snapshot):
        """Create a volume from a snapshot."""
        pass

    def delete_snapshot(self, snapshot):
        """Delete a snapshot."""
        pass

    def do_setup(self, context):
        """Instantiate common class and log in storage system."""
        pass

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

    def extend_volume(self, volume, new_size):
        """Extend a volume."""
        pass

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

    def terminate_connection(self, volume, connector, **kwargs):
        """Disallow connection from connector"""
        LOG.debug('vCloud Driver: terminate_connection')
        pass

    def validate_connector(self, connector):
        """Fail if connector doesn't contain all the data needed by driver."""
        LOG.debug('vCloud Driver: validate_connector')
        pass
Exemplo n.º 7
0
class HWSDriver(driver.VolumeDriver):
    VERSION = "1.0"

    def __init__(self, *args, **kwargs):
        super(HWSDriver, self).__init__( *args, **kwargs)
        gong_yao = CONF.hws.gong_yao
        si_yao = CONF.hws.si_yao
        region = CONF.hws.service_region
        protocol = CONF.hws.service_protocol
        port = CONF.hws.service_port
        self.hws_client = HWSClient(gong_yao, si_yao, region, protocol, port)
        self.db_manager = DatabaseManager()
        self.project_id = CONF.hws.project_id
        self.availability_zone = CONF.hws.resource_region
        self.volume_type_default = CONF.hws.volume_type

        self.hws_vgw_user = CONF.hws_vgw.user_name
        self.hws_vgw_password = CONF.hws_vgw.password
        self.hws_wgw_ip = CONF.hws_vgw.host_ip
        self.hws_vgw_ip = CONF.hws_vgw.hws_vgw_ip

    def create_volume(self, volume):
        """Create a volume.
        """
        LOG.info('VOLUME: %s' % dir(volume))
        LOG.info('IMAGE ID: %s' % volume.get('image_id'))
        if not volume.get('image_id'):
            volume_name = self._get_display_name(volume)
            project_id = self.project_id
            size = volume.size
            volume_type = self.volume_type_default

            job_info = self.hws_client.evs.create_volume(project_id, self.availability_zone,
                                                         size, volume_type, name=volume_name)
            self._deal_with_job(job_info, project_id, self._add_volume_mapping_to_db, None, volume)
        else:
            return {'provider_location': 'HWS CLOUD'}

    def _get_display_name(self, volume):
        original_display_name = volume.display_name
        if len(original_display_name) < 20:
            display_name = original_display_name
        else:
            display_name = self._get_random_name(8)

        return display_name

    def _get_random_name(self, length):
        return ''.join(random.sample(string.ascii_letters + string.digits, length))

    def _get_instance_volume_list(self, instance_id):
        """

        :param project_id: string, hws project id
        :param volume_id: string, hws volume id
        :return volume_list_rsp:
        """
        volume_list_rsp = self.hws_client.ecs.get_volume_list(self.project_id, instance_id)
        if volume_list_rsp['status'] != 200:
            error_info = 'hws_v2v: get hws v2v gateway host volume list error, Exception: %s' \
                         % json.dumps(volume_list_rsp)
            LOG.error(error_info)
            raise Exception(error_info)
        return volume_list_rsp

    def _get_volume_detail(self, volume_id):
        """

        :param project_id: string, hws project id
        :param volume_id: string, hws volume id
        :return volume_detail_rsp:
        """
        volume_detail_rsp = self.hws_client.evs.get_volume_detail(self.project_id, volume_id)
        if volume_detail_rsp['status'] != 200:
            error_info = 'hws_v2v: get hws volume detail error, Exception: %s' \
                         % json.dumps(volume_detail_rsp)
            LOG.error(error_info)
            raise Exception(error_info)
        return volume_detail_rsp

    def _attach_volume(self, instance_id, volume_id, device_name):
        """

        :param project: string, hws project id
        :param instance_id: string, hws server id
        :param volume_id: string, hws volume id
        :param device_name: device name, e.g. '/dev/sdb'
        :param cascading_volume_id:  string, cascading volume id
        :return:
        """
        job_attach_volume = self.hws_client.ecs.attach_volume(self.project_id,
                                                              instance_id,
                                                              volume_id,
                                                              device_name)
        self._deal_with_job(job_attach_volume, self.project_id)

    def _deal_java_error(self, java_response):
        """
        {
          'status': 'error',
          'body': {
            'message': '<MESSAGE>',
            'exception': '<EXCEPTION>'
          }
        }
        :param java_response: dict
        :return:
        """
        if 'error' == java_response['status']:
            error_message = java_response['body']['message']
            exception = java_response['body']['exception']
            LOG.error('Java error message: %s, exception: %s' % (error_message, exception))
            raise exception.NovaException(exception)
        if 200 == java_response['status']:
            return
        elif 202 == java_response['status']:
            return
        else:
            error_info = json.dumps(java_response)
            LOG.error(error_info)
            raise Exception(error_info)

    def _power_on(self, instance_id):
        start_result = self.hws_client.ecs.start_server(self.project_id, instance_id)
        self._deal_java_error(start_result)

    def _power_off(self,  instance_id):
        stop_result = self.hws_client.ecs.stop_server(self.project_id, instance_id)
        self._deal_java_error(stop_result)

    def _get_server_status(self, instance_id):
        try:
            server = self.hws_client.ecs.get_detail(self.project_id, instance_id)
            if server and server['status'] == 200:
                status = server['body']['server']['status']
        except Exception:
            msg = traceback.format_exc()
            raise Exception(msg)
        return status

    def _stop_server(self,  instance_id):
        status = self._get_server_status(instance_id)
        if HWS_SERVER_STATUS['active'] == status:
            self._power_off(instance_id)
            time.sleep(20)
            retry_times = 10
            # query server status until server status is SHUTOFF
            while retry_times > 0:
                time.sleep(5)
                status = self._get_server_status(instance_id)
                LOG.error('status: %s' % status)
                if HWS_SERVER_STATUS['shutoff'] == status:
                    break
                retry_times -= 1
        if HWS_SERVER_STATUS['shutoff'] != status:
            msg = "hws_v2v: stop server failed, hws_instance_id: %s, status: %s " %\
                  (instance_id, status)
            raise Exception(msg)

    def _detach_volume(self, instance_id, volume_id):
        """
        Detach the disk attached to the instance.

        :param connection_info:
        {
            u'driver_volume_type': u'vcloud_volume',
            u'serial': u'824d397e-4138-48e4-b00b-064cf9ef4ed8',
            u'data': {
                u'backend': u'vcloud',
                u'qos_specs': None,
                u'access_mode': u'rw',
                u'display_name': u'volume_02',
                u'volume_id': u'824d397e-4138-48e4-b00b-064cf9ef4ed8'
            }
        }
        :param instance:
        :param mountpoint: string, e.g. '/dev/sdb'
        :param encryption:
        :return:
        """
        job_detach_volume = self.hws_client.ecs.detach_volume(self.project_id,
                                                              instance_id,
                                                              volume_id)
        self._deal_with_job(job_detach_volume, self.project_id)

    def _get_instance_next_devname(self, instance_id):
        volume_list_rsp = self._get_instance_volume_list(instance_id)
        volume_list = volume_list_rsp['body']['volumeAttachments']
        used_device_letter = set()
        all_letters = set(string.ascii_lowercase)
        for volume in volume_list:
            used_device_letter.add(volume.get('device')[-1])
        unused_device_letter = list(all_letters - used_device_letter)
        LOG.error(used_device_letter)
        LOG.error(all_letters)
        next_dev_name = volume.get('device')[:-1] + unused_device_letter[0]
        return next_dev_name

    def _get_management_url(self, kc, image_name, **kwargs):
        endpoint_info = kc.service_catalog.get_endpoints(**kwargs)
        endpoint_list = endpoint_info.get(kwargs.get('service_type'), None)
        region_name = image_name.split('_')[-1]
        if endpoint_list:
            for endpoint in endpoint_list:
                if region_name == endpoint.get('region'):
                    return endpoint.get('publicURL')

    def _copy_volume_to_file(self, image_meta, dev_name):
        image_id = image_meta.get('id')
        dest_file_path = os.path.join('/tmp', image_id)
        real_devname = HWS_REAL_DEVNAME[dev_name]
        try:
            ssh_client = sshclient.SSH(user=self.hws_vgw_user,
                                       host=self.hws_vgw_ip,
                                       password=self.hws_vgw_password)
            # convert volume to image
            cmd = 'qemu-img convert -c -O qcow2 %s %s' % \
                  (real_devname, dest_file_path)
            LOG.error('begin time of %s is %s' %
                      (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()
                                          )))
            ssh_client.run(cmd)
            LOG.debug("Finished running cmd : %s" % cmd)
            LOG.error('end time of %s is %s' %
                      (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.error('Failed to copy volume to image by vgw.',
                          traceback.format_exc())
        finally:
            if ssh_client:
                # delete the temp file which is used for convert volume to image
                ssh_client.close()

    @sshclient.RetryDecorator(max_retry_count=CONF.hws_vgw.ssh_retry_times,
                    exceptions=(sshclient.SSHError, sshclient.SSHTimeout))
    def _copy_file_to_remote_vgw(self, image_meta):
        image_id = image_meta.get('id')
        image_name = image_meta.get('name')
        dest_file_path = os.path.join('/tmp', image_id)
        kwargs = {
            'auth_url': CONF.keystone_authtoken.keystone_auth_url,
            'tenant_name': CONF.keystone_authtoken.tenant_name,
            'user_name': CONF.keystone_authtoken.user_name,
            'password': CONF.keystone_authtoken.password,
            'insecure': True
        }
        keystone_client = kc.Client(**kwargs)
        # get remote v2v gateway
        vgw_url = self._get_management_url(keystone_client, image_name, service_type='v2v')
        try:
            ssh_client = sshclient.SSH(user=self.hws_vgw_user,
                                       host=self.hws_vgw_ip,
                                       password=self.hws_vgw_password)
            LOG.debug('The remote vgw url is %(vgw_url)s',
                      {'vgw_url': vgw_url})
            # eg: curl -X POST --http1.0 -T
            # /tmp/467bd6e1-5a6e-4daa-b8bc-356b718834f2
            # http://172.27.12.245:8090/467bd6e1-5a6e-4daa-b8bc-356b718834f2
            cmd = 'curl -X POST --http1.0 -T %s ' % dest_file_path
            cmd += vgw_url
            if cmd.endswith('/'):
                cmd += image_id
            else:
                cmd += '/' + image_id
            LOG.error('begin time of %s is %s' %
                      (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()
                                          )))
            ssh_client.run(cmd)
            LOG.error('end time of %s is %s' %
                      (cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()
                                          )))

            LOG.debug("Finished running cmd : %s" % cmd)
            ssh_client.run('rm -f %s' % dest_file_path)
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.error('Failed to copy volume to image by vgw.',
                          traceback.format_exc())
        finally:
            if ssh_client:
                ssh_client.close()

    def copy_volume_to_image(self, context, volume, image_service, image_meta):
        container_format = image_meta.get('container_format')
        #if container_format == 'vgw_url':
        if container_format == 'bare':
            try:
                # 1.get the hws volume id
                cascaded_volume_id = volume['id']
                hws_volume_id = self.db_manager.get_cascaded_volume_id(cascaded_volume_id)
                if not hws_volume_id:
                    msg = 'get hws volume id error, cascaded id: %s' % cascaded_volume_id
                    LOG.error(msg)
                    raise Exception('get hws volume id error')
                # 2. get the hws_volume status
                volume_detail_rsp = self._get_volume_detail(hws_volume_id)
                status = volume_detail_rsp['body']['volume']['status']
                # attachments = volume_detail_rsp['body']['volume']['attachments']
                # attach_num = len(attachments)
                # origin_instance_id = None
                # attach_back = False
                # 3. detach volume from origin instance
                # if status == 'in-use':
                #     if attach_num != 1:
                #         msg = 'hws_v2v: get attachments info error, num: %s' % attach_num
                #         LOG.error(msg)
                #         raise Exception(msg)
                #     origin_instance_id = attachments[0]['server_id']
                #     # volume can only be detached when sever stop
                #     self._stop_server(origin_instance_id)
                #     self._detach_volume(origin_instance_id, hws_volume_id)
                #     attach_back = True
                #     volume_detail_rsp = self._get_volume_detail(hws_volume_id)
                #     status = volume_detail_rsp['body']['status']

                # 4. attach volume to hws v2v gateway host
                if status != 'available':
                    msg = 'attach volume to local v2v gateway host error, status : %s, cascaded_volume_id: %s, ' \
                          'hws_volume_id %s' % (status, cascaded_volume_id, hws_volume_id)
                    LOG.error(msg)
                    raise Exception('attach volume to local v2v gateway failed')
                hws_vgw_instance_id = CONF.hws_vgw.hws_instance_id
                # if not hws_vgw_instance_id:
                #     LOG.error(
                #         'hws_v2v: get cascaded v2v gateway instance id error: %s' % CONF.hws_vgw.cascaded_instance_id)
                #     raise Exception('hws_v2v: get cascaded v2v gateway instance error.')
                dev_name = self._get_instance_next_devname(hws_vgw_instance_id)
                self._attach_volume(hws_vgw_instance_id, hws_volume_id, dev_name)
                # 5. copy volume to file
                self._copy_volume_to_file(image_meta, dev_name)

                # 6. copy file to remote v2v gateway
                # self._copy_file_to_remote_vgw(image_meta)
                # 7. create a empty file to glance
                with image_utils.temporary_file() as tmp:
                    image_utils.upload_volume(context,
                                              image_service,
                                              image_meta,
                                              tmp)
                fileutils.delete_if_exists(tmp)
                # 8. detach volume from hws v2v gateway
                self._stop_server(hws_vgw_instance_id)
                self._detach_volume(hws_vgw_instance_id, hws_volume_id)
                self._power_on(hws_vgw_instance_id)
            finally:
                attach_back = True
                # if attach_back is True:
                #     origin_dev_name = attachments[0]['device']
                #     self._attach_volume(origin_instance_id, hws_volume_id, origin_dev_name)
                #     self._power_on(origin_instance_id)

    @sshclient.RetryDecorator(max_retry_count=CONF.hws_vgw.ssh_retry_times,
                                  exceptions=(sshclient.SSHError, sshclient.SSHTimeout))
    def _copy_file_to_volume(self, image_id, dev_name):
        try:
            real_devname = HWS_REAL_DEVNAME[dev_name]
            dest_file_path = os.path.join('/tmp', image_id)
            ssh_client = sshclient.SSH(user=self.hws_vgw_user,
                                       host=self.hws_vgw_ip,
                                       password=self.hws_vgw_password)
            # copy data to volume
            cmd = 'qemu-img convert %s %s' % \
                  (dest_file_path, real_devname)
            ssh_client.run(cmd)
            LOG.debug("Finished running cmd : %s" % cmd)

            # cmd = 'rm -rf %s' % dest_file_path
            # ssh_client.run(cmd)

        except Exception as e:
            LOG.error('Failed to copy data to volume from vgw. '
                      'traceback: %s', traceback.format_exc())
            raise e
        finally:
            if ssh_client:
                ssh_client.close()

    def copy_image_to_volume(self, context, volume, image_service, image_id):
        image_meta = image_service.show(context, image_id)
        container_format = image_meta.get('container_format')
        # if container_format == 'vgw_url':
        if container_format == 'bare':
            # 1.get the hws_volume_id
            cascaded_volume_id = volume['id']
            self.create_volume(volume)
            hws_volume_id = self.db_manager.get_cascaded_volume_id(cascaded_volume_id)
            if not cascaded_volume_id:
                LOG.error('get cascaded volume id error: %s' % cascaded_volume_id)
                raise Exception('get cascaded volume id error.')
            # 2. get the hws_volume status
            time.sleep(30)
            retry_times = 10
            while retry_times > 0:
                volume_detail_rsp = self._get_volume_detail(hws_volume_id)
                status = volume_detail_rsp['body']['volume']['status']
                if status == 'available':
                    break
                else:
                    time.sleep(5)
                    retry_times -= 1
            if status != 'available':
                LOG.error('create hws volume failed, status: %s, cascaded_volume_id: %s, hws_volume_id: %s'
                          % (status, cascaded_volume_id, hws_volume_id))
                raise Exception('create hws volume failed.')
            # 2. attach volume to hws v2v gateway host
            hws_vgw_instance_id = CONF.hws_vgw.hws_instance_id
            # if not hws_vgw_instance_id:
            #     LOG.error('hws_v2v: get cascaded v2v gateway instance id error.' % CONF.hws_vgw.cascaded_instance_id)
            #     raise Exception('get cascaded v2v gateway instance id error.')
            dev_name = self._get_instance_next_devname(hws_vgw_instance_id)
            self._attach_volume(hws_vgw_instance_id, hws_volume_id, dev_name)
            # 3. copy image's file to volume
            self._copy_file_to_volume(image_id, dev_name)

            # 4. detach volume from hws v2v gateway
            self._stop_server(hws_vgw_instance_id)
            self._detach_volume(hws_vgw_instance_id, hws_volume_id)
            self._power_on(hws_vgw_instance_id)

        # Not to create volume when call cinder create volume API
        # Only when attache or detach, or create server by volume, then create volume.
        elif not image_id:
            volume_name = self._get_display_name(volume)
            project_id = self.project_id
            size = volume.size
            volume_type = self.volume_type_default
            image_hws_id = self._get_cascaded_image_id(image_id)

            job_info = self.hws_client.evs.create_volume(project_id, self.availability_zone,
                                                         size, volume_type, name=volume_name, imageRef=image_hws_id)
            self._deal_with_job(job_info, project_id, self._add_volume_mapping_to_db, None, volume)

    def _get_volume_type(self, volume_type):
        if volume_type not in SUPPORT_VOLUME_TYPE:
            LOG.info('VOLUME TYPE: %s is not support in HWS Clouds, support type is: [%s]. Use SATA as default' %
                     (volume_type, SUPPORT_VOLUME_TYPE))
            volume_type = SATA

        return volume_type

    def _get_cascaded_image_id(self, cascading_image_id):
        cascaded_image_id = self.db_manager.get_cascaded_image_id(cascading_image_id)
        if not cascaded_image_id:
            LOG.error('No image mapping in HWS Cloud.')
            raise Exception('No image mapping in HWS Cloud.')

        return cascaded_image_id

    def _add_volume_mapping_to_db(self, job_detail_of_create_volume, volume):
        """

        :param job_detail_of_create_volume:
        :return:
        """
        hws_volume_id = job_detail_of_create_volume['body']['entities']['volume_id']
        volume_id = volume.id
        self.db_manager.add_volume_mapping(volume_id, hws_volume_id)
        LOG.info('Success to add volume mapping: {%s: %s}' % (volume_id, hws_volume_id))

    def _deal_with_job(self, job_info, project_id,
                       function_deal_with_success=None,
                       function_deal_with_fail=None,
                       object=None):
        if job_info['status'] == 200:
            job_id = job_info['body']['job_id']
            while True:
                time.sleep(5)
                job_detail_info = self.hws_client.evs.get_job_detail(project_id, job_id)
                if job_detail_info:
                    if job_detail_info['status'] == 200:
                        job_status = job_detail_info['body']['status']
                        if job_status == 'RUNNING':
                            LOG.debug('job<%s> is still RUNNING.' % job_id)
                            continue
                        elif job_status == 'FAIL':
                            if function_deal_with_fail:
                                function_deal_with_fail(job_detail_info, object)
                            error_info = 'job<%s> FAIL, ERROR INFO: %s' % (job_id, json.dumps(job_detail_info))
                            raise Exception(error_info)
                        elif job_status == 'SUCCESS':
                            if function_deal_with_success:
                                function_deal_with_success(job_detail_info, object)
                            success_info = 'job<%s> SUCCESS.' % job_id
                            LOG.info(success_info)
                            break
                    elif job_detail_info['status'] == 'error':
                        error_message = job_detail_info['body']['message']
                        exception = job_detail_info['body']['exception']
                        LOG.error('Java error message: %s, exception: %s' % (error_message, exception))
                        continue
                    else:
                        info = json.dumps(job_detail_info)
                        LOG.info('Job info get has some issue: %s, will retry to get again.' % info )
                        continue
                else:
                    retry_info = 'job detail info is empty, will retry to get. JOB DETAIL: %s' % job_detail_info
                    LOG.info(retry_info)
                    continue
        else:
            error_info = json.dumps(job_info)
            LOG.error('Job init FAIL, error info: %s' % error_info)
            raise Exception(error_info)

    def _deal_with_create_volume_fail(self, job_detail_info, volume):
        """
        deal with create volume fail.
        If hws volume is created, but fail, then save id mapping in db. then raise exception.
        if hws volume id is not created, raise exception directly.
        {
            "body": {
                "status": "FAIL",
                "entities": {
                    "volume_id": "1be7a768-59b6-4ef6-b4c0-a4f8039fa626"
                },
                "job_id": "8aace0c751b0a3bd01523529e4f70d35",
                "job_type": "createVolume",
                "begin_time": "2016-01-12T09:28:04.086Z",
                "end_time": "2016-01-12T09:28:32.252Z",
                "error_code": "EVS.2024",
                "fail_reason": "EbsCreateVolumeTask-fail:volume is error!"
            },
            "status": 200
        }
        :param job_detail_info:
        :param volume:
        :return:
        """
        job_id = job_detail_info.get('body').get('job_id')
        error_info = 'job<%s> FAIL, ERROR INFO: %s' % (job_id, json.dumps(job_detail_info))
        if job_detail_info.get('body').get('entities'):
            hws_volume_id = job_detail_info.get('body').get('entities').get('volume_id')
            if hws_volume_id:
                LOG.info('HWS volume is created, id is: %s' % hws_volume_id)
                volume_id = volume.id
                self.db_manager.add_volume_mapping(volume_id, hws_volume_id)
                LOG.debug('Success to add volume mapping: {%s: %s}' % (volume_id, hws_volume_id))
                raise Exception(error_info)

        raise Exception(error_info)

    def delete_volume(self, volume):
        cascading_volume_id = volume.id
        project_id = self.project_id
        cascaded_volume_id = self.db_manager.get_cascaded_volume_id(cascading_volume_id)
        LOG.info('VOLUME_ID: %s' % cascaded_volume_id)

        if cascaded_volume_id:
            volume_get = self.hws_client.evs.get_volume_detail(project_id, cascaded_volume_id)
            if volume_get['status'] == 200:
                job_info = self.hws_client.evs.delete_volume(project_id, cascaded_volume_id)
                self._deal_with_job(job_info,project_id, self._delete_volume_mapping, None, volume)
            elif volume_get['status'] == 404 and volume_get.get('body').get('itemNotFound'):
                LOG.info('cascaded volume is not exist, so directly return delete success')
                return
            else:
                error_info = 'Delete volume fail, Exception: %s' % json.dumps(volume_get)
                LOG.error(error_info)
                raise Exception(error_info)
        else:
            LOG.info('cascaded volume is not exist, so directly return delete success')
            return

    def _delete_volume_mapping(self, job_detail_info, volume):
        cascading_volume_id = volume.id
        self.db_manager.delete_volume_mapping(cascading_volume_id)
        LOG.info('Delete volume mapping for cascading volume id: %s' % cascading_volume_id)

    def get_volume_stats(self, refresh=False):
        """Get volume stats."""
        # pdb.set_trace()
        if not self._stats:
            backend_name = self.configuration.safe_get('volume_backend_name')
            LOG.debug('*******backend_name is %s' %backend_name)
            if not backend_name:
                backend_name = 'HC_HWS'
            data = {'volume_backend_name': backend_name,
                    'vendor_name': 'Huawei',
                    'driver_version': self.VERSION,
                    'storage_protocol': 'LSI Logic SCSI',
                    'reserved_percentage': 0,
                    'total_capacity_gb': 1000,
                    'free_capacity_gb': 1000}
            self._stats = data
        return self._stats

    def initialize_connection(self, volume, connector):
        """Allow connection to connector and return connection info."""
        LOG.debug('vCloud Driver: initialize_connection')

        driver_volume_type = 'hwclouds_volume'
        data = {}
        data['backend'] = 'hwclouds'
        data['volume_id'] = volume['id']
        data['display_name'] = volume['display_name']

        return {'driver_volume_type': driver_volume_type,
                 'data': data}

    def check_for_setup_error(self):
        """Check configuration file."""
        pass

    def create_cloned_volume(self, volume, src_vref):
        """Create a clone of the specified volume."""
        pass

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

    def create_snapshot(self, snapshot):
        pass

    def create_volume_from_snapshot(self, volume, snapshot):
        """Create a volume from a snapshot."""
        pass

    def delete_snapshot(self, snapshot):
        """Delete a snapshot."""
        pass

    def do_setup(self, context):
        """Instantiate common class and log in storage system."""
        pass

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

    def extend_volume(self, volume, new_size):
        """Extend a volume."""
        pass

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

    def terminate_connection(self, volume, connector, **kwargs):
        """Disallow connection from connector"""
        LOG.debug('vCloud Driver: terminate_connection')
        pass

    def validate_connector(self, connector):
        """Fail if connector doesn't contain all the data needed by driver."""
        LOG.debug('vCloud Driver: validate_connector')
        pass