#!/usr/bin/python3 # encoding:utf-8 """ created by jd 2018-03-05 """ import io import os import sys import atexit #import requests import ssl from pyVmomi import vim from pyVmomi import vmodl from pyVim import connect from pyVim.connect import SmartConnect from pyVim.task import WaitForTask from pysphere import VIServer import importlib server = VIServer() ssl._create_default_https_context = ssl._create_unverified_context def get_physical_cdrom(host): for lun in host.configManager.storageSystem.storageDeviceInfo.scsiLun: if lun.lunType == 'cdrom': return lun return None def find_free_ide_controller(vm): for dev in vm.config.hardware.device: if isinstance(dev, vim.vm.device.VirtualIDEController): # If there are less than 2 devices attached, we can use it. if len(dev.device) < 2: return dev return None def find_device(vm, device_type): result = [] for dev in vm.config.hardware.device: if isinstance(dev, device_type): result.append(dev) return result def new_cdrom_spec(controller_key, backing): connectable = vim.vm.device.VirtualDevice.ConnectInfo() connectable.allowGuestControl = True connectable.startConnected = True cdrom = vim.vm.device.VirtualCdrom() cdrom.controllerKey = controller_key cdrom.key = -1 cdrom.connectable = connectable cdrom.backing = backing return cdrom def upload_file_to_datastore(self_host,self_user,self_pwd,self_datastore,self_remote_file,self_local_file): """从本地上传文件到datastore指定目录 :param self_host:ESXI IP地址 :param self_user:用户名 :param self_pwd:用户密码 :param self_datastore:ESXI datastore名称 :param self_remote_file:远程文件名称 :param self_local_file:本地文件名称 :return:None | upload_file_to_datastore | self_host | self_user | self_pwd | self_datastore | self_remote_file | self_local_file | """ try: service_instance = None sslContext = None verify_cert = None sslContext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) sslContext.verify_mode = ssl.CERT_NONE verify_cert = False # disable urllib3 warnings if hasattr(requests.packages.urllib3, 'disable_warnings'): requests.packages.urllib3.disable_warnings() try: service_instance = connect.SmartConnect(host=self_host, user=self_user, pwd=self_pwd, port=443, sslContext=sslContext) except IOError as e: pass if not service_instance: print("Could not connect to the specified host using specified " "username and password") raise SystemExit(-1) # Ensure that we cleanly disconnect in case our code dies atexit.register(connect.Disconnect, service_instance) content = service_instance.RetrieveContent() session_manager = content.sessionManager # Get the list of all datacenters we have available to us datacenters_object_view = content.viewManager.CreateContainerView( content.rootFolder, [vim.Datacenter], True) # Find the datastore and datacenter we are using datacenter = None datastore = None for dc in datacenters_object_view.view: datastores_object_view = content.viewManager.CreateContainerView( dc, [vim.Datastore], True) for ds in datastores_object_view.view: if ds.info.name == self_datastore: datacenter = dc datastore = ds if not datacenter or not datastore: print("Could not find the datastore specified") raise SystemExit(-1) # Clean up the views now that we have what we need datastores_object_view.Destroy() datacenters_object_view.Destroy() # Build the url to put the file - https://hostname:port/resource?params if not self_remote_file.startswith("/"): remote_file = "/" + self_remote_file else: remote_file = self_remote_file resource = "/folder" + remote_file params = {"dsName": datastore.info.name, "dcPath": datacenter.name} http_url = "https://" + self_host + ":443" + resource # Get the cookie built from the current session client_cookie = service_instance._stub.cookie # Break apart the cookie into it's component parts - This is more than # is needed, but a good example of how to break apart the cookie # anyways. The verbosity makes it clear what is happening. cookie_name = client_cookie.split("=", 1)[0] cookie_value = client_cookie.split("=", 1)[1].split(";", 1)[0] cookie_path = client_cookie.split("=", 1)[1].split(";", 1)[1].split( ";", 1)[0].lstrip() cookie_text = " " + cookie_value + "; $" + cookie_path # Make a cookie cookie = dict() cookie[cookie_name] = cookie_text # Get the request headers set up headers = {'Content-Type': 'application/octet-stream'} # Get the file to upload ready, extra protection by using with against # leaving open threads with open(self_local_file, "rb") as f: # Connect and upload the file request = requests.put(http_url, params=params, data=f, headers=headers, cookies=cookie, verify=verify_cert) except vmodl.MethodFault as e: print(("Caught vmodl fault : " + e.msg)) raise SystemExit(-1) def sphere_login(host, user, password): """通过用户名,密码登录指定的ESXi :param host:ESXi的host ip :param user:ESXi的用户名 :param password:ESXi的密码 :return:None | sphere login | host | user | password | """ server.connect(host, user, password, trace_file="debug.txt") if(server.is_connected()): print('exsi is connected.') return True else: print('error:exsi server can not connected') return False pass def vm_poweron(path): """通过虚拟机路径打开对应虚拟机 :param path:虚拟机配置文件所在路径 :return:None | vm poweron | path | """ vm = server.get_vm_by_path(path) if vm.get_status() == 'POWERED OFF': task = vm.power_on(sync_run=False) task.wait_for_state(['error', 'success'], timeout=60) if task.get_state() == 'error': print(task.get_error_message()) pass def vm_poweron_name(name): """通过虚拟机名称打开对应虚拟机 :参数为虚拟机名称 :return:None | vm poweron | name | """ vm = server.get_vm_by_name(name) if vm.get_status() == 'POWERED OFF': task = vm.power_on(sync_run=False) task.wait_for_state(['error', 'success'], timeout=60) if task.get_state() == 'error': print(task.get_error_message()) pass def vm_poweroff(path): """通过虚拟机路径关闭对应虚拟机 :param path:虚拟机配置文件所在路径 :return:None | vm poweroff | path | """ vm = server.get_vm_by_path(path) if vm.get_status() != 'POWERED OFF': task = vm.power_off(sync_run=False) task.wait_for_state(['error', 'success'], timeout=60) if task.get_state() == 'error': print(task.get_error_message()) pass def vm_poweroff_name(name): """通过虚拟机名称关闭对应虚拟机 :参数为虚拟机名称 :return:None | vm poweroff | name | """ vm = server.get_vm_by_name(name) if vm.get_status() != 'POWERED OFF': task = vm.power_off(sync_run=False) task.wait_for_state(['error', 'success'], timeout=60) if task.get_state() == 'error': print(task.get_error_message()) pass def vm_shutdown_name(name): """通过虚拟机名称注销用户并关闭对应虚拟机 :参数为虚拟机名称 :return:None | vm poweroff | name | """ vm = server.get_vm_by_name(name) if vm.get_status() != 'POWERED OFF': vm.shutdown_guest() pass else: print('VM has been poweroff.') def vm_revert_snapshot(path, snapshot_name): """通过快照名恢复虚拟机路径指定的虚拟机 :param path:虚拟机配置文件所在路径 :param snapshot_name:快照名 :return:None | vm revert snapshot | path | snapshot_name | """ vm = server.get_vm_by_path(path) snapshot_list = vm.get_snapshots() for snapshot in snapshot_list: print(snapshot.get_name()) print(snapshot.get_description()) if snapshot_name in snapshot.get_name(): task = vm.revert_to_named_snapshot(snapshot.get_name(), sync_run=False) print('revert snapshot %s' % snapshot.get_name()) task.wait_for_state(['error', 'success'], timeout=60) if task.get_state() == 'error': print(task.get_error_message()) break pass def download_file(name, guest_name, guest_password, filepath, dirpath): #从虚拟机上拷贝文件到本机指定位置下(虚拟机必须安装vmtools) #name:虚拟机名字 #guest_name:虚拟机用户名 #guest_password:用户密码 #filepath:虚拟机上文件路径 #dirpath:本机目标文件路径 #| vm poweroff | name | guest_name | guest_password | filepath | dirpath | #return:none,失败弹出原因 vm = server.get_vm_by_name(name) vm.login_in_guest(guest_name, guest_password) if vm.get_status() == 'POWERED ON': vm.get_file(filepath, dirpath, True) def sphere_logoff(): """登出指定的ESXi :return:None | sphere logoff | """ if server.is_connected(): server.disconnect() pass def upload_file(system_type, name, guest_name, guest_password, filepath, dirpath): """从虚拟机指定位置下运行程序(虚拟机必须安装vmtools) :param system_type:操作系统类型 :param name:虚拟机名字 :param guest_name:虚拟机用户名 :param guest_password:用户密码 :param filepath:虚拟机上文件路径 :param dirpath:本机目标文件路径 :return:None | upload file | system_type | name | guest_name | guest_password | filepath | dirpath | """ if system_type == "windows": importlib.reload(sys) sys.setdefaultencoding('gbk') vm = server.get_vm_by_name(name) vm.login_in_guest(guest_name, guest_password) if vm.get_status() == 'POWERED ON': vm.send_file(filepath, dirpath, True) def run_process(name, guest_name, guest_password, program_path, program_args, program_cwd): """从虚拟机指定位置下运行程序(虚拟机必须安装vmtools) :param name:虚拟机名字 :param guest_name:虚拟机用户名 :param guest_password:用户密码 :param program_path:程序的绝对地址 :param program_args:程序的参数列表 :param program_cwd:程序的工作目录 :return:None | run process | name | guest_name | guest_password | program_path | program_args | program_cwd | """ vm = server.get_vm_by_name(name) vm.login_in_guest(guest_name, guest_password) if vm.get_status() == 'POWERED ON': vm.start_process(program_path,args=[program_args],env="",cwd=program_cwd) def rm_cdrom(host, user, password, name): """卸载指定虚拟机ISO :param host:EXSI IP :param user:EXSI用户名 :param password:EXSI密码 :param name:虚拟机名字 :return:None | rm cdrom | host | user | password | name | """ sslContext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) sslContext.verify_mode = ssl.CERT_NONE verify_cert = False # disable urllib3 warnings if hasattr(requests.packages.urllib3, 'disable_warnings'): requests.packages.urllib3.disable_warnings() try: si = connect.SmartConnect(host=host, user=user,pwd=password,port=443,sslContext=sslContext) except IOError as e: pass #si = SmartConnect(host=args.host, user=args.user, pwd=args.password) dc = si.content.rootFolder.childEntity[0] vm = si.content.searchIndex.FindChild(dc.vmFolder, name) if vm is None: raise Exception('Failed to find VM %s in datacenter %s' % (dc.name, name)) controller = find_free_ide_controller(vm) if controller is None: raise Exception('Failed to find a free slot on the IDE controller') cdrom = None cdrom_lun = get_physical_cdrom(vm.runtime.host) if cdrom_lun is not None: backing = vim.vm.device.VirtualCdrom.AtapiBackingInfo() backing.deviceName = cdrom_lun.deviceName deviceSpec = vim.vm.device.VirtualDeviceSpec() deviceSpec.device = new_cdrom_spec(controller.key, backing) deviceSpec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add configSpec = vim.vm.ConfigSpec(deviceChange=[deviceSpec]) WaitForTask(vm.Reconfigure(configSpec)) cdroms = find_device(vm, vim.vm.device.VirtualCdrom) cdrom = [x for x in cdroms if type(x.backing) == type(backing) and x.backing.deviceName == cdrom_lun.deviceName][0] else: cdroms = find_device(vm, vim.vm.device.VirtualCdrom) if len(cdroms) != 0: cdrom = cdroms[0] print('Skipping physical CD-Rom test as no device present.') op = vim.vm.device.VirtualDeviceSpec.Operation if cdrom is not None: # Remove it deviceSpec = vim.vm.device.VirtualDeviceSpec() deviceSpec.device = cdrom deviceSpec.operation = op.remove configSpec = vim.vm.ConfigSpec(deviceChange=[deviceSpec]) WaitForTask(vm.Reconfigure(configSpec))