From d247e49b709d2f8027ecae300495a7ae4e3d2836 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 2 Apr 2018 15:54:49 +0800 Subject: [PATCH] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9celery=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/api.py | 31 +-- apps/common/models.py | 8 - apps/common/urls/api_urls.py | 1 - apps/common/urls/view_urls.py | 2 - apps/common/views.py | 15 - apps/i18n/zh/LC_MESSAGES/django.mo | Bin 30239 -> 30286 bytes apps/i18n/zh/LC_MESSAGES/django.po | 263 +++++++++--------- apps/ops/api.py | 27 +- apps/ops/apps.py | 4 + apps/ops/celery/__init__.py | 1 - apps/ops/celery/signal_handler.py | 63 +++-- apps/ops/models/__init__.py | 5 + apps/ops/{models.py => models/adhoc.py} | 11 +- apps/ops/models/celery.py | 36 +++ apps/ops/tasks.py | 1 - .../templates/ops/adhoc_history_detail.html | 2 +- .../templates/ops/adhoc_history_output.html | 37 --- .../templates/ops}/celery_task_log.html | 2 +- .../ops/templates/ops/celery_task_output.html | 94 ------- apps/ops/templates/ops/task_adhoc.html | 3 + apps/ops/templates/ops/task_detail.html | 2 +- apps/ops/templates/ops/task_history.html | 3 + apps/ops/templates/ops/task_list.html | 4 +- apps/ops/urls/api_urls.py | 2 +- apps/ops/urls/view_urls.py | 3 +- apps/ops/views.py | 21 +- 26 files changed, 250 insertions(+), 391 deletions(-) create mode 100644 apps/ops/models/__init__.py rename apps/ops/{models.py => models/adhoc.py} (97%) create mode 100644 apps/ops/models/celery.py delete mode 100644 apps/ops/templates/ops/adhoc_history_output.html rename apps/{common/templates/common => ops/templates/ops}/celery_task_log.html (97%) delete mode 100644 apps/ops/templates/ops/celery_task_output.html diff --git a/apps/common/api.py b/apps/common/api.py index 11828a81e..4b74b6b0d 100644 --- a/apps/common/api.py +++ b/apps/common/api.py @@ -1,21 +1,15 @@ # -*- coding: utf-8 -*- # -import os import json -import uuid -from django.core.cache import cache -from rest_framework.generics import RetrieveAPIView from rest_framework.views import Response, APIView from ldap3 import Server, Connection from django.core.mail import get_connection, send_mail from django.utils.translation import ugettext_lazy as _ from django.conf import settings -from .permissions import IsSuperUser, IsAppUser +from .permissions import IsSuperUser from .serializers import MailTestSerializer, LDAPTestSerializer -from .celery import FINISHED -from .const import FILE_END_GUARD, celery_task_pre_key class MailTestingAPI(APIView): @@ -112,27 +106,4 @@ class DjangoSettingsAPI(APIView): return Response(configs) -class CeleryTaskLogApi(APIView): - permission_classes = (IsSuperUser,) - buff_size = 1024 * 10 - end = False - def get(self, request, *args, **kwargs): - task_id = kwargs.get('pk') - info = cache.get(celery_task_pre_key + task_id, {}) - log_path = info.get("log_path") - mark = request.query_params.get("mark") or str(uuid.uuid4()) - - if not log_path or not os.path.isfile(log_path): - return Response({"data": _("Waiting ...")}, status=203) - - with open(log_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 data == '' and info["status"] == FINISHED: - self.end = True - return Response({"data": data, 'end': self.end, 'mark': mark}) diff --git a/apps/common/models.py b/apps/common/models.py index 24922300f..c90458985 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -80,11 +80,3 @@ class Setting(models.Model): class Meta: db_table = "settings" - -class CeleryTask(models.Model): - id = models.UUIDField() - name = models.CharField(max_length=1024) - status = models.CharField(max_length=128) - date_published = models.DateTimeField(auto_now_add=True) - date_start = models.DateTimeField(null=True) - date_finished = models.DateTimeField(null=True) \ No newline at end of file diff --git a/apps/common/urls/api_urls.py b/apps/common/urls/api_urls.py index 37629b801..a9075e66e 100644 --- a/apps/common/urls/api_urls.py +++ b/apps/common/urls/api_urls.py @@ -10,5 +10,4 @@ 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/celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'), ] diff --git a/apps/common/urls/view_urls.py b/apps/common/urls/view_urls.py index 0483d4dbb..466f7c49c 100644 --- a/apps/common/urls/view_urls.py +++ b/apps/common/urls/view_urls.py @@ -11,6 +11,4 @@ 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'^celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', views.CeleryTaskLogView.as_view(), name='celery-task-log'), ] diff --git a/apps/common/views.py b/apps/common/views.py index e51400d12..ee7a2225f 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -122,18 +122,3 @@ class TerminalSettingView(AdminUserRequiredMixin, TemplateView): return render(request, self.template_name, context) -class CeleryTaskLogView(AdminUserRequiredMixin, TemplateView): - template_name = 'common/celery_task_log.html' - task_log_path = None - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - task_id = self.kwargs.get("pk") - - if cache.get(celery_task_pre_key+task_id) is None: - raise Http404() - - context.update({ - "task_id": self.kwargs.get("pk") - }) - return context diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index da9fcf171523f8a8a892810b7222767dd1e8c87c..0282d293f7b5e37fba6923114945dc7cdff3444e 100644 GIT binary patch delta 10678 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 diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 78691d4e7..6409056f8 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-03-23 19:50+0800\n" +"POT-Creation-Date: 2018-04-02 15:49+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -51,7 +51,7 @@ msgid "Labels" msgstr "标签管理" #: assets/forms/asset.py:34 assets/forms/asset.py:70 assets/models/asset.py:52 -#: assets/models/domain.py:34 +#: assets/models/domain.py:46 msgid "Domain" msgstr "网域" @@ -74,12 +74,12 @@ msgstr "" #: assets/forms/asset.py:90 assets/forms/asset.py:94 assets/forms/domain.py:16 #: assets/forms/label.py:15 -#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:270 +#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:272 msgid "Select assets" msgstr "选择资产" #: assets/forms/asset.py:99 assets/models/asset.py:51 -#: assets/models/domain.py:32 assets/templates/assets/admin_user_assets.html:53 +#: assets/models/domain.py:44 assets/templates/assets/admin_user_assets.html:53 #: assets/templates/assets/asset_detail.html:69 #: assets/templates/assets/domain_gateway_list.html:58 #: assets/templates/assets/system_user_asset.html:51 @@ -96,7 +96,7 @@ msgid "Select nodes" msgstr "选择节点" #: assets/forms/domain.py:14 assets/forms/label.py:13 -#: assets/models/asset.py:156 assets/templates/assets/admin_user_list.html:25 +#: assets/models/asset.py:165 assets/templates/assets/admin_user_list.html:25 #: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_list.html:15 #: assets/templates/assets/label_list.html:16 @@ -111,7 +111,7 @@ msgstr "资产" #: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:120 #: assets/models/base.py:20 assets/models/cluster.py:18 -#: assets/models/domain.py:15 assets/models/group.py:20 +#: assets/models/domain.py:17 assets/models/group.py:20 #: assets/models/label.py:17 assets/templates/assets/admin_user_detail.html:56 #: assets/templates/assets/admin_user_list.html:23 #: assets/templates/assets/domain_detail.html:56 @@ -121,8 +121,8 @@ msgstr "资产" #: assets/templates/assets/system_user_detail.html:58 #: assets/templates/assets/system_user_list.html:26 common/models.py:26 #: common/templates/common/terminal_setting.html:67 -#: common/templates/common/terminal_setting.html:85 ops/models.py:31 -#: ops/templates/ops/task_detail.html:56 ops/templates/ops/task_list.html:34 +#: common/templates/common/terminal_setting.html:85 ops/models/adhoc.py:36 +#: ops/templates/ops/task_detail.html:59 ops/templates/ops/task_list.html:34 #: perms/models.py:14 perms/templates/perms/asset_permission_detail.html:62 #: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:16 #: terminal/models.py:149 terminal/templates/terminal/terminal_detail.html:43 @@ -190,7 +190,7 @@ msgid "" "than 2 system user" msgstr "高优先级的系统用户将会作为默认登录用户" -#: assets/models/asset.py:49 assets/models/domain.py:31 +#: assets/models/asset.py:49 assets/models/domain.py:43 #: assets/templates/assets/_asset_list_modal.html:21 #: assets/templates/assets/admin_user_assets.html:52 #: assets/templates/assets/asset_detail.html:61 @@ -217,7 +217,7 @@ msgstr "IP" msgid "Hostname" msgstr "主机名" -#: assets/models/asset.py:54 assets/models/domain.py:36 +#: assets/models/asset.py:54 assets/models/domain.py:48 #: assets/models/label.py:20 assets/templates/assets/asset_detail.html:105 #: perms/templates/perms/asset_permission_list.html:70 msgid "Is active" @@ -300,11 +300,11 @@ msgid "Created by" msgstr "创建者" #: assets/models/asset.py:83 assets/models/cluster.py:26 -#: assets/models/domain.py:18 assets/models/group.py:22 +#: assets/models/domain.py:20 assets/models/group.py:22 #: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:64 #: assets/templates/assets/domain_detail.html:68 #: assets/templates/assets/system_user_detail.html:92 -#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:60 +#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:63 #: perms/models.py:23 perms/models.py:80 #: perms/templates/perms/asset_permission_detail.html:90 #: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17 @@ -313,8 +313,8 @@ msgid "Date created" msgstr "创建日期" #: assets/models/asset.py:84 assets/models/base.py:25 -#: assets/models/cluster.py:29 assets/models/domain.py:16 -#: assets/models/domain.py:35 assets/models/group.py:23 +#: assets/models/cluster.py:29 assets/models/domain.py:18 +#: assets/models/domain.py:47 assets/models/group.py:23 #: assets/models/label.py:21 assets/templates/assets/admin_user_detail.html:72 #: assets/templates/assets/admin_user_list.html:29 #: assets/templates/assets/asset_detail.html:125 @@ -323,7 +323,7 @@ msgstr "创建日期" #: assets/templates/assets/domain_list.html:17 #: assets/templates/assets/system_user_detail.html:100 #: assets/templates/assets/system_user_list.html:33 common/models.py:30 -#: ops/models.py:37 perms/models.py:24 perms/models.py:81 +#: ops/models/adhoc.py:42 perms/models.py:24 perms/models.py:81 #: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:26 #: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15 #: users/models/user.py:47 users/templates/users/user_detail.html:111 @@ -387,7 +387,7 @@ msgstr "默认Cluster" msgid "Cluster" msgstr "集群" -#: assets/models/domain.py:33 assets/models/user.py:104 +#: assets/models/domain.py:45 assets/models/user.py:104 #: assets/templates/assets/domain_gateway_list.html:59 #: assets/templates/assets/system_user_detail.html:66 #: assets/templates/assets/system_user_list.html:28 @@ -407,7 +407,7 @@ msgstr "默认资产组" #: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/session_list.html:33 -#: terminal/templates/terminal/session_list.html:71 users/forms.py:218 +#: terminal/templates/terminal/session_list.html:71 users/forms.py:220 #: users/models/user.py:30 users/models/user.py:254 #: users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_list.html:13 users/views/user.py:334 @@ -493,7 +493,7 @@ msgstr "" msgid "推送系统用户到节点资产: {} => {}" msgstr "" -#: assets/tasks.py:431 +#: assets/tasks.py:432 msgid "推送节点系统用户到新加入资产中: {}" msgstr "" @@ -509,9 +509,9 @@ msgstr "仅修改你需要更新的字段" #: assets/templates/assets/system_user_asset.html:21 #: assets/views/admin_user.py:29 assets/views/admin_user.py:47 #: assets/views/admin_user.py:63 assets/views/admin_user.py:78 -#: assets/views/admin_user.py:102 assets/views/asset.py:48 -#: assets/views/asset.py:94 assets/views/asset.py:154 assets/views/asset.py:171 -#: assets/views/asset.py:195 assets/views/domain.py:29 +#: assets/views/admin_user.py:102 assets/views/asset.py:49 +#: assets/views/asset.py:95 assets/views/asset.py:155 assets/views/asset.py:172 +#: assets/views/asset.py:196 assets/views/domain.py:29 #: assets/views/domain.py:45 assets/views/domain.py:61 #: assets/views/domain.py:74 assets/views/domain.py:98 #: assets/views/domain.py:126 assets/views/domain.py:150 @@ -602,8 +602,8 @@ msgstr "可连接" #: assets/templates/assets/domain_list.html:18 #: assets/templates/assets/label_list.html:17 #: assets/templates/assets/system_user_list.html:34 -#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:61 -#: ops/templates/ops/task_history.html:62 ops/templates/ops/task_list.html:41 +#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64 +#: ops/templates/ops/task_history.html:65 ops/templates/ops/task_list.html:41 #: perms/templates/perms/asset_permission_list.html:72 #: terminal/templates/terminal/session_list.html:80 #: terminal/templates/terminal/terminal_list.html:36 @@ -774,9 +774,9 @@ msgstr "重置" #: assets/templates/assets/domain_gateway_list.html:18 #: assets/templates/assets/system_user_asset.html:17 #: assets/templates/assets/system_user_detail.html:18 -#: ops/templates/ops/adhoc_history.html:129 -#: ops/templates/ops/task_adhoc.html:109 -#: ops/templates/ops/task_history.html:132 +#: ops/templates/ops/adhoc_history.html:130 +#: ops/templates/ops/task_adhoc.html:116 +#: ops/templates/ops/task_history.html:136 #: perms/templates/perms/asset_permission_asset.html:18 #: perms/templates/perms/asset_permission_detail.html:18 #: perms/templates/perms/asset_permission_user.html:18 @@ -822,7 +822,7 @@ msgstr "替换资产的管理员" #: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/asset_detail.html:200 -#: assets/templates/assets/asset_list.html:586 +#: assets/templates/assets/asset_list.html:594 #: assets/templates/assets/system_user_detail.html:183 #: assets/templates/assets/system_user_list.html:138 templates/_modal.html:16 #: terminal/templates/terminal/session_detail.html:108 @@ -849,7 +849,7 @@ msgstr "不可达" #: assets/templates/assets/admin_user_list.html:28 #: assets/templates/assets/system_user_list.html:32 #: ops/templates/ops/adhoc_history.html:54 -#: ops/templates/ops/task_history.html:57 +#: ops/templates/ops/task_history.html:60 msgid "Ratio" msgstr "比例" @@ -866,7 +866,7 @@ msgstr "节点" msgid "Label" msgstr "标签" -#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:196 +#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:197 msgid "Asset detail" msgstr "资产详情" @@ -905,7 +905,7 @@ msgid "Update successfully!" msgstr "更新成功" #: assets/templates/assets/asset_list.html:63 -#: assets/templates/assets/asset_list.html:120 assets/views/asset.py:95 +#: assets/templates/assets/asset_list.html:120 assets/views/asset.py:96 msgid "Create asset" msgstr "创建资产" @@ -955,7 +955,7 @@ msgstr "创建节点失败" msgid "Have child node, cancel" msgstr "存在子节点,不能删除" -#: assets/templates/assets/asset_list.html:581 +#: assets/templates/assets/asset_list.html:589 #: assets/templates/assets/system_user_list.html:133 #: users/templates/users/user_detail.html:334 #: users/templates/users/user_detail.html:359 @@ -964,20 +964,20 @@ msgstr "存在子节点,不能删除" msgid "Are you sure?" msgstr "你确认吗?" -#: assets/templates/assets/asset_list.html:582 +#: assets/templates/assets/asset_list.html:590 msgid "This will delete the selected assets !!!" msgstr "删除选择资产" -#: assets/templates/assets/asset_list.html:590 +#: assets/templates/assets/asset_list.html:598 msgid "Asset Deleted." msgstr "已被删除" -#: assets/templates/assets/asset_list.html:591 -#: assets/templates/assets/asset_list.html:596 +#: assets/templates/assets/asset_list.html:599 +#: assets/templates/assets/asset_list.html:604 msgid "Asset Delete" msgstr "删除" -#: assets/templates/assets/asset_list.html:595 +#: assets/templates/assets/asset_list.html:603 msgid "Asset Deleting failed." msgstr "删除失败" @@ -1104,23 +1104,23 @@ msgstr "更新管理用户" msgid "Admin user detail" msgstr "管理用户详情" -#: assets/views/asset.py:49 templates/_nav.html:23 +#: assets/views/asset.py:50 templates/_nav.html:23 msgid "Asset list" msgstr "资产列表" -#: assets/views/asset.py:61 templates/_nav_user.html:4 +#: assets/views/asset.py:62 templates/_nav_user.html:4 msgid "My assets" msgstr "我的资产" -#: assets/views/asset.py:155 +#: assets/views/asset.py:156 msgid "Bulk update asset" msgstr "批量更新资产" -#: assets/views/asset.py:172 +#: assets/views/asset.py:173 msgid "Update asset" msgstr "更新资产" -#: assets/views/asset.py:296 +#: assets/views/asset.py:299 msgid "already exists" msgstr "已经存在" @@ -1172,15 +1172,15 @@ msgstr "资产管理" msgid "System user asset" msgstr "系统用户集群资产" -#: common/api.py:19 +#: common/api.py:18 msgid "Test mail sent to {}, please check" msgstr "邮件已经发送{}, 请检查" -#: common/api.py:53 +#: common/api.py:52 msgid "Test ldap success" msgstr "连接LDAP成功" -#: common/api.py:91 +#: common/api.py:90 msgid "Match {} s users" msgstr "匹配 {} 个用户" @@ -1299,7 +1299,7 @@ msgstr "资产列表排序" msgid "Heartbeat interval" msgstr "心跳间隔" -#: common/forms.py:150 ops/models.py:32 +#: common/forms.py:150 ops/models/adhoc.py:37 msgid "Units: seconds" msgstr "单位: 秒" @@ -1349,28 +1349,28 @@ msgstr "启用" #: common/templates/common/email_setting.html:15 #: common/templates/common/ldap_setting.html:15 #: common/templates/common/terminal_setting.html:16 -#: common/templates/common/terminal_setting.html:42 common/views.py:21 +#: common/templates/common/terminal_setting.html:42 common/views.py:22 msgid "Basic setting" msgstr "基本设置" #: common/templates/common/basic_setting.html:18 #: common/templates/common/email_setting.html:18 #: common/templates/common/ldap_setting.html:18 -#: common/templates/common/terminal_setting.html:20 common/views.py:47 +#: common/templates/common/terminal_setting.html:20 common/views.py:48 msgid "Email setting" msgstr "邮件设置" #: common/templates/common/basic_setting.html:21 #: common/templates/common/email_setting.html:21 #: common/templates/common/ldap_setting.html:21 -#: common/templates/common/terminal_setting.html:24 common/views.py:73 +#: common/templates/common/terminal_setting.html:24 common/views.py:74 msgid "LDAP setting" msgstr "LDAP设置" #: common/templates/common/basic_setting.html:24 #: common/templates/common/email_setting.html:24 #: common/templates/common/ldap_setting.html:24 -#: common/templates/common/terminal_setting.html:28 common/views.py:103 +#: common/templates/common/terminal_setting.html:28 common/views.py:104 msgid "Terminal setting" msgstr "终端设置" @@ -1380,97 +1380,101 @@ msgstr "终端设置" msgid "Type" msgstr "类型" -#: common/views.py:20 common/views.py:46 common/views.py:72 common/views.py:102 +#: common/views.py:21 common/views.py:47 common/views.py:73 common/views.py:103 #: templates/_nav.html:73 msgid "Settings" msgstr "系统设置" -#: common/views.py:31 common/views.py:57 common/views.py:85 common/views.py:115 +#: common/views.py:32 common/views.py:58 common/views.py:86 common/views.py:116 msgid "Update setting successfully, please restart program" msgstr "更新设置成功, 请手动重启程序" -#: ops/models.py:32 +#: ops/api.py:79 +msgid "Waiting ..." +msgstr "" + +#: ops/models/adhoc.py:37 msgid "Interval" msgstr "间隔" -#: ops/models.py:33 +#: ops/models/adhoc.py:38 msgid "Crontab" msgstr "Crontab" -#: ops/models.py:33 +#: ops/models/adhoc.py:38 msgid "5 * * * *" msgstr "" -#: ops/models.py:35 +#: ops/models/adhoc.py:40 msgid "Callback" msgstr "回调" -#: ops/models.py:149 ops/templates/ops/adhoc_detail.html:114 +#: ops/models/adhoc.py:154 ops/templates/ops/adhoc_detail.html:114 msgid "Tasks" msgstr "任务" -#: ops/models.py:150 ops/templates/ops/adhoc_detail.html:57 -#: ops/templates/ops/task_adhoc.html:57 +#: ops/models/adhoc.py:155 ops/templates/ops/adhoc_detail.html:57 +#: ops/templates/ops/task_adhoc.html:60 msgid "Pattern" msgstr "" -#: ops/models.py:151 ops/templates/ops/adhoc_detail.html:61 +#: ops/models/adhoc.py:156 ops/templates/ops/adhoc_detail.html:61 msgid "Options" msgstr "选项" -#: ops/models.py:152 ops/templates/ops/adhoc_detail.html:53 -#: ops/templates/ops/task_adhoc.html:56 ops/templates/ops/task_list.html:37 +#: ops/models/adhoc.py:157 ops/templates/ops/adhoc_detail.html:53 +#: ops/templates/ops/task_adhoc.html:59 ops/templates/ops/task_list.html:37 msgid "Hosts" msgstr "主机" -#: ops/models.py:153 +#: ops/models/adhoc.py:158 msgid "Run as admin" msgstr "再次执行" -#: ops/models.py:154 ops/templates/ops/adhoc_detail.html:72 -#: ops/templates/ops/adhoc_detail.html:77 ops/templates/ops/task_adhoc.html:58 +#: ops/models/adhoc.py:159 ops/templates/ops/adhoc_detail.html:72 +#: ops/templates/ops/adhoc_detail.html:77 ops/templates/ops/task_adhoc.html:61 msgid "Run as" msgstr "用户" -#: ops/models.py:155 ops/templates/ops/adhoc_detail.html:82 -#: ops/templates/ops/task_adhoc.html:59 +#: ops/models/adhoc.py:160 ops/templates/ops/adhoc_detail.html:82 +#: ops/templates/ops/task_adhoc.html:62 msgid "Become" msgstr "Become" -#: ops/models.py:156 users/templates/users/user_group_detail.html:59 +#: ops/models/adhoc.py:161 users/templates/users/user_group_detail.html:59 msgid "Create by" msgstr "创建者" -#: ops/models.py:307 +#: ops/models/adhoc.py:323 msgid "Start time" msgstr "开始时间" -#: ops/models.py:308 +#: ops/models/adhoc.py:324 msgid "End time" msgstr "完成时间" -#: ops/models.py:309 ops/templates/ops/adhoc_history.html:57 -#: ops/templates/ops/task_history.html:60 ops/templates/ops/task_list.html:40 +#: ops/models/adhoc.py:325 ops/templates/ops/adhoc_history.html:57 +#: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:40 msgid "Time" msgstr "时间" -#: ops/models.py:310 ops/templates/ops/adhoc_detail.html:106 +#: ops/models/adhoc.py:326 ops/templates/ops/adhoc_detail.html:106 #: ops/templates/ops/adhoc_history.html:55 -#: ops/templates/ops/adhoc_history_detail.html:66 -#: ops/templates/ops/task_detail.html:80 ops/templates/ops/task_history.html:58 +#: ops/templates/ops/adhoc_history_detail.html:69 +#: ops/templates/ops/task_detail.html:83 ops/templates/ops/task_history.html:61 msgid "Is finished" msgstr "是否完成" -#: ops/models.py:311 ops/templates/ops/adhoc_history.html:56 -#: ops/templates/ops/task_history.html:59 +#: ops/models/adhoc.py:327 ops/templates/ops/adhoc_history.html:56 +#: ops/templates/ops/task_history.html:62 msgid "Is success" msgstr "是否成功" -#: ops/models.py:312 +#: ops/models/adhoc.py:328 msgid "Adhoc raw result" msgstr "结果" -#: ops/models.py:313 +#: ops/models/adhoc.py:329 msgid "Adhoc result summary" msgstr "汇总" @@ -1485,8 +1489,8 @@ msgid "Version run history" msgstr "执行历史" #: ops/templates/ops/adhoc_detail.html:49 -#: ops/templates/ops/adhoc_history_detail.html:46 -#: ops/templates/ops/task_detail.html:52 +#: ops/templates/ops/adhoc_history_detail.html:49 +#: ops/templates/ops/task_detail.html:55 #: terminal/templates/terminal/session_list.html:70 #: users/templates/users/login_log_list.html:48 msgid "ID" @@ -1496,59 +1500,59 @@ msgstr "ID" msgid "Run times" msgstr "执行次数" -#: ops/templates/ops/adhoc_detail.html:98 ops/templates/ops/task_detail.html:72 +#: ops/templates/ops/adhoc_detail.html:98 ops/templates/ops/task_detail.html:75 msgid "Last run" msgstr "最后运行" #: ops/templates/ops/adhoc_detail.html:102 -#: ops/templates/ops/adhoc_history_detail.html:62 -#: ops/templates/ops/task_detail.html:76 +#: ops/templates/ops/adhoc_history_detail.html:65 +#: ops/templates/ops/task_detail.html:79 msgid "Time delta" msgstr "运行时间" #: ops/templates/ops/adhoc_detail.html:110 -#: ops/templates/ops/adhoc_history_detail.html:70 -#: ops/templates/ops/task_detail.html:84 +#: ops/templates/ops/adhoc_history_detail.html:73 +#: ops/templates/ops/task_detail.html:87 msgid "Is success " msgstr "成功" #: ops/templates/ops/adhoc_detail.html:131 -#: ops/templates/ops/task_detail.html:105 +#: ops/templates/ops/task_detail.html:108 msgid "Last run failed hosts" msgstr "最后运行失败主机" #: ops/templates/ops/adhoc_detail.html:151 #: ops/templates/ops/adhoc_detail.html:176 -#: ops/templates/ops/task_detail.html:125 -#: ops/templates/ops/task_detail.html:150 +#: ops/templates/ops/task_detail.html:128 +#: ops/templates/ops/task_detail.html:153 msgid "No hosts" msgstr "没有主机" #: ops/templates/ops/adhoc_detail.html:161 -#: ops/templates/ops/task_detail.html:135 +#: ops/templates/ops/task_detail.html:138 msgid "Last run success hosts" msgstr "最后运行成功主机" #: ops/templates/ops/adhoc_history.html:30 -#: ops/templates/ops/task_history.html:33 +#: ops/templates/ops/task_history.html:36 msgid "History of " msgstr "执行历史" #: ops/templates/ops/adhoc_history.html:52 -#: ops/templates/ops/adhoc_history_detail.html:58 -#: ops/templates/ops/task_history.html:55 terminal/models.py:132 +#: ops/templates/ops/adhoc_history_detail.html:61 +#: ops/templates/ops/task_history.html:58 terminal/models.py:132 #: terminal/templates/terminal/session_list.html:77 msgid "Date start" msgstr "开始日期" #: ops/templates/ops/adhoc_history.html:53 -#: ops/templates/ops/task_history.html:56 +#: ops/templates/ops/task_history.html:59 msgid "F/S/T" msgstr "失败/成功/总" #: ops/templates/ops/adhoc_history.html:58 -#: ops/templates/ops/adhoc_history_detail.html:54 -#: ops/templates/ops/task_adhoc.html:55 ops/templates/ops/task_history.html:61 +#: ops/templates/ops/adhoc_history_detail.html:57 +#: ops/templates/ops/task_adhoc.html:58 ops/templates/ops/task_history.html:64 msgid "Version" msgstr "版本" @@ -1556,24 +1560,29 @@ msgstr "版本" msgid "Run history detail" msgstr "执行历史详情" -#: ops/templates/ops/adhoc_history_detail.html:27 +#: ops/templates/ops/adhoc_history_detail.html:22 +#: terminal/backends/command/models.py:14 +msgid "Output" +msgstr "输出" + +#: ops/templates/ops/adhoc_history_detail.html:30 msgid "History detail of" msgstr "执行历史详情" -#: ops/templates/ops/adhoc_history_detail.html:50 +#: ops/templates/ops/adhoc_history_detail.html:53 msgid "Task name" msgstr "任务名称" -#: ops/templates/ops/adhoc_history_detail.html:81 +#: ops/templates/ops/adhoc_history_detail.html:84 msgid "Failed assets" msgstr "失败资产" -#: ops/templates/ops/adhoc_history_detail.html:101 -#: ops/templates/ops/adhoc_history_detail.html:126 +#: ops/templates/ops/adhoc_history_detail.html:104 +#: ops/templates/ops/adhoc_history_detail.html:129 msgid "No assets" msgstr "没有资产" -#: ops/templates/ops/adhoc_history_detail.html:111 +#: ops/templates/ops/adhoc_history_detail.html:114 msgid "Success assets" msgstr "成功资产" @@ -1592,25 +1601,30 @@ msgstr "任务各版本" msgid "Run history" msgstr "执行历史" -#: ops/templates/ops/task_adhoc.html:33 +#: ops/templates/ops/task_adhoc.html:28 ops/templates/ops/task_detail.html:28 +#: ops/templates/ops/task_history.html:28 +msgid "Last run output" +msgstr "输出" + +#: ops/templates/ops/task_adhoc.html:36 msgid "Versions of " msgstr "版本" -#: ops/templates/ops/task_adhoc.html:60 +#: ops/templates/ops/task_adhoc.html:63 #: terminal/templates/terminal/command_list.html:76 #: terminal/templates/terminal/session_detail.html:50 msgid "Datetime" msgstr "日期" -#: ops/templates/ops/task_detail.html:64 +#: ops/templates/ops/task_detail.html:67 msgid "Total versions" msgstr "版本数量" -#: ops/templates/ops/task_detail.html:68 +#: ops/templates/ops/task_detail.html:71 msgid "Latest version" msgstr "最新版本" -#: ops/templates/ops/task_detail.html:88 +#: ops/templates/ops/task_detail.html:91 msgid "Contents" msgstr "内容" @@ -1639,7 +1653,7 @@ msgstr "日期" msgid "Run" msgstr "执行" -#: ops/templates/ops/task_list.html:123 +#: ops/templates/ops/task_list.html:124 msgid "Task start: " msgstr "任务开始: " @@ -1705,7 +1719,7 @@ msgstr "添加" msgid "Add asset group to this permission" msgstr "添加资产组" -#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:273 +#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:275 msgid "Select asset groups" msgstr "选择资产组" @@ -1734,7 +1748,7 @@ msgstr "资产组数量" msgid "System user count" msgstr "系统用户数量" -#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:276 +#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:278 msgid "Select system users" msgstr "选择系统用户" @@ -1787,7 +1801,7 @@ msgstr "商业支持" msgid "Docs" msgstr "文档" -#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:92 +#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:94 #: users/templates/users/_user.html:36 #: users/templates/users/user_password_update.html:37 #: users/templates/users/user_profile.html:17 @@ -1912,10 +1926,6 @@ msgstr "过滤" msgid "Input" msgstr "输入" -#: terminal/backends/command/models.py:14 -msgid "Output" -msgstr "输出" - #: terminal/backends/command/models.py:15 #: terminal/templates/terminal/command_list.html:75 #: terminal/templates/terminal/terminal_list.html:33 @@ -2179,11 +2189,11 @@ msgstr "" msgid "Role" msgstr "角色" -#: users/forms.py:30 users/forms.py:138 +#: users/forms.py:30 users/forms.py:140 msgid "ssh public key" msgstr "ssh公钥" -#: users/forms.py:31 users/forms.py:139 +#: users/forms.py:31 users/forms.py:141 msgid "ssh-rsa AAAA..." msgstr "" @@ -2195,39 +2205,39 @@ msgstr "复制用户公钥到这里" msgid "Join user groups" msgstr "添加到用户组" -#: users/forms.py:58 users/forms.py:153 +#: users/forms.py:60 users/forms.py:155 msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:62 users/forms.py:157 users/serializers.py:42 +#: users/forms.py:64 users/forms.py:159 users/serializers.py:42 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" -#: users/forms.py:98 +#: users/forms.py:100 msgid "Old password" msgstr "原来密码" -#: users/forms.py:103 +#: users/forms.py:105 msgid "New password" msgstr "新密码" -#: users/forms.py:108 +#: users/forms.py:110 msgid "Confirm password" msgstr "确认密码" -#: users/forms.py:118 +#: users/forms.py:120 msgid "Old password error" msgstr "原来密码错误" -#: users/forms.py:126 +#: users/forms.py:128 msgid "Password does not match" msgstr "密码不一致" -#: users/forms.py:140 +#: users/forms.py:142 msgid "Paste your id_rsa.pub here." msgstr "复制你的公钥到这里" -#: users/forms.py:168 users/models/user.py:46 +#: users/forms.py:170 users/models/user.py:46 #: users/templates/users/user_password_update.html:43 #: users/templates/users/user_profile.html:71 #: users/templates/users/user_profile_update.html:43 @@ -2235,7 +2245,7 @@ msgstr "复制你的公钥到这里" msgid "Public key" msgstr "ssh公钥" -#: users/forms.py:175 users/forms.py:180 users/forms.py:192 users/forms.py:222 +#: users/forms.py:177 users/forms.py:182 users/forms.py:194 users/forms.py:224 msgid "Select users" msgstr "选择用户" @@ -2777,4 +2787,3 @@ msgstr "密码更新" #: users/views/user.py:375 msgid "Public key update" msgstr "密钥更新" - diff --git a/apps/ops/api.py b/apps/ops/api.py index db27653cb..0134fbd4a 100644 --- a/apps/ops/api.py +++ b/apps/ops/api.py @@ -6,18 +6,15 @@ 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.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 .models import Task, AdHoc, AdHocRunHistory, CeleryTask +from .serializers import TaskSerializer, AdHocSerializer, \ + AdHocRunHistorySerializer from .tasks import run_ansible_task - - class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer @@ -67,28 +64,28 @@ class AdHocRunHistorySet(viewsets.ModelViewSet): return self.queryset -class LogFileViewApi(APIView): +class CeleryTaskLogApi(generics.RetrieveAPIView): permission_classes = (IsSuperUser,) buff_size = 1024 * 10 end = False + queryset = CeleryTask.objects.all() def get(self, request, *args, **kwargs): - file_path = request.query_params.get("file") mark = request.query_params.get("mark") or str(uuid.uuid4()) + task = super().get_object() + log_path = task.full_log_path - if not os.path.isfile(file_path): - print(file_path) - return Response({"error": _("Log file not found")}, status=204) + if not log_path or not os.path.isfile(log_path): + return Response({"data": _("Waiting ...")}, status=203) - with open(file_path, 'r') as f: + with open(log_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.replace(FILE_END_GUARD, '') + if data == '' and task.is_finished(): self.end = True - return Response({"data": data, 'end': self.end, 'mark': mark}) + diff --git a/apps/ops/apps.py b/apps/ops/apps.py index 35f09c4b7..d5d6879b1 100644 --- a/apps/ops/apps.py +++ b/apps/ops/apps.py @@ -5,3 +5,7 @@ from django.apps import AppConfig class OpsConfig(AppConfig): name = 'ops' + + def ready(self): + super().ready() + from .celery import signal_handler diff --git a/apps/ops/celery/__init__.py b/apps/ops/celery/__init__.py index 9cdcb3063..04f48299e 100644 --- a/apps/ops/celery/__init__.py +++ b/apps/ops/celery/__init__.py @@ -15,4 +15,3 @@ app = Celery('jumpserver') # pickle the object when using Windows. app.config_from_object('django.conf:settings', namespace='CELERY') app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS]) - diff --git a/apps/ops/celery/signal_handler.py b/apps/ops/celery/signal_handler.py index 6ce6c5d9d..8309c0246 100644 --- a/apps/ops/celery/signal_handler.py +++ b/apps/ops/celery/signal_handler.py @@ -5,27 +5,21 @@ import datetime import sys from django.conf import settings +from django.utils import timezone from django.core.cache import cache +from django.db import transaction from celery import subtask from celery.signals import worker_ready, worker_shutdown, task_prerun, \ task_postrun, after_task_publish - from django_celery_beat.models import PeriodicTask -from common.utils import get_logger, TeeObj +from common.utils import get_logger, TeeObj, get_object_or_none from common.const import celery_task_pre_key - from .utils import get_after_app_ready_tasks, get_after_app_shutdown_clean_tasks - +from ..models import CeleryTask logger = get_logger(__file__) -WAITING = "waiting" -RUNNING = "running" -FINISHED = "finished" - -EXPIRE_TIME = 3600 - @worker_ready.connect def on_app_ready(sender=None, headers=None, body=None, **kwargs): @@ -54,18 +48,31 @@ def after_app_shutdown(sender=None, headers=None, body=None, **kwargs): PeriodicTask.objects.filter(name__in=tasks).delete() +@after_task_publish.connect +def after_task_publish_signal_handler(sender, headers=None, **kwargs): + CeleryTask.objects.create( + id=headers["id"], status=CeleryTask.WAITING, name=headers["task"] + ) + + @task_prerun.connect def pre_run_task_signal_handler(sender, task_id=None, task=None, **kwargs): - task_key = celery_task_pre_key + task_id - info = cache.get(task_key, {}) + t = get_object_or_none(CeleryTask, id=task_id) + if t is None: + logger.warn("Not get the task: {}".format(task_id)) + return now = datetime.datetime.now().strftime("%Y-%m-%d") - log_dir = os.path.join(settings.PROJECT_DIR, "data", "celery", now) - if not os.path.exists(log_dir): - os.makedirs(log_dir) - log_path = os.path.join(log_dir, task_id + '.log') - info.update({"status": RUNNING, "log_path": log_path}) - cache.set(task_key, info, EXPIRE_TIME) - f = open(log_path, 'w') + log_path = os.path.join(now, task_id + '.log') + full_path = os.path.join(CeleryTask.LOG_DIR, log_path) + + if not os.path.exists(os.path.dirname(full_path)): + os.makedirs(os.path.dirname(full_path)) + with transaction.atomic(): + t.date_start = timezone.now() + t.status = CeleryTask.RUNNING + t.log_path = log_path + t.save() + f = open(full_path, 'w') tee = TeeObj(f) sys.stdout = tee task.log_f = tee @@ -73,17 +80,15 @@ def pre_run_task_signal_handler(sender, task_id=None, task=None, **kwargs): @task_postrun.connect def post_run_task_signal_handler(sender, task_id=None, task=None, **kwargs): - task_key = celery_task_pre_key + task_id - info = cache.get(task_key, {}) - info.update({"status": FINISHED}) - cache.set(task_key, info, EXPIRE_TIME) + t = get_object_or_none(CeleryTask, id=task_id) + if t is None: + logger.warn("Not get the task: {}".format(task_id)) + return + with transaction.atomic(): + t.status = CeleryTask.FINISHED + t.date_finished = timezone.now() + t.save() task.log_f.flush() sys.stdout = task.log_f.origin_stdout task.log_f.close() - -@after_task_publish.connect -def after_task_publish_signal_handler(sender, headers=None, **kwargs): - task_id = headers["id"] - key = celery_task_pre_key + task_id - cache.set(key, {"status": WAITING}, EXPIRE_TIME) \ No newline at end of file diff --git a/apps/ops/models/__init__.py b/apps/ops/models/__init__.py new file mode 100644 index 000000000..68920eb42 --- /dev/null +++ b/apps/ops/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# + +from .adhoc import * +from .celery import * \ No newline at end of file diff --git a/apps/ops/models.py b/apps/ops/models/adhoc.py similarity index 97% rename from apps/ops/models.py rename to apps/ops/models/adhoc.py index 636d27b0c..beba34fe6 100644 --- a/apps/ops/models.py +++ b/apps/ops/models/adhoc.py @@ -11,15 +11,14 @@ from django.db import models from django.conf import settings from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from django_celery_beat.models import CrontabSchedule, IntervalSchedule, \ - PeriodicTask +from django_celery_beat.models import PeriodicTask from common.utils import get_signer, get_logger -from .celery.utils import delete_celery_periodic_task, \ +from ..celery.utils import delete_celery_periodic_task, \ create_or_update_celery_periodic_tasks, \ disable_celery_periodic_task -from .ansible import AdHocRunner, AnsibleError -from .inventory import JMSInventory +from ..ansible import AdHocRunner, AnsibleError +from ..inventory import JMSInventory __all__ = ["Task", "AdHoc", "AdHocRunHistory"] @@ -91,7 +90,7 @@ class Task(models.Model): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): - from .tasks import run_ansible_task + from ..tasks import run_ansible_task super().save( force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields, diff --git a/apps/ops/models/celery.py b/apps/ops/models/celery.py new file mode 100644 index 000000000..0e3b99d19 --- /dev/null +++ b/apps/ops/models/celery.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# +import uuid +import os +from django.conf import settings +from django.db import models + + +class CeleryTask(models.Model): + WAITING = "waiting" + RUNNING = "running" + FINISHED = "finished" + LOG_DIR = os.path.join(settings.PROJECT_DIR, 'data', 'celery') + + STATUS_CHOICES = ( + (WAITING, WAITING), + (RUNNING, RUNNING), + (FINISHED, FINISHED), + ) + id = models.UUIDField(primary_key=True, default=uuid.uuid4) + name = models.CharField(max_length=1024) + status = models.CharField(max_length=128, choices=STATUS_CHOICES) + log_path = models.CharField(max_length=256, blank=True, null=True) + date_published = models.DateTimeField(auto_now_add=True) + date_start = models.DateTimeField(null=True) + date_finished = models.DateTimeField(null=True) + + def __str__(self): + return "{}: {}".format(self.name, self.id) + + def is_finished(self): + return self.status == self.FINISHED + + @property + def full_log_path(self): + return os.path.join(self.LOG_DIR, self.log_path) diff --git a/apps/ops/tasks.py b/apps/ops/tasks.py index 5a2738cd9..8df803207 100644 --- a/apps/ops/tasks.py +++ b/apps/ops/tasks.py @@ -1,7 +1,6 @@ # coding: utf-8 from celery import shared_task, subtask - from common.utils import get_logger, get_object_or_none from .models import Task diff --git a/apps/ops/templates/ops/adhoc_history_detail.html b/apps/ops/templates/ops/adhoc_history_detail.html index 15c26376f..16adbc4e3 100644 --- a/apps/ops/templates/ops/adhoc_history_detail.html +++ b/apps/ops/templates/ops/adhoc_history_detail.html @@ -19,7 +19,7 @@ {% trans 'Run history detail' %}
  • - {% trans 'History output' %} + {% trans 'Output' %}
  • diff --git a/apps/ops/templates/ops/adhoc_history_output.html b/apps/ops/templates/ops/adhoc_history_output.html deleted file mode 100644 index 781c2adf5..000000000 --- a/apps/ops/templates/ops/adhoc_history_output.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends 'base.html' %} -{% load static %} -{% load i18n %} - -{% block custom_head_css_js %} - - - - - -{% endblock %} -{% block content %} -
    - -
    -{% endblock %} - diff --git a/apps/common/templates/common/celery_task_log.html b/apps/ops/templates/ops/celery_task_log.html similarity index 97% rename from apps/common/templates/common/celery_task_log.html rename to apps/ops/templates/ops/celery_task_log.html index 23b676dd2..9b0826949 100644 --- a/apps/common/templates/common/celery_task_log.html +++ b/apps/ops/templates/ops/celery_task_log.html @@ -35,7 +35,7 @@ var rowHeight = 1; var colWidth = 1; var mark = ''; - var url = "{% url 'api-common:celery-task-log' pk=task_id %}"; + var url = "{% url 'api-ops:celery-task-log' pk=object.id %}"; var term; var end = false; var error = false; diff --git a/apps/ops/templates/ops/celery_task_output.html b/apps/ops/templates/ops/celery_task_output.html deleted file mode 100644 index d48b35dcf..000000000 --- a/apps/ops/templates/ops/celery_task_output.html +++ /dev/null @@ -1,94 +0,0 @@ -{% load static %} - - term.js - - - -
    -
    -
    -
    - - - - diff --git a/apps/ops/templates/ops/task_adhoc.html b/apps/ops/templates/ops/task_adhoc.html index 381048602..2762e3f8c 100644 --- a/apps/ops/templates/ops/task_adhoc.html +++ b/apps/ops/templates/ops/task_adhoc.html @@ -24,6 +24,9 @@
  • {% trans 'Run history' %}
  • +
  • + {% trans 'Last run output' %} +
  • diff --git a/apps/ops/templates/ops/task_detail.html b/apps/ops/templates/ops/task_detail.html index 4aba9998a..f6e832bd7 100644 --- a/apps/ops/templates/ops/task_detail.html +++ b/apps/ops/templates/ops/task_detail.html @@ -25,7 +25,7 @@ {% trans 'Run history' %}
  • - {% trans 'Last run output' %} + {% trans 'Last run output' %}
  • diff --git a/apps/ops/templates/ops/task_history.html b/apps/ops/templates/ops/task_history.html index 92401a0f9..9604b3402 100644 --- a/apps/ops/templates/ops/task_history.html +++ b/apps/ops/templates/ops/task_history.html @@ -24,6 +24,9 @@
  • {% trans 'Run history' %}
  • +
  • + {% trans 'Last run output' %} +
  • diff --git a/apps/ops/templates/ops/task_list.html b/apps/ops/templates/ops/task_list.html index af4c8ea8f..4dc24e7ba 100644 --- a/apps/ops/templates/ops/task_list.html +++ b/apps/ops/templates/ops/task_list.html @@ -113,8 +113,8 @@ $(document).ready(function() { }; var success = function(data) { var task_id = data.task; - var url = '{% url "common:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); - window.open(url, '', 'width=800,height=800') + var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); + window.open(url, '', 'width=800,height=600') }; APIUpdateAttr({ url: the_url, diff --git a/apps/ops/urls/api_urls.py b/apps/ops/urls/api_urls.py index a5f94da29..d6390fed9 100644 --- a/apps/ops/urls/api_urls.py +++ b/apps/ops/urls/api_urls.py @@ -15,7 +15,7 @@ router.register(r'v1/history', api.AdHocRunHistorySet, 'history') urlpatterns = [ url(r'^v1/tasks/(?P[0-9a-zA-Z\-]{36})/run/$', api.TaskRun.as_view(), name='task-run'), - # url(r'^v1/history/(?P[0-9a-zA-Z\-]{36})/output/$', api.CeleryTaskOutputApi.as_view(), name='history-output'), + url(r'^v1/celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'), ] urlpatterns += router.urls diff --git a/apps/ops/urls/view_urls.py b/apps/ops/urls/view_urls.py index e6979bbf3..470d6f06d 100644 --- a/apps/ops/urls/view_urls.py +++ b/apps/ops/urls/view_urls.py @@ -18,6 +18,5 @@ urlpatterns = [ url(r'^adhoc/(?P[0-9a-zA-Z\-]{36})/$', views.AdHocDetailView.as_view(), name='adhoc-detail'), url(r'^adhoc/(?P[0-9a-zA-Z\-]{36})/history/$', views.AdHocHistoryView.as_view(), name='adhoc-history'), url(r'^adhoc/history/(?P[0-9a-zA-Z\-]{36})/$', views.AdHocHistoryDetailView.as_view(), name='adhoc-history-detail'), - url(r'^adhoc/history/(?P[0-9a-zA-Z\-]{36})/_output/$', views.CeleryTaskOutputView.as_view(), name='adhoc-history-output-alone'), - url(r'^adhoc/history/(?P[0-9a-zA-Z\-]{36})/output/$', views.AdHocHistoryOutputView.as_view(), name='adhoc-history-output'), + url(r'^celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', views.CeleryTaskLogView.as_view(), name='celery-task-log'), ] diff --git a/apps/ops/views.py b/apps/ops/views.py index 5a294e7c7..e3ba2789a 100644 --- a/apps/ops/views.py +++ b/apps/ops/views.py @@ -5,7 +5,7 @@ from django.conf import settings from django.views.generic import ListView, DetailView, TemplateView from common.mixins import DatetimeSearchMixin -from .models import Task, AdHoc, AdHocRunHistory +from .models import Task, AdHoc, AdHocRunHistory, CeleryTask from .hands import AdminUserRequiredMixin @@ -121,19 +121,6 @@ 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' - - def get_context_data(self, **kwargs): - context = { - 'app': _('Ops'), - 'action': _('Run history detail'), - } - kwargs.update(context) - return super().get_context_data(**kwargs) \ No newline at end of file +class CeleryTaskLogView(AdminUserRequiredMixin, DetailView): + template_name = 'ops/celery_task_log.html' + model = CeleryTask