class VCloudVolumeDriver(driver.VolumeDriver): VERSION = "1.0" def __init__(self, scheme="https", *args, **kwargs): super(VCloudVolumeDriver, self).__init__( *args, **kwargs) self._stats = None self._nova_api = nova.API() self._node_name = CONF.vcloud.vcloud_node_name self._vcloud_client = VCloudClient(scheme=scheme) self._vgw_host = CONF.vgw.vcloud_vgw_host self._vgw_name = CONF.vgw.vcloud_vgw_name self._vgw_username = CONF.vgw.vcloud_vgw_username self._vgw_password = CONF.vgw.vcloud_vgw_password #self._vgw_url = CONF.vgw.vcloud_vgw_url self._vgw_store_file_dir = CONF.vgw.store_file_dir self.db = kwargs.get('db') def _get_vcloud_volume_name(self, volume_id, volume_name): volume_prefix = 'volume@' snapshot_prefix = 'snapshot@' backup_prefix = 'backup@' if volume_name.startswith(volume_prefix): vcloud_volume_name = volume_name[len(volume_prefix):] elif volume_name.startswith(snapshot_prefix): vcloud_volume_name = volume_name[len(snapshot_prefix):] elif volume_name.startswith(backup_prefix): vcloud_volume_name = volume_name[len(backup_prefix):] else: vcloud_volume_name = volume_id return vcloud_volume_name def do_setup(self, context): """Instantiate common class and log in storage system.""" pass def check_for_setup_error(self): """Check configuration file.""" pass def create_volume(self, volume): """Create a volume.""" # use volume_name as vcloud disk name, remove prefix str `volume@` # if volume_name does not start with volume@, then use volume id instead volume_name = volume['display_name'] vcloud_volume_name = self._get_vcloud_volume_name(volume['id'],volume_name) LOG.debug('Creating vcloud volume %(name)s of size %(size)s Gb', {'name': vcloud_volume_name, 'size': volume['size']}) self._vcloud_client.create_volume(vcloud_volume_name, volume['size']) def delete_volume(self, volume): """Delete a volume.""" volume_name = volume['display_name'] vcloud_volume_name = self._get_vcloud_volume_name(volume['id'], volume_name) LOG.debug('Deleting vcloud volume %s', vcloud_volume_name) self._vcloud_client.delete_volume(vcloud_volume_name) def _copy_volume_to_volume(self, dest_volume_name, dest_volume_size, dest_volume_id, source_volume_name, source_volume_size, source_volume_id, created = False, source_attached = False, dest_attached = False): """ """ def _attach_disks_to_vm(): for detached_disk in detached_disks: self._vcloud_client.attach_disk_to_vm(detached_disk['vapp_name'], detached_disk['disk_ref']) def _delete_volumes(): for created_volume in created_volumes: self._vcloud_client.delete_volume(created_volume) def _delete_vapp(): self._vcloud_client.delete_vapp(clone_vapp_name) def _detach_disks_to_vm(): for attached_disk_name in attached_disk_names: result, disk_ref = self._vcloud_client.get_disk_ref(attached_disk_name) if result: self._vcloud_client.detach_disk_from_vm(clone_vapp_name, disk_ref) def _power_off_vapp(): self._vcloud_client.power_off_vapp(clone_vapp_name) LOG.debug('copy volume to volume: dest: %s ID %s %s GB source: %s id: %s %s GB', dest_volume_name, dest_volume_id, dest_volume_size, source_volume_name, source_volume_id, source_volume_size) detached_disks = [] created_volumes = [] attached_disk_names =[] undo_mgr = util.UndoManager() try: if not created and dest_attached: msg = _('only created destination volume can be attached') LOG.error(msg) raise exception.CinderException(msg) result, source_disk_ref = self._vcloud_client.get_disk_ref(source_volume_name) if not result: msg = _('source volume %s cannot found') % source_volume_name LOG.error(msg) raise exception.CinderException(msg) if source_attached: source_vapp_name = self._vcloud_client.get_disk_attached_vapp(source_volume_name) source_vapp = self._vcloud_client._get_vcloud_vapp(source_vapp_name) if self._vcloud_client._get_status_first_vm(source_vapp) != constants.VM_POWER_OFF_STATUS: msg = "when source volume is attached, the vm must be in power off state" LOG.info(msg) raise exception.CinderException(msg) self._vcloud_client.detach_disk_from_vm(source_vapp_name, source_disk_ref) source_detached_disk = {} source_detached_disk['vapp_name'] = source_vapp_name source_detached_disk['disk_ref'] = source_disk_ref detached_disks.append(source_detached_disk) undo_mgr.undo_with(_attach_disks_to_vm) LOG.debug("source volume %s has been detached from vapp %s", source_volume_name, source_vapp_name) if not created: self._vcloud_client.create_volume(dest_volume_name, dest_volume_size) created_volumes.append(dest_volume_name) undo_mgr.undo_with(_delete_volumes) LOG.debug("dst volume %s(%s GB) has been created", dest_volume_name, dest_volume_size) result, dest_disk_ref = self._vcloud_client.get_disk_ref(dest_volume_name) if not result: msg = _('dest volume %s cannot found') % dest_volume_name LOG.error(msg) raise exception.CinderException(msg) if dest_attached: dest_vapp_name = self._vcloud_client.get_disk_attached_vapp(dest_volume_name) dest_vapp = self._vcloud_client._get_vcloud_vapp(dest_vapp_name) if self._vcloud_client._get_status_first_vm(dest_vapp) != constants.VM_POWER_OFF_STATUS: msg = "when dest volume is attached, the vm must be in power off state" LOG.info(msg) raise exception.CinderException(msg) self._vcloud_client.detach_disk_from_vm(dest_vapp_name, dest_disk_ref) dest_detached_disk = {} dest_detached_disk['vapp_name'] = dest_vapp_name dest_detached_disk['disk_ref'] = dest_disk_ref detached_disks.append(dest_detached_disk) if undo_mgr.count_func(_attach_disks_to_vm) == 0: undo_mgr.undo_with(_attach_disks_to_vm) LOG.debug("dst volume %s has been detached from vapp %s", dest_volume_name, dest_vapp_name) #NOTE(nkapotoxin): create vapp with vapptemplate network_names = [CONF.vcloud.provider_tunnel_network_name, CONF.vcloud.provider_base_network_name] network_configs = self._vcloud_client.get_network_configs(network_names) # create vapp clone_vapp_name = 'server@%s' % dest_volume_name clone_vapp = self._vcloud_client.create_vapp(clone_vapp_name, CONF.vcloud.base_image_id, network_configs) undo_mgr.undo_with(_delete_vapp) LOG.debug("Create clone vapp %s successful" % clone_vapp_name) # generate the network_connection network_connections = self._vcloud_client.get_network_connections(clone_vapp, network_names) # update network self._vcloud_client.update_vms_connections(clone_vapp, network_connections) # update vm specification #self._vcloud_client.modify_vm_cpu(clone_vapp, instance.get_flavor().vcpus) #self._vcloud_client.modify_vm_memory(clone_vapp, instance.get_flavor().memory_mb) LOG.debug("Config vapp %s successful" % clone_vapp_name) local_disk_name = 'Local@%s' % clone_vapp_name[len('server@'):] self._vcloud_client.create_volume(local_disk_name, 1) created_volumes.append(local_disk_name) if undo_mgr.count_func(_delete_volumes) == 0: undo_mgr.undo_with(_delete_volumes) LOG.debug("Create Local disk %s for vapp %s successful", local_disk_name, clone_vapp_name) result, local_disk_ref = self._vcloud_client.get_disk_ref(local_disk_name) self._vcloud_client.attach_disk_to_vm(clone_vapp_name, local_disk_ref) attached_disk_names.append(local_disk_name) undo_mgr.undo_with(_detach_disks_to_vm) LOG.debug("attach local disk %s to vapp %s successful", local_disk_name, clone_vapp_name) # power on it self._vcloud_client.power_on_vapp(clone_vapp_name) undo_mgr.undo_with(_power_off_vapp) vapp_ip = self.get_vapp_ip(clone_vapp_name) client = Client(vapp_ip, CONF.vcloud.hybrid_service_port) self._wait_hybrid_service_up(vapp_ip, CONF.vcloud.hybrid_service_port) LOG.debug("vapp %s(ip: %s) hybrid service has been up", clone_vapp_name, vapp_ip) odevs = set(client.list_volume()['devices']) if self._vcloud_client.attach_disk_to_vm(clone_vapp_name, source_disk_ref): attached_disk_names.append(source_volume_name) LOG.debug("Volume %s attached to: %s",source_volume_name, clone_vapp_name) ndevs = set(client.list_volume()['devices']) devs = ndevs - odevs for dev in devs: client.attach_volume(source_volume_id, dev, constants.DEV1) odevs = set(client.list_volume()['devices']) if self._vcloud_client.attach_disk_to_vm(clone_vapp_name, dest_disk_ref): attached_disk_names.append(dest_volume_name) LOG.debug("Volume %s attached to: %s", dest_volume_name, clone_vapp_name) ndevs = set(client.list_volume()['devices']) devs = ndevs - odevs for dev in devs: client.attach_volume(dest_volume_id, dev, constants.DEV2) LOG.debug('begin time of copy vloume(size %s GB) is %s', source_volume_size, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) dest_volume = {} dest_volume['id'] = dest_volume_id dest_volume['size'] = dest_volume_size src_volume = {} src_volume['id'] = source_volume_id src_volume['size'] = source_volume_size task = client.clone_volume(dest_volume, src_volume) while task['code'] == client_constants.TASK_DOING: eventlet.greenthread.sleep(30) task = client.query_task(task) if task['code'] != client_constants.TASK_SUCCESS: LOG.error(task['message']) raise exception.CinderException(task['message']) else: LOG.debug('end time of copy vloume is %s', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) undo_mgr.cancel_undo(_power_off_vapp) self._vcloud_client.power_off_vapp(clone_vapp_name) attached_disk_names.remove(source_volume_name) self._vcloud_client.detach_disk_from_vm(clone_vapp_name, source_disk_ref) if source_attached: detached_disks.remove(source_detached_disk) self._vcloud_client.attach_disk_to_vm(source_vapp_name, source_disk_ref) attached_disk_names.remove(local_disk_name) self._vcloud_client.detach_disk_from_vm(clone_vapp_name, local_disk_ref) attached_disk_names.remove(dest_volume_name) self._vcloud_client.detach_disk_from_vm(clone_vapp_name, dest_disk_ref) if dest_attached: detached_disks.remove(dest_detached_disk) self._vcloud_client.attach_disk_to_vm(dest_vapp_name, dest_disk_ref) undo_mgr.cancel_undo(_delete_vapp) self._vcloud_client.delete_vapp(clone_vapp_name) created_volumes.remove(local_disk_name) self._vcloud_client.delete_volume(local_disk_name) return True except Exception as e: msg = _("Failed to copy volume to volume reason %s, rolling back") % e LOG.error(msg) undo_mgr.rollback_and_reraise(msg=msg) return False def create_volume_from_snapshot(self, volume, snapshot): """Create a volume from a snapshot.""" LOG.debug('create volume from snapshot volume: %s snapshot: %s', vars(volume), vars(snapshot)) volume_name = volume['display_name'] vcloud_volume_name = self._get_vcloud_volume_name(volume['id'], volume_name) snapshot_name = snapshot['display_name'] vcloud_snapshot_volume_name = self._get_vcloud_volume_name(snapshot['id'], snapshot_name) result = self._copy_volume_to_volume(vcloud_volume_name, volume['size'], volume['id'], vcloud_snapshot_volume_name, snapshot['volume_size'], snapshot['id']) if result: LOG.debug('create volume from snapshot successful') else: LOG.debug('create volume from snapshot failed') def create_cloned_volume(self, volume, src_vref): """Create a clone of the specified volume.""" LOG.debug('create cloned volume:%s src_vref %s', vars(volume), vars(src_vref)) source_volume_name = src_vref['display_name'] vcloud_source_volume_name = self._get_vcloud_volume_name(src_vref['id'], source_volume_name) dest_volume_name = volume['display_name'] vcloud_dest_volume_name = self._get_vcloud_volume_name(volume['id'], dest_volume_name) if src_vref['volume_attachment']: source_attached = True else: source_attached = False result = self._copy_volume_to_volume(vcloud_dest_volume_name, volume['size'], volume['id'], vcloud_source_volume_name, src_vref['size'], src_vref['id'], source_attached = source_attached) if result: LOG.debug('create cloned volume successful') else: LOG.debug('create cloned volume failed') def extend_volume(self, volume, new_size): """Extend a volume.""" pass def create_snapshot(self, snapshot): """Create a snapshot.""" LOG.debug('create snapshot: %s', vars(snapshot)) source_volume_name = snapshot['volume']['display_name'] vcloud_source_volume_name = self._get_vcloud_volume_name(snapshot['volume_id'], source_volume_name) if snapshot['volume']['attach_status'] == 'attached': source_attached= True else: source_attached = False snapshot_name = snapshot['display_name'] vcloud_dest_volume_name = self._get_vcloud_volume_name(snapshot['id'], snapshot_name) result = self._copy_volume_to_volume(vcloud_dest_volume_name, snapshot['volume_size'], snapshot['id'], vcloud_source_volume_name, snapshot['volume_size'], snapshot['volume_id'], source_attached = source_attached) if result: LOG.debug('create snapshot successful') else: LOG.debug('create snapshot failed') def delete_snapshot(self, snapshot): """Delete a snapshot.""" snapshot_name = snapshot['display_name'] vcloud_snapshot_volume_name = self._get_vcloud_volume_name(snapshot['id'], snapshot_name) LOG.debug('Deleting volume %s', vcloud_snapshot_volume_name) self._vcloud_client.delete_volume(vcloud_snapshot_volume_name) def get_volume_stats(self, refresh=False): """Get volume stats.""" vdc = self._vcloud_client.vdc 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_vcloud' data = {'volume_backend_name': backend_name, 'vendor_name': 'Huawei', 'driver_version': self.VERSION, 'storage_protocol': 'LSI Logic SCSI', # xxx(wangfeng): get from vcloud 'reserved_percentage': 0, 'total_capacity_gb': 1000, 'free_capacity_gb': 1000} self._stats = data return self._stats 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 _query_vmdk_url(self, the_vapp): # node_name = instance.node # 0. shut down the app first # node_name = instance.node # 0. shut down the app first try: the_vapp = self._vcloud_client.power_off_vapp(the_vapp) except: LOG.error('power off failed') # 1.enable download. task = self._vcloud_client._invoke_vapp_api(the_vapp, 'enableDownload') if not task: raise exception.CinderException( "enable vmdk file download failed, task:") self._session._wait_for_task(task) # 2.get vapp info and ovf descriptor the_vapp = self._vcloud_client._get_vcloud_vapp(the_vapp.name) # the_vapp = self._vcloud_client._invoke_vapp_api(the_vapp, 'get_updated_vapp') ovf = self._vcloud_client._invoke_vapp_api(the_vapp, 'get_ovf_descriptor') # 3.get referenced file url referenced_file_url = self._vcloud_client._invoke_vapp_api(the_vapp, 'get_referenced_file_url',ovf) if not referenced_file_url: raise exception.CinderException("get vmdk file url failed") return referenced_file_url def _download_vmdk_from_vcloud(self,context, src_url,dst_file_name): local_file_handle = open(dst_file_name, "wb") remote_file_handle = urllib2.urlopen(src_url) file_size = remote_file_handle.headers['content-length'] util.start_transfer(context, IMAGE_TRANSFER_TIMEOUT_SECS,remote_file_handle, file_size, write_file_handle=local_file_handle) def _attach_volume_to_vgw(self, volume): volume_name = volume['display_name'] vcloud_volume_name = self._get_vcloud_volume_name(volume['id'], volume_name) # get the provider_volume at provider cloud # find volume reference by it's name result, disk_ref = self._vcloud_client.get_disk_ref(vcloud_volume_name) if result: LOG.debug("Find volume successful, disk name is: %(disk_name)s " "disk ref's href is: %(disk_href)s.", {'disk_name': vcloud_volume_name, 'disk_href': disk_ref.href}) else: LOG.error(_('Unable to find volume %s'), vcloud_volume_name) raise exception.VolumeNotFound(volume_id=vcloud_volume_name) # Check whether the volume is attached to vm or not, # Make sure the volume is available vpp_name = self._vcloud_client.get_disk_attached_vapp(vcloud_volume_name) if vpp_name: self._vcloud_client.detach_disk_from_vm(vpp_name, disk_ref) # get the vgw host vapp_name = self._vgw_name # attach volume to vgw when the vgw is in stopped status if self._vcloud_client.attach_disk_to_vm(vapp_name, disk_ref): LOG.info("Volume %(volume_name)s attached to " "vgw host: %(instance_name)s", {'volume_name': vcloud_volume_name, 'instance_name': vapp_name}) return disk_ref, vapp_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') @RetryDecorator(max_retry_count=CONF.vcloud.vcloud_api_retry_count, exceptions=(sshclient.SSHError, sshclient.SSHTimeout)) def _copy_volume_to_file_to_vgw(self, image_meta): try: image_id = image_meta.get('id') image_name = image_meta.get('name') container_format = image_meta.get('container_format') dest_file_path = os.path.join('/tmp', image_id) ssh_client = sshclient.SSH(user=self._vgw_username, host=self._vgw_host, password=self._vgw_password) cmd = '/usr/bin/rescan-scsi-bus.sh -a -r' ssh_client.run(cmd) # convert volume to image cmd = 'qemu-img convert -c -O qcow2 %s %s' %\ ('/dev/sdb', 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() ))) # push the converted image to remote vgw host # vgw_url = CONF.vgw.vcloud_vgw_url[container_format] kwargs = {'auth_url': CONF.keystone_authtoken.keystone_auth_url, 'tenant_name': CONF.keystone_authtoken.tenant_name, 'username': CONF.keystone_authtoken.user_name, 'password': CONF.keystone_authtoken.admin_password, 'insecure': True} keystoneclient = kc.Client(**kwargs) vgw_url = '' vgw_url = self._get_management_url(keystoneclient, image_name, service_type='v2v') 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) 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.run('rm -f %s' % dest_file_path) ssh_client.close() @utils.synchronized("vcloud_volume_copy_lock", external=True) def copy_volume_to_image(self, context, volume, image_service, image_meta): """Creates glance image from volume.""" def _delete_vapp(): self._vcloud_client.delete_vapp(clone_vapp_name) def _detach_disks_to_vm(): for attached_disk_name in attached_disk_names: result, disk_ref = self._vcloud_client.get_disk_ref(attached_disk_name) if result: self._vcloud_client.detach_disk_from_vm(clone_vapp_name, disk_ref) def _power_off_vapp(): self._vcloud_client.power_off_vapp(clone_vapp_name) undo_mgr = util.UndoManager() attached_disk_names = [] try: LOG.info('begin time of copy_volume_to_image is %s' % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))) container_format = image_meta.get('container_format') if container_format in VGW_URLS: # attach the volume to vgw vm disk_ref, vapp_name = self._attach_volume_to_vgw(volume) try: # use ssh client connect to vgw_host and copy image file to volume self._copy_volume_to_file_to_vgw(image_meta) finally: self._vcloud_client.detach_disk_from_vm(vapp_name, disk_ref) # create an empty file to glance with image_utils.temporary_file() as tmp: image_utils.upload_volume(context, image_service, image_meta, tmp) elif container_format == constants.HYBRID_VM: volume_name = volume['display_name'] vcloud_volume_name = self._get_vcloud_volume_name(volume['id'], volume_name) result,disk_ref = self._vcloud_client.get_disk_ref(vcloud_volume_name) if not result: msg = 'can not find volume %s in vcloud!' % vcloud_volume_name LOG.error(msg) raise exception.CinderException(msg) if volume['volume_attachment']: msg = 'copy volume to image not support attached volume!' LOG.error(msg) raise exception.CinderException(msg) #NOTE(nkapotoxin): create vapp with vapptemplate network_names = [CONF.vcloud.provider_tunnel_network_name, CONF.vcloud.provider_base_network_name] network_configs = self._vcloud_client.get_network_configs(network_names) # create vapp clone_vapp_name = 'server@%s' % image_meta['id'] clone_vapp = self._vcloud_client.create_vapp(clone_vapp_name, CONF.vcloud.base_image_id, network_configs) undo_mgr.undo_with(_delete_vapp) LOG.debug("Create clone vapp %s successful" % clone_vapp_name) # generate the network_connection network_connections = self._vcloud_client.get_network_connections(clone_vapp, network_names) # update network self._vcloud_client.update_vms_connections(clone_vapp, network_connections) # update vm specification #self._vcloud_client.modify_vm_cpu(clone_vapp, instance.get_flavor().vcpus) #self._vcloud_client.modify_vm_memory(clone_vapp, instance.get_flavor().memory_mb) LOG.debug("Config vapp %s successful" % clone_vapp_name) if self._vcloud_client.attach_disk_to_vm(clone_vapp_name, disk_ref): attached_disk_names.append(vcloud_volume_name) undo_mgr.undo_with(_detach_disks_to_vm) LOG.debug("Volume %s attached to: %s", vcloud_volume_name, clone_vapp_name) # power on it self._vcloud_client.power_on_vapp(clone_vapp_name) undo_mgr.undo_with(_power_off_vapp) vapp_ip = self.get_vapp_ip(clone_vapp_name) client = Client(vapp_ip, CONF.vcloud.hybrid_service_port) self._wait_hybrid_service_up(vapp_ip, CONF.vcloud.hybrid_service_port) LOG.debug("vapp %s(ip: %s) hybrid service has been up", clone_vapp_name, vapp_ip) LOG.debug('begin time of create image is %s' % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) task = client.create_image(image_meta['name'], image_meta['id']) while task['code'] == client_constants.TASK_DOING: eventlet.greenthread.sleep(10) task = client.query_task(task) if task['code'] != client_constants.TASK_SUCCESS: LOG.error(task['message']) raise exception.CinderException(task['message']) else: LOG.debug('end time of create image is %s' % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) image_info = client.image_info(image_meta['name'], image_meta['id']) if not image_info: LOG.error('cannot get image info') raise exception.CinderException('cannot get image info') LOG.debug('begin time of image_utils upload_volume %s' % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) with image_utils.temporary_file() as tmp: with fileutils.file_open(tmp, 'wb+') as f: f.truncate(image_info['size']) image_utils.upload_volume(context, image_service, image_meta, tmp, volume_format=image_meta['disk_format']) LOG.debug('end time of image_utils upload_volume %s' % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) undo_mgr.cancel_undo(_power_off_vapp) self._vcloud_client.power_off_vapp(clone_vapp_name) attached_disk_names.remove(vcloud_volume_name) self._vcloud_client.detach_disk_from_vm(clone_vapp_name, disk_ref) undo_mgr.cancel_undo(_delete_vapp) self._vcloud_client.delete_vapp(clone_vapp_name) LOG.info('end time of copy_volume_to_image is %s' % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))) except Exception as e: msg = _("Failed to copy volume to image reason %s, rolling back") % e LOG.error(msg) undo_mgr.rollback_and_reraise(msg=msg) @RetryDecorator(max_retry_count=CONF.vcloud.vcloud_api_retry_count, exceptions=(sshclient.SSHError, sshclient.SSHTimeout)) def _copy_file_to_volume_from_vgw(self, image_id): try: dest_file_path = os.path.join(self._vgw_store_file_dir, image_id) ssh_client = sshclient.SSH(user=self._vgw_username, host=self._vgw_host, password=self._vgw_password) cmd = '/usr/bin/rescan-scsi-bus.sh -a -r' ssh_client.run(cmd) # copy data to volume # TODO(luqitao): need to get device name, does not use sdb. # TODO(luqitao): check the dest_file does exist or not? cmd = 'qemu-img convert %s %s' %\ (dest_file_path, '/dev/sdb') 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() @utils.synchronized("vcloud_volume_copy_lock", external=True) def copy_image_to_volume(self, context, volume, image_service, image_id): """Creates volume from image.""" LOG.info('begin time of copy_image_to_volume is %s' % (time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime()))) image_meta = image_service.show(context, image_id) container_format = image_meta.get('container_format') if container_format in VGW_URLS: disk_ref, vapp_name = self._attach_volume_to_vgw(volume) # start the vgw, so it can recognize the volume # (vcloud does not support online attach or detach volume) # self._vcloud_client.power_on_vapp(the_vapp) try: # use ssh client connect to vgw_host and # copy image file to volume LOG.debug('begin time of _copy_file_to_volume_from_vgw is %s' % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))) self._copy_file_to_volume_from_vgw(image_id) LOG.debug('end time of _copy_file_to_volume_from_vgw is %s' % (time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))) finally: # detach volume from vgw and self._vcloud_client.detach_disk_from_vm(vapp_name, disk_ref) # shutdown the vgw, do some clean env work # self._vcloud_client.power_off_vapp(the_vapp) elif container_format == constants.HYBRID_VM: #if container formate eq hybrivm, doing nothing pass LOG.info('end time of copy_image_to_volume is %s' % (time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime()))) def backup_volume(self, context, backup, backup_service): """Create a new backup from an existing volume.""" LOG.debug("backup volume: backup:%s backup_service:%s", vars(backup), vars(backup_service)) if backup_service.DRIVER_NAME == 'VCloudBackupDriver': volume = self.db.volume_get(context, backup['volume_id']) vcloud_volume_name = self._get_vcloud_volume_name(volume['id'], volume['display_name']) if volume['volume_attachment']: source_attached = True else: source_attached = False backup_name = backup['display_name'] vcloud_backup_volume_name = self._get_vcloud_volume_name(backup['id'], backup_name) result = self._copy_volume_to_volume(vcloud_backup_volume_name, backup['size'], backup['id'], vcloud_volume_name, volume['size'], volume['id'], source_attached = source_attached) if result: LOG.debug("backup volume successful") else: LOG.debug("backup volume failed") else: super(VCloudVolumeDriver, self).backup_volume(context, backup, backup_service) def restore_backup(self, context, backup, volume, backup_service): """Restore an existing backup to a new or existing volume.""" LOG.debug("restore_backup backup:%s volume:%s backup_service:%s", vars(backup), vars(volume), vars(backup_service)) if backup_service.DRIVER_NAME == 'VCloudBackupDriver': vcloud_volume_name = self._get_vcloud_volume_name(volume['id'], volume['display_name']) backup_name = backup['display_name'] vcloud_backup_volume_name = self._get_vcloud_volume_name(backup['id'], backup_name) if volume['volume_attachment']: dest_attached = True else: dest_attached = False result = self._copy_volume_to_volume(vcloud_volume_name, volume['size'], volume['id'], vcloud_backup_volume_name, backup['size'], backup['id'], created = True, dest_attached = dest_attached) if result: LOG.debug("restore backup successful") else: LOG.debug("restore backup failed") else: super(VCloudVolumeDriver, self).restore_backup(context, backup, volume, backup_service) def initialize_connection(self, volume, connector): """Allow connection to connector and return connection info.""" LOG.debug('vCloud Driver: initialize_connection') driver_volume_type = 'vcloud_volume' data = {} data['backend'] = 'vcloud' data['volume_id'] = volume['id'] data['display_name'] = volume['display_name'] return {'driver_volume_type': driver_volume_type, 'data': data} 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 @_retry_decorator(max_retry_count=60,exceptions = (errors.APIError,errors.NotFound)) def get_vapp_ip(self, vapp_name): return self._vcloud_client.get_vapp_ip(vapp_name) @_retry_decorator(max_retry_count=60,exceptions=(errors.APIError,errors.NotFound, errors.ConnectionError, errors.InternalError)) def _wait_hybrid_service_up(self, server_ip, port = '7127'): client = Client(server_ip, port = port) return client.get_version()