Skip to content

Commit df80e80

Browse files
committed
[Update] 修改日志
1 parent 09fc277 commit df80e80

21 files changed

Lines changed: 372 additions & 120 deletions

apps/common/api.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# -*- coding: utf-8 -*-
22
#
3+
import os
34
import json
5+
import uuid
46

7+
from django.core.cache import cache
58
from rest_framework.views import APIView
69
from rest_framework.views import Response
710
from ldap3 import Server, Connection
@@ -11,6 +14,7 @@
1114

1215
from .permissions import IsSuperUser, IsAppUser
1316
from .serializers import MailTestSerializer, LDAPTestSerializer
17+
from .const import FILE_END_GUARD
1418

1519

1620
class MailTestingAPI(APIView):
@@ -105,3 +109,30 @@ def get(self, request):
105109
if i.isupper():
106110
configs[i] = str(getattr(settings, i))
107111
return Response(configs)
112+
113+
114+
class FileTailApi(APIView):
115+
permission_classes = (IsSuperUser,)
116+
default_buff_size = 1024 * 10
117+
end = False
118+
buff_size = None
119+
120+
def get(self, request, *args, **kwargs):
121+
file_path = request.query_params.get("file")
122+
self.buff_size = request.query_params.get('buffer') or self.default_buff_size
123+
mark = request.query_params.get("mark") or str(uuid.uuid4())
124+
125+
if not os.path.isfile(file_path):
126+
return Response({"data": _("Waiting ...")}, status=203)
127+
128+
with open(file_path, 'r') as f:
129+
offset = cache.get(mark, 0)
130+
f.seek(offset)
131+
data = f.read(self.buff_size).replace('\n', '\r\n')
132+
mark = str(uuid.uuid4())
133+
cache.set(mark, f.tell(), 5)
134+
135+
if FILE_END_GUARD in data:
136+
data = data.replace(FILE_END_GUARD, '')
137+
self.end = True
138+
return Response({"data": data, 'end': self.end, 'mark': mark})

apps/common/celery.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from functools import wraps
66

77
from celery import Celery, subtask
8-
from celery.signals import worker_ready, worker_shutdown
8+
from celery.signals import worker_ready, worker_shutdown, task_prerun, task_postrun
99
from django.db.utils import ProgrammingError, OperationalError
1010

1111
from .utils import get_logger
@@ -199,3 +199,16 @@ def after_app_shutdown(sender=None, headers=None, body=None, **kwargs):
199199
', '.join(__AFTER_APP_SHUTDOWN_CLEAN_TASKS))
200200
)
201201
PeriodicTask.objects.filter(name__in=__AFTER_APP_SHUTDOWN_CLEAN_TASKS).delete()
202+
203+
204+
@task_prerun.connect
205+
def pre_run_task_signal_handler(sender, task, *args, **kwargs):
206+
207+
print("Sender: {}".format(sender))
208+
print("Task: {}".format(task))
209+
210+
211+
@task_postrun.connect
212+
def post_run_task_signal_handler(sender, task, *args, **kwargs):
213+
print("Sender: {}".format(sender))
214+
print("Task: {}".format(task))

apps/common/const.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
from django.utils.translation import ugettext as _
55

66
create_success_msg = _("<b>%(name)s</b> was created successfully")
7-
update_success_msg = _("<b>%(name)s</b> was updated successfully")
7+
update_success_msg = _("<b>%(name)s</b> was updated successfully")
8+
FILE_END_GUARD = ">>> Content End <<<"
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{% load static %}
2+
<head>
3+
<title>term.js</title>
4+
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
5+
<style>
6+
html {
7+
background: #000;
8+
}
9+
h1 {
10+
margin-bottom: 20px;
11+
font: 20px/1.5 sans-serif;
12+
}
13+
.terminal {
14+
float: left;
15+
font-family: 'Monaco', 'Consolas', "DejaVu Sans Mono", "Liberation Mono", monospace;
16+
font-size: 12px;
17+
color: #f0f0f0;
18+
background-color: #555;
19+
padding: 20px 20px 20px;
20+
}
21+
.terminal-cursor {
22+
color: #000;
23+
background: #f0f0f0;
24+
}
25+
</style>
26+
</head>
27+
<div class="container">
28+
<div id="term">
29+
</div>
30+
</div>
31+
32+
33+
<script src="{% static 'js/term.js' %}"></script>
34+
<script>
35+
var rowHeight = 1;
36+
var colWidth = 1;
37+
var mark = '';
38+
var url = "{% url 'api-common:tail-file' %}?file={{ file_path }}";
39+
var term;
40+
var end = false;
41+
var error = false;
42+
var interval = 200;
43+
44+
function calWinSize() {
45+
var t = $('.terminal');
46+
rowHeight = 1.00 * t.height() / 24;
47+
colWidth = 1.00 * t.width() / 80;
48+
}
49+
function resize() {
50+
var rows = Math.floor(window.innerHeight / rowHeight) - 2;
51+
var cols = Math.floor(window.innerWidth / colWidth) - 10;
52+
term.resize(cols, rows);
53+
}
54+
function requestAndWrite() {
55+
if (!end) {
56+
$.ajax({
57+
url: url + '&mark=' + mark,
58+
method: "GET",
59+
contentType: "application/json; charset=utf-8"
60+
}).done(function(data, textStatue, jqXHR) {
61+
if (jqXHR.status === 203) {
62+
error = true;
63+
term.write('.');
64+
interval = 500;
65+
}
66+
if (jqXHR.status === 200){
67+
term.write(data.data);
68+
mark = data.mark;
69+
if (data.end){
70+
end = true
71+
}
72+
}
73+
})
74+
}
75+
}
76+
$(document).ready(function () {
77+
term = new Terminal({
78+
cols: 80,
79+
rows: 24,
80+
useStyle: true,
81+
screenKeys: false,
82+
convertEol: false,
83+
cursorBlink: false
84+
});
85+
term.open();
86+
term.on('data', function (data) {
87+
term.write(data.replace('\r', '\r\n'))
88+
});
89+
calWinSize();
90+
resize();
91+
$('.terminal').detach().appendTo('#term');
92+
setInterval(function () {
93+
requestAndWrite()
94+
}, interval)
95+
});
96+
</script>

apps/common/urls/api_urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
url(r'^v1/mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'),
1111
url(r'^v1/ldap/testing/$', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
1212
url(r'^v1/django-settings/$', api.DjangoSettingsAPI.as_view(), name='django-settings'),
13+
url(r'^v1/tail-file/$', api.FileTailApi.as_view(), name='tail-file'),
1314
]

apps/common/urls/view_urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@
1111
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
1212
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
1313
url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
14+
15+
url(r'^tail-file/$', views.TailFileView.as_view(), name='tail-file'),
16+
url(r'^celery/task/log/$', views.CeleryTaskLogView.as_view(), name='celery-task-log'),
1417
]

apps/common/views.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
from django.views.generic import TemplateView
2-
from django.shortcuts import render, redirect
1+
2+
from django.core.cache import cache
3+
from django.views.generic import TemplateView, View
4+
from django.shortcuts import render, redirect, Http404, reverse
35
from django.contrib import messages
46
from django.utils.translation import ugettext as _
57
from django.conf import settings
@@ -120,3 +122,34 @@ def post(self, request):
120122
context.update({"form": form})
121123
return render(request, self.template_name, context)
122124

125+
126+
class TailFileView(AdminUserRequiredMixin, TemplateView):
127+
template_name = 'common/tail_file.html'
128+
129+
def get_context_data(self, **kwargs):
130+
file_path = self.request.GET.get("file")
131+
context = super().get_context_data(**kwargs)
132+
context.update({"file_path": file_path})
133+
return context
134+
135+
136+
class CeleryTaskLogView(AdminUserRequiredMixin, TemplateView):
137+
template_name = 'common/tail_file.html'
138+
task_log_path = None
139+
140+
def get(self, request, *args, **kwargs):
141+
task = self.request.GET.get('task')
142+
if not task:
143+
raise Http404("Not found task")
144+
145+
self.task_log_path = cache.get(task)
146+
if not self.task_log_path:
147+
raise Http404("Not found task log file")
148+
return super().get(request, *args, **kwargs)
149+
150+
def get_context_data(self, **kwargs):
151+
context = super().get_context_data(**kwargs)
152+
context.update({
153+
'file_path': self.task_log_path
154+
})
155+
return context

apps/ops/api.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
# ~*~ coding: utf-8 ~*~
22
import uuid
3-
import re
3+
import os
44

55
from django.core.cache import cache
66
from django.shortcuts import get_object_or_404
7+
from django.utils.translation import ugettext as _
78
from rest_framework import viewsets, generics
8-
from rest_framework.generics import RetrieveAPIView
9+
from rest_framework.views import APIView
910
from rest_framework.views import Response
1011

1112
from .hands import IsSuperUser
13+
from common.const import FILE_END_GUARD
1214
from .models import Task, AdHoc, AdHocRunHistory
1315
from .serializers import TaskSerializer, AdHocSerializer, AdHocRunHistorySerializer
1416
from .tasks import run_ansible_task
1517

1618

19+
20+
1721
class TaskViewSet(viewsets.ModelViewSet):
1822
queryset = Task.objects.all()
1923
serializer_class = TaskSerializer
@@ -27,8 +31,8 @@ class TaskRun(generics.RetrieveAPIView):
2731

2832
def retrieve(self, request, *args, **kwargs):
2933
task = self.get_object()
30-
run_ansible_task.delay(str(task.id))
31-
return Response({"msg": "start"})
34+
t = run_ansible_task.delay(str(task.id))
35+
return Response({"task": t.id})
3236

3337

3438
class AdHocViewSet(viewsets.ModelViewSet):
@@ -63,24 +67,28 @@ def get_queryset(self):
6367
return self.queryset
6468

6569

66-
class AdHocHistoryOutputAPI(RetrieveAPIView):
67-
queryset = AdHocRunHistory.objects.all()
70+
class LogFileViewApi(APIView):
6871
permission_classes = (IsSuperUser,)
6972
buff_size = 1024 * 10
7073
end = False
7174

72-
def retrieve(self, request, *args, **kwargs):
73-
history = self.get_object()
75+
def get(self, request, *args, **kwargs):
76+
file_path = request.query_params.get("file")
7477
mark = request.query_params.get("mark") or str(uuid.uuid4())
7578

76-
with open(history.log_path, 'r') as f:
79+
if not os.path.isfile(file_path):
80+
print(file_path)
81+
return Response({"error": _("Log file not found")}, status=204)
82+
83+
with open(file_path, 'r') as f:
7784
offset = cache.get(mark, 0)
7885
f.seek(offset)
7986
data = f.read(self.buff_size).replace('\n', '\r\n')
80-
print(repr(data))
8187
mark = str(uuid.uuid4())
8288
cache.set(mark, f.tell(), 5)
8389

84-
if history.is_finished and data == '':
90+
if FILE_END_GUARD in data:
91+
data.replace(FILE_END_GUARD, '')
8592
self.end = True
93+
8694
return Response({"data": data, 'end': self.end, 'mark': mark})

apps/ops/models.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import time
77
import datetime
88

9+
from celery import current_task
910
from django.db import models
1011
from django.conf import settings
1112
from django.utils import timezone
@@ -211,11 +212,20 @@ def run(self, record=True):
211212
return self._run_only()
212213

213214
def _run_and_record(self):
214-
history = AdHocRunHistory(adhoc=self, task=self.task)
215+
try:
216+
hid = current_task.request.id
217+
except AttributeError:
218+
hid = str(uuid.uuid4())
219+
history = AdHocRunHistory(id=hid, adhoc=self, task=self.task)
215220
time_start = time.time()
221+
# f = open(history.log_path, 'w')
216222
try:
217-
with open(history.log_path, 'w') as f:
218-
raw, summary = self._run_only(file_obj=f)
223+
date_start = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
224+
# f.write("{} {}\r\n\r\n".format(date_start, self.task.name))
225+
raw, summary = self._run_only()
226+
# raw, summary = self._run_only(file_obj=f)
227+
date_end = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
228+
# f.write("\r\n{} Task finish\r\n".format(date_end))
219229
history.is_finished = True
220230
if summary.get('dark'):
221231
history.is_success = False
@@ -227,6 +237,7 @@ def _run_and_record(self):
227237
except Exception as e:
228238
return {}, {"dark": {"all": str(e)}, "contacted": []}
229239
finally:
240+
# f.close()
230241
history.date_finished = timezone.now()
231242
history.timedelta = time.time() - time_start
232243
history.save()

apps/ops/tasks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# coding: utf-8
22
from celery import shared_task, subtask
33

4+
45
from common.utils import get_logger, get_object_or_none
56
from .models import Task
67

@@ -12,14 +13,13 @@ def rerun_task():
1213

1314

1415
@shared_task
15-
def run_ansible_task(task_id, callback=None, **kwargs):
16+
def run_ansible_task(tid, callback=None, **kwargs):
1617
"""
17-
:param task_id: is the tasks serialized data
18+
:param tid: is the tasks serialized data
1819
:param callback: callback function name
1920
:return:
2021
"""
21-
22-
task = get_object_or_none(Task, id=task_id)
22+
task = get_object_or_none(Task, id=tid)
2323
if task:
2424
result = task.run()
2525
if callback is not None:

0 commit comments

Comments
 (0)