前言
Web Service SDK中提供了创建虚拟机的代码,但是比较冗余,而且有些地方没有说清楚,贸然使用会遇到一些问题,所以我对示例代码整理了下,也会说明哪些地方要小心(我遇到的坑),下图是SDK的示例代码路径。
代码实现
void createVirtualMachine() {
String dataCenterName = "datacenterxxx";
String hostname = "Esxi-xxx";
String dataStore = "datastore-xxx";
String virtualMachineName = "csdn";
long vmMemory = 1024L;
int numCpus = 2;
String guestId = "centos64Guest"; //操作系统标志
try {
getMOREFs = new GetMOREF(connection);
ManagedObjectReference dcmor = getMOREFs.inContainerByType(connection.getServiceContent().getRootFolder(),
"Datacenter").get(dataCenterName);
ManagedObjectReference hostmor = getMOREFs.inContainerByType(dcmor, "HostSystem").get(
hostname);
ManagedObjectReference crmor =
(ManagedObjectReference) getMOREFs.entityProps(hostmor,
new String[]{"parent"}).get("parent");
ManagedObjectReference resourcepoolmor =
(ManagedObjectReference) getMOREFs.entityProps(crmor,
new String[]{"resourcePool"}).get("resourcePool");
ManagedObjectReference vmFolderMor =
(ManagedObjectReference) getMOREFs.entityProps(dcmor,
new String[]{"vmFolder"}).get("vmFolder");
//填充vmConfigSpec对象
VirtualMachineConfigSpec vmConfigSpec =
createVmConfigSpec(dataStore, 1048576, crmor, hostmor);
vmConfigSpec.setName(virtualMachineName);
vmConfigSpec.setAnnotation("VirtualMachine Annotation");
vmConfigSpec.setMemoryMB(vmMemory);
vmConfigSpec.setNumCPUs(numCpus);
vmConfigSpec.setGuestId(guestId);
ManagedObjectReference taskmor =
connection.getVimPort().createVMTask(vmFolderMor, vmConfigSpec, resourcepoolmor, hostmor);
if (getTaskResultAfterDone(taskmor)) {
System.out.println("Success: Creating VM - [ " + virtualMachineName + " ]");
} else {
String msg = "Failure: Creating [ " + virtualMachineName + "] VM";
System.out.println(msg);
}
} catch (InvalidPropertyFaultMsg|RuntimeFaultFaultMsg|AlreadyExistsFaultMsg|DuplicateNameFaultMsg|
FileFaultFaultMsg|InsufficientResourcesFaultFaultMsg |InvalidDatastoreFaultMsg|InvalidNameFaultMsg|
InvalidStateFaultMsg|OutOfBoundsFaultMsg|VmConfigFaultFaultMsg|InvalidCollectorVersionFaultMsg
invalidPropertyFaultMsg) {
invalidPropertyFaultMsg.printStackTrace();
}
}
VirtualMachineConfigSpec createVmConfigSpec(String datastoreName, int diskSizeKB,
ManagedObjectReference computeResMor, ManagedObjectReference hostMor)
throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg {
ConfigTarget configTarget = getConfigTargetForHost(computeResMor, hostMor);
List<VirtualDevice> defaultDevices = getDefaultDevices(computeResMor, hostMor);
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
String networkName = null;
if (configTarget.getNetwork() != null) {
for (int i = 0; i < configTarget.getNetwork().size(); i++) {
VirtualMachineNetworkInfo netInfo =
configTarget.getNetwork().get(i);
NetworkSummary netSummary = netInfo.getNetwork();
if (netSummary.isAccessible()) {
networkName = netSummary.getName();
break;
}
}
}
ManagedObjectReference datastoreRef = null;
if (datastoreName != null) {
boolean flag = false;
for (int i = 0; i < configTarget.getDatastore().size(); i++) {
VirtualMachineDatastoreInfo vdsInfo =
configTarget.getDatastore().get(i);
DatastoreSummary dsSummary = vdsInfo.getDatastore();
if (dsSummary.getName().equals(datastoreName)) {
flag = true;
if (dsSummary.isAccessible()) {
datastoreRef = dsSummary.getDatastore();
} else {
throw new RuntimeException(
"Specified Datastore is not accessible");
}
break;
}
}
if (!flag) {
throw new RuntimeException("Specified Datastore is not Found");
}
} else {
boolean flag = false;
for (int i = 0; i < configTarget.getDatastore().size(); i++) {
VirtualMachineDatastoreInfo vdsInfo =
configTarget.getDatastore().get(i);
DatastoreSummary dsSummary = vdsInfo.getDatastore();
if (dsSummary.isAccessible()) {
datastoreName = dsSummary.getName();
datastoreRef = dsSummary.getDatastore();
flag = true;
break;
}
}
if (!flag) {
throw new RuntimeException("No Datastore found on host");
}
}
String datastoreVolume = getVolumeName(datastoreName);
VirtualMachineFileInfo vmfi = new VirtualMachineFileInfo();
vmfi.setVmPathName(datastoreVolume);
configSpec.setFiles(vmfi);
// Add a scsi controller
int diskCtlrKey = 1;
VirtualDeviceConfigSpec scsiCtrlSpec = new VirtualDeviceConfigSpec();
scsiCtrlSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
VirtualLsiLogicController scsiCtrl = new VirtualLsiLogicController(); //这里的控制器要注意与系统兼容
scsiCtrl.setBusNumber(0);
scsiCtrlSpec.setDevice(scsiCtrl);
scsiCtrl.setKey(diskCtlrKey);
scsiCtrl.setSharedBus(VirtualSCSISharing.NO_SHARING);
String ctlrType = scsiCtrl.getClass().getName();
ctlrType = ctlrType.substring(ctlrType.lastIndexOf(".") + 1);
// Find the IDE controller
VirtualDevice ideCtlr = null;
for (VirtualDevice device : defaultDevices) {
if (device instanceof VirtualIDEController) {
ideCtlr = device;
break;
}
}
// Add a floppy
VirtualDeviceConnectInfo cInfo = new VirtualDeviceConnectInfo();
cInfo.setConnected(false);
cInfo.setStartConnected(false);
VirtualDeviceConfigSpec floppySpec = new VirtualDeviceConfigSpec();
floppySpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
VirtualFloppy floppy = new VirtualFloppy();
VirtualFloppyRemoteDeviceBackingInfo flpBacking =
new VirtualFloppyRemoteDeviceBackingInfo();
flpBacking.setDeviceName("要进行连接,请打开虚拟机电源,然后从“摘要”选项卡的“虚拟机硬件”面板中选择介质。");
floppy.setBacking(flpBacking);
floppy.setKey(3);
floppy.setConnectable(cInfo);
floppySpec.setDevice(floppy);
// Add a cdrom based on a physical device
VirtualDeviceConfigSpec cdSpec = null;
if (ideCtlr != null) {
cdSpec = new VirtualDeviceConfigSpec();
cdSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
VirtualCdrom cdrom = new VirtualCdrom();
VirtualCdromIsoBackingInfo cdDeviceBacking =
new VirtualCdromIsoBackingInfo();
cdDeviceBacking.setDatastore(datastoreRef);
cdDeviceBacking.setFileName("");
cdrom.setBacking(cdDeviceBacking);
cdrom.setKey(20);
cdrom.setControllerKey(ideCtlr.getKey());
cdrom.setUnitNumber(0);
cdrom.setConnectable(cInfo);
cdSpec.setDevice(cdrom);
}
// Create a new disk - file based - for the vm
VirtualDeviceConfigSpec diskSpec;
diskSpec = createVirtualDisk(datastoreName, diskCtlrKey, diskSizeKB);
// Add a NIC. the network Name must be set as the device name to create the NIC.
VirtualDeviceConfigSpec nicSpec = new VirtualDeviceConfigSpec();
if (networkName != null) {
nicSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
VirtualEthernetCard nic = new VirtualVmxnet3(); //推荐使用的网卡类型
VirtualEthernetCardNetworkBackingInfo nicBacking =
new VirtualEthernetCardNetworkBackingInfo();
nicBacking.setDeviceName(networkName);
nic.setAddressType("generated");
nic.setBacking(nicBacking);
nic.setKey(4);
nicSpec.setDevice(nic);
}
List<VirtualDeviceConfigSpec> deviceConfigSpec =
new ArrayList<>();
deviceConfigSpec.add(scsiCtrlSpec);
deviceConfigSpec.add(floppySpec);
deviceConfigSpec.add(diskSpec);
if (ideCtlr != null) {
deviceConfigSpec.add(cdSpec);
deviceConfigSpec.add(nicSpec);
} else {
deviceConfigSpec = new ArrayList<>();
deviceConfigSpec.add(nicSpec);
}
configSpec.getDeviceChange().addAll(deviceConfigSpec);
return configSpec;
}
VirtualDeviceConfigSpec createVirtualDisk(String volName, int diskCtlrKey, int diskSizeKB) {
String volumeName = getVolumeName(volName);
VirtualDeviceConfigSpec diskSpec = new VirtualDeviceConfigSpec();
diskSpec.setFileOperation(VirtualDeviceConfigSpecFileOperation.CREATE);
diskSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
VirtualDisk disk = new VirtualDisk();
VirtualDiskFlatVer2BackingInfo diskfileBacking =
new VirtualDiskFlatVer2BackingInfo();
diskfileBacking.setFileName(volumeName);
diskfileBacking.setDiskMode("persistent"); //磁盘模式
diskfileBacking.setEagerlyScrub(true); //是否延迟置零,默认为厚置备
disk.setKey(0);
disk.setControllerKey(diskCtlrKey);
disk.setUnitNumber(0);
disk.setBacking(diskfileBacking);
disk.setCapacityInKB(diskSizeKB);
diskSpec.setDevice(disk);
return diskSpec;
}
String getVolumeName(String volName) {
String volumeName;
if (volName != null && volName.length() > 0) {
volumeName = "[" + volName + "]";
} else {
volumeName = "[Local]";
}
return volumeName;
}
/**
* This method returns the ConfigTarget for a HostSystem.
*
* @param computeResMor A MoRef to the ComputeResource used by the HostSystem
* @param hostMor A MoRef to the HostSystem
* @return Instance of ConfigTarget for the supplied
* HostSystem/ComputeResource
* @throws Exception When no ConfigTarget can be found
*/
ConfigTarget getConfigTargetForHost(
ManagedObjectReference computeResMor, ManagedObjectReference hostMor)
throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg {
ManagedObjectReference envBrowseMor =
(ManagedObjectReference) getMOREFs.entityProps(computeResMor,
new String[]{"environmentBrowser"}).get(
"environmentBrowser");
ConfigTarget configTarget =
connection.getVimPort().queryConfigTarget(envBrowseMor, hostMor);
if (configTarget == null) {
throw new RuntimeException("No ConfigTarget found in ComputeResource");
}
return configTarget;
}
/**
* The method returns the default devices from the HostSystem.
*
* @param computeResMor A MoRef to the ComputeResource used by the HostSystem
* @param hostMor A MoRef to the HostSystem
* @return Array of VirtualDevice containing the default devices for the
* HostSystem
* @throws Exception
*/
List<VirtualDevice> getDefaultDevices(
ManagedObjectReference computeResMor, ManagedObjectReference hostMor)
throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg {
ManagedObjectReference envBrowseMor =
(ManagedObjectReference) getMOREFs.entityProps(computeResMor,
new String[]{"environmentBrowser"}).get(
"environmentBrowser");
VirtualMachineConfigOption cfgOpt =
connection.getVimPort().queryConfigOption(envBrowseMor, null, hostMor);
List<VirtualDevice> defaultDevs;
if (cfgOpt == null) {
throw new RuntimeException(
"No VirtualHardwareInfo found in ComputeResource");
} else {
List<VirtualDevice> lvds = cfgOpt.getDefaultDevice();
if (lvds == null) {
throw new RuntimeException("No Datastore found in ComputeResource");
} else {
defaultDevs = lvds;
}
}
return defaultDevs;
}
/**
* This method returns a boolean value specifying whether the Task is
* succeeded or failed.
*
* @param task ManagedObjectReference representing the Task.
* @return boolean value representing the Task result.
* @throws InvalidCollectorVersionFaultMsg
*
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
*/
boolean getTaskResultAfterDone(ManagedObjectReference task)
throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg,
InvalidCollectorVersionFaultMsg {
boolean retVal = false;
// info has a property - state for state of the task
waitForValues = new WaitForValues(connection);
Object[] result =
waitForValues.wait(task, new String[]{"info.state", "info.error"},
new String[]{"state"}, new Object[][]{new Object[]{
TaskInfoState.SUCCESS, TaskInfoState.ERROR}});
if (result[0].equals(TaskInfoState.SUCCESS)) {
retVal = true;
}
if (result[1] instanceof LocalizedMethodFault) {
throw new RuntimeException(
((LocalizedMethodFault) result[1]).getLocalizedMessage());
}
return retVal;
}
代码略多,但围绕一个中心就是对VirtualMachineConfigSpec对象进行填充,这个类包含了创建虚拟机的所需的所有配置项。下面对几处要点进行说明。
1、关于guestId的值的说明。
VMware对于这个值有定义,每种操作系统都对应有相应的值,具体查找方法如下:
2、对SCSI控制器的说明
https://docs.vmware.com/cn/VMware-vSphere/6.0/com.vmware.vsphere.vm_admin.doc/GUID-5872D173-A076-42FE-8D0B-9DB0EB0E7362.html,这个网页里面对于各种控制器的兼容性做了很详细的说明,这里就不再赘述。目前我只找到windows系统SCSI控制器类型的对照表,如下图所示。
所以,如果操作系统类型为Windows Server 2012 则SCSI控制器类应为
VirtualLsiLogicSASController scsiCtrl = new VirtualLsiLogicSASController();
3、对网卡类型的说明
SDK示例代码中使用的是
VirtualEthernetCard nic = new VirtualPCNet32();
这种网卡类型局限性很大,推荐使用代码中提供的
VirtualEthernetCard nic = new VirtualVmxnet3();
4、对磁盘模式的说明