diff --git a/spug_api/apps/exec/executors.py b/spug_api/apps/exec/executors.py
index 7a0d0b8..0122dcd 100644
--- a/spug_api/apps/exec/executors.py
+++ b/spug_api/apps/exec/executors.py
@@ -14,7 +14,7 @@ def exec_worker_handler(job):
class Job:
- def __init__(self, key, name, hostname, port, username, pkey, command, interpreter, token=None):
+ def __init__(self, key, name, hostname, port, username, pkey, command, interpreter, params=None, token=None):
self.ssh = SSH(hostname, port, username, pkey)
self.key = key
self.command = self._handle_command(command, interpreter)
@@ -28,6 +28,8 @@ class Job:
SPUG_SSH_USERNAME=username,
SPUG_INTERPRETER=interpreter
)
+ if isinstance(params, dict):
+ self.env.update({f'_SPUG_{k}': str(v) for k, v in params.items()})
def _send(self, message, with_expire=False):
if self.rds_cli is None:
diff --git a/spug_api/apps/exec/models.py b/spug_api/apps/exec/models.py
index f3cb77d..d084d52 100644
--- a/spug_api/apps/exec/models.py
+++ b/spug_api/apps/exec/models.py
@@ -14,7 +14,7 @@ class ExecTemplate(models.Model, ModelMixin):
interpreter = models.CharField(max_length=20, default='sh')
host_ids = models.TextField(default='[]')
desc = models.CharField(max_length=255, null=True)
-
+ parameters = models.TextField(default='[]')
created_at = models.CharField(max_length=20, default=human_datetime)
created_by = models.ForeignKey(User, models.PROTECT, related_name='+')
updated_at = models.CharField(max_length=20, null=True)
@@ -26,6 +26,7 @@ class ExecTemplate(models.Model, ModelMixin):
def to_view(self):
tmp = self.to_dict()
tmp['host_ids'] = json.loads(self.host_ids)
+ tmp['parameters'] = json.loads(self.parameters)
return tmp
class Meta:
@@ -45,8 +46,11 @@ class ExecHistory(models.Model, ModelMixin):
def to_view(self):
tmp = self.to_dict()
tmp['host_ids'] = json.loads(self.host_ids)
- if hasattr(self, 'template_name'):
- tmp['template_name'] = self.template_name
+ if self.template:
+ tmp['template_name'] = self.template.name
+ tmp['interpreter'] = self.template.interpreter
+ tmp['parameters'] = json.loads(self.template.parameters)
+ tmp['command'] = self.template.body
return tmp
class Meta:
diff --git a/spug_api/apps/exec/views.py b/spug_api/apps/exec/views.py
index ba16175..06a8cf4 100644
--- a/spug_api/apps/exec/views.py
+++ b/spug_api/apps/exec/views.py
@@ -31,6 +31,7 @@ class TemplateView(View):
Argument('body', help='请输入模版内容'),
Argument('interpreter', default='sh'),
Argument('host_ids', type=list, handler=json.dumps, default=[]),
+ Argument('parameters', type=list, handler=json.dumps, default=[]),
Argument('desc', required=False)
).parse(request.body)
if error is None:
@@ -59,7 +60,8 @@ def do_task(request):
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择执行主机'),
Argument('command', help='请输入执行命令内容'),
Argument('interpreter', default='sh'),
- Argument('template_id', type=int, required=False)
+ Argument('template_id', type=int, required=False),
+ Argument('params', type=dict, required=False)
).parse(request.body)
if error is None:
if not has_host_perm(request.user, form.host_ids):
@@ -76,6 +78,7 @@ def do_task(request):
username=host.username,
command=form.command,
pkey=host.private_key,
+ params=form.params
)
rds.rpush(settings.EXEC_WORKER_KEY, json.dumps(data))
form.host_ids.sort()
@@ -106,5 +109,5 @@ def do_task(request):
@auth('exec.task.do')
def get_histories(request):
- records = ExecHistory.objects.filter(user=request.user).annotate(template_name=F('template__name'))
+ records = ExecHistory.objects.filter(user=request.user).select_related('template')
return json_response([x.to_view() for x in records])
diff --git a/spug_web/src/pages/exec/task/Output.js b/spug_web/src/pages/exec/task/Output.js
index 3ba9233..4729940 100644
--- a/spug_web/src/pages/exec/task/Output.js
+++ b/spug_web/src/pages/exec/task/Output.js
@@ -23,13 +23,13 @@ let gCurrent;
function OutView(props) {
const el = useRef()
- const [term, setTerm] = useState(new Terminal())
+ const [term] = useState(new Terminal());
+ const [fitPlugin] = useState(new FitAddon());
const [current, setCurrent] = useState(Object.keys(store.outputs)[0])
useEffect(() => {
store.tag = ''
gCurrent = current
- const fitPlugin = new FitAddon()
term.setOption('disableStdin', false)
term.setOption('fontFamily', 'Source Code Pro, Courier New, Courier, Monaco, monospace, PingFang SC, Microsoft YaHei')
term.setOption('theme', {background: '#f0f0f0', foreground: '#000', selection: '#999', cursor: '#f0f0f0'})
@@ -39,7 +39,6 @@ function OutView(props) {
term.write('\x1b[36m### WebSocket connecting ...\x1b[0m')
const resize = () => fitPlugin.fit();
window.addEventListener('resize', resize)
- setTerm(term)
return () => window.removeEventListener('resize', resize);
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -55,6 +54,7 @@ function OutView(props) {
}
term.write(message)
socket.send('ok');
+ fitPlugin.fit()
}
socket.onmessage = e => {
if (e.data === 'pong') {
@@ -145,8 +145,8 @@ function OutView(props) {
{store.outputs[current].title}
openTerminal(current)}/>
-
diff --git a/spug_web/src/pages/exec/task/Parameter.js b/spug_web/src/pages/exec/task/Parameter.js
new file mode 100644
index 0000000..d355a6e
--- /dev/null
+++ b/spug_web/src/pages/exec/task/Parameter.js
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
+ * Copyright (c)
+ * Released under the AGPL-3.0 License.
+ */
+import React from 'react';
+import { Modal, Form, Input, Select, message } from 'antd';
+
+
+function Render(props) {
+ switch (props.type) {
+ case 'string':
+ return
+ case 'password':
+ return
+ case 'select':
+ const options = props.options.split('\n').map(x => x.split(':'))
+ return (
+
+ )
+ default:
+ return null
+ }
+}
+
+export default function Parameter(props) {
+ const [form] = Form.useForm();
+
+ function handleSubmit() {
+ const formData = form.getFieldsValue();
+ for (let item of props.parameters.filter(x => x.required)) {
+ if (!formData[item.variable]) {
+ return message.error(`${item.name} 是必填项。`)
+ }
+ }
+ props.onOk(formData);
+ props.onCancel()
+ }
+
+ return (
+
+
+
+
+ ))}
+
+
+ )
+}
\ No newline at end of file
diff --git a/spug_web/src/pages/exec/task/index.js b/spug_web/src/pages/exec/task/index.js
index b49ec96..3ca8b8a 100644
--- a/spug_web/src/pages/exec/task/index.js
+++ b/spug_web/src/pages/exec/task/index.js
@@ -10,6 +10,7 @@ import { Form, Button, Alert, Radio, Tooltip } from 'antd';
import { ACEditor, AuthDiv, Breadcrumb } from 'components';
import Selector from 'pages/host/Selector';
import TemplateSelector from './TemplateSelector';
+import Parameter from './Parameter';
import Output from './Output';
import { http, cleanCommand } from 'libs';
import moment from 'moment';
@@ -22,6 +23,8 @@ function TaskIndex() {
const [command, setCommand] = useState('')
const [template_id, setTemplateId] = useState()
const [histories, setHistories] = useState([])
+ const [parameters, setParameters] = useState([])
+ const [visible, setVisible] = useState(false)
useEffect(() => {
if (!loading) {
@@ -30,6 +33,12 @@ function TaskIndex() {
}
}, [loading])
+ useEffect(() => {
+ if (!command) {
+ setParameters([])
+ }
+ }, [command])
+
useEffect(() => {
return () => {
store.host_ids = []
@@ -39,9 +48,12 @@ function TaskIndex() {
}
}, [])
- function handleSubmit() {
+ function handleSubmit(params) {
+ if (!params && parameters.length > 0) {
+ return setVisible(true)
+ }
setLoading(true)
- const formData = {interpreter, template_id, host_ids: store.host_ids, command: cleanCommand(command)}
+ const formData = {interpreter, template_id, params, host_ids: store.host_ids, command: cleanCommand(command)}
http.post('/api/exec/do/', formData)
.then(store.switchConsole)
.finally(() => setLoading(false))
@@ -52,12 +64,14 @@ function TaskIndex() {
setTemplateId(tpl.id)
setInterpreter(tpl.interpreter)
setCommand(tpl.body)
+ setParameters(tpl.parameters)
}
function handleClick(item) {
setTemplateId(item.template_id)
setInterpreter(item.interpreter)
setCommand(item.command)
+ setParameters(item.parameters || [])
store.host_ids = item.host_ids
}
@@ -98,7 +112,8 @@ function TaskIndex() {
-
+
@@ -124,14 +139,15 @@ function TaskIndex() {
- {store.showTemplate &&
- }
+ {store.showTemplate && }
{store.showConsole &&