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