Files
aaPanel/class/monitorModel/process_managementModel.py
T
2024-04-29 15:35:04 +08:00

557 lines
26 KiB
Python

import json
import os
import time
import traceback
import re
import psutil
import public
from monitorModel.base import monitorBase
from pluginAuth import Plugin
class main(monitorBase):
setupPath = '/www/server'
__panel_path = '/www/server/panel/class/monitorModel'
__data_path = os.path.join(__panel_path, 'data')
cpu_old_path = os.path.join(__data_path, 'cpu_old.json')
disk_read_old_path = os.path.join(__data_path, 'disk_read_old.json')
disk_write_old_path = os.path.join(__data_path, 'disk_write_old.json')
old_net_path = os.path.join(__data_path, 'network_old.json')
old_disk_path = os.path.join(__data_path, 'disk_old.json')
old_site_path = os.path.join(__data_path, 'site_old.json')
nethogs_out = os.path.join(__data_path, 'process_flow.log')
disk_write_new_info = {}
disk_write_old_info = {}
disk_read_new_info = {}
disk_read_old_info = {}
cpu_new_info = {}
old_disk_info = {}
new_disk_info = {}
cpu_old_info = {}
log_path = {
"mongod": "/www/server/mongodb/log/config.log",
"nginx": "/www/wwwlogs/nginx_error.log",
"httpd": "/www/wwwlogs/nginx_error.log",
"mysqld": "/www/server/data/mysql-slow.log",
}
pids = None
__cpu_time = None
panel_pid = None
task_pid = None
processPs = {
'bioset': '用于处理块设备上的I/O请求的进程',
'BT-MonitorAgent': '面板程序的进程',
'rngd': '一个熵守护的进程',
'master': '用于管理和协调子进程的活动的进程',
'irqbalance': '一个IRQ平衡守护的进程',
'rhsmcertd': '主要用于管理Red Hat订阅证书,并维护系统的订阅状态的进程',
'auditd': '是Linux审计系统中用户空间的一个组的进程',
'chronyd': '调整内核中运行的系统时钟和时钟服务器同步的进程',
'qmgr': 'PBS管理器的进程',
'oneavd': '面板微步木马检测的进程',
'postgres': 'PostgreSQL数据库的进程',
'grep': '一个命令行工具的进程',
'lsof': '一个命令行工具的进程',
'containerd-shim-runc-v2': 'Docker容器的一个组件的进程',
'pickup': '用于监听Unix域套接字的进程',
'cleanup': '邮件传输代理(MTA)中的一个组件的进程',
'trivial-rewrite': '邮件传输代理(MTA)中的一个组件的进程',
'containerd': 'docker依赖服务的进程',
'redis-server': 'redis服务的进程',
'rcu_sched': 'linux系统rcu机制服务的进程',
'jsvc': '面板tomcat服务的进程',
'oneav': '面板微步木马检测的进程',
'mysqld': 'MySQL服务的进程',
'php-fpm': 'PHP的子进程',
'php-cgi': 'PHP-CGI的进程',
'nginx': 'Nginx服务的进程',
'httpd': 'Apache服务的进程',
'sshd': 'SSH服务的进程',
'pure-ftpd': 'FTP服务的进程',
'sftp-server': 'SFTP服务的进程',
'mysqld_safe': 'MySQL服务的进程',
'firewalld': '防火墙服务的进程',
'BT-Panel': '宝塔面板-主的进程',
'BT-Task': '宝塔面板-后台任务的进程',
'NetworkManager': '网络管理服务的进程',
'svlogd': '日志守护的进程',
'memcached': 'Memcached缓存器的进程',
'gunicorn': "宝塔面板的进程",
"BTPanel": '宝塔面板的进程',
'baota_coll': "堡塔云控-主控端的进程",
'baota_client': "堡塔云控-被控端的进程",
'node': 'Node.js程序的进程',
'supervisord': 'Supervisor的进程',
'rsyslogd': 'rsyslog日志服务的进程',
'crond': '计划任务服务的进程',
'cron': '计划任务服务的进程',
'rsync': 'rsync文件同步的进程',
'ntpd': '网络时间同步服务的进程',
'rpc.mountd': 'NFS网络文件系统挂载服务的进程',
'sendmail': 'sendmail邮件服务的进程',
'postfix': 'postfix邮件服务的进程',
'npm': 'Node.js NPM管理器的进程',
'PM2': 'Node.js PM2进程管理器的进程',
'htop': 'htop进程监控软件的进程',
'btpython': '宝塔面板-独立Python环境的进程',
'btappmanagerd': '宝塔应用管理器插件的进程',
'dockerd': 'Docker容器管理器的进程',
'docker-proxy': 'Docker容器管理器的进程',
'docker-registry': 'Docker容器管理器的进程',
'docker-distribution': 'Docker容器管理器的进程',
'docker-network': 'Docker容器管理器的进程',
'docker-volume': 'Docker容器管理器的进程',
'docker-swarm': 'Docker容器管理器的进程',
'docker-systemd': 'Docker容器管理器的进程',
'docker-containerd': 'Docker容器管理器的进程',
'docker-containerd-shim': 'Docker容器管理器的进程',
'docker-runc': 'Docker容器管理器的进程',
'docker-init': 'Docker容器管理器的进程',
'docker-init-systemd': 'Docker容器管理器的进程',
'docker-init-upstart': 'Docker容器管理器的进程',
'docker-init-sysvinit': 'Docker容器管理器的进程',
'docker-init-openrc': 'Docker容器管理器的进程',
'docker-init-runit': 'Docker容器管理器的进程',
'docker-init-systemd-resolved': 'Docker容器管理器的进程',
'rpcbind': 'NFS网络文件系统服务的进程',
'dbus-daemon': 'D-Bus消息总线守护的进程',
'systemd-logind': '登录管理器的进程',
'systemd-journald': 'Systemd日志管理服务的进程',
'systemd-udevd': '系统设备管理服务的进程',
'systemd-timedated': '系统时间日期服务的进程',
'systemd-timesyncd': '系统时间同步服务的进程',
'systemd-resolved': '系统DNS解析服务的进程',
'systemd-hostnamed': '系统主机名服务的进程',
'systemd-networkd': '系统网络管理服务的进程',
'systemd-resolvconf': '系统DNS解析服务的进程',
'systemd-local-resolv': '系统DNS解析服务的进程',
'systemd-sysctl': '系统系统参数服务的进程',
'systemd-modules-load': '系统模块加载服务的进程',
'systemd-modules-restore': '系统模块恢复服务的进程',
'agetty': 'TTY登陆验证程序的进程',
'sendmail-mta': 'MTA邮件传送代理的进程',
'(sd-pam)': '可插入认证模块的进程',
'polkitd': '授权管理服务的进程',
'mongod': 'MongoDB数据库服务的进程',
'mongodb': 'MongoDB数据库服务的进程',
'mongodb-mms-monitor': 'MongoDB数据库服务的进程',
'mongodb-mms-backup': 'MongoDB数据库服务的进程',
'mongodb-mms-restore': 'MongoDB数据库服务的进程',
'mongodb-mms-agent': 'MongoDB数据库服务的进程',
'mongodb-mms-analytics': 'MongoDB数据库服务的进程',
'mongodb-mms-tools': 'MongoDB数据库服务的进程',
'mongodb-mms-backup-agent': 'MongoDB数据库服务的进程',
'mongodb-mms-backup-tools': 'MongoDB数据库服务的进程',
'mongodb-mms-restore-agent': 'MongoDB数据库服务的进程',
'mongodb-mms-restore-tools': 'MongoDB数据库服务的进程',
'mongodb-mms-analytics-agent': 'MongoDB数据库服务的进程',
'mongodb-mms-analytics-tools': 'MongoDB数据库服务的进程',
'dhclient': 'DHCP协议客户端的进程',
'dhcpcd': 'DHCP协议客户端的进程',
'dhcpd': 'DHCP服务器的进程',
'isc-dhcp-server': 'DHCP服务器的进程',
'isc-dhcp-server6': 'DHCP服务器的进程',
'dhcp6c': 'DHCP服务器的进程',
'dhcpcd': 'DHCP服务器的进程',
'dhcpd': 'DHCP服务器的进程',
'avahi-daemon': 'Zeroconf守护的进程',
'login': '登录的进程',
'systemd': '系统管理服务的进程',
'systemd-sysv': '系统管理服务的进程',
'systemd-journal-gateway': '系统管理服务的进程',
'systemd-journal-remote': '系统管理服务的进程',
'systemd-journal-upload': '系统管理服务的进程',
'systemd-networkd': '系统网络管理服务的进程',
'rpc.idmapd': 'NFS网络文件系统相关服务的进程',
'cupsd': '打印服务的进程',
'cups-browsed': '打印服务的进程',
'sh': 'shell的进程',
'php': 'PHP CLI模式的进程',
'blkmapd': 'NFS映射服务的进程',
'lsyncd': '文件同步服务的进程',
'sleep': '延迟的进程',
}
def __init__(self):
if not os.path.isdir(self.__data_path):
os.makedirs(self.__data_path, 384)
plugin_obj = Plugin(False)
plugin_list = plugin_obj.get_plugin_list()
public.print_log(plugin_list['ltd'])
ped = int(plugin_list['ltd']) > time.time()
if ped:
self.add_nethogs_task()
def specific_resource_load_type(self, get):
"""
查询具体资源类型负载
:param get: None
:return: 资源占用字典
"""
try:
plugin_obj = Plugin(False)
plugin_list = plugin_obj.get_plugin_list()
public.print_log(plugin_list['ltd'])
ped = int(plugin_list['ltd']) > time.time()
if not ped: return {'status': False, 'msg': "该功能为企业版专享!"}
infos = {}
load_avg = os.getloadavg()
infos['info'] = {}
infos['info']['physical_cpu'] = psutil.cpu_count(logical=False)
infos['info']['logical_cpu'] = psutil.cpu_count(logical=True)
c_tmp = public.readFile('/proc/cpuinfo')
d_tmp = re.findall("physical id.+", c_tmp)
cpuW = len(set(d_tmp))
infos['info']['cpu_name'] = public.getCpuType() + " * {}".format(cpuW)
infos['info']['num_phys_cores'] = cpuW
infos['info']['load_avg'] = {"1": load_avg[0], "5": load_avg[1], "15": load_avg[2]}
infos['info']['active_processes'] = len(
[p for p in psutil.process_iter() if p.status() == psutil.STATUS_RUNNING])
infos['info']['total_processes'] = len(psutil.pids())
cpu_percent = self.get_process_cpu(get)
cpu_proc = cpu_percent["process_list"]
mem = self.get_mem_info()
infos['CPU_percentage_of_load'] = cpu_percent["info"]["cpu"]
infos['percentage_of_memory_usage'] = round(mem['memRealUsed'] / mem['memTotal'] * 100, 2)
infos['CPU_high_occupancy_software_list'] = {}
for i in range(5):
try:
infos['CPU_high_occupancy_software_list'][i] = {"name": cpu_proc[i]['name'],
'pid': cpu_proc[i]['pid'],
'cpu_percent': cpu_proc[i]['cpu_percent'],
'proc_survive': cpu_proc[i]['proc_survive']}
except:
pass
b = []
for i, j in infos['CPU_high_occupancy_software_list'].items():
cpu_info = {'proc_name': infos['CPU_high_occupancy_software_list'][i]['name'],
'pid': infos['CPU_high_occupancy_software_list'][i]['pid'],
'cpu_percent': str(infos['CPU_high_occupancy_software_list'][i]['cpu_percent']) + "%"}
cpu_info['explain'], cpu_info['num_threads'], cpu_info['exe_path'], cpu_info['cwd_path'], cpu_info[
'important'], cpu_info['proc_survive'] = self.__process_analysis(
infos['CPU_high_occupancy_software_list'][i]['pid'])
b.append(cpu_info)
infos['CPU_high_occupancy_software_list'] = b
infos["memory_high_occupancy_software_list"] = self.__use_mem_list()
c = []
for i, j in infos["memory_high_occupancy_software_list"].items():
mem_info = {'proc_name': i, 'pid': infos['memory_high_occupancy_software_list'][i]['pid'],
"memory_usage": infos["memory_high_occupancy_software_list"][i]['memory_usage']}
mem_info['explain'], mem_info['num_threads'], mem_info['exe_path'], mem_info['cwd_path'], mem_info[
'important'], mem_info['proc_survive'] = self.__process_analysis(
infos["memory_high_occupancy_software_list"][i]['pid'])
c.append(mem_info)
infos["memory_high_occupancy_software_list"] = c
return infos
except:
public.print_log(traceback.format_exc())
# 按cpu资源获取进程列表
def get_process_cpu(self, get):
self.pids = psutil.pids()
process_list = []
if type(self.cpu_new_info) != dict: self.cpu_new_info = {}
self.cpu_new_info['cpu_time'] = self.get_cpu_time()
self.cpu_new_info['time'] = time.time()
if 'sort' not in get: get.sort = 'cpu_percent'
get.reverse = bool(int(get.reverse)) if 'reverse' in get else True
info = {}
info['activity'] = 0
info['cpu'] = 0.00
status_ps = {'sleeping': '睡眠', 'running': '活动'}
limit = 1000
for pid in self.pids:
tmp = {}
try:
p = psutil.Process(pid)
except:
continue
with p.oneshot():
p_cpus = p.cpu_times()
p_state = p.status()
if p_state == 'running': info['activity'] += 1
if p_state in status_ps:
p_state = status_ps[p_state]
else:
continue
tmp['exe'] = p.exe()
timestamp = time.time() - p.create_time()
time_info = {}
time_info["天"] = int(timestamp // (24 * 3600))
time_info["小时"] = int((timestamp - time_info['天'] * 24 * 3600) // 3600)
time_info["分钟"] = int((timestamp - time_info['天'] * 24 * 3600 - time_info['小时'] * 3600) // 60)
ll = [str(v) + k for k, v in time_info.items() if v != 0]
tmp['proc_survive'] = ''.join(ll)
tmp['name'] = p.name()
tmp['pid'] = pid
tmp['ppid'] = p.ppid()
# tmp['create_time'] = int(p.create_time())
tmp['status'] = p_state
tmp['user'] = p.username()
tmp['cpu_percent'] = self.get_cpu_percent(str(pid), p_cpus, self.cpu_new_info['cpu_time'])
tmp['threads'] = p.num_threads()
tmp['ps'] = self.get_process_ps(tmp['name'], pid)
if tmp['cpu_percent'] > 100: tmp['cpu_percent'] = 0.1
info['cpu'] += tmp['cpu_percent']
process_list.append(tmp)
limit -= 1
if limit <= 0: break
del p
del tmp
public.writeFile(self.cpu_old_path, json.dumps(self.cpu_new_info))
# process_list = self.handle_process_list(process_list)
process_list = sorted(process_list, key=lambda x: x[get.sort], reverse=get.reverse)
info['load_average'] = self.get_load_average()
data = {}
data['process_list'] = process_list[:10]
info['cpu'] = round(info['cpu'], 2)
data['info'] = info
return data
# 获取负载
def get_load_average(self, get=None):
b = public.ExecShell("uptime")[0].replace(',', '')
c = b.split()
data = {}
data['1'] = float(c[-3])
data['5'] = float(c[-2])
data['15'] = float(c[-1])
return data
# 获取总的cpu时间
def get_cpu_time(self, get=None):
if self.__cpu_time: return self.__cpu_time
self.__cpu_time = 0.00
s = psutil.cpu_times()
self.__cpu_time = s.user + s.system + s.nice + s.idle
return self.__cpu_time
# 获取进程cpu利用率
def get_cpu_percent(self, pid, cpu_times, cpu_time):
self.get_cpu_old()
percent = 0.00
process_cpu_time = self.get_process_cpu_time(cpu_times)
if not self.cpu_old_info: self.cpu_old_info = {}
if pid not in self.cpu_old_info:
self.cpu_new_info[pid] = {}
self.cpu_new_info[pid]['cpu_time'] = process_cpu_time
return percent
percent = round(100.00 * (process_cpu_time - self.cpu_old_info[pid]['cpu_time']) / (
cpu_time - self.cpu_old_info['cpu_time']), 2)
self.cpu_new_info[pid] = {}
self.cpu_new_info[pid]['cpu_time'] = process_cpu_time
if percent > 0: return percent
return 0.00
# 获取信息,如果存在返回true,不存在读取gson后存在true:不存在flase
def get_cpu_old(self):
if self.cpu_old_info: return True
if not os.path.exists(self.cpu_old_path): return False
data = public.readFile(self.cpu_old_path)
if not data: return False
data = json.loads(data)
if not data: return False
self.cpu_old_info = data
del data
return True
# 获取进程占用的cpu时间
def get_process_cpu_time(self, cpu_times):
cpu_time = 0.00
for s in cpu_times: cpu_time += s
return cpu_time
def get_process_ps(self, name, pid):
if name in self.processPs: return self.processPs[name]
# 增加使用nethogs收集进程流量定时任务
def add_nethogs_task(self, get=None):
# self.add_process_white('nethogs')
import crontab
if public.M('crontab').where('name=?', u'[勿删]资源管理器-获取进程流量').count():
return public.returnMsg(True, '定时任务已存在!')
s_body = '''ps -ef | grep nethogs | grep -v grep | awk '{print $2}' | xargs kill 2>/dev/null
count=0
while [ $count -lt 2 ]
do
count=$(($count+1))
/usr/sbin/nethogs -t -a -d 2 -c 5 > %s 2>/dev/null
if [[ $count == 2 ]];then
exit
else
sleep 20
fi
done''' % self.nethogs_out
p = crontab.crontab()
args = {
"name": u'[勿删]资源管理器-获取进程流量',
"type": 'minute-n',
"where1": 5,
"hour": '',
"minute": '',
"week": '',
"sType": "toShell",
"sName": "",
"backupTo": "",
"save": '',
"sBody": s_body,
"urladdress": "undefined"
}
p.AddCrontab(args)
return public.returnMsg(True, '设置成功!')
# 获取内存情况
def get_mem_info(self, get=None):
mem = psutil.virtual_memory()
memInfo = {'memTotal': int(mem.total / 1024 / 1024), 'memFree': int(mem.free / 1024 / 1024),
'memBuffers': int(mem.buffers / 1024 / 1024), 'memCached': int(mem.cached / 1024 / 1024)}
memInfo['memRealUsed'] = memInfo['memTotal'] - memInfo['memFree'] - memInfo['memBuffers'] - memInfo['memCached']
return memInfo
def __use_mem_list(self):
processes = []
for proc in psutil.process_iter():
try:
# 获取进程详细信息
pinfo = proc.as_dict(attrs=['pid', 'name', 'memory_info'])
# 添加到进程列表
processes.append(pinfo)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
processes = sorted(processes, key=lambda p: p['memory_info'].rss, reverse=True)
l = {}
mem_total = psutil.virtual_memory().total
for p in processes:
l[p['name']] = {'pid': p['pid'],
'memory_usage': '%.2f' % (int(p['memory_info'].rss) / int(mem_total) * 100) + "%"}
if len(l) >= 5:
break
return l
# 常用软件分析
def __process_analysis(self, pid):
process = psutil.Process(pid)
important = 0
explain = self.processPs.get(process.name(),
'未知程序的进程')
if 'BT-Panel' == process.name() or 'BT-Task' == process.name():
important = 1
num_threads = process.num_threads()
exe_path = process.exe()
cwd_path = process.cwd()
timestamp = time.time() - process.create_time()
time_info = {}
time_info["天"] = int(timestamp // (24 * 3600))
time_info["小时"] = int((timestamp - time_info['天'] * 24 * 3600) // 3600)
time_info["分钟"] = int((timestamp - time_info['天'] * 24 * 3600 - time_info['小时'] * 3600) // 60)
ll = [str(v) + k for k, v in time_info.items() if v != 0]
pro_time = ''.join(ll)
if ''.join(ll) == '':
pro_time = '小于1分钟'
return explain, num_threads, exe_path, cwd_path, important, pro_time
def kill_process_all(self, get):
pid = int(get.pid)
if pid < 30: return public.returnMsg(False, '不能结束系统关键进程!')
if pid not in psutil.pids(): return public.returnMsg(False, '指定进程不存在!')
p = psutil.Process(pid)
if self.is_panel_process(pid): return public.returnMsg(False, '不能结束面板服务进程')
p.kill()
return self.kill_process_tree_all(pid)
# 结束进程树
def kill_process_tree_all(self, pid):
if pid < 30: return public.returnMsg(True, '已结束此进程树!')
if self.is_panel_process(pid): return public.returnMsg(False, '不能结束面板服务进程')
try:
if pid not in psutil.pids(): public.returnMsg(True, '已结束此进程树!')
p = psutil.Process(pid)
ppid = p.ppid()
name = p.name()
p.kill()
public.ExecShell('pkill -9 ' + name)
if name.find('php-') != -1:
public.ExecShell("rm -f /tmp/php-cgi-*.sock")
elif name.find('mysql') != -1:
public.ExecShell("rm -f /tmp/mysql.sock")
elif name.find('mongod') != -1:
public.ExecShell("rm -f /tmp/mongod*.sock")
self.kill_process_lower(pid)
if ppid: return self.kill_process_all(ppid)
except:
pass
return public.returnMsg(True, '已结束此进程树!')
def kill_process_lower(self, pid):
pids = psutil.pids()
for lpid in pids:
if lpid < 30: continue
if self.is_panel_process(lpid): continue
p = psutil.Process(lpid)
ppid = p.ppid()
if ppid == pid:
p.kill()
return self.kill_process_lower(lpid)
return True
# 判断是否是面板进程
def is_panel_process(self, pid):
if not self.panel_pid:
self.panel_pid = os.getpid()
if pid == self.panel_pid: return True
if not self.task_pid:
try:
self.task_pid = int(
public.ExecShell("ps aux | grep 'python task.py'|grep -v grep|head -n1|awk '{print $2}'")[0])
except:
self.task_pid = -1
if pid == self.task_pid: return True
return False
def __get_number_of_processes(self):
import psutil
from collections import Counter
ll = []
processes = psutil.process_iter()
process_names = [process.name() for process in processes]
process_item = Counter(process_names)
process_item = dict(sorted(process_item.items(), key=lambda item: item[1], reverse=True)[:5])
for key, value in process_item.items():
procs = {'proc_name': key, 'proc_description': '此进程的进程数有' + str(value) + '个,进程是{}'.format(
self.processPs.get(key, "未知进程"))}
ll.append(procs)
return ll
def process_description(self, get):
try:
updatas = json.loads(get.information_collection)
data = json.loads(public.readFile('/www/server/panel/class/monitorModel/common_process.json'))
data.update(updatas)
public.writeFile('/www/server/panel/class/monitorModel/common_process.json', json.dumps(data))
return public.returnMsg(True, "进程添加成功")
except:
return public.returnMsg(False, "进程添加失败")
def universal(self, get):
method = {
"题目1": "遇到未知进程解决办法。",
"1.1": "观察进程可执行目录和运行目录,是否与BT、项目名、常用软件相关,若与项目或者系统相关的进程且占用资源不大可不管。",
"1.2": "去‘百度’上搜索进程名,查看进程的归属,以及是否有害。ps:https://www.baidu.com",
"1.3": "咨询项目开发人员,看此进程是否由部署的项目所创建,若是,可加入到常见进程列表中。",
"1.4": "实在判断不了进程的性质,可到宝塔论坛发帖求助.ps:https://www.bt.cn/bbs/portal.php",
"1.5": "对进程做出详细的判断后,无用且占用资源较高,可关闭该进程。",
"1.6": "若占用资源较多的是使用当中的软件或项目,则可以尝试适当的优化,比如mysql优化、适当限制php的并发等。",
"题目2": "内存,cpu使用率不高,但负载很高解决办法",
"2.1": "负载高低还与线程数量、IO使用率、服务器本身有联系,可查看线程数量以及磁盘使用情况进行综合判断",
"2.2": "若本身服务器的配置较低,可以适当的考虑升级服务器配置",
"2.3": "若是遭受到网络攻击,也可导致服务器的负载偏高,可以开启宝塔防火墙以及安全插件进行防护。",
"2.4": "若服务器使用的是云服务器,也可能是服务器商家限制,可以咨询一下服务器商家的客服。"
}
return method