mirror of https://github.com/jumpserver/jumpserver
[Update] 修改日志
parent
09fc2776df
commit
df80e8047a
|
@ -1,7 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from django.core.cache import cache
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.views import Response
|
||||
from ldap3 import Server, Connection
|
||||
|
@ -11,6 +14,7 @@ from django.conf import settings
|
|||
|
||||
from .permissions import IsSuperUser, IsAppUser
|
||||
from .serializers import MailTestSerializer, LDAPTestSerializer
|
||||
from .const import FILE_END_GUARD
|
||||
|
||||
|
||||
class MailTestingAPI(APIView):
|
||||
|
@ -105,3 +109,30 @@ class DjangoSettingsAPI(APIView):
|
|||
if i.isupper():
|
||||
configs[i] = str(getattr(settings, i))
|
||||
return Response(configs)
|
||||
|
||||
|
||||
class FileTailApi(APIView):
|
||||
permission_classes = (IsSuperUser,)
|
||||
default_buff_size = 1024 * 10
|
||||
end = False
|
||||
buff_size = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
file_path = request.query_params.get("file")
|
||||
self.buff_size = request.query_params.get('buffer') or self.default_buff_size
|
||||
mark = request.query_params.get("mark") or str(uuid.uuid4())
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return Response({"data": _("Waiting ...")}, status=203)
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
offset = cache.get(mark, 0)
|
||||
f.seek(offset)
|
||||
data = f.read(self.buff_size).replace('\n', '\r\n')
|
||||
mark = str(uuid.uuid4())
|
||||
cache.set(mark, f.tell(), 5)
|
||||
|
||||
if FILE_END_GUARD in data:
|
||||
data = data.replace(FILE_END_GUARD, '')
|
||||
self.end = True
|
||||
return Response({"data": data, 'end': self.end, 'mark': mark})
|
|
@ -5,7 +5,7 @@ import json
|
|||
from functools import wraps
|
||||
|
||||
from celery import Celery, subtask
|
||||
from celery.signals import worker_ready, worker_shutdown
|
||||
from celery.signals import worker_ready, worker_shutdown, task_prerun, task_postrun
|
||||
from django.db.utils import ProgrammingError, OperationalError
|
||||
|
||||
from .utils import get_logger
|
||||
|
@ -199,3 +199,16 @@ def after_app_shutdown(sender=None, headers=None, body=None, **kwargs):
|
|||
', '.join(__AFTER_APP_SHUTDOWN_CLEAN_TASKS))
|
||||
)
|
||||
PeriodicTask.objects.filter(name__in=__AFTER_APP_SHUTDOWN_CLEAN_TASKS).delete()
|
||||
|
||||
|
||||
@task_prerun.connect
|
||||
def pre_run_task_signal_handler(sender, task, *args, **kwargs):
|
||||
|
||||
print("Sender: {}".format(sender))
|
||||
print("Task: {}".format(task))
|
||||
|
||||
|
||||
@task_postrun.connect
|
||||
def post_run_task_signal_handler(sender, task, *args, **kwargs):
|
||||
print("Sender: {}".format(sender))
|
||||
print("Task: {}".format(task))
|
|
@ -4,4 +4,5 @@
|
|||
from django.utils.translation import ugettext as _
|
||||
|
||||
create_success_msg = _("<b>%(name)s</b> was created successfully")
|
||||
update_success_msg = _("<b>%(name)s</b> was updated successfully")
|
||||
update_success_msg = _("<b>%(name)s</b> was updated successfully")
|
||||
FILE_END_GUARD = ">>> Content End <<<"
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
{% load static %}
|
||||
<head>
|
||||
<title>term.js</title>
|
||||
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
|
||||
<style>
|
||||
html {
|
||||
background: #000;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 20px;
|
||||
font: 20px/1.5 sans-serif;
|
||||
}
|
||||
.terminal {
|
||||
float: left;
|
||||
font-family: 'Monaco', 'Consolas', "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||
font-size: 12px;
|
||||
color: #f0f0f0;
|
||||
background-color: #555;
|
||||
padding: 20px 20px 20px;
|
||||
}
|
||||
.terminal-cursor {
|
||||
color: #000;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<div class="container">
|
||||
<div id="term">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="{% static 'js/term.js' %}"></script>
|
||||
<script>
|
||||
var rowHeight = 1;
|
||||
var colWidth = 1;
|
||||
var mark = '';
|
||||
var url = "{% url 'api-common:tail-file' %}?file={{ file_path }}";
|
||||
var term;
|
||||
var end = false;
|
||||
var error = false;
|
||||
var interval = 200;
|
||||
|
||||
function calWinSize() {
|
||||
var t = $('.terminal');
|
||||
rowHeight = 1.00 * t.height() / 24;
|
||||
colWidth = 1.00 * t.width() / 80;
|
||||
}
|
||||
function resize() {
|
||||
var rows = Math.floor(window.innerHeight / rowHeight) - 2;
|
||||
var cols = Math.floor(window.innerWidth / colWidth) - 10;
|
||||
term.resize(cols, rows);
|
||||
}
|
||||
function requestAndWrite() {
|
||||
if (!end) {
|
||||
$.ajax({
|
||||
url: url + '&mark=' + mark,
|
||||
method: "GET",
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function(data, textStatue, jqXHR) {
|
||||
if (jqXHR.status === 203) {
|
||||
error = true;
|
||||
term.write('.');
|
||||
interval = 500;
|
||||
}
|
||||
if (jqXHR.status === 200){
|
||||
term.write(data.data);
|
||||
mark = data.mark;
|
||||
if (data.end){
|
||||
end = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
$(document).ready(function () {
|
||||
term = new Terminal({
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
useStyle: true,
|
||||
screenKeys: false,
|
||||
convertEol: false,
|
||||
cursorBlink: false
|
||||
});
|
||||
term.open();
|
||||
term.on('data', function (data) {
|
||||
term.write(data.replace('\r', '\r\n'))
|
||||
});
|
||||
calWinSize();
|
||||
resize();
|
||||
$('.terminal').detach().appendTo('#term');
|
||||
setInterval(function () {
|
||||
requestAndWrite()
|
||||
}, interval)
|
||||
});
|
||||
</script>
|
|
@ -10,4 +10,5 @@ urlpatterns = [
|
|||
url(r'^v1/mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'),
|
||||
url(r'^v1/ldap/testing/$', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
|
||||
url(r'^v1/django-settings/$', api.DjangoSettingsAPI.as_view(), name='django-settings'),
|
||||
url(r'^v1/tail-file/$', api.FileTailApi.as_view(), name='tail-file'),
|
||||
]
|
||||
|
|
|
@ -11,4 +11,7 @@ urlpatterns = [
|
|||
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
|
||||
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
|
||||
url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
|
||||
|
||||
url(r'^tail-file/$', views.TailFileView.as_view(), name='tail-file'),
|
||||
url(r'^celery/task/log/$', views.CeleryTaskLogView.as_view(), name='celery-task-log'),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django.views.generic import TemplateView
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.views.generic import TemplateView, View
|
||||
from django.shortcuts import render, redirect, Http404, reverse
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
|
@ -120,3 +122,34 @@ class TerminalSettingView(AdminUserRequiredMixin, TemplateView):
|
|||
context.update({"form": form})
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
|
||||
class TailFileView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'common/tail_file.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
file_path = self.request.GET.get("file")
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({"file_path": file_path})
|
||||
return context
|
||||
|
||||
|
||||
class CeleryTaskLogView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'common/tail_file.html'
|
||||
task_log_path = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
task = self.request.GET.get('task')
|
||||
if not task:
|
||||
raise Http404("Not found task")
|
||||
|
||||
self.task_log_path = cache.get(task)
|
||||
if not self.task_log_path:
|
||||
raise Http404("Not found task log file")
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'file_path': self.task_log_path
|
||||
})
|
||||
return context
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
import uuid
|
||||
import re
|
||||
import os
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import ugettext as _
|
||||
from rest_framework import viewsets, generics
|
||||
from rest_framework.generics import RetrieveAPIView
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.views import Response
|
||||
|
||||
from .hands import IsSuperUser
|
||||
from common.const import FILE_END_GUARD
|
||||
from .models import Task, AdHoc, AdHocRunHistory
|
||||
from .serializers import TaskSerializer, AdHocSerializer, AdHocRunHistorySerializer
|
||||
from .tasks import run_ansible_task
|
||||
|
||||
|
||||
|
||||
|
||||
class TaskViewSet(viewsets.ModelViewSet):
|
||||
queryset = Task.objects.all()
|
||||
serializer_class = TaskSerializer
|
||||
|
@ -27,8 +31,8 @@ class TaskRun(generics.RetrieveAPIView):
|
|||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
task = self.get_object()
|
||||
run_ansible_task.delay(str(task.id))
|
||||
return Response({"msg": "start"})
|
||||
t = run_ansible_task.delay(str(task.id))
|
||||
return Response({"task": t.id})
|
||||
|
||||
|
||||
class AdHocViewSet(viewsets.ModelViewSet):
|
||||
|
@ -63,24 +67,28 @@ class AdHocRunHistorySet(viewsets.ModelViewSet):
|
|||
return self.queryset
|
||||
|
||||
|
||||
class AdHocHistoryOutputAPI(RetrieveAPIView):
|
||||
queryset = AdHocRunHistory.objects.all()
|
||||
class LogFileViewApi(APIView):
|
||||
permission_classes = (IsSuperUser,)
|
||||
buff_size = 1024 * 10
|
||||
end = False
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
history = self.get_object()
|
||||
def get(self, request, *args, **kwargs):
|
||||
file_path = request.query_params.get("file")
|
||||
mark = request.query_params.get("mark") or str(uuid.uuid4())
|
||||
|
||||
with open(history.log_path, 'r') as f:
|
||||
if not os.path.isfile(file_path):
|
||||
print(file_path)
|
||||
return Response({"error": _("Log file not found")}, status=204)
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
offset = cache.get(mark, 0)
|
||||
f.seek(offset)
|
||||
data = f.read(self.buff_size).replace('\n', '\r\n')
|
||||
print(repr(data))
|
||||
mark = str(uuid.uuid4())
|
||||
cache.set(mark, f.tell(), 5)
|
||||
|
||||
if history.is_finished and data == '':
|
||||
if FILE_END_GUARD in data:
|
||||
data.replace(FILE_END_GUARD, '')
|
||||
self.end = True
|
||||
|
||||
return Response({"data": data, 'end': self.end, 'mark': mark})
|
||||
|
|
|
@ -6,6 +6,7 @@ import os
|
|||
import time
|
||||
import datetime
|
||||
|
||||
from celery import current_task
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
@ -211,11 +212,20 @@ class AdHoc(models.Model):
|
|||
return self._run_only()
|
||||
|
||||
def _run_and_record(self):
|
||||
history = AdHocRunHistory(adhoc=self, task=self.task)
|
||||
time_start = time.time()
|
||||
try:
|
||||
with open(history.log_path, 'w') as f:
|
||||
raw, summary = self._run_only(file_obj=f)
|
||||
hid = current_task.request.id
|
||||
except AttributeError:
|
||||
hid = str(uuid.uuid4())
|
||||
history = AdHocRunHistory(id=hid, adhoc=self, task=self.task)
|
||||
time_start = time.time()
|
||||
# f = open(history.log_path, 'w')
|
||||
try:
|
||||
date_start = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
# f.write("{} {}\r\n\r\n".format(date_start, self.task.name))
|
||||
raw, summary = self._run_only()
|
||||
# raw, summary = self._run_only(file_obj=f)
|
||||
date_end = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
# f.write("\r\n{} Task finish\r\n".format(date_end))
|
||||
history.is_finished = True
|
||||
if summary.get('dark'):
|
||||
history.is_success = False
|
||||
|
@ -227,6 +237,7 @@ class AdHoc(models.Model):
|
|||
except Exception as e:
|
||||
return {}, {"dark": {"all": str(e)}, "contacted": []}
|
||||
finally:
|
||||
# f.close()
|
||||
history.date_finished = timezone.now()
|
||||
history.timedelta = time.time() - time_start
|
||||
history.save()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# coding: utf-8
|
||||
from celery import shared_task, subtask
|
||||
|
||||
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from .models import Task
|
||||
|
||||
|
@ -12,14 +13,13 @@ def rerun_task():
|
|||
|
||||
|
||||
@shared_task
|
||||
def run_ansible_task(task_id, callback=None, **kwargs):
|
||||
def run_ansible_task(tid, callback=None, **kwargs):
|
||||
"""
|
||||
:param task_id: is the tasks serialized data
|
||||
:param tid: is the tasks serialized data
|
||||
:param callback: callback function name
|
||||
:return:
|
||||
"""
|
||||
|
||||
task = get_object_or_none(Task, id=task_id)
|
||||
task = get_object_or_none(Task, id=tid)
|
||||
if task:
|
||||
result = task.run()
|
||||
if callback is not None:
|
||||
|
|
|
@ -82,7 +82,8 @@ function initTable() {
|
|||
select: [],
|
||||
columnDefs: [
|
||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||
$(td).html(cellData);
|
||||
var d = new Date(cellData);
|
||||
$(td).html(d);
|
||||
}},
|
||||
{targets: 2, createdCell: function (td, cellData) {
|
||||
var total = "<span>" + cellData.total + "</span>";
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
<li class="active">
|
||||
<a href="{% url 'ops:adhoc-history-detail' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Run history detail' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'ops:adhoc-history-output' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'History output' %} </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
|
|
|
@ -1,93 +1,37 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>term.js</title>
|
||||
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
|
||||
<style>
|
||||
html {
|
||||
background: #000;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 20px;
|
||||
font: 20px/1.5 sans-serif;
|
||||
}
|
||||
.terminal {
|
||||
float: left;
|
||||
font-family: 'Monaco', 'Consolas', "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||
font-size: 14px;
|
||||
color: #f0f0f0;
|
||||
background-color: #555;
|
||||
padding: 20px 20px 20px;
|
||||
}
|
||||
.terminal-cursor {
|
||||
color: #000;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
{% load i18n %}
|
||||
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
|
||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="term">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<li>
|
||||
<a href="{% url 'ops:adhoc-history-detail' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Run history detail' %} </a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{% url 'ops:adhoc-history-output' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'History output' %} </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content" style="height: 800px">
|
||||
<iframe src="{% url 'ops:adhoc-history-output-alone' pk=object.pk %}" width="100%" height="100%">
|
||||
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
<script src="{% static 'js/term.js' %}"></script>
|
||||
<script>
|
||||
var rowHeight = 1;
|
||||
var colWidth = 1;
|
||||
var mark = '';
|
||||
var url = "{% url 'api-ops:history-output' pk=object.id %}";
|
||||
var term;
|
||||
var end = false;
|
||||
|
||||
function calWinSize() {
|
||||
var t = $('.terminal');
|
||||
console.log(t.height());
|
||||
rowHeight = 1.00 * t.height() / 24;
|
||||
colWidth = 1.00 * t.width() / 80;
|
||||
}
|
||||
function resize() {
|
||||
var rows = Math.floor(window.innerHeight / rowHeight) - 2;
|
||||
var cols = Math.floor(window.innerWidth / colWidth) - 5;
|
||||
term.resize(cols, rows);
|
||||
}
|
||||
function requestAndWrite() {
|
||||
if (!end) {
|
||||
$.ajax({
|
||||
url: url + '?mark=' + mark,
|
||||
method: "GET",
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function(data, textStatue, jqXHR) {
|
||||
term.write(data.data);
|
||||
mark = data.mark;
|
||||
if (data.end){
|
||||
end = true
|
||||
}
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
});
|
||||
}
|
||||
}
|
||||
$(document).ready(function () {
|
||||
term = new Terminal({
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
useStyle: true,
|
||||
screenKeys: false
|
||||
});
|
||||
term.open();
|
||||
term.on('data', function (data) {
|
||||
term.write(data.replace('\r', '\r\n'))
|
||||
});
|
||||
calWinSize();
|
||||
resize();
|
||||
$('.terminal').detach().appendTo('#term');
|
||||
term.write('\x1b[31mWelcome to term.js!\x1b[m\r\n');
|
||||
setInterval(function () {
|
||||
requestAndWrite()
|
||||
}, 100)
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
{% load static %}
|
||||
<head>
|
||||
<title>term.js</title>
|
||||
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
|
||||
<style>
|
||||
html {
|
||||
background: #000;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 20px;
|
||||
font: 20px/1.5 sans-serif;
|
||||
}
|
||||
.terminal {
|
||||
float: left;
|
||||
font-family: 'Monaco', 'Consolas', "DejaVu Sans Mono", "Liberation Mono", monospace;
|
||||
font-size: 12px;
|
||||
color: #f0f0f0;
|
||||
background-color: #555;
|
||||
padding: 20px 20px 20px;
|
||||
}
|
||||
.terminal-cursor {
|
||||
color: #000;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<div class="container">
|
||||
<div id="term">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="{% static 'js/term.js' %}"></script>
|
||||
<script>
|
||||
var rowHeight = 1;
|
||||
var colWidth = 1;
|
||||
var mark = '';
|
||||
var url = "{% url 'api-ops:history-output' pk=object.id %}";
|
||||
var term;
|
||||
var end = false;
|
||||
var has_error = false;
|
||||
|
||||
function calWinSize() {
|
||||
var t = $('.terminal');
|
||||
rowHeight = 1.00 * t.height() / 24;
|
||||
colWidth = 1.00 * t.width() / 80;
|
||||
}
|
||||
function resize() {
|
||||
var rows = Math.floor(window.innerHeight / rowHeight) - 2;
|
||||
var cols = Math.floor(window.innerWidth / colWidth) - 5;
|
||||
term.resize(cols, rows);
|
||||
}
|
||||
function requestAndWrite() {
|
||||
if (!end) {
|
||||
$.ajax({
|
||||
url: url + '?mark=' + mark,
|
||||
method: "GET",
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function(data, textStatue, jqXHR) {
|
||||
term.write(data.data);
|
||||
mark = data.mark;
|
||||
if (data.end){
|
||||
end = true
|
||||
}
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
if (!has_error) {
|
||||
var error = jqXHR.responseJSON.error;
|
||||
term.write('\x1b[31m' + error + '\x1b[m\r\n');
|
||||
has_error = true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
$(document).ready(function () {
|
||||
term = new Terminal({
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
useStyle: true,
|
||||
screenKeys: false,
|
||||
convertEol: false,
|
||||
cursorBlink: false
|
||||
});
|
||||
term.open();
|
||||
term.on('data', function (data) {
|
||||
term.write(data.replace('\r', '\r\n'))
|
||||
});
|
||||
calWinSize();
|
||||
resize();
|
||||
$('.terminal').detach().appendTo('#term');
|
||||
setInterval(function () {
|
||||
requestAndWrite()
|
||||
}, 200)
|
||||
});
|
||||
</script>
|
|
@ -105,6 +105,10 @@
|
|||
$(td).html(cellData.user)
|
||||
}
|
||||
}},
|
||||
{targets: 6, createdCell: function (td, cellData) {
|
||||
var d = new Date(cellData);
|
||||
$(td).html(d.toLocaleString())
|
||||
}},
|
||||
{targets: 7, createdCell: function (td, cellData, rowData) {
|
||||
var detail_btn = '<a class="btn btn-xs btn-primary m-l-xs btn-run" href="{% url 'ops:adhoc-detail' pk=DEFAULT_PK %}">{% trans "Detail" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||
if (cellData) {
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
<li>
|
||||
<a href="{% url 'ops:task-history' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Run history' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'ops:adhoc-history-output' pk=object.latest_history.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Last run output' %} </a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
|
@ -160,6 +163,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'users/_user_update_pk_modal.html' %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<div class="col-sm-12" style="padding-left: 0">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span style="float: left">{% trans 'History of ' %} <b>{{ object.task.name }}:{{ object.short_id }}</b></span>
|
||||
<span style="float: left">{% trans 'History of ' %} <b>{{ object.name }}:{{ object.short_id }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
|
@ -85,7 +85,8 @@ function initTable() {
|
|||
select: [],
|
||||
columnDefs: [
|
||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||
$(td).html(cellData);
|
||||
var d = new Date(cellData);
|
||||
$(td).html(d.toLocaleString());
|
||||
}},
|
||||
{targets: 2, createdCell: function (td, cellData) {
|
||||
var total = "<span>" + cellData.total + "</span>";
|
||||
|
|
|
@ -111,9 +111,9 @@ $(document).ready(function() {
|
|||
var error = function (data) {
|
||||
alert(data)
|
||||
};
|
||||
var success = function () {
|
||||
alert("任务开始执行,重定向到任务详情页面,多刷新几次查看结果")
|
||||
window.location = "{% url 'ops:task-detail' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', uid);
|
||||
var success = function(data) {
|
||||
var task_id = data.task;
|
||||
window.location = "{% url 'ops:adhoc-history-output' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", task_id);
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
|
|
|
@ -15,7 +15,7 @@ router.register(r'v1/history', api.AdHocRunHistorySet, 'history')
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^v1/tasks/(?P<pk>[0-9a-zA-Z\-]{36})/run/$', api.TaskRun.as_view(), name='task-run'),
|
||||
url(r'^v1/history/(?P<pk>[0-9a-zA-Z\-]{36})/output/$', api.AdHocHistoryOutputAPI.as_view(), name='history-output'),
|
||||
# url(r'^v1/history/(?P<pk>[0-9a-zA-Z\-]{36})/output/$', api.CeleryTaskOutputApi.as_view(), name='history-output'),
|
||||
]
|
||||
|
||||
urlpatterns += router.urls
|
||||
|
|
|
@ -18,5 +18,6 @@ urlpatterns = [
|
|||
url(r'^adhoc/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AdHocDetailView.as_view(), name='adhoc-detail'),
|
||||
url(r'^adhoc/(?P<pk>[0-9a-zA-Z\-]{36})/history/$', views.AdHocHistoryView.as_view(), name='adhoc-history'),
|
||||
url(r'^adhoc/history/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AdHocHistoryDetailView.as_view(), name='adhoc-history-detail'),
|
||||
url(r'^adhoc/history/(?P<pk>[0-9a-zA-Z\-]{36})/_output/$', views.CeleryTaskOutputView.as_view(), name='adhoc-history-output-alone'),
|
||||
url(r'^adhoc/history/(?P<pk>[0-9a-zA-Z\-]{36})/output/$', views.AdHocHistoryOutputView.as_view(), name='adhoc-history-output'),
|
||||
]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
|
||||
from common.mixins import DatetimeSearchMixin
|
||||
from .models import Task, AdHoc, AdHocRunHistory
|
||||
|
@ -121,6 +121,11 @@ class AdHocHistoryDetailView(AdminUserRequiredMixin, DetailView):
|
|||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class CeleryTaskOutputView(AdminUserRequiredMixin, TemplateView):
|
||||
model = AdHocRunHistory
|
||||
template_name = 'ops/celery_task_output.html'
|
||||
|
||||
|
||||
class AdHocHistoryOutputView(AdminUserRequiredMixin, DetailView):
|
||||
model = AdHocRunHistory
|
||||
template_name = 'ops/adhoc_history_output.html'
|
||||
|
|
Loading…
Reference in New Issue