VMware 虚拟化编程(1) — VMDK/VDDK/VixDiskLib/VADP 概念简析
VMware 虚拟化编程(2) — 虚拟磁盘文件类型详解
VMware 虚拟化编程(3) —VMware vSphere Web Service API 解析
VMware 虚拟化编程(4) — VDDK 安装
VMware 虚拟化编程(5) — VixDiskLib 虚拟磁盘库详解之一
VMware 虚拟化编程(6) — VixDiskLib 虚拟磁盘库详解之二
VMware 虚拟化编程(7) — VixDiskLib 虚拟磁盘库详解之三
VMware 虚拟化编程(8) — 多线程中的 VixDiskLib
VMware 虚拟化编程(9) — VMware 虚拟机的快照
VMware 虚拟化编程(10) — VMware 数据块修改跟踪技术 CBT
VMware 虚拟化编程(11) — VMware 虚拟机的全量备份与增量备份方案
VMware 虚拟化编程(12) — VixDiskLib Sample 程序使用
VMware 虚拟化编程(13) — VMware 虚拟机的备份方案设计
VMware 虚拟化编程(14) — VDDK 的高级传输模式详解
使用 vSphere WS API 中的 CreateVM_Task 创建 RDM 磁盘时,需要用到了一个可用且未被占用的 LUN 设备。在组装 VirtualDiskRawDiskMappingVer1BackInfo 类型虚拟磁盘的 backingInfo 时,我们可以按照下述方法来获取 LUN 设备的相关信息,并以此组装创建 RDM 磁盘所需要的参数:
调用 QueryConfigTarget 获得 ConfigTarget.ScsiDisk.Disk.CanonicalName 属性值,并设置到 VirtualDiskRawDiskMappingVer1BackInfo.deviceName
属性中。
调用 QueryConfgiTarget 获得 ConfigTarget.ScsiDisk.Disk.uuid 属性值,并设置到 VirtualDiskRawDiskMappingVer1BackInfo.lunUuid
属性中。
NOTE:有些开发者可能会使用 ESXi configInfo 对象中的 LUN uuid。需要注意的是,这样做很可能会导致错误,因为实际可用的 LUN uuid 是由 Datastore 指定的,而非 ESXi。
在实际调用 vSphere WS API 中的 CreateVM_Task 新建一个虚拟机前,需要组装一个 VirtualMachineConfigSpec 配置数据集来描述虚拟机的各项配置以及虚拟设备。大部分需要的信息都可以从 VirtualMachine Managed Object 的 VirtualMachineConfigInfo 得到,其中的 config.hardware.device 就包含了虚拟机所有虚拟设备配置信息。不同设备间的关系使用 key 的值表示,它是设备的唯一标识符。除此之外,每个设备还都拥有 controllerKey 属性,controllerKey 的值就是设备所连接的控制器的唯一标识符。我们在组装 VirtualMachineConfigSpec 时,应该使用一个负整数作为临时的 controllerKey 值,以此来保证临时 controllerKey 值不会和实际分配给这些控制器的值发送冲突。当虚拟设备关联到默认设备时,controllerKey 值应该重置为对应控制器的 key 值。
一般的,为了恢复一个新的虚拟机,我们会在备份时保留备份目标虚拟机的 VirtualMachineConfigInfo 数据,为了恢复出「一模一样」的虚拟机,甚至有些开发者会完整的将其保留。但实际上,VirtualMachineConfigInfo 中的一些数据是不需要的,而且如果强行引用到 VirtualMachineConfigSpec 中的话,还会导致虚拟机创建失败。例如,包含了「Default Devices」的 VirtualMachineConfigSpec 就会创建失败,「Default Devices」列表如下,这些设备的信息都是无需组装到 VirtualMachineConfigInfo 中的。但是,除此之外的其他控制器和虚拟设备就必须组装到 VirtualMachineConfigSpec 中了。
还有一些设备的信息如果被提供了的话也可能会出问题,组装 VirtualMachineConfigSpec 的要点如下:
// beginning of VirtualMachineConfigSpec, ends several pages later
{
dynamicType = <unset>,
changeVersion = <unset>,
//This is the display name of the VM
name = “My New VM“,
version = "vmx-04",
uuid = <unset>,
instanceUuid = <unset>,
npivWorldWideNameType = <unset>,
npivDesiredNodeWwns = <unset>,
npivDesiredPortWwns = <unset>,
npivTemporaryDisabled = <unset>,
npivOnNonRdmDisks = <unset>,
npivWorldWideNameOp = <unset>,
locationId = <unset>,
// This is advisory, the disk determines the O/S
guestId = "winNetStandardGuest",
alternateGuestName = "Microsoft Windows Server 2008, Enterprise Edition",
annotation = <unset>,
files = (vim.vm.FileInfo) {
dynamicType = <unset>,
vmPathName = "[plat004-local]",
snapshotDirectory = "[plat004-local]",
suspendDirectory = <unset>,
logDirectory = <unset>,
},
tools = (vim.vm.ToolsConfigInfo) {
dynamicType = <unset>,
toolsVersion = <unset>,
afterPowerOn = true,
afterResume = true,
beforeGuestStandby = true,
beforeGuestShutdown = true,
beforeGuestReboot = true,
toolsUpgradePolicy = <unset>,
pendingCustomization = <unset>,
syncTimeWithHost = <unset>,
},
flags = (vim.vm.FlagInfo) {
dynamicType = <unset>,
disableAcceleration = <unset>,
enableLogging = <unset>,
useToe = <unset>,
runWithDebugInfo = <unset>,
monitorType = <unset>,
htSharing = <unset>,
snapshotDisabled = <unset>,
snapshotLocked = <unset>,
diskUuidEnabled = <unset>,
virtualMmuUsage = <unset>,
snapshotPowerOffBehavior = "powerOff",
recordReplayEnabled = <unset>,
},
consolePreferences = (vim.vm.ConsolePreferences) null,
powerOpInfo = (vim.vm.DefaultPowerOpInfo) {
dynamicType = <unset>,
powerOffType = "preset",
suspendType = "preset",
resetType = "preset",
defaultPowerOffType = <unset>,
defaultSuspendType = <unset>,
defaultResetType = <unset>,
standbyAction = "powerOnSuspend",
},
// the number of CPUs
numCPUs = 1,
// the number of memory megabytes
memoryMB = 256,
memoryHotAddEnabled = <unset>,
cpuHotAddEnabled = <unset>,
cpuHotRemoveEnabled = <unset>,
deviceChange = (vim.vm.device.VirtualDeviceSpec) [
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = <unset>,
// CDROM
device = (vim.vm.device.VirtualCdrom) {
dynamicType = <unset>,
// key number of CDROM
key = -42,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo) {
dynamicType = <unset>,
deviceName = "",
useAutoDetect = <unset>,
exclusive = false,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
startConnected = false,
allowGuestControl = true,
connected = false,
},
// connects to this controller
controllerKey = 200,
unitNumber = 0,
},
},
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = <unset>,
// SCSI controller
device = (vim.vm.device.VirtualLsiLogicController) {
dynamicType = <unset>,
// key number of SCSI controller
key = -44,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualDevice.BackingInfo) null,
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) null,
controllerKey = <unset>,
unitNumber = <unset>,
busNumber = 0,
hotAddRemove = <unset>,
sharedBus = "noSharing",
scsiCtlrUnitNumber = <unset>,
}, },
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = <unset>,
// Network controller
device = (vim.vm.device.VirtualPCNet32) {
dynamicType = <unset>,
// key number of Network controller
key = -48,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualEthernetCard.NetworkBackingInfo) {
dynamicType = <unset>,
deviceName = "Virtual Machine Network",
useAutoDetect = <unset>,
network = <unset>,
inPassthroughMode = <unset>,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
startConnected = true,
allowGuestControl = true,
connected = true,
},
controllerKey = <unset>,
unitNumber = <unset>,
addressType = "generated",
macAddress = <unset>,
wakeOnLanEnabled = true,
}, },
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = "create",
// SCSI disk one
device = (vim.vm.device.VirtualDisk) {
dynamicType = <unset>,
// key number for SCSI disk one
key = -1000000,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) {
dynamicType = <unset>,
fileName = "",
datastore = <unset>,
diskMode = "persistent",
split = false,
writeThrough = false,
thinProvisioned = <unset>,
eagerlyScrub = <unset>,
uuid = <unset>,
contentId = <unset>,
changeId = <unset>,
parent = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) null,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
startConnected = true,
allowGuestControl = false,
connected = true,
},
// controller for SCSI disk one
controllerKey = -44,
unitNumber = 0,
// size in MB SCSI disk one
capacityInKB = 524288,
committedSpace = <unset>,
shares = (vim.SharesInfo) null,
}, },
(vim.vm.device.VirtualDeviceSpec) {
dynamicType = <unset>,
operation = "add",
fileOperation = "create",
// SCSI disk two
device = (vim.vm.device.VirtualDisk) {
dynamicType = <unset>,
// key number of SCSI disk two
key = -100,
deviceInfo = (vim.Description) null,
backing = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) {
dynamicType = <unset>,
fileName = "",
datastore = <unset>,
diskMode = "persistent",
split = false,
writeThrough = false,
thinProvisioned = <unset>,
eagerlyScrub = <unset>,
uuid = <unset>,
contentId = <unset>,
changeId = <unset>,
parent = (vim.vm.device.VirtualDisk.FlatVer2BackingInfo) null,
},
connectable = (vim.vm.device.VirtualDevice.ConnectInfo) {
dynamicType = <unset>,
startConnected = true,
allowGuestControl = false,
connected = true,
},
// controller for SCSI disk two
controllerKey = -44,
unitNumber = 1,
// size in MB SCSI disk two
capacityInKB = 131072,
committedSpace = <unset>,
shares = (vim.SharesInfo) null,
}, }
},
cpuAllocation = (vim.ResourceAllocationInfo) {
dynamicType = <unset>,
reservation = 0,
expandableReservation = <unset>,
limit = <unset>,
shares = (vim.SharesInfo) {
dynamicType = <unset>,
shares = 100,
level = "normal",
},
overheadLimit = <unset>,
},
memoryAllocation = (vim.ResourceAllocationInfo) {
dynamicType = <unset>,
reservation = 0,
expandableReservation = <unset>,
limit = <unset>,
shares = (vim.SharesInfo) {
dynamicType = <unset>,
shares = 100,
level = "normal",
},
overheadLimit = <unset>,
},
cpuAffinity = (vim.vm.AffinityInfo) null,
memoryAffinity = (vim.vm.AffinityInfo) null,
networkShaper = (vim.vm.NetworkShaperInfo) null,
swapPlacement = <unset>,
swapDirectory = <unset>,
preserveSwapOnPowerOff = <unset>,
bootOptions = (vim.vm.BootOptions) null,
appliance = (vim.vService.ConfigSpec) null,
ftInfo = (vim.vm.FaultToleranceConfigInfo) null,
applianceConfigRemoved = <unset>,
vAssertsEnabled = <unset>,
changeTrackingEnabled = <unset>,
}
// end of VirtualMachineConfigSpec
// Duplicate virtual machine configuration
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
// Set the VM values
configSpec.setName("My New VM");
configSpec.setVersion("vmx-04");
configSpec.setGuestId("winNetStandardGuest");
configSpec.setNumCPUs(1);
configSpec.setMemoryMB(256);
// Set up file storage info
VirtualMachineFileInfo vmfi = new VirtualMachineFileInfo(); vmfi.setVmPathName("[plat004-local]");
configSpec.setFiles(vmfi);
vmfi.setSnapshotDirectory("[plat004-local]");
// Set up tools config info
ToolsConfigInfo tools = new ToolsConfigInfo();
configSpec.setTools(tools);
tools.setAfterPowerOn(new Boolean(true));
tools.setAfterResume(new Boolean(true));
tools.setBeforeGuestStandby(new Boolean(true));
tools.setBeforeGuestShutdown(new Boolean(true));
tools.setBeforeGuestReboot(new Boolean(true));
// Set flags
VirtualMachineFlagInfo flags = new VirtualMachineFlagInfo(); configSpec.setFlags(flags);
flags.setSnapshotPowerOffBehavior("powerOff");
// Set power op info
VirtualMachineDefaultPowerOpInfo powerInfo = new VirtualMachineDefaultPowerOpInfo(); configSpec.setPowerOpInfo(powerInfo);
powerInfo.setPowerOffType("preset");
powerInfo.setSuspendType("preset");
powerInfo.setResetType("preset");
powerInfo.setStandbyAction("powerOnSuspend");
// Now add in the devices
VirtualDeviceConfigSpec[] deviceConfigSpec = new VirtualDeviceConfigSpec [5]; configSpec.setDeviceChange(deviceConfigSpec);
// Formulate the CDROM deviceConfigSpec[0].setOperation(VirtualDeviceConfigSpecOperation.add);
VirtualCdrom cdrom = new VirtualCdrom();
VirtualCdromIsoBackingInfo cdDeviceBacking = new VirtualCdromRemotePassthroughBackingInfo(); cdDeviceBacking.setDatastore(datastoreRef);
cdrom.setBacking(cdDeviceBacking);
cdrom.setKey(-42);
cdrom.setControllerKey(new Integer(-200)); // Older Java required type for optional properties cdrom.setUnitNumber(new Integer(0));
deviceConfigSpec[0].setDevice(cdrom);
// Formulate the SCSI controller deviceConfigSpec[1].setOperation(VirtualDeviceConfigSpecOperation.add); VirtualLsiLogicController scsiCtrl = new VirtualLsiLogicController(); scsiCtrl.setBusNumber(0);
deviceConfigSpec[1].setDevice(scsiCtrl);
scsiCtrl.setKey(-44);
scsiCtrl.setSharedBus(VirtualSCSISharing.noSharing);
// Formulate SCSI disk one deviceConfigSpec[2].setFileOperation(VirtualDeviceConfigSpecFileOperation.create); deviceConfigSpec[2].setOperation(VirtualDeviceConfigSpecOperation.add); VirtualDisk disk = new VirtualDisk();
VirtualDiskFlatVer2BackingInfo diskfileBacking = new VirtualDiskFlatVer2BackingInfo(); diskfileBacking.setDatastore(datastoreRef);
diskfileBacking.setFileName(volumeName);
diskfileBacking.setDiskMode("persistent");
diskfileBacking.setSplit(new Boolean(false));
diskfileBacking.setWriteThrough(new Boolean(false));
disk.setKey(-1000000);
disk.setControllerKey(new Integer(-44));
disk.setUnitNumber(new Integer(0));
disk.setBacking(diskfileBacking);
disk.setCapacityInKB(524288);
deviceConfigSpec[2].setDevice(disk);
// Formulate SCSI disk two deviceConfigSpec[3].setFileOperation(VirtualDeviceConfigSpecFileOperation.create); deviceConfigSpec[3].setOperation(VirtualDeviceConfigSpecOperation.add);
VirtualDisk disk2 = new VirtualDisk();
VirtualDiskFlatVer2BackingInfo diskfileBacking2 = new VirtualDiskFlatVer2BackingInfo(); diskfileBacking2.setDatastore(datastoreRef);
diskfileBacking2.setFileName(volumeName);
diskfileBacking2.setDiskMode("persistent");
diskfileBacking2.setSplit(new Boolean(false));
diskfileBacking2.setWriteThrough(new Boolean(false));
disk2.setKey(-100);
disk2.setControllerKey(new Integer(-44));
disk2.setUnitNumber(new Integer(1));
disk2.setBacking(diskfileBacking2);
disk2.setCapacityInKB(131072);
deviceConfigSpec[3].setDevice(disk2);
// Finally, formulate the NIC deviceConfigSpec[4].setOperation(VirtualDeviceConfigSpecOperation.add); com.VMware.vim.VirtualEthernetCard nic = new VirtualPCNet32(); VirtualEthernetCardNetworkBackingInfo nicBacking = new VirtualEthernetCardNetworkBackingInfo(); nicBacking.setNetwork(networkRef);
nicBacking.setDeviceName(networkName);
nic.setAddressType("generated");
nic.setBacking(nicBacking);
nic.setKey(-48);
deviceConfigSpec[4].setDevice(nic);
// Now that it is all put together, create the virtual machine.
// Note that folderMo, resourcePool, and hostMo, are moRefs to
// the Folder, ResourcePool, and Host where the VM is to be created.
ManagedObjectReference taskMoRef =
serviceConnection.getService().createVM_Task(folderMo, configSpec, resourcePool, hostMo);