| /** |
| * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. |
| */ |
| |
| package com.cloud.hypervisor.vmware.util;
|
|
|
| import java.io.BufferedWriter;
|
| import java.io.ByteArrayOutputStream;
|
| import java.io.IOException;
|
| import java.io.OutputStreamWriter;
|
| import java.io.PrintWriter;
|
| import java.io.StringWriter;
|
| import java.util.ArrayList; |
| import java.util.List;
|
| import java.util.Random; |
|
|
| import org.apache.log4j.Logger;
|
|
|
| import com.cloud.hypervisor.vmware.mo.DatacenterMO;
|
| import com.cloud.hypervisor.vmware.mo.DatastoreMO;
|
| import com.cloud.hypervisor.vmware.mo.HostMO; |
| import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; |
| import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
|
| import com.cloud.utils.Pair;
|
| import com.cloud.utils.Ternary;
|
| import com.cloud.utils.exception.ExceptionUtil;
|
| import com.vmware.vim25.DynamicProperty;
|
| import com.vmware.vim25.ManagedObjectReference;
|
| import com.vmware.vim25.MethodFault;
|
| import com.vmware.vim25.ObjectContent;
|
| import com.vmware.vim25.OptionValue;
|
| import com.vmware.vim25.ResourceAllocationInfo;
|
| import com.vmware.vim25.VirtualCdrom;
|
| import com.vmware.vim25.VirtualCdromIsoBackingInfo;
|
| import com.vmware.vim25.VirtualCdromRemotePassthroughBackingInfo;
|
| import com.vmware.vim25.VirtualDevice;
|
| import com.vmware.vim25.VirtualDeviceBackingInfo;
|
| import com.vmware.vim25.VirtualDeviceConnectInfo;
|
| import com.vmware.vim25.VirtualDisk;
|
| import com.vmware.vim25.VirtualDiskFlatVer1BackingInfo;
|
| import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
|
| import com.vmware.vim25.VirtualDiskMode;
|
| import com.vmware.vim25.VirtualDiskRawDiskMappingVer1BackingInfo;
|
| import com.vmware.vim25.VirtualDiskSparseVer1BackingInfo;
|
| import com.vmware.vim25.VirtualDiskSparseVer2BackingInfo;
|
| import com.vmware.vim25.VirtualE1000;
|
| import com.vmware.vim25.VirtualEthernetCard; |
| import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
|
| import com.vmware.vim25.VirtualMachineConfigSpec;
|
| import com.vmware.vim25.VirtualMachineSnapshotTree;
|
| import com.vmware.vim25.VirtualPCNet32; |
| import com.vmware.vim25.VirtualVmxnet2; |
| import com.vmware.vim25.VirtualVmxnet3; |
|
|
| public class VmwareHelper {
|
| private static final Logger s_logger = Logger.getLogger(VmwareHelper.class);
|
|
|
| public static VirtualDevice prepareNicDevice(VirtualMachineMO vmMo, ManagedObjectReference morNetwork, VirtualEthernetCardType deviceType,
|
| String portGroupName, String macAddress, int deviceNumber, int contextNumber, boolean conntected, boolean connectOnStart) throws Exception {
|
|
|
| VirtualEthernetCard nic; |
| switch(deviceType) { |
| case E1000 : |
| nic = new VirtualE1000(); |
| break; |
| |
| case PCNet32 : |
| nic = new VirtualPCNet32(); |
| break; |
| |
| case Vmxnet2 : |
| nic = new VirtualVmxnet2(); |
| break; |
| |
| case Vmxnet3 : |
| nic = new VirtualVmxnet3(); |
| break; |
| |
| default : |
| assert(false); |
| nic = new VirtualE1000(); |
| } |
|
|
| VirtualEthernetCardNetworkBackingInfo nicBacking = new VirtualEthernetCardNetworkBackingInfo();
|
| nicBacking.setDeviceName(portGroupName);
|
| nicBacking.setNetwork(morNetwork);
|
| nic.setBacking(nicBacking);
|
|
|
| VirtualDeviceConnectInfo connectInfo = new VirtualDeviceConnectInfo();
|
| connectInfo.setAllowGuestControl(true);
|
| connectInfo.setConnected(conntected);
|
| connectInfo.setStartConnected(connectOnStart);
|
| nic.setAddressType("Manual");
|
| nic.setConnectable(connectInfo);
|
| nic.setMacAddress(macAddress); |
| |
| nic.setUnitNumber(deviceNumber);
|
| nic.setKey(-contextNumber);
|
| return nic;
|
| }
|
|
|
| // vmdkDatastorePath: [datastore name] vmdkFilePath
|
| public static VirtualDevice prepareDiskDevice(VirtualMachineMO vmMo, int controllerKey, String vmdkDatastorePath,
|
| int sizeInMb, ManagedObjectReference morDs, int deviceNumber, int contextNumber) throws Exception {
|
|
|
| VirtualDisk disk = new VirtualDisk();
|
|
|
| VirtualDiskFlatVer2BackingInfo backingInfo = new VirtualDiskFlatVer2BackingInfo();
|
| backingInfo.setDiskMode(VirtualDiskMode.persistent.toString());
|
| backingInfo.setThinProvisioned(true);
|
| backingInfo.setEagerlyScrub(false);
|
| backingInfo.setDatastore(morDs);
|
| backingInfo.setFileName(vmdkDatastorePath);
|
| disk.setBacking(backingInfo);
|
|
|
| if(controllerKey < 0) |
| controllerKey = vmMo.getIDEDeviceControllerKey(); |
| if(deviceNumber < 0)
|
| deviceNumber = vmMo.getNextDeviceNumber(controllerKey);
|
| disk.setControllerKey(controllerKey); |
|
|
| disk.setKey(-contextNumber);
|
| disk.setUnitNumber(deviceNumber);
|
| disk.setCapacityInKB(sizeInMb*1024);
|
|
|
| VirtualDeviceConnectInfo connectInfo = new VirtualDeviceConnectInfo();
|
| connectInfo.setConnected(true);
|
| connectInfo.setStartConnected(true);
|
| disk.setConnectable(connectInfo);
|
|
|
| return disk;
|
| }
|
|
|
| // vmdkDatastorePath: [datastore name] vmdkFilePath, create delta disk based on disk from template
|
| public static VirtualDevice prepareDiskDevice(VirtualMachineMO vmMo, int controllerKey, String vmdkDatastorePath,
|
| int sizeInMb, ManagedObjectReference morDs, VirtualDisk templateDisk, int deviceNumber, int contextNumber) throws Exception {
|
|
|
| assert(templateDisk != null);
|
| VirtualDeviceBackingInfo parentBacking = templateDisk.getBacking();
|
| assert(parentBacking != null);
|
|
|
| // TODO Not sure if we need to check if the disk in template and the new disk needs to share the
|
| // same datastore
|
| VirtualDisk disk = new VirtualDisk();
|
| if(parentBacking instanceof VirtualDiskFlatVer1BackingInfo) {
|
| VirtualDiskFlatVer1BackingInfo backingInfo = new VirtualDiskFlatVer1BackingInfo();
|
| backingInfo.setDiskMode(((VirtualDiskFlatVer1BackingInfo)parentBacking).getDiskMode());
|
| backingInfo.setDatastore(morDs);
|
| backingInfo.setFileName(vmdkDatastorePath);
|
| backingInfo.setParent((VirtualDiskFlatVer1BackingInfo)parentBacking);
|
| disk.setBacking(backingInfo);
|
| } else if(parentBacking instanceof VirtualDiskFlatVer2BackingInfo) {
|
| VirtualDiskFlatVer2BackingInfo backingInfo = new VirtualDiskFlatVer2BackingInfo();
|
| backingInfo.setDiskMode(((VirtualDiskFlatVer2BackingInfo)parentBacking).getDiskMode());
|
| backingInfo.setDatastore(morDs);
|
| backingInfo.setFileName(vmdkDatastorePath);
|
| backingInfo.setParent((VirtualDiskFlatVer2BackingInfo)parentBacking);
|
| disk.setBacking(backingInfo);
|
| } else if(parentBacking instanceof VirtualDiskRawDiskMappingVer1BackingInfo) {
|
| VirtualDiskRawDiskMappingVer1BackingInfo backingInfo = new VirtualDiskRawDiskMappingVer1BackingInfo();
|
| backingInfo.setDiskMode(((VirtualDiskRawDiskMappingVer1BackingInfo)parentBacking).getDiskMode());
|
| backingInfo.setDatastore(morDs);
|
| backingInfo.setFileName(vmdkDatastorePath);
|
| backingInfo.setParent((VirtualDiskRawDiskMappingVer1BackingInfo)parentBacking);
|
| disk.setBacking(backingInfo);
|
| } else if(parentBacking instanceof VirtualDiskSparseVer1BackingInfo) {
|
| VirtualDiskSparseVer1BackingInfo backingInfo = new VirtualDiskSparseVer1BackingInfo();
|
| backingInfo.setDiskMode(((VirtualDiskSparseVer1BackingInfo)parentBacking).getDiskMode());
|
| backingInfo.setDatastore(morDs);
|
| backingInfo.setFileName(vmdkDatastorePath);
|
| backingInfo.setParent((VirtualDiskSparseVer1BackingInfo)parentBacking);
|
| disk.setBacking(backingInfo);
|
| } else if(parentBacking instanceof VirtualDiskSparseVer2BackingInfo) {
|
| VirtualDiskSparseVer2BackingInfo backingInfo = new VirtualDiskSparseVer2BackingInfo();
|
| backingInfo.setDiskMode(((VirtualDiskSparseVer2BackingInfo)parentBacking).getDiskMode());
|
| backingInfo.setDatastore(morDs);
|
| backingInfo.setFileName(vmdkDatastorePath);
|
| backingInfo.setParent((VirtualDiskSparseVer2BackingInfo)parentBacking);
|
| disk.setBacking(backingInfo);
|
| } else {
|
| throw new Exception("Unsupported disk backing: " + parentBacking.getClass().getCanonicalName());
|
| }
|
|
|
| if(controllerKey < 0)
|
| controllerKey = vmMo.getIDEDeviceControllerKey();
|
| disk.setControllerKey(controllerKey);
|
| if(deviceNumber < 0)
|
| deviceNumber = vmMo.getNextDeviceNumber(controllerKey); |
|
|
| disk.setKey(-contextNumber);
|
| disk.setUnitNumber(deviceNumber);
|
| disk.setCapacityInKB(sizeInMb*1024);
|
|
|
| VirtualDeviceConnectInfo connectInfo = new VirtualDeviceConnectInfo();
|
| connectInfo.setConnected(true);
|
| connectInfo.setStartConnected(true);
|
| disk.setConnectable(connectInfo);
|
| return disk;
|
| }
|
|
|
| // vmdkDatastorePath: [datastore name] vmdkFilePath
|
| public static VirtualDevice prepareDiskDevice(VirtualMachineMO vmMo, int controllerKey, String vmdkDatastorePathChain[],
|
| ManagedObjectReference morDs, int deviceNumber, int contextNumber) throws Exception {
|
|
|
| assert(vmdkDatastorePathChain != null);
|
| assert(vmdkDatastorePathChain.length >= 1);
|
|
|
| VirtualDisk disk = new VirtualDisk();
|
|
|
| VirtualDiskFlatVer2BackingInfo backingInfo = new VirtualDiskFlatVer2BackingInfo();
|
| backingInfo.setDatastore(morDs);
|
| backingInfo.setFileName(vmdkDatastorePathChain[0]);
|
| backingInfo.setDiskMode(VirtualDiskMode.persistent.toString());
|
| if(vmdkDatastorePathChain.length > 1) {
|
| String[] parentDisks = new String[vmdkDatastorePathChain.length - 1];
|
| for(int i = 0; i < vmdkDatastorePathChain.length - 1; i++)
|
| parentDisks[i] = vmdkDatastorePathChain[i + 1];
|
|
|
| setParentBackingInfo(backingInfo, morDs, parentDisks);
|
| }
|
|
|
| disk.setBacking(backingInfo);
|
|
|
| if(controllerKey < 0) |
| controllerKey = vmMo.getIDEDeviceControllerKey(); |
| if(deviceNumber < 0)
|
| deviceNumber = vmMo.getNextDeviceNumber(controllerKey);
|
|
|
| disk.setControllerKey(controllerKey);
|
| disk.setKey(-contextNumber);
|
| disk.setUnitNumber(deviceNumber);
|
|
|
| VirtualDeviceConnectInfo connectInfo = new VirtualDeviceConnectInfo();
|
| connectInfo.setConnected(true);
|
| connectInfo.setStartConnected(true);
|
| disk.setConnectable(connectInfo);
|
|
|
| return disk;
|
| }
|
|
|
| private static void setParentBackingInfo(VirtualDiskFlatVer2BackingInfo backingInfo,
|
| ManagedObjectReference morDs, String[] parentDatastorePathList) {
|
|
|
| VirtualDiskFlatVer2BackingInfo parentBacking = new VirtualDiskFlatVer2BackingInfo();
|
| parentBacking.setDatastore(morDs);
|
| parentBacking.setDiskMode(VirtualDiskMode.persistent.toString());
|
|
|
| if(parentDatastorePathList.length > 1) {
|
| String[] nextDatastorePathList = new String[parentDatastorePathList.length -1];
|
| for(int i = 0; i < parentDatastorePathList.length -1; i++)
|
| nextDatastorePathList[i] = parentDatastorePathList[i + 1];
|
| setParentBackingInfo(parentBacking, morDs, nextDatastorePathList);
|
| }
|
| parentBacking.setFileName(parentDatastorePathList[0]);
|
|
|
| backingInfo.setParent(parentBacking);
|
| }
|
|
|
| public static Pair<VirtualDevice, Boolean> prepareIsoDevice(VirtualMachineMO vmMo, String isoDatastorePath, ManagedObjectReference morDs,
|
| boolean connect, boolean connectAtBoot, int deviceNumber, int contextNumber) throws Exception {
|
|
|
| boolean newCdRom = false;
|
| VirtualCdrom cdRom = (VirtualCdrom )vmMo.getIsoDevice();
|
| if(cdRom == null) {
|
| newCdRom = true;
|
| cdRom = new VirtualCdrom(); |
|
|
| assert(vmMo.getIDEDeviceControllerKey() >= 0);
|
| cdRom.setControllerKey(vmMo.getIDEDeviceControllerKey());
|
| if(deviceNumber < 0)
|
| deviceNumber = vmMo.getNextIDEDeviceNumber();
|
| |
| cdRom.setUnitNumber(deviceNumber);
|
| cdRom.setKey(-contextNumber);
|
| }
|
|
|
| VirtualDeviceConnectInfo cInfo = new VirtualDeviceConnectInfo();
|
| cInfo.setConnected(connect);
|
| cInfo.setStartConnected(connectAtBoot);
|
| cdRom.setConnectable(cInfo);
|
|
|
| if(isoDatastorePath != null) {
|
| VirtualCdromIsoBackingInfo backingInfo = new VirtualCdromIsoBackingInfo();
|
| backingInfo.setFileName(isoDatastorePath);
|
| backingInfo.setDatastore(morDs);
|
| cdRom.setBacking(backingInfo);
|
| } else {
|
| VirtualCdromRemotePassthroughBackingInfo backingInfo = new VirtualCdromRemotePassthroughBackingInfo();
|
| backingInfo.setDeviceName("");
|
| cdRom.setBacking(backingInfo);
|
| }
|
|
|
| return new Pair<VirtualDevice, Boolean>(cdRom, newCdRom);
|
| }
|
|
|
| public static VirtualDisk getRootDisk(VirtualDisk[] disks) {
|
| if(disks.length == 1)
|
| return disks[0];
|
|
|
| // TODO : for now, always return the first disk as root disk
|
| return disks[0];
|
| }
|
|
|
| public static ManagedObjectReference findSnapshotInTree(VirtualMachineSnapshotTree[] snapTree, String findName) {
|
| assert(findName != null);
|
|
|
| ManagedObjectReference snapMor = null;
|
| if (snapTree == null)
|
| return snapMor;
|
|
|
| for (int i = 0; i < snapTree.length && snapMor == null; i++) {
|
| VirtualMachineSnapshotTree node = snapTree[i];
|
|
|
| if (node.getName().equals(findName)) {
|
| snapMor = node.getSnapshot();
|
| } else {
|
| VirtualMachineSnapshotTree[] childTree = node.getChildSnapshotList();
|
| snapMor = findSnapshotInTree(childTree, findName);
|
| }
|
| }
|
| return snapMor;
|
| }
|
|
|
| public static byte[] composeDiskInfo(List<Ternary<String, String, String>> diskInfo, int disksInChain, boolean includeBase) throws IOException {
|
|
|
| BufferedWriter out = null;
|
| ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
|
| try {
|
| out = new BufferedWriter(new OutputStreamWriter(bos));
|
|
|
| out.write("disksInChain=" + disksInChain);
|
| out.newLine();
|
|
|
| out.write("disksInBackup=" + diskInfo.size());
|
| out.newLine();
|
|
|
| out.write("baseDiskIncluded=" + includeBase);
|
| out.newLine();
|
|
|
| int seq = disksInChain - 1;
|
| for(Ternary<String, String, String> item : diskInfo) {
|
| out.write(String.format("disk%d.fileName=%s", seq, item.first()));
|
| out.newLine();
|
|
|
| out.write(String.format("disk%d.baseFileName=%s", seq, item.second()));
|
| out.newLine();
|
|
|
| if(item.third() != null) {
|
| out.write(String.format("disk%d.parentFileName=%s", seq, item.third()));
|
| out.newLine();
|
| }
|
| seq--;
|
| }
|
|
|
| out.newLine();
|
| } finally {
|
| if(out != null)
|
| out.close();
|
| }
|
|
|
| return bos.toByteArray();
|
| }
|
|
|
| public static OptionValue[] composeVncOptions(OptionValue[] optionsToMerge,
|
| boolean enableVnc, String vncPassword, int vncPort, String keyboardLayout) {
|
|
|
| int numOptions = 3; |
| boolean needKeyboardSetup = false; |
| if(keyboardLayout != null && !keyboardLayout.isEmpty()) { |
| numOptions++; |
| needKeyboardSetup = true; |
| } |
|
|
| if(optionsToMerge != null)
|
| numOptions += optionsToMerge.length;
|
|
|
| OptionValue[] options = new OptionValue[numOptions];
|
| int i = 0;
|
| if(optionsToMerge != null) {
|
| for(int j = 0; j < optionsToMerge.length; j++)
|
| options[i++] = optionsToMerge[j];
|
| }
|
|
|
| options[i] = new OptionValue();
|
| options[i].setKey("RemoteDisplay.vnc.enabled");
|
| options[i++].setValue(enableVnc ? "true" : "false");
|
|
|
| options[i] = new OptionValue();
|
| options[i].setKey("RemoteDisplay.vnc.password");
|
| options[i++].setValue(vncPassword);
|
|
|
| options[i] = new OptionValue();
|
| options[i].setKey("RemoteDisplay.vnc.port");
|
| options[i++].setValue("" + vncPort); |
| |
| if(needKeyboardSetup) { |
| options[i] = new OptionValue(); |
| options[i].setKey("RemoteDisplay.vnc.keymap"); |
| options[i++].setValue(keyboardLayout); |
| } |
|
|
| return options;
|
| }
|
|
|
| public static void setBasicVmConfig(VirtualMachineConfigSpec vmConfig, int cpuCount, int cpuSpeedMHz, int cpuReservedMhz,
|
| int memoryMB, int memoryReserveMB, String guestOsIdentifier, boolean limitCpuUse) {
|
|
|
| // VM config basics
|
| vmConfig.setMemoryMB((long)memoryMB);
|
| vmConfig.setNumCPUs(cpuCount);
|
|
|
| ResourceAllocationInfo cpuInfo = new ResourceAllocationInfo();
|
| if (limitCpuUse) { |
| cpuInfo.setLimit((long)cpuSpeedMHz); |
| } else { |
| cpuInfo.setLimit(-1L); |
| } |
| |
| cpuInfo.setReservation((long)cpuReservedMhz); |
| vmConfig.setCpuAllocation(cpuInfo);
|
|
|
| ResourceAllocationInfo memInfo = new ResourceAllocationInfo();
|
| memInfo.setLimit((long)memoryMB);
|
| memInfo.setReservation((long)memoryReserveMB);
|
| vmConfig.setMemoryAllocation(memInfo);
|
|
|
| vmConfig.setGuestId(guestOsIdentifier);
|
| }
|
|
|
| public static ManagedObjectReference getDiskDeviceDatastore(VirtualDisk diskDevice) throws Exception {
|
| VirtualDeviceBackingInfo backingInfo = diskDevice.getBacking();
|
| assert(backingInfo instanceof VirtualDiskFlatVer2BackingInfo);
|
| return ((VirtualDiskFlatVer2BackingInfo)backingInfo).getDatastore();
|
| }
|
|
|
| public static Object getPropValue(ObjectContent oc, String name) {
|
| DynamicProperty[] props = oc.getPropSet();
|
|
|
| for(DynamicProperty prop : props) {
|
| if(prop.getName().equalsIgnoreCase(name))
|
| return prop.getVal();
|
| }
|
|
|
| return null;
|
| }
|
|
|
| public static String getFileExtension(String fileName, String defaultExtension) {
|
| int pos = fileName.lastIndexOf('.');
|
| if(pos < 0)
|
| return defaultExtension;
|
|
|
| return fileName.substring(pos);
|
| }
|
|
|
| public static boolean isSameHost(String ipAddress, String destName) {
|
| // TODO : may need to do DNS lookup to compare IP address exactly
|
| return ipAddress.equals(destName);
|
| }
|
|
|
| public static void deleteVolumeVmdkFiles(DatastoreMO dsMo, String volumeName, DatacenterMO dcMo) throws Exception {
|
| String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), volumeName);
|
| dsMo.deleteFile(volumeDatastorePath, dcMo.getMor(), true);
|
|
|
| volumeDatastorePath = String.format("[%s] %s-flat.vmdk", dsMo.getName(), volumeName);
|
| dsMo.deleteFile(volumeDatastorePath, dcMo.getMor(), true);
|
|
|
| volumeDatastorePath = String.format("[%s] %s-delta.vmdk", dsMo.getName(), volumeName);
|
| dsMo.deleteFile(volumeDatastorePath, dcMo.getMor(), true);
|
| }
|
|
|
| public static String getExceptionMessage(Throwable e) {
|
| return getExceptionMessage(e, false);
|
| }
|
|
|
| public static String getExceptionMessage(Throwable e, boolean printStack) {
|
| if(e instanceof MethodFault) {
|
| final StringWriter writer = new StringWriter();
|
| writer.append("Exception: " + e.getClass().getName() + "\n");
|
| writer.append("message: " + ((MethodFault)e).getFaultString() + "\n");
|
|
|
| if(printStack) {
|
| writer.append("stack: ");
|
| e.printStackTrace(new PrintWriter(writer));
|
| }
|
| return writer.toString();
|
| }
|
|
|
| return ExceptionUtil.toString(e, printStack);
|
| } |
| |
| public static VirtualMachineMO pickOneVmOnRunningHost(List<VirtualMachineMO> vmList, boolean bFirstFit) throws Exception { |
| List<VirtualMachineMO> candidates = new ArrayList<VirtualMachineMO>(); |
| |
| for(VirtualMachineMO vmMo : vmList) { |
| HostMO hostMo = vmMo.getRunningHost(); |
| if(hostMo.isHyperHostConnected()) |
| candidates.add(vmMo); |
| } |
| |
| if(candidates.size() == 0) |
| return null; |
| |
| if(bFirstFit) |
| return candidates.get(0); |
| |
| Random random = new Random(); |
| return candidates.get(random.nextInt(candidates.size())); |
| } |
| }
|