/***************************************************************************
* Copyright (c) 2012-2013 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 java.util.List;
import com.google.gson.Gson;
import com.vmware.aurora.global.Configuration;
import com.vmware.aurora.global.DiskSize;
import com.vmware.aurora.util.AuAssert;
import com.vmware.vim.binding.impl.vim.DescriptionImpl;
import com.vmware.vim.binding.impl.vim.ext.ManagedByInfoImpl;
import com.vmware.vim.binding.impl.vim.option.OptionValueImpl;
import com.vmware.vim.binding.impl.vim.vm.ConfigSpecImpl;
import com.vmware.vim.binding.impl.vim.vm.device.ParaVirtualSCSIControllerImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualBusLogicControllerImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualCdromImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualDeviceImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualDeviceSpecImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualDiskImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualE1000Impl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualEthernetCardImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualFloppyImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualLsiLogicControllerImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualLsiLogicSASControllerImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualSCSIControllerImpl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualVmxnet3Impl;
import com.vmware.vim.binding.impl.vim.vm.device.VirtualVmxnetImpl;
import com.vmware.vim.binding.vim.Description;
import com.vmware.vim.binding.vim.ext.ManagedByInfo;
import com.vmware.vim.binding.vim.option.OptionValue;
import com.vmware.vim.binding.vim.vm.ConfigSpec;
import com.vmware.vim.binding.vim.vm.device.ParaVirtualSCSIController;
import com.vmware.vim.binding.vim.vm.device.VirtualBusLogicController;
import com.vmware.vim.binding.vim.vm.device.VirtualController;
import com.vmware.vim.binding.vim.vm.device.VirtualDevice;
import com.vmware.vim.binding.vim.vm.device.VirtualDeviceSpec;
import com.vmware.vim.binding.vim.vm.device.VirtualDisk;
import com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode;
import com.vmware.vim.binding.vim.vm.device.VirtualEthernetCard;
import com.vmware.vim.binding.vim.vm.device.VirtualLsiLogicController;
import com.vmware.vim.binding.vim.vm.device.VirtualLsiLogicSASController;
import com.vmware.vim.binding.vim.vm.device.VirtualSCSIController;
import com.vmware.vim.binding.vim.vm.device.VirtualSCSIController.Sharing;
/**
* Utility functions for help configuring virtual machine.
*/
public class VmConfigUtil {
public enum ScsiControllerType {
PVSCSI(ParaVirtualSCSIController.class, ParaVirtualSCSIControllerImpl.class),
LSILOGIC(VirtualLsiLogicController.class, VirtualLsiLogicControllerImpl.class),
LSILOGICSAS(VirtualLsiLogicSASController.class, VirtualLsiLogicSASControllerImpl.class),
BUSLOGIC(VirtualBusLogicController.class, VirtualBusLogicControllerImpl.class);
public final Class<? extends VirtualSCSIController> infClass;
public final Class<? extends VirtualSCSIControllerImpl> implClass;
ScsiControllerType(Class<? extends VirtualSCSIController> infClass,
Class<? extends VirtualSCSIControllerImpl> implClass) {
this.infClass = infClass;
this.implClass = implClass;
}
VirtualSCSIController createController() {
try {
return implClass.newInstance();
} catch (Exception e) {
AuAssert.INTERNAL(e);
return null;
}
}
public static ScsiControllerType findController(Class<? extends VirtualSCSIController> clazz) {
for (ScsiControllerType type : ScsiControllerType.values()) {
if (type.infClass == clazz) {
return type;
}
}
return null;
}
}
public enum EthernetControllerType {
E1000(VirtualE1000Impl.class), VMXNET(VirtualVmxnetImpl.class), VMXNET3(
VirtualVmxnet3Impl.class);
private final Class<? extends VirtualEthernetCardImpl> clazz;
EthernetControllerType(Class<? extends VirtualEthernetCardImpl> clazz) {
this.clazz = clazz;
}
VirtualEthernetCard createController() {
try {
return clazz.newInstance();
} catch (Exception e) {
AuAssert.INTERNAL(e);
return null;
}
}
}
private static final String HOT_ADD_CPU_KEY = "vcpu.hotadd";
private static final String HOT_ADD_MEMORY_KEY = "mem.hotadd";
public static final String MACHINE_ID = "machine.id";
public static final String DBVM_CONFIG = "dbvm.config";
/**
* Create a scsi controller device for a vm.
*
* @param type
* @param busNum
* @return virtual device spec of the controller
*/
public static VirtualDeviceSpec createControllerDevice(
ScsiControllerType type, int busNum) {
VirtualSCSIController controller = type.createController();
controller.setBusNumber(busNum);
controller.setHotAddRemove(true);
controller.setKey(-1);
controller.setSharedBus(Sharing.noSharing);
return addDeviceSpec(controller);
}
public static VirtualDeviceSpec removeDeviceSpec(int key) {
VirtualDevice dev = new VirtualDeviceImpl();
dev.setKey(key);
VirtualDeviceSpec devSpec = new VirtualDeviceSpecImpl();
devSpec.setOperation(VirtualDeviceSpec.Operation.remove);
devSpec.setDevice(dev);
return devSpec;
}
public static VirtualDeviceSpec removeDeviceSpec(VirtualDevice dev) {
VirtualDeviceSpec devSpec = new VirtualDeviceSpecImpl();
devSpec.setOperation(VirtualDeviceSpec.Operation.remove);
devSpec.setDevice(dev);
return devSpec;
}
public static VirtualDeviceSpec createNetworkDevice(
EthernetControllerType type) {
VirtualEthernetCard nic = type.createController();
nic.setKey(-1);
return addDeviceSpec(nic);
}
public static VirtualDeviceSpec createNetworkDevice(
EthernetControllerType type, String label, VcNetwork vN) {
VirtualDeviceSpec deviceSpec = createNetworkDevice(type);
VirtualDevice device = deviceSpec.getDevice();
Description deviceInfo = new DescriptionImpl();
deviceInfo.setSummary("XXX");
deviceInfo.setLabel(label);
device.setDeviceInfo(deviceInfo);
setVirtualDeviceBacking(device, vN.getBackingInfo());
return deviceSpec;
}
public static VirtualDeviceSpec addDeviceSpec(VirtualDevice dev) {
VirtualDeviceSpec devSpec = new VirtualDeviceSpecImpl();
devSpec.setOperation(VirtualDeviceSpec.Operation.add);
devSpec.setDevice(dev);
return devSpec;
}
/**
* Create a default scsi controller at bus 0.
*
* @return virtual device spec of the controller
*/
public static VirtualDeviceSpec createControllerDevice() {
return createControllerDevice(ScsiControllerType.LSILOGIC, 0);
}
/**
* Create a virtual disk backing object.
*
* @param vmdkPath
* full vmdk path in the datastore
* @param diskMode
* virtual disk access mode
* @return backing object
*/
public static VirtualDevice.BackingInfo createVmdkBackingInfo(
String vmdkPath, DiskMode diskMode) {
return createVmdkBackingInfo(vmdkPath, diskMode, null, null, null);
}
/**
* Create a virtual disk backing object.
*
* @param vmdkPath
* full vmdk path in the datastore
* @param diskMode
* virtual disk access mode
* @param parentBacking
* indicates creation of a child delta disk attached to this parent
* backing.
* @param thinDisk
* requests creation of a thin disk, null if the field should not
* be set
* @param eagerlyScrub Flag to indicate to the underlying filesystem whether the
* virtual disk backing file should be scrubbed completely at this time.
* @see com.vmware.vim.binding.vim.vm.device.VirtualDisk.FlatVer2BackingInfo#setEagerlyScrub
* @return backing object
*/
public static VirtualDevice.BackingInfo createVmdkBackingInfo(
String vmdkPath, DiskMode diskMode,
VirtualDisk.FlatVer2BackingInfo parentBacking, Boolean thinDisk, Boolean eagerlyScrub) {
VirtualDisk.FlatVer2BackingInfo vmdkBacking =
new VirtualDiskImpl.FlatVer2BackingInfoImpl();
vmdkBacking.setFileName(vmdkPath);
vmdkBacking.setDiskMode(diskMode.toString());
if (parentBacking != null) {
vmdkBacking.setParent(parentBacking);
}
if (thinDisk != null) {
vmdkBacking.setThinProvisioned(thinDisk);
}
if (eagerlyScrub != null) {
vmdkBacking.setEagerlyScrub(eagerlyScrub);
}
return vmdkBacking;
}
/**
* Create a virtual disk backing object. The disk will be under the VM
* directory on the specified datastore.
*
* @param vm
* virtual machine
* @param ds
* datastore (null if using the default VM datastore.)
* @param diskName
* virtual disk file name
* @param diskMode
* virtual disk access mode
* @param parentBacking
* indicates creation of a child delta disk attached to this parent
* backing.
* @return backing object
*/
public static VirtualDevice.BackingInfo createVmdkBackingInfo(
VcVirtualMachine vm, VcDatastore ds, String diskName,
DiskMode diskMode, VirtualDevice.BackingInfo parentBacking) {
String vmdkPath = VcFileManager.getDsPath(vm, ds, diskName);
return createVmdkBackingInfo(vmdkPath, diskMode,
(VirtualDisk.FlatVer2BackingInfo) parentBacking, null, null);
}
/**
* Create a virtual disk backing object. The disk will be under the VM
* directory on the specified datastore.
*
* @param vm
* virtual machine
* @param ds
* datastore (null if using the default VM datastore.)
* @param diskName
* virtual disk file name
* @param diskMode
* virtual disk access mode
* @return backing object
*/
public static VirtualDevice.BackingInfo createVmdkBackingInfo(
VcVirtualMachine vm, VcDatastore ds, String diskName, DiskMode diskMode, Boolean thin, Boolean eagerlyScrub) {
String vmdkPath = VcFileManager.getDsPath(vm, ds, diskName);
return createVmdkBackingInfo(vmdkPath, diskMode, null, thin, eagerlyScrub);
}
public static VirtualDevice.BackingInfo createVmdkBackingInfo(
VcVirtualMachine vm, VcDatastore ds, DiskType diskType) {
String vmdkPath = VcFileManager.getDsPath(vm, ds, diskType.getDiskName());
return createVmdkBackingInfo(vmdkPath, diskType.getDiskMode(), null,
diskType.isThinDisk(), null);
}
public static VirtualDevice.BackingInfo createVmdkBackingInfo(
VcVirtualMachine vm, VcDatastore ds, String diskName,
DiskMode diskMode, Boolean thinDisk) {
String vmdkPath = VcFileManager.getDsPath(vm, ds, diskName);
return createVmdkBackingInfo(vmdkPath, diskMode, null, thinDisk, null);
}
public static VirtualDevice.BackingInfo createVmdkBackingInfo(
VcVirtualMachine vm, String diskName, DiskMode diskMode) {
return createVmdkBackingInfo(vm, null, diskName, diskMode, false);
}
/**
* @return the full Datastore path of a virtual disk.
*/
public static String getVmdkPath(VirtualDisk vmdk) {
VirtualDisk.FileBackingInfo backing =
(VirtualDisk.FileBackingInfo) vmdk.getBacking();
return backing.getFileName();
}
/**
* Create a ISO backing object.
*
* @param isoPath
* pathname to the ISO image file.
* @return backing object
*/
public static VirtualDevice.BackingInfo createCdromBackingInfo(String isoPath) {
return new VirtualCdromImpl.IsoBackingInfoImpl(isoPath, null);
}
/**
* Set a new backing object for the virtual device. The state of the device
* will be "startConnected" and "connected".
*
* @param device
* the virtual device device
* @param backing
* the backing object
*/
public static void setVirtualDeviceBacking(VirtualDevice device,
VirtualDevice.BackingInfo backing) {
VirtualDevice.ConnectInfo connectInfo =
new VirtualDeviceImpl.ConnectInfoImpl();
connectInfo.setConnected(true);
connectInfo.setStartConnected(true);
device.setBacking(backing);
device.setConnectable(connectInfo);
}
/**
* Create a virtual disk object.
*
* @param controller
* virtual adapter to add the virtual disk
* @param unitNum
* slot number under the adapter
* @param backing
* backing of the virtual device
* @param capacity
* size of the disk
* @return virtual disk object
*/
public static VirtualDisk createVirtualDisk(VirtualDevice controller,
int unitNum, VirtualDevice.BackingInfo backing, DiskSize size) {
AuAssert.check(controller instanceof VirtualController);
VirtualDisk vmdk = new VirtualDiskImpl();
vmdk.setUnitNumber(unitNum);
vmdk.setControllerKey(controller.getKey());
if (size != null) {
vmdk.setCapacityInKB(size.getKiB());
}
setVirtualDeviceBacking(vmdk, backing);
return vmdk;
}
/**
* Create a VM configuration specification. The result {\link ConfigSpec} can
* be passed to {\link VcVirtualMachine.clone()} to clone a VM, or {\link
* VcVirtualMachine.reconfigure()} to reconfigure the VM.
*
* @param deviceChange
* a list of change descriptions
* @return configuration specification
* @throws Exception
*/
public static ConfigSpec createConfigSpec(
List<VirtualDeviceSpec> deviceChange) throws Exception {
ConfigSpec config = new ConfigSpecImpl();
config.setDeviceChange(deviceChange
.toArray(new VirtualDeviceSpec[deviceChange.size()]));
return config;
}
public static ConfigSpec createConfigSpec(VirtualDeviceSpec... deviceChanges)
throws Exception {
ConfigSpec config = new ConfigSpecImpl();
config.setDeviceChange(deviceChanges);
return config;
}
/**
* Wrapper around ConfigSpecImpl.setMemoryMB that also adjusts the maximum
* allowed balloon size, to keep it proportional to the memory size
*
* @param spec
* -- vm spec
* @param memSize
* -- memory size in MB
*/
public static void setMemoryAndBalloon(ConfigSpec spec, Long memSize) {
spec.setMemoryMB(memSize);
Long maxBalloon = memSize * 75 / 100;
OptionValue balloonOption =
new OptionValueImpl("sched.mem.maxmemctl", maxBalloon.toString());
spec.setExtraConfig(new OptionValue[] { balloonOption });
}
/**
* Get debug information of a virtual device
*
* @param device
* @return info
*/
public static String getVirtualDeviceInfo(VirtualDevice device) {
String type = device.getClass().getSimpleName();
if (device instanceof VirtualController) {
VirtualController controller = (VirtualController) device;
return device.getDeviceInfo().getLabel() + ":" + type + " bus="
+ controller.getBusNumber() + ",key=" + device.getKey();
} else {
String backingInfo = null;
VirtualDevice.BackingInfo backing = device.getBacking();
if (backing instanceof VirtualDisk.FileBackingInfo) {
if (device instanceof VirtualDisk) {
backingInfo =
((VirtualDisk.FileBackingInfo) backing).getFileName();
backingInfo +=
"(" + ((VirtualDisk) device).getCapacityInKB() + "KB)";
}
} else if (backing instanceof VirtualEthernetCard.NetworkBackingInfo) {
backingInfo =
((VirtualEthernetCard.NetworkBackingInfo) backing)
.getDeviceName();
} else if (backing != null) {
backingInfo = backing.toString();
}
return device.getDeviceInfo().getLabel() + ":" + type + " unit="
+ device.getUnitNumber() + ",key=" + device.getKey()
+ ",controllerKey=" + device.getControllerKey() + ",backing="
+ backingInfo;
}
}
public static void addManagedByToConfigSpec(ConfigSpec spec, String owner,
String type) {
ManagedByInfo managedBy = new ManagedByInfoImpl();
managedBy.setExtensionKey(owner);
managedBy.setType(type);
spec.setManagedBy(managedBy);
}
/**
* Set VMX extra config value encoded in JSON.
*
* @param optionKey
* unique name of the config value
* @param value
* object representing the config value
*/
public static void setExtraConfigInSpec(ConfigSpec spec, String optionKey,
Object value) {
String jsonString = (new Gson()).toJson(value);
OptionValue[] extraConfig = new OptionValueImpl[1];
extraConfig[0] = new OptionValueImpl(optionKey, jsonString);
spec.setExtraConfig(extraConfig);
}
public static void copyAttachDisk(VcVirtualMachine dstVcVm,
VcVirtualMachine srcVcVm, VcSnapshot srcVcSnap, DeviceId srcDiskId,
DeviceId dstDiskId, String diskName, DiskMode diskMode,
VcDatastore ds, String tmpSnapName, String tmpSnapDescription,
boolean isChangeTrackingEnabled, boolean fullCopy) throws Exception {
VcSnapshot tmpSnap = null;
if (srcVcSnap == null) {
/*
* Testing shows that a child delta disk can only be attached to a
* disk that belongs to a snapshot, so create a temporary snapshot.
*/
// XXX assert srcVcVm.isPoweredOff(); This assert sometimes fails due to a race.
srcVcSnap = srcVcVm.createSnapshot(tmpSnapName, tmpSnapDescription);
tmpSnap = srcVcSnap;
}
dstVcVm.attachChildDisk(dstDiskId, srcVcSnap, srcDiskId, ds, diskName,
diskMode);
if (fullCopy) {
dstVcVm.promoteDisk(dstDiskId);
/*
* See PR 823579 for an explanation of the following hack.
* To work-around an ESX-side bug, we need to delete the tracker file
* per disk link used by ESX for changed block tracking (CBT) and then
* re-create it.
*
* For a VM which already has CBT enabled (eg. a DBVM), disabling and
* then re-enabling CBT achieves the above. However this does not work
* for a VM that does not have CBT enabled (eg. an external backup VM).
* For such a VM, an operation like changing the disk mode to any
* other mode and back, achieves the above.
*/
if (isChangeTrackingEnabled) {
dstVcVm.setRequestedChangeTracking(false);
}
AuAssert.check(!DiskMode.independent_persistent.equals(diskMode));
dstVcVm.editVirtualDisk(dstDiskId, DiskMode.independent_persistent);
if (isChangeTrackingEnabled) {
dstVcVm.setRequestedChangeTracking(true);
}
dstVcVm.editVirtualDisk(dstDiskId, diskMode);
}
if (tmpSnap != null) {
tmpSnap.remove();
}
}
public static ConfigSpec createEnableDisableHotAddConfigSpec(
boolean enableHotAddCpu, boolean enableHotAddMemory) {
ConfigSpec cfgSpec = new ConfigSpecImpl();
OptionValue hotAddMemory =
new OptionValueImpl(HOT_ADD_MEMORY_KEY, String.valueOf(
enableHotAddMemory).toUpperCase());
OptionValue hotAddCpu =
new OptionValueImpl(HOT_ADD_CPU_KEY, String
.valueOf(enableHotAddCpu).toUpperCase());
cfgSpec.setExtraConfig(new OptionValue[] { hotAddMemory, hotAddCpu });
return cfgSpec;
}
/**
* Create virtual device object for a CD-ROM/DVD-ROM device.
*
* @param backing
* the backing info for the new device
* @return cdrom the device spec
*/
public static VirtualDevice createCdromDevice(
VirtualDevice.BackingInfo backing) {
VirtualCdromImpl cdrom = new VirtualCdromImpl();
setVirtualDeviceBacking(cdrom, backing);
return cdrom;
}
public static VirtualDevice.BackingInfo createFloppyBackingInfo(
String flpPath) {
return new VirtualFloppyImpl.ImageBackingInfoImpl(flpPath, null);
}
public static VirtualDevice createFloppyDevice(
VirtualDevice.BackingInfo backing) {
VirtualFloppyImpl floppy = new VirtualFloppyImpl();
setVirtualDeviceBacking(floppy, backing);
return floppy;
}
/**
* Support workaround for PR 904771. Detach disk (FileOperation.destroy not
* set) is not enabled in vSphere 5.1 if snapshot exists. If this detach disk
* is disabled, we will use destroy disk to replace detach disk operation.
*/
private static final boolean DETACH_DISK_IS_ENABLED = false;
private static final String DETACH_DISK_IS_ENABLED_KEY =
"vc.detachDiskEnabled";
public static boolean isDetachDiskEnabled() {
return Configuration.getBoolean(DETACH_DISK_IS_ENABLED_KEY,
DETACH_DISK_IS_ENABLED);
}
/**
* Define whether use single VM operation solution in the Sagas such as
* repair/upgrade. Should change this setting according to work flow
* requirement. In vSphere 5.1, use this flag as a workaround. As destroy
* independent disks is not enabled if snapshot exist. Single VM operation
* solutions cannot work. So do not use single VM operation by default.
*/
private static final boolean SINGLE_VM_OPS_IS_ENABLED = false;
private static final String SINGLE_VM_OPS_IS_ENABLED_KEY =
"cms.singleVMOpsEnabled";
public static boolean isSingleVMOperationEnabled() {
return Configuration.getBoolean(SINGLE_VM_OPS_IS_ENABLED_KEY,
SINGLE_VM_OPS_IS_ENABLED);
}
}