mirror of https://github.com/openspug/spug
improve host import
parent
e0e791b01c
commit
afe5d351ad
|
@ -14,7 +14,6 @@ from libs.ssh import SSH, AuthenticationException
|
||||||
from paramiko.ssh_exception import BadAuthenticationType
|
from paramiko.ssh_exception import BadAuthenticationType
|
||||||
from libs import AttrDict
|
from libs import AttrDict
|
||||||
from openpyxl import load_workbook
|
from openpyxl import load_workbook
|
||||||
import socket
|
|
||||||
|
|
||||||
|
|
||||||
class HostView(View):
|
class HostView(View):
|
||||||
|
@ -90,7 +89,7 @@ class HostView(View):
|
||||||
task = Task.objects.filter(targets__regex=fr'[^0-9]{form.id}[^0-9]').first()
|
task = Task.objects.filter(targets__regex=fr'[^0-9]{form.id}[^0-9]').first()
|
||||||
if task:
|
if task:
|
||||||
return json_response(error=f'任务计划中的任务【{task.name}】关联了该主机,请解除关联后再尝试删除该主机')
|
return json_response(error=f'任务计划中的任务【{task.name}】关联了该主机,请解除关联后再尝试删除该主机')
|
||||||
detection = Detection.objects.filter(type__in=('3', '4'), addr=form.id).first()
|
detection = Detection.objects.filter(type__in=('3', '4'), targets__regex=fr'[^0-9]{form.id}[^0-9]').first()
|
||||||
if detection:
|
if detection:
|
||||||
return json_response(error=f'监控中心的任务【{detection.name}】关联了该主机,请解除关联后再尝试删除该主机')
|
return json_response(error=f'监控中心的任务【{detection.name}】关联了该主机,请解除关联后再尝试删除该主机')
|
||||||
Host.objects.filter(pk=form.id).delete()
|
Host.objects.filter(pk=form.id).delete()
|
||||||
|
@ -99,6 +98,7 @@ class HostView(View):
|
||||||
|
|
||||||
def post_import(request):
|
def post_import(request):
|
||||||
password = request.POST.get('password')
|
password = request.POST.get('password')
|
||||||
|
group_id = request.POST.get('group_id')
|
||||||
file = request.FILES['file']
|
file = request.FILES['file']
|
||||||
ws = load_workbook(file, read_only=True)['Sheet1']
|
ws = load_workbook(file, read_only=True)['Sheet1']
|
||||||
summary = {'invalid': [], 'skip': [], 'repeat': [], 'success': []}
|
summary = {'invalid': [], 'skip': [], 'repeat': [], 'success': []}
|
||||||
|
@ -109,13 +109,12 @@ def post_import(request):
|
||||||
summary['invalid'].append(i)
|
summary['invalid'].append(i)
|
||||||
continue
|
continue
|
||||||
data = AttrDict(
|
data = AttrDict(
|
||||||
zone=row[0].value,
|
name=row[0].value,
|
||||||
name=row[1].value,
|
hostname=row[1].value,
|
||||||
hostname=row[2].value,
|
port=row[2].value,
|
||||||
port=row[3].value,
|
username=row[3].value,
|
||||||
username=row[4].value,
|
password=row[4].value,
|
||||||
password=row[5].value,
|
desc=row[5].value
|
||||||
desc=row[6].value
|
|
||||||
)
|
)
|
||||||
if Host.objects.filter(hostname=data.hostname, port=data.port, username=data.username).exists():
|
if Host.objects.filter(hostname=data.hostname, port=data.port, username=data.username).exists():
|
||||||
summary['skip'].append(i)
|
summary['skip'].append(i)
|
||||||
|
@ -130,6 +129,7 @@ def post_import(request):
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
host = Host.objects.create(created_by=request.user, **data)
|
host = Host.objects.create(created_by=request.user, **data)
|
||||||
|
host.groups.add(group_id)
|
||||||
if request.user.role:
|
if request.user.role:
|
||||||
request.user.role.add_host_perm(host.id)
|
request.user.role.add_host_perm(host.id)
|
||||||
summary['success'].append(i)
|
summary['success'].append(i)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { UploadOutlined } from '@ant-design/icons';
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
import { Modal, Form, Input, Upload, Button, Tooltip, Alert } from 'antd';
|
import { Modal, Form, Input, Upload, Button, Tooltip, Alert, Cascader, message } from 'antd';
|
||||||
import http from 'libs/http';
|
import http from 'libs/http';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
|
|
||||||
|
@ -14,11 +14,14 @@ export default observer(function () {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [fileList, setFileList] = useState([]);
|
const [fileList, setFileList] = useState([]);
|
||||||
|
const [groupId, setGroupId] = useState([]);
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
|
if (groupId.length === 0) return message.error('请选择要导入的分组');
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', fileList[0]);
|
formData.append('file', fileList[0]);
|
||||||
|
formData.append('group_id', groupId[groupId.length - 1]);
|
||||||
if (password) formData.append('password', password);
|
if (password) formData.append('password', password);
|
||||||
http.post('/api/host/import/', formData, {timeout: 120000})
|
http.post('/api/host/import/', formData, {timeout: 120000})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
|
@ -35,7 +38,11 @@ export default observer(function () {
|
||||||
{res['repeat'].length > 0 && <Form.Item style={{margin: 0, color: '#1890ff'}} label="重复主机名">
|
{res['repeat'].length > 0 && <Form.Item style={{margin: 0, color: '#1890ff'}} label="重复主机名">
|
||||||
<Tooltip title={`相关行:${res['repeat'].join(', ')}`}>{res['repeat'].length}</Tooltip>
|
<Tooltip title={`相关行:${res['repeat'].join(', ')}`}>{res['repeat'].length}</Tooltip>
|
||||||
</Form.Item>}
|
</Form.Item>}
|
||||||
</Form>
|
</Form>,
|
||||||
|
onOk: () => {
|
||||||
|
store.fetchRecords();
|
||||||
|
store.importVisible = false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
|
@ -52,7 +59,6 @@ export default observer(function () {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible
|
visible
|
||||||
width={800}
|
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
title="批量导入"
|
title="批量导入"
|
||||||
okText="导入"
|
okText="导入"
|
||||||
|
@ -60,13 +66,23 @@ export default observer(function () {
|
||||||
confirmLoading={loading}
|
confirmLoading={loading}
|
||||||
okButtonProps={{disabled: !fileList.length}}
|
okButtonProps={{disabled: !fileList.length}}
|
||||||
onOk={handleSubmit}>
|
onOk={handleSubmit}>
|
||||||
<Alert closable showIcon type="info" message={null}
|
<Alert
|
||||||
style={{width: 600, margin: '0 auto 20px', color: '#31708f !important'}}
|
showIcon
|
||||||
description="导入或输入的密码仅作首次验证使用,并不会存储密码。"/>
|
type="info"
|
||||||
|
style={{width: 365, margin: '0 auto 20px'}}
|
||||||
|
message="导入或输入的密码仅作首次验证使用,不会存储。"/>
|
||||||
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||||
<Form.Item label="模板下载" help="请下载使用该模板填充数据后导入">
|
<Form.Item label="模板下载" help="请下载使用该模板填充数据后导入">
|
||||||
<a href="/resource/主机导入模板.xlsx">主机导入模板.xlsx</a>
|
<a href="/resource/主机导入模板.xlsx">主机导入模板.xlsx</a>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item required label="选择分组">
|
||||||
|
<Cascader
|
||||||
|
value={groupId}
|
||||||
|
onChange={setGroupId}
|
||||||
|
options={store.treeData}
|
||||||
|
fieldNames={{label: 'title'}}
|
||||||
|
placeholder="请选择"/>
|
||||||
|
</Form.Item>
|
||||||
<Form.Item label="默认密码" help="如果excel中密码为空则使用该密码">
|
<Form.Item label="默认密码" help="如果excel中密码为空则使用该密码">
|
||||||
<Input
|
<Input
|
||||||
value={password}
|
value={password}
|
||||||
|
@ -74,11 +90,15 @@ export default observer(function () {
|
||||||
placeholder="请输入默认主机密码"/>
|
placeholder="请输入默认主机密码"/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item required label="导入数据">
|
<Form.Item required label="导入数据">
|
||||||
<Upload name="file" accept=".xls, .xlsx" fileList={fileList} beforeUpload={() => false}
|
<Upload
|
||||||
onChange={handleUpload}>
|
name="file"
|
||||||
<Button>
|
accept=".xls, .xlsx"
|
||||||
<UploadOutlined/> 点击上传
|
fileList={fileList}
|
||||||
</Button>
|
beforeUpload={() => false}
|
||||||
|
onChange={handleUpload}>
|
||||||
|
{fileList.length === 0 && (
|
||||||
|
<Button><UploadOutlined/> 点击上传</Button>
|
||||||
|
)}
|
||||||
</Upload>
|
</Upload>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { observable, computed } from 'mobx';
|
import { observable, computed } from 'mobx';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import http from 'libs/http';
|
import http from 'libs/http';
|
||||||
|
import lds from 'lodash';
|
||||||
|
|
||||||
class Store {
|
class Store {
|
||||||
counter = {};
|
counter = {};
|
||||||
|
@ -87,10 +88,11 @@ class Store {
|
||||||
|
|
||||||
refreshCounter = () => {
|
refreshCounter = () => {
|
||||||
if (this.treeData.length && this.records.length) {
|
if (this.treeData.length && this.records.length) {
|
||||||
for (let item of this.treeData) {
|
const treeData = lds.cloneDeep(this.treeData);
|
||||||
|
for (let item of treeData) {
|
||||||
this._refreshCounter(item)
|
this._refreshCounter(item)
|
||||||
}
|
}
|
||||||
this.treeData = [...this.treeData]
|
this.treeData = treeData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +103,7 @@ class Store {
|
||||||
item.all_host_ids = item.all_host_ids.concat(ids)
|
item.all_host_ids = item.all_host_ids.concat(ids)
|
||||||
}
|
}
|
||||||
item.all_host_ids = Array.from(new Set(item.all_host_ids));
|
item.all_host_ids = Array.from(new Set(item.all_host_ids));
|
||||||
|
if (this.group.key === item.key) this.group = item;
|
||||||
return item.all_host_ids
|
return item.all_host_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue