From ac30b34e54ed2bd7174317f1ddbdc421c4defd83 Mon Sep 17 00:00:00 2001 From: vapao Date: Fri, 15 May 2020 10:06:41 +0800 Subject: [PATCH] =?UTF-8?q?A=20=E6=B7=BB=E5=8A=A0=E4=B8=BB=E6=9C=BA?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spug_api/apps/host/urls.py | 1 + spug_api/apps/host/views.py | 35 +++++++- spug_api/requirements.txt | 1 + spug_web/public/resource/主机导入模板.xlsx | Bin 0 -> 8673 bytes spug_web/src/pages/host/Import.js | 92 +++++++++++++++++++++ spug_web/src/pages/host/Table.js | 2 + spug_web/src/pages/host/index.js | 2 + spug_web/src/pages/host/store.js | 1 + 8 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 spug_web/public/resource/主机导入模板.xlsx create mode 100644 spug_web/src/pages/host/Import.js diff --git a/spug_api/apps/host/urls.py b/spug_api/apps/host/urls.py index 7be76ea..2acdb2e 100644 --- a/spug_api/apps/host/urls.py +++ b/spug_api/apps/host/urls.py @@ -7,5 +7,6 @@ from .views import * urlpatterns = [ path('', HostView.as_view()), + path('import/', post_import), path('ssh//', web_ssh), ] diff --git a/spug_api/apps/host/views.py b/spug_api/apps/host/views.py index 83291fc..49b8d66 100644 --- a/spug_api/apps/host/views.py +++ b/spug_api/apps/host/views.py @@ -12,7 +12,8 @@ from apps.app.models import Deploy from apps.schedule.models import Task from apps.monitor.models import Detection from libs.ssh import SSH, AuthenticationException -from libs import human_datetime +from libs import human_datetime, AttrDict +from openpyxl import load_workbook class HostView(View): @@ -69,6 +70,38 @@ class HostView(View): return json_response(error=error) +def post_import(request): + password = request.POST.get('password') + file = request.FILES['file'] + ws = load_workbook(file, read_only=True)['Sheet1'] + summary = {'invalid': [], 'skip': [], 'fail': [], 'success': []} + for i, row in enumerate(ws.rows): + if i == 0: # 第1行是表头 略过 + continue + if not all([row[x].value for x in range(5)]): + 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 + ) + if Host.objects.filter(hostname=data.hostname, port=data.port, username=data.username, + deleted_by_id__isnull=True).exists(): + summary['skip'].append(i) + continue + if valid_ssh(data.hostname, data.port, data.username, data.pop('password') or password) is False: + summary['fail'].append(i) + continue + Host.objects.create(created_by=request.user, **data) + summary['success'].append(i) + return json_response(summary) + + def web_ssh(request, h_id): host = Host.objects.filter(pk=h_id).first() if not host: diff --git a/spug_api/requirements.txt b/spug_api/requirements.txt index 1532c93..4134d20 100644 --- a/spug_api/requirements.txt +++ b/spug_api/requirements.txt @@ -7,3 +7,4 @@ django-redis==4.10.0 requests==2.22.0 GitPython==3.0.8 python-ldap==3.2.0 +openpyxl==3.0.3 \ No newline at end of file diff --git a/spug_web/public/resource/主机导入模板.xlsx b/spug_web/public/resource/主机导入模板.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1c3c1dd3bff5d85df5039fee4da4c20890382c15 GIT binary patch literal 8673 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 literal 0 HcmV?d00001 diff --git a/spug_web/src/pages/host/Import.js b/spug_web/src/pages/host/Import.js new file mode 100644 index 0000000..0c5f718 --- /dev/null +++ b/spug_web/src/pages/host/Import.js @@ -0,0 +1,92 @@ +/** + * Copyright (c) OpenSpug Organization. https://github.com/openspug/spug + * Copyright (c) + * Released under the MIT License. + */ +import React from 'react'; +import { observer } from 'mobx-react'; +import { Modal, Form, Input, Upload, Icon, Button, Tooltip, Alert } from 'antd'; +import http from 'libs/http'; +import store from './store'; + +@observer +class ComImport extends React.Component { + constructor(props) { + super(props); + this.state = { + loading: false, + password: null, + fileList: [], + } + } + + handleSubmit = () => { + this.setState({loading: true}); + const formData = new FormData(); + formData.append('file', this.state.fileList[0]); + formData.append('password', this.state.password); + http.post('/api/host/import/', formData) + .then(res => { + Modal.info({ + title: '导入结果', + content:
+ {res.success.length} + {res['fail'].length > 0 && + {res['fail'].length} + } + {res['skip'].length > 0 && + {res['skip'].length} + } + {res['invalid'].length > 0 && + {res['invalid'].length} + } +
+ }) + }) + .finally(() => this.setState({loading: false})) + }; + + beforeUpload = (file) => { + this.setState({fileList: [file]}); + return false + }; + + render() { + return ( + store.importVisible = false} + confirmLoading={this.state.loading} + okButtonProps={{disabled: !this.state.fileList.length}} + onOk={this.handleSubmit}> + +
+ + 主机导入模板.xlsx + + + this.setState({password: e.target.value})} + placeholder="请输入默认主机密码"/> + + + + + + +
+
+ ) + } +} + +export default ComImport diff --git a/spug_web/src/pages/host/Table.js b/spug_web/src/pages/host/Table.js index 5543c5b..0289dbb 100644 --- a/spug_web/src/pages/host/Table.js +++ b/spug_web/src/pages/host/Table.js @@ -8,6 +8,7 @@ import { observer } from 'mobx-react'; import { Table, Divider, Modal, message } from 'antd'; import { LinkButton } from 'components'; import ComForm from './Form'; +import ComImport from './Import'; import http from 'libs/http'; import store from './store'; @@ -85,6 +86,7 @@ class ComTable extends React.Component { {store.formVisible && } + {store.importVisible && } ) } diff --git a/spug_web/src/pages/host/index.js b/spug_web/src/pages/host/index.js index 5f13a3a..536745e 100644 --- a/spug_web/src/pages/host/index.js +++ b/spug_web/src/pages/host/index.js @@ -33,6 +33,8 @@ export default observer(function () { + diff --git a/spug_web/src/pages/host/store.js b/spug_web/src/pages/host/store.js index 9bb731c..88a4273 100644 --- a/spug_web/src/pages/host/store.js +++ b/spug_web/src/pages/host/store.js @@ -13,6 +13,7 @@ class Store { @observable idMap = {}; @observable isFetching = false; @observable formVisible = false; + @observable importVisible = false; @observable f_name; @observable f_zone;