def merge(self, driveSpec, base, top, bandwidth, job_id): bandwidth = int(bandwidth) if job_id is None: job_id = str(uuid.uuid4()) try: drive = self._vm.findDriveByUUIDs(driveSpec) except LookupError: raise exception.ImageFileNotFound("Cannot find drive", driveSpec=driveSpec, job=job_id) job = self._create_job(job_id, drive, base, top, bandwidth) try: base_info = self._vm.getVolumeInfo(drive.domainID, drive.poolID, drive.imageID, job.base) top_info = self._vm.getVolumeInfo(drive.domainID, drive.poolID, drive.imageID, job.top) except errors.StorageUnavailableError as e: raise exception.MergeFailed(str(e), top=top, base=job.base, job=job_id) if base_info['voltype'] == 'SHARED': raise exception.MergeFailed("Base volume is shared", base_info=base_info, job=job_id) self._validate_base_size(drive, base_info, top_info) if self._base_needs_refresh(drive, base_info): self._refresh_base(drive, base_info) with self._lock: try: self._track_job(job, drive) except JobExistsError as e: raise exception.MergeFailed(str(e), job=job.id) if self._base_needs_extend(drive, job, base_info): job.extend = { "attempt": 1, "base_size": int(base_info["apparentsize"]), "top_size": int(top_info["apparentsize"]), "capacity": int(top_info["capacity"]), } self._start_extend(drive, job) else: self._start_commit(drive, job) # Trigger the collection of stats before returning so that callers # of getVmStats after this returns will see the new job self._vm.updateVmJobs()
def _start_commit(self, drive, job): """ Start libvirt blockCommit block job. Must be called under self._lock. """ # Persist the job before starting the commit, to ensure that vdsm will # know about the commit if it was killed after the block job was # started. job.state = Job.COMMIT self._persist_jobs() # Check that libvirt exposes full volume chain information actual_chain = self._vm.query_drive_volume_chain(drive) if actual_chain is None: self._untrack_job(job.id) raise exception.MergeFailed("Cannot get drive actual volume chain", drive=drive.name, alias=drive.alias, job=job.id) try: base_target = drive.volume_target(job.base, actual_chain) top_target = drive.volume_target(job.top, actual_chain) except VolumeNotFound as e: self._untrack_job(job.id) raise exception.MergeFailed(str(e), top=job.top, base=job.base, chain=actual_chain, job=job.id) # Indicate that we expect libvirt to maintain the relative paths of # backing files. This is necessary to ensure that a volume chain is # visible from any host even if the mountpoint is different. flags = libvirt.VIR_DOMAIN_BLOCK_COMMIT_RELATIVE if job.top == drive.volumeID: # Pass a flag to libvirt to indicate that we expect a two phase # block job. In the first phase, data is copied to base. Once # completed, an event is raised to indicate that the job has # transitioned to the second phase. We must then tell libvirt to # pivot to the new active layer (base). flags |= libvirt.VIR_DOMAIN_BLOCK_COMMIT_ACTIVE orig_chain = [entry.uuid for entry in actual_chain] chain_str = logutils.volume_chain_to_str(orig_chain) log.info( "Starting merge with job_id=%r, original chain=%s, " "disk=%r, base=%r, top=%r, bandwidth=%d, flags=%d", job.id, chain_str, drive.name, base_target, top_target, job.bandwidth, flags) try: # pylint: disable=no-member self._dom.blockCommit(drive.name, base_target, top_target, job.bandwidth, flags=flags) except libvirt.libvirtError as e: self._untrack_job(job.id) raise exception.MergeFailed(str(e), job=job.id)
# codes 20-35 are reserved for add/delNetwork # code 39 was used for: # wrongHost - migration destination has an invalid hostname 'unavail': exception.ResourceUnavailable().response(), 'changeDisk': exception.ChangeDiskFailed().response(), 'destroyErr': exception.VMDestroyFailed().response(), 'fenceAgent': exception.UnsupportedFenceAgent().response(), 'noimpl': exception.MethodNotImplemented().response(), 'hotplugDisk': exception.HotplugDiskFailed().response(), 'hotunplugDisk': exception.HotunplugDiskFailed().response(), 'migCancelErr': exception.MigrationCancelationFailed().response(), 'snapshotErr': exception.SnapshotFailed().response(), 'hotplugNic': exception.HotplugNicFailed().response(), 'hotunplugNic': exception.HotunplugNicFailed().response(), 'migInProgress': exception.MigrationInProgress().response(), 'mergeErr': exception.MergeFailed().response(), 'balloonErr': exception.BalloonError().response(), 'momErr': exception.MOMPolicyUpdateFailed().response(), 'replicaErr': exception.ReplicaError().response(), 'updateDevice': exception.UpdateDeviceFailed().response(), 'hwInfoErr': exception.CannotRetrieveHWInfo().response(), 'resizeErr': exception.BadDiskResizeParameter().response(), 'transientErr': exception.TransientError().response(), 'setNumberOfCpusErr': exception.SetNumberOfCpusFailed().response(), 'haErr': exception.SetHAPolicyFailed().response(), 'cpuTuneErr': exception.CpuTuneError().response(), 'updateVmPolicyErr': exception.UpdateVMPolicyFailed().response(), 'updateIoTuneErr': exception.UpdateIOTuneError().response(), 'V2VConnection': exception.V2VConnectionError().response(), 'NoSuchJob': exception.NoSuchJob().response(), 'V2VNoSuchOvf': exception.V2VNoSuchOVF().response(),