from pyVim.connect import SmartConnectNoSSL, Disconnect
from pyVmomi import vim
from datetime import timedelta
import atexit
from optparse import OptionParser
from requests.auth import HTTPBasicAuth
import requests
import json
import traceback
import schedule
import copy
esxi_host = None
class EsxiHost:
_si = None
_username: str = None
_password: str = None
_vc_ip: str = None
_vc_port: str = None
_perf_dict: dict = {}
def __init__(self, username: str, password: str, vc_ip: str,
vc_port: str) -> None:
self._username = username
self._password = password
self._vc_ip = vc_ip
self._vc_port = vc_port
def _connect_to_server(self):
try:
self._si = SmartConnectNoSSL(host=self._vc_ip,
user=self._username,
pwd=self._password,
port=self._vc_port)
atexit.register(Disconnect, self._si)
return self._si
except Exception as e:
print(e)
self._si = None
def _si_check_wrapper(func):
def wrapper(self, *args, **kwargs):
if self._si is None:
self._connect_to_server()
return func(self, *args, **kwargs)
return wrapper
@_si_check_wrapper
def get_content(self):
return self._si.RetrieveContent()
@_si_check_wrapper
def get_esxi_time(self):
return self._si.CurrentTime()
@_si_check_wrapper
def get_retrieve_content(self):
return self._si.RetrieveContent()
@_si_check_wrapper
def get_perf_dict(self) -> dict:
self._perf_dict.clear()
content = self.get_retrieve_content()
perf_list = content.perfManager.perfCounter
for counter in perf_list:
counter_full = "{}.{}.{}".format(counter.groupInfo.key,
counter.nameInfo.key,
counter.rollupType)
self._perf_dict[counter_full] = counter.key
return self._perf_dict
@_si_check_wrapper
def get_esxi_host_obj(self):
content = self.get_retrieve_content()
return content.viewManager.CreateContainerView(content.rootFolder,
[vim.HostSystem], True)
def is_alive(self) -> bool:
if self._si is None:
return False
try:
self.get_esxi_time()
except Exception as e:
self._si = None
return False
return True
class EsxiHostUtils:
statistics_interval_time: int = 20
def __init__(self, content, vc_time, perf_dict, host_obj) -> None:
self.content = content
self.vc_time = vc_time
self.perf_dict = perf_dict
self.host = host_obj
def get_type_id(self, query_type):
counter_id = self.perf_dict[query_type]
return counter_id
def get_network_tx(self):
stat_network_tx = self.build_query(
query_type="net.transmitted.average")
network_tx = int(round(
sum(stat_network_tx[0].value[0].value) /
self.statistics_interval_time,2)) * 1024
return network_tx * 1024
def get_network_rx(self):
stat_network_rx = self.build_query(query_type="net.received.average")
network_rx = int(round(
sum(stat_network_rx[0].value[0].value) /
self.statistics_interval_time,2)) * 1024
return network_rx * 1024
def get_cpu_usage(self):
state_cpu_usage = self.build_query(query_type="cpu.usage.average")
cpu_usage = float(
round((((sum(state_cpu_usage[0].value[0].value))) / 100 /
self.statistics_interval_time), 1))
return cpu_usage
def get_disk_capacity_and_usage(self, content) -> tuple:
capacity = 0
free_size = 0
for datacenter in content.rootFolder.childEntity:
datastores = datacenter.datastore
for ds in datastores:
capacity += ds.summary.capacity
free_size += ds.summary.freeSpace
capacity = int(round(capacity / 1024.0 / 1024.0, 0))
freesize = int(round(free_size / 1024.0 / 1024.0, 0))
return (capacity, freesize)
def build_query(self, query_type: str):
counter_id = self.get_type_id(query_type)
metric_id = vim.PerformanceManager.MetricId(counterId=counter_id,
instance="")
start_time = self.vc_time - timedelta(
seconds=(self.statistics_interval_time))
end_time = self.vc_time
query = vim.PerformanceManager.QuerySpec(intervalId=20,
entity=self.host,
metricId=[metric_id],
startTime=start_time,
endTime=end_time)
perf_results = self.content.perfManager.QueryPerf(querySpec=[query])
if perf_results:
return perf_results
else:
pass
PostData = {
"frame": "data",
"version": "py",
"gid": '',
"alias": '',
"name": '',
"weight": 0,
"vnstat": False,
"notify": False,
"online4": True,
"oneline6": '',
"uptime": 0,
"load_1": 0,
"load_5": 0,
"load_15": 0,
"memory_total": 0,
"memory_used": 0,
"swap_total": 0,
"swap_used": 0,
"hdd_total": 0,
"hdd_used": 0,
"cpu": 0,
"network_rx": 0,
"network_tx": 0,
"network_in": 0,
"network_out": 0,
"ping_10010": 0,
"ping_189": 0,
"ping_10086": 0,
"time_10010": 0,
"time_189": 0,
"time_10086": 0,
"tcp": 0,
"udp": 0,
"process": 0,
"thread": 0,
}
def gather_data(username: str, password: str, vc_ip: str, vc_port: str,
host_username: str) -> dict:
global esxi_host
if esxi_host is None:
esxi_host = EsxiHost(username, password, vc_ip, vc_port)
post_data = copy.deepcopy(PostData)
host_view = esxi_host.get_esxi_host_obj()
if len(host_view.view) > 1:
raise "暂时不支持集群或vcenter"
host = host_view.view[0]
stats = host.summary.quickStats
hardware = host.hardware
post_data["name"] = host_username
post_data["uptime"] = stats.uptime
post_data["memory_total"] = int(round(hardware.memorySize / 1024, 0))
post_data["memory_used"] = stats.overallMemoryUsage * 1024
esxi_utils = EsxiHostUtils(content=esxi_host.get_content(),
vc_time=esxi_host.get_esxi_time(),
perf_dict=esxi_host.get_perf_dict(),
host_obj=host)
volume_capacity, volume_free_sapce = esxi_utils.get_disk_capacity_and_usage(
content=esxi_host.get_content())
volume_used_space = volume_capacity - volume_free_sapce
post_data["hdd_total"] = volume_capacity
post_data["hdd_used"] = volume_used_space
post_data["cpu"] = esxi_utils.get_cpu_usage()
post_data["network_rx"] = esxi_utils.get_network_rx()
post_data["network_tx"] = esxi_utils.get_network_tx()
return post_data
def report(username: str, password: str, addr: str, data):
try:
sess = requests.Session()
ssr_auth = "single"
http_headers = {"ssr-auth": ssr_auth}
auth = HTTPBasicAuth(username, password)
r = sess.post(addr, auth=auth, json=data, headers=http_headers)
print(r)
except Exception as e:
print(e)
traceback.print_exc()
def run(options):
s_addr = options.addr
s_username = options.username
s_gid = options.gid
s_alias = options.alias
s_password = options.password
esxi_username = options.esxi_username
esxi_password = options.esxi_password
esxi_addr = options.esxi_addr
esxi_port = options.esxi_port
data = gather_data(esxi_username, esxi_password, esxi_addr, esxi_port,
s_username)
report(s_username, s_password, s_addr, data)
if __name__ == "__main__":
usage = """usage: python3 %prog [options] arg
eg:
python3 %prog -a http://127.0.0.1:8080/report -u h1 -p p1 --esxiuser root --esxipasswd password --esxiaddr 192.169.1.2 --esxiport 443
"""
parser = OptionParser(usage)
parser.add_option("-a",
"--addr",
dest="addr",
default="http://10.10.10.100/report",
help="http/tcp addr [default: %default]")
parser.add_option("-u",
"--user",
dest="username",
default="h1",
help="auth user [default: %default]")
parser.add_option("-g",
"--gid",
dest="gid",
default="",
help="group id [default: %default]")
parser.add_option("--alias",
dest="alias",
default="unknown",
help="alias for host [default: %default]")
parser.add_option("-p",
"--pass",
dest="password",
default="password",
help="auth pass [default: %default]")
parser.add_option("-e",
"--esxiuser",
dest="esxi_username",
default="root",
help="esxi username [default: %default]")
parser.add_option("-f",
"--esxipasswd",
dest="esxi_password",
default="password",
help="esxi password [default: %default]")
parser.add_option("-w",
"--esxiaddr",
dest="esxi_addr",
default="192.168.1.2",
help="esxi addr [default: %default]")
parser.add_option("-r",
"--esxiport",
dest="esxi_port",
default="443",
help="esxi port [default: %default]")
(options, args) = parser.parse_args()
print(json.dumps(options.__dict__, indent=2))
schedule.every(5).seconds.do(run, options)
print("running... report every 5 seconds")
while True:
try:
schedule.run_pending()
except KeyboardInterrupt:
exit(0)