I'm attempting to create a new VM using pyvmomi. I'm using snippets from the pyvmomi community samples. As of now I've been able to come up with the following:
"""
Example for creating a VM
"""
import sys
from pyVmomi import vim
from pyVim.task import WaitForTask
import atexit
import ssl
from pyVim import connect
from pyVmomi import vim
import pdb
import sys
import os
import math
def vconnect(hostIP, username=None, password=None, port=None):
if (True):
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE # disable our certificate checking for lab
else:
context = ssl.create_default_context()
context.options |= ssl.OP_NO_TLSv1_3
#pdb.set_trace()
hostIP = hostIP.strip('[\'\"]')
username = username.strip('[\'\"]')
password = password.strip('[\'\"]')
port = port.strip('[\'\"]')
if (port):
if '@' not in username:
sys.exit("Please include domain name with username")
service_instance = connect.SmartConnect(host=str(hostIP), # build python connection to vSphere
#user="Administrator@vsphere.local",
user=username,
pwd=password,
port=int(port),
sslContext=context)
else:
service_instance = connect.SmartConnect(host=str(hostIP), # build python connection to vSphere
user=username,
pwd=password,
sslContext=context)
atexit.register(connect.Disconnect, service_instance) # build disconnect logic
return service_instance
def create_vm(si, vm_name, minMHz, minMem, minStorage, datacenter_name=None, host_ip=None, datastore_name=None):
content = si.RetrieveContent()
container = content.rootFolder # starting point to look into
viewType = [vim.ResourcePool] # object types to look for
recursive = True # whether we should look into it recursively
containerView = content.viewManager.CreateContainerView(container, viewType, recursive) # create container view
resourcePools = containerView.view
datastoreFound = False
SelectedHost = None
SelectedDS = None
SelectedPool = None
for pool in resourcePools:
# get pool quick stats
quickStats = pool.summary.quickStats
# get memory info
ConfiguredHostMem = pool.summary.configuredMemoryMB
HostMemUsage = quickStats.hostMemoryUsage
GuestConsumedMem = quickStats.guestMemoryUsage
GuestPrivateMem = quickStats.privateMemory
sharedMem = quickStats.sharedMemory
AvailMem = ConfiguredHostMem - HostMemUsage
# get Compute info
CurrentCPUDemand = quickStats.overallCpuDemand
MaxcpuAllocation = pool.summary.config.cpuAllocation.limit
AvailMHz = MaxcpuAllocation - CurrentCPUDemand
print(f"Available Mem is {AvailMem}MB and available compute capacity is {AvailMHz}MHz for {pool.name}")
if 'mhz' in minMHz.lower():
minMHz = int([x for x in minMHz.lower().split('mhz')][0])
if 'mb' in minMem.lower():
minMem = int([x for x in minMem.lower().split('mb')][0])
if 'gb' in minStorage.lower():
minStorage = int([x for x in minStorage.lower().split('gb')][0])
if ((AvailMem < minMem) or (AvailMHz < minMHz)):
print(f"Unable to allocate enough resources on the \"{pool.name}\" resource pool")
continue
# get host info
cpu = pool.parent
hostList = cpu.host
for host in hostList:
if host.summary.overallStatus.lower() is 'red':
continue
else:
dsList = host.datastore
for datastore in dsList:
freespace = datastore.summary.freeSpace
freespace = math.floor(freespace/(1024*1024*1024))
if (freespace) < int(minStorage):
continue
else:
SelectedHost = host
SelectedDS = datastore
SelectedPool = pool
datastoreFound = True
break
if (datastoreFound):
break
if (datastoreFound):
print(f"Selected datastore \"{datastore.summary.name}\" with {freespace}GB on it")
break
if not (datastoreFound):
print(f"Unable to allocate enough resources on any resource pool, quitting")
sys.exit(1)
else:
datastore_path = '[' + SelectedDS.name + '] ' + name
config = create_config_spec(SelectedDS.name, vm_name, minMem, SelectedHost.network[0], int(minStorage))
vmfolder = SelectedDS.parent.parent.vmFolder
try:
WaitForTask(vmfolder.CreateVm(config, pool=SelectedPool, host=SelectedHost))
print(f"VM {vm_name} created")
except vim.fault.DuplicateName:
print("VM duplicate name: %s" % vm_name, file=sys.stderr)
except vim.fault.AlreadyExists:
print("VM name %s already exists." % vm_name, file=sys.stderr)
def create_config_spec(datastore_name, name, memory, network, sizeGB, guest="otherGuest",
annotation="Sample", cpus=1):
config = vim.vm.ConfigSpec()
config.annotation = annotation
config.memoryMB = int(memory)
config.guestId = guest
config.name = name
config.numCPUs = cpus
# List of all device controllers we plan to attach to this VM
vmControllers = []
# Adding the NIC
nicspec = vim.vm.device.VirtualDeviceSpec()
nicspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
nic_type = vim.vm.device.VirtualVmxnet3()
nicspec.device = nic_type
nicspec.device.deviceInfo = vim.Description()
nicspec.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
nicspec.device.backing.network = network
nicspec.device.backing.deviceName = network.name
nicspec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
nicspec.device.connectable.startConnected = True
nicspec.device.connectable.allowGuestControl = True
vmControllers.append(nicspec)
# SCSI controller
scsi_ctlr = vim.vm.device.VirtualDeviceSpec()
scsi_ctlr.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
scsi_ctlr.device = vim.vm.device.ParaVirtualSCSIController()
scsi_ctlr.device.deviceInfo = vim.Description()
scsi_ctlr.device.slotInfo = vim.vm.device.VirtualDevice.PciBusSlotInfo()
scsi_ctlr.device.slotInfo.pciSlotNumber = 16
scsi_ctlr.device.controllerKey = 100
scsi_ctlr.device.unitNumber = 3
scsi_ctlr.device.busNumber = 0
scsi_ctlr.device.hotAddRemove = True
scsi_ctlr.device.sharedBus = 'noSharing'
scsi_ctlr.device.scsiCtlrUnitNumber = 7
vmControllers.append(scsi_ctlr)
# vDisk
unit_number = 0
controller = scsi_ctlr.device # this is the controller we defined above
disk_spec = vim.vm.device.VirtualDeviceSpec()
disk_spec.fileOperation = "create"
disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
disk_spec.device = vim.vm.device.VirtualDisk()
disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
disk_spec.device.backing.diskMode = 'persistent'
disk_spec.device.backing.fileName = '[%s]%s.vmdk' % ( datastore_name, name )
disk_spec.device.unitNumber = unit_number
disk_spec.device.capacityInKB = sizeGB * 1024 * 1024
disk_spec.device.controllerKey = controller.key
vmControllers.append(disk_spec)
# finalizing the spec
files = vim.vm.FileInfo()
files.vmPathName = "[" + datastore_name + "] " + vm_name
config.files = files
return config
def main():
try:
vcsahostIP = sys.argv[1]
vcsaUserName = sys.argv[2]
vcsaPasswd = sys.argv[3]
vcsaAdminPort = sys.argv[4]
vmName = sys.argv[5]
minimumMHz = sys.argv[6]
minimumMBs = sys.argv[7]
minimumGB = sys.argv[8]
except IndexError as e:
print("usage:\n listVMs.py vcsahostIP vcsaUserName vcsaPasswd vcsaAdminport vmName memory-in-MB compute-capacity-MHz disk-space-GB")
print(e)
sys.exit(1)
si = vconnect(vcsahostIP,vcsaUserName,vcsaPasswd,vcsaAdminPort)
create_vm(si, vmName, minimumMHz, minimumMBs, minimumGB)
# start this thing
if __name__ == "__main__":
main()
It does create a VM but it has neither the NIC or the storage controller attached to it:
I wonder what's happening and looking for a heads up here
I, perhaps, missed the most important link in the chain viz. attaching vmControllers
to the ConfigSpec.
Before
return config
I should have done
config.deviceChange = vmControllers
After making this change, it works now.