mirror of https://github.com/openspug/spug
A 监控中心新增Ping检测 #195
parent
2ad8356c0a
commit
62386f98d8
|
@ -3,6 +3,8 @@
|
|||
# Released under the AGPL-3.0 License.
|
||||
from apps.host.models import Host
|
||||
from socket import socket
|
||||
import subprocess
|
||||
import platform
|
||||
import requests
|
||||
import logging
|
||||
|
||||
|
@ -22,11 +24,27 @@ def port_check(addr, port):
|
|||
sock = socket()
|
||||
sock.settimeout(5)
|
||||
sock.connect((addr, int(port)))
|
||||
sock.close()
|
||||
return True, '端口状态检测正常'
|
||||
except Exception as e:
|
||||
return False, f'异常信息:{e}'
|
||||
|
||||
|
||||
def ping_check(addr):
|
||||
try:
|
||||
if platform.system().lower() == 'windows':
|
||||
command = f'ping -n 1 -w 3000 {addr}'
|
||||
else:
|
||||
command = f'ping -c 1 -t 3 {addr}'
|
||||
task = subprocess.run(command, shell=True, stdout=subprocess.PIPE)
|
||||
if task.returncode == 0:
|
||||
return True, 'Ping检测正常'
|
||||
else:
|
||||
return False, 'Ping检测失败'
|
||||
except Exception as e:
|
||||
return False, f'异常信息:{e}'
|
||||
|
||||
|
||||
def host_executor(host, command):
|
||||
try:
|
||||
cli = host.get_ssh()
|
||||
|
@ -44,6 +62,8 @@ def dispatch(tp, addr, extra):
|
|||
return site_check(addr)
|
||||
elif tp == '2':
|
||||
return port_check(addr, extra)
|
||||
elif tp == '5':
|
||||
return ping_check(addr)
|
||||
elif tp == '3':
|
||||
command = f'ps -ef|grep -v grep|grep {extra!r}'
|
||||
elif tp == '4':
|
||||
|
|
|
@ -13,6 +13,7 @@ class Detection(models.Model, ModelMixin):
|
|||
('2', '端口检测'),
|
||||
('3', '进程检测'),
|
||||
('4', '自定义脚本'),
|
||||
('5', 'Ping检测'),
|
||||
)
|
||||
STATUS = (
|
||||
(0, '成功'),
|
||||
|
|
|
@ -27,7 +27,7 @@ class Scheduler:
|
|||
timezone = settings.TIME_ZONE
|
||||
|
||||
def __init__(self):
|
||||
self.scheduler = BackgroundScheduler(timezone=self.timezone, executors={'default': ThreadPoolExecutor(20)})
|
||||
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)
|
||||
|
@ -61,7 +61,7 @@ class Scheduler:
|
|||
if obj.latest_status == 0:
|
||||
if is_notified:
|
||||
self._record_alarm(obj, '2')
|
||||
logger.info(f'{human_datetime()} recover job_id: {obj.id}')
|
||||
logger.info(f'{human_datetime()} recover job_id: {obj.id}, job_name: {obj.name}')
|
||||
self._do_notify('2', obj, out)
|
||||
else:
|
||||
if obj.fault_times >= obj.threshold:
|
||||
|
@ -69,7 +69,7 @@ class Scheduler:
|
|||
obj.latest_notify_time = int(time.time())
|
||||
obj.save()
|
||||
self._record_alarm(obj, '1')
|
||||
logger.info(f'{human_datetime()} notify job_id: {obj.id}')
|
||||
logger.info(f'{human_datetime()} notify job_id: {obj.id}, job_name: {obj.name}')
|
||||
self._do_notify('1', obj, out)
|
||||
|
||||
def _handle_event(self, event):
|
||||
|
|
|
@ -39,7 +39,7 @@ class Scheduler:
|
|||
}
|
||||
|
||||
def __init__(self):
|
||||
self.scheduler = BackgroundScheduler(timezone=self.timezone, executors={'default': ThreadPoolExecutor(20)})
|
||||
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)
|
||||
|
|
|
@ -13,56 +13,98 @@ import store from './store';
|
|||
import hostStore from '../host/store';
|
||||
import groupStore from '../alarm/group/store';
|
||||
import styles from './index.module.css';
|
||||
import lds from 'lodash';
|
||||
|
||||
@observer
|
||||
class ComForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.fieldMap = {
|
||||
'1': ['domain'],
|
||||
'2': ['addr', 'port'],
|
||||
'3': ['host', 'process'],
|
||||
'4': ['host', 'command'],
|
||||
'5': ['addr'],
|
||||
}
|
||||
this.modeOptions = [
|
||||
{label: '微信', 'value': '1'},
|
||||
{label: '短信', 'value': '2', disabled: true},
|
||||
{label: '钉钉', 'value': '3'},
|
||||
{label: '邮件', 'value': '4'},
|
||||
{label: '企业微信', 'value': '5'},
|
||||
]
|
||||
this.helpMap = {
|
||||
'1': '返回HTTP状态码200-399则判定为正常,其他为异常。',
|
||||
'4': '脚本执行退出状态码为 0 则判定为正常,其他为异常。'
|
||||
}
|
||||
this.state = {
|
||||
loading: false,
|
||||
sitePrefix: 'http://',
|
||||
extra: {[store.record.type]: store.record.extra},
|
||||
addr: {},
|
||||
domain: undefined,
|
||||
addr: undefined,
|
||||
port: undefined,
|
||||
host: undefined,
|
||||
process: undefined,
|
||||
command: undefined,
|
||||
showTmp: false,
|
||||
page: 0,
|
||||
modeOptions: [
|
||||
{label: '微信', 'value': '1'},
|
||||
{label: '短信', 'value': '2', disabled: true},
|
||||
{label: '钉钉', 'value': '3'},
|
||||
{label: '邮件', 'value': '4'},
|
||||
{label: '企业微信', 'value': '5'},
|
||||
],
|
||||
helpMap: {
|
||||
'1': '返回HTTP状态码200-399则判定为正常,其他为异常。',
|
||||
'4': '脚本执行退出状态码为 0 则判定为正常,其他为异常。'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let [sitePrefix, value] = ['http://', ''];
|
||||
if (store.record.type === '1') {
|
||||
if (store.record.addr.includes('http://')) {
|
||||
value = store.record.addr.replace('http://', '')
|
||||
} else {
|
||||
sitePrefix = 'https://';
|
||||
value = store.record.addr.replace('https://', '')
|
||||
}
|
||||
this.setState({sitePrefix, addr: {'1': value}})
|
||||
} else if ('34'.includes(store.record.type)) {
|
||||
this.setState({addr: {'3': store.record.addr, '4': store.record.addr}})
|
||||
} else {
|
||||
this.setState({addr: {[store.record.type]: store.record.addr}})
|
||||
const {type, addr, extra} = store.record;
|
||||
switch (type) {
|
||||
case '1':
|
||||
if (addr.startsWith('http://')) {
|
||||
this.setState({sitePrefix: 'http://', domain: addr.replace('http://', '')})
|
||||
} else {
|
||||
this.setState({sitePrefix: 'https://', domain: addr.replace('https://', '')})
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
this.setState({addr, port: extra});
|
||||
break;
|
||||
case '3':
|
||||
this.setState({host: addr, process: extra});
|
||||
break;
|
||||
case '4':
|
||||
this.setState({host: addr, command: extra});
|
||||
break;
|
||||
case '5':
|
||||
this.setState({addr});
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
this.setState({loading: true});
|
||||
const {sitePrefix, domain, addr, host, port, command, process} = this.state;
|
||||
const formData = this.props.form.getFieldsValue();
|
||||
const type = formData['type'];
|
||||
formData['id'] = store.record.id;
|
||||
formData['extra'] = this.state.extra[type];
|
||||
formData['addr'] = type === '1' ? this.state.sitePrefix + this.state.addr[type] : this.state.addr[type];
|
||||
switch (type) {
|
||||
case '1':
|
||||
formData['addr'] = sitePrefix + domain;
|
||||
break;
|
||||
case '2':
|
||||
formData['addr'] = addr;
|
||||
formData['extra'] = port;
|
||||
break;
|
||||
case '3':
|
||||
formData['addr'] = host;
|
||||
formData['extra'] = process
|
||||
break;
|
||||
case '4':
|
||||
formData['addr'] = host;
|
||||
formData['extra'] = command;
|
||||
break;
|
||||
case '5':
|
||||
formData['addr'] = addr;
|
||||
break;
|
||||
default:
|
||||
throw Error('unknown type')
|
||||
}
|
||||
http.post('/api/monitor/', formData)
|
||||
.then(() => {
|
||||
message.success('操作成功');
|
||||
|
@ -73,21 +115,12 @@ class ComForm extends React.Component {
|
|||
|
||||
getStyle = (t) => {
|
||||
const type = this.props.form.getFieldValue('type');
|
||||
return t.indexOf(type) !== -1 ? {display: 'block'} : {display: 'none'}
|
||||
return this.fieldMap[type].includes(t) ? {display: 'block'} : {display: 'none'}
|
||||
};
|
||||
|
||||
handleExtra = (t, e) => {
|
||||
const value = t === '4' ? e : e.target.value;
|
||||
this.setState({extra: Object.assign({}, this.state.extra, {[t]: value})})
|
||||
};
|
||||
|
||||
handleAddr = (t, e) => {
|
||||
if (t === '3') {
|
||||
this.setState({addr: Object.assign({}, this.state.addr, {'3': e, '4': e})})
|
||||
} else {
|
||||
this.setState({addr: Object.assign({}, this.state.addr, {[t]: e.target.value})})
|
||||
}
|
||||
};
|
||||
handleInput = (key, value) => {
|
||||
this.setState({[key]: value})
|
||||
}
|
||||
|
||||
siteBefore = () => (
|
||||
<Select style={{width: 90}} value={this.state.sitePrefix} onChange={v => this.setState({sitePrefix: v})}>
|
||||
|
@ -99,17 +132,15 @@ class ComForm extends React.Component {
|
|||
verifyButtonStatus = () => {
|
||||
const data = this.props.form.getFieldsValue();
|
||||
const {notify_grp, notify_mode, type, name} = data;
|
||||
let b1 = this.state.addr[type] && name;
|
||||
if (type !== '1') {
|
||||
b1 = b1 && this.state.extra[type]
|
||||
}
|
||||
const fields = Object.values(lds.pick(this.state, this.fieldMap[type])).filter(x => x)
|
||||
const b1 = name && fields.length === this.fieldMap[type].length
|
||||
const b2 = notify_grp && notify_grp.length && notify_mode && notify_mode.length;
|
||||
return [b1, b2];
|
||||
};
|
||||
|
||||
render() {
|
||||
const info = store.record;
|
||||
const {loading, extra, addr, showTmp, page, modeOptions, helpMap} = this.state;
|
||||
const {loading, domain, host, port, process, command, addr, showTmp, page} = this.state;
|
||||
const {getFieldDecorator, getFieldValue} = this.props.form;
|
||||
const [b1, b2] = this.verifyButtonStatus();
|
||||
return (
|
||||
|
@ -126,11 +157,12 @@ class ComForm extends React.Component {
|
|||
</Steps>
|
||||
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||
<div style={{display: page === 0 ? 'block' : 'none'}}>
|
||||
<Form.Item label="监控类型" help={helpMap[getFieldValue('type')]}>
|
||||
<Form.Item label="监控类型" help={this.helpMap[getFieldValue('type') || '1']}>
|
||||
{getFieldDecorator('type', {initialValue: info['type'] || '1'})(
|
||||
<Select placeholder="请选择监控类型">
|
||||
<Select.Option value="1">站点检测</Select.Option>
|
||||
<Select.Option value="2">端口检测</Select.Option>
|
||||
<Select.Option value="5">Ping检测</Select.Option>
|
||||
<Select.Option value="3">进程检测</Select.Option>
|
||||
<Select.Option value="4">自定义脚本</Select.Option>
|
||||
</Select>
|
||||
|
@ -141,45 +173,46 @@ class ComForm extends React.Component {
|
|||
<Input placeholder="请输入任务名称"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控地址" style={this.getStyle('1')}>
|
||||
<Input value={addr['1']} addonBefore={this.siteBefore()} placeholder="请输入监控地址"
|
||||
onChange={e => this.handleAddr('1', e)}/>
|
||||
<Form.Item required label="监控地址" style={this.getStyle('domain')}>
|
||||
<Input value={domain} addonBefore={this.siteBefore()} placeholder="请输入监控地址"
|
||||
onChange={e => this.handleInput('domain', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控地址" style={this.getStyle('2')}>
|
||||
<Input value={addr['2']} placeholder="请输入监控地址(IP/域名)" onChange={e => this.handleAddr('2', e)}/>
|
||||
<Form.Item required label="监控地址" style={this.getStyle('addr')}>
|
||||
<Input value={addr} placeholder="请输入监控地址(IP/域名)"
|
||||
onChange={e => this.handleInput('addr', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控主机" style={this.getStyle('34')}>
|
||||
<Form.Item required label="监控主机" style={this.getStyle('host')}>
|
||||
<Select
|
||||
showSearch
|
||||
value={addr['3']}
|
||||
value={host}
|
||||
placeholder="请选择主机"
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
|
||||
onChange={v => this.handleAddr('3', v)}>
|
||||
{hostStore.records.filter(x => x.id === Number(addr['3']) || hasHostPermission(x.id)).map(item => (
|
||||
onChange={v => this.handleInput('host', v)}>
|
||||
{hostStore.records.filter(x => x.id === Number(host) || hasHostPermission(x.id)).map(item => (
|
||||
<Select.Option value={String(item.id)} key={item.id}>
|
||||
{`${item.name}(${item.hostname}:${item.port})`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item required label="检测端口" style={this.getStyle('2')}>
|
||||
<Input value={extra['2']} placeholder="请输入端口号" onChange={e => this.handleExtra('2', e)}/>
|
||||
<Form.Item required label="检测端口" style={this.getStyle('port')}>
|
||||
<Input value={port} placeholder="请输入端口号" onChange={e => this.handleInput('port', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="进程名称" style={this.getStyle('3')} help="执行 ps -ef 看到的进程名称。">
|
||||
<Input value={extra['3']} placeholder="请输入进程名称" onChange={e => this.handleExtra('3', e)}/>
|
||||
<Form.Item required label="进程名称" style={this.getStyle('process')} help="执行 ps -ef 看到的进程名称。">
|
||||
<Input value={process} placeholder="请输入进程名称" onChange={e => this.handleInput('process', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
required
|
||||
label="脚本内容"
|
||||
style={this.getStyle('4')}
|
||||
style={this.getStyle('command')}
|
||||
extra={<LinkButton onClick={() => this.setState({showTmp: true})}>从模板添加</LinkButton>}>
|
||||
<ACEditor
|
||||
mode="sh"
|
||||
value={extra['4']}
|
||||
value={command}
|
||||
width="100%"
|
||||
height="200px"
|
||||
onChange={e => this.handleExtra('4', cleanCommand(e))}/>
|
||||
onChange={e => this.handleInput('command', cleanCommand(e))}/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注信息">
|
||||
{getFieldDecorator('desc', {initialValue: info['desc']})(
|
||||
|
@ -223,7 +256,7 @@ class ComForm extends React.Component {
|
|||
</Form.Item>
|
||||
<Form.Item required label="报警方式">
|
||||
{getFieldDecorator('notify_mode', {initialValue: info['notify_mode']})(
|
||||
<Checkbox.Group options={modeOptions}/>
|
||||
<Checkbox.Group options={this.modeOptions}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="通道沉默" help="相同的告警信息,沉默期内只发送一次。">
|
||||
|
@ -252,7 +285,7 @@ class ComForm extends React.Component {
|
|||
</Form.Item>
|
||||
</Form>
|
||||
{showTmp && <TemplateSelector
|
||||
onOk={v => this.handleExtra('4', extra['4'] + v)}
|
||||
onOk={v => this.handleInput('command', command + v)}
|
||||
onCancel={() => this.setState({showTmp: false})}/>}
|
||||
</Modal>
|
||||
)
|
||||
|
|
|
@ -42,11 +42,6 @@ class ComTable extends React.Component {
|
|||
};
|
||||
|
||||
columns = [{
|
||||
title: '序号',
|
||||
key: 'series',
|
||||
render: (_, __, index) => index + 1,
|
||||
width: 80
|
||||
}, {
|
||||
title: '任务名称',
|
||||
dataIndex: 'name',
|
||||
}, {
|
||||
|
|
Loading…
Reference in New Issue