/***************************************************************************
* 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;
}
}
}