esxi-vm/esxi-vm-create

519 lines
16 KiB
Plaintext
Raw Normal View History

2017-05-03 17:00:03 -03:00
#!/usr/bin/python
import argparse # Argument parser
import sys # For args
import re # For regex
import paramiko # For remote ssh
from esxi_vm_functions import *
# Defaults and Variable setup
ConfigData = setup_config()
NAME = ""
LOG = ConfigData['LOG']
isDryRun = ConfigData['isDryRun']
isVerbose = ConfigData['isVerbose']
2017-05-16 12:49:38 -04:00
isSummary = ConfigData['isSummary']
2017-05-03 17:00:03 -03:00
HOST = ConfigData['HOST']
PORT = ConfigData['PORT']
2017-05-03 17:00:03 -03:00
USER = ConfigData['USER']
PASSWORD = ConfigData['PASSWORD']
KEY = ConfigData['KEY']
2017-05-03 17:00:03 -03:00
CPU = ConfigData['CPU']
MEM = ConfigData['MEM']
2017-05-16 12:49:38 -04:00
HDISK = int(ConfigData['HDISK'])
2017-05-03 17:00:03 -03:00
DISKFORMAT = ConfigData['DISKFORMAT']
VIRTDEV = ConfigData['VIRTDEV']
STORE = ConfigData['STORE']
NET = ConfigData['NET']
ISO = ConfigData['ISO']
GUESTOS = ConfigData['GUESTOS']
VMXOPTS = ConfigData['VMXOPTS']
2017-05-03 17:00:03 -03:00
2017-05-16 12:49:38 -04:00
ErrorMessages = ""
MAC = ""
GeneratedMAC = ""
2017-05-16 12:49:38 -04:00
ISOfound = False
CheckHasErrors = False
LeastUsedDS = ""
DSPATH=""
DSSTORE=""
FullPathExists = False
2017-05-03 17:00:03 -03:00
#
# Process Arguments
#
parser = argparse.ArgumentParser(description='ESXi Create VM utility.')
parser.add_argument('-d', '--dry', dest='isDryRunarg', action='store_true', help="Enable Dry Run mode (" + str(isDryRun) + ")")
2017-05-16 12:49:38 -04:00
parser.add_argument("-H", "--Host", dest='HOST', type=str, help="ESXi Host/IP (" + str(HOST) + ")")
parser.add_argument("-T", "--Port", dest='PORT', type=str, help="ESXi Port number (" + str(PORT) + ")")
2017-05-03 17:00:03 -03:00
parser.add_argument("-U", "--User", dest='USER', type=str, help="ESXi Host username (" + str(USER) + ")")
parser.add_argument("-P", "--Password", dest='PASSWORD', type=str, help="ESXi Host password (*****)")
parser.add_argument("-K", "--Key", dest='KEY', type=str, help="ESXi Host connection key (path to private key)")
2017-05-03 17:00:03 -03:00
parser.add_argument("-n", "--name", dest='NAME', type=str, help="VM name")
parser.add_argument("-c", "--cpu", dest='CPU', type=int, help="Number of vCPUS (" + str(CPU) + ")")
parser.add_argument("-m", "--mem", type=int, help="Memory in GB (" + str(MEM) + ")")
2017-05-16 12:49:38 -04:00
parser.add_argument("-v", "--vdisk", dest='HDISK', type=str, help="Size of virt hdisk (" + str(HDISK) + ")")
2017-05-03 17:00:03 -03:00
parser.add_argument("-i", "--iso", dest='ISO', type=str, help="CDROM ISO Path | None (" + str(ISO) + ")")
parser.add_argument("-N", "--net", dest='NET', type=str, help="Network Interface | None (" + str(NET) + ")")
2017-05-16 12:49:38 -04:00
parser.add_argument("-M", "--mac", dest='MAC', type=str, help="MAC address")
2017-05-03 17:00:03 -03:00
parser.add_argument("-S", "--store", dest='STORE', type=str, help="vmfs Store | LeastUsed (" + str(STORE) + ")")
parser.add_argument("-g", "--guestos", dest='GUESTOS', type=str, help="Guest OS. (" + str(GUESTOS) + ")")
parser.add_argument("-o", "--options", dest='VMXOPTS', type=str, default='NIL', help="Comma list of VMX Options.")
2017-05-16 12:49:38 -04:00
parser.add_argument('-V', '--verbose', dest='isVerbosearg', action='store_true', help="Enable Verbose mode (" + str(isVerbose) + ")")
parser.add_argument('--summary', dest='isSummaryarg', action='store_true', help="Display Summary (" + str(isSummary) + ")")
2017-05-03 17:00:03 -03:00
parser.add_argument("-u", "--updateDefaults", dest='UPDATE', action='store_true', help="Update Default VM settings stored in ~/.esxi-vm.yml")
2017-05-16 12:49:38 -04:00
#parser.add_argument("--showDefaults", dest='SHOW', action='store_true', help="Show Default VM settings stored in ~/.esxi-vm.yml")
2017-05-03 17:00:03 -03:00
args = parser.parse_args()
if args.isDryRunarg:
isDryRun = True
if args.isVerbosearg:
isVerbose = True
2017-05-16 12:49:38 -04:00
if args.isSummaryarg:
isSummary = True
2017-05-03 17:00:03 -03:00
if args.HOST:
HOST=args.HOST
if args.PORT:
PORT=args.PORT
2017-05-03 17:00:03 -03:00
if args.USER:
USER=args.USER
if args.PASSWORD:
PASSWORD=args.PASSWORD
if args.KEY:
KEY=args.KEY
2017-05-03 17:00:03 -03:00
if args.NAME:
NAME=args.NAME
if args.CPU:
CPU=int(args.CPU)
if args.mem:
MEM=int(args.mem)
2017-05-16 12:49:38 -04:00
if args.HDISK:
HDISK=int(args.HDISK)
2017-05-03 17:00:03 -03:00
if args.ISO:
ISO=args.ISO
if args.NET:
NET=args.NET
if args.MAC:
MAC=args.MAC
2017-05-03 17:00:03 -03:00
if args.STORE:
STORE=args.STORE
if STORE == "":
STORE = "LeastUsed"
if args.GUESTOS:
GUESTOS=args.GUESTOS
if args.VMXOPTS == '' and VMXOPTS != '':
VMXOPTS=''
if args.VMXOPTS and args.VMXOPTS != 'NIL':
VMXOPTS=args.VMXOPTS.split(",")
2017-05-03 17:00:03 -03:00
if args.UPDATE:
print "Saving new Defaults to ~/.esxi-vm.yml"
ConfigData['isDryRun'] = isDryRun
ConfigData['isVerbose'] = isVerbose
ConfigData['isSummary'] = isSummary
2017-05-03 17:00:03 -03:00
ConfigData['HOST'] = HOST
ConfigData['PORT'] = PORT
2017-05-03 17:00:03 -03:00
ConfigData['USER'] = USER
ConfigData['PASSWORD'] = PASSWORD
ConfigData['KEY'] = KEY
2017-05-03 17:00:03 -03:00
ConfigData['CPU'] = CPU
ConfigData['MEM'] = MEM
2017-05-16 12:49:38 -04:00
ConfigData['HDISK'] = HDISK
2017-05-03 17:00:03 -03:00
ConfigData['DISKFORMAT'] = DISKFORMAT
ConfigData['VIRTDEV'] = VIRTDEV
ConfigData['STORE'] = STORE
ConfigData['NET'] = NET
ConfigData['ISO'] = ISO
ConfigData['GUESTOS'] = GUESTOS
ConfigData['VMXOPTS'] = VMXOPTS
2017-05-03 17:00:03 -03:00
SaveConfig(ConfigData)
if NAME == "":
sys.exit(0)
#
# main()
#
2017-05-16 12:49:38 -04:00
LogOutput = '{'
LogOutput += '"datetime":"' + str(theCurrDateTime()) + '",'
2017-05-03 17:00:03 -03:00
if NAME == "":
print "ERROR: Missing required option --name"
sys.exit(1)
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(HOST, port=PORT, username=USER, password=PASSWORD, key_filename=KEY)
2017-05-03 17:00:03 -03:00
(stdin, stdout, stderr) = ssh.exec_command("esxcli system version get |grep Version")
type(stdin)
if re.match("Version", str(stdout.readlines())) is not None:
print "Unable to determine if this is a ESXi Host: %s, port: %s, username: %s" % (HOST, PORT, USER)
2017-05-03 17:00:03 -03:00
sys.exit(1)
except:
print "The Error is " + str(sys.exc_info()[0])
print "Unable to access ESXi Host: %s, port: %s, username: %s" % (HOST, PORT, USER)
sys.exit(1)
2017-05-03 17:00:03 -03:00
#
# Get list of DataStores, store in VOLUMES
#
try:
(stdin, stdout, stderr) = ssh.exec_command("esxcli storage filesystem list |grep '/vmfs/volumes/.*true VMFS' |sort -nk7")
type(stdin)
VOLUMES = {}
for line in stdout.readlines():
splitLine = line.split()
VOLUMES[splitLine[0]] = splitLine[1]
LeastUsedDS = splitLine[1]
except:
print "The Error is " + str(sys.exc_info()[0])
2017-05-03 17:00:03 -03:00
sys.exit(1)
if STORE == "LeastUsed":
STORE = LeastUsedDS
#
# Get list of Networks available, store in VMNICS
#
try:
(stdin, stdout, stderr) = ssh.exec_command("esxcli network vswitch standard list|grep Portgroups|sed 's/^ Portgroups: //g'")
type(stdin)
VMNICS = []
for line in stdout.readlines():
splitLine = re.split(',|\n', line)
VMNICS.append(splitLine[0])
except:
print "The Error is " + str(sys.exc_info()[0])
2017-05-03 17:00:03 -03:00
sys.exit(1)
#
# Check MAC address
#
2017-05-16 12:49:38 -04:00
MACarg = MAC
if MAC != "":
MACregex = '^([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}$'
if re.compile(MACregex).search(MAC):
# Full MAC found. OK
MAC = MAC.replace("-",":")
elif re.compile(MACregex).search("00:50:56:" + MAC):
MAC="00:50:56:" + MAC.replace("-",":")
else:
print "ERROR: " + MAC + " Invalid MAC address."
2017-05-16 12:49:38 -04:00
ErrorMessages += " " + MAC + " Invalid MAC address."
CheckHasErrors = True
2017-05-03 17:00:03 -03:00
#
# Get from ESXi host if ISO exists
#
2017-05-16 12:49:38 -04:00
ISOarg = ISO
2017-05-03 17:00:03 -03:00
if ISO == "None":
ISO = ""
if ISO != "":
try:
# If ISO has no "/", try to find the ISO
if not re.match('/', ISO):
(stdin, stdout, stderr) = ssh.exec_command("find /vmfs/volumes/ -type f -name " + ISO + " -exec sh -c 'echo $1; kill $PPID' sh {} 2>/dev/null \;")
type(stdin)
FoundISOPath = str(stdout.readlines()[0]).strip('\n')
if isVerbose:
print "FoundISOPath: " + str(FoundISOPath)
ISO = str(FoundISOPath)
2017-05-03 17:00:03 -03:00
(stdin, stdout, stderr) = ssh.exec_command("ls " + str(ISO))
type(stdin)
2017-05-07 22:09:01 -03:00
if stdout.readlines() and not stderr.readlines():
2017-05-03 17:00:03 -03:00
ISOfound = True
2017-05-03 17:00:03 -03:00
except:
print "The Error is " + str(sys.exc_info()[0])
2017-05-03 17:00:03 -03:00
sys.exit(1)
2017-05-16 12:49:38 -04:00
2017-05-03 17:00:03 -03:00
#
# Check if VM already exists
#
VMID = -1
try:
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd vmsvc/getallvms")
type(stdin)
for line in stdout.readlines():
splitLine = line.split()
if NAME == splitLine[1]:
VMID = splitLine[0]
print "ERROR: VM " + NAME + " already exists."
2017-05-16 12:49:38 -04:00
ErrorMessages += " VM " + NAME + " already exists."
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
except:
print "The Error is " + str(sys.exc_info()[0])
2017-05-03 17:00:03 -03:00
sys.exit(1)
#
# Do checks here
#
# Check CPU
if CPU < 1 or CPU > 128:
2017-05-16 12:49:38 -04:00
print str(CPU) + " CPU out of range. [1-128]."
ErrorMessages += " " + str(CPU) + " CPU out of range. [1-128]."
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
# Check MEM
if MEM < 1 or MEM > 4080:
2017-05-16 12:49:38 -04:00
print str(MEM) + "GB Memory out of range. [1-4080]."
ErrorMessages += " " + str(MEM) + "GB Memory out of range. [1-4080]."
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
2017-05-16 12:49:38 -04:00
# Check HDISK
if HDISK < 1 or HDISK > 63488:
print "Virtual Disk size " + str(HDISK) + "GB out of range. [1-63488]."
ErrorMessages += " Virtual Disk size " + str(HDISK) + "GB out of range. [1-63488]."
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
2017-05-16 12:49:38 -04:00
# Convert STORE to path and visa-versa
2017-05-03 17:00:03 -03:00
V = []
for Path in VOLUMES:
V.append(VOLUMES[Path])
if STORE == Path or STORE == VOLUMES[Path]:
DSPATH = Path
DSSTORE = VOLUMES[Path]
if DSSTORE not in V:
print "ERROR: Disk Storage " + STORE + " doesn't exist. "
2017-05-16 12:49:38 -04:00
print " Available Disk Stores: " + str([str(item) for item in V])
2017-05-03 17:00:03 -03:00
print " LeastUsed Disk Store : " + str(LeastUsedDS)
2017-05-16 12:49:38 -04:00
ErrorMessages += " Disk Storage " + STORE + " doesn't exist. "
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
# Check NIC (NIC record)
if (NET not in VMNICS) and (NET != "None"):
print "ERROR: Virtual NIC " + NET + " doesn't exist."
2017-05-16 12:49:38 -04:00
print " Available VM NICs: " + str([str(item) for item in VMNICS]) + " or 'None'"
ErrorMessages += " Virtual NIC " + NET + " doesn't exist."
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
# Check ISO exists
if ISO != "" and not ISOfound:
print "ERROR: ISO " + ISO + " not found. Use full path to ISO"
2017-05-16 12:49:38 -04:00
ErrorMessages += " ISO " + ISO + " not found. Use full path to ISO"
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
# Check if DSPATH/NAME aready exists
try:
FullPath = DSPATH + "/" + NAME
(stdin, stdout, stderr) = ssh.exec_command("ls -d " + FullPath)
type(stdin)
if stdout.readlines() and not stderr.readlines():
print "ERROR: Directory " + FullPath + " already exists."
2017-05-16 12:49:38 -04:00
ErrorMessages += " Directory " + FullPath + " already exists."
2017-05-03 17:00:03 -03:00
CheckHasErrors = True
except:
pass
#
# Create the VM
#
VMX = []
VMX.append('config.version = "8"')
VMX.append('virtualHW.version = "8"')
VMX.append('vmci0.present = "TRUE"')
VMX.append('displayName = "' + NAME + '"')
VMX.append('floppy0.present = "FALSE"')
VMX.append('numvcpus = "' + str(CPU) + '"')
VMX.append('scsi0.present = "TRUE"')
VMX.append('scsi0.sharedBus = "none"')
VMX.append('scsi0.virtualDev = "pvscsi"')
VMX.append('memsize = "' + str(MEM * 1024) + '"')
VMX.append('scsi0:0.present = "TRUE"')
VMX.append('scsi0:0.fileName = "' + NAME + '.vmdk"')
VMX.append('scsi0:0.deviceType = "scsi-hardDisk"')
if ISO == "":
VMX.append('ide1:0.present = "TRUE"')
VMX.append('ide1:0.fileName = "emptyBackingString"')
VMX.append('ide1:0.deviceType = "atapi-cdrom"')
VMX.append('ide1:0.startConnected = "FALSE"')
VMX.append('ide1:0.clientDevice = "TRUE"')
else:
VMX.append('ide1:0.present = "TRUE"')
VMX.append('ide1:0.fileName = "' + ISO + '"')
VMX.append('ide1:0.deviceType = "cdrom-image"')
VMX.append('pciBridge0.present = "TRUE"')
VMX.append('pciBridge4.present = "TRUE"')
VMX.append('pciBridge4.virtualDev = "pcieRootPort"')
VMX.append('pciBridge4.functions = "8"')
VMX.append('pciBridge5.present = "TRUE"')
VMX.append('pciBridge5.virtualDev = "pcieRootPort"')
VMX.append('pciBridge5.functions = "8"')
VMX.append('pciBridge6.present = "TRUE"')
VMX.append('pciBridge6.virtualDev = "pcieRootPort"')
VMX.append('pciBridge6.functions = "8"')
VMX.append('pciBridge7.present = "TRUE"')
VMX.append('pciBridge7.virtualDev = "pcieRootPort"')
VMX.append('pciBridge7.functions = "8"')
VMX.append('guestOS = "' + GUESTOS + '"')
if NET != "None":
VMX.append('ethernet0.virtualDev = "vmxnet3"')
VMX.append('ethernet0.present = "TRUE"')
VMX.append('ethernet0.networkName = "' + NET + '"')
if MAC == "":
VMX.append('ethernet0.addressType = "generated"')
else:
VMX.append('ethernet0.addressType = "static"')
VMX.append('ethernet0.address = "' + MAC + '"')
2017-05-03 17:00:03 -03:00
#
# Merge extra VMX options
for VMXopt in VMXOPTS:
try:
k,v = VMXopt.split("=")
except:
k=""
v=""
key = k.lstrip().strip()
value = v.lstrip().strip()
for i in VMX:
try:
ikey,ivalue = i.split("=")
except:
break
if ikey.lstrip().strip().lower() == key.lower():
index = VMX.index(i)
VMX[index] = ikey + " = " + value
break
else:
if key != '' and value != '':
VMX.append(key + " = " + value)
if isVerbose and VMXOPTS != '':
print "VMX file:"
for i in VMX:
print i
2017-05-16 12:49:38 -04:00
MyVM = FullPath + "/" + NAME
if CheckHasErrors:
Result = "Errors"
else:
Result = "Success"
if not isDryRun and not CheckHasErrors:
2017-05-03 17:00:03 -03:00
try:
# Create NAME.vmx
if isVerbose:
print "Create " + NAME + ".vmx file"
(stdin, stdout, stderr) = ssh.exec_command("mkdir " + FullPath )
type(stdin)
for line in VMX:
(stdin, stdout, stderr) = ssh.exec_command("echo " + line + " >>" + MyVM + ".vmx")
type(stdin)
# Create vmdk
if isVerbose:
print "Create " + NAME + ".vmdk file"
2017-05-16 12:49:38 -04:00
(stdin, stdout, stderr) = ssh.exec_command("vmkfstools -c " + str(HDISK) + "G -d " + DISKFORMAT + " " + MyVM + ".vmdk")
2017-05-03 17:00:03 -03:00
type(stdin)
# Register VM
if isVerbose:
print "Register VM"
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd solo/registervm " + MyVM + ".vmx")
type(stdin)
VMID = int(stdout.readlines()[0])
2017-05-03 17:00:03 -03:00
# Power on VM
if isVerbose:
print "Power ON VM"
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd vmsvc/power.on " + str(VMID))
type(stdin)
if stderr.readlines():
print "Error Power.on VM."
2017-05-16 12:49:38 -04:00
Result="Fail"
# Get Generated MAC
if NET != "None":
(stdin, stdout, stderr) = ssh.exec_command(
"grep -i 'ethernet0.*ddress = ' " + MyVM + ".vmx |tail -1|awk '{print $NF}'")
type(stdin)
GeneratedMAC = str(stdout.readlines()[0]).strip('\n"')
2017-05-03 17:00:03 -03:00
except:
2017-05-16 12:49:38 -04:00
print "There was an error creating the VM."
ErrorMessages += " There was an error creating the VM."
Result = "Fail"
2017-05-03 17:00:03 -03:00
# Print Summary
2017-05-16 12:49:38 -04:00
#
# The output log string
LogOutput += '"Host":"' + HOST + '",'
LogOutput += '"Port":"' + PORT + '",'
2017-05-16 12:49:38 -04:00
LogOutput += '"Name":"' + NAME + '",'
LogOutput += '"CPU":"' + str(CPU) + '",'
LogOutput += '"Mem":"' + str(MEM) + '",'
LogOutput += '"Hdisk":"' + str(HDISK) + '",'
LogOutput += '"DiskFormat":"' + DISKFORMAT + '",'
LogOutput += '"Virtual Device":"' + VIRTDEV + '",'
LogOutput += '"Store":"' + STORE + '",'
LogOutput += '"Store Used":"' + DSPATH + '",'
LogOutput += '"Network":"' + NET + '",'
LogOutput += '"ISO":"' + ISOarg + '",'
LogOutput += '"ISO used":"' + ISO + '",'
LogOutput += '"Guest OS":"' + GUESTOS + '",'
LogOutput += '"MAC":"' + MACarg + '",'
LogOutput += '"MAC Used":"' + GeneratedMAC + '",'
LogOutput += '"Dry Run":"' + str(isDryRun) + '",'
LogOutput += '"Verbose":"' + str(isVerbose) + '",'
if ErrorMessages != "":
LogOutput += '"Error Message":"' + ErrorMessages + '",'
LogOutput += '"Result":"' + Result + '",'
LogOutput += '"Completion Time":"' + str(theCurrDateTime()) + '"'
LogOutput += '}\n'
try:
with open(LOG, "a+w") as FD:
FD.write(LogOutput)
except:
print "Error writing to log file: " + LOG
if isSummary:
if isDryRun:
print "\nDry Run summary:"
else:
print "\nCreate VM Success:"
if isVerbose:
print "ESXi Host: " + HOST
print "ESXi Port: " + PORT
2017-05-16 12:49:38 -04:00
print "VM NAME: " + NAME
print "vCPU: " + str(CPU)
print "Memory: " + str(MEM) + "GB"
print "VM Disk: " + str(HDISK) + "GB"
if isVerbose:
print "Format: " + DISKFORMAT
print "DS Store: " + DSSTORE
print "Network: " + NET
2017-05-16 12:49:38 -04:00
if ISO:
print "ISO: " + ISO
if isVerbose:
print "Guest OS: " + GUESTOS
print "MAC: " + GeneratedMAC
else:
2017-05-16 12:49:38 -04:00
pass
2017-05-16 12:49:38 -04:00
if CheckHasErrors:
if isDryRun:
print "Dry Run: Failed."
sys.exit(1)
else:
if isDryRun:
print "Dry Run: Success."
else:
print GeneratedMAC
sys.exit(0)