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 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.
@ -34,12 +34,12 @@ Usage
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.
Python and paramiko is a software requirement.
```
yum -y install python python-paramiko
```
@ -50,9 +50,11 @@ Command Line Args
```
./esxi-vm-create --help
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]
[-S STORE] [-g GUESTOS] [-V] [--summary] [-u]
[-S STORE] [-g GUESTOS] [-o VMXOPTS] [-V] [--summary]
[-u]
ESXi Create VM utility.
@ -65,20 +67,21 @@ optional arguments:
ESXi Host password (*****)
-n NAME, --name NAME VM name
-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
Size of virt hdisk (12)
Size of virt hdisk (20)
-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
-S STORE, --store STORE
vmfs Store | LeastUsed (DS_3TB_m)
vmfs Store | LeastUsed (LeastUsed)
-g GUESTOS, --guestos GUESTOS
Guest OS. (centos-64)
-o VMXOPTS, --options VMXOPTS
Comma list of VMX Options.
-V, --verbose Enable Verbose mode (False)
--summary Display Summary (False)
-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
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
@ -202,4 +211,3 @@ Support
Website : http://www.jintegrate.co
github : http://github.com/josenk/

View File

@ -31,6 +31,7 @@ STORE = ConfigData['STORE']
NET = ConfigData['NET']
ISO = ConfigData['ISO']
GUESTOS = ConfigData['GUESTOS']
VMXOPTS = ConfigData['VMXOPTS']
ErrorMessages = ""
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("-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.")
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")
@ -100,11 +102,17 @@ 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(",")
if args.UPDATE:
print "Saving new Defaults to ~/.esxi-vm.yml"
ConfigData['isDryRun'] = isDryRun
ConfigData['isVerbose'] = isVerbose
ConfigData['isSummary'] = isSummary
ConfigData['HOST'] = HOST
ConfigData['USER'] = USER
ConfigData['PASSWORD'] = PASSWORD
@ -117,6 +125,7 @@ if args.UPDATE:
ConfigData['NET'] = NET
ConfigData['ISO'] = ISO
ConfigData['GUESTOS'] = GUESTOS
ConfigData['VMXOPTS'] = VMXOPTS
SaveConfig(ConfigData)
if NAME == "":
sys.exit(0)
@ -194,7 +203,7 @@ if MAC != "":
print "ERROR: " + MAC + " Invalid MAC address."
ErrorMessages += " " + MAC + " Invalid MAC address."
CheckHasErrors = True
#
# Get from ESXi host if ISO exists
@ -353,6 +362,33 @@ if NET != "None":
VMX.append('ethernet0.addressType = "static"')
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
if CheckHasErrors:
@ -384,7 +420,7 @@ if not isDryRun and not CheckHasErrors:
(stdin, stdout, stderr) = ssh.exec_command("vim-cmd solo/registervm " + MyVM + ".vmx")
type(stdin)
VMID = int(stdout.readlines()[0])
# Power on VM
if isVerbose:
print "Power ON VM"
@ -453,7 +489,7 @@ if isSummary:
if isVerbose:
print "Format: " + DISKFORMAT
print "DS Store: " + DSSTORE
print "Network: " + NET
print "Network: " + NET
if ISO:
print "ISO: " + ISO
if isVerbose:
@ -472,5 +508,3 @@ else:
else:
print GeneratedMAC
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",
# 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"
@ -112,4 +115,3 @@ def float2human(num):
return '0 bytes'
if num == 1:
return '1 byte'