def migrate_background( domain, source, destination, migrate_params, migrate_flags, ): # As it seems it is possible to call multiple functions in parallel # from different threads. try: domain.migrateToURI3( MIGRATE_CONFIG.get((source.dataset_obj['os'], destination.dataset_obj['os']))['uri'].format( destination=destination.fqdn), migrate_params, migrate_flags, ) except libvirtError as e: if virGetLastError()[0] == VIR_ERR_OPERATION_ABORTED: raise MigrationAborted('Migration aborted by user') raise MigrationError(e)
def migrate_live(source, destination, vm, domain): """Live-migrates a VM via libvirt.""" # Reduce CPU pinning to minimum number of available cores on both # hypervisors to avoid "invalid cpuset" errors. props = DomainProperties.from_running(source, vm, domain) _live_repin_cpus( domain, props, min(source.dataset_obj['num_cpu'], destination.dataset_obj['num_cpu']), ) migrate_flags = ( VIR_MIGRATE_LIVE | # Do it live VIR_MIGRATE_PERSIST_DEST | # Define the VM on the new host VIR_MIGRATE_CHANGE_PROTECTION | # Protect source VM VIR_MIGRATE_NON_SHARED_DISK | # Copy non-shared storage VIR_MIGRATE_AUTO_CONVERGE | # Slow down VM if can't migrate memory VIR_MIGRATE_ABORT_ON_ERROR # Don't tolerate soft errors ) migrate_params = {} # Append OS-specific migration commands. They might not exist for some # combinations but this should have already been checked by the caller. migrate_flags |= MIGRATE_CONFIG.get( (source.dataset_obj['os'], destination.dataset_obj['os']))['flags'] log.info('Starting online migration of vm {} from {} to {}'.format( vm, source, destination, )) future = parallel( migrate_background, args=[[ domain, source, destination, migrate_params, migrate_flags, ]], workers=1, return_results=False, )[0] try: while future.running(): try: js = domain.jobStats() except libvirtError: # When migration is finished, jobStats will fail break if 'memory_total' in js and 'disk_total' in js: log.info( ('Migration progress: ' 'disk {:.0f}% {:.0f}/{:.0f}MiB, ' 'memory {:.0f}% {:.0f}/{:.0f}MiB, ').format( js['disk_processed'] / (js['disk_total'] + 1) * 100, js['disk_processed'] / 1024 / 1024, js['disk_total'] / 1024 / 1024, js['memory_processed'] / (js['memory_total'] + 1) * 100, js['memory_processed'] / 1024 / 1024, js['memory_total'] / 1024 / 1024, )) else: log.info('Waiting for migration stats to show up') time.sleep(1) except KeyboardInterrupt: domain.abortJob() log.info('Awaiting migration to abort') future.result() # Nothing to log, the function above raised an exception else: log.info('Awaiting migration to finish') future.result() # Exception from slave thread will re-raise here log.info('Migration finished') # And pin again, in case we migrated to a host with more physical cores domain = destination._get_domain(vm) _live_repin_cpus(domain, props, destination.dataset_obj['num_cpu'])