diff --git a/apps/assets/api/admin_user.py b/apps/assets/api/admin_user.py index e32bbe02a..968cd6594 100644 --- a/apps/assets/api/admin_user.py +++ b/apps/assets/api/admin_user.py @@ -79,5 +79,5 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView): def retrieve(self, request, *args, **kwargs): admin_user = self.get_object() - test_admin_user_connectability_manual.delay(admin_user) - return Response({"msg": "Task created"}) \ No newline at end of file + task = test_admin_user_connectability_manual.delay(admin_user) + return Response({"task": task.id}) diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index 405beaf53..9520ff120 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -87,12 +87,8 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView): def retrieve(self, request, *args, **kwargs): asset_id = kwargs.get('pk') asset = get_object_or_404(Asset, pk=asset_id) - summary = update_asset_hardware_info_manual(asset)[1] - logger.debug("Refresh summary: {}".format(summary)) - if summary.get('dark'): - return Response(summary['dark'].values(), status=501) - else: - return Response({"msg": "ok"}) + task = update_asset_hardware_info_manual.delay(asset) + return Response({"task": task.id}) class AssetAdminUserTestApi(generics.RetrieveAPIView): @@ -105,8 +101,5 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView): def retrieve(self, request, *args, **kwargs): asset_id = kwargs.get('pk') asset = get_object_or_404(Asset, pk=asset_id) - ok, msg = test_asset_connectability_manual(asset) - if ok: - return Response({"msg": "pong"}) - else: - return Response({"error": msg}, status=502) \ No newline at end of file + task = test_asset_connectability_manual.delay(asset) + return Response({"task": task.id}) diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index 9c002b271..3b61ceb51 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -130,10 +130,9 @@ class RefreshNodeHardwareInfoApi(APIView): node_id = kwargs.get('pk') node = get_object_or_404(self.model, id=node_id) assets = node.assets.all() - # task_name = _("Refresh node assets hardware info: {}".format(node.name)) task_name = _("更新节点资产硬件信息: {}".format(node.name)) - update_assets_hardware_info_util.delay(assets, task_name=task_name) - return Response({"msg": "Task created"}) + task = update_assets_hardware_info_util.delay(assets, task_name=task_name) + return Response({"task": task.id}) class TestNodeConnectiveApi(APIView): @@ -145,6 +144,6 @@ class TestNodeConnectiveApi(APIView): node = get_object_or_404(self.model, id=node_id) assets = node.assets.all() task_name = _("测试节点下资产是否可连接: {}".format(node.name)) - test_asset_connectability_util.delay(assets, task_name=task_name) - return Response({"msg": "Task created"}) + task = test_asset_connectability_util.delay(assets, task_name=task_name) + return Response({"task": task.id}) diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index ca2a6b7f0..b4e46cc78 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -58,8 +58,8 @@ class SystemUserPushApi(generics.RetrieveAPIView): def retrieve(self, request, *args, **kwargs): system_user = self.get_object() - push_system_user_to_assets_manual.delay(system_user) - return Response({"msg": "Task created"}) + task = push_system_user_to_assets_manual.delay(system_user) + return Response({"task": task.id}) class SystemUserTestConnectiveApi(generics.RetrieveAPIView): @@ -71,5 +71,5 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView): def retrieve(self, request, *args, **kwargs): system_user = self.get_object() - test_system_user_connectability_manual.delay(system_user) - return Response({"msg": "Task created"}) \ No newline at end of file + task = test_system_user_connectability_manual.delay(system_user) + return Response({"task": task.id}) diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index b8c1f768e..a5e578760 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -96,7 +96,7 @@ class Asset(models.Model): return False, warning def is_unixlike(self): - if self.platform not in ("Windows", "Other"): + if self.platform not in ("Windows",): return True else: return False @@ -132,6 +132,15 @@ class Asset(models.Model): info["gateways"] = [d.id for d in self.domain.gateway_set.all()] return info + def get_auth_info(self): + if self.admin_user: + return { + 'username': self.admin_user.username, + 'password': self.admin_user.password, + 'private_key': self.admin_user.private_key_file, + 'become': self.admin_user.become_info, + } + def _to_secret_json(self): """ Ansible use it create inventory, First using asset user, @@ -175,4 +184,3 @@ class Asset(models.Model): except IntegrityError: print('Error continue') continue - diff --git a/apps/assets/tasks.py b/apps/assets/tasks.py index e47d15d41..53a54764d 100644 --- a/apps/assets/tasks.py +++ b/apps/assets/tasks.py @@ -3,15 +3,15 @@ import json import re import os -import paramiko from celery import shared_task from django.core.cache import cache from django.utils.translation import ugettext as _ from common.utils import get_object_or_none, capacity_convert, \ sum_capacity, encrypt_password, get_logger -from common.celery import register_as_period_task, after_app_shutdown_clean, \ - after_app_ready_start, app as celery_app +from ops.celery.utils import register_as_period_task, after_app_shutdown_clean, \ + after_app_ready_start +from ops.celery import app as celery_app from .models import SystemUser, AdminUser, Asset from . import const @@ -215,7 +215,7 @@ def test_admin_user_connectability_period(): def test_admin_user_connectability_manual(admin_user): # task_name = _("Test admin user connectability: {}").format(admin_user.name) task_name = _("测试管理行号可连接性: {}").format(admin_user.name) - return test_admin_user_connectability_util.delay(admin_user, task_name) + return test_admin_user_connectability_util(admin_user, task_name) @shared_task @@ -395,11 +395,12 @@ def get_node_push_system_user_task_name(system_user, node): ) +@shared_task def push_system_user_to_node(system_user, node): logger.info("Start push system user node: {} => {}".format(system_user.name, node.value)) assets = node.get_all_assets() task_name = get_node_push_system_user_task_name(system_user, node) - push_system_user_util.delay([system_user], assets, task_name) + push_system_user_util([system_user], assets, task_name) @shared_task diff --git a/apps/assets/templates/assets/admin_user_assets.html b/apps/assets/templates/assets/admin_user_assets.html index fa72abaa5..80ff0cd5f 100644 --- a/apps/assets/templates/assets/admin_user_assets.html +++ b/apps/assets/templates/assets/admin_user_assets.html @@ -121,14 +121,16 @@ $(document).ready(function () { }) .on('click', '.btn-test-connective', function () { var the_url = "{% url 'api-assets:admin-user-connective' pk=admin_user.id %}"; - var error = function (data) { - alert(data) + var success = function (data) { + var task_id = data.task; + 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, - error: error, method: 'GET', - success_message: "{% trans 'Task has been send, seen left asset status' %}" + success: success, + flash_message: false }); }) diff --git a/apps/assets/templates/assets/asset_detail.html b/apps/assets/templates/assets/asset_detail.html index 9767320a3..fd57d9643 100644 --- a/apps/assets/templates/assets/asset_detail.html +++ b/apps/assets/templates/assets/asset_detail.html @@ -269,16 +269,15 @@ function updateAssetNodes(nodes) { function refreshAssetHardware() { var the_url = "{% url 'api-assets:asset-refresh' pk=asset.id %}"; - var success = function (data) { - location.reload(); - }; - var error = function (data) { - alert(data) + var success = function(data) { + console.log(data); + var task_id = data.task; + 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, success: success, - error: error, method: 'GET' }); } @@ -344,19 +343,20 @@ $(document).ready(function () { var redirect_url = "{% url 'assets:asset-list' %}"; objectDelete($this, name, the_url, redirect_url); }).on('click', '#btn_refresh_asset', function () { - alert('关闭alert, 等待完成, 自动刷新页面'); refreshAssetHardware() }).on('click', '#btn-test-is-alive', function () { var the_url = "{% url 'api-assets:asset-alive-test' pk=asset.id %}"; - var error = function (data) { - alert(data) + + var success = function(data) { + var task_id = data.task; + var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id); + window.open(url, '', 'width=800,height=600') }; - alert('关闭alert, 等待完成'); + APIUpdateAttr({ url: the_url, - error: error, method: 'GET', - success_message: "{% trans "Reachable" %}" + success: success }); }) diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index ec905b119..25e206c0c 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -497,14 +497,17 @@ $(document).ready(function(){ } var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id); - function success() { + function success(data) { rMenu.css({"visibility" : "hidden"}); + var task_id = data.task; + 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, method: "GET", - success_message: "更新硬件信息任务下发成功", - success: success + success: success, + flash_message: false }); }) @@ -519,14 +522,17 @@ $(document).ready(function(){ } var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id); - function success() { + function success(data) { rMenu.css({"visibility" : "hidden"}); + var task_id = data.task; + 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, method: "GET", - success_message: "测试可连接性任务下发成功", - success: success + success: success, + flash_message: false }); }) .on('click', '.btn_asset_delete', function () { diff --git a/apps/assets/templates/assets/domain_gateway_list.html b/apps/assets/templates/assets/domain_gateway_list.html index 1f25e6ac1..3dc82652f 100644 --- a/apps/assets/templates/assets/domain_gateway_list.html +++ b/apps/assets/templates/assets/domain_gateway_list.html @@ -95,8 +95,8 @@ function initTable() { ], ajax_url: '{% url "api-assets:gateway-list" %}?domain={{ object.id }}', columns: [ - {data: "id"}, {data: "name" }, {data: 'ip'}, {data: 'port'}, {data: "username" }, - {data: "protocol"}, {data: "comment" }, {data: "id"} + {data: "id"}, {data: "name" }, {data: 'ip'}, {data: 'port'}, + {data: "protocol"}, {data: "username" }, {data: "comment" }, {data: "id"} ], op_html: $('#actions').html() }; diff --git a/apps/assets/templates/assets/domain_list.html b/apps/assets/templates/assets/domain_list.html index 03b671bf3..926c4bbc3 100644 --- a/apps/assets/templates/assets/domain_list.html +++ b/apps/assets/templates/assets/domain_list.html @@ -33,7 +33,11 @@ function initTable() { var detail_btn = '' + cellData + ''; $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); }}, - + {targets: 3, createdCell: function (td, cellData, rowData) { + var gateway_list_btn = '' + cellData + ''; + gateway_list_btn = gateway_list_btn.replace("{{ DEFAULT_PK }}", rowData.id); + $(td).html(gateway_list_btn); + }}, {targets: 5, createdCell: function (td, cellData, rowData) { var update_btn = '{% trans "Update" %}'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '{% trans "Delete" %}'.replace('{{ DEFAULT_PK }}', cellData); diff --git a/apps/assets/templates/assets/system_user_detail.html b/apps/assets/templates/assets/system_user_detail.html index b74b402cf..3572cfd3d 100644 --- a/apps/assets/templates/assets/system_user_detail.html +++ b/apps/assets/templates/assets/system_user_detail.html @@ -293,26 +293,30 @@ $(document).ready(function () { }) .on('click', '.btn-push', function () { var the_url = "{% url 'api-assets:system-user-push' pk=system_user.id %}"; - var error = function (data) { - alert(data) + var success = function (data) { + var task_id = data.task; + 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, - error: error, method: 'GET', - success_message: "{% trans "Task has been send, Go to ops task list seen result" %}" + success: success, + flash_message: false }); }) .on('click', '.btn-test-connective', function () { var the_url = "{% url 'api-assets:system-user-connective' pk=system_user.id %}"; - var error = function (data) { - alert(data) + var success = function (data) { + var task_id = data.task; + 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, - error: error, method: 'GET', - success_message: "{% trans "Task has been send, seen left assets status" %}" + success: success, + flash_message: false }); }) diff --git a/apps/common/__init__.py b/apps/common/__init__.py index b64e43e83..fdb34b225 100644 --- a/apps/common/__init__.py +++ b/apps/common/__init__.py @@ -2,4 +2,4 @@ from __future__ import absolute_import # This will make sure the app is always imported when # Django starts so that shared_task will use this app. -from .celery import app as celery_app + diff --git a/apps/common/api.py b/apps/common/api.py index 58a5822c1..4b74b6b0d 100644 --- a/apps/common/api.py +++ b/apps/common/api.py @@ -2,14 +2,13 @@ # import json -from rest_framework.views import APIView -from rest_framework.views import Response +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 @@ -105,3 +104,6 @@ class DjangoSettingsAPI(APIView): if i.isupper(): configs[i] = str(getattr(settings, i)) return Response(configs) + + + diff --git a/apps/common/const.py b/apps/common/const.py index a28b0f1db..6652593cb 100644 --- a/apps/common/const.py +++ b/apps/common/const.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- # -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _ create_success_msg = _("%(name)s was created successfully") -update_success_msg = _("%(name)s was updated successfully") \ No newline at end of file +update_success_msg = _("%(name)s was updated successfully") +FILE_END_GUARD = ">>> Content End <<<" +celery_task_pre_key = "CELERY_" diff --git a/apps/common/models.py b/apps/common/models.py index 1f634bce2..c90458985 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -79,3 +79,4 @@ class Setting(models.Model): class Meta: db_table = "settings" + diff --git a/apps/common/tasks.py b/apps/common/tasks.py index e8d6ba8b0..dec738921 100644 --- a/apps/common/tasks.py +++ b/apps/common/tasks.py @@ -1,13 +1,13 @@ from django.core.mail import send_mail from django.conf import settings -from .celery import app +from celery import shared_task from .utils import get_logger logger = get_logger(__file__) -@app.task +@shared_task def send_mail_async(*args, **kwargs): """ Using celery to send email async diff --git a/apps/common/utils.py b/apps/common/utils.py index 7e7aa9aae..b4dd0aef8 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # import re +import sys from collections import OrderedDict from six import string_types import base64 @@ -360,3 +361,20 @@ def get_signer(): signer = Signer(settings.SECRET_KEY) return signer + +class TeeObj: + origin_stdout = sys.stdout + + def __init__(self, file_obj): + self.file_obj = file_obj + + def write(self, msg): + self.origin_stdout.write(msg) + self.file_obj.write(msg.replace('*', '')) + + def flush(self): + self.origin_stdout.flush() + self.file_obj.flush() + + def close(self): + self.file_obj.close() diff --git a/apps/common/views.py b/apps/common/views.py index 99b924423..ee7a2225f 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -1,12 +1,13 @@ -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, DetailView +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 from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \ TerminalSettingForm -from .models import Setting from .mixins import AdminUserRequiredMixin from .signals import ldap_auth_enable @@ -120,3 +121,4 @@ class TerminalSettingView(AdminUserRequiredMixin, TemplateView): context.update({"form": form}) return render(request, self.template_name, context) + diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 78000366c..7d305070e 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-28 11:13+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" @@ -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 @@ -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 @@ -304,7 +304,7 @@ msgstr "创建者" #: 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 @@ -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 @@ -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 @@ -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 "比例" @@ -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 "任务开始: " @@ -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 diff --git a/apps/ops/__init__.py b/apps/ops/__init__.py index 8b1378917..cf2e85f6d 100644 --- a/apps/ops/__init__.py +++ b/apps/ops/__init__.py @@ -1 +1 @@ - +from .celery import app as celery_app diff --git a/apps/ops/ansible/callback.py b/apps/ops/ansible/callback.py index 810b14c51..0af872b25 100644 --- a/apps/ops/ansible/callback.py +++ b/apps/ops/ansible/callback.py @@ -1,14 +1,18 @@ # ~*~ coding: utf-8 ~*~ +import sys + from ansible.plugins.callback import CallbackBase from ansible.plugins.callback.default import CallbackModule +from .display import TeeObj + class AdHocResultCallback(CallbackModule): """ Task result Callback """ - def __init__(self, display=None, options=None): + def __init__(self, display=None, options=None, file_obj=None): # result_raw example: { # "ok": {"hostname": {"task_name": {},...},..}, # "failed": {"hostname": {"task_name": {}..}, ..}, @@ -22,6 +26,8 @@ class AdHocResultCallback(CallbackModule): self.results_raw = dict(ok={}, failed={}, unreachable={}, skipped={}) self.results_summary = dict(contacted=[], dark={}) super().__init__() + if file_obj is not None: + sys.stdout = TeeObj(file_obj) def gather_result(self, t, res): self._clean_results(res._result, res._task.action) diff --git a/apps/ops/ansible/display.py b/apps/ops/ansible/display.py new file mode 100644 index 000000000..1494eb5ef --- /dev/null +++ b/apps/ops/ansible/display.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# + +import sys + + +class TeeObj: + origin_stdout = sys.stdout + + def __init__(self, file_obj): + self.file_obj = file_obj + + def write(self, msg): + self.origin_stdout.write(msg) + self.file_obj.write(msg.replace('*', '')) + + def flush(self): + self.origin_stdout.flush() + self.file_obj.flush() diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index c8224c8c8..707855cc3 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -29,7 +29,6 @@ class BaseHost(Host): } "groups": [], "vars": {}, - "other_ansbile_vars": } """ self.host_data = host_data @@ -79,7 +78,7 @@ class BaseInventory(InventoryManager): variable_manager_class = VariableManager host_manager_class = BaseHost - def __init__(self, host_list=None): + def __init__(self, host_list=None, group_list=None): """ 用于生成动态构建Ansible Inventory. super().__init__ 会自动调用 host_list: [{ @@ -98,11 +97,14 @@ class BaseInventory(InventoryManager): "vars": {}, }, ] + group_list: [ + {"name: "", children: [""]}, + ] :param host_list: + :param group_list """ - if host_list is None: - host_list = [] - self.host_list = host_list + self.host_list = host_list or [] + self.group_list = group_list or [] assert isinstance(host_list, list) self.loader = self.loader_class() self.variable_manager = self.variable_manager_class() @@ -114,25 +116,40 @@ class BaseInventory(InventoryManager): def get_group(self, name): return self._inventory.groups.get(name, None) - def parse_sources(self, cache=False): - group_all = self.get_group('all') - ungrouped = self.get_group('ungrouped') + def get_or_create_group(self, name): + group = self.get_group(name) + if not group: + self.add_group(name) + return self.get_or_create_group(name) + else: + return group + def parse_groups(self): + for g in self.group_list: + parent = self.get_or_create_group(g.get("name")) + children = [self.get_or_create_group(n) for n in g.get('children', [])] + for child in children: + parent.add_child_group(child) + + def parse_hosts(self): + group_all = self.get_or_create_group('all') + ungrouped = self.get_or_create_group('ungrouped') for host_data in self.host_list: host = self.host_manager_class(host_data=host_data) self.hosts[host_data['hostname']] = host groups_data = host_data.get('groups') if groups_data: for group_name in groups_data: - group = self.get_group(group_name) - if group is None: - self.add_group(group_name) - group = self.get_group(group_name) + group = self.get_or_create_group(group_name) group.add_host(host) else: ungrouped.add_host(host) group_all.add_host(host) + def parse_sources(self, cache=False): + self.parse_groups() + self.parse_hosts() + def get_matched_hosts(self, pattern): return self.get_hosts(pattern) diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py index d9a8c7e6e..3e168e987 100644 --- a/apps/ops/ansible/runner.py +++ b/apps/ops/ansible/runner.py @@ -9,6 +9,7 @@ from ansible.parsing.dataloader import DataLoader from ansible.executor.playbook_executor import PlaybookExecutor from ansible.playbook.play import Play import ansible.constants as C +from ansible.utils.display import Display from .callback import AdHocResultCallback, PlaybookResultCallBack, \ CommandResultCallback @@ -21,6 +22,13 @@ C.HOST_KEY_CHECKING = False logger = get_logger(__name__) +class CustomDisplay(Display): + def display(self, msg, color=None, stderr=False, screen_only=False, log_only=False): + pass + +display = CustomDisplay() + + Options = namedtuple('Options', [ 'listtags', 'listtasks', 'listhosts', 'syntax', 'connection', 'module_path', 'forks', 'remote_user', 'private_key_file', 'timeout', @@ -123,20 +131,22 @@ class AdHocRunner: ADHoc Runner接口 """ results_callback_class = AdHocResultCallback + results_callback = None loader_class = DataLoader variable_manager_class = VariableManager - options = get_default_options() default_options = get_default_options() def __init__(self, inventory, options=None): - if options: - self.options = options + self.options = self.update_options(options) self.inventory = inventory self.loader = DataLoader() self.variable_manager = VariableManager( loader=self.loader, inventory=self.inventory ) + def get_result_callback(self, file_obj=None): + return self.__class__.results_callback_class(file_obj=file_obj) + @staticmethod def check_module_args(module_name, module_args=''): if module_name in C.MODULE_REQUIRE_ARGS and not module_args: @@ -160,19 +170,24 @@ class AdHocRunner: cleaned_tasks.append(task) return cleaned_tasks - def set_option(self, k, v): - kwargs = {k: v} - self.options = self.options._replace(**kwargs) + def update_options(self, options): + if options and isinstance(options, dict): + options = self.__class__.default_options._replace(**options) + else: + options = self.__class__.default_options + return options - def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'): + def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no', file_obj=None): """ :param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ] :param pattern: all, *, or others :param play_name: The play name + :param gather_facts: + :param file_obj: logging to file_obj :return: """ self.check_pattern(pattern) - results_callback = self.results_callback_class() + self.results_callback = self.get_result_callback(file_obj) cleaned_tasks = self.clean_tasks(tasks) play_source = dict( @@ -193,16 +208,16 @@ class AdHocRunner: variable_manager=self.variable_manager, loader=self.loader, options=self.options, - stdout_callback=results_callback, + stdout_callback=self.results_callback, passwords=self.options.passwords, ) - logger.debug("Get inventory matched hosts: {}".format( + print("Get matched hosts: {}".format( self.inventory.get_matched_hosts(pattern) )) try: tqm.run(play) - return results_callback + return self.results_callback except Exception as e: raise AnsibleError(e) finally: diff --git a/apps/ops/api.py b/apps/ops/api.py index 09f5e67e7..0134fbd4a 100644 --- a/apps/ops/api.py +++ b/apps/ops/api.py @@ -1,13 +1,17 @@ # ~*~ coding: utf-8 ~*~ +import uuid +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.views import Response from .hands import IsSuperUser -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 @@ -24,8 +28,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): @@ -58,3 +62,30 @@ class AdHocRunHistorySet(viewsets.ModelViewSet): adhoc = get_object_or_404(AdHoc, id=adhoc_id) self.queryset = self.queryset.filter(adhoc=adhoc) return self.queryset + + +class CeleryTaskLogApi(generics.RetrieveAPIView): + permission_classes = (IsSuperUser,) + buff_size = 1024 * 10 + end = False + queryset = CeleryTask.objects.all() + + def get(self, request, *args, **kwargs): + mark = request.query_params.get("mark") or str(uuid.uuid4()) + task = super().get_object() + log_path = task.full_log_path + + 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 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 new file mode 100644 index 000000000..04f48299e --- /dev/null +++ b/apps/ops/celery/__init__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +import os + +from celery import Celery + +# set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') + +from django.conf import settings + +app = Celery('jumpserver') + +# Using a string here means the worker will not have to +# 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/const.py b/apps/ops/celery/const.py new file mode 100644 index 000000000..3d98261b1 --- /dev/null +++ b/apps/ops/celery/const.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +# + diff --git a/apps/ops/celery/signal_handler.py b/apps/ops/celery/signal_handler.py new file mode 100644 index 000000000..8309c0246 --- /dev/null +++ b/apps/ops/celery/signal_handler.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +# +import os +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, 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__) + + +@worker_ready.connect +def on_app_ready(sender=None, headers=None, body=None, **kwargs): + if cache.get("CELERY_APP_READY", 0) == 1: + return + cache.set("CELERY_APP_READY", 1, 10) + logger.debug("App ready signal recv") + tasks = get_after_app_ready_tasks() + logger.debug("Start need start task: [{}]".format( + ", ".join(tasks)) + ) + for task in tasks: + subtask(task).delay() + + +@worker_shutdown.connect +def after_app_shutdown(sender=None, headers=None, body=None, **kwargs): + if cache.get("CELERY_APP_SHUTDOWN", 0) == 1: + return + cache.set("CELERY_APP_SHUTDOWN", 1, 10) + tasks = get_after_app_shutdown_clean_tasks() + logger.debug("App shutdown signal recv") + logger.debug("Clean need cleaned period tasks: [{}]".format( + ', '.join(tasks)) + ) + 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): + 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_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 + + +@task_postrun.connect +def post_run_task_signal_handler(sender, task_id=None, task=None, **kwargs): + 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() + diff --git a/apps/common/celery.py b/apps/ops/celery/utils.py similarity index 68% rename from apps/common/celery.py rename to apps/ops/celery/utils.py index e79f1d434..b4f5a80db 100644 --- a/apps/common/celery.py +++ b/apps/ops/celery/utils.py @@ -1,33 +1,50 @@ -# ~*~ coding: utf-8 ~*~ - -import os +# -*- coding: utf-8 -*- +# import json from functools import wraps -from celery import Celery, subtask -from celery.signals import worker_ready, worker_shutdown from django.db.utils import ProgrammingError, OperationalError - -from .utils import get_logger - -logger = get_logger(__file__) - -# set the default Django settings module for the 'celery' program. -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') - -from django.conf import settings from django.core.cache import cache +from django_celery_beat.models import PeriodicTask, IntervalSchedule, CrontabSchedule -app = Celery('jumpserver') -# Using a string here means the worker will not have to -# 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]) +def add_register_period_task(name): + key = "__REGISTER_PERIODIC_TASKS" + value = cache.get(key, []) + value.append(name) + cache.set(key, value) + + +def get_register_period_tasks(): + key = "__REGISTER_PERIODIC_TASKS" + return cache.get(key, []) + + +def add_after_app_shutdown_clean_task(name): + key = "__AFTER_APP_SHUTDOWN_CLEAN_TASKS" + value = cache.get(key, []) + value.append(name) + cache.set(key, value) + + +def get_after_app_shutdown_clean_tasks(): + key = "__AFTER_APP_SHUTDOWN_CLEAN_TASKS" + return cache.get(key, []) + + +def add_after_app_ready_task(name): + key = "__AFTER_APP_READY_RUN_TASKS" + value = cache.get(key, []) + value.append(name) + cache.set(key, value) + + +def get_after_app_ready_tasks(): + key = "__AFTER_APP_READY_RUN_TASKS" + return cache.get(key, []) def create_or_update_celery_periodic_tasks(tasks): - from django_celery_beat.models import PeriodicTask, IntervalSchedule, CrontabSchedule """ :param tasks: { 'add-every-monday-morning': { @@ -106,11 +123,6 @@ def delete_celery_periodic_task(task_name): PeriodicTask.objects.filter(name=task_name).delete() -__REGISTER_PERIODIC_TASKS = [] -__AFTER_APP_SHUTDOWN_CLEAN_TASKS = [] -__AFTER_APP_READY_RUN_TASKS = [] - - def register_as_period_task(crontab=None, interval=None): """ Warning: Task must be have not any args and kwargs @@ -128,7 +140,7 @@ def register_as_period_task(crontab=None, interval=None): # Because when this decorator run, the task was not created, # So we can't use func.name name = '{func.__module__}.{func.__name__}'.format(func=func) - if name not in __REGISTER_PERIODIC_TASKS: + if name not in get_register_period_tasks(): create_or_update_celery_periodic_tasks({ name: { 'task': name, @@ -138,7 +150,7 @@ def register_as_period_task(crontab=None, interval=None): 'enabled': True, } }) - __REGISTER_PERIODIC_TASKS.append(name) + add_register_period_task(name) @wraps(func) def wrapper(*args, **kwargs): @@ -151,13 +163,12 @@ def after_app_ready_start(func): # Because when this decorator run, the task was not created, # So we can't use func.name name = '{func.__module__}.{func.__name__}'.format(func=func) - if name not in __AFTER_APP_READY_RUN_TASKS: - __AFTER_APP_READY_RUN_TASKS.append(name) + if name not in get_after_app_ready_tasks(): + add_after_app_ready_task(name) @wraps(func) def decorate(*args, **kwargs): return func(*args, **kwargs) - return decorate @@ -165,37 +176,10 @@ def after_app_shutdown_clean(func): # Because when this decorator run, the task was not created, # So we can't use func.name name = '{func.__module__}.{func.__name__}'.format(func=func) - if name not in __AFTER_APP_READY_RUN_TASKS: - __AFTER_APP_SHUTDOWN_CLEAN_TASKS.append(name) + if name not in get_after_app_shutdown_clean_tasks(): + add_after_app_shutdown_clean_task(name) @wraps(func) def decorate(*args, **kwargs): return func(*args, **kwargs) - return decorate - - -@worker_ready.connect -def on_app_ready(sender=None, headers=None, body=None, **kwargs): - if cache.get("CELERY_APP_READY", 0) == 1: - return - cache.set("CELERY_APP_READY", 1, 10) - logger.debug("App ready signal recv") - logger.debug("Start need start task: [{}]".format( - ", ".join(__AFTER_APP_READY_RUN_TASKS)) - ) - for task in __AFTER_APP_READY_RUN_TASKS: - subtask(task).delay() - - -@worker_shutdown.connect -def after_app_shutdown(sender=None, headers=None, body=None, **kwargs): - if cache.get("CELERY_APP_SHUTDOWN", 0) == 1: - return - cache.set("CELERY_APP_SHUTDOWN", 1, 10) - from django_celery_beat.models import PeriodicTask - logger.debug("App shutdown signal recv") - logger.debug("Clean need cleaned period tasks: [{}]".format( - ', '.join(__AFTER_APP_SHUTDOWN_CLEAN_TASKS)) - ) - PeriodicTask.objects.filter(name__in=__AFTER_APP_SHUTDOWN_CLEAN_TASKS).delete() diff --git a/apps/ops/inventory.py b/apps/ops/inventory.py index 6e3d6325c..6230e8167 100644 --- a/apps/ops/inventory.py +++ b/apps/ops/inventory.py @@ -9,29 +9,18 @@ __all__ = [ ] -def make_proxy_command(asset): - gateway = asset.domain.random_gateway() - - proxy_command = [ - "ssh", "-p", str(gateway.port), - "{}@{}".format(gateway.username, gateway.ip), - "-W", "%h:%p", "-q", - ] - - if gateway.password: - proxy_command.insert(0, "sshpass -p {}".format(gateway.password)) - if gateway.private_key: - proxy_command.append("-i {}".format(gateway.private_key_file)) - - return {"ansible_ssh_common_args": "'-o ProxyCommand={}'".format(" ".join(proxy_command))} - - class JMSInventory(BaseInventory): """ JMS Inventory is the manager with jumpserver assets, so you can write you own manager, construct you inventory """ def __init__(self, hostname_list, run_as_admin=False, run_as=None, become_info=None): + """ + :param hostname_list: ["test1", ] + :param run_as_admin: True 是否使用管理用户去执行, 每台服务器的管理用户可能不同 + :param run_as: 是否统一使用某个系统用户去执行 + :param become_info: 是否become成某个用户去执行 + """ self.hostname_list = hostname_list self.using_admin = run_as_admin self.run_as = run_as @@ -41,23 +30,14 @@ class JMSInventory(BaseInventory): host_list = [] for asset in assets: - vars = {} - if run_as_admin: - info = asset._to_secret_json() - else: - info = asset.to_json() - - info["vars"] = vars - if asset.domain and asset.domain.has_gateway(): - vars.update(make_proxy_command(asset)) - info.update(vars) - + info = self.convert_to_ansible(asset, run_as_admin=run_as_admin) host_list.append(info) if run_as: run_user_info = self.get_run_user_info() for host in host_list: host.update(run_user_info) + if become_info: for host in host_list: host.update(become_info) @@ -67,9 +47,57 @@ class JMSInventory(BaseInventory): assets = get_assets_by_hostname_list(self.hostname_list) return assets + def convert_to_ansible(self, asset, run_as_admin=False): + info = { + 'id': asset.id, + 'hostname': asset.hostname, + 'ip': asset.ip, + 'port': asset.port, + 'vars': dict(), + 'groups': [], + } + if asset.domain and asset.domain.has_gateway(): + info["vars"].update(self.make_proxy_command(asset)) + if run_as_admin: + info.update(asset.get_auth_info()) + for node in asset.nodes.all(): + info["groups"].append(node.value) + for label in asset.labels.all(): + info["vars"].update({ + label.name: label.value + }) + info["groups"].append("{}:{}".format(label.name, label.value)) + if asset.domain: + info["vars"].update({ + "domain": asset.domain.name, + }) + info["groups"].append("domain_"+asset.domain.name) + return info + def get_run_user_info(self): system_user = get_system_user_by_name(self.run_as) if not system_user: return {} else: return system_user._to_secret_json() + + @staticmethod + def make_proxy_command(asset): + gateway = asset.domain.random_gateway() + proxy_command_list = [ + "ssh", "-p", str(gateway.port), + "{}@{}".format(gateway.username, gateway.ip), + "-W", "%h:%p", "-q", + ] + + if gateway.password: + proxy_command_list.insert( + 0, "sshpass -p {}".format(gateway.password) + ) + if gateway.private_key: + proxy_command_list.append("-i {}".format(gateway.private_key_file)) + + proxy_command = "'-o ProxyCommand={}'".format( + " ".join(proxy_command_list) + ) + return {"ansible_ssh_common_args": proxy_command} 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 88% rename from apps/ops/models.py rename to apps/ops/models/adhoc.py index 1120bb206..beba34fe6 100644 --- a/apps/ops/models.py +++ b/apps/ops/models/adhoc.py @@ -2,18 +2,23 @@ import json import uuid - +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 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 common.celery 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 ..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 __all__ = ["Task", "AdHoc", "AdHocRunHistory"] @@ -85,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, @@ -206,10 +211,18 @@ 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() try: + date_start = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print("{} Start task: {}\r\n".format(date_start, self.task.name)) raw, summary = self._run_only() + date_end = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print("\r\n{} Task finished".format(date_end)) history.is_finished = True if summary.get('dark'): history.is_success = False @@ -221,17 +234,20 @@ 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() - def _run_only(self): - runner = AdHocRunner(self.inventory) - for k, v in self.options.items(): - runner.set_option(k, v) - + def _run_only(self, file_obj=None): + runner = AdHocRunner(self.inventory, options=self.options) try: - result = runner.run(self.tasks, self.pattern, self.task.name) + result = runner.run( + self.tasks, + self.pattern, + self.task.name, + file_obj=file_obj, + ) return result.results_raw, result.results_summary except AnsibleError as e: logger.warn("Failed run adhoc {}, {}".format(self.task.name, e)) @@ -316,6 +332,14 @@ class AdHocRunHistory(models.Model): def short_id(self): return str(self.id).split('-')[-1] + @property + def log_path(self): + dt = datetime.datetime.now().strftime('%Y-%m-%d') + log_dir = os.path.join(settings.PROJECT_DIR, 'data', 'ansible', dt) + if not os.path.exists(log_dir): + os.makedirs(log_dir) + return os.path.join(log_dir, str(self.id) + '.log') + @property def result(self): if self._result: 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 41f60f20c..8df803207 100644 --- a/apps/ops/tasks.py +++ b/apps/ops/tasks.py @@ -12,14 +12,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: diff --git a/apps/ops/templates/ops/adhoc_history.html b/apps/ops/templates/ops/adhoc_history.html index 802e8e8f6..cab76f692 100644 --- a/apps/ops/templates/ops/adhoc_history.html +++ b/apps/ops/templates/ops/adhoc_history.html @@ -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 = "" + cellData.total + ""; diff --git a/apps/ops/templates/ops/adhoc_history_detail.html b/apps/ops/templates/ops/adhoc_history_detail.html index f3d18f8ba..16adbc4e3 100644 --- a/apps/ops/templates/ops/adhoc_history_detail.html +++ b/apps/ops/templates/ops/adhoc_history_detail.html @@ -18,6 +18,9 @@
  • {% trans 'Run history detail' %}
  • +
  • + {% trans 'Output' %} +
  • diff --git a/apps/ops/templates/ops/celery_task_log.html b/apps/ops/templates/ops/celery_task_log.html new file mode 100644 index 000000000..9b0826949 --- /dev/null +++ b/apps/ops/templates/ops/celery_task_log.html @@ -0,0 +1,96 @@ +{% load static %} + + term.js + + + +
    +
    +
    +
    + + + + diff --git a/apps/ops/templates/ops/task_adhoc.html b/apps/ops/templates/ops/task_adhoc.html index 2d2c4de5c..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' %} +
  • @@ -105,6 +108,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 = '{% trans "Detail" %}'.replace('{{ DEFAULT_PK }}', cellData); if (cellData) { diff --git a/apps/ops/templates/ops/task_detail.html b/apps/ops/templates/ops/task_detail.html index 4da7e0f58..f6e832bd7 100644 --- a/apps/ops/templates/ops/task_detail.html +++ b/apps/ops/templates/ops/task_detail.html @@ -24,6 +24,9 @@
  • {% trans 'Run history' %}
  • +
  • + {% trans 'Last run output' %} +
  • @@ -160,6 +163,5 @@
    - {% include 'users/_user_update_pk_modal.html' %} {% endblock %} diff --git a/apps/ops/templates/ops/task_history.html b/apps/ops/templates/ops/task_history.html index cc620237d..9604b3402 100644 --- a/apps/ops/templates/ops/task_history.html +++ b/apps/ops/templates/ops/task_history.html @@ -24,13 +24,16 @@
  • {% trans 'Run history' %}
  • +
  • + {% trans 'Last run output' %} +
  • - {% trans 'History of ' %} {{ object.task.name }}:{{ object.short_id }} + {% trans 'History of ' %} {{ object.name }}:{{ object.short_id }}
    @@ -85,7 +88,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 = "" + cellData.total + ""; diff --git a/apps/ops/templates/ops/task_list.html b/apps/ops/templates/ops/task_list.html index f098a4d78..2b65c9e3e 100644 --- a/apps/ops/templates/ops/task_list.html +++ b/apps/ops/templates/ops/task_list.html @@ -1,8 +1,9 @@ {% extends '_base_list.html' %} {% load i18n %} {% load static %} + {% block content_left_head %} - +{# #} {% endblock %} @@ -111,9 +112,10 @@ $(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; + 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 ab007c383..d6390fed9 100644 --- a/apps/ops/urls/api_urls.py +++ b/apps/ops/urls/api_urls.py @@ -15,6 +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/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 080bdd06c..470d6f06d 100644 --- a/apps/ops/urls/view_urls.py +++ b/apps/ops/urls/view_urls.py @@ -18,4 +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'^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 ba9e2cfeb..e3ba2789a 100644 --- a/apps/ops/views.py +++ b/apps/ops/views.py @@ -2,10 +2,10 @@ 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 +from .models import Task, AdHoc, AdHocRunHistory, CeleryTask from .hands import AdminUserRequiredMixin @@ -118,4 +118,9 @@ class AdHocHistoryDetailView(AdminUserRequiredMixin, DetailView): 'action': _('Run history detail'), } kwargs.update(context) - return super().get_context_data(**kwargs) \ No newline at end of file + return super().get_context_data(**kwargs) + + +class CeleryTaskLogView(AdminUserRequiredMixin, DetailView): + template_name = 'ops/celery_task_log.html' + model = CeleryTask diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index c4d18815b..1083f3520 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -157,7 +157,7 @@ function APIUpdateAttr(props) { props = props || {}; var success_message = props.success_message || '更新成功!'; var fail_message = props.fail_message || '更新时发生未知错误.'; - var flash_message = true; + var flash_message = props.flash_message || true; if (props.flash_message === false){ flash_message = false; } diff --git a/apps/terminal/signals_handler.py b/apps/terminal/signals_handler.py index 3926a5751..883dd51f2 100644 --- a/apps/terminal/signals_handler.py +++ b/apps/terminal/signals_handler.py @@ -5,8 +5,6 @@ from django.core.cache import cache from django.db.utils import ProgrammingError, OperationalError from common.utils import get_logger -from common.celery import after_app_ready_start, register_as_period_task, \ - after_app_shutdown_clean from .const import ASSETS_CACHE_KEY, USERS_CACHE_KEY, SYSTEM_USER_CACHE_KEY RUNNING = False diff --git a/apps/terminal/tasks.py b/apps/terminal/tasks.py index e267b30b7..4e57c5f5e 100644 --- a/apps/terminal/tasks.py +++ b/apps/terminal/tasks.py @@ -6,7 +6,7 @@ import datetime from celery import shared_task from django.utils import timezone -from common.celery import register_as_period_task, after_app_ready_start, \ +from ops.celery.utils import register_as_period_task, after_app_ready_start, \ after_app_shutdown_clean from .models import Status, Session diff --git a/jms b/jms index ea48ab6a7..0f07f0760 100755 --- a/jms +++ b/jms @@ -155,7 +155,7 @@ def start_celery(): cmd = [ 'celery', 'worker', - '-A', 'common', + '-A', 'ops', '-l', LOG_LEVEL.lower(), '--pidfile', pid_file, '-c', str(WORKERS), @@ -182,7 +182,7 @@ def start_beat(): scheduler = "django_celery_beat.schedulers:DatabaseScheduler" cmd = [ 'celery', 'beat', - '-A', 'common', + '-A', 'ops', '--pidfile', pid_file, '-l', LOG_LEVEL, '--scheduler', scheduler,