master
Jonathan Senkerik 2017-05-16 12:49:38 -04:00
parent 4fc8d3b7e7
commit 76043fcab2
2 changed files with 154 additions and 61 deletions

View File

@ -18,12 +18,13 @@ NAME = ""
LOG = ConfigData['LOG'] LOG = ConfigData['LOG']
isDryRun = ConfigData['isDryRun'] isDryRun = ConfigData['isDryRun']
isVerbose = ConfigData['isVerbose'] isVerbose = ConfigData['isVerbose']
isSummary = ConfigData['isSummary']
HOST = ConfigData['HOST'] HOST = ConfigData['HOST']
USER = ConfigData['USER'] USER = ConfigData['USER']
PASSWORD = ConfigData['PASSWORD'] PASSWORD = ConfigData['PASSWORD']
CPU = ConfigData['CPU'] CPU = ConfigData['CPU']
MEM = ConfigData['MEM'] MEM = ConfigData['MEM']
SIZE = int(ConfigData['SIZE']) HDISK = int(ConfigData['HDISK'])
DISKFORMAT = ConfigData['DISKFORMAT'] DISKFORMAT = ConfigData['DISKFORMAT']
VIRTDEV = ConfigData['VIRTDEV'] VIRTDEV = ConfigData['VIRTDEV']
STORE = ConfigData['STORE'] STORE = ConfigData['STORE']
@ -31,8 +32,15 @@ NET = ConfigData['NET']
ISO = ConfigData['ISO'] ISO = ConfigData['ISO']
GUESTOS = ConfigData['GUESTOS'] GUESTOS = ConfigData['GUESTOS']
ErrorMessages = ""
MAC = "" MAC = ""
GeneratedMAC = "" GeneratedMAC = ""
ISOfound = False
CheckHasErrors = False
LeastUsedDS = ""
DSPATH=""
DSSTORE=""
FullPathExists = False
# #
# Process Arguments # Process Arguments
@ -40,20 +48,23 @@ GeneratedMAC = ""
parser = argparse.ArgumentParser(description='ESXi Create VM utility.') 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) + ")") parser.add_argument('-d', '--dry', dest='isDryRunarg', action='store_true', help="Enable Dry Run mode (" + str(isDryRun) + ")")
parser.add_argument('-v', '--verbose', dest='isVerbosearg', action='store_true', help="Enable Verbose mode (" + str(isVerbose) + ")") parser.add_argument("-H", "--Host", dest='HOST', type=str, help="ESXi Host/IP (" + str(HOST) + ")")
parser.add_argument("-H", "--Host", dest='HOST', type=str, help="ESXi Host (" + str(HOST) + ")")
parser.add_argument("-U", "--User", dest='USER', type=str, help="ESXi Host username (" + str(USER) + ")") 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("-P", "--Password", dest='PASSWORD', type=str, help="ESXi Host password (*****)")
parser.add_argument("-n", "--name", dest='NAME', type=str, help="VM name") 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("-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) + ")") parser.add_argument("-m", "--mem", type=int, help="Memory in GB (" + str(MEM) + ")")
parser.add_argument("-s", "--size", dest='SIZE', type=str, help="Size of virt disk (" + str(SIZE) + ")") parser.add_argument("-v", "--vdisk", dest='HDISK', type=str, help="Size of virt hdisk (" + str(HDISK) + ")")
parser.add_argument("-i", "--iso", dest='ISO', type=str, help="CDROM ISO Path | None (" + str(ISO) + ")") 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) + ")") parser.add_argument("-N", "--net", dest='NET', type=str, help="Network Interface | None (" + str(NET) + ")")
parser.add_argument("-M", "--MAC", dest='MAC', type=str, help="MAC address") parser.add_argument("-M", "--mac", dest='MAC', type=str, help="MAC address")
parser.add_argument("-S", "--store", dest='STORE', type=str, help="vmfs Store | LeastUsed (" + str(STORE) + ")") 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("-g", "--guestos", dest='GUESTOS', type=str, help="Guest OS. (" + str(GUESTOS) + ")")
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) + ")")
parser.add_argument("-u", "--updateDefaults", dest='UPDATE', action='store_true', help="Update Default VM settings stored in ~/.esxi-vm.yml") parser.add_argument("-u", "--updateDefaults", dest='UPDATE', action='store_true', help="Update Default VM settings stored in ~/.esxi-vm.yml")
#parser.add_argument("--showDefaults", dest='SHOW', action='store_true', help="Show Default VM settings stored in ~/.esxi-vm.yml")
args = parser.parse_args() args = parser.parse_args()
@ -61,6 +72,8 @@ if args.isDryRunarg:
isDryRun = True isDryRun = True
if args.isVerbosearg: if args.isVerbosearg:
isVerbose = True isVerbose = True
if args.isSummaryarg:
isSummary = True
if args.HOST: if args.HOST:
HOST=args.HOST HOST=args.HOST
if args.USER: if args.USER:
@ -73,8 +86,8 @@ if args.CPU:
CPU=int(args.CPU) CPU=int(args.CPU)
if args.mem: if args.mem:
MEM=int(args.mem) MEM=int(args.mem)
if args.SIZE: if args.HDISK:
SIZE=int(args.SIZE) HDISK=int(args.HDISK)
if args.ISO: if args.ISO:
ISO=args.ISO ISO=args.ISO
if args.NET: if args.NET:
@ -97,7 +110,7 @@ if args.UPDATE:
ConfigData['PASSWORD'] = PASSWORD ConfigData['PASSWORD'] = PASSWORD
ConfigData['CPU'] = CPU ConfigData['CPU'] = CPU
ConfigData['MEM'] = MEM ConfigData['MEM'] = MEM
ConfigData['SIZE'] = SIZE ConfigData['HDISK'] = HDISK
ConfigData['DISKFORMAT'] = DISKFORMAT ConfigData['DISKFORMAT'] = DISKFORMAT
ConfigData['VIRTDEV'] = VIRTDEV ConfigData['VIRTDEV'] = VIRTDEV
ConfigData['STORE'] = STORE ConfigData['STORE'] = STORE
@ -111,8 +124,8 @@ if args.UPDATE:
# #
# main() # main()
# #
# print "Current Date " + theCurrDateTime() LogOutput = '{'
CheckHasErrors = False LogOutput += '"datetime":"' + str(theCurrDateTime()) + '",'
if NAME == "": if NAME == "":
print "ERROR: Missing required option --name" print "ERROR: Missing required option --name"
@ -136,7 +149,6 @@ except:
# #
# Get list of DataStores, store in VOLUMES # Get list of DataStores, store in VOLUMES
# #
LeastUsedDS = ""
try: try:
(stdin, stdout, stderr) = ssh.exec_command("esxcli storage filesystem list |grep '/vmfs/volumes/.*true VMFS' |sort -nk7") (stdin, stdout, stderr) = ssh.exec_command("esxcli storage filesystem list |grep '/vmfs/volumes/.*true VMFS' |sort -nk7")
type(stdin) type(stdin)
@ -170,6 +182,7 @@ except:
# #
# Check MAC address # Check MAC address
# #
MACarg = MAC
if MAC != "": if MAC != "":
MACregex = '^([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}$' MACregex = '^([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}$'
if re.compile(MACregex).search(MAC): if re.compile(MACregex).search(MAC):
@ -179,18 +192,18 @@ if MAC != "":
MAC="00:50:56:" + MAC.replace("-",":") MAC="00:50:56:" + MAC.replace("-",":")
else: else:
print "ERROR: " + MAC + " Invalid MAC address." print "ERROR: " + MAC + " Invalid MAC address."
ErrorMessages += " " + MAC + " Invalid MAC address."
CheckHasErrors = True CheckHasErrors = True
# #
# Get from ESXi host if ISO exists # Get from ESXi host if ISO exists
# #
ISOfound = False ISOarg = ISO
if ISO == "None": if ISO == "None":
ISO = "" ISO = ""
if ISO != "": if ISO != "":
try: try:
# If ISO has no "/", try to find the ISO # If ISO has no "/", try to find the ISO
if not re.match('/', 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 \;") (stdin, stdout, stderr) = ssh.exec_command("find /vmfs/volumes/ -type f -name " + ISO + " -exec sh -c 'echo $1; kill $PPID' sh {} 2>/dev/null \;")
@ -208,6 +221,7 @@ if ISO != "":
except: except:
print "The Error is " + str(sys.exc_info()[0]) print "The Error is " + str(sys.exc_info()[0])
sys.exit(1) sys.exit(1)
# #
# Check if VM already exists # Check if VM already exists
# #
@ -220,6 +234,7 @@ try:
if NAME == splitLine[1]: if NAME == splitLine[1]:
VMID = splitLine[0] VMID = splitLine[0]
print "ERROR: VM " + NAME + " already exists." print "ERROR: VM " + NAME + " already exists."
ErrorMessages += " VM " + NAME + " already exists."
CheckHasErrors = True CheckHasErrors = True
except: except:
print "The Error is " + str(sys.exc_info()[0]) print "The Error is " + str(sys.exc_info()[0])
@ -231,23 +246,24 @@ except:
# Check CPU # Check CPU
if CPU < 1 or CPU > 128: if CPU < 1 or CPU > 128:
print str(CPU) + " CPU out of range. [1-128]" print str(CPU) + " CPU out of range. [1-128]."
ErrorMessages += " " + str(CPU) + " CPU out of range. [1-128]."
CheckHasErrors = True CheckHasErrors = True
# Check MEM # Check MEM
if MEM < 1 or MEM > 4080: if MEM < 1 or MEM > 4080:
print str(MEM) + "GB Memory out of range. [1-4080]" print str(MEM) + "GB Memory out of range. [1-4080]."
ErrorMessages += " " + str(MEM) + "GB Memory out of range. [1-4080]."
CheckHasErrors = True CheckHasErrors = True
# Check SIZE # Check HDISK
if SIZE < 1 or SIZE > 63488: if HDISK < 1 or HDISK > 63488:
print "Virtual Disk size " + str(SIZE) + "GB out of range. [1-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]."
CheckHasErrors = True CheckHasErrors = True
# Check STORE # Convert STORE to path and visa-versa
V = [] V = []
DSPATH=""
DSSTORE=""
for Path in VOLUMES: for Path in VOLUMES:
V.append(VOLUMES[Path]) V.append(VOLUMES[Path])
if STORE == Path or STORE == VOLUMES[Path]: if STORE == Path or STORE == VOLUMES[Path]:
@ -256,39 +272,36 @@ for Path in VOLUMES:
if DSSTORE not in V: if DSSTORE not in V:
print "ERROR: Disk Storage " + STORE + " doesn't exist. " print "ERROR: Disk Storage " + STORE + " doesn't exist. "
print " Available Disk Stores: " + str(V) print " Available Disk Stores: " + str([str(item) for item in V])
print " LeastUsed Disk Store : " + str(LeastUsedDS) print " LeastUsed Disk Store : " + str(LeastUsedDS)
ErrorMessages += " Disk Storage " + STORE + " doesn't exist. "
CheckHasErrors = True CheckHasErrors = True
# Check NIC (NIC record) # Check NIC (NIC record)
if (NET not in VMNICS) and (NET != "None"): if (NET not in VMNICS) and (NET != "None"):
print "ERROR: Virtual NIC " + NET + " doesn't exist." print "ERROR: Virtual NIC " + NET + " doesn't exist."
print " Available VM NICs: " + str(VMNICS) + " or 'None'" print " Available VM NICs: " + str([str(item) for item in VMNICS]) + " or 'None'"
ErrorMessages += " Virtual NIC " + NET + " doesn't exist."
CheckHasErrors = True CheckHasErrors = True
# Check ISO exists # Check ISO exists
if ISO != "" and not ISOfound: if ISO != "" and not ISOfound:
print "ERROR: ISO " + ISO + " not found. Use full path to ISO" print "ERROR: ISO " + ISO + " not found. Use full path to ISO"
ErrorMessages += " ISO " + ISO + " not found. Use full path to ISO"
CheckHasErrors = True CheckHasErrors = True
# Check if DSPATH/NAME aready exists # Check if DSPATH/NAME aready exists
FullPathExists = False
try: try:
FullPath = DSPATH + "/" + NAME FullPath = DSPATH + "/" + NAME
(stdin, stdout, stderr) = ssh.exec_command("ls -d " + FullPath) (stdin, stdout, stderr) = ssh.exec_command("ls -d " + FullPath)
type(stdin) type(stdin)
if stdout.readlines() and not stderr.readlines(): if stdout.readlines() and not stderr.readlines():
print "ERROR: Directory " + FullPath + " already exists." print "ERROR: Directory " + FullPath + " already exists."
ErrorMessages += " Directory " + FullPath + " already exists."
CheckHasErrors = True CheckHasErrors = True
except: except:
pass pass
#
# Exit if there are any errors
#
if CheckHasErrors:
sys.exit(1)
# #
# Create the VM # Create the VM
# #
@ -341,13 +354,18 @@ if NET != "None":
VMX.append('ethernet0.address = "' + MAC + '"') VMX.append('ethernet0.address = "' + MAC + '"')
if not isDryRun: MyVM = FullPath + "/" + NAME
if CheckHasErrors:
Result = "Errors"
else:
Result = "Success"
if not isDryRun and not CheckHasErrors:
try: try:
# Create NAME.vmx # Create NAME.vmx
if isVerbose: if isVerbose:
print "Create " + NAME + ".vmx file" print "Create " + NAME + ".vmx file"
MyVM = FullPath + "/" + NAME
(stdin, stdout, stderr) = ssh.exec_command("mkdir " + FullPath ) (stdin, stdout, stderr) = ssh.exec_command("mkdir " + FullPath )
type(stdin) type(stdin)
for line in VMX: for line in VMX:
@ -357,7 +375,7 @@ if not isDryRun:
# Create vmdk # Create vmdk
if isVerbose: if isVerbose:
print "Create " + NAME + ".vmdk file" print "Create " + NAME + ".vmdk file"
(stdin, stdout, stderr) = ssh.exec_command("vmkfstools -c " + str(SIZE) + "G -d " + DISKFORMAT + " " + MyVM + ".vmdk") (stdin, stdout, stderr) = ssh.exec_command("vmkfstools -c " + str(HDISK) + "G -d " + DISKFORMAT + " " + MyVM + ".vmdk")
type(stdin) type(stdin)
# Register VM # Register VM
@ -374,36 +392,85 @@ if not isDryRun:
type(stdin) type(stdin)
if stderr.readlines(): if stderr.readlines():
print "Error Power.on VM." print "Error Power.on VM."
sys.exit(1) Result="Fail"
except:
print "There was an error creating the VM!"
sys.exit(1)
# Print Summary
if isDryRun:
print "\nDry Run summary:"
if MAC != "":
GeneratedMAC = MAC
else:
print "\nCreate VM Success:"
# Get Generated MAC
if NET != "None": if NET != "None":
(stdin, stdout, stderr) = ssh.exec_command("grep -i 'ethernet0.*ddress = ' " + MyVM + ".vmx |tail -1|awk '{print $NF}'") (stdin, stdout, stderr) = ssh.exec_command(
"grep -i 'ethernet0.*ddress = ' " + MyVM + ".vmx |tail -1|awk '{print $NF}'")
type(stdin) type(stdin)
GeneratedMAC = str(stdout.readlines()[0]).strip('\n"') GeneratedMAC = str(stdout.readlines()[0]).strip('\n"')
except:
print "There was an error creating the VM."
ErrorMessages += " There was an error creating the VM."
Result = "Fail"
# Print Summary
#
# The output log string
LogOutput += '"Host":"' + HOST + '",'
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 Host: " + HOST
print "VM NAME: " + NAME print "VM NAME: " + NAME
print "vCPU: " + str(CPU) print "vCPU: " + str(CPU)
print "Memory: " + str(MEM) + "GB" print "Memory: " + str(MEM) + "GB"
print "VM Disk: " + str(SIZE) + "GB" print "VM Disk: " + str(HDISK) + "GB"
if isVerbose: if isVerbose:
print "Format: " + DISKFORMAT print "Format: " + DISKFORMAT
print "DS Store: " + DSSTORE print "DS Store: " + DSSTORE
print "Network: " + NET + " (" + GeneratedMAC + ")" print "Network: " + NET
if ISO: if ISO:
print "ISO: " + ISO print "ISO: " + ISO
if isVerbose: if isVerbose:
print "Guest OS: " + GUESTOS print "Guest OS: " + GUESTOS
print "MAC: " + GeneratedMAC
else:
pass
if CheckHasErrors:
if isDryRun:
print "Dry Run: Failed."
sys.exit(1)
else:
if isDryRun:
print "Dry Run: Success."
else:
print GeneratedMAC
sys.exit(0)

View File

@ -18,20 +18,45 @@ def setup_config():
# System wide defaults # System wide defaults
# #
ConfigData = dict( ConfigData = dict(
LOG="~/esxi-vm.log",
# Your logfile
LOG= os.path.expanduser("~") + "/esxi-vm.log",
# Enable/Disable dryrun by default
isDryRun=False, isDryRun=False,
# Enable/Disable Verbose output by default
isVerbose=False, isVerbose=False,
# Enable/Disable exit summary by default
isSummary=False,
# ESXi host/IP, root login & password
HOST="esxi", HOST="esxi",
USER="root", USER="root",
PASSWORD="", PASSWORD="",
# Default number of vCPU's, GB Mem, & GB boot disk
CPU=2, CPU=2,
MEM=4, MEM=4,
SIZE=20, HDISK=20,
# Default Disk format thin, zeroedthick, eagerzeroedthick
DISKFORMAT="thin", DISKFORMAT="thin",
# Virtual Disk device type
VIRTDEV="pvscsi", VIRTDEV="pvscsi",
# Specify default Disk store to "LeastUsed"
STORE="LeastUsed", STORE="LeastUsed",
# Default Network Interface (vswitch)
NET="None", NET="None",
# Default ISO
ISO="None", ISO="None",
# Default GuestOS type. (See VMware documentation for all available options)
GUESTOS="centos-64" GUESTOS="centos-64"
) )
@ -39,6 +64,7 @@ def setup_config():
# #
# Get ConfigData from ConfigDataFile, then merge. # Get ConfigData from ConfigDataFile, then merge.
#
if os.path.exists(ConfigDataFileLocation): if os.path.exists(ConfigDataFileLocation):
FromFileConfigData = yaml.safe_load(open(ConfigDataFileLocation)) FromFileConfigData = yaml.safe_load(open(ConfigDataFileLocation))
ConfigData.update(FromFileConfigData) ConfigData.update(FromFileConfigData)