Add -o option, fix isSummary save, add beta esxi-vm-destroy

master
Jonathan Senkerik 2017-10-23 21:44:46 -04:00
parent 625e4f269c
commit 5809a2df0b
6 changed files with 300 additions and 17 deletions

1
.gitignore vendored 100644
View File

@ -0,0 +1 @@
esxi_vm_functions.pyc

14
Makefile 100644
View File

@ -0,0 +1,14 @@
all:
@echo "make install"
install:
install -m 755 ./esxi-vm-create /usr/local/bin/
install -m 755 ./esxi-vm-destroy /usr/local/bin/
install -m 755 ./esxi_vm_functions.py /usr/local/bin/
@echo "Install Success."
uninstall:
rm -fr /usr/local/bin/esxi-vm-create
rm -fr /usr/local/bin/esxi-vm-destroy
rm -fr /usr/local/bin/esxi_vm_functions.py
rm -fr /usr/local/bin/esxi_vm_functions.pyc

View File

@ -26,7 +26,7 @@ Usage
By default the Network set set to "None". A full or partial MAC address can be specified. A partial MAC address argument would be 3 Hex pairs which would then be prepended by VMware's OEM "00:50:56". By default the Network set set to "None". A full or partial MAC address can be specified. A partial MAC address argument would be 3 Hex pairs which would then be prepended by VMware's OEM "00:50:56".
By default the VM is powered on. If an ISO was specified, then it will boot the ISO image. Otherwise, the VM will attempt a PXE boot if a Network Interface was specified. You could customize the ISO image to specify the kickstart file, or PXE boot using COBBLER, Foreman, Razor, or your favorite provisioning tool. By default the VM is powered on. If an ISO was specified, then it will boot the ISO image. Otherwise, the VM will attempt a PXE boot if a Network Interface was specified. You could customize the ISO image to specify the kickstart file, or PXE boot using COBBLER, Foreman, Razor, or your favorite provisioning tool.
To help with automated provisioning, the script will output the full MAC address and exit code 0 on success. You can specify --summary to get a more detailed summary of the VM that was created. To help with automated provisioning, the script will output the full MAC address and exit code 0 on success. You can specify --summary to get a more detailed summary of the VM that was created.
@ -34,12 +34,12 @@ Usage
Requirements Requirements
------------ ------------
You must enable ssh access on your ESXi server. The VMware VIX API tools are not required. You must enable ssh access on your ESXi server. Google 'how to enable ssh access on esxi' for instructions. The VMware VIX API tools are not required.
It's HIGHLY RECOMMENDED to use password-less authentication by copying your ssh public keys to the ESXi host, otherwise your ESXi root password could be stored in clear-text in your home directory. It's HIGHLY RECOMMENDED to use password-less authentication by copying your ssh public keys to the ESXi host, otherwise your ESXi root password could be stored in clear-text in your home directory.
Python and paramiko is a software requirement. Python and paramiko is a software requirement.
``` ```
yum -y install python python-paramiko yum -y install python python-paramiko
``` ```
@ -50,9 +50,11 @@ Command Line Args
``` ```
./esxi-vm-create --help ./esxi-vm-create --help
usage: esxi-vm-create [-h] [-d] [-H HOST] [-U USER] [-P PASSWORD] [-n NAME] usage: esxi-vm-create [-h] [-d] [-H HOST] [-U USER] [-P PASSWORD] [-n NAME]
[-c CPU] [-m MEM] [-v HDISK] [-i ISO] [-N NET] [-M MAC] [-c CPU] [-m MEM] [-v HDISK] [-i ISO] [-N NET] [-M MAC]
[-S STORE] [-g GUESTOS] [-V] [--summary] [-u] [-S STORE] [-g GUESTOS] [-o VMXOPTS] [-V] [--summary]
[-u]
ESXi Create VM utility. ESXi Create VM utility.
@ -65,20 +67,21 @@ optional arguments:
ESXi Host password (*****) ESXi Host password (*****)
-n NAME, --name NAME VM name -n NAME, --name NAME VM name
-c CPU, --cpu CPU Number of vCPUS (2) -c CPU, --cpu CPU Number of vCPUS (2)
-m MEM, --mem MEM Memory in GB (2) -m MEM, --mem MEM Memory in GB (4)
-v HDISK, --vdisk HDISK -v HDISK, --vdisk HDISK
Size of virt hdisk (12) Size of virt hdisk (20)
-i ISO, --iso ISO CDROM ISO Path | None (None) -i ISO, --iso ISO CDROM ISO Path | None (None)
-N NET, --net NET Network Interface | None (192.168.1) -N NET, --net NET Network Interface | None (None)
-M MAC, --mac MAC MAC address -M MAC, --mac MAC MAC address
-S STORE, --store STORE -S STORE, --store STORE
vmfs Store | LeastUsed (DS_3TB_m) vmfs Store | LeastUsed (LeastUsed)
-g GUESTOS, --guestos GUESTOS -g GUESTOS, --guestos GUESTOS
Guest OS. (centos-64) Guest OS. (centos-64)
-o VMXOPTS, --options VMXOPTS
Comma list of VMX Options.
-V, --verbose Enable Verbose mode (False) -V, --verbose Enable Verbose mode (False)
--summary Display Summary (False) --summary Display Summary (False)
-u, --updateDefaults Update Default VM settings stored in ~/.esxi-vm.yml -u, --updateDefaults Update Default VM settings stored in ~/.esxi-vm.yml
``` ```
@ -176,6 +179,12 @@ Guest OS: centos-64
MAC: 00:0c:29:ea:a0:42 MAC: 00:0c:29:ea:a0:42
00:0c:29:ea:a0:42 00:0c:29:ea:a0:42
```
Merge/Add extra VMX options, saved as default.
```
./esxi-vm-create -o 'floppy0.present = "TRUE",svga.autodetect = "TRUE",svga.present = "TRUE"' -u
Saving new Defaults to ~/.esxi-vm.yml
``` ```
License License
@ -202,4 +211,3 @@ Support
Website : http://www.jintegrate.co Website : http://www.jintegrate.co
github : http://github.com/josenk/ github : http://github.com/josenk/

View File

@ -31,6 +31,7 @@ STORE = ConfigData['STORE']
NET = ConfigData['NET'] NET = ConfigData['NET']
ISO = ConfigData['ISO'] ISO = ConfigData['ISO']
GUESTOS = ConfigData['GUESTOS'] GUESTOS = ConfigData['GUESTOS']
VMXOPTS = ConfigData['VMXOPTS']
ErrorMessages = "" ErrorMessages = ""
MAC = "" MAC = ""
@ -60,6 +61,7 @@ parser.add_argument("-N", "--net", dest='NET', type=str, help="Network Interface
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("-o", "--options", dest='VMXOPTS', type=str, default='NIL', help="Comma list of VMX Options.")
parser.add_argument('-V', '--verbose', dest='isVerbosearg', action='store_true', help="Enable Verbose mode (" + str(isVerbose) + ")") 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('--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")
@ -100,11 +102,17 @@ if STORE == "":
STORE = "LeastUsed" STORE = "LeastUsed"
if args.GUESTOS: if args.GUESTOS:
GUESTOS=args.GUESTOS GUESTOS=args.GUESTOS
if args.VMXOPTS == '' and VMXOPTS != '':
VMXOPTS=''
if args.VMXOPTS and args.VMXOPTS != 'NIL':
VMXOPTS=args.VMXOPTS.split(",")
if args.UPDATE: if args.UPDATE:
print "Saving new Defaults to ~/.esxi-vm.yml" print "Saving new Defaults to ~/.esxi-vm.yml"
ConfigData['isDryRun'] = isDryRun ConfigData['isDryRun'] = isDryRun
ConfigData['isVerbose'] = isVerbose ConfigData['isVerbose'] = isVerbose
ConfigData['isSummary'] = isSummary
ConfigData['HOST'] = HOST ConfigData['HOST'] = HOST
ConfigData['USER'] = USER ConfigData['USER'] = USER
ConfigData['PASSWORD'] = PASSWORD ConfigData['PASSWORD'] = PASSWORD
@ -117,6 +125,7 @@ if args.UPDATE:
ConfigData['NET'] = NET ConfigData['NET'] = NET
ConfigData['ISO'] = ISO ConfigData['ISO'] = ISO
ConfigData['GUESTOS'] = GUESTOS ConfigData['GUESTOS'] = GUESTOS
ConfigData['VMXOPTS'] = VMXOPTS
SaveConfig(ConfigData) SaveConfig(ConfigData)
if NAME == "": if NAME == "":
sys.exit(0) sys.exit(0)
@ -194,7 +203,7 @@ if MAC != "":
print "ERROR: " + MAC + " Invalid MAC address." print "ERROR: " + MAC + " Invalid MAC address."
ErrorMessages += " " + 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
@ -353,6 +362,33 @@ if NET != "None":
VMX.append('ethernet0.addressType = "static"') VMX.append('ethernet0.addressType = "static"')
VMX.append('ethernet0.address = "' + MAC + '"') VMX.append('ethernet0.address = "' + MAC + '"')
#
# 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
MyVM = FullPath + "/" + NAME MyVM = FullPath + "/" + NAME
if CheckHasErrors: if CheckHasErrors:
@ -384,7 +420,7 @@ if not isDryRun and not CheckHasErrors:
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd solo/registervm " + MyVM + ".vmx") (stdin, stdout, stderr) = ssh.exec_command("vim-cmd solo/registervm " + MyVM + ".vmx")
type(stdin) type(stdin)
VMID = int(stdout.readlines()[0]) VMID = int(stdout.readlines()[0])
# Power on VM # Power on VM
if isVerbose: if isVerbose:
print "Power ON VM" print "Power ON VM"
@ -453,7 +489,7 @@ if isSummary:
if isVerbose: if isVerbose:
print "Format: " + DISKFORMAT print "Format: " + DISKFORMAT
print "DS Store: " + DSSTORE print "DS Store: " + DSSTORE
print "Network: " + NET print "Network: " + NET
if ISO: if ISO:
print "ISO: " + ISO print "ISO: " + ISO
if isVerbose: if isVerbose:
@ -472,5 +508,3 @@ else:
else: else:
print GeneratedMAC print GeneratedMAC
sys.exit(0) sys.exit(0)

224
esxi-vm-destroy 100755
View File

@ -0,0 +1,224 @@
#!/usr/bin/python
import argparse # Argument parser
import datetime # For current Date/Time
import time
import os.path # To check if file exists
import sys # For args
import re # For regex
import paramiko # For remote ssh
import yaml
import warnings
from esxi_vm_functions import *
# Defaults and Variable setup
ConfigData = setup_config()
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']
HDISK = int(ConfigData['HDISK'])
DISKFORMAT = ConfigData['DISKFORMAT']
VIRTDEV = ConfigData['VIRTDEV']
STORE = ConfigData['STORE']
NET = ConfigData['NET']
ISO = ConfigData['ISO']
GUESTOS = ConfigData['GUESTOS']
ErrorMessages = ""
CheckHasErrors = False
DSPATH=""
DSSTORE=""
#
# Process Arguments
#
parser = argparse.ArgumentParser(description='ESXi Create VM utility.')
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('-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) + ")")
args = parser.parse_args()
if args.isVerbosearg:
isVerbose = True
if args.isSummaryarg:
isSummary = True
if args.HOST:
HOST=args.HOST
if args.USER:
USER=args.USER
if args.PASSWORD:
PASSWORD=args.PASSWORD
if args.NAME:
NAME=args.NAME
#
# main()
#
LogOutput = '{'
LogOutput += '"datetime":"' + str(theCurrDateTime()) + '",'
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, username=USER, password=PASSWORD)
(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, username: %s" % (HOST, USER)
sys.exit(1)
except:
print "The Error is " + str(sys.exc_info()[0])
print "Unable to access ESXi Host: %s, username: %s" % (HOST, USER)
sys.exit(1)
#
# Check if VM 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]
JNK = line.split('[')[1]
STORE = JNK.split(']')[0]
VMDIR = splitLine[3]
if VMID == -1:
print "Warning: VM " + NAME + " doesn't exists."
ErrorMessages += " VM " + NAME + " doesn't exists."
CheckHasErrors = True
CheckHasWarnings = True
except:
print "The Error is " + str(sys.exc_info()[0])
sys.exit(1)
# Get List of 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]
except:
print "The Error is " + str(sys.exc_info()[0])
sys.exit(1)
# Convert STORE to path and visa-versa
V = []
for Path in VOLUMES:
V.append(VOLUMES[Path])
if STORE == Path or STORE == VOLUMES[Path]:
DSPATH = Path
DSSTORE = VOLUMES[Path]
if CheckHasErrors:
Result = "Errors"
else:
Result = "Success"
if not CheckHasErrors:
try:
CurrentState = ""
CurrentStateCounter = 0
while CurrentState != "off":
if isVerbose:
print "Get state VM"
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd vmsvc/power.getstate " + str(VMID))
type(stdin)
lines = str(stdout.readlines()) + str(stderr.readlines())
if isVerbose:
print "power.getstate: " + lines
if re.search("Powered off", lines):
CurrentState = "off"
# Power off VM
if isVerbose:
print "Power OFF VM"
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd vmsvc/power.off " + str(VMID) + " ||echo")
type(stdin)
lines = str(stdout.readlines()) + str(stderr.readlines())
if isVerbose:
print "power.off: " + str(lines)
CurrentStateCounter += 1
if CurrentStateCounter >10:
break
time.sleep(1)
# destroy VM
if isVerbose:
print "Destroy VM"
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd vmsvc/destroy " + str(VMID))
type(stdin)
lines = str(stdout.readlines()) + str(stderr.readlines())
if isVerbose:
print "destroy: " + str(lines)
except:
print "There was an error destroying the VM."
ErrorMessages += " There was an error destroying the VM."
CheckHasErrors = True
Result = "Fail"
# Print Summary
#
# The output log string
LogOutput += '"Host":"' + HOST + '",'
LogOutput += '"Name":"' + NAME + '",'
LogOutput += '"Store Used":"' + DSPATH + '",'
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 isVerbose:
print "ESXi Host: " + HOST
print "VM NAME: " + NAME
print "Path: " + DSSTORE
else:
pass
if CheckHasErrors and not CheckHasWarnings:
print "Failed"
sys.exit(1)
else:
print "Success"
sys.exit(0)

View File

@ -57,7 +57,10 @@ def setup_config():
ISO="None", ISO="None",
# Default GuestOS type. (See VMware documentation for all available options) # Default GuestOS type. (See VMware documentation for all available options)
GUESTOS="centos-64" GUESTOS="centos-64",
# Extra VMX options
VMXOPTS=""
) )
ConfigDataFileLocation = os.path.expanduser("~") + "/.esxi-vm.yml" ConfigDataFileLocation = os.path.expanduser("~") + "/.esxi-vm.yml"
@ -112,4 +115,3 @@ def float2human(num):
return '0 bytes' return '0 bytes'
if num == 1: if num == 1:
return '1 byte' return '1 byte'