-
-
Save snobear/8788977 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
""" | |
NOTE: | |
This gist has been moved to EZmomi: | |
https://github.com/snobear/ezmomi | |
Give it a star or fork. Contributions are more than welcome. I'm hoping it will become an easy cli tool for | |
common VMware tasks. | |
(Notes from the original gist start here) | |
Example usage: | |
./clone.py --hostname test01 --template CentOS65 --ips 172.9.9.11 172.12.120.22 --cpus 2 --mem 4 | |
Pip requirements: | |
----------------- | |
ecdsa==0.10 | |
netaddr==0.7.10 | |
pycrypto==2.6.1 | |
pyvmomi==5.5.0 | |
wsgiref==0.1.2 | |
""" | |
from pyVim.connect import SmartConnect, Disconnect | |
from pyVmomi import vim, vmodl | |
import atexit | |
import os | |
import sys | |
from pprint import pprint, pformat | |
import time | |
from netaddr import IPNetwork, IPAddress | |
import argparse | |
import getpass | |
from settings import * | |
from copy import deepcopy | |
""" | |
Send an email | |
""" | |
def send_email(deploy_settings, ip_settings, output): | |
# Import smtplib for the actual sending function | |
import smtplib | |
from email.mime.text import MIMEText | |
me = os.getenv("SUDO_USER") # who ran sudo | |
if me is None: | |
me = os.getenv("USER") # will always be root when this scripts is run as sudo | |
email_body = "%s" % output | |
msg = MIMEText(email_body) | |
msg['Subject'] = "%s - VM deploy complete" % deploy_settings["new_vm_name"] | |
msg['From'] = deploy_settings["mailfrom"] | |
msg['To'] = me | |
s = smtplib.SMTP('localhost') | |
s.sendmail(deploy_settings["mailfrom"] , [me], msg.as_string()) | |
s.quit() | |
""" | |
Waits and provides updates on a vSphere task | |
""" | |
def WaitTask(task, actionName='job', hideResult=False): | |
#print 'Waiting for %s to complete.' % actionName | |
while task.info.state == vim.TaskInfo.State.running: | |
time.sleep(2) | |
if task.info.state == vim.TaskInfo.State.success: | |
if task.info.result is not None and not hideResult: | |
out = '%s completed successfully, result: %s' % (actionName, task.info.result) | |
else: | |
out = '%s completed successfully.' % actionName | |
else: | |
out = '%s did not complete successfully: %s' % (actionName, task.info.error) | |
print out | |
raise task.info.error # should be a Fault... check XXX | |
# may not always be applicable, but can't hurt. | |
return task.info.result | |
""" | |
Get the vsphere object associated with a given text name | |
""" | |
def get_obj(content, vimtype, name): | |
obj = None | |
container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True) | |
for c in container.view: | |
if c.name == name: | |
obj = c | |
break | |
return obj | |
""" | |
Connect to vCenter server and deploy a VM from template | |
""" | |
def clone(deploy_settings, ip_settings): | |
fqdn = "%s.%s" % (deploy_settings["new_vm_name"], ip_settings[0]["domain"]) | |
# connect to vCenter server | |
try: | |
si = SmartConnect(host=deploy_settings["vserver"], user=deploy_settings["username"], pwd=deploy_settings["password"], port=int(deploy_settings["port"])) | |
except IOError, e: | |
sys.exit("Unable to connect to vsphere server. Error message: %s" % e) | |
# add a clean up routine | |
atexit.register(Disconnect, si) | |
content = si.RetrieveContent() | |
# get the vSphere objects associated with the human-friendly labels we supply | |
datacenter = get_obj(content, [vim.Datacenter], ip_settings[0]["datacenter_name"]) | |
# get the folder where VMs are kept for this datacenter | |
destfolder = datacenter.vmFolder | |
cluster = get_obj(content, [vim.ClusterComputeResource], ip_settings[0]["cluster_name"]) | |
resource_pool = cluster.resourcePool # use same root resource pool that my desired cluster uses | |
datastore = get_obj(content, [vim.Datastore], ip_settings[0]["datastore_name"]) | |
template_vm = get_obj(content, [vim.VirtualMachine], deploy_settings["template_name"]) | |
# Relocation spec | |
relospec = vim.vm.RelocateSpec() | |
relospec.datastore = datastore | |
relospec.pool = resource_pool | |
''' | |
Networking config for VM and guest OS | |
''' | |
devices = [] | |
adaptermaps = [] | |
# create a Network device for each static IP | |
for key, ip in enumerate(ip_settings): | |
# VM device | |
nic = vim.vm.device.VirtualDeviceSpec() | |
nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.add # or edit if a device exists | |
nic.device = vim.vm.device.VirtualVmxnet3() | |
nic.device.wakeOnLanEnabled = True | |
nic.device.addressType = 'assigned' | |
nic.device.key = 4000 # 4000 seems to be the value to use for a vmxnet3 device | |
nic.device.deviceInfo = vim.Description() | |
nic.device.deviceInfo.label = "Network Adapter %s" % (key + 1 ) | |
nic.device.deviceInfo.summary = ip_settings[key]["network_name"] | |
nic.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() | |
nic.device.backing.network = get_obj(content, [vim.Network], ip_settings[key]["network_name"]) | |
nic.device.backing.deviceName = ip_settings[key]["network_name"] | |
nic.device.backing.useAutoDetect = False | |
nic.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo() | |
nic.device.connectable.startConnected = True | |
nic.device.connectable.allowGuestControl = True | |
devices.append(nic) | |
# guest NIC settings, i.e. "adapter map" | |
guest_map = vim.vm.customization.AdapterMapping() | |
guest_map.adapter = vim.vm.customization.IPSettings() | |
guest_map.adapter.ip = vim.vm.customization.FixedIp() | |
guest_map.adapter.ip.ipAddress = str(ip_settings[key]["ip"]) | |
guest_map.adapter.subnetMask = str(ip_settings[key]["subnet_mask"]) | |
# these may not be set for certain IPs, e.g. storage IPs | |
try: | |
guest_map.adapter.gateway = ip_settings[key]["gateway"] | |
except: | |
pass | |
try: | |
guest_map.adapter.dnsDomain = ip_settings[key]["domain"] | |
except: | |
pass | |
adaptermaps.append(guest_map) | |
# VM config spec | |
vmconf = vim.vm.ConfigSpec() | |
vmconf.numCPUs = deploy_settings['cpus'] | |
vmconf.memoryMB = deploy_settings['mem'] | |
vmconf.cpuHotAddEnabled = True | |
vmconf.memoryHotAddEnabled = True | |
vmconf.deviceChange = devices | |
# DNS settings | |
globalip = vim.vm.customization.GlobalIPSettings() | |
globalip.dnsServerList = deploy_settings['dns_servers'] | |
globalip.dnsSuffixList = ip_settings[0]['domain'] | |
# Hostname settings | |
ident = vim.vm.customization.LinuxPrep() | |
ident.domain = ip_settings[0]['domain'] | |
ident.hostName = vim.vm.customization.FixedName() | |
ident.hostName.name = deploy_settings["new_vm_name"] | |
customspec = vim.vm.customization.Specification() | |
customspec.nicSettingMap = adaptermaps | |
customspec.globalIPSettings = globalip | |
customspec.identity = ident | |
# Clone spec | |
clonespec = vim.vm.CloneSpec() | |
clonespec.location = relospec | |
clonespec.config = vmconf | |
clonespec.customization = customspec | |
clonespec.powerOn = True | |
clonespec.template = False | |
# fire the clone task | |
task = template_vm.Clone(folder=destfolder, name=deploy_settings["new_vm_name"].title(), spec=clonespec) | |
result = WaitTask(task, 'VM clone task') | |
# send notification | |
send_email(deploy_settings, ip_settings, output) | |
def main(**kwargs): | |
deploy_settings["new_vm_name"] = kwargs['hostname'].lower() | |
deploy_settings['cpus'] = kwargs['cpus'] | |
deploy_settings['mem'] = kwargs['mem'] * 1024 | |
# initialize a list to hold our network settings | |
ip_settings = list() | |
''' | |
Get settings for each IP given | |
''' | |
for key, ip_string in enumerate(kwargs['ips']): | |
# convert ip from string to the "IPAddress" type | |
ip = IPAddress(ip_string) | |
# determine network this IP is in | |
for network_address in net: | |
if ip in network_address: | |
net[network_address]["ip"] = ip | |
ip_settings.append(net[network_address]) | |
# throw an error if we couldn't find a network for this ip | |
if not any(d['ip'] == ip for d in ip_settings): | |
sys.exit("ERROR: I don't know what network %s is in. Please add settings for this network." % ip_string) | |
# our default domain | |
if 'domain' not in ip_settings[0]: | |
ip_settings[0]['domain'] = 'example.com' | |
# what VM template to use | |
deploy_settings['template_name'] = kwargs['template'] | |
# clone template to a new VM with our specified settings | |
clone(deploy_settings, ip_settings) | |
""" | |
Main program | |
""" | |
if __name__ == "__main__": | |
if getpass.getuser() != 'root': | |
sys.exit("You must be root to run this. Quitting.") | |
# Define command line arguments | |
parser = argparse.ArgumentParser(description='Deploy a new VM in vSphere') | |
parser.add_argument('--template', type=str, help='VMware template to clone', default='CentOS65') | |
parser.add_argument('--hostname', type=str, required=True, help='New host name',) | |
parser.add_argument('--ips', type=str, help='Static IPs of new host, separated by a space', nargs='+', required=True) | |
parser.add_argument('--cpus', type=int, help='Number of CPUs', default=1) | |
parser.add_argument('--mem', type=int, help='Memory in GB', default=3) | |
# Parse arguments and hand off to main() | |
args = parser.parse_args() | |
main(**vars(args)) | |
""" | |
Network, VMware, and general settings for deploying a new Linux VM | |
""" | |
from netaddr import IPNetwork, IPAddress | |
""" | |
General settings | |
""" | |
deploy_settings = dict() | |
deploy_settings["dns_servers"] = ['8.8.8.8','8.8.4.4'] | |
deploy_settings["vserver"] = "vcenter.example.com" | |
deploy_settings["port"] = 443 | |
deploy_settings["username"] = "admin" | |
deploy_settings["password"] = "password" | |
deploy_settings["mailfrom"] = '[email protected]' | |
""" | |
Networks | |
""" | |
# define settings for each of our networks | |
net = dict() | |
internal_omaha = IPNetwork("172.9.9.0/24") | |
net[internal_omaha] = dict() | |
net[internal_omaha]["datacenter_name"] = 'Omaha' | |
net[internal_omaha]["cluster_name"] = 'Omaha Server Cluster' | |
net[internal_omaha]["datastore_name"] = 'Omaha datastore' | |
net[internal_omaha]["network_name"] = 'Omaha Internal 1' | |
net[internal_omaha]["gateway"] = '172.9.9.1' | |
net[internal_omaha]["subnet_mask"] = str(internal_omaha.netmask) | |
routable_omaha = IPNetwork("123.456.78.90/25") | |
net[routable_omaha] = dict() | |
net[routable_omaha]["datacenter_name"] = 'Omaha' | |
net[routable_omaha]["cluster_name"] = 'Omaha Server Cluster' | |
net[routable_omaha]["datastore_name"] = 'Omaha datastore' | |
net[routable_omaha]["network_name"] = 'Omaha Routable 1' | |
net[routable_omaha]["gateway"] = '123.456.78.91' | |
net[routable_omaha]["subnet_mask"] = str(routable_omaha.netmask) | |
''' | |
Storage networks | |
''' | |
internal_storage = IPNetwork("172.12.120.1/24") | |
net[internal_storage] = dict() | |
net[internal_storage]["network_name"] = '172.12.120.x storage net' | |
net[internal_storage]["subnet_mask"] = str(internal_storage.netmask) |
@rreubenur - do you have a NIC already present in your template? You may need an existing NIC in order to do Operation.edit on it.
With that said, I just updated the script to allow for multiple NICs. If you go with this newer version, you'll want to convert your template to a VM, delete all NICs, and then convert the VM back to a template.
If you still have issues, let me know the exact exception or error messages you're getting and I'll try to help.
Ah ! I'm a dumb a%@ . I was not initializing vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() .
Any how great job and Thank you very much for the updated code !
@snobear - have you ever faced with situation when you need to attach 2 networks during VM creation: one without DHCP (like in your example) and one with DHCP (just create device without AdapterMapping)?
I will be really appreciate you if you can help.
@AlexRaskosov - No I have not tried to do DHCP yet with pyvmomi. However, I'm about to work on adding that and will let you know.
Seeing there has been a good bit of interest in this gist, I'm going to create a github repository to start building this script out a little more into a useful tool to abstract pyvmomi and make common vmware tasks easier. I'd love some contributors to help! Will keep you posted on that.
I moved this script to its own repo
https://github.com/snobear/ezmomi
Please lend a hand in making this an easy cli tool using pyvmomi for common vmware tasks.
Hello,
I have a problem when I use this method
device = template_vm.config.hardware.device
Do you have an idea to help me?
raceback (most recent call last):
File "vsphere.py", line 460, in
opt_parser(opt)
File "vsphere.py", line 423, in opt_parser
vm_parser(opt)
File "vsphere.py", line 238, in vm_parser
vm_create(opt)
File "vsphere.py", line 157, in vm_create
device = template_vm.config.hardware.device
File "/Library/Python/2.7/site-packages/pyVmomi/VmomiSupport.py", line 563, in call
return self.f(args, *kwargs)
File "/Library/Python/2.7/site-packages/pyVmomi/VmomiSupport.py", line 386, in InvokeAccessor
return self.stub.InvokeAccessor(self, info)
File "/Library/Python/2.7/site-packages/pyVmomi/StubAdapterAccessorImpl.py", line 42, in InvokeAccessor
options=self.pcType.RetrieveOptions(maxObjects=1))
File "/Library/Python/2.7/site-packages/pyVmomi/VmomiSupport.py", line 569, in
self.f((self.args + (obj,) + args), _kwargs)
File "/Library/Python/2.7/site-packages/pyVmomi/VmomiSupport.py", line 378, in _InvokeMethod
return self._stub.InvokeMethod(self, info, args)
File "/Library/Python/2.7/site-packages/pyVmomi/SoapAdapter.py", line 1292, in InvokeMethod
raise exc
pyVmomi.SoapAdapter.ParserError: 'xml document: \x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xd5\x1b]s\xe3\xb8\xed\xfd~\x85&O\xed\x83\xad\x0f\xcb\x92\xedju\x93\xd8\xbb\xdd\xccn6\x998\xc9v\xda\xe9th\x89\xb6\xd9H\x94RJ\xdc__P\xd4\xa7-'r.\xb9\x9b\xcedb\x11\x00\t\x10\x04\x01\x10\xa2\x9c\x9f\xc3@\xc9p\xc2HD?\x9d\xe9C\xedL\xc1\xd4\x8b|B7\x9f\xce\xee\xef\xbe\x0c&g\xbf\xba\xbf8,B1\xa6\xd9\xec3\xcdp\x10\xc5X\x81n\x94\xcd$\xd8\xfbt\xb6\xe5<\x9e\xa9\xf3\xb68Dl\x08X\x81\x1aF\xc9F\x15\x0fj9\xa6z\xf6K\xabk\xd6\xa7\xabdYw}f~\xd5\xed\xe9\xe9i\xf84\xca\xa9\rM\xd3\xd5\xbf]}\xe6#5\xa8\xc9\xeb\xd4\x03B\x19G\xd4\xc3g\x8d\xc9^D\xfe\x0e\x9a\xb7\x98'\x04g\xf8&\x01)\x12N0\xfb\xfc|\x8bY\x1cQV\xe8\xe1\xd3Y\x9a\xd0YFBc|\xe6:\t\xe6\xd0\xccP\xe0:\xd1\xea\xdf\xd8\xe3,\x7fP\xf8.\xc6\x9f\xce\x1eH\xc2S\x14!oK(\xb0\xcb\xc2\x81aL\x1c\x15(\'\x06\x16K\xcc]\x87\xa2\x10\xbb^D\xd7d\xe3\xa8y\xc3\x81\x01\x15\x98\xcb\xack\x98yNyI\xd7\x11\xf0\xf7\xb6\x88n\xf0\x83\S\xd7\xd0\xf4\xf1@\xd3\x07\xda\xf4N\xd7f\xe3\xc9ll\x0f\x8d\xb152\xc7\x7fw\xd46\xa9\x13\xc2\x1a\xad\t\xf6]}jky'\xfdN\xd3f\xf9\x1fPWh)\x1e\xc7a\x1c \x8e\x07 \xb5_J\xb9I1\xe3\xd2 \xf8!\x9a\x0b\xbc"\x88_\x7f\xfdq\xaf~'4}Vl\xe5O\x969X\x11\xfegGm\x93:\x85\x11\x82B\x9e\x07\xba\xe6\xa8e\xdbIS\xe2\xbb\xa6\xa1\xfb\xc82G\x03\xcdX\xeb\x03\xcf\xd3F\x83\x11\x9e\x8c\x07\xfa\xd80\xc7\xd8\x1cas\x05J\xccI\x9dr-\xefEk\xac\xe9\xbe?B\xd3\x81o\x1b\xe6\xb2Fh
kSk\xae\xd6\x96\xb1\xd2\xa6So:v\xd4V\x17\x87\xc6$\xbb\x83\xd9E\tJv\x0b\xc2\xd0*\x80Y\xf3$\xc50\xcdN\x9c\x13D\x1e\xe2 \xee%<\xab\xcdF\xa9\xa5\xa2{\xd5\x94\x9a\x02\n?\xd7\x91\xfd/\xcb\xfc\xab\x80\x14z\x11]Q\xc0qB\x818GH-\xa9]@Di\xc4s\x8e\x82\xa0\xd1X\x93\x00\x83\xf5e\xe1\r\xe2\xdb\x9c\xf6\x1f\xb1\xe7\r4md\x18\xda?\x95j\x05\xa5\x0c\x03[=\x80\x0c\xb3\x90?\xc3b\xd4#8\x8c\xa2\x98m#\xbe \t\x18w\x94\xec^\x1b\xd4Q\x0f\xbb8,e\xb0\xc9\xfcS\x06\xd9\xef\x01J\xdf\x9c\xd0\xbfE\xed\xa8\x85nx\x14\x05\xe5O\xb5gt\xd36\'#\xcb\xb4a\xc1\x9a\x08\x07\xadA\xfb7\xd1\x13N\xaei\xb1\xa2-\x90$\x00\xef\x90\x86\xb8\x89/ \xce\n\xaf\xa3D.\xdd\x12\x0c\xce_\xed\n\xaa\x0eD\x8bx\x9br?z\xa2\x1d\xd4%FN\xe1>\xde$\xc8\xc77Q@\xbc\x9d\x1b"\nN\xa2\x98D\x1b\xe5\xb0\x1d\xf5\xeeH\x88\x7f\x12\xbe\xfd\x1a1\xee\xaeQ\xc0
\xec\x03\xb8\x13 \xb0G\xb1A\x82@\xb8\x18\xf00QJaR.\xec\xd2\xf2\x11\xd4\xbbO\xa5\x16\xaa]\x07h\x03?\xbe\xdc+\xe7\x9e\x87\x03\x9cH\x03-Xv\xa1\x1cL\x05\xec{\xb4\xd9@\xdc(\xe6\xdd\x869)\xc3w\x11.G)ZN\x92R!;\xf8\x9e4w\x89%\xfe\x00\x0e\xfe\x8e\x12\xb0\x86;\xf0\xa8n\x02\xac\x91 k\x02\x9d-\xe8\x17%\x82\x19\xa2;G\xad\x9b\x8d]P\xb8\x80R}\xfb\xf0\x8a\xf2{\xe4=\x1e\xd2\x15P\xa1\x9eG\xe1}>\xd3\xd6p\xfb\'+\x1c\x7f\x98\xde3\xb4\xc1.Jy\x14\x82\xca<\xd8\xa4{\xa8\x92\xf6\xf33\xf6$d\x9b\x9d\x03yEY#*!\xa5!\xaf\xd7\x17x\x8b2\x12%n\\\x00j\x89\x0fH \xe2yQ\xe2\xdfb\xd8r\xbb=\xf9\xbbP\xb0\xf7\n\x93\xc0k\x94\x06\xc5\x801\x00Jf\xb9\xf2Y\xb4\x06o\xd8\x02\x95^#o\xc0J\xf8\x95W\x90\xe8\x043\xcc\x1b\x9d\xebv\x9bW\x8bC\x17\xa6$_\x1e\xf0\xeb@\x94\xc4\xb7{\xdc\x0f\xc0\x0e\x93[\xfb\xdc\xcbM\x1c2\x0f\xef1\x8e\x08\x05\xda6fO(\xa1\x1a\xc1\xfd\t%0\x08M\xc3\xf9\xcd\xbd\xabC,\x92O9\x04\xfc\x01\xbb\xc1\xc9R\x98\x13/\x91m\xa0\x13\xe2\x10|\xdf\xd5\x05$\x05&D\xcb\xaaY\x1a\xca\xe5\xfc\xab}u#\x94F+w\xd0\x85*\xe9\x97W\xf3n\xea\x06\x02\x94\x93\x11\x0f\x1f\xa6.\x97\x8b\xcf\x90\xb6\xf0$\n
\xcfC\xda\xf2\x88w \x17\xb8\x14\xf1P\xf4\x92\xdb4@+\x1c\xb8@\xafh\xc2\xcd\x88\x06\x18B\x18B\x00.\xa1eS(\xae\xee\xb8J\xd9\x8f4\IOU7J\xa2Se\xd3_\x94M\xef\x94M\x7f]6\xbd%[!\xdaH\xd3\x8cZ\xceW\x05\xbeY\x1a\x07\x02\x8f^P&\xd0+^\xd5\xa1C\xaf\x87\x04\xa7\xa9\xb8\x10\xd8\x122\xb4\x85w\xed&\xec\xf5\x99\xcd/\x0ff\xa6\xbf4\xb3\xf9\xe5+3; x\xd3\xcc\xc6\x1d3\xd3\xc1~;\xa0]@s;I\x0b\xcb\xcb\xeb\x03-\x98/h\x01\xe8_\xd6\xc2!\xc1\x9b\xb409m\x1a\xdf\xf0n\x15\x81#+f\xbd0\x83\x92T9\x10\xbd\xc4\x1c\x13\xb9\x9e\xd6\xb7r\x17\xb4AN\n\x11\xbe\x9eU\xa3\xd5\xc3\x1c\x85\xbb\x86\x04
\x91\xe3\x8by\xd8/\xd9c\xd1A\x91\xa8Csl\xe3\xff\xa2,\n\xba#\xeb\x81\xbcGA\xfd\x8a\xf2\xff\x85$.\xce\x85r\x98\xe2\x18\xd1l@\xe2$\xd2\x82\x05\xe6\x90\x1c7\xb2\xa9\x06\xd0\xd9B&\xd8f\x91g\x1e~\x8e\x86\xc4\xe8\x10\r\xe6"\xd9\x9f\xb8"\xfa\x89+R\x1c\x81\x1f\x88\x8f\xa3ym[\xe3\x17\xd6$\xa7U\xbcN\xeb\xaaq\xfd\xecK?\xc9\xbe21\xfa-\n\x97\xe4\xbf0\xda\xb7\x0bw\xa2O\r\x116\xf7\xc0"\xa4C\x06)\xb2&V\x04\xf3\xaa\xd9k\xbdd\xa6<Z,\xd3\x18\x8e\xaa\x15\xd5>X\x8c5\x82\xf4\x8c\xfa8\x01\xf9\x1a\xc9d\x1b\xd1\x7f\x19\xae\xe6\x97\xad\xbdQ\xf8\xc4c+\x01\xe4\xc7v\x86\x1cF\x89\xa8\xc2\xb7X)2\x0b%\x94|\x14\xe1\xc6\xc1\x1f\x01\x0eq%N"\xa1B\xa609/\x05\x8eG\x9d\xbd\xbc(\x0ca=\xe4\xf1\\!\xe2\xe8\xb2F\xc7w\x1b\x0b".\x9e\x0e\xa7+\x85\xbb\xf1\xc8E\xca\x96\x05\x15\xcc8\xf6\x88h\x15\xcb=\x82\xa5mC\x80QA|\xa2\x1d\xe9v\xdb\x90\x88\xef\x0e\xc0.H^&\x08\xa2\xa7{\n\xf9\x16O\x88\xc7\xb1?oN\xb2\\\xf9W\xa8^X\xe0\x1b\x94\xa02\x0c\xcd\x97\x9d\xd1\xf8\x85@\x04=^\x8eD\x0fW"\xadUb\xe0R\xae\x96\xe8\xf4AK\xa2[\xda\xbb\xad\xc9\xa8\xbd$\xaf\x84\xcavn\x00\xe7\xa8s\x1f\xf6W\x18ee\xa1\xa0\x05r\x18$\xfc\xd8\x87\xa9\xb84*\x0e\x9d h\x05t\x98\xc7\xc8\x9c\x07\xc9}-\x01\x98H\x07\xf4\xf5\xad;\xf7\x93(\xac\x93F\xe3\xe8b\xce\x17\xea\xe2a\xa1\xf8\t\xc9pG\xbe+D\xe7\xb8\xda\xce\'\x06\xb0\\\n9\xc4\rb\x8co\x93(\xddl\xdf1\x84\xe1g/H\x19\x88^9\xc3\n\xd0\x8eU\x14\xc8\x85\x9b\xcc\xcfk\t\x9fKH\xe3\xe8\xde\x86\xca\r\x98\x17c\x8a\xbdQV~\x0e\xe0\xe5\xe8\xf5X5@0\xe3\xb0\xb2)\x15\xe5f?g#\xda\x15\x8d\x14\xa9m\x8e\xf9\xa1\xe4\xfd\xb2\x1b\x881\x8f\xf5Q\xec\xf8\xae\xfe*B\xa7(MtX\x81n)\xb6m+\x06\xfc~\xbb8\xd9\n\x84\x04_\x02\xc4\x1fpb\xb4\xd7^\xd4\xeb\xdeZ\xc2\xf4\x1fe\xbdO\x1a\x8b\x8f8b\x1c\xce\xc6EI~Q\xb6\xcf\xdc\n5\xd0\xf5)\x88\\6+\x89\xaf\xf3\x8a\xfe\xa5\xef\x9a\x93\x81P\xd1@\xabl\xa7B\xe5\xb5\x9c+\xc8\x8d\xdcX\xd4\r\x19\xc7\xe2\x84_\x01\x1d\x88\xe3\xa4.\xb7\xe5\r\xe7)!\x1c\xdfI\x9b/Q-\x98\xc3!\x80\xdd\x888\'*\x91U9z\x1f*\xab\xe5\x90Xksc\nj\xf2\xd0z\xb0\x9e\xae\xf5\xc1\xc40\xec\xc1\xd4_\xfb\xc86\x8d\xb5\x81\x8d\xb2Z.\xac\x07\x04\x04\xb9\xad\xd5\xc8\x1cO\xec\xb1\xa7\xdb\xfe\xd4\xc2\xba\xe5Y+M7<\xdf\x98"\xcb\x98N\xa5\xa5IZ\x98\xe3\x06\x0c\xfb\xa0Z\xd5\x04\x1e\xcd\x00e\xc4\xe8m\xb6\x1e\x8a\x91G\xf8.\xcf\x8ct\x0b\xac\x0b\x8c\x0b\xfa7\xc1\r\xa2\x8b\x1d\xc7\x904\xd9\xba=\x9dXS}b6I%Rz\xd7\xfaW\nT\x02\x03\x9c\x81A\xd3(\tE\xedT\xb6j\xac\xb0\x07\xb4\xc1\x97\xd7\xe7AY\xe7\x87.$\x84U\x14\x11Y>\xfd\x16\x06\xa2p\x92dr
M\xd6\xb0\xca\xa6p\n\x1d\xdc\x85i\xed\x1b\xa64\xb8\xda&\xdd\xfb\x82(\x8ew\xc5\xee\x9f\xbc\xb4\xfb%\xe5+a\xe0\xe4\x9d/G\x95\x9d\xdf\xfd\xf8\xf2\xff\xea\xdf\xcd\xf7=\xbd>\x84\xcf\x14\xf3Q]@8\xbe\xc8?0\x7f\x8a\x92G\x05\xf9(\x86,\xb9c\x9d\x17\x0f\xcb'\xc2\xbd\xedL\x19\x9b\x8a\x06\x96\xe0+cM\xb1\x90\xe2y\x8ai\x03d0\x1a\xc1AJ\xd1\xe1OS\xec\xb1\xb2\xf2\x95\xc9T\xf1W'\x9b\xc6gH\xe5\x13\x10\x1c\xef @\x80\xfeV)\xa8\xb4:\xf9&\xbcm_\xf2h\xc3r\xe9\xe4K\xc0\x93\x05\xac\xfb\xe6\xa3m\xc0\t\xc7B\xfbV5\x07\xb6.\xca\xd3\rdN\x9a\x87\xe5\xf1T\xa2rh\xb1\xbe\xb0U\xe7Q\xf4H\xb0\xab\x8f,c26M\xabZ\xfb\x1a'\xfb\xf5\xb5Xi\x88\x7f\xa8\xc1\xfe\xd6\x84|\xfa\x87\xa4\xbd3\x12\xf2}p\x9e,/\xbdCFI6T\xcc\xa2\tu\xe0\x.\xdb\xae\xa6\xcd\xc6\xf0g\xcd\xa6\xfeL\x1b\xcd, m\x9d\'\xf4\x88\xaf\xe9wD\xcb\xe8&Uz\x00nT\xc3\xea\x92\xbd\x17\xa7M\x7f\xfd\x92\x87\xc7\xcf1\xa2\xbe\x18\xeb\xb6\x01\xae2\xd6.\xe4\xbbF\x1fuOV\xf9\x92\xe0\x8f\x94^\xbc\xac\xe8/\xfe\xa1\xbc"\x19\xa4\xden\x89)#\x9cd\x90\x06\x1c\x1b\xa4\x8bR\x8e\xf75?\x9b\xede;](\xb1\xd4\x9d\xc4\x07\xf0\x82R\x9e\xf7:\x89\xdb(\xd0-O\x90\xbc\xe9!\xfd\xb7\xb82\xe3\x0f\x81vx(x\xe1\xd73\x14\xa4\xcd8\xf0\xcc\xfc\x99\xf0\x9ftsV\xcd>\xa7q\xc5\xf24\xc6?
\x96\xbf\xcc\x1d\xe6w\x13\x86>f\x8f<\x8a\x87\xa2\x04z~\xec\xc1\xec\xcb\xf9\xf7\xe5\xe7\xbe\xbch\x96\xa0\xb0\xc7\xa0\x87\xa9~\xd1\xb3\x1f\x1b\xf0:\x17\t\xf17X\x1b\xc6\xf2mU\x0f\x9ew\xb7\xf7\xbd\xe7\xc1\xb2\r\xfa\xa0\xa1+\xd9\xcd\x8fg\x90U\xee\xbc\x07\x0f\xe8\x87o#\xf1\x968\xe1o\xe0\xb5Ni\x1e\rY\x0fV\x93\x93\xc7\x1f\x7f\xb4\xb2\xc6\xbf\xa3\xb2\xc6\x1f\xac,\xeb\xa3\x95e\xfd\x8e\xca\xb2>XY\xf6G+\xcb\xfe\x1d\x95e\x7f\x8c\xb2\xb61\xe6\xa7\xb8Z\x99a\xf5\x1b\xbbP\xce\xd7\x9fCq\x1b1\xf5 \xc3\rcH\x00V$\xe8\x17\r\xc5\x1b,\x91\x1e\xf6\xf4\xecy\xe8\x15\xd5Vm\xa6\r\x8bZe\x9c\xc2\t%\xee\xc1+\x12\xd7iz\xce+\xf4\xc8pM\xc4\xa5\xbf\xa1|y\xf3\xce\x8a\xc3\xc5\xd9\n\x16\xa6\x99\x84\xf7\xe0"\xdf\x9d\x1ciO\xe5b\xbf%$\x9e\xc8\xc4\xd0\xdf\x12JNer\xba\xba\xac\xd3\x99\x8c\xde\xe2\xbaNeb\xf6e\x92\xe4\x17\xbf\x86\xc5k9\xb1\xbf^\x1d\xbc\xbcI\xd4s\x1f\xc2\x0e<\xdd\xa6\xc4K\xa0S\x180\xc4~\xfe\xbc\xf4\x18z\xac)\xdaX\xfc\x892\xc3ZA\x13\x05\xfb\x8a\xd6\x9f]\xb4\xae.\xa2\xbds$\x11\x9e\xe4tU\x8dz\x1bm\x16F"b\x0c\xeb[m_.\xc4\xfb\xec>\x11d4\x99XZ\xef8\x92\x85\xe2T=\x94'\x92\xfc\x1d.EAq]\xbc\xd7f\xafo\xf7\xbe\x81a\x82\xff\x93\x92\x04\xfb\xfd\x19NG\xe3\xde\x1b&$\x9b\x04N4C\x11\x8e\xbeG\x9b%G\xbc\x8f\x06iD{\xef\x99\x92\x85\xfc\xcd\xaf\xa9\xf7\xe0p\xcc\x84\xabW$\xf7IP|\x18P\xbf\x9f)\xbf\nH\x01\xa7f\xe1\x9a\xa9Y\x14\xa4!f_^\x8d\x91\xaf\xafW\x03O\x9bh62\xc0\xda\x05Q\xe3\x95K>\x1e{B\xf1M\x80<\x1c\x8a\xbb\x84\x84B\xac"\xe2\xb6d\x0b\xec\xac \xd1\xb9\x8e\xf3|E6\x16\x18\xbcN\xfe\x1a\xb6j8X\x18\xca\xc5\xe5\xf5r\x89y\x1a\xd7\x17"Z\xd0\xbc\xb7\xf8\xe0c\xff\x1e\xeb\x01\xbc\xa6\x94\xe3\x8b\x1a\x8b\xd6\xa0+\xb8\xaa-\xd9\xb2s\xc6p\xc2\xd9\xde\xe0\xfb\xe0\xe2\x13\x8e\xbbD\x96\x00\xf7\x8b\x03\x9dHgM\x92\xdcL\xdd\x15\x89\x98x\xddU4\x9d\x10=_=\xb2yUhd\xae\xa9\x89\xda\xd6>T~\x91 \xaa\xe6\xe2N\xf2\x1e\xd3N\Q\x1ciTs\xe4m\xe6\xbb\xe8\n=\xb7\xab$Gh\x1cB\t'(\xb8\x86\xdd\xb4\xc5\xc8\xaf\x00W\xfb\xbd\xdd\x98\xe8\x96=\x1dY\xe2k\x8d#4e\xef%\xd8G\x13n\x98\x869\xd5G"a9BQ!jI(\x16\x19\xe1\xd7\x87=M\xec\x83\x9d\xec\xe6j\xbe\xbf\xa0\r\x90\x93\xe7\x8a)<~-\xea\x81\xc5E|Y\xe0L[\xb7\xf2)\xce\x84[N\xf7\xae\xea\x1f\x19a)\x0b\xb2r\xf3\xbfBt\x9c@\xca\x91}\t\x10\xdb\xce\x11\x10\xdd\xb6\x0b|G0\xb9?\x10\xe5\xea\xf2\x9b%\xb5\xfa\xdaIm|\x02\xa5\xbe\xf4\xf9\x94\xfb\x8b\xa3\xee}m\xa5\xee\x7fj\xe6\xfe\x0f\x06\xf0\xa4$\xa46\x00\x00 parse error at: line:1, col:0'
Thanks.
Regards
This is a slick script. any chance you could add functionality that would allow you to clone more than 1 VM at a time? e.g. So if you needed 10 vm's test-vm1 through test-vm10 starting with IP: 10.0.0.10 and ending at 10.0.0.20
command would look something like:
ezmomi clone --template my_template --hostname test-vm -num10 --cpus 2 --mem 4 --ip's 10.0.0.10, 10.0.0.19
Thanks.
I'm looking for a way to deploy from template without setting the IP address, the template is set to DHCP from an adapter it already has.
@notsure44 hi , i want to deploy a vm from template too, with dhcp , can you share your thoughts. thanks
Hi ,
I am facing a problem while setting network for multiple NIC . Exactly when I am doing nicspec.device.backing.network = net_obj . I am facing exception. Any clues ????