381 lines
14 KiB
Python
381 lines
14 KiB
Python
|
|
#coding: utf-8
|
||
|
|
# +-------------------------------------------------------------------
|
||
|
|
# | YakPanel
|
||
|
|
# +-------------------------------------------------------------------
|
||
|
|
# | Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||
|
|
# +-------------------------------------------------------------------
|
||
|
|
# | Author: wzjie <wzj@yakpanel.com>
|
||
|
|
# +-------------------------------------------------------------------
|
||
|
|
|
||
|
|
import os
|
||
|
|
import json
|
||
|
|
import time
|
||
|
|
import datetime
|
||
|
|
import re
|
||
|
|
import sqlite3
|
||
|
|
|
||
|
|
import public
|
||
|
|
|
||
|
|
|
||
|
|
class Monitor:
|
||
|
|
def __init__(self):
|
||
|
|
pass
|
||
|
|
|
||
|
|
def __get_file_json(self, filename):
|
||
|
|
try:
|
||
|
|
if not os.path.exists(filename): return {}
|
||
|
|
return json.loads(public.readFile(filename))
|
||
|
|
except:
|
||
|
|
return {}
|
||
|
|
|
||
|
|
def __get_file_nums(self, filepath):
|
||
|
|
if not os.path.exists(filepath): return 0
|
||
|
|
|
||
|
|
count = 0
|
||
|
|
for index, line in enumerate(open(filepath, 'r')):
|
||
|
|
count += 1
|
||
|
|
return count
|
||
|
|
|
||
|
|
def _get_site_list(self):
|
||
|
|
sites = public.M('sites').where('status=?', (1,)).field('name').get()
|
||
|
|
return sites
|
||
|
|
|
||
|
|
def _statuscode_distribute_site(self, site_name):
|
||
|
|
|
||
|
|
try:
|
||
|
|
day_401 = 0
|
||
|
|
day_500 = 0
|
||
|
|
day_502 = 0
|
||
|
|
day_503 = 0
|
||
|
|
conn = None
|
||
|
|
ts = None
|
||
|
|
start_date, end_date = self.get_time_interval(time.localtime())
|
||
|
|
select_sql = "select time/100 as time1, sum(status_401), sum(status_500), sum(status_502), sum(status_503) from request_stat where time between {} and {}"\
|
||
|
|
.format(start_date, end_date)
|
||
|
|
|
||
|
|
db_path = os.path.join("/www/server/total/", "logs/{}/logs.db".format(site_name))
|
||
|
|
if os.path.isfile(db_path):
|
||
|
|
conn = sqlite3.connect(db_path)
|
||
|
|
ts = conn.cursor()
|
||
|
|
ts.execute(select_sql)
|
||
|
|
results = ts.fetchall()
|
||
|
|
|
||
|
|
if type(results) == list:
|
||
|
|
for result in results:
|
||
|
|
time_key = str(result[0])
|
||
|
|
day_401 = result[1]
|
||
|
|
day_500 = result[2]
|
||
|
|
day_502 = result[3]
|
||
|
|
day_503 = result[4]
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
finally:
|
||
|
|
if ts:
|
||
|
|
ts.close()
|
||
|
|
if conn:
|
||
|
|
conn.close()
|
||
|
|
|
||
|
|
return day_401, day_500, day_502, day_503
|
||
|
|
|
||
|
|
def _statuscode_distribute_site_old(self, site_name):
|
||
|
|
today = time.strftime('%Y-%m-%d', time.localtime())
|
||
|
|
path = '/www/server/total/total/' + site_name + '/request/' + today + '.json'
|
||
|
|
|
||
|
|
day_401 = 0
|
||
|
|
day_500 = 0
|
||
|
|
day_502 = 0
|
||
|
|
day_503 = 0
|
||
|
|
if os.path.exists(path):
|
||
|
|
spdata = self.__get_file_json(path)
|
||
|
|
|
||
|
|
for c in spdata.values():
|
||
|
|
for d in c:
|
||
|
|
if '401' == d: day_401 += c['401'] or 0
|
||
|
|
if '500' == d: day_500 += c['500'] or 0
|
||
|
|
if '502' == d: day_502 += c['502'] or 0
|
||
|
|
if '503' == d: day_503 += c['503'] or 0
|
||
|
|
|
||
|
|
return day_401, day_500, day_502, day_503
|
||
|
|
|
||
|
|
def _statuscode_distribute(self, args):
|
||
|
|
sites = self._get_site_list()
|
||
|
|
|
||
|
|
count_401, count_500, count_502, count_503 = 0, 0, 0, 0
|
||
|
|
for site in sites:
|
||
|
|
site_name = site['name']
|
||
|
|
day_401, day_500, day_502, day_503 = self._statuscode_distribute_site(site_name)
|
||
|
|
day_401 = day_401 or 0
|
||
|
|
day_500 = day_500 or 0
|
||
|
|
day_502 = day_502 or 0
|
||
|
|
day_503 = day_503 or 0
|
||
|
|
count_401 += day_401
|
||
|
|
count_500 += day_500
|
||
|
|
count_502 += day_502
|
||
|
|
count_503 += day_503
|
||
|
|
return {'401': count_401, '500': count_500, '502': count_502, '503': count_503}
|
||
|
|
|
||
|
|
# 获取mysql当天的慢查询数量
|
||
|
|
def _get_slow_log_nums(self, args):
|
||
|
|
if not os.path.exists('/etc/my.cnf'):
|
||
|
|
return 0
|
||
|
|
|
||
|
|
ret = re.findall(r'datadir\s*=\s*(.+)', public.ReadFile('/etc/my.cnf'))
|
||
|
|
if not ret:
|
||
|
|
return 0
|
||
|
|
filename = ret[0] + '/mysql-slow.log'
|
||
|
|
|
||
|
|
if not os.path.exists(filename):
|
||
|
|
return 0
|
||
|
|
|
||
|
|
count = 0
|
||
|
|
zero_point = int(time.time()) - int(time.time() - time.timezone) % 86400
|
||
|
|
with open(filename) as f:
|
||
|
|
for line in f:
|
||
|
|
line = line.strip().lower()
|
||
|
|
if line.startswith('set timestamp='):
|
||
|
|
timestamp = int(line.split('=')[-1].strip(';'))
|
||
|
|
if timestamp >= zero_point:
|
||
|
|
count += 1
|
||
|
|
return count
|
||
|
|
|
||
|
|
# 判断字符串格式的时间是不是今天
|
||
|
|
def _is_today(self, time_str):
|
||
|
|
try:
|
||
|
|
time_date = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S").date()
|
||
|
|
except:
|
||
|
|
try:
|
||
|
|
time_date = datetime.datetime.strptime(time_str, "%y%m%d %H:%M:%S").date()
|
||
|
|
except:
|
||
|
|
time_date = datetime.datetime.strptime(time_str, "%d-%b-%Y %H:%M:%S").date()
|
||
|
|
today = datetime.date.today()
|
||
|
|
if time_date == today:
|
||
|
|
return True
|
||
|
|
return False
|
||
|
|
|
||
|
|
# PHP慢日志
|
||
|
|
def _php_count(self, args):
|
||
|
|
result = 0
|
||
|
|
if not os.path.exists('/www/server/php'):
|
||
|
|
return result
|
||
|
|
|
||
|
|
for i in os.listdir('/www/server/php'):
|
||
|
|
if os.path.isdir('/www/server/php/' + i):
|
||
|
|
php_slow = '/www/server/php/' + i + '/var/log/slow.log'
|
||
|
|
if os.path.exists(php_slow):
|
||
|
|
php_info = open(php_slow, 'r')
|
||
|
|
for j in php_info.readlines():
|
||
|
|
if re.search(r'\[\d+-\w+-\d+.+', j):
|
||
|
|
time_str = re.findall(r'\[\d+-\w+-\d+\s+\d+:\d+:\d+\]', j)
|
||
|
|
time_str = time_str[0].replace('[', '').replace(']', '')
|
||
|
|
if self._is_today(time_str):
|
||
|
|
result += 1
|
||
|
|
else:
|
||
|
|
break
|
||
|
|
|
||
|
|
return result
|
||
|
|
|
||
|
|
# 获取当天cc攻击数
|
||
|
|
def _get_cc_attack_num(self, args):
|
||
|
|
zero_point = int(time.time()) - int(time.time() - time.timezone) % 86400
|
||
|
|
log_path = '/www/server/btwaf/drop_ip.log'
|
||
|
|
if not os.path.exists(log_path): return 0
|
||
|
|
|
||
|
|
num = 100
|
||
|
|
log_body = public.GetNumLines(log_path, num).split('\n')
|
||
|
|
while True:
|
||
|
|
if len(log_body) < num:
|
||
|
|
break
|
||
|
|
if json.loads(log_body[0])[0] < zero_point:
|
||
|
|
break
|
||
|
|
else:
|
||
|
|
num += 100
|
||
|
|
log_body = public.GetNumLines(log_path, num).split('\n')
|
||
|
|
|
||
|
|
num = 0
|
||
|
|
for line in log_body:
|
||
|
|
try:
|
||
|
|
item = json.loads(line)
|
||
|
|
if item[0] > zero_point and item[-1] == 'cc':
|
||
|
|
num += 1
|
||
|
|
except: continue
|
||
|
|
|
||
|
|
return num
|
||
|
|
|
||
|
|
# 获取当天攻击总数
|
||
|
|
def _get_attack_num(self, args):
|
||
|
|
today = time.strftime('%Y-%m-%d', time.localtime())
|
||
|
|
sites = self._get_site_list()
|
||
|
|
|
||
|
|
count = 0
|
||
|
|
for site in sites:
|
||
|
|
file_path = '/www/wwwlogs/btwaf/{0}_{1}.log'.format(site['name'], today)
|
||
|
|
count += self.__get_file_nums(file_path)
|
||
|
|
return count
|
||
|
|
|
||
|
|
def get_exception(self, args):
|
||
|
|
data = {'mysql_slow': self._get_slow_log_nums(args), 'php_slow': self._php_count(args),
|
||
|
|
'attack_num': self._get_attack_num(args), 'cc_attack_num': self._get_cc_attack_num(args)}
|
||
|
|
statuscode_distribute = self._statuscode_distribute(args)
|
||
|
|
data.update(statuscode_distribute)
|
||
|
|
return data
|
||
|
|
|
||
|
|
def get_spider(self, args):
|
||
|
|
request_data = {}
|
||
|
|
sites = public.M('sites').field('name').order("addtime").select();
|
||
|
|
for site_info in sites:
|
||
|
|
ts = None
|
||
|
|
conn = None
|
||
|
|
try:
|
||
|
|
site_name = site_info["name"]
|
||
|
|
start_date, end_date = self.get_time_interval(time.localtime())
|
||
|
|
select_sql = "select time, spider from request_stat where time between {} and {}"\
|
||
|
|
.format(start_date, end_date)
|
||
|
|
|
||
|
|
db_path = os.path.join("/www/server/total/", "logs/{}/logs.db".format(site_name))
|
||
|
|
if not os.path.isfile(db_path): continue
|
||
|
|
conn = sqlite3.connect(db_path)
|
||
|
|
ts = conn.cursor()
|
||
|
|
ts.execute(select_sql)
|
||
|
|
results = ts.fetchall()
|
||
|
|
|
||
|
|
if type(results) == list:
|
||
|
|
for result in results:
|
||
|
|
time_key = str(result[0])
|
||
|
|
hour = time_key[len(time_key)-2:]
|
||
|
|
value = result[1]
|
||
|
|
if hour not in request_data:
|
||
|
|
request_data[hour] = value
|
||
|
|
else:
|
||
|
|
request_data[hour] += value
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
finally:
|
||
|
|
if ts:
|
||
|
|
ts.close()
|
||
|
|
if conn:
|
||
|
|
conn.close()
|
||
|
|
return request_data
|
||
|
|
|
||
|
|
# 获取蜘蛛数量分布
|
||
|
|
def get_spider_old(self, args):
|
||
|
|
today = time.strftime('%Y-%m-%d', time.localtime())
|
||
|
|
sites = self._get_site_list()
|
||
|
|
|
||
|
|
data = {}
|
||
|
|
for site in sites:
|
||
|
|
site_name = site['name']
|
||
|
|
file_name = '/www/server/total/total/' + site_name + '/spider/' + today + '.json'
|
||
|
|
if not os.path.exists(file_name): continue
|
||
|
|
day_data = self.__get_file_json(file_name)
|
||
|
|
for s_data in day_data.values():
|
||
|
|
for s_key in s_data.keys():
|
||
|
|
if s_key not in data:
|
||
|
|
data[s_key] = s_data[s_key]
|
||
|
|
else:
|
||
|
|
data[s_key] += s_data[s_key]
|
||
|
|
return data
|
||
|
|
|
||
|
|
# 获取负载和上行流量
|
||
|
|
def load_and_up_flow(self, args):
|
||
|
|
import psutil
|
||
|
|
|
||
|
|
load_five = float(os.getloadavg()[1])
|
||
|
|
cpu_count = psutil.cpu_count()
|
||
|
|
|
||
|
|
up_flow = 0
|
||
|
|
data = public.M('network').dbfile('system').field('up').order('id desc').limit("5").get()
|
||
|
|
if len(data) == 5:
|
||
|
|
up_flow = round(sum([item['up'] for item in data]) / 5, 2)
|
||
|
|
|
||
|
|
return {'load_five': load_five, 'cpu_count': cpu_count, 'up_flow': up_flow}
|
||
|
|
|
||
|
|
def get_time_interval(self, local_time):
|
||
|
|
start = None
|
||
|
|
end = None
|
||
|
|
time_key_format = "%Y%m%d00"
|
||
|
|
start = int(time.strftime(time_key_format, local_time))
|
||
|
|
time_key_format = "%Y%m%d23"
|
||
|
|
end = int(time.strftime(time_key_format, local_time))
|
||
|
|
return start, end
|
||
|
|
|
||
|
|
def get_request_count_by_hour(self, args):
|
||
|
|
# 获取站点每小时的请求数据
|
||
|
|
request_data = {}
|
||
|
|
import sqlite3
|
||
|
|
sites = public.M('sites').field('name').order("addtime").select();
|
||
|
|
for site_info in sites:
|
||
|
|
ts = None
|
||
|
|
conn = None
|
||
|
|
try:
|
||
|
|
site_name = site_info["name"]
|
||
|
|
start_date, end_date = self.get_time_interval(time.localtime())
|
||
|
|
select_sql = "select time, req from request_stat where time between {} and {}"\
|
||
|
|
.format(start_date, end_date)
|
||
|
|
db_path = os.path.join("/www/server/total/", "logs/{}/logs.db".format(site_name))
|
||
|
|
if not os.path.isfile(db_path): continue
|
||
|
|
conn = sqlite3.connect(db_path)
|
||
|
|
ts = conn.cursor()
|
||
|
|
ts.execute(select_sql)
|
||
|
|
results = ts.fetchall()
|
||
|
|
if type(results) == list:
|
||
|
|
for result in results:
|
||
|
|
time_key = str(result[0])
|
||
|
|
hour = time_key[len(time_key)-2:]
|
||
|
|
value = result[1]
|
||
|
|
if hour not in request_data:
|
||
|
|
request_data[hour] = value
|
||
|
|
else:
|
||
|
|
request_data[hour] += value
|
||
|
|
except: pass
|
||
|
|
finally:
|
||
|
|
if ts:
|
||
|
|
ts.close()
|
||
|
|
if conn:
|
||
|
|
conn.close()
|
||
|
|
return request_data
|
||
|
|
|
||
|
|
# 取每小时的请求数
|
||
|
|
def get_request_count_by_hour_old(self, args):
|
||
|
|
today = time.strftime('%Y-%m-%d', time.localtime())
|
||
|
|
|
||
|
|
request_data = {}
|
||
|
|
sites = self._get_site_list()
|
||
|
|
for site in sites:
|
||
|
|
path = '/www/server/total/total/' + site['name'] + '/request/' + today + '.json'
|
||
|
|
if os.path.exists(path):
|
||
|
|
spdata = self.__get_file_json(path)
|
||
|
|
for hour, value in spdata.items():
|
||
|
|
count = value.get('GET', 0) + value.get('POST', 0)
|
||
|
|
if hour not in request_data:
|
||
|
|
request_data[hour] = count
|
||
|
|
else:
|
||
|
|
request_data[hour] = request_data[hour] + count
|
||
|
|
|
||
|
|
return request_data
|
||
|
|
|
||
|
|
# 取服务器的请求数
|
||
|
|
def _get_request_count(self, args):
|
||
|
|
request_data = self.get_request_count_by_hour(args)
|
||
|
|
return sum(request_data.values())
|
||
|
|
|
||
|
|
# 获取瞬时请求数和qps
|
||
|
|
def get_request_count_qps(self, args):
|
||
|
|
from YakPanel import cache
|
||
|
|
|
||
|
|
cache_timeout = 86400
|
||
|
|
|
||
|
|
old_total_request = cache.get('old_total_request')
|
||
|
|
otime = cache.get("old_get_time")
|
||
|
|
if not old_total_request or not otime:
|
||
|
|
otime = time.time()
|
||
|
|
old_total_request = self._get_request_count(args)
|
||
|
|
time.sleep(2)
|
||
|
|
ntime = time.time()
|
||
|
|
new_total_request = self._get_request_count(args)
|
||
|
|
|
||
|
|
qps = float(new_total_request - old_total_request) / (ntime - otime)
|
||
|
|
|
||
|
|
cache.set('old_total_request', new_total_request, cache_timeout)
|
||
|
|
cache.set('old_get_time', ntime, cache_timeout)
|
||
|
|
return {'qps': qps, 'request_count': new_total_request}
|