Last active
October 17, 2023 06:39
-
-
Save snobear/8788977 to your computer and use it in GitHub Desktop.
Clone VM from template with pyVmomi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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)) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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"] = 'root@example.com' | |
""" | |
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) |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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