Commit df80e804 authored by ibuler's avatar ibuler

[Update] 修改日志

parent 09fc2776
# -*- 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})
\ No newline at end of file
......@@ -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))
\ No newline at end of file
......@@ -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")
\ No newline at end of file
update_success_msg = _("<b>%(name)s</b> was updated successfully")
FILE_END_GUARD = ">>> Content End <<<"
{% 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'),
]
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
# ~*~ 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)
try:
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:
with open(history.log_path, 'w') as f:
raw, summary = self._run_only(file_obj=f)
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()
......
# 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">
......
{% 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>
</head>
<body>
<div class="container">
<div id="term">
</div>
</div>
</body>
{% 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>
{% 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%">
<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;
</iframe>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
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>
{% 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'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment