A 任务计划增加立即执行测试功能

pull/137/head
vapao 2020-06-10 23:38:57 +08:00
parent b0879bfa6c
commit 46cb442d50
7 changed files with 51 additions and 37 deletions

View File

@ -36,7 +36,8 @@ def host_executor(q, host, pkey, command):
q.put((host.id, exit_code, round(time.time() - now, 3), out)) q.put((host.id, exit_code, round(time.time() - now, 3), out))
def dispatch(command, targets): def dispatch(command, targets, in_view=False):
if not in_view:
close_old_connections() close_old_connections()
threads, pkey, q = [], AppSetting.get('private_key'), Queue() threads, pkey, q = [], AppSetting.get('private_key'), Queue()
for t in targets: for t in targets:

View File

@ -7,6 +7,5 @@ from .views import *
urlpatterns = [ urlpatterns = [
path('', Schedule.as_view()), path('', Schedule.as_view()),
path('<int:h_id>/', ScheduleInfo.as_view()), path('<int:t_id>/', HistoryView.as_view()),
path('<int:t_id>/history/', HistoryView.as_view()),
] ]

View File

@ -5,6 +5,7 @@ from django.views.generic import View
from django_redis import get_redis_connection from django_redis import get_redis_connection
from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.cron import CronTrigger
from apps.schedule.models import Task, History from apps.schedule.models import Task, History
from apps.schedule.executors import dispatch
from apps.host.models import Host from apps.host.models import Host
from django.conf import settings from django.conf import settings
from libs import json_response, JsonParser, Argument, human_datetime from libs import json_response, JsonParser, Argument, human_datetime
@ -90,13 +91,34 @@ class Schedule(View):
class HistoryView(View): class HistoryView(View):
def get(self, request, t_id): def get(self, request, t_id):
task = Task.objects.filter(pk=t_id).first()
if not task:
return json_response(error='未找到指定任务')
h_id = request.GET.get('id') h_id = request.GET.get('id')
if h_id: if h_id:
return json_response(self.fetch_detail(h_id)) h_id = task.latest_id if h_id == 'latest' else h_id
return json_response(self._fetch_detail(h_id))
histories = History.objects.filter(task_id=t_id) histories = History.objects.filter(task_id=t_id)
return json_response([x.to_list() for x in histories]) return json_response([x.to_list() for x in histories])
def fetch_detail(self, h_id): def post(self, request, t_id):
task = Task.objects.filter(pk=t_id).first()
if not task:
return json_response(error='未找到指定任务')
data = dispatch(task.command, json.loads(task.targets), True)
score = 0
for item in data:
score += 1 if item[1] else 0
history = History.objects.create(
task_id=t_id,
status=2 if score == len(data) else 1 if score else 0,
run_time=human_datetime(),
output=json.dumps(data)
)
return json_response(history.id)
def _fetch_detail(self, h_id):
record = History.objects.filter(pk=h_id).first() record = History.objects.filter(pk=h_id).first()
outputs = json.loads(record.output) outputs = json.loads(record.output)
host_ids = (x[0] for x in outputs if isinstance(x[0], int)) host_ids = (x[0] for x in outputs if isinstance(x[0], int))
@ -113,23 +135,3 @@ class HistoryView(View):
'output': out}) 'output': out})
data['duration'] = f"{data['duration'] / len(outputs):.3f}" data['duration'] = f"{data['duration'] / len(outputs):.3f}"
return data return data
class ScheduleInfo(View):
def get(self, request, h_id):
history = History.objects.filter(pk=h_id).first()
outputs = json.loads(history.output)
host_ids = (x[0] for x in outputs if isinstance(x[0], int))
hosts_info = {x.id: x.name for x in Host.objects.filter(id__in=host_ids)}
data = {'run_time': history.run_time, 'success': 0, 'failure': 0, 'duration': 0, 'outputs': []}
for h_id, code, duration, out in outputs:
key = 'success' if code == 0 else 'failure'
data[key] += 1
data['duration'] += duration
data['outputs'].append({
'name': hosts_info.get(h_id, '本机'),
'code': code,
'duration': duration,
'output': out})
data['duration'] = f"{data['duration'] / len(outputs):.3f}"
return json_response(data)

View File

@ -20,7 +20,7 @@ class ComForm extends React.Component {
} }
componentDidMount() { componentDidMount() {
http.get(`/api/schedule/${store.record.id}/`) http.get(`/api/schedule/${store.record.id}/?id=${store.record.h_id}`)
.then(info => this.setState({info})) .then(info => this.setState({info}))
.finally(() => this.setState({loading: false})) .finally(() => this.setState({loading: false}))
} }
@ -56,7 +56,7 @@ class ComForm extends React.Component {
tab={item.code === 0 ? item.name : <span style={{color: 'red'}}>{item.name}</span>}> tab={item.code === 0 ? item.name : <span style={{color: 'red'}}>{item.name}</span>}>
<div>执行时间 {run_time}{moment(run_time).fromNow()}</div> <div>执行时间 {run_time}{moment(run_time).fromNow()}</div>
<div style={{marginTop: 5}}>运行耗时 {item.duration} s</div> <div style={{marginTop: 5}}>运行耗时 {item.duration} s</div>
<div style={{marginTop: 5}}>返回状态 {item.code}</div> <div style={{marginTop: 5}}>返回状态 {item.code} 0 则判定为失败</div>
<div style={{marginTop: 5}}>执行输出 <pre style={preStyle}>{item.output}</pre></div> <div style={{marginTop: 5}}>执行输出 <pre style={preStyle}>{item.output}</pre></div>
</Tabs.TabPane> </Tabs.TabPane>
))} ))}

View File

@ -21,7 +21,7 @@ class Record extends React.Component {
} }
componentDidMount() { componentDidMount() {
http.get(`/api/schedule/${store.record.id}/history/`) http.get(`/api/schedule/${store.record.id}/`)
.then(res => this.setState({records: res})) .then(res => this.setState({records: res}))
.finally(() => this.setState({loading: false})) .finally(() => this.setState({loading: false}))
} }
@ -36,7 +36,7 @@ class Record extends React.Component {
render: info => <Tag color={this.colors[info['status']]}>{info['status_alias']}</Tag> render: info => <Tag color={this.colors[info['status']]}>{info['status_alias']}</Tag>
}, { }, {
title: '操作', title: '操作',
render: info => <LinkButton onClick={() => store.showInfo(info)}>详情</LinkButton> render: info => <LinkButton onClick={() => store.showInfo(null, info.id)}>详情</LinkButton>
}]; }];
render() { render() {

View File

@ -24,10 +24,13 @@ class ComTable extends React.Component {
moreMenus = (info) => ( moreMenus = (info) => (
<Menu> <Menu>
<Menu.Item> <Menu.Item>
<LinkButton auth="schedule.schedule.edit" onClick={() => this.handleActive(info)}>{info.is_active ? '禁用' : '激活'}</LinkButton> <LinkButton onClick={() => this.handleTest(info)}>执行测试</LinkButton>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<LinkButton onClick={() => store.showRecord(info)}>历史</LinkButton> <LinkButton auth="schedule.schedule.edit" onClick={() => this.handleActive(info)}>{info.is_active ? '禁用任务' : '激活任务'}</LinkButton>
</Menu.Item>
<Menu.Item>
<LinkButton onClick={() => store.showRecord(info)}>历史记录</LinkButton>
</Menu.Item> </Menu.Item>
<Menu.Divider/> <Menu.Divider/>
<Menu.Item> <Menu.Item>
@ -72,7 +75,7 @@ class ComTable extends React.Component {
width: 180, width: 180,
render: info => ( render: info => (
<span> <span>
<LinkButton disabled={!info['latest_run_time']} onClick={() => store.showInfo(info, true)}>详情</LinkButton> <LinkButton disabled={!info['latest_run_time']} onClick={() => store.showInfo(info)}>详情</LinkButton>
<Divider type="vertical"/> <Divider type="vertical"/>
<LinkButton auth="schedule.schedule.edit" onClick={() => store.showForm(info)}>编辑</LinkButton> <LinkButton auth="schedule.schedule.edit" onClick={() => store.showForm(info)}>编辑</LinkButton>
<Divider type="vertical"/> <Divider type="vertical"/>
@ -113,6 +116,15 @@ class ComTable extends React.Component {
}) })
}; };
handleTest = (text) => {
Modal.confirm({
title: '操作确认',
content: '立即执行该任务(不影响调度规则)?',
onOk: () => http.post(`/api/schedule/${text.id}/`)
.then(res => store.showInfo(text, res))
})
};
render() { render() {
let data = store.records; let data = store.records;
if (store.f_status !== undefined) { if (store.f_status !== undefined) {

View File

@ -41,10 +41,10 @@ class Store {
this.record = info this.record = info
}; };
showInfo = (info = {}, isTask) => { showInfo = (info, h_id='latest') => {
const record = isTask ? {id: info['latest_id']} : info; if (info) this.record = info;
this.infoVisible = true; this.record.h_id = h_id;
this.record = record this.infoVisible = true
}; };
showRecord = (info) => { showRecord = (info) => {