317 lines
12 KiB
Python
317 lines
12 KiB
Python
|
|
# coding: utf-8
|
|||
|
|
# -------------------------------------------------------------------
|
|||
|
|
# YakPanel
|
|||
|
|
# -------------------------------------------------------------------
|
|||
|
|
# Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
|||
|
|
# -------------------------------------------------------------------
|
|||
|
|
# Author: wzz <wzz@yakpanel.com>
|
|||
|
|
# -------------------------------------------------------------------
|
|||
|
|
import gettext
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
_ = gettext.gettext
|
|||
|
|
|
|||
|
|
import time
|
|||
|
|
|
|||
|
|
# ------------------------------
|
|||
|
|
# Docker模型
|
|||
|
|
# ------------------------------
|
|||
|
|
import public
|
|||
|
|
from btdockerModelV2 import dk_public as dp
|
|||
|
|
from btdockerModelV2.dockerBase import dockerBase
|
|||
|
|
from public.validate import Param
|
|||
|
|
|
|||
|
|
class main(dockerBase):
|
|||
|
|
__stats_tmp = dict()
|
|||
|
|
__docker = None
|
|||
|
|
|
|||
|
|
def docker_client(self, url):
|
|||
|
|
if not self.__docker:
|
|||
|
|
self.__docker = dp.docker_client(url)
|
|||
|
|
return self.__docker
|
|||
|
|
|
|||
|
|
def io_stats(self, stats, write=None):
|
|||
|
|
drive_io = stats['blkio_stats']['io_service_bytes_recursive']
|
|||
|
|
if drive_io:
|
|||
|
|
if len(drive_io) <= 2:
|
|||
|
|
try:
|
|||
|
|
now = drive_io[0]['value']
|
|||
|
|
self.__stats_tmp['read_total'] = now
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['read_total'] = 0
|
|||
|
|
try:
|
|||
|
|
now = drive_io[1]['value']
|
|||
|
|
self.__stats_tmp['write_total'] = now
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['write_total'] = 0
|
|||
|
|
else:
|
|||
|
|
try:
|
|||
|
|
now = drive_io[0]['value'] + drive_io[2]['value']
|
|||
|
|
self.__stats_tmp['read_total'] = now
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['read_total'] = 0
|
|||
|
|
try:
|
|||
|
|
now = drive_io[1]['value'] + drive_io[3]['value']
|
|||
|
|
self.__stats_tmp['write_total'] = now
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['write_total'] = 0
|
|||
|
|
if write:
|
|||
|
|
self.__stats_tmp['container_id'] = stats['id']
|
|||
|
|
self.write_io(self.__stats_tmp)
|
|||
|
|
|
|||
|
|
def net_stats(self, stats, cache, write=None):
|
|||
|
|
try:
|
|||
|
|
net_io = stats['networks']['eth0']
|
|||
|
|
net_io_old = cache['networks']['eth0']
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['rx_total'] = 0
|
|||
|
|
self.__stats_tmp['rx'] = 0
|
|||
|
|
self.__stats_tmp['tx_total'] = 0
|
|||
|
|
self.__stats_tmp['tx'] = 0
|
|||
|
|
if write:
|
|||
|
|
self.__stats_tmp['container_id'] = stats['id']
|
|||
|
|
self.write_net(self.__stats_tmp)
|
|||
|
|
return
|
|||
|
|
time_now = stats["time"]
|
|||
|
|
time_old = cache["time"]
|
|||
|
|
try:
|
|||
|
|
now = net_io["rx_bytes"]
|
|||
|
|
self.__stats_tmp['rx_total'] = now
|
|||
|
|
old = net_io_old["rx_bytes"]
|
|||
|
|
self.__stats_tmp['rx'] = int((now - old) / (time_now - time_old))
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['rx_total'] = 0
|
|||
|
|
self.__stats_tmp['rx'] = 0
|
|||
|
|
try:
|
|||
|
|
now = net_io["tx_bytes"]
|
|||
|
|
old = net_io_old["tx_bytes"]
|
|||
|
|
self.__stats_tmp['tx_total'] = now
|
|||
|
|
self.__stats_tmp['tx'] = int((now - old) / (time_now - time_old))
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['tx_total'] = 0
|
|||
|
|
self.__stats_tmp['tx'] = 0
|
|||
|
|
if write:
|
|||
|
|
self.__stats_tmp['container_id'] = stats['id']
|
|||
|
|
self.write_net(self.__stats_tmp)
|
|||
|
|
# return data
|
|||
|
|
|
|||
|
|
def mem_stats(self, stats, write=None):
|
|||
|
|
mem = stats['memory_stats']
|
|||
|
|
try:
|
|||
|
|
self.__stats_tmp['limit'] = mem['limit']
|
|||
|
|
self.__stats_tmp['usage_total'] = mem['usage']
|
|||
|
|
if 'cache' not in mem['stats']:
|
|||
|
|
mem['stats']['cache'] = 0
|
|||
|
|
self.__stats_tmp['usage'] = mem['usage'] - mem['stats']['cache']
|
|||
|
|
self.__stats_tmp['cache'] = mem['stats']['cache']
|
|||
|
|
# data['mem_useage'] = round(mem['usage'] * 100 / data['limit'],2)
|
|||
|
|
except:
|
|||
|
|
# return public.get_error_info()
|
|||
|
|
self.__stats_tmp['limit'] = 0
|
|||
|
|
self.__stats_tmp['usage'] = 0
|
|||
|
|
self.__stats_tmp['cache'] = 0
|
|||
|
|
self.__stats_tmp['usage_total'] = 0
|
|||
|
|
# data['mem_useage'] = 0
|
|||
|
|
if write:
|
|||
|
|
self.__stats_tmp['container_id'] = stats['id']
|
|||
|
|
self.write_mem(self.__stats_tmp)
|
|||
|
|
# return data
|
|||
|
|
|
|||
|
|
def cpu_stats(self, stats, write=None):
|
|||
|
|
# cpu_limit = dp.sql('container').where("c_id=?",(stats['id'],)).find()
|
|||
|
|
# if cpu_limit:
|
|||
|
|
# cpu_limit = cpu_limit['cpu_limit']
|
|||
|
|
# else:
|
|||
|
|
# cpu_limit = 1
|
|||
|
|
try:
|
|||
|
|
cpu = stats['cpu_stats']['cpu_usage']['total_usage'] - stats[
|
|||
|
|
'precpu_stats']['cpu_usage']['total_usage']
|
|||
|
|
except:
|
|||
|
|
cpu = 0
|
|||
|
|
try:
|
|||
|
|
system = stats['cpu_stats']['system_cpu_usage'] - stats[
|
|||
|
|
'precpu_stats']['system_cpu_usage']
|
|||
|
|
except:
|
|||
|
|
system = 0
|
|||
|
|
try:
|
|||
|
|
self.__stats_tmp['online_cpus'] = stats['cpu_stats']['online_cpus']
|
|||
|
|
except:
|
|||
|
|
self.__stats_tmp['online_cpus'] = 0
|
|||
|
|
if cpu > 0 and system > 0:
|
|||
|
|
self.__stats_tmp['cpu_usage'] = round(
|
|||
|
|
(cpu / system) * 100 * self.__stats_tmp['online_cpus'], 2)
|
|||
|
|
else:
|
|||
|
|
self.__stats_tmp['cpu_usage'] = 0.0
|
|||
|
|
if write:
|
|||
|
|
self.__stats_tmp['container_id'] = stats['id']
|
|||
|
|
self.write_cpu(self.__stats_tmp)
|
|||
|
|
# return data
|
|||
|
|
|
|||
|
|
def stats(self, args):
|
|||
|
|
"""
|
|||
|
|
获取某个容器的cpu,内存,网络io,磁盘io.
|
|||
|
|
:param url
|
|||
|
|
:param id
|
|||
|
|
:param args:
|
|||
|
|
:return:
|
|||
|
|
"""
|
|||
|
|
# {"id": "d58097084d43324643efde5cc8d30643901c27366d35238801a2119509352ab7", "dk_status": "running"}
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
args.validate([
|
|||
|
|
Param('id').Require().String(),
|
|||
|
|
Param('dk_status').Require().String(),
|
|||
|
|
], [
|
|||
|
|
public.validate.trim_filter(),
|
|||
|
|
])
|
|||
|
|
except Exception as ex:
|
|||
|
|
public.print_log("error info: {}".format(ex))
|
|||
|
|
return public.return_message(-1, 0, ex)
|
|||
|
|
try:
|
|||
|
|
container = self.docker_client(self._url).containers.get(args.id)
|
|||
|
|
stats = container.stats(decode=None, stream=False)
|
|||
|
|
stats['time'] = time.time()
|
|||
|
|
cache = public.cache_get('stats')
|
|||
|
|
if not cache:
|
|||
|
|
cache = stats
|
|||
|
|
public.cache_set('stats', stats)
|
|||
|
|
write = None
|
|||
|
|
if hasattr(args, "write"):
|
|||
|
|
write = args.write
|
|||
|
|
self.__stats_tmp['expired'] = time.time() - (args.save_date * 86400)
|
|||
|
|
stats['id'] = args.id
|
|||
|
|
import json
|
|||
|
|
from pygments import highlight, lexers, formatters
|
|||
|
|
formatted_json = json.dumps(stats, indent=3)
|
|||
|
|
colorful_json = highlight(formatted_json.encode('utf-8'), lexers.JsonLexer(), formatters.TerminalFormatter())
|
|||
|
|
# print(colorful_json)
|
|||
|
|
self.io_stats(stats, write)
|
|||
|
|
self.net_stats(stats, cache, write)
|
|||
|
|
self.cpu_stats(stats, write)
|
|||
|
|
self.mem_stats(stats, write)
|
|||
|
|
public.cache_set('stats', stats)
|
|||
|
|
self.__stats_tmp['detail'] = stats
|
|||
|
|
if 'dk_status' in args and args.dk_status != 'running':
|
|||
|
|
self.__stats_tmp['read_total'] = 0
|
|||
|
|
self.__stats_tmp['write_total'] = 0
|
|||
|
|
return public.return_message(0, 0, self.__stats_tmp)
|
|||
|
|
except Exception as ex:
|
|||
|
|
if "No such container" in str(ex):
|
|||
|
|
return public.return_message(-1, 0, public.lang("The container does not exist, please refresh the browser and try again!"))
|
|||
|
|
return public.return_message(-1, 0, public.lang('Failed to get container status: ' + str(ex)))
|
|||
|
|
|
|||
|
|
def top(self, get):
|
|||
|
|
"""
|
|||
|
|
获取容器内进程信息
|
|||
|
|
@param get:
|
|||
|
|
@return:
|
|||
|
|
"""
|
|||
|
|
container = self.docker_client(self._url).containers.get(get.id)
|
|||
|
|
return public.return_message(0, 0, container.top())
|
|||
|
|
|
|||
|
|
def write_cpu(self, data):
|
|||
|
|
pdata = {
|
|||
|
|
"time": time.time(),
|
|||
|
|
"cpu_usage": data['cpu_usage'],
|
|||
|
|
"online_cpus": data['online_cpus'],
|
|||
|
|
"container_id": data['container_id']
|
|||
|
|
}
|
|||
|
|
dp.sql("cpu_stats").where("time<?", (self.__stats_tmp['expired'],)).delete()
|
|||
|
|
dp.sql("cpu_stats").insert(pdata)
|
|||
|
|
|
|||
|
|
def write_io(self, data):
|
|||
|
|
pdata = {
|
|||
|
|
"time": time.time(),
|
|||
|
|
"write_total": data['write_total'],
|
|||
|
|
"read_total": data['read_total'],
|
|||
|
|
"container_id": data['container_id']
|
|||
|
|
}
|
|||
|
|
dp.sql("io_stats").where("time<?", (self.__stats_tmp['expired'],)).delete()
|
|||
|
|
dp.sql("io_stats").insert(pdata)
|
|||
|
|
|
|||
|
|
def write_net(self, data):
|
|||
|
|
pdata = {
|
|||
|
|
"time": time.time(),
|
|||
|
|
"tx_total": data['tx_total'],
|
|||
|
|
"rx_total": data['rx_total'],
|
|||
|
|
"tx": data['tx'],
|
|||
|
|
"rx": data['rx'],
|
|||
|
|
"container_id": data['container_id']
|
|||
|
|
}
|
|||
|
|
dp.sql("net_stats").where("time<?", (self.__stats_tmp['expired'],)).delete()
|
|||
|
|
dp.sql("net_stats").insert(pdata)
|
|||
|
|
|
|||
|
|
def write_mem(self, data):
|
|||
|
|
pdata = {
|
|||
|
|
"time": time.time(),
|
|||
|
|
"mem_limit": data['limit'],
|
|||
|
|
"cache": data['cache'],
|
|||
|
|
"usage": data['usage'],
|
|||
|
|
"usage_total": data['usage_total'],
|
|||
|
|
"container_id": data['container_id']
|
|||
|
|
}
|
|||
|
|
dp.sql("mem_stats").where("time<?", (self.__stats_tmp['expired'],)).delete()
|
|||
|
|
dp.sql("mem_stats").insert(pdata)
|
|||
|
|
|
|||
|
|
# 获取某服务器容器总数
|
|||
|
|
def get_container_count(self, args):
|
|||
|
|
return public.return_message(0, 0, len(self.docker_client(self._url).containers.list()))
|
|||
|
|
|
|||
|
|
# 获取监控容器资源并记录每分钟
|
|||
|
|
|
|||
|
|
# 获取Docker总览信息
|
|||
|
|
def get_docker_system_info(self, get):
|
|||
|
|
result = {
|
|||
|
|
"containers": {"usage": "-", "total": "-", "size": "-"},
|
|||
|
|
"images": {"usage": "-", "total": "-", "size": "-"},
|
|||
|
|
"volumes": {"usage": "-", "total": "-", "size": "-"},
|
|||
|
|
"networks": {"usage": 0, "total": 0},
|
|||
|
|
"composes": {"usage": 0, "total": 0},
|
|||
|
|
"mirrors": 0,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 获取 mirrors
|
|||
|
|
system_info = self.docker_client(self._url).info()
|
|||
|
|
result["mirrors"] = len(system_info.get("RegistryConfig", {}).get("IndexConfigs", {}))
|
|||
|
|
|
|||
|
|
# 解析 docker system df
|
|||
|
|
dfs, err = public.ExecShell("docker system df --format json")
|
|||
|
|
|
|||
|
|
for line in dfs.strip().split('\n'):
|
|||
|
|
line = line.strip()
|
|||
|
|
if not line:
|
|||
|
|
continue
|
|||
|
|
try:
|
|||
|
|
df = json.loads(line)
|
|||
|
|
df_type = df.get("Type")
|
|||
|
|
if df_type == "Images":
|
|||
|
|
result["images"]["usage"] = df.get("Active", 0)
|
|||
|
|
result["images"]["total"] = df.get("TotalCount", 0)
|
|||
|
|
result["images"]["size"] = df.get("Size", "0B")
|
|||
|
|
elif df_type == "Containers":
|
|||
|
|
result["containers"]["usage"] = df.get("Active", 0)
|
|||
|
|
result["containers"]["total"] = df.get("TotalCount", 0)
|
|||
|
|
result["containers"]["size"] = df.get("Size", "0B")
|
|||
|
|
elif df_type == "Local Volumes":
|
|||
|
|
result["volumes"]["usage"] = df.get("Active", 0)
|
|||
|
|
result["volumes"]["total"] = df.get("TotalCount", 0)
|
|||
|
|
result["volumes"]["size"] = df.get("Size", "0B")
|
|||
|
|
except Exception as e:
|
|||
|
|
public.print_log('Docker', 'Parse docker system df failed: {}, error: {}'.format(line, str(e)))
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# Networks
|
|||
|
|
network_info = self.docker_client(self._url).networks.list()
|
|||
|
|
result["networks"]["total"] = len(network_info)
|
|||
|
|
|
|||
|
|
# Compose
|
|||
|
|
from mod.project.docker.composeMod import main as Compose
|
|||
|
|
dk_compose = Compose()
|
|||
|
|
compose_list = dk_compose.ls(get)
|
|||
|
|
result["composes"]["total"] = len(compose_list)
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
public.print_log('Docker', 'Get system info failed: {}'.format(str(e)))
|
|||
|
|
|
|||
|
|
return public.return_message(0, 0, result)
|