Search in sources :

Example 6 with LibvirtDiskDef

use of com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef in project cosmic by MissionCriticalCloud.

the class LibvirtStopCommandWrapper method execute.

@Override
public Answer execute(final StopCommand command, final LibvirtComputingResource libvirtComputingResource) {
    final String vmName = command.getVmName();
    final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
    if (command.checkBeforeCleanup()) {
        try {
            final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
            final Domain vm = conn.domainLookupByName(command.getVmName());
            if (vm != null && vm.getInfo().state == DomainState.VIR_DOMAIN_RUNNING) {
                return new StopAnswer(command, "vm is still running on host", false);
            }
        } catch (final Exception e) {
            s_logger.debug("Failed to get vm status in case of checkboforecleanup is true", e);
        }
    }
    try {
        final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
        final List<LibvirtDiskDef> disks = libvirtComputingResource.getDisks(conn, vmName);
        final List<InterfaceDef> ifaces = libvirtComputingResource.getInterfaces(conn, vmName);
        final String result = libvirtComputingResource.stopVm(conn, vmName, command.isForceStop());
        if (result == null) {
            for (final LibvirtDiskDef disk : disks) {
                libvirtComputingResource.cleanupDisk(disk);
            }
            for (final InterfaceDef iface : ifaces) {
                // each interface at this point, so inform all vif drivers
                for (final VifDriver vifDriver : libvirtComputingResource.getAllVifDrivers()) {
                    vifDriver.unplug(iface);
                }
            }
        }
        return new StopAnswer(command, result, true);
    } catch (final LibvirtException e) {
        return new StopAnswer(command, e.getMessage(), false);
    }
}
Also used : InterfaceDef(com.cloud.hypervisor.kvm.resource.LibvirtVmDef.InterfaceDef) LibvirtDiskDef(com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef) LibvirtException(org.libvirt.LibvirtException) Connect(org.libvirt.Connect) Domain(org.libvirt.Domain) StopAnswer(com.cloud.agent.api.StopAnswer) LibvirtException(org.libvirt.LibvirtException) VifDriver(com.cloud.hypervisor.kvm.resource.VifDriver)

Example 7 with LibvirtDiskDef

use of com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef in project cosmic by MissionCriticalCloud.

the class KvmStorageProcessor method attachOrDetachDisk.

protected synchronized String attachOrDetachDisk(final Connect conn, final boolean attach, final String vmName, final KvmPhysicalDisk attachingDisk, final int devId, final String serial) throws LibvirtException, InternalErrorException {
    List<LibvirtDiskDef> disks = null;
    Domain dm = null;
    LibvirtDiskDef diskdef = null;
    final KvmStoragePool attachingPool = attachingDisk.getPool();
    try {
        dm = conn.domainLookupByName(vmName);
        final LibvirtDomainXmlParser parser = new LibvirtDomainXmlParser();
        final String domXml = dm.getXMLDesc(0);
        parser.parseDomainXml(domXml);
        disks = parser.getDisks();
        if (!attach) {
            for (final LibvirtDiskDef disk : disks) {
                final String file = disk.getDiskPath();
                if (file != null && file.equalsIgnoreCase(attachingDisk.getPath())) {
                    diskdef = disk;
                    break;
                }
            }
            if (diskdef == null) {
                throw new InternalErrorException("disk: " + attachingDisk.getPath() + " is not attached before");
            }
        } else {
            LibvirtDiskDef.DiskBus diskBusType = LibvirtDiskDef.DiskBus.VIRTIO;
            for (final LibvirtDiskDef disk : disks) {
                logger.debug("disk is type : " + disk.toString());
                if (disk.getDeviceType() == LibvirtDiskDef.DeviceType.DISK) {
                    if (disk.getBusType() == LibvirtDiskDef.DiskBus.SCSI) {
                        diskBusType = LibvirtDiskDef.DiskBus.SCSI;
                    }
                    logger.debug("Disk bus type: " + disk.getDeviceType().toString() + ", diskBusType: " + diskBusType.toString());
                    break;
                }
            }
            diskdef = new LibvirtDiskDef();
            if (diskBusType == LibvirtDiskDef.DiskBus.SCSI) {
                diskdef.setQemuDriver(true);
                diskdef.setDiscard(LibvirtDiskDef.DiscardType.UNMAP);
            }
            diskdef.setSerial(serial);
            if (attachingPool.getType() == StoragePoolType.RBD) {
                diskdef.defNetworkBasedDisk(attachingDisk.getPath(), attachingPool.getSourceHost(), attachingPool.getSourcePort(), attachingPool.getAuthUserName(), attachingPool.getUuid(), devId, diskBusType, LibvirtDiskDef.DiskProtocol.RBD, LibvirtDiskDef.DiskFmtType.RAW);
            } else if (attachingPool.getType() == StoragePoolType.Gluster) {
                final String mountpoint = attachingPool.getLocalPath();
                final String path = attachingDisk.getPath();
                final String glusterVolume = attachingPool.getSourceDir().replace("/", "");
                diskdef.defNetworkBasedDisk(glusterVolume + path.replace(mountpoint, ""), attachingPool.getSourceHost(), attachingPool.getSourcePort(), null, null, devId, diskBusType, LibvirtDiskDef.DiskProtocol.GLUSTER, LibvirtDiskDef.DiskFmtType.QCOW2);
            } else if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
                diskdef.defFileBasedDisk(attachingDisk.getPath(), devId, diskBusType, LibvirtDiskDef.DiskFmtType.QCOW2);
            } else if (attachingDisk.getFormat() == PhysicalDiskFormat.RAW) {
                diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId, diskBusType);
            }
        }
        final String xml = diskdef.toString();
        return attachOrDetachDevice(conn, attach, vmName, xml);
    } finally {
        if (dm != null) {
            dm.free();
        }
    }
}
Also used : LibvirtDiskDef(com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef) LibvirtDomainXmlParser(com.cloud.hypervisor.kvm.resource.LibvirtDomainXmlParser) InternalErrorException(com.cloud.exception.InternalErrorException) Domain(org.libvirt.Domain)

Example 8 with LibvirtDiskDef

use of com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef in project cosmic by MissionCriticalCloud.

the class KvmStorageProcessor method attachOrDetachIso.

protected synchronized String attachOrDetachIso(final Connect conn, final String vmName, String isoPath, final boolean isAttach) throws LibvirtException, URISyntaxException, InternalErrorException {
    String isoXml = null;
    if (isoPath != null && isAttach) {
        final int index = isoPath.lastIndexOf("/");
        final String path = isoPath.substring(0, index);
        final String name = isoPath.substring(index + 1);
        final KvmStoragePool secondaryPool = storagePoolMgr.getStoragePoolByUri(path);
        final KvmPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
        isoPath = isoVol.getPath();
        final LibvirtDiskDef iso = new LibvirtDiskDef();
        iso.defIsoDisk(isoPath);
        isoXml = iso.toString();
    } else {
        final LibvirtDiskDef iso = new LibvirtDiskDef();
        iso.defIsoDisk(null);
        isoXml = iso.toString();
    }
    final List<LibvirtDiskDef> disks = resource.getDisks(conn, vmName);
    final String result = attachOrDetachDevice(conn, true, vmName, isoXml);
    if (result == null && !isAttach) {
        for (final LibvirtDiskDef disk : disks) {
            if (disk.getDeviceType() == LibvirtDiskDef.DeviceType.CDROM) {
                resource.cleanupDisk(disk);
            }
        }
    }
    return result;
}
Also used : LibvirtDiskDef(com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef)

Example 9 with LibvirtDiskDef

use of com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef in project cosmic by MissionCriticalCloud.

the class LibvirtComputingResource method createVbd.

public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVmDef vm) throws InternalErrorException, LibvirtException, URISyntaxException {
    final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
    Collections.sort(disks, new Comparator<DiskTO>() {

        @Override
        public int compare(final DiskTO arg0, final DiskTO arg1) {
            return arg0.getDiskSeq() > arg1.getDiskSeq() ? 1 : -1;
        }
    });
    for (final DiskTO volume : disks) {
        KvmPhysicalDisk physicalDisk = null;
        KvmStoragePool pool = null;
        final DataTO data = volume.getData();
        if (volume.getType() == Volume.Type.ISO && data.getPath() != null) {
            final NfsTO nfsStore = (NfsTO) data.getDataStore();
            final String volPath = nfsStore.getUrl() + File.separator + data.getPath();
            final int index = volPath.lastIndexOf("/");
            final String volDir = volPath.substring(0, index);
            final String volName = volPath.substring(index + 1);
            final KvmStoragePool secondaryStorage = storagePoolMgr.getStoragePoolByUri(volDir);
            physicalDisk = secondaryStorage.getPhysicalDisk(volName);
        } else if (volume.getType() != Volume.Type.ISO) {
            final PrimaryDataStoreTO store = (PrimaryDataStoreTO) data.getDataStore();
            physicalDisk = storagePoolMgr.getPhysicalDisk(store.getPoolType(), store.getUuid(), data.getPath());
            pool = physicalDisk.getPool();
        }
        String volPath = null;
        if (physicalDisk != null) {
            volPath = physicalDisk.getPath();
        }
        // check for disk activity, if detected we should exit because vm is running elsewhere
        if (diskActivityCheckEnabled && physicalDisk != null && physicalDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
            logger.debug("Checking physical disk file at path " + volPath + " for disk activity to ensure vm is not running elsewhere");
            try {
                HypervisorUtils.checkVolumeFileForActivity(volPath, diskActivityCheckTimeoutSeconds, diskActivityInactiveThresholdMilliseconds, diskActivityCheckFileSizeMin);
            } catch (final IOException ex) {
                throw new CloudRuntimeException("Unable to check physical disk file for activity", ex);
            }
            logger.debug("Disk activity check cleared");
        }
        // if params contains a rootDiskController key, use its value (this is what other HVs are doing)
        LibvirtDiskDef.DiskBus diskBusType = getDiskModelFromVmDetail(vmSpec);
        if (diskBusType == null) {
            diskBusType = getGuestDiskModel(vmSpec.getPlatformEmulator());
            logger.debug("disk bus type for " + vmName + " derived from getPlatformEmulator: " + vmSpec.getPlatformEmulator() + ", diskbustype is: " + diskBusType.toString());
        }
        final LibvirtDiskDef disk = new LibvirtDiskDef();
        if (volume.getType() == Volume.Type.ISO) {
            if (volPath == null) {
                /* Add iso as placeholder */
                disk.defIsoDisk(null);
            } else {
                disk.defIsoDisk(volPath);
            }
        } else {
            final int devId = volume.getDiskSeq().intValue();
            if (diskBusType == LibvirtDiskDef.DiskBus.SCSI) {
                disk.setQemuDriver(true);
                disk.setDiscard(DiscardType.UNMAP);
            }
            if (pool.getType() == StoragePoolType.RBD) {
                /*
                     * For RBD pools we use the secret mechanism in libvirt. We store the secret under the UUID of the pool,
                     * that's why we pass the pool's UUID as the authSecret
                     */
                disk.defNetworkBasedDisk(physicalDisk.getPath().replace("rbd:", ""), pool.getSourceHost(), pool.getSourcePort(), pool.getAuthUserName(), pool.getUuid(), devId, diskBusType, DiskProtocol.RBD, LibvirtDiskDef.DiskFmtType.RAW);
            } else if (pool.getType() == StoragePoolType.Gluster) {
                final String mountpoint = pool.getLocalPath();
                final String path = physicalDisk.getPath();
                final String glusterVolume = pool.getSourceDir().replace("/", "");
                disk.defNetworkBasedDisk(glusterVolume + path.replace(mountpoint, ""), pool.getSourceHost(), pool.getSourcePort(), null, null, devId, diskBusType, DiskProtocol.GLUSTER, LibvirtDiskDef.DiskFmtType.QCOW2);
            } else if (pool.getType() == StoragePoolType.CLVM || physicalDisk.getFormat() == PhysicalDiskFormat.RAW) {
                disk.defBlockBasedDisk(physicalDisk.getPath(), devId, diskBusType);
            } else {
                if (volume.getType() == Volume.Type.DATADISK) {
                    disk.defFileBasedDisk(physicalDisk.getPath(), devId, LibvirtDiskDef.DiskBus.VIRTIO, LibvirtDiskDef.DiskFmtType.QCOW2);
                } else {
                    disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, LibvirtDiskDef.DiskFmtType.QCOW2);
                }
            }
        }
        if (data instanceof VolumeObjectTO) {
            final VolumeObjectTO volumeObjectTo = (VolumeObjectTO) data;
            disk.setSerial(volumeObjectTo.getDeviceId() + "-" + diskUuidToSerial(volumeObjectTo.getUuid()));
            if (volumeObjectTo.getBytesReadRate() != null && volumeObjectTo.getBytesReadRate() > 0) {
                disk.setBytesReadRate(volumeObjectTo.getBytesReadRate());
            }
            if (volumeObjectTo.getBytesWriteRate() != null && volumeObjectTo.getBytesWriteRate() > 0) {
                disk.setBytesWriteRate(volumeObjectTo.getBytesWriteRate());
            }
            if (volumeObjectTo.getIopsReadRate() != null && volumeObjectTo.getIopsReadRate() > 0) {
                disk.setIopsReadRate(volumeObjectTo.getIopsReadRate());
            }
            if (volumeObjectTo.getIopsWriteRate() != null && volumeObjectTo.getIopsWriteRate() > 0) {
                disk.setIopsWriteRate(volumeObjectTo.getIopsWriteRate());
            }
            if (volumeObjectTo.getCacheMode() != null) {
                disk.setCacheMode(LibvirtDiskDef.DiskCacheMode.valueOf(volumeObjectTo.getCacheMode().toString().toUpperCase()));
            }
        }
        logger.debug("Adding disk: " + disk.toString());
        vm.getDevices().addDevice(disk);
    }
    if (vmSpec.getType() != VirtualMachine.Type.User) {
        final String sysvmIsoPath = getSysvmIsoPath();
        if (sysvmIsoPath != null) {
            final LibvirtDiskDef iso = new LibvirtDiskDef();
            iso.defIsoDisk(sysvmIsoPath);
            vm.getDevices().addDevice(iso);
        }
    }
}
Also used : KvmStoragePool(com.cloud.hypervisor.kvm.storage.KvmStoragePool) IOException(java.io.IOException) NfsTO(com.cloud.agent.api.to.NfsTO) DataTO(com.cloud.agent.api.to.DataTO) LibvirtDiskDef(com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef) PrimaryDataStoreTO(com.cloud.storage.to.PrimaryDataStoreTO) CloudRuntimeException(com.cloud.utils.exception.CloudRuntimeException) VolumeObjectTO(com.cloud.storage.to.VolumeObjectTO) KvmPhysicalDisk(com.cloud.hypervisor.kvm.storage.KvmPhysicalDisk) DiskTO(com.cloud.agent.api.to.DiskTO)

Example 10 with LibvirtDiskDef

use of com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef in project cosmic by MissionCriticalCloud.

the class LibvirtMigrateCommandWrapper method execute.

@Override
public Answer execute(final MigrateCommand command, final LibvirtComputingResource libvirtComputingResource) {
    final String vmName = command.getVmName();
    String result = null;
    List<InterfaceDef> ifaces = null;
    List<LibvirtDiskDef> disks;
    Domain dm = null;
    Connect dconn = null;
    Domain destDomain = null;
    Connect conn = null;
    String xmlDesc;
    List<Ternary<String, Boolean, String>> vmsnapshots = null;
    try {
        final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
        conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
        ifaces = libvirtComputingResource.getInterfaces(conn, vmName);
        disks = libvirtComputingResource.getDisks(conn, vmName);
        dm = conn.domainLookupByName(vmName);
        /*
             * We replace the private IP address with the address of the destination host. This is because the VNC listens on
             * the private IP address of the hypervisor, but that address is ofcourse different on the target host.
             *
             * MigrateCommand.getDestinationIp() returns the private IP address of the target hypervisor. So it's safe to use.
             *
             * The Domain.migrate method from libvirt supports passing a different XML description for the instance to be used
             * on the target host.
             *
             * This is supported by libvirt-java from version 0.50.0
             *
             * CVE-2015-3252: Get XML with sensitive information suitable for migration by using VIR_DOMAIN_XML_MIGRATABLE
             * flag (value = 8) https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainXMLFlags
             *
             * Use VIR_DOMAIN_XML_SECURE (value = 1) prior to v1.0.0.
             */
        // 1000000 equals v1.0.0
        final int xmlFlag = conn.getLibVirVersion() >= 1000000 ? 8 : 1;
        xmlDesc = dm.getXMLDesc(xmlFlag).replace(libvirtComputingResource.getPrivateIp(), command.getDestinationIp());
        // delete the metadata of vm snapshots before migration
        vmsnapshots = libvirtComputingResource.cleanVMSnapshotMetadata(dm);
        dconn = libvirtUtilitiesHelper.retrieveQemuConnection("qemu+tcp://" + command.getDestinationIp() + "/system");
        // run migration in thread so we can monitor it
        s_logger.info("Live migration of instance " + vmName + " initiated");
        final ExecutorService executor = Executors.newFixedThreadPool(1);
        final Callable<Domain> worker = new MigrateKvmAsync(libvirtComputingResource, dm, dconn, xmlDesc, vmName, command.getDestinationIp());
        final Future<Domain> migrateThread = executor.submit(worker);
        executor.shutdown();
        long sleeptime = 0;
        while (!executor.isTerminated()) {
            Thread.sleep(100);
            sleeptime += 100;
            if (sleeptime == 1000) {
                final int migrateDowntime = libvirtComputingResource.getMigrateDowntime();
                if (migrateDowntime > 0) {
                    try {
                        final int setDowntime = dm.migrateSetMaxDowntime(migrateDowntime);
                        if (setDowntime == 0) {
                            s_logger.debug("Set max downtime for migration of " + vmName + " to " + String.valueOf(migrateDowntime) + "ms");
                        }
                    } catch (final LibvirtException e) {
                        s_logger.debug("Failed to set max downtime for migration, perhaps migration completed? Error: " + e.getMessage());
                    }
                }
            }
            if (sleeptime % 1000 == 0) {
                s_logger.info("Waiting for migration of " + vmName + " to complete, waited " + sleeptime + "ms");
            }
            // pause vm if we meet the vm.migrate.pauseafter threshold and not already paused
            final int migratePauseAfter = libvirtComputingResource.getMigratePauseAfter();
            if (migratePauseAfter > 0 && sleeptime > migratePauseAfter) {
                DomainState state = null;
                try {
                    state = dm.getInfo().state;
                } catch (final LibvirtException e) {
                    s_logger.info("Couldn't get VM domain state after " + sleeptime + "ms: " + e.getMessage());
                }
                if (state != null && state == DomainState.VIR_DOMAIN_RUNNING) {
                    try {
                        s_logger.info("Pausing VM " + vmName + " due to property vm.migrate.pauseafter setting to " + migratePauseAfter + "ms to complete migration");
                        dm.suspend();
                    } catch (final LibvirtException e) {
                        // pause could be racy if it attempts to pause right when vm is finished, simply warn
                        s_logger.info("Failed to pause vm " + vmName + " : " + e.getMessage());
                    }
                }
            }
        }
        s_logger.info("Migration thread for " + vmName + " is done");
        destDomain = migrateThread.get(10, TimeUnit.SECONDS);
        if (destDomain != null) {
            for (final LibvirtDiskDef disk : disks) {
                libvirtComputingResource.cleanupDisk(disk);
            }
        }
    } catch (final LibvirtException e) {
        s_logger.debug("Can't migrate domain: " + e.getMessage());
        result = e.getMessage();
    } catch (final InterruptedException e) {
        s_logger.debug("Interrupted while migrating domain: " + e.getMessage());
        result = e.getMessage();
    } catch (final ExecutionException e) {
        s_logger.debug("Failed to execute while migrating domain: " + e.getMessage());
        result = e.getMessage();
    } catch (final TimeoutException e) {
        s_logger.debug("Timed out while migrating domain: " + e.getMessage());
        result = e.getMessage();
    } finally {
        try {
            if (dm != null && result != null) {
                // restore vm snapshots in case of failed migration
                if (vmsnapshots != null) {
                    libvirtComputingResource.restoreVMSnapshotMetadata(dm, vmName, vmsnapshots);
                }
            }
            if (dm != null) {
                if (dm.isPersistent() == 1) {
                    dm.undefine();
                }
                dm.free();
            }
            if (dconn != null) {
                dconn.close();
            }
            if (destDomain != null) {
                destDomain.free();
            }
        } catch (final LibvirtException e) {
            s_logger.trace("Ignoring libvirt error.", e);
        }
    }
    if (result == null) {
        for (final InterfaceDef iface : ifaces) {
            // We don't know which "traffic type" is associated with
            // each interface at this point, so inform all vif drivers
            final List<VifDriver> allVifDrivers = libvirtComputingResource.getAllVifDrivers();
            for (final VifDriver vifDriver : allVifDrivers) {
                vifDriver.unplug(iface);
            }
        }
    }
    return new MigrateAnswer(command, result == null, result, null);
}
Also used : LibvirtException(org.libvirt.LibvirtException) Ternary(com.cloud.utils.Ternary) Connect(org.libvirt.Connect) VifDriver(com.cloud.hypervisor.kvm.resource.VifDriver) MigrateAnswer(com.cloud.agent.api.MigrateAnswer) InterfaceDef(com.cloud.hypervisor.kvm.resource.LibvirtVmDef.InterfaceDef) LibvirtDiskDef(com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef) DomainState(org.libvirt.DomainInfo.DomainState) ExecutorService(java.util.concurrent.ExecutorService) Domain(org.libvirt.Domain) ExecutionException(java.util.concurrent.ExecutionException) MigrateKvmAsync(com.cloud.hypervisor.kvm.resource.async.MigrateKvmAsync) TimeoutException(java.util.concurrent.TimeoutException)

Aggregations

LibvirtDiskDef (com.cloud.hypervisor.kvm.resource.xml.LibvirtDiskDef)13 Domain (org.libvirt.Domain)8 InterfaceDef (com.cloud.hypervisor.kvm.resource.LibvirtVmDef.InterfaceDef)6 Connect (org.libvirt.Connect)5 LibvirtException (org.libvirt.LibvirtException)4 IOException (java.io.IOException)3 DomainBlockStats (org.libvirt.DomainBlockStats)3 VmStatsEntry (com.cloud.agent.api.VmStatsEntry)2 InternalErrorException (com.cloud.exception.InternalErrorException)2 VifDriver (com.cloud.hypervisor.kvm.resource.VifDriver)2 KvmPhysicalDisk (com.cloud.hypervisor.kvm.storage.KvmPhysicalDisk)2 KvmStoragePool (com.cloud.hypervisor.kvm.storage.KvmStoragePool)2 CloudRuntimeException (com.cloud.utils.exception.CloudRuntimeException)2 ArrayList (java.util.ArrayList)2 ParserConfigurationException (javax.xml.parsers.ParserConfigurationException)2 Test (org.junit.Test)2 DomainInfo (org.libvirt.DomainInfo)2 Answer (com.cloud.agent.api.Answer)1 CheckRouterAnswer (com.cloud.agent.api.CheckRouterAnswer)1 MigrateAnswer (com.cloud.agent.api.MigrateAnswer)1