From 540fc3511ced42bc23953daf444db673deafbc52 Mon Sep 17 00:00:00 2001 From: vapao Date: Sat, 24 Apr 2021 02:56:39 +0800 Subject: [PATCH] upgrade monitor module --- .../exec/management/commands/runexecutor.py | 3 +- spug_api/apps/monitor/executors.py | 43 +++++++- spug_api/apps/monitor/models.py | 4 +- spug_api/apps/monitor/scheduler.py | 21 ++-- spug_api/apps/monitor/views.py | 9 +- spug_web/src/pages/monitor/Step1.js | 98 +++++++++---------- spug_web/src/pages/monitor/Step2.js | 2 +- spug_web/src/pages/monitor/store.js | 2 +- 8 files changed, 112 insertions(+), 70 deletions(-) diff --git a/spug_api/apps/exec/management/commands/runexecutor.py b/spug_api/apps/exec/management/commands/runexecutor.py index d2a33f9..2a51cc8 100644 --- a/spug_api/apps/exec/management/commands/runexecutor.py +++ b/spug_api/apps/exec/management/commands/runexecutor.py @@ -6,6 +6,7 @@ from django.conf import settings from django_redis import get_redis_connection from concurrent.futures import ThreadPoolExecutor from apps.schedule.executors import schedule_worker_handler +from apps.monitor.executors import monitor_worker_handler import logging MONITOR_WORKER_KEY = settings.MONITOR_WORKER_KEY @@ -24,7 +25,7 @@ class Worker: if key.decode() == SCHEDULE_WORKER_KEY: self._executor.submit(schedule_worker_handler, job) else: - pass + self._executor.submit(monitor_worker_handler, job) class Command(BaseCommand): diff --git a/spug_api/apps/monitor/executors.py b/spug_api/apps/monitor/executors.py index 46998ba..b2cf0c7 100644 --- a/spug_api/apps/monitor/executors.py +++ b/spug_api/apps/monitor/executors.py @@ -8,13 +8,18 @@ import subprocess import platform import requests import logging +import json logging.captureWarnings(True) -def site_check(url): +def site_check(url, limit): try: res = requests.get(url, timeout=10, verify=False) + if limit: + duration = int(res.elapsed.total_seconds() * 1000) + if duration > int(limit): + return False, f'响应时间:{duration}ms' return 200 <= res.status_code < 400, f'返回状态码:{res.status_code}' except Exception as e: return False, f'异常信息:{e}' @@ -58,9 +63,41 @@ def host_executor(host, command): return False, f'异常信息:{e}' -def dispatch(tp, addr, extra, in_view=False): - if not in_view: +def monitor_worker_handler(job): + print('enter: ', job) + task_id, tp, addr, extra = json.loads(job) + if tp == '1': + is_ok, message = site_check(addr, extra) + elif tp == '2': + is_ok, message = port_check(addr, extra) + elif tp == '5': + is_ok, message = ping_check(addr) + elif tp not in ('3', '4'): + is_ok, message = False, f'invalid monitor type for {tp!r}' + else: close_old_connections() + command = f'ps -ef|grep -v grep|grep {extra!r}' if tp == '3' else extra + host = Host.objects.filter(pk=addr).first() + if not host: + is_ok, message = False, f'unknown host id for {addr!r}' + else: + is_ok, message = host_executor(host, command) + + + # is_notified = True if obj.latest_notify_time else False + # if obj.latest_status in [0, None] and is_ok is False: + # obj.latest_fault_time = int(time.time()) + # if is_ok: + # obj.latest_notify_time = 0 + # obj.fault_times = 0 + # else: + # obj.fault_times += 1 + # obj.latest_status = 0 if is_ok else 1 + # obj.latest_run_time = human_datetime(event.scheduled_run_time) + # obj.save() + # self._handle_notify(obj, is_notified, out) + +def dispatch(tp, addr, extra): if tp == '1': return site_check(addr) elif tp == '2': diff --git a/spug_api/apps/monitor/models.py b/spug_api/apps/monitor/models.py index 177375a..17e765b 100644 --- a/spug_api/apps/monitor/models.py +++ b/spug_api/apps/monitor/models.py @@ -22,7 +22,8 @@ class Detection(models.Model, ModelMixin): name = models.CharField(max_length=50) type = models.CharField(max_length=2, choices=TYPES) group = models.CharField(max_length=255, null=True) - addr = models.CharField(max_length=255) + addr = models.CharField(max_length=255) # 要删除的 + targets = models.TextField() extra = models.TextField(null=True) desc = models.CharField(max_length=255, null=True) is_active = models.BooleanField(default=True) @@ -48,6 +49,7 @@ class Detection(models.Model, ModelMixin): tmp['latest_status_alias'] = self.get_latest_status_display() tmp['notify_mode'] = json.loads(self.notify_mode) tmp['notify_grp'] = json.loads(self.notify_grp) + tmp['targets'] = json.loads(self.targets) return tmp def __repr__(self): diff --git a/spug_api/apps/monitor/scheduler.py b/spug_api/apps/monitor/scheduler.py index 458e9f6..6b49473 100644 --- a/spug_api/apps/monitor/scheduler.py +++ b/spug_api/apps/monitor/scheduler.py @@ -10,7 +10,6 @@ from django.utils.functional import SimpleLazyObject from django.db import close_old_connections from apps.monitor.models import Detection from apps.alarm.models import Alarm -from apps.monitor.executors import dispatch from apps.monitor.utils import seconds_to_human from apps.notify.models import Notify from django.conf import settings @@ -20,6 +19,8 @@ import logging import json import time +MONITOR_WORKER_KEY = settings.MONITOR_WORKER_KEY + class Scheduler: timezone = settings.TIME_ZONE @@ -28,7 +29,8 @@ class Scheduler: self.scheduler = BackgroundScheduler(timezone=self.timezone, executors={'default': ThreadPoolExecutor(30)}) self.scheduler.add_listener( self._handle_event, - EVENT_SCHEDULER_SHUTDOWN | EVENT_JOB_ERROR | EVENT_JOB_MAX_INSTANCES | EVENT_JOB_EXECUTED) + EVENT_SCHEDULER_SHUTDOWN | EVENT_JOB_ERROR | EVENT_JOB_MAX_INSTANCES + ) def _record_alarm(self, obj, status): duration = seconds_to_human(time.time() - obj.latest_fault_time) @@ -97,15 +99,22 @@ class Scheduler: obj.save() self._handle_notify(obj, is_notified, out) + def _dispatch(self, task_id, tp, targets, extra): + close_old_connections() + Detection.objects.filter(pk=task_id).update(latest_run_time=human_datetime()) + rds_cli = get_redis_connection() + for t in json.loads(targets): + rds_cli.rpush(MONITOR_WORKER_KEY, json.dumps([task_id, tp, t, extra])) + def _init(self): self.scheduler.start() for item in Detection.objects.filter(is_active=True): trigger = IntervalTrigger(minutes=int(item.rate), timezone=self.timezone) self.scheduler.add_job( - dispatch, + self._dispatch, trigger, id=str(item.id), - args=(item.type, item.addr, item.extra), + args=(item.id, item.type, item.targets, item.extra), ) def run(self): @@ -119,10 +128,10 @@ class Scheduler: if task.action in ('add', 'modify'): trigger = IntervalTrigger(minutes=int(task.rate), timezone=self.timezone) self.scheduler.add_job( - dispatch, + self._dispatch, trigger, id=str(task.id), - args=(task.type, task.addr, task.extra), + args=(task.id, task.type, task.targets, task.extra), replace_existing=True ) elif task.action == 'remove': diff --git a/spug_api/apps/monitor/views.py b/spug_api/apps/monitor/views.py index e9ba109..3f9674c 100644 --- a/spug_api/apps/monitor/views.py +++ b/spug_api/apps/monitor/views.py @@ -21,7 +21,7 @@ class DetectionView(View): Argument('id', type=int, required=False), Argument('name', help='请输入任务名称'), Argument('group', help='请选择任务分组'), - Argument('addr', help='请输入监控地址'), + Argument('targets', type=list, filter=lambda x: len(x), help='请输入监控地址'), Argument('type', filter=lambda x: x in dict(Detection.TYPES), help='请选择监控类型'), Argument('extra', required=False), Argument('desc', required=False), @@ -32,6 +32,7 @@ class DetectionView(View): Argument('notify_mode', type=list, help='请选择报警方式'), ).parse(request.body) if error is None: + form.targets = json.dumps(form.targets) form.notify_grp = json.dumps(form.notify_grp) form.notify_mode = json.dumps(form.notify_mode) if form.id: @@ -63,7 +64,7 @@ class DetectionView(View): if form.is_active: task = Detection.objects.filter(pk=form.id).first() message = {'id': form.id, 'action': 'add'} - message.update(task.to_dict(selects=('addr', 'extra', 'rate', 'type'))) + message.update(task.to_dict(selects=('targets', 'extra', 'rate', 'type'))) else: message = {'id': form.id, 'action': 'remove'} rds_cli = get_redis_connection() @@ -86,10 +87,10 @@ class DetectionView(View): def run_test(request): form, error = JsonParser( Argument('type', help='请选择监控类型'), - Argument('addr', help='请输入监控地址'), + Argument('targets', type=list, filter=lambda x: len(x), help='请输入监控地址'), Argument('extra', required=False) ).parse(request.body) if error is None: - is_success, message = dispatch(form.type, form.addr, form.extra, True) + is_success, message = dispatch(form.type, form.targets[0], form.extra) return json_response({'is_success': is_success, 'message': message}) return json_response(error=error) diff --git a/spug_web/src/pages/monitor/Step1.js b/spug_web/src/pages/monitor/Step1.js index b318f3c..85e7269 100644 --- a/spug_web/src/pages/monitor/Step1.js +++ b/spug_web/src/pages/monitor/Step1.js @@ -3,15 +3,16 @@ * Copyright (c) * Released under the AGPL-3.0 License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { observer } from 'mobx-react'; import { ExclamationCircleOutlined } from '@ant-design/icons'; -import { Modal, Form, Input, Select, Button } from 'antd'; +import { Modal, Form, Input, Select, Button, message } from 'antd'; import TemplateSelector from '../exec/task/TemplateSelector'; +import Selector from 'pages/host/Selector'; import { LinkButton, ACEditor } from 'components'; -import { http, cleanCommand, hasHostPermission } from 'libs'; +import { http, cleanCommand } from 'libs'; import store from './store'; -import hostStore from '../host/store'; +import lds from 'lodash'; const helpMap = { '1': '返回HTTP状态码200-399则判定为正常,其他为异常。', @@ -21,20 +22,12 @@ const helpMap = { export default observer(function () { const [loading, setLoading] = useState(false); const [showTmp, setShowTmp] = useState(false); - - useEffect(() => { - const { type, addr } = store.record; - if (type === '1' && addr) { - store.record.sitePrefix = addr.startsWith('http://') ? 'http://' : 'https://'; - store.record.domain = store.record.addr.replace(store.record.sitePrefix, '') - } - }, []) + const [showSelector, setShowSelector] = useState(false); function handleTest() { setLoading(true) - const { type, sitePrefix, domain } = store.record; - if (type === '1') store.record.addr = sitePrefix + domain; - http.post('/api/monitor/test/', store.record, { timeout: 120000 }) + const formData = lds.pick(store.record, ['type', 'targets', 'extra']) + http.post('/api/monitor/test/', formData, { timeout: 120000 }) .then(res => { if (res.is_success) { Modal.success({ content: res.message }) @@ -47,7 +40,7 @@ export default observer(function () { function handleChangeType(v) { store.record.type = v; - store.record.addr = undefined; + store.record.targets = []; store.record.extra = undefined; }; @@ -71,35 +64,30 @@ export default observer(function () { }) } - const SiteBefore = ( - - ) - function canNext() { - const { type, addr, extra, domain, group } = store.record; - if (type === '1') { - return name && domain && group - } else if (type === '5') { - return name && addr && group + const { type, targets, extra, group } = store.record; + const is_verify = name && group && targets.length; + if (['2', '3', '4'].includes(type)) { + return is_verify && extra } else { - return name && addr && extra && group + return is_verify } } function toNext() { + const {type, extra} = store.record; + if (!Number(extra) > 0) { + if (type === '1' && extra) return message.error('请输入正确的响应时间') + if (type === '2') return message.error('请输入正确的端口号') + } store.page += 1; - const { type, sitePrefix, domain } = store.record; - if (type === '1') store.record.addr = sitePrefix + domain; } function getStyle(t) { return t.includes(store.record.type) ? { display: 'flex' } : { display: 'none' } } - const { name, desc, type, addr, extra, domain, group } = store.record; + const { name, desc, type, targets, extra, group } = store.record; return (
@@ -114,7 +102,7 @@ export default observer(function () { - + store.record.name = e.target.value} placeholder="请输入监控名称" /> - store.record.domain = e.target.value} /> + store.record.addr = e.target.value} /> + option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} - onChange={v => store.record.addr = v}> - {hostStore.records.filter(x => x.id === Number(addr) || hasHostPermission(x.id)).map(item => ( - - {`${item.name}(${item.hostname}:${item.port})`} - - ))} - + {store.record.targets?.length > 0 && `已选择 ${store.record.targets.length} 台`} + + + + store.record.extra = e.target.value}/> store.record.extra = e.target.value} /> @@ -175,9 +161,15 @@ export default observer(function () { - + + Tips: 仅测试第一个监控地址 {showTmp && store.record.extra += v} onCancel={() => setShowTmp(false)} />} + setShowSelector(false)} + onOk={(_, ids) => store.record.targets = ids}/> ) }) \ No newline at end of file diff --git a/spug_web/src/pages/monitor/Step2.js b/spug_web/src/pages/monitor/Step2.js index b0bb04b..a8f96d2 100644 --- a/spug_web/src/pages/monitor/Step2.js +++ b/spug_web/src/pages/monitor/Step2.js @@ -34,7 +34,7 @@ export default observer(function () { function handleSubmit() { setLoading(true) const formData = form.getFieldsValue(); - Object.assign(formData, lds.pick(store.record, ['id', 'name', 'desc', 'addr', 'extra', 'type', 'group'])) + Object.assign(formData, lds.pick(store.record, ['id', 'name', 'desc', 'targets', 'extra', 'type', 'group'])) formData['id'] = store.record.id; http.post('/api/monitor/', formData) .then(() => { diff --git a/spug_web/src/pages/monitor/store.js b/spug_web/src/pages/monitor/store.js index 15746ce..39f8e3b 100644 --- a/spug_web/src/pages/monitor/store.js +++ b/spug_web/src/pages/monitor/store.js @@ -58,7 +58,7 @@ class Store { }; showForm = (info) => { - info = info || {type: '1', sitePrefix: 'http://'}; + info = info || {type: '1', targets: []}; this.page = 0; this.record = lds.cloneDeep(info); this.formVisible = true;