A 监控中心 增加分组

pull/289/head
zyupo 2020-12-02 18:59:35 +08:00
parent 8bf264bcd2
commit d11ee1258d
8 changed files with 80 additions and 31 deletions

View File

@ -21,6 +21,7 @@ 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)
extra = models.TextField(null=True)
desc = models.CharField(max_length=255, null=True)

View File

@ -13,12 +13,14 @@ import json
class DetectionView(View):
def get(self, request):
detections = Detection.objects.all()
return json_response(detections)
groups = [x['group'] for x in detections.order_by('group').values('group').distinct()]
return json_response({'groups': groups, 'detections': [x.to_dict() for x in detections]})
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
Argument('name', help='请输入任务名称'),
Argument('group', help='请选择任务分组'),
Argument('addr', help='请输入监控地址'),
Argument('type', filter=lambda x: x in dict(Detection.TYPES), help='请选择监控类型'),
Argument('extra', required=False),

1
spug_web/.gitignore vendored
View File

@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
/.idea/
# testing
/coverage

View File

@ -5,6 +5,7 @@
*/
import React, { useState, useEffect } from 'react';
import { observer } from 'mobx-react';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Modal, Form, Input, Select, Button } from 'antd';
import TemplateSelector from '../exec/task/TemplateSelector';
import { LinkButton, ACEditor } from 'components';
@ -22,7 +23,7 @@ export default observer(function () {
const [showTmp, setShowTmp] = useState(false);
useEffect(() => {
const {type, addr} = store.record;
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, '')
@ -31,14 +32,14 @@ export default observer(function () {
function handleTest() {
setLoading(true)
const {type, sitePrefix, domain} = store.record;
const { type, sitePrefix, domain } = store.record;
if (type === '1') store.record.addr = sitePrefix + domain;
http.post('/api/monitor/test/', store.record, {timeout: 120000})
http.post('/api/monitor/test/', store.record, { timeout: 120000 })
.then(res => {
if (res.is_success) {
Modal.success({content: res.message})
Modal.success({ content: res.message })
} else {
Modal.warning({content: res.message})
Modal.warning({ content: res.message })
}
})
.finally(() => setLoading(false))
@ -48,39 +49,59 @@ export default observer(function () {
store.record.type = v;
store.record.addr = undefined;
store.record.extra = undefined;
};
function handleAddGroup() {
Modal.confirm({
icon: <ExclamationCircleOutlined />,
title: '添加监控分组',
content: (
<Form layout="vertical" style={{ marginTop: 24 }}>
<Form.Item required label="监控分组">
<Input onChange={e => store.record.group = e.target.value} />
</Form.Item>
</Form>
),
onOk: () => {
if (store.record.group) {
store.groups.push(store.record.group);
}
},
})
}
const SiteBefore = (
<Select style={{width: 90}} value={store.record.sitePrefix} onChange={v => store.record.sitePrefix = v}>
<Select style={{ width: 90 }} value={store.record.sitePrefix} onChange={v => store.record.sitePrefix = v}>
<Select.Option value="http://">http://</Select.Option>
<Select.Option value="https://">https://</Select.Option>
</Select>
)
function canNext() {
const {type, addr, extra, domain} = store.record;
const { type, addr, extra, domain, group } = store.record;
if (type === '1') {
return name && domain
return name && domain && group
} else if (type === '5') {
return name && addr
return name && addr && group
} else {
return name && addr && extra
return name && addr && extra && group
}
}
function toNext() {
store.page += 1;
const {type, sitePrefix, domain} = store.record;
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'}
return t.includes(store.record.type) ? { display: 'flex' } : { display: 'none' }
}
const {name, desc, type, addr, extra, domain} = store.record;
const { name, desc, type, addr, extra, domain, group } = store.record;
return (
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
<Form labelCol={{ span: 6 }} wrapperCol={{ span: 14 }}>
<Form.Item label="监控类型" help={helpMap[type]}>
<Select placeholder="请选择监控类型" value={type} onChange={handleChangeType}>
<Select.Option value="1">站点检测</Select.Option>
@ -90,18 +111,30 @@ export default observer(function () {
<Select.Option value="4">自定义脚本</Select.Option>
</Select>
</Form.Item>
<Form.Item required label="任务名称">
<Input value={name} onChange={e => store.record.name = e.target.value} placeholder="请输入任务名称"/>
<Form.Item required label="监控分组" style={{ marginBottom: 0 }}>
<Form.Item style={{ display: 'inline-block', width: 'calc(75%)', marginRight: 8 }}>
<Select value={group} placeholder="请选择监控分组" onChange={v => store.record.group = v}>
{store.groups.map(item => (
<Select.Option value={item} key={item}>{item}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item style={{ display: 'inline-block', width: 'calc(25%-8px)' }}>
<Button type="link" onClick={handleAddGroup}>添加分组</Button>
</Form.Item>
</Form.Item>
<Form.Item required label="监控名称">
<Input value={name} onChange={e => store.record.name = e.target.value} placeholder="请输入监控名称" />
</Form.Item>
<Form.Item required label="监控地址" style={getStyle(['1'])}>
<Input
value={domain}
addonBefore={SiteBefore}
placeholder="请输入监控地址"
onChange={e => store.record.domain = e.target.value}/>
onChange={e => store.record.domain = e.target.value} />
</Form.Item>
<Form.Item required label="监控地址" style={getStyle(['2', '5'])}>
<Input value={addr} placeholder="请输入监控地址IP/域名)" onChange={e => store.record.addr = e.target.value}/>
<Input value={addr} placeholder="请输入监控地址IP/域名)" onChange={e => store.record.addr = e.target.value} />
</Form.Item>
<Form.Item required label="监控主机" style={getStyle(['3', '4'])}>
<Select
@ -119,10 +152,10 @@ export default observer(function () {
</Select>
</Form.Item>
<Form.Item required label="检测端口" style={getStyle(['2'])}>
<Input value={extra} placeholder="请输入端口号" onChange={e => store.record.extra = e.target.value}/>
<Input value={extra} placeholder="请输入端口号" onChange={e => store.record.extra = e.target.value} />
</Form.Item>
<Form.Item required label="进程名称" help="执行 ps -ef 看到的进程名称。" style={getStyle(['3'])}>
<Input value={extra} placeholder="请输入进程名称" onChange={e => store.record.extra = e.target.value}/>
<Input value={extra} placeholder="请输入进程名称" onChange={e => store.record.extra = e.target.value} />
</Form.Item>
<Form.Item
required
@ -134,17 +167,17 @@ export default observer(function () {
value={extra || ''}
width="100%"
height="200px"
onChange={e => store.record.extra = cleanCommand(e)}/>
onChange={e => store.record.extra = cleanCommand(e)} />
</Form.Item>
<Form.Item label="备注信息">
<Input.TextArea value={desc} onChange={e => store.record.desc = e.target.value} placeholder="请输入备注信息"/>
<Input.TextArea value={desc} onChange={e => store.record.desc = e.target.value} placeholder="请输入备注信息" />
</Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}} style={{marginTop: 12}}>
<Form.Item wrapperCol={{ span: 14, offset: 6 }} style={{ marginTop: 12 }}>
<Button disabled={!canNext()} type="primary" onClick={toNext}>下一步</Button>
<Button disabled={false} type="link" loading={loading} onClick={handleTest}>执行测试</Button>
</Form.Item>
{showTmp && <TemplateSelector onOk={v => store.record.extra += v} onCancel={() => setShowTmp(false)}/>}
{showTmp && <TemplateSelector onOk={v => store.record.extra += v} onCancel={() => setShowTmp(false)} />}
</Form>
)
})

View File

@ -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']))
Object.assign(formData, lds.pick(store.record, ['id', 'name', 'desc', 'addr', 'extra', 'type', 'group']))
formData['id'] = store.record.id;
http.post('/api/monitor/', formData)
.then(() => {

View File

@ -96,7 +96,8 @@ class ComTable extends React.Component {
showTotal: total => `${total}`,
pageSizeOptions: ['10', '20', '50', '100']
}}>
<Table.Column title="任务名称" dataIndex="name"/>
<Table.Column title="监控分组" dataIndex="group" />
<Table.Column title="监控名称" dataIndex="name"/>
<Table.Column title="类型" dataIndex="type_alias"/>
<Table.Column ellipsis title="地址" render={info => {
if ('34'.includes(info.type)) {

View File

@ -19,7 +19,14 @@ export default observer(function () {
<Breadcrumb.Item>监控中心</Breadcrumb.Item>
</Breadcrumb>
<SearchForm>
<SearchForm.Item span={7} title="任务名称">
<SearchForm.Item span={7} title="监控分组">
<Select allowClear value={store.f_group} onChange={v => store.f_group = v} placeholder="请选择">
{store.groups.map(item => (
<Select.Option value={item} key={item}>{item}</Select.Option>
))}
</Select>
</SearchForm.Item>
<SearchForm.Item span={7} title="监控名称">
<Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
</SearchForm.Item>
<SearchForm.Item span={7} title="检测类型">

View File

@ -12,6 +12,7 @@ class Store {
@observable records = [];
@observable record = {};
@observable types = [];
@observable groups = [];
@observable page = 0;
@observable isFetching = false;
@observable formVisible = false;
@ -20,12 +21,14 @@ class Store {
@observable f_type;
@observable f_status;
@observable f_active = '';
@observable f_group;
@computed get dataSource() {
let records = this.records;
if (this.f_active) records = records.filter(x => x.is_active === (this.f_active === '1'));
if (this.f_name) records = records.filter(x => x.name.toLowerCase().includes(this.f_name.toLowerCase()));
if (this.f_type) records = records.filter(x => x.type_alias === this.f_type);
if (this.f_group) records = records.filter(x => x.group === this.f_group);
if (this.f_status !== undefined) {
if (this.f_status === -1) {
records = records.filter(x => x.is_active && !x.latest_status_alias);
@ -39,16 +42,17 @@ class Store {
fetchRecords = () => {
this.isFetching = true;
http.get('/api/monitor/')
.then(res => {
.then(({groups, detections}) => {
const tmp = new Set();
res.map(item => {
detections.map(item => {
tmp.add(item['type_alias']);
const value = item['latest_run_time'];
item['latest_run_time_alias'] = value ? moment(value).fromNow() : null;
return null
});
this.types = Array.from(tmp);
this.records = res
this.records = detections;
this.groups = groups;
})
.finally(() => this.isFetching = false)
};