/*************************************************************************** * Copyright (c) 2015 VMware, Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ package com.vmware.aurora.vc; import com.google.gson.Gson; import com.google.gson.internal.Pair; import com.google.gson.reflect.TypeToken; import com.vmware.aurora.exception.AuroraException; import com.vmware.aurora.exception.GuestVariableException; import com.vmware.aurora.exception.VcException; import com.vmware.aurora.global.DiskSize; import com.vmware.aurora.util.AuAssert; import com.vmware.aurora.vc.vcevent.VcEventHandlers; import com.vmware.aurora.vc.vcevent.VcEventListener; import com.vmware.aurora.vc.vcservice.VcContext; import com.vmware.aurora.vc.vcservice.VcLongCallHandler; import com.vmware.bdd.clone.spec.VmCreateSpec; import com.vmware.vim.binding.impl.vim.cluster.*; import com.vmware.vim.binding.impl.vim.option.OptionValueImpl; import com.vmware.vim.binding.impl.vim.vm.CloneSpecImpl; import com.vmware.vim.binding.impl.vim.vm.ConfigSpecImpl; import com.vmware.vim.binding.impl.vim.vm.CreateChildSpecImpl; import com.vmware.vim.binding.impl.vim.vm.RelocateSpecImpl; import com.vmware.vim.binding.impl.vim.vm.device.VirtualDeviceSpecImpl; import com.vmware.vim.binding.vim.ClusterComputeResource; import com.vmware.vim.binding.vim.Folder; import com.vmware.vim.binding.vim.VirtualDiskManager; import com.vmware.vim.binding.vim.VirtualMachine; import com.vmware.vim.binding.vim.cluster.*; import com.vmware.vim.binding.vim.event.Event; import com.vmware.vim.binding.vim.event.VmEvent; import com.vmware.vim.binding.vim.ext.ManagedByInfo; import com.vmware.vim.binding.vim.fault.FileNotFound; import com.vmware.vim.binding.vim.fault.InvalidPowerState; import com.vmware.vim.binding.vim.fault.InvalidState; import com.vmware.vim.binding.vim.fault.ToolsUnavailable; import com.vmware.vim.binding.vim.net.IpConfigInfo; import com.vmware.vim.binding.vim.option.ArrayUpdateSpec; import com.vmware.vim.binding.vim.option.OptionValue; import com.vmware.vim.binding.vim.vApp.VmConfigInfo; import com.vmware.vim.binding.vim.vm.*; import com.vmware.vim.binding.vim.vm.ConfigInfo; import com.vmware.vim.binding.vim.vm.ConfigSpec; import com.vmware.vim.binding.vim.vm.device.*; import com.vmware.vim.binding.vmodl.ManagedObject; import com.vmware.vim.binding.vmodl.ManagedObjectReference; import com.vmware.vim.binding.vmodl.fault.ManagedObjectNotFound; import org.apache.commons.lang.ArrayUtils; import java.lang.reflect.Type; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Created by xiaoliangl on 8/6/15. */ @SuppressWarnings("serial") class VcVirtualMachineImpl extends VcVmBaseImpl implements VcVirtualMachine { /** * Inner class that allows threads to wait until the arrival of any * requested VmEvent. Clients can wait only for "future" events - the * ones arriving after a new instance of this class was created. Upon * the event arrival all waiting threads are released. */ private abstract class WaitForVmEventHandler implements VcEventHandlers.IVcEventHandler { // Threshold for single wait. private final long waitThresholdNanos = TimeUnit.SECONDS.toNanos(20); private final CountDownLatch eventLatch; // Drops to 0 when event arrives. private final VcEventHandlers.VcEventType eventType; // VcEventType to wait for. private final boolean external; // External event to wait for? /** * Create a new handler and register it with VcEventListener. * @param eventType */ WaitForVmEventHandler(VcEventHandlers.VcEventType eventType, boolean external) { /* Make sure we are waiting for VmEvent subtypes only. */ AuAssert.check(VmEvent.class.isAssignableFrom(eventType.getEventClass())); this.eventType = eventType; this.external = external; eventLatch = new CountDownLatch(1); // Will wait for 1 event. if (external) { VcEventListener.installExtEventHandler(eventType, this); } else { VcEventListener.installEventHandler(eventType, this); } } /** * Deactivate the handler by removing it from VcEventListener. */ void disable() { if (external) { VcEventListener.removeExtEventHandler(eventType, this); } else { VcEventListener.removeEventHandler(eventType, this); } } private String msg(String text) { StringBuilder buf = new StringBuilder(text); buf = buf.append(" ").append(eventType).append(" "). append(VcVirtualMachineImpl.this); return buf.toString(); } /** * VmEvent handler: if event is for this VM, release all waiters and * return true. Otherwise, return false. * @param type VcEventType * @param e VmEvent * @return true, if event for this vm */ @Override public boolean eventHandler(VcEventHandlers.VcEventType type, Event e) throws Exception { AuAssert.check(eventLatch.getCount() <= 1); VmEvent event = (VmEvent)e; AuAssert.check(event.getVm() != null); /* If VmEvent is for this VM, release waiters. */ if (VcVirtualMachineImpl.this.getMoRef().equals(event.getVm().getVm())) { eventCallback(); eventLatch.countDown(); logger.debug(msg("VmEventHandler: match")); return true; } return false; } /** * Called by threads to wait until event arrival. Waits until either: * - a latch is dropped * - a timeout expires * - resumeWaiting condition turns to false * Returns false on timeout, true in other cases. Does not break out * of wait on InterruptedException. Rechecks vc condition every * waitThresholdNanos to make sure vc does not drop events permanently. * @param timeout * @param unit * @return true if event arrived * @throws Exception */ boolean await(long timeout, TimeUnit unit) throws Exception { long timeoutNanos = unit.toNanos(timeout); boolean res = false; long finishNanos = System.nanoTime() + timeoutNanos; if (!resumeWaiting()) { logger.warn(msg("Unnecessary wait for event skipped:")); return true; } logger.info(msg("Waiting for event:")); while (!res && timeoutNanos > 0) { /* Wake up periodically because we don't quite trust VC. */ if (timeoutNanos > waitThresholdNanos) { timeoutNanos = waitThresholdNanos; } try { res = eventLatch.await(timeoutNanos, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { /* Ok to swallow. */ } finally { timeoutNanos = finishNanos - System.nanoTime(); } /* * If we did not get an event we were waiting for, check with vc. * Until vc convinces us that it does not lose events, do periodic * checks to make sure that the "event condition" is still false. */ if (!res && !resumeWaiting()) { logger.warn(msg("Dropped event?")); res = true; } } if (res) { logger.info(msg("Success: wait for event")); } else { logger.info(msg("Failure: wait for event")); } return res; } /** * Since we don't fully trust VC, this function must be supplied by * subtypes to periodically check the server condition matching * waiting for the expected event. For example, * VmPoweredOffEvent -> vm.runtimeInfo.powerState == poweredOn * This function would normally return true while we are still * waiting for an event and false after event's arrival. * @return true, if we need to continue waiting for event */ protected abstract boolean resumeWaiting() throws Exception; /** * A callback when the expected event has arrived. * The callback typically refreshes the target object's state * as a result of the task. Even if the task caller fails after * receiving an exception, the target object's state will still * be updated by the event handler. */ protected abstract void eventCallback(); } /** * A handler that waits until this VM is powered off. */ private class WaitForPowerOffHandler extends WaitForVmEventHandler { WaitForPowerOffHandler(boolean external) { super(VcEventHandlers.VcEventType.VmPoweredOff, external); } /** * Continue to wait as long as vm stays powered on. */ @Override protected boolean resumeWaiting() throws Exception { updateRuntime(); return !isPoweredOff(); } /** * Refresh vm state after receiving the event. */ @Override protected void eventCallback() { VcCache.refreshRuntime(getMoRef()); } } public interface VmOp<T> { public T exec() throws Exception; } /* * Retry VM operation if we detect that an invalid state * exception has been thrown. */ <T> T safeExecVmOp(VmOp<T> vmOp) throws Exception { int retries = 5; while (true) { try { return vmOp.exec(); } catch (InvalidState e) { if (retries <= 0) { throw e; } logger.info("got invalid state exception on vm " + this); if (e instanceof InvalidPowerState) { InvalidPowerState e1 = (InvalidPowerState)e; if (e1.getRequestedState().equals(VirtualMachine.PowerState.poweredOff)) { logger.info("power off and retry operation on " + this); powerOff(); } else { logger.warn("expecting power state: " + e1.getRequestedState()); } } // VC might be in an inconsistent state, retry the operation. Thread.sleep(20 * 1000); retries--; } } } private int key; private ConfigInfo config; private RuntimeInfo runtime; private VirtualMachine.PowerState cachedPowerState = null; private DiskSize storageUsage; private DiskSize storageCommitted; private ManagedObjectReference resourcePool; private ManagedObjectReference parentVApp; private List<ManagedObjectReference> datastores; // datastores accessed by this VM private FileLayoutEx layoutEx; private Map<ManagedObjectReference, VcSnapshotImpl> snapshots = new HashMap<ManagedObjectReference, VcSnapshotImpl>(); private ManagedObjectReference currentSnapshot; private volatile boolean needsStorageInfoRefresh = true; static final String MACHINE_ID = "machine.id"; static final String DBVM_CONFIG = "dbvm.config"; static final long GUEST_VAR_CHECK_INTERVAL = 3000; // in milliseconds @Override protected void update(ManagedObject mo) throws Exception { VirtualMachine vm = (VirtualMachine)mo; config = checkReady(vm.getConfig()); resourcePool = vm.getResourcePool(); if (!isTemplate()) { checkReady(resourcePool); } parentVApp = vm.getParentVApp(); datastores = Arrays.asList(checkReady(vm.getDatastore())); layoutEx = checkReady(vm.getLayoutEx()); updateSnapshots(vm); } @Override protected synchronized void updateRuntime(ManagedObject mo) throws Exception { VirtualMachine vm = (VirtualMachine)mo; this.runtime = checkReady(vm.getRuntime()); Summary summary = checkReady(vm.getSummary()); Summary.StorageSummary storageSummary = checkReady(summary.getStorage()); storageUsage = new DiskSize(storageSummary.getUnshared()); storageCommitted = new DiskSize(storageSummary.getCommitted()); /* XXX Layout needs to be updated in both update() and updateRuntime() * as it contains both configuration & runtime data. */ this.layoutEx = checkReady(vm.getLayoutEx()); /** * VC sometime can get out of sync with hostd on VM's power state. * This cachedPowerState is computed base on return values of events * and/or VC calls, which may be different from VC's runtime state. * We trust cachedPowerState more than runtime state if they are out of sync. */ if (cachedPowerState != null) { if (cachedPowerState == runtime.getPowerState()) { /* At some point, runtime state becomes in sync. * We set cachedPowerState to null to indicate that VC contains * the consistent state. */ cachedPowerState = null; } } } @Override protected void processNotFoundException() throws Exception { logger.error("vm " + MoUtil.morefToString(moRef) + " is already deleted in VC. Purge from vc cache"); VcCache.purge(moRef); VcCache.removeVmRpPair(moRef); } /** * Refresh the storage info if the storage refresh flag is dirty. */ @Override public void updateStorageInfoIfNeeded() throws Exception { boolean needsRefresh = needsStorageInfoRefresh(); if (needsRefresh) { try { // clear dirty flag setNeedsStorageInfoRefresh(false); VcContext.getVcLongCallHandler().execute( new VcLongCallHandler.VcLongCall<Void>() { @Override public Void callVc() throws Exception { VirtualMachine vm = getManagedObject(); vm.refreshStorageInfo(); return null; } }); } catch (Exception e) { setNeedsStorageInfoRefresh(true); throw e; } VirtualMachine vm = getManagedObject(); Summary summary = checkReady(vm.getSummary()); Summary.StorageSummary storageSummary = checkReady(summary.getStorage()); storageUsage = new DiskSize(storageSummary.getUnshared()); storageCommitted = new DiskSize(storageSummary.getCommitted()); layoutEx = checkReady(vm.getLayoutEx()); } } protected VcVirtualMachineImpl(final VirtualMachine vm) throws Exception { super(vm); safeExecVmOp(new VmOp<Void>() { public Void exec() throws Exception { update(vm); updateRuntime(vm); return null; } }); // key is used in ConfigSpec when updating multiple aspects, such as devices // The key we specify does not matter, and will get reassigned, so we start with -1 // and go lower (the system assigned keys are positive). key = -1; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getConfig() */ @Override public ConfigInfo getConfig() { return config; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getVAppConfig() */ @Override public VmConfigInfo getVAppConfig() { return config.getVAppConfig(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getName() */ @Override public String getName() { return MoUtil.fromURLString(config.getName()); } /* * (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualMachine#rename(java.lang.String) */ @Override public void rename(String newName) throws Exception { ConfigSpec spec = new ConfigSpecImpl(); spec.setName(newName); reconfigure(spec); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getURLName() */ @Override public String getURLName() { return config.getName(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getPathName() */ @Override public String getPathName() { String vmxPath = config.getFiles().getVmPathName(); // get directory name of VMX file, excluding the last '/' return vmxPath.substring(0, vmxPath.lastIndexOf('/')); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#isTemplate() */ @Override public boolean isTemplate() { return config.isTemplate(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getHost() */ @Override public VcHost getHost() { return VcCache.get(runtime.getHost()); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getDatacenter() */ @Override public VcDatacenter getDatacenter() { if (datastores.size() > 0) { VcDatastore ds = VcCache.get(datastores.get(0)); return ds.getDatacenter(); } else { return null; } } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getDatastores() */ @Override public VcDatastore[] getDatastores() { List<VcDatastore> dsList = VcCache.getPartialList(datastores, getMoRef()); return dsList.toArray(new VcDatastore[dsList.size()]); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getDefaultDatastore() */ @Override public VcDatastore getDefaultDatastore() { if (datastores.size() > 0) { return VcCache.get(datastores.get(0)); } else { return null; } } public String toString() { return String.format("%s[%s](%s)", isTemplate()? "VM_T":"VM", getName(), isCached() ? "c":""); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getInfo() */ @Override public String getInfo() { Long cpuLimit=config.getCpuAllocation().getLimit(); Long cpuReservation=config.getCpuAllocation().getReservation(); Long memLimit=config.getMemoryAllocation().getLimit(); Long memReservation=config.getMemoryAllocation().getReservation(); return String.format("%s[%s](cpu:R=%d,L=%d,mem:R=%d,L=%d,%d cpus,CS:%s,PS:%s)", isTemplate()? "VM_T": "VM", getName(), cpuReservation, cpuLimit, memReservation, memLimit, config.getHardware().getNumCPU(), runtime.getConnectionState(), runtime.getPowerState()); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#refreshRP() */ public void refreshRP() { if (!isTemplate()) { VcCache.refresh(resourcePool); } } private VirtualDeviceSpec detachVirtualDiskSpec(DeviceId deviceId, boolean destroyDisk) throws Exception { VirtualDevice device = getVirtualDevice(deviceId); if (device == null || !(device instanceof VirtualDisk)) { String deviceInfo = device != null ? VmConfigUtil .getVirtualDeviceInfo(device) : "device not found"; logger.info("cannot detach disk " + deviceId + ": " + deviceInfo); throw VcException.INTERNAL_DISK_DETACHMENT_ERROR(); } VirtualDeviceSpec spec = new VirtualDeviceSpecImpl(); spec.setDevice(device); spec.setOperation(VirtualDeviceSpec.Operation.remove); if (destroyDisk) { spec.setFileOperation(VirtualDeviceSpec.FileOperation.destroy); } return spec; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#detachVirtualDisk(com.vmware.aurora.vc.DeviceId, boolean) */ @Override public void detachVirtualDisk(DeviceId deviceId, boolean destroyDisk) throws Exception { String dsPath = null; if (destroyDisk) { VirtualDisk vmdk = (VirtualDisk) getVirtualDevice(deviceId); if (vmdk == null) { throw new ManagedObjectNotFound(); } dsPath = VmConfigUtil.getVmdkPath(vmdk); } /* * Workaround for PR 904771. * Detach disk (FileOperation.destroy not set) is not enabled in vSphere 5.1 if snapshot exists. * Use destroy disk to replace detach disk operation. * Disks that are still needed should be protected by snapshot. */ VirtualDeviceSpec change = detachVirtualDiskSpec(deviceId, !VmConfigUtil.isDetachDiskEnabled() || destroyDisk /* "destroyDisk" Comment below. */); reconfigure(VmConfigUtil.createConfigSpec(change)); /* * A work-around for PR 742324. * The previous call may not have succeeded for yet unknown reasons. Try * deletion one more time through a direct disk-level call if the disk * still exists. * * Another variation is that reconfigure() above can only detach * the disk, and the following is always used as the primary deletion * mechanism instead of a fall-back mechanism, but I am scared * to do it so close to the dead-line. */ if (destroyDisk) { try { String uuid = VcFileManager.queryVirtualDiskUuid(dsPath, getDatacenter()); if (uuid != null) { VcFileManager.deleteVirtualDisk(dsPath, getDatacenter()); logger.info("The disk at " + deviceId + " was deleted on second try."); } } catch (FileNotFound exc) { logger.info("The disk at " + deviceId + " is probably already deleted. OK."); } catch (Exception exc) { logger.warn( "The disk at " + deviceId + " could not be deleted through the direct method.", exc); } } } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#detachVirtualDisk(com.vmware.aurora.vc.DeviceId, boolean) */ @Override public void detachVirtualDiskIfExists(DeviceId deviceId, boolean destroyDisk) throws Exception { if (isDiskAttached(deviceId)) { detachVirtualDisk(deviceId, destroyDisk); } } private VirtualController attachVirtualController(DeviceId deviceId) throws Exception { VmConfigUtil.ScsiControllerType scsiType = VmConfigUtil.ScsiControllerType.findController(deviceId.getTypeClass()); if (scsiType != null) { logger.info("Adding " + deviceId.controllerType + " SCSI controller to VM " + this.getName() + " at bus " + deviceId.busNum); } else { logger.error("Unsupported SCSI type creation: " + deviceId.controllerType); throw VcException.INTERNAL(); } if (reconfigure(VmConfigUtil.createConfigSpec(VmConfigUtil.createControllerDevice(scsiType, deviceId.busNum)))) { return getVirtualController(deviceId); } else { return null; } } public VirtualDeviceSpec attachVirtualDiskSpec(DeviceId deviceId, VirtualDevice.BackingInfo backing, boolean createDisk, DiskSize size) throws Exception { VirtualController controller = getVirtualController(deviceId); if (controller == null) { // Add the controller to the VM if it does not exist controller = attachVirtualController(deviceId); if (controller == null) { throw VcException.CONTROLLER_NOT_FOUND(deviceId.toString()); } } VirtualDisk vmdk = VmConfigUtil.createVirtualDisk(controller, deviceId.unitNum, backing, size); // key is used in ConfigSpec when updating multiple aspects, such as devices // The key we specify does not matter, and will get reassigned, so we start with -1 // and go lower (the system assigned keys are positive). Without specifying the keys, // multiple updates in a single call will not work. vmdk.setKey(key); key--; VirtualDeviceSpec spec = new VirtualDeviceSpecImpl(); spec.setOperation(VirtualDeviceSpec.Operation.add); if (createDisk) { spec.setFileOperation(VirtualDeviceSpec.FileOperation.create); } spec.setDevice(vmdk); return spec; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#attachVirtualDisk(com.vmware.aurora.vc.DeviceId, com.vmware.vim.binding.vim.vm.device.VirtualDevice.BackingInfo, boolean, com.vmware.aurora.global.DiskSize) */ @Override public void attachVirtualDisk(DeviceId deviceId, VirtualDevice.BackingInfo backing, boolean createDisk, DiskSize size) throws Exception { // Here we attach a disk once at a time. // VC can attach multiple disks in one change set - if you want that, use attachVirtualDiskSpec // to build up an array of VirtualDeviceSpecs and call reconfigure directly on the created // config spec. VirtualDeviceSpec change = attachVirtualDiskSpec(deviceId, backing, createDisk, size); reconfigure(VmConfigUtil.createConfigSpec(change)); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#copyAttachVirtualDisk(com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcVmBase, com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcDatastore, java.lang.String, com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode) */ @Override public void copyAttachVirtualDisk(DeviceId deviceId, VcVmBase srcVm, DeviceId srcDeviceId, VcDatastore dstDs, String diskName, VirtualDiskOption.DiskMode diskMode) throws Exception { VirtualDisk vmdk = (VirtualDisk)srcVm.getVirtualDevice(srcDeviceId); String srcPath = VmConfigUtil.getVmdkPath(vmdk); String dstPath = VcFileManager.getDsPath(this, dstDs, diskName); logger.info("Copying disk '" + srcPath + "' to '" + dstPath + "'"); // By default it would use settings from the parent disk, // verified for sparse & thin provisioned disks. VirtualDiskManager.VirtualDiskSpec spec = null; VcFileManager.copyVirtualDisk(srcPath, srcVm.getDatacenter(), dstPath, getDatacenter(), spec); attachVirtualDisk(deviceId, VmConfigUtil.createVmdkBackingInfo(this, dstDs, diskName, diskMode, null, null), false, null); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#copyAttachVirtualDisk(com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcVmBase, com.vmware.aurora.vc.DeviceId, java.lang.String, com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode) */ @Override public void copyAttachVirtualDisk(DeviceId deviceId, VcVmBase srcVm, DeviceId srcDeviceId, String diskName, VirtualDiskOption.DiskMode diskMode) throws Exception { copyAttachVirtualDisk(deviceId, srcVm, srcDeviceId, null, diskName, diskMode); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#attachChildDisk(com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcSnapshot, com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcDatastore, java.lang.String, com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode, java.lang.Boolean) */ @Override public void attachChildDiskPath(DeviceId deviceId, VcSnapshot srcSnap, DeviceId srcDeviceId, String diskPath, VirtualDiskOption.DiskMode diskMode) throws Exception { VirtualDisk vmdk = (VirtualDisk) srcSnap.getVirtualDevice(srcDeviceId); VirtualDevice.BackingInfo parentBacking = vmdk.getBacking(); VirtualDevice.BackingInfo backing = VmConfigUtil.createVmdkBackingInfo(diskPath, diskMode, (VirtualDisk.FlatVer2BackingInfo)parentBacking, null, null); attachVirtualDisk(deviceId, backing, true, null); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#attachChildDisk(com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcSnapshot, com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcDatastore, java.lang.String, com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode) */ @Override public void attachChildDisk(DeviceId deviceId, VcSnapshot srcSnap, DeviceId srcDeviceId, VcDatastore dstDs, String diskName, VirtualDiskOption.DiskMode diskMode) throws Exception { VirtualDisk vmdk = (VirtualDisk) srcSnap.getVirtualDevice(srcDeviceId); VirtualDevice.BackingInfo parentBacking = vmdk.getBacking(); VirtualDevice.BackingInfo backing = VmConfigUtil.createVmdkBackingInfo(this, dstDs, diskName, diskMode, parentBacking); attachVirtualDisk(deviceId, backing, true, null); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#attachChildDisk(com.vmware.aurora.vc.DeviceId, com.vmware.aurora.vc.VcSnapshot, com.vmware.aurora.vc.DeviceId, java.lang.String, com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode) */ @Override public void attachChildDisk(DeviceId deviceId, VcSnapshot srcSnap, DeviceId srcDeviceId, String diskName, VirtualDiskOption.DiskMode diskMode) throws Exception { attachChildDisk(deviceId, srcSnap, srcDeviceId, null, diskName, diskMode); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#promoteDisk(com.vmware.aurora.vc.DeviceId) */ @Override public void promoteDisk(DeviceId deviceId) throws Exception { VirtualDisk vmdk = (VirtualDisk) this.getVirtualDevice(deviceId); this.promoteDisk(vmdk, true); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#extendVirtualDisk(com.vmware.aurora.vc.DeviceId, com.vmware.aurora.global.DiskSize) */ @Override public void extendVirtualDisk(DeviceId deviceId, DiskSize size) throws Exception { VirtualDisk vmdk = (VirtualDisk)getVirtualDevice(deviceId); VirtualDeviceSpec spec = new VirtualDeviceSpecImpl(); vmdk.setCapacityInKB(size.getKiB()); spec.setOperation(VirtualDeviceSpec.Operation.edit); spec.setDevice(vmdk); reconfigure(VmConfigUtil.createConfigSpec(spec)); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#editVirtualDisk(com.vmware.aurora.vc.DeviceId, com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode) */ @Override public VirtualDeviceSpec editVirtualDiskSpec(DeviceId deviceId, VirtualDiskOption.DiskMode newMode) throws Exception { VirtualDisk vmdk = (VirtualDisk) getVirtualDevice(deviceId); VirtualDevice.BackingInfo backing = vmdk.getBacking(); if (backing instanceof VirtualDisk.FlatVer2BackingInfo) { ((VirtualDisk.FlatVer2BackingInfo) backing).setDiskMode(newMode .toString()); } else if (backing instanceof VirtualDisk.SparseVer2BackingInfo) { ((VirtualDisk.SparseVer2BackingInfo) backing).setDiskMode(newMode .toString()); } else { AuAssert.check(backing instanceof VirtualDisk.SeSparseBackingInfo); ((VirtualDisk.SeSparseBackingInfo) backing).setDiskMode(newMode .toString()); } vmdk.setBacking(backing); VirtualDeviceSpec spec = new VirtualDeviceSpecImpl(); spec.setOperation(VirtualDeviceSpec.Operation.edit); spec.setDevice(vmdk); return spec; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#editVirtualDisk(com.vmware.aurora.vc.DeviceId, com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode) */ @Override public void editVirtualDisk(DeviceId deviceId, VirtualDiskOption.DiskMode newMode) throws Exception { VirtualDeviceSpec spec = editVirtualDiskSpec(deviceId, newMode); boolean success = reconfigure(VmConfigUtil.createConfigSpec(spec)); if (!success) { throw new Exception("Failed to change the disk mode of " + deviceId + " to " + newMode); } } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#isBaseDisk(com.vmware.aurora.vc.DeviceId) */ @Override public boolean isBaseDisk(DeviceId deviceId) { VirtualDevice device = getVirtualDevice(deviceId); VirtualDevice.BackingInfo backing = device.getBacking(); if (backing != null && backing instanceof VirtualDisk.FlatVer2BackingInfo && ((VirtualDisk.FlatVer2BackingInfo) backing).getParent() == null) { return true; } return false; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getDiskCapacity(com.vmware.aurora.vc.DeviceId) */ @Override public DiskSize getDiskCapacity(DeviceId deviceId) { VirtualDevice device = getVirtualDevice(deviceId); if (device instanceof VirtualDisk) { return DiskSize.sizeFromKiB(((VirtualDisk) device).getCapacityInKB()); } else { return new DiskSize(0); } } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getDiskDatastore(com.vmware.aurora.vc.DeviceId) */ @Override public VcDatastore getDiskDatastore(DeviceId deviceId) { VirtualDisk vmdk = (VirtualDisk) getVirtualDevice(deviceId); if (vmdk == null) { throw VcException.DISK_NOT_FOUND(deviceId.toString()); } VirtualDisk.FileBackingInfo diskBacking = (VirtualDisk.FileBackingInfo) vmdk.getBacking(); return (VcDatastore) VcCache.get(diskBacking.getDatastore()); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#isDiskAttached(com.vmware.aurora.vc.saga.DbvmConfig.DiskId) */ @Override public boolean isDiskAttached(DeviceId deviceId) { VirtualDevice device = getVirtualDevice(deviceId); return device != null && device instanceof VirtualDisk; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#QueryChangedDiskAreas */ @Override public VirtualMachine.DiskChangeInfo queryChangedDiskAreas(VcSnapshot endMarkerSnapshot, DeviceId deviceId, long startOffset, String diskChangeId) throws Exception { VirtualDisk vmdk = (VirtualDisk) getVirtualDevice(deviceId); VirtualMachine vm = getManagedObject(); return vm.queryChangedDiskAreas(endMarkerSnapshot.getMoRef(), vmdk.getKey(), startOffset, diskChangeId); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#mountISO(com.vmware.aurora.vc.DeviceId, com.vmware.vim.binding.vim.vm.device.VirtualDevice.BackingInfo) */ @Override public VirtualDeviceSpec mountISO(DeviceId deviceId, VirtualDevice.BackingInfo backing) throws Exception { VirtualCdrom cdrom = (VirtualCdrom)getVirtualDevice(deviceId); VmConfigUtil.setVirtualDeviceBacking(cdrom, backing); VirtualDeviceSpec spec = new VirtualDeviceSpecImpl(); spec.setOperation(VirtualDeviceSpec.Operation.edit); spec.setDevice(cdrom); return spec; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#reconfigNetworkSpec(java.lang.String, com.vmware.aurora.vc.VcNetwork) */ @Override public VirtualDeviceSpec reconfigNetworkSpec(String label, VcNetwork network) throws Exception { VirtualDevice nic = getDeviceByLabel(label); VmConfigUtil.setVirtualDeviceBacking(nic, network.getBackingInfo()); VirtualDeviceSpec spec = new VirtualDeviceSpecImpl(); spec.setOperation(VirtualDeviceSpec.Operation.edit); spec.setDevice(nic); return spec; } /* * Wait in a loop for VC's view of the VM's power state * to be back in sync. */ private void waitForPowerStateToSync(VirtualMachine.PowerState state, int timeout) throws Exception { AuAssert.check(Thread.holdsLock(this)); VirtualMachine vm = getManagedObject(); try { while (timeout > 0) { runtime = checkReady(vm.getRuntime()); // If runtime state matches the claimed power state, done. if (runtime.getPowerState() == state) { return; } logger.info("syncing power state " + state + " on " + this); timeout -= 10; wait(10 * 1000); } } catch (InterruptedException e) { // break out if the thread is interrupted } } /* * Set requested power state of the VM if the known VM state retrieved from * task or exception is inconsistent with VC value. This could happen if * we get an external power event from VC before VC updates the VM's * power state. When this happens, we wait for VC to become in sync. * If that doesn't work, mark the inconsistent state. */ private synchronized void setRequestedPowerState(VirtualMachine.PowerState state) throws Exception { if (runtime.getPowerState() != state) { waitForPowerStateToSync(state, WAIT_FOR_VC_STATE_TIMEOUT_SECS); } // If still not in sync, warn and record inconsistent state. if (runtime.getPowerState() != state) { logger.warn("inconsistent requested power state " + state + " on " + this); cachedPowerState = state; } } @Override public void setRequestedChangeTracking(boolean enabled) throws Exception { ConfigSpec spec = new ConfigSpecImpl(); spec.setChangeTrackingEnabled(enabled); reconfigure(spec); } /* * Get power state. If power state may be inconsistent, return null. */ private VirtualMachine.PowerState getPowerState() { if (cachedPowerState != null) { return null; } return runtime.getPowerState(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#isPoweredOn() */ @Override public boolean isPoweredOn() { return getPowerState() == VirtualMachine.PowerState.poweredOn; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#isPoweredOff() */ @Override public boolean isPoweredOff() { return getPowerState() == VirtualMachine.PowerState.poweredOff; } public VirtualMachine.ConnectionState getConnectionState() { return runtime.getConnectionState(); } public boolean isConnected() { return (runtime.getConnectionState() == VirtualMachine.ConnectionState.connected); } public VirtualMachine.FaultToleranceState getFTState() { return runtime.getFaultToleranceState(); } @Override public VcTask powerOn(final IVcTaskCallback callback) throws Exception { return powerOn(null, callback); } private boolean powerOnInt(final VcHost host) throws Exception { try { VcTask task = powerOn(host, VcCache.getRefreshRuntimeVcTaskCB(this)); task.waitForCompletion(); if (task.taskCompleted()) { setRequestedPowerState(VirtualMachine.PowerState.poweredOn); } return task.taskCompleted(); } catch (InvalidPowerState e) { if (e.getExistingState().equals(VirtualMachine.PowerState.poweredOn)) { setRequestedPowerState(VirtualMachine.PowerState.poweredOn); return true; } else { throw e; } } } @Override public boolean powerOn() throws Exception { return powerOn((VcHost)null); } @Override public VcTask powerOn(final VcHost host, final IVcTaskCallback callback) throws Exception { VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.PowerOn, vm.powerOn(host == null ? null : host.getMoRef()), callback); } }); logger.debug("power_on " + this + " task created"); return task; } @Override public boolean powerOn(final VcHost host) throws Exception { try { return safeExecVmOp(new VmOp<Boolean>() { public Boolean exec() throws Exception { return powerOnInt(host); } }); } catch (Exception e) { throw VcException.POWER_ON_VM_FAILED(e, getName(), e.getMessage()); } } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#powerOff(com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask powerOff(final IVcTaskCallback callback) throws Exception { VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.PowerOff, vm.powerOff(), callback); } }); logger.debug("power_off " + this + " task created"); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#powerOff() */ @Override public boolean powerOff() throws Exception { try { VcTask task = powerOff(VcCache.getRefreshRuntimeVcTaskCB(this)); task.waitForCompletion(); if (task.taskCompleted()) { setRequestedPowerState(VirtualMachine.PowerState.poweredOff); } return task.taskCompleted(); } catch (InvalidPowerState e) { if (e.getExistingState().equals(VirtualMachine.PowerState.poweredOff)) { setRequestedPowerState(VirtualMachine.PowerState.poweredOff); return true; } else { throw VcException.POWER_OFF_VM_FAILED(e, getName(), e.getMessage()); } } } private boolean waitForPowerOff(long timeoutMillis, boolean external) throws Exception { WaitForPowerOffHandler eventHandler = new WaitForPowerOffHandler(external); boolean res; try { res = eventHandler.await(timeoutMillis, TimeUnit.MILLISECONDS); if (res) { setRequestedPowerState(VirtualMachine.PowerState.poweredOff); } } finally { eventHandler.disable(); } return res; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#waitForExternalPowerOff(long) */ @Override public boolean waitForExternalPowerOff(long timeoutMillis) throws Exception { return waitForPowerOff(timeoutMillis, true); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#waitForPowerOff(long) */ @Override public boolean waitForPowerOff(long timeoutMillis) throws Exception { return waitForPowerOff(timeoutMillis, false); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#shutdownGuest(long) */ @Override public boolean shutdownGuest(final long timeoutMillis) throws Exception { try { VcContext.getTaskMgr().execPseudoTask("VirtualMachine.shutDownGuest", VcEventHandlers.VcEventType.VmPoweredOff, getMoRef(), new VcTaskMgr.IVcPseudoTaskBody() { @Override public ManagedObjectReference body() throws Exception { VirtualMachine vm = getManagedObject(); vm.shutdownGuest(); // Initiates shutdown. if (!waitForPowerOff(timeoutMillis)) { throw VcException.GUEST_TIMEOUT(); } return getMoRef(); } }); } catch (InvalidPowerState e) { if (e.getExistingState().equals(VirtualMachine.PowerState.poweredOff)) { setRequestedPowerState(VirtualMachine.PowerState.poweredOff); return true; } else { return false; } } catch (Exception e) { if (e instanceof ToolsUnavailable || e instanceof VcException) { logger.info("shutdownGuest got ", e); } else { logger.warn("shutdownGuest got unexpected ", e); } return false; } return true; } private VcTask destroy(final IVcTaskCallback callback) throws Exception { return VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.DestroyVm, vm.destroy(), callback); } }); } void destroyInt() throws Exception { try { final ManagedObjectReference oldRp = resourcePool; final ManagedObjectReference oldVm = getMoRef(); VcTask task = destroy(new IVcTaskCallback() { @Override public final void completeCB(VcTask task) { VcCache.purge(oldVm); if (oldRp != null) { VcCache.refresh(oldRp); } VcCache.removeVmRpPair(oldVm); } @Override public final void syncCB() { if (oldRp != null) { VcCache.sync(oldRp); } } }); task.waitForCompletion(); } catch (ManagedObjectNotFound e) { // The object is gone. Nothing to do. logger.info("cannot destroy " + this + ", not found."); } } @Override public void destroy() throws Exception { destroy(true); } @Override public void destroy(final boolean removeSnapShot) throws Exception { try { safeExecVmOp(new VmOp<Void>() { public Void exec() throws Exception { if (removeSnapShot) { removeAllSnapshots(); // PR 878822 } destroyInt(); return null; } }); } catch(Exception e) { throw VcException.DELETE_VM_FAILED(e, getName(), e.getMessage()); } } @Override public void unregister() throws Exception { final ManagedObjectReference oldRp = resourcePool; final ManagedObjectReference oldVm = getMoRef(); VirtualMachine vm = getManagedObject(); vm.unregister(); VcCache.purge(oldVm); VcCache.removeVmRpPair(oldVm); if (oldRp != null) { VcCache.refresh(oldRp); VcCache.sync(oldRp); } } private VcTask relocateDisksWork(DeviceId[] deviceIds, ManagedObjectReference dsMoRef, final IVcTaskCallback callback) throws Exception { final RelocateSpec relocSpec = new RelocateSpecImpl(); List<RelocateSpec.DiskLocator> diskList = new ArrayList<RelocateSpec.DiskLocator>(); for(DeviceId deviceId : deviceIds) { VirtualDevice device = getVirtualDevice(deviceId); RelocateSpec.DiskLocator disk = new RelocateSpecImpl.DiskLocatorImpl(); disk.setDatastore(dsMoRef); disk.setDiskId(device.getKey()); diskList.add(disk); } relocSpec.setDisk(diskList.toArray(new RelocateSpec.DiskLocator[diskList.size()])); VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.RelocateVm, vm.relocate(relocSpec, VirtualMachine.MovePriority.defaultPriority), callback); } }); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#relocateDisks() */ @Override public void relocateDisks(DeviceId[] deviceIds, VcDatastore ds) throws Exception { VcTask task = relocateDisksWork(deviceIds, ds.getMoRef(), VcCache.getRefreshAllVcTaskCB(this)); task.waitForCompletion(); setNeedsStorageInfoRefresh(true); } /* * Clone a new VM (low level code). */ private VcTask cloneWork(final VcDatacenter dc, ManagedObjectReference rpMoRef, ManagedObjectReference dsMoRef, ManagedObjectReference snapMoRef, final ManagedObjectReference folderMoRef, ManagedObjectReference hostMoRef, boolean isLinked, final String name, ConfigSpec config, final IVcTaskCallback callback) throws Exception { final CloneSpec spec = new CloneSpecImpl(); RelocateSpec relocSpec = new RelocateSpecImpl(); relocSpec.setPool(rpMoRef); relocSpec.setDatastore(dsMoRef); if (hostMoRef != null) { relocSpec.setHost(hostMoRef); } if (isLinked) { relocSpec.setDiskMoveType("createNewChildDiskBacking"); } spec.setLocation(relocSpec); spec.setSnapshot(snapMoRef); spec.setTemplate(false); spec.setConfig(config); VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.CloneVm, vm.clone(folderMoRef == null ? dc.getVmFolderMoRef() : folderMoRef, name, spec), callback); } }); logger.debug("clone_vm task on " + this + " to VM[" + name + "] created"); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#cloneTemplate(java.lang.String, com.vmware.aurora.vc.VcResourcePool, com.vmware.aurora.vc.VcDatastore, com.vmware.vim.binding.vim.vm.ConfigSpec, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask cloneTemplate(String name, VcResourcePool rp, VcDatastore ds, ConfigSpec config, IVcTaskCallback callback) throws Exception { AuAssert.check(isTemplate()); AuAssert.check(rp != null && ds != null); // All disks of a template VM must reside on the same datastore. AuAssert.check(datastores.size() == 1); /* * XXX * Currently an incremental backup VM is cloned from the same template as * the one used for DBVMs. After that issue is resolved, this assert will either * be removed or uncommented. */ //AuAssert.check(ds.getMoRef().equals(datastore[0])); VcDatacenter dc = rp.getVcCluster().getDatacenter(); /* * To support link-clone, a snapshot of the VM must have be taken * before being marked as a template. We then use the snapshot to clone. */ return cloneWork(dc, rp.getMoRef(), ds.getMoRef(), currentSnapshot, null, null, true, name, config, callback); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#cloneTemplate(java.lang.String, com.vmware.aurora.vc.VcResourcePool, com.vmware.aurora.vc.VcDatastore, com.vmware.vim.binding.vim.vm.ConfigSpec) */ @Override public VcVirtualMachine cloneTemplate(String name, VcResourcePool rp, VcDatastore ds, ConfigSpec config) throws Exception { VcTask task = cloneTemplate(name, rp, ds, config, VcCache.getRefreshVcTaskCB(rp)); return (VcVirtualMachine)task.waitForCompletion(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#cloneVm(java.lang.String, com.vmware.aurora.vc.VcResourcePool, com.vmware.aurora.vc.VcDatastore, com.vmware.vim.binding.vim.Folder, boolean, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask cloneVm(String name, VcResourcePool rp, VcDatastore ds, Folder folder, boolean isLinked, IVcTaskCallback callback) throws Exception { VcDatacenter dc = rp.getVcCluster().getDatacenter(); ManagedObjectReference snapMoRef = null; if (isLinked) { // To support link-clone, a snapshot of the VM must have been taken. // We use the current snapshot to clone. snapMoRef = currentSnapshot; } return cloneWork(dc, rp.getMoRef(), ds.getMoRef(), snapMoRef, folder == null ? null : folder._getRef(), null, isLinked, name, null, callback); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#cloneVm(java.lang.String, com.vmware.aurora.vc.VcResourcePool, com.vmware.aurora.vc.VcDatastore, com.vmware.vim.binding.vim.Folder, boolean) */ @Override public VcVirtualMachine cloneVm(String name, VcResourcePool rp, VcDatastore ds, Folder folder, boolean isLinked) throws Exception { VcTask task = cloneVm(name, rp, ds, folder, isLinked, VcCache.getRefreshVcTaskCB(rp)); task.waitForCompletion(); return (VcVirtualMachine)task.getResult(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#cloneSnapshot(java.lang.String, com.vmware.aurora.vc.VcResourcePool, com.vmware.aurora.vc.VcSnapshot, com.vmware.vim.binding.vim.Folder, boolean, com.vmware.vim.binding.vim.vm.ConfigSpec, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask cloneSnapshot(String name, VcResourcePool rp, VcDatastore ds, VcSnapshot snap, Folder folder, VcHost host, boolean isLinked, ConfigSpec config, IVcTaskCallback callback) throws Exception { // no change to ds AuAssert.check(!isTemplate()); AuAssert.check(snap != null); final VcDatacenter dc = getDatacenter(); ManagedObjectReference dsMoRef = null; if (ds != null) { dsMoRef = ds.getMoRef(); } return cloneWork(dc, rp.getMoRef(), dsMoRef, snap.getMoRef(), folder == null ? null : folder._getRef(), host == null ? null : host.getMoRef(), isLinked, name, config, callback); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#cloneSnapshot(java.lang.String, com.vmware.aurora.vc.VcResourcePool, com.vmware.aurora.vc.VcSnapshot, com.vmware.vim.binding.vim.Folder, boolean, com.vmware.vim.binding.vim.vm.ConfigSpec) */ @Override public VcVirtualMachine cloneSnapshot(String name, VcResourcePool rp, VcDatastore ds, VcSnapshot snap, Folder folder, VcHost host, boolean isLinked, ConfigSpec config) throws Exception { VcTask task = cloneSnapshot(name, rp, ds, snap, folder, host, isLinked, config, VcCache.getRefreshVcTaskCB(rp)); return (VcVirtualMachine)task.waitForCompletion(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#cloneSnapshot(java.lang.String, com.vmware.aurora.vc.VcResourcePool, com.vmware.aurora.vc.VcSnapshot, com.vmware.vim.binding.vim.Folder, boolean, com.vmware.vim.binding.vim.vm.ConfigSpec) */ @Override public VcVirtualMachine cloneSnapshot(String name, VcResourcePool rp, VcSnapshot snap, Folder folder, VcHost host, boolean isLinked, ConfigSpec config) throws Exception { return cloneSnapshot(name, rp, null, snap, folder, host, isLinked, config); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#createSnapshot(java.lang.String, java.lang.String, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask createSnapshot(final String name, final String description, final IVcTaskCallback callback) throws Exception { VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.Snapshot, vm.createSnapshot(name, description, false, false), VcVirtualMachineImpl.this, callback); } }); logger.debug("snap_vm task on " + this + ":" + name + " created"); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#createSnapshot(java.lang.String, java.lang.String) */ @Override public VcSnapshot createSnapshot(final String name, final String description) throws Exception { VcTask task = createSnapshot(name, description, VcCache.getRefreshVcTaskCB(this)); return (VcSnapshot)task.waitForCompletion(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#createSnapshot(com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask removeAllSnapshots(final IVcTaskCallback callback) throws Exception { VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.RemoveSnap, vm.removeAllSnapshots(true), callback); } }); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#removeAllSnapshots() */ @Override public void removeAllSnapshots() throws Exception { VcTask task = removeAllSnapshots(VcCache.getRefreshVcTaskCB(this)); task.waitForCompletion(); } /* * Insert and update all snapshots into a newMap from the oldMap. */ private void updateSnapshotTree(Map<ManagedObjectReference, VcSnapshotImpl> newMap, Map<ManagedObjectReference, VcSnapshotImpl> oldMap, final SnapshotTree[] list) throws Exception { if (list != null) { for (SnapshotTree snap : list) { ManagedObjectReference mo = snap.getSnapshot(); String name = snap.getName(); VcSnapshotImpl snapObj = oldMap.get(mo); if (snapObj == null) { snapObj = VcObjectImpl.loadSnapshotFromMoRef(mo, this, name); } else { snapObj.update(); } newMap.put(mo, snapObj); updateSnapshotTree(newMap, oldMap, snap.getChildSnapshotList()); } } } /* * Updates all snapshots of this VM. */ private synchronized void updateSnapshots(VirtualMachine vm) throws Exception { SnapshotInfo snapInfo = vm.getSnapshot(); if (snapInfo != null) { Map<ManagedObjectReference, VcSnapshotImpl> newMap = new HashMap<ManagedObjectReference, VcSnapshotImpl>(); updateSnapshotTree(newMap, snapshots, snapInfo.getRootSnapshotList()); snapshots = newMap; currentSnapshot = snapInfo.getCurrentSnapshot(); } else { snapshots.clear(); currentSnapshot = null; } } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getSnapshotByName(java.lang.String) */ @Override public synchronized VcSnapshot getSnapshotByName(final String name) { for (VcSnapshotImpl snap : snapshots.values()) { if (snap.getName().equals(name)) { return snap; } } return null; } protected synchronized VcSnapshot getSnapshot(ManagedObjectReference moref) { VcSnapshot snap = snapshots.get(moref); if (snap == null) { throw VcException.INVALID_MOREF(MoUtil.morefToString(moref)); } return snap; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getSnapshotById(String) */ @Override public VcSnapshot getSnapshotById(String id) { return getSnapshot(MoUtil.stringToMoref(id)); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getCurrentSnapshot() */ @Override public VcSnapshot getCurrentSnapshot() { return getSnapshot(currentSnapshot); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#reconfigure(com.vmware.vim.binding.vim.vm.ConfigSpec, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask reconfigure(final ConfigSpec spec, final IVcTaskCallback callback) throws Exception { VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); spec.setName(MoUtil.toURLString(spec.getName())); return new VcTask(VcTask.TaskType.ReconfigVm, vm.reconfigure(spec), callback); } }); return task; } private boolean reconfigureInt(final ConfigSpec spec) throws Exception { VcTask task = reconfigure(spec, VcCache.getRefreshAllVcTaskCB(this)); task.waitForCompletion(); setNeedsStorageInfoRefresh(true); return task.taskCompleted(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#reconfigure(com.vmware.vim.binding.vim.vm.ConfigSpec) */ @Override public boolean reconfigure(final ConfigSpec spec) throws Exception { return safeExecVmOp(new VmOp<Boolean>() { public Boolean exec() throws Exception { return reconfigureInt(spec); } }); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#promoteDisk(com.vmware.vim.binding.vim.vm.device.VirtualDisk, boolean, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask promoteDisk(final VirtualDisk disk, final boolean unlink, final IVcTaskCallback callback) throws Exception { final VirtualDisk[] diskArray = { disk }; VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.PromoteDisks, vm.promoteDisks(unlink, diskArray), callback); } }); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#promoteDisk(com.vmware.vim.binding.vim.vm.device.VirtualDisk, boolean) */ @Override public void promoteDisk(VirtualDisk disk, boolean unlink) throws Exception { VcTask task = promoteDisk(disk, unlink, VcCache.getRefreshAllVcTaskCB(this)); task.waitForCompletion(); setNeedsStorageInfoRefresh(true); } /** * @see VcVirtualMachine#promoteDisks(DeviceId[], IVcTaskCallback) */ @Override public VcTask promoteDisks(DeviceId[] diskIds, final IVcTaskCallback callback) throws Exception { final VirtualDisk[] diskArray = new VirtualDisk[diskIds.length]; for (int i = 0; i < diskIds.length; i++) { diskArray[i] = (VirtualDisk)getVirtualDevice(diskIds[i]); AuAssert.check(diskArray[i] != null); } VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.PromoteDisks, vm.promoteDisks(true, diskArray), callback); } }); return task; } @Override public void promoteDisks(DeviceId[] diskIds) throws Exception { promoteDisks(diskIds, VcCache.getRefreshAllVcTaskCB(this)).waitForCompletion(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#markAsTemplate() */ @Override public void markAsTemplate() throws Exception { AuAssert.check(VcContext.isInTaskSession()); VirtualMachine vm = getManagedObject(); vm.markAsTemplate(); update(); } /* * (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualMachine#markAsVirtualMachine() */ @Override public void markAsVirtualMachine(VcResourcePool rp, String hostName) throws Exception { AuAssert.check(VcContext.isInTaskSession()); VirtualMachine vm = getManagedObject(); List<VcHost> hosts = rp.getVcCluster().getHosts(); VcHost targetHost = null; if (hostName != null) { for (VcHost host : hosts) { if (host.getName().equals(hostName)) { targetHost = host; break; } } if (targetHost == null) { // TODO: throw Exception } } else { targetHost = hosts.get(0); } vm.markAsVirtualMachine(rp.getMoRef(), targetHost.getMoRef()); update(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getGuestVariables() */ @Override public Map<String, String> getGuestVariables() { // force update to get new guest variables VcVirtualMachine vm = VcCache.load(getMoRef()); // XXX We should be able to assert that vm == this, // but let's delay it to post 2.0. Map<String, String> guestVariables = new HashMap<String, String>(); for (OptionValue val : vm.getConfig().getExtraConfig()) { if (val.getKey().contains("guestinfo")) { if (val.getValue() != null) { guestVariables.put(val.getKey(), val.getValue().toString()); } else { // XXX This logging should be turned off if we are no longer curious. logger.info("got null val on " + val.getKey()); } } } return guestVariables; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getExtraConfigMap(java.lang.String) */ @Override public Map<String, String> getExtraConfigMap(String optionKey) { /* * update() is not needed, because only the CMS server should initiate * machine.id updates. */ for (OptionValue val : config.getExtraConfig()) { if (val.getKey().equals(optionKey)) { String value = (String) val.getValue(); if (value == null) { return null; } try { Gson gson = new Gson(); Type type = new TypeToken<Map<String, String>>(){}.getType(); return gson.fromJson(value, type); } catch (Throwable t) { logger.warn("Failed to parse " + optionKey + "=" + value); throw AuroraException.wrapIfNeeded(t); } } } return null; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getGuestConfigs() */ @Override public Map<String, String> getGuestConfigs() { Map<String, String> map = getExtraConfigMap(MACHINE_ID); if (map == null) { return new HashMap<String, String>(); } return map; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getDbvmConfig() */ @Override public Map<String, String> getDbvmConfig() throws Exception { Map<String, String> map = getExtraConfigMap(DBVM_CONFIG); if (map == null) { return new HashMap<String, String>(); } return map; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getGuestConfig(java.lang.String) */ @Override public String getGuestConfig(String key) { return getGuestConfigs().get(key); } /** * Set VMX extra config value encoded in JSON. * @param optionKey unique name of the config value * @param value object representing the config value */ private void setExtraConfig(String optionKey, Object value) throws Exception { VcTask task = setExtraConfig(optionKey, value, VcCache.getRefreshAllVcTaskCB(this)); task.waitForCompletion(); } /** * The async version of setExtraConfig. * @param callback The callback function. */ private VcTask setExtraConfig(String optionKey, Object value, final IVcTaskCallback callback) throws Exception { String jsonString = (new Gson()).toJson(value); ConfigSpec spec = new ConfigSpecImpl(); OptionValue[] extraConfig = new OptionValueImpl[1]; extraConfig[0] = new OptionValueImpl(optionKey, jsonString); spec.setExtraConfig(extraConfig); return reconfigure(spec, callback); } /** * Sends variables to guest via "machine.id" mechanism. Format: a JSON * encoded string created from Map<String, String>. * @param guestVariables */ private void setMachineIdVariables(Map<String, String> guestVariables) throws Exception { setExtraConfig(MACHINE_ID, guestVariables); } /** * The async version of setMachineIdVariables. * @param callback The callback function. */ private VcTask setMachineIdVariables(Map<String, String> guestVariables, final IVcTaskCallback callback) throws Exception { return setExtraConfig(MACHINE_ID, guestVariables, callback); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualMachine#setGuestConfigs(java.util.Map) */ @Override public void setGuestConfigs(Map<String, String> guestVariables) throws Exception { setMachineIdVariables(guestVariables); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualMachine#setGuestConfigs(java.util.Map) */ @Override public VcTask setGuestConfigs(Map<String, String> guestVariables, final IVcTaskCallback callback) throws Exception { return setMachineIdVariables(guestVariables, callback); } @Override public void setExtraConfig(Pair<String, String>[] configs) throws Exception { OptionValue[] extraConfigs = new OptionValueImpl[configs.length]; for (int i = configs.length - 1; i >= 0; --i) { extraConfigs[i] = new OptionValueImpl(configs[i].first, configs[i].second); } ConfigSpec spec = new ConfigSpecImpl(); spec.setExtraConfig(extraConfigs); reconfigure(spec); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#setDbvmConfig(java.util.Map) */ @Override public void setDbvmConfig(Map<String, String> config) throws Exception { setExtraConfig(DBVM_CONFIG, config); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getStorageUsage() */ @Override public DiskSize getStorageUsage() { return storageUsage; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getStorageCommitted() */ @Override public DiskSize getStorageCommitted() { return storageCommitted; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getFileLayout() */ @Override public FileLayoutEx getFileLayout() { return layoutEx; } /* * (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getIpAddresses() */ @Override public List<String> queryIpAddresses(long timeoutMs, int expectNum) throws Exception { long expTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs); while (true) { List<String> ipAddrList = queryIpAddresses(); /* * only when the size of IP address list equals to expected number, * stop retry. */ if (ipAddrList.size() != expectNum) { if (System.nanoTime() > expTime) { throw VcException.GUEST_TIMEOUT(); } Thread.sleep(2000); } else { return ipAddrList; } } } /* * (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualMachine#getIpAddresses() */ @Override public List<String> queryIpAddresses() throws Exception { List<String> ipAddrList = new ArrayList<String>(); VirtualMachine vm = getManagedObject(); if (vm.getGuest() != null) { GuestInfo.NicInfo[] nicInfoArray = vm.getGuest().getNet(); if (nicInfoArray != null) { for (GuestInfo.NicInfo nicInfo : nicInfoArray) { if (nicInfo != null && nicInfo.getIpConfig() != null && nicInfo.getIpConfig().getIpAddress() != null) { for (IpConfigInfo.IpAddress ip : nicInfo.getIpConfig().getIpAddress()) { if (IpConfigInfo.IpAddressStatus.preferred.toString().equals(ip.getState())) { ipAddrList.add(ip.getIpAddress()); } } } } } } return ipAddrList; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getResourcePool() */ @Override public VcResourcePool getResourcePool() { if (isTemplate()) { // template doesn't have resource pool throw VcException.INVALID_ARGUMENT(); } else { return VcCache.get(resourcePool); } } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getParentVApp() */ @Override public VcResourcePool getParentVApp() { return VcCache.get(parentVApp); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#migrate(java.lang.String, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask migrate(final VcResourcePool rp, final IVcTaskCallback callback) throws Exception { VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.MigrateVm, vm.migrate(rp.getMoRef(), null, VirtualMachine.MovePriority.defaultPriority, null), callback); } }); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#migrate(java.lang.String) */ @Override public void migrate(final VcResourcePool rp) throws Exception { final ManagedObjectReference oldRp = resourcePool; final ManagedObjectReference newRp = rp.getMoRef(); VcTask task = migrate(rp, new IVcTaskCallback() { @Override public void completeCB(VcTask task) { VcCache.refresh(moRef); VcCache.refresh(oldRp); VcCache.refresh(newRp); } @Override public void syncCB() { VcCache.sync(moRef); VcCache.sync(oldRp); VcCache.sync(newRp); } }); task.waitForCompletion(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#migrate(java.lang.String, com.vmware.aurora.vc.IVcTaskCallback) */ @Override public VcTask migrate(final VcHost host, final IVcTaskCallback callback) throws Exception { VcTask task = VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.MigrateVm, vm.migrate(null, host.getMoRef(), VirtualMachine.MovePriority.defaultPriority, null), callback); } }); return task; } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#migrate(java.lang.String) */ @Override public void migrate(final VcHost host) throws Exception { VcTask task = migrate(host, new IVcTaskCallback() { @Override public void completeCB(VcTask task) { VcCache.refresh(moRef); } @Override public void syncCB() { VcCache.sync(moRef); } }); task.waitForCompletion(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getCpuReservationHZ() */ @Override public Long getCpuReservationHZ() { return config.getCpuAllocation().getReservation(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getMemReservationMB() */ @Override public Long getMemReservationMB() { return config.getMemoryAllocation().getReservation(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getMemSizeMB() */ @Override public Integer getMemSizeMB() { return config.getHardware().getMemoryMB(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getManagedBy() */ @Override public ManagedByInfo getManagedBy() { return config.getManagedBy(); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#setManagedBy(java.lang.String, java.lang.String) */ @Override public void setManagedBy(String owner, String type) throws Exception { VirtualMachine vm = getManagedObject(); ConfigSpec spec = new ConfigSpecImpl(); VmConfigUtil.addManagedByToConfigSpec(spec, owner, type); reconfigure(spec); update(vm); // propagate VC changes back to this object } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#isManagedByThisCms() */ @Override public boolean isManagedByThisCms() { ManagedByInfo manager = getManagedBy(); return manager != null && manager.getExtensionKey().equals(VcContext.getService().getExtensionKey()) && manager.getType().equals("dbvm"); } /* (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualmachine#getVmHAConfig() */ @Override public VcClusterConfig.VmHAConfig getVmHAConfig() { VcClusterConfig.VmHAConfig vmHAConfig = null; // Since the vm cluster information is stored with the cluster, we have to retrieve it VcCluster cluster = getResourcePool().getVcCluster(); vmHAConfig = cluster.getConfig().getDefaultVmHAConfig(); DasVmConfigInfo[] dasInfo = cluster.getVmConfigInfo(); if (dasInfo != null) { for (DasVmConfigInfo vmConfig : dasInfo) { if (vmConfig.getKey().equals(getMoRef())) { vmHAConfig = new VcClusterConfig.VmHAConfig(vmConfig, vmHAConfig); } } } return vmHAConfig; } /* * (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualMachine#queryGuest() */ @Override public GuestInfo queryGuest() throws Exception { VirtualMachine vm = getManagedObject(); return vm.getGuest(); } /* * (non-Javadoc) * @see com.vmware.aurora.vc.VcVirtualMachine#getSnapshots() */ @Override public synchronized List<VcSnapshot> getSnapshots() { return new ArrayList<VcSnapshot>(snapshots.values()); } @Override public GuestVarReturnCode waitForPowerOnResult(Integer timeOutSecs) throws Exception { long finishNanos = 0; Map<String, String> guestVariables; GuestVarReturnCode returnCode; if (timeOutSecs != null) { finishNanos = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeOutSecs); } while (true) { try { guestVariables = this.getGuestVariables(); } catch (Exception ex) { logger.error("Failed to get guest variables on VM " + this.getName(), ex); throw GuestVariableException.COMMUNICATION_ERROR(ex); } if (guestVariables != null && guestVariables.get("guestinfo.return_code") != null) { returnCode = new GuestVarReturnCode(guestVariables); if (!returnCode.isBusy()) { break; } } /* * Abort wait in two cases: * - target vm is powered-off * Unexpected, we generally wait for results from a VM that was just powered-on. * The fix is likely possible only via "Repair" and might take a while, so don't * tie up this thread. * - target vm is powered-on, but is truly stuck, so time-out. */ /* getGuestVariable() gets external vc event and will refresh the power state. * It doesn't update runtime state. */ if (isPoweredOff()) { logger.warn("waitForResult() aborted for powered-off vm " + this.getName()); throw GuestVariableException.POWERED_OFF(); } if (timeOutSecs != null && System.nanoTime() >= finishNanos) { logger.warn("waitForStartupResult() aborted due to time-out for vm" + this.getName()); throw GuestVariableException.TIMEOUT(); } Thread.sleep(GUEST_VAR_CHECK_INTERVAL); } logger.info(returnCode.getGuestVariables() + " guest variables are returned from vm " + this.getName() + " return code: " + returnCode.getStatusMsg()); if (returnCode.isError()) { throw GuestVariableException.RETURN_CODE_ERROR(returnCode.getStatusMsg()); } return returnCode; } /** * Sets the needsStorageInfoRefresh flag. * @param value True if the storage info needs to be refreshed. */ private void setNeedsStorageInfoRefresh(boolean value) { this.needsStorageInfoRefresh = value; } private boolean needsStorageInfoRefresh() { return this.needsStorageInfoRefresh; } @Override public void updateVmNic(String pubNICLabel, String privNICLabel, String pubNetId, String privNetId) throws Exception { List<VirtualDeviceSpec> changes = new ArrayList<VirtualDeviceSpec>(); if (pubNetId != null) { VcNetwork pubNet = VcCache.get(pubNetId); if (pubNet != null) { changes.add(this.reconfigNetworkSpec(pubNICLabel, pubNet)); } } VcNetwork privNet = VcCache.get(privNetId); changes.add(this.reconfigNetworkSpec(privNICLabel, privNet)); this.reconfigure(VmConfigUtil.createConfigSpec(changes)); } @Override public void detachAllCdroms() throws Exception { AuAssert.check(VcContext.isInTaskSession()); List<VirtualDeviceSpec> changes = new ArrayList<VirtualDeviceSpec>(); for (VirtualDevice device : getDevice()) { if (device instanceof VirtualCdrom) { VirtualDeviceSpec spec = new VirtualDeviceSpecImpl(); spec.setDevice(device); spec.setOperation(VirtualDeviceSpec.Operation.remove); changes.add(spec); } } if (!changes.isEmpty()) { ConfigSpec config = new ConfigSpecImpl(); config.setDeviceChange(changes.toArray(new VirtualDeviceSpec[changes.size()])); reconfigure(config); } } @SuppressWarnings("deprecation") @Override public void modifyHASettings(DasVmSettings.RestartPriority restartPriority, DasVmSettings.IsolationResponse isolationResponse, DasConfigInfo.VmMonitoringState vmMonitoringState) throws Exception { AuAssert.check(VcContext.isInTaskSession()); ClusterComputeResource cluster = MoUtil.getManagedObject(getResourcePool().getVcCluster().getMoRef()); DasVmSettings dasVmSettings = null; boolean found = false; DasVmConfigInfo[] dasVmConfig = cluster.getConfiguration().getDasVmConfig(); if (dasVmConfig != null) { for (DasVmConfigInfo iter : dasVmConfig) { if (iter.getKey().equals(getMoRef())) { found = true; dasVmSettings = iter.getDasSettings(); break; } } } if (dasVmSettings == null) { dasVmSettings = new DasVmSettingsImpl(); } if (restartPriority != null) { dasVmSettings.setRestartPriority(restartPriority.name()); } if (isolationResponse != null) { dasVmSettings.setIsolationResponse(isolationResponse.name()); } VmToolsMonitoringSettings vmToolsMonitoringSettings = dasVmSettings.getVmToolsMonitoringSettings(); if (vmToolsMonitoringSettings == null) { // Use the default settings for VmToolsMonitoringSettings vmToolsMonitoringSettings = cluster.getConfiguration().getDasConfig().getDefaultVmSettings().getVmToolsMonitoringSettings(); dasVmSettings.setVmToolsMonitoringSettings(vmToolsMonitoringSettings); } if (vmMonitoringState != null) { vmToolsMonitoringSettings.setVmMonitoring(vmMonitoringState.name()); } DasVmConfigInfo dasVmConfigInfo = new DasVmConfigInfoImpl(); dasVmConfigInfo.setKey(getMoRef()); dasVmConfigInfo.setDasSettings(dasVmSettings); ConfigSpecExImpl configSpec = new ConfigSpecExImpl(); configSpec.setDasVmConfigSpec(new DasVmConfigSpec[] {new DasVmConfigSpecImpl(found? ArrayUpdateSpec.Operation.edit : ArrayUpdateSpec.Operation.add, null, dasVmConfigInfo)}); getResourcePool().getVcCluster().reconfigure(configSpec); } @Override public VcTask turnOnFT(final VcHost host, final IVcTaskCallback callback) throws Exception { return VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.TurnOnFT, vm.createSecondary(host == null ? null : host.getMoRef()), callback); } }); } @Override public void turnOnFT(VcHost host) throws Exception { VcTask task = turnOnFT(host, new IVcTaskCallback () { @Override public void completeCB(VcTask task) { VcCache.refresh(moRef); } @Override public void syncCB() { VcCache.sync(moRef); } }); task.waitForCompletion(); } @Override public VcTask turnOffFT(final IVcTaskCallback callback) throws Exception { return VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { VirtualMachine vm = getManagedObject(); return new VcTask(VcTask.TaskType.TurnOffFT, vm.turnOffFaultTolerance(), callback); } }); } @Override public void turnOffFT() throws Exception { VcTask task = turnOffFT(new IVcTaskCallback () { @Override public void completeCB(VcTask task) { VcCache.refresh(moRef); } @Override public void syncCB() { VcCache.sync(moRef); } }); task.waitForCompletion(); } private VcTask toggleFT(final boolean enable, final IVcTaskCallback callback, final VirtualMachine primaryVm, final ManagedObjectReference secondaryVMRef) throws Exception { return VcContext.getTaskMgr().execute(new VcTaskMgr.IVcTaskBody() { public VcTask body() throws Exception { return new VcTask(enable ? VcTask.TaskType.EnableFT : VcTask.TaskType.DisableFT, enable ? primaryVm.enableSecondary(secondaryVMRef, null) : primaryVm.disableSecondary(secondaryVMRef), callback); } }); } private void toggleFT(final boolean enable) throws Exception { VcTask task = toggleFT(enable, new IVcTaskCallback () { @Override public void completeCB(VcTask task) { VcCache.refresh(moRef); } @Override public void syncCB() { VcCache.sync(moRef); } }); task.waitForCompletion(); } private VcTask toggleFT(final boolean enable, final IVcTaskCallback callback) throws Exception { ManagedObjectReference secondaryVMRef = null; VirtualMachine vm = getManagedObject(); FaultToleranceConfigInfo ftConfigInfo = vm.getConfig().getFtInfo(); if (ftConfigInfo instanceof FaultTolerancePrimaryConfigInfo) { FaultTolerancePrimaryConfigInfo primaryFtConfigInfo = (FaultTolerancePrimaryConfigInfo)ftConfigInfo; AuAssert.check(primaryFtConfigInfo.getSecondaries().length == 1); secondaryVMRef = primaryFtConfigInfo.getSecondaries()[0]; } else { AuAssert.check(false, "Should not reach here"); } return toggleFT(enable, callback, vm, secondaryVMRef); } @Override public VcTask enableFT(IVcTaskCallback callback) throws Exception { return toggleFT(true, callback); } @Override public void enableFT() throws Exception { toggleFT(true); } @Override public VcTask disableFT(final IVcTaskCallback callback) throws Exception { return toggleFT(false, callback); } @Override public void disableFT() throws Exception { toggleFT(false); } @Override public VcTask disableDrs() throws Exception { AuAssert.check(VcContext.isInTaskSession()); ClusterComputeResource cluster = MoUtil.getManagedObject(getResourcePool().getVcCluster().getMoRef()); boolean found = false; DrsVmConfigInfo[] drsVmConfig = cluster.getConfiguration().getDrsVmConfig(); if (drsVmConfig != null) { for (DrsVmConfigInfo iter : drsVmConfig) { if (iter.getKey().equals(getMoRef())) { found = true; break; } } } DrsVmConfigInfo drsVmConfigInfo = new DrsVmConfigInfoImpl(); drsVmConfigInfo.setKey(getMoRef()); drsVmConfigInfo.setEnabled(false); ConfigSpecExImpl configSpec = new ConfigSpecExImpl(); configSpec.setDrsVmConfigSpec(new DrsVmConfigSpec[]{new DrsVmConfigSpecImpl(found ? ArrayUpdateSpec.Operation.edit : ArrayUpdateSpec.Operation.add, null, drsVmConfigInfo)}); return getResourcePool().getVcCluster().reconfigure(configSpec, VcCache.getRefreshRuntimeVcTaskCB(this)); } @Override public VcVirtualMachine cloneVm(final CreateSpec vmSpec, final DeviceId[] removeDisks) throws Exception { VcTask task = cloneVmAsync(vmSpec, removeDisks); return ((VcVirtualMachine)task.waitForCompletion()); } @Override public VcTask cloneVmAsync(final CreateSpec vmSpec, final DeviceId[] removeDisks) throws Exception { final VcSnapshot parentVcSnap = vmSpec.getParentSnapshot(); final ConfigSpec configSpec = (vmSpec.spec != null ? vmSpec.spec : new ConfigSpecImpl()); List<VirtualDeviceSpec> devChanges = new ArrayList<VirtualDeviceSpec>(); /* * No device changes should be set already. */ if (configSpec.getDeviceChange() != null && configSpec.getDeviceChange().length > 0) { throw AuAssert.INTERNAL(); } /* * Append config for removing disks. */ if (removeDisks != null) { for (DeviceId deviceId : removeDisks) { VirtualDevice dev = parentVcSnap.getVirtualDevice(deviceId); if (dev != null) { devChanges.add(VmConfigUtil.removeDeviceSpec(dev)); } } } if (!devChanges.isEmpty()) { configSpec.setDeviceChange(devChanges.toArray(new VirtualDeviceSpec[devChanges.size()])); } switch (vmSpec.cloneType) { case FULL: return vmSpec.getParentVm().cloneSnapshot(vmSpec.name, vmSpec.rp, vmSpec.ds, parentVcSnap, vmSpec.folder, vmSpec.host, false/*not linked*/, configSpec, VcCache.getRefreshVcTaskCB(vmSpec.rp)); case LINKED: return vmSpec.getParentVm().cloneSnapshot(vmSpec.name, vmSpec.rp, vmSpec.ds, parentVcSnap, vmSpec.folder, vmSpec.host, true/*linked*/, configSpec, VcCache.getRefreshVcTaskCB(vmSpec.rp)); default: VcTask vcTask = handleUnknownCloneType(vmSpec); if(vcTask == null) { throw AuAssert.INTERNAL(new RuntimeException("Unsupported Clone Type: " + vmSpec.cloneType)); } else { return vcTask; } } } protected VcTask handleUnknownCloneType(CreateSpec vmSpec) throws Exception { return null; } /** * Change the VM disks layout. * * @param removeDisks disks to be removed * @param addDisks disks to be added */ @Override public void changeDisks(final DeviceId[] removeDisks, final DiskCreateSpec[] addDisks) throws Exception { final ConfigSpec configSpec = new ConfigSpecImpl(); final List<VirtualDeviceSpec> devChanges = new ArrayList<VirtualDeviceSpec>(); if (removeDisks != null) { for (DeviceId deviceId : removeDisks) { VirtualDevice dev = getVirtualDevice(deviceId); if (dev != null) { devChanges.add(VmConfigUtil.removeDeviceSpec(dev)); } } } if (addDisks != null) { for (DiskCreateSpec spec : addDisks) { devChanges.add(spec.getVcSpec(VcVirtualMachineImpl.this)); } } configSpec.setDeviceChange(devChanges.toArray(new VirtualDeviceSpec[devChanges.size()])); reconfigure(configSpec); } public Folder getParentFolder() { VirtualMachine vm = this.getManagedObject(); ManagedObjectReference mo = vm.getParent(); if (mo != null) { return MoUtil.getManagedObject(mo); } else { return null; } } }