412 lines
12 KiB
Python
412 lines
12 KiB
Python
#!/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))
|