From 76043fcab2f4a59f5e151990f7468f60bcf264ff Mon Sep 17 00:00:00 2001 From: Jonathan Senkerik Date: Tue, 16 May 2017 12:49:38 -0400 Subject: [PATCH] bugfix --- esxi-vm-create | 185 +++++++++++++++++++++++++++++-------------- esxi_vm_functions.py | 30 ++++++- 2 files changed, 154 insertions(+), 61 deletions(-) diff --git a/esxi-vm-create b/esxi-vm-create index 1b04d24..ed9a4b3 100755 --- a/esxi-vm-create +++ b/esxi-vm-create @@ -18,12 +18,13 @@ NAME = "" LOG = ConfigData['LOG'] isDryRun = ConfigData['isDryRun'] isVerbose = ConfigData['isVerbose'] +isSummary = ConfigData['isSummary'] HOST = ConfigData['HOST'] USER = ConfigData['USER'] PASSWORD = ConfigData['PASSWORD'] CPU = ConfigData['CPU'] MEM = ConfigData['MEM'] -SIZE = int(ConfigData['SIZE']) +HDISK = int(ConfigData['HDISK']) DISKFORMAT = ConfigData['DISKFORMAT'] VIRTDEV = ConfigData['VIRTDEV'] STORE = ConfigData['STORE'] @@ -31,8 +32,15 @@ NET = ConfigData['NET'] ISO = ConfigData['ISO'] GUESTOS = ConfigData['GUESTOS'] +ErrorMessages = "" MAC = "" GeneratedMAC = "" +ISOfound = False +CheckHasErrors = False +LeastUsedDS = "" +DSPATH="" +DSSTORE="" +FullPathExists = False # # Process Arguments @@ -40,20 +48,23 @@ GeneratedMAC = "" 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('-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 (" + str(HOST) + ")") +parser.add_argument("-H", "--Host", dest='HOST', type=str, help="ESXi Host/IP (" + str(HOST) + ")") 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("-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) + ")") -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("-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("-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("--showDefaults", dest='SHOW', action='store_true', help="Show Default VM settings stored in ~/.esxi-vm.yml") + args = parser.parse_args() @@ -61,6 +72,8 @@ if args.isDryRunarg: isDryRun = True if args.isVerbosearg: isVerbose = True +if args.isSummaryarg: + isSummary = True if args.HOST: HOST=args.HOST if args.USER: @@ -73,8 +86,8 @@ if args.CPU: CPU=int(args.CPU) if args.mem: MEM=int(args.mem) -if args.SIZE: - SIZE=int(args.SIZE) +if args.HDISK: + HDISK=int(args.HDISK) if args.ISO: ISO=args.ISO if args.NET: @@ -97,7 +110,7 @@ if args.UPDATE: ConfigData['PASSWORD'] = PASSWORD ConfigData['CPU'] = CPU ConfigData['MEM'] = MEM - ConfigData['SIZE'] = SIZE + ConfigData['HDISK'] = HDISK ConfigData['DISKFORMAT'] = DISKFORMAT ConfigData['VIRTDEV'] = VIRTDEV ConfigData['STORE'] = STORE @@ -111,8 +124,8 @@ if args.UPDATE: # # main() # -# print "Current Date " + theCurrDateTime() -CheckHasErrors = False +LogOutput = '{' +LogOutput += '"datetime":"' + str(theCurrDateTime()) + '",' if NAME == "": print "ERROR: Missing required option --name" @@ -136,7 +149,6 @@ except: # # Get list of DataStores, store in VOLUMES # -LeastUsedDS = "" try: (stdin, stdout, stderr) = ssh.exec_command("esxcli storage filesystem list |grep '/vmfs/volumes/.*true VMFS' |sort -nk7") type(stdin) @@ -170,6 +182,7 @@ except: # # Check MAC address # +MACarg = MAC if MAC != "": MACregex = '^([a-fA-F0-9]{2}[:|\-]){5}[a-fA-F0-9]{2}$' if re.compile(MACregex).search(MAC): @@ -179,18 +192,18 @@ if MAC != "": MAC="00:50:56:" + MAC.replace("-",":") else: print "ERROR: " + MAC + " Invalid MAC address." + ErrorMessages += " " + MAC + " Invalid MAC address." CheckHasErrors = True # # Get from ESXi host if ISO exists # -ISOfound = False +ISOarg = ISO 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 \;") @@ -208,6 +221,7 @@ if ISO != "": except: print "The Error is " + str(sys.exc_info()[0]) sys.exit(1) + # # Check if VM already exists # @@ -220,6 +234,7 @@ try: if NAME == splitLine[1]: VMID = splitLine[0] print "ERROR: VM " + NAME + " already exists." + ErrorMessages += " VM " + NAME + " already exists." CheckHasErrors = True except: print "The Error is " + str(sys.exc_info()[0]) @@ -231,23 +246,24 @@ except: # Check CPU 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 # Check MEM 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 -# Check SIZE -if SIZE < 1 or SIZE > 63488: - print "Virtual Disk size " + str(SIZE) + "GB out of range. [1-63488]" +# 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]." CheckHasErrors = True -# Check STORE +# Convert STORE to path and visa-versa V = [] -DSPATH="" -DSSTORE="" for Path in VOLUMES: V.append(VOLUMES[Path]) if STORE == Path or STORE == VOLUMES[Path]: @@ -256,39 +272,36 @@ for Path in VOLUMES: if DSSTORE not in V: 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) + ErrorMessages += " Disk Storage " + STORE + " doesn't exist. " CheckHasErrors = True # Check NIC (NIC record) if (NET not in VMNICS) and (NET != "None"): 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 # Check ISO exists if ISO != "" and not ISOfound: print "ERROR: ISO " + ISO + " not found. Use full path to ISO" + ErrorMessages += " ISO " + ISO + " not found. Use full path to ISO" CheckHasErrors = True # Check if DSPATH/NAME aready exists -FullPathExists = False 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." + ErrorMessages += " Directory " + FullPath + " already exists." CheckHasErrors = True except: pass -# -# Exit if there are any errors -# -if CheckHasErrors: - sys.exit(1) - # # Create the VM # @@ -341,13 +354,18 @@ if NET != "None": 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: # Create NAME.vmx if isVerbose: print "Create " + NAME + ".vmx file" - MyVM = FullPath + "/" + NAME (stdin, stdout, stderr) = ssh.exec_command("mkdir " + FullPath ) type(stdin) for line in VMX: @@ -357,7 +375,7 @@ if not isDryRun: # Create vmdk if isVerbose: 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) # Register VM @@ -374,36 +392,85 @@ if not isDryRun: type(stdin) if stderr.readlines(): print "Error Power.on VM." - sys.exit(1) + 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"') + except: - print "There was an error creating the VM!" - sys.exit(1) + print "There was an error creating the VM." + ErrorMessages += " There was an error creating the VM." + Result = "Fail" # Print Summary -if isDryRun: - print "\nDry Run summary:" - if MAC != "": - GeneratedMAC = MAC + +# +# 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 "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 + if ISO: + print "ISO: " + ISO + if isVerbose: + print "Guest OS: " + GUESTOS + print "MAC: " + GeneratedMAC else: - print "\nCreate VM Success:" + pass - 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"') - -print "ESXi Host: " + HOST -print "VM NAME: " + NAME -print "vCPU: " + str(CPU) -print "Memory: " + str(MEM) + "GB" -print "VM Disk: " + str(SIZE) + "GB" -if isVerbose: - print "Format: " + DISKFORMAT -print "DS Store: " + DSSTORE -print "Network: " + NET + " (" + GeneratedMAC + ")" -if ISO: - print "ISO: " + ISO -if isVerbose: - print "Guest OS: " + GUESTOS +if CheckHasErrors: + if isDryRun: + print "Dry Run: Failed." + sys.exit(1) +else: + if isDryRun: + print "Dry Run: Success." + else: + print GeneratedMAC + sys.exit(0) diff --git a/esxi_vm_functions.py b/esxi_vm_functions.py index b9ac4ee..f973b21 100755 --- a/esxi_vm_functions.py +++ b/esxi_vm_functions.py @@ -18,20 +18,45 @@ def setup_config(): # System wide defaults # ConfigData = dict( - LOG="~/esxi-vm.log", + + # Your logfile + LOG= os.path.expanduser("~") + "/esxi-vm.log", + + # Enable/Disable dryrun by default isDryRun=False, + + # Enable/Disable Verbose output by default isVerbose=False, + + # Enable/Disable exit summary by default + isSummary=False, + + # ESXi host/IP, root login & password HOST="esxi", USER="root", PASSWORD="", + + # Default number of vCPU's, GB Mem, & GB boot disk CPU=2, MEM=4, - SIZE=20, + HDISK=20, + + # Default Disk format thin, zeroedthick, eagerzeroedthick DISKFORMAT="thin", + + # Virtual Disk device type VIRTDEV="pvscsi", + + # Specify default Disk store to "LeastUsed" STORE="LeastUsed", + + # Default Network Interface (vswitch) NET="None", + + # Default ISO ISO="None", + + # Default GuestOS type. (See VMware documentation for all available options) GUESTOS="centos-64" ) @@ -39,6 +64,7 @@ def setup_config(): # # Get ConfigData from ConfigDataFile, then merge. + # if os.path.exists(ConfigDataFileLocation): FromFileConfigData = yaml.safe_load(open(ConfigDataFileLocation)) ConfigData.update(FromFileConfigData)