From d0b0c87d3c742c64e4ae2e958a927b98f8ae5f10 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Tue, 5 Dec 2023 19:09:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=8F=91=E9=80=81=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 76 +++++++++-------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 84 +++++++++---------- apps/ops/ansible/runner.py | 31 ++++++- apps/ops/api/job.py | 45 +++++++++- apps/ops/const.py | 1 + apps/ops/models/job.py | 14 +++- apps/ops/serializers/job.py | 14 +++- apps/perms/notifications.py | 2 +- .../perms/_msg_item_permissions_expire.html | 2 +- 11 files changed, 186 insertions(+), 91 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 17fc22c3a..5e4c9d63f 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d443763c06877304dca8dac76131271287acc6594df665bdf9445455c5187f1 -size 167791 +oid sha256:a00c0d53df7fa88fc2fe69adda31fd9ab581b5a0362a01b8191924f74fab800d +size 167820 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index df7dee7d5..8dad75a93 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,8 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-08 14:51+0800\n" -"POT-Creation-Date: 2023-12-08 15:33+0800\n" +"POT-Creation-Date: 2023-12-11 14:54+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -88,7 +87,7 @@ msgstr "集めました" msgid "Template" msgstr "テンプレート" -#: accounts/const/account.py:31 ops/const.py:45 +#: accounts/const/account.py:31 ops/const.py:46 msgid "Skip" msgstr "スキップ" @@ -100,7 +99,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 +#: ops/const.py:75 terminal/const.py:79 xpack/plugins/cloud/const.py:46 msgid "Failed" msgstr "失敗しました" @@ -208,7 +207,6 @@ msgstr "作成のみ" msgid "Email" msgstr "メール" -#: accounts/const/automation.py:104 terminal/const.py:87 #: accounts/const/automation.py:105 terminal/const.py:87 msgid "SFTP" msgstr "SFTP" @@ -265,7 +263,7 @@ msgstr "資産" #: accounts/models/account.py:53 accounts/models/template.py:16 #: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:268 -#: accounts/serializers/account/template.py:25 +#: accounts/serializers/account/template.py:27 #: authentication/serializers/connect_token_secret.py:50 msgid "Su from" msgstr "から切り替え" @@ -397,7 +395,7 @@ msgstr "理由" #: accounts/models/automations/backup_account.py:135 #: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:128 -#: ops/serializers/job.py:55 terminal/serializers/session.py:49 +#: ops/serializers/job.py:65 terminal/serializers/session.py:49 msgid "Is success" msgstr "成功は" @@ -585,7 +583,7 @@ msgstr "ひみつ" msgid "Secret strategy" msgstr "鍵ポリシー" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "パスワードルール" @@ -902,19 +900,19 @@ msgstr "数値#スウスウ#" msgid "Special symbol" msgstr "特殊記号" -#: accounts/serializers/account/template.py:18 +#: accounts/serializers/account/template.py:19 msgid "Exclude symbol" msgstr "除外文字" -#: accounts/serializers/account/template.py:36 +#: accounts/serializers/account/template.py:38 msgid "Secret generation strategy for account creation" msgstr "账号创建时,密文生成策略" -#: accounts/serializers/account/template.py:37 +#: accounts/serializers/account/template.py:39 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:40 +#: accounts/serializers/account/template.py:42 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -980,7 +978,7 @@ msgstr "自動タスク実行履歴" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 +#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40 #: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" @@ -1118,7 +1116,7 @@ msgid "Accounts" msgstr "アカウント" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:54 terminal/const.py:86 +#: ops/serializers/job.py:64 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1668,7 +1666,7 @@ msgstr "プロトコル" msgid "Sudo" msgstr "すど" -#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 +#: assets/models/_user.py:55 ops/const.py:50 ops/const.py:60 msgid "Shell" msgstr "シェル" @@ -3639,7 +3637,7 @@ msgstr "の準備を" msgid "Pending" msgstr "未定" -#: common/const/choices.py:17 ops/const.py:71 +#: common/const/choices.py:17 ops/const.py:72 msgid "Running" msgstr "ランニング" @@ -4053,6 +4051,10 @@ msgstr "タスクは存在しません" msgid "Task {} args or kwargs error" msgstr "タスク実行パラメータエラー" +#: ops/api/job.py:128 +msgid "Duplicate file exists" +msgstr "重複したファイルが存在する" + #: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "現在プレイブックは1つのジョブで使用されています" @@ -4125,47 +4127,53 @@ msgstr "コマンド#コマンド#" msgid "Playbook" msgstr "Playbook" -#: ops/const.py:43 +#: ops/const.py:40 +#, fuzzy +#| msgid "Upload" +msgid "Upload File" +msgstr "アップロード" + +#: ops/const.py:44 msgid "Privileged Only" msgstr "特権アカウントのみ" -#: ops/const.py:44 +#: ops/const.py:45 msgid "Privileged First" msgstr "特権アカウント優先" -#: ops/const.py:50 ops/const.py:60 +#: ops/const.py:51 ops/const.py:61 msgid "Powershell" msgstr "PowerShell" -#: ops/const.py:51 ops/const.py:61 +#: ops/const.py:52 ops/const.py:62 msgid "Python" msgstr "Python" -#: ops/const.py:52 ops/const.py:62 +#: ops/const.py:53 ops/const.py:63 msgid "MySQL" msgstr "MySQL" -#: ops/const.py:53 ops/const.py:64 +#: ops/const.py:54 ops/const.py:65 msgid "PostgreSQL" msgstr "PostgreSQL" -#: ops/const.py:54 ops/const.py:65 +#: ops/const.py:55 ops/const.py:66 msgid "SQLServer" msgstr "SQLServer" -#: ops/const.py:55 ops/const.py:67 +#: ops/const.py:56 ops/const.py:68 msgid "Raw" msgstr "" -#: ops/const.py:63 +#: ops/const.py:64 msgid "MariaDB" msgstr "MariaDB" -#: ops/const.py:66 +#: ops/const.py:67 msgid "Oracle" msgstr "Oracle" -#: ops/const.py:73 +#: ops/const.py:74 msgid "Timeout" msgstr "タイムアウト" @@ -4299,7 +4307,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material を選択してオプションを設定します。" -#: ops/models/job.py:557 +#: ops/models/job.py:565 msgid "Job Execution" msgstr "ジョブ実行" @@ -4343,15 +4351,15 @@ msgstr "{max_threshold} を超えるCPUロード: => {value}" msgid "Run after save" msgstr "保存後に実行" -#: ops/serializers/job.py:53 +#: ops/serializers/job.py:63 msgid "Job type" msgstr "タスクの種類" -#: ops/serializers/job.py:56 terminal/serializers/session.py:53 +#: ops/serializers/job.py:66 terminal/serializers/session.py:53 msgid "Is finished" msgstr "終了しました" -#: ops/serializers/job.py:57 +#: ops/serializers/job.py:67 msgid "Time cost" msgstr "時を過ごす" @@ -4567,7 +4575,8 @@ msgstr "認定アカウント" msgid "today" msgstr "今日" -#: perms/notifications.py:12 settings/serializers/feature.py:106 +#: perms/notifications.py:12 perms/notifications.py:44 +#: settings/serializers/feature.py:106 msgid "day" msgstr "日" @@ -4595,8 +4604,9 @@ msgstr "アセット認証ルールの有効期限が切れていることを確 msgid "Send asset permission expired notification" msgstr "アセット許可の有効期限通知を送信する" - +#: perms/templates/perms/_msg_item_permissions_expire.html:7 #: perms/templates/perms/_msg_permed_items_expire.html:7 +#, python-format msgid "" "\n" " The following %(item_type)s will expire in %(count)s\n" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 4de90d618..16c60122c 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8976c6b41e2c0ce591b2b257fd0352b4ed7517661f7df83ba74b302b8cf94b00 -size 137479 +oid sha256:d4a9a61bf1b247d3843001737ebfa6d5f8580f2d9c1ae0fc76649ecc535f5d96 +size 137563 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index a34de449a..29b3e9c50 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-08 15:33+0800\n" +"POT-Creation-Date: 2023-12-11 14:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -86,7 +86,7 @@ msgstr "收集" msgid "Template" msgstr "模板" -#: accounts/const/account.py:31 ops/const.py:45 +#: accounts/const/account.py:31 ops/const.py:46 msgid "Skip" msgstr "跳过" @@ -98,7 +98,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 +#: ops/const.py:75 terminal/const.py:79 xpack/plugins/cloud/const.py:46 msgid "Failed" msgstr "失败" @@ -262,7 +262,7 @@ msgstr "资产" #: accounts/models/account.py:53 accounts/models/template.py:16 #: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:268 -#: accounts/serializers/account/template.py:25 +#: accounts/serializers/account/template.py:27 #: authentication/serializers/connect_token_secret.py:50 msgid "Su from" msgstr "切换自" @@ -394,7 +394,7 @@ msgstr "原因" #: accounts/models/automations/backup_account.py:135 #: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:128 -#: ops/serializers/job.py:55 terminal/serializers/session.py:49 +#: ops/serializers/job.py:65 terminal/serializers/session.py:49 msgid "Is success" msgstr "是否成功" @@ -582,7 +582,7 @@ msgstr "密钥" msgid "Secret strategy" msgstr "密文策略" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "密码规则" @@ -898,19 +898,19 @@ msgstr "数字" msgid "Special symbol" msgstr "特殊字符" -#: accounts/serializers/account/template.py:18 +#: accounts/serializers/account/template.py:19 msgid "Exclude symbol" msgstr "排除字符" -#: accounts/serializers/account/template.py:36 +#: accounts/serializers/account/template.py:38 msgid "Secret generation strategy for account creation" msgstr "密码生成策略,用于账号创建时,设置密码" -#: accounts/serializers/account/template.py:37 +#: accounts/serializers/account/template.py:39 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:40 +#: accounts/serializers/account/template.py:42 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -975,7 +975,7 @@ msgstr "自动化任务执行历史" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 +#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40 #: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" @@ -1113,7 +1113,7 @@ msgid "Accounts" msgstr "账号管理" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:54 terminal/const.py:86 +#: ops/serializers/job.py:64 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1659,7 +1659,7 @@ msgstr "协议" msgid "Sudo" msgstr "Sudo" -#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 +#: assets/models/_user.py:55 ops/const.py:50 ops/const.py:60 msgid "Shell" msgstr "Shell" @@ -3594,7 +3594,7 @@ msgstr "准备" msgid "Pending" msgstr "待定的" -#: common/const/choices.py:17 ops/const.py:71 +#: common/const/choices.py:17 ops/const.py:72 msgid "Running" msgstr "运行中" @@ -4003,6 +4003,10 @@ msgstr "任务 {} 不存在" msgid "Task {} args or kwargs error" msgstr "任务 {} 执行参数错误" +#: ops/api/job.py:128 +msgid "Duplicate file exists" +msgstr "存在同名文件" + #: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "当前 playbook 正在作业中使用" @@ -4075,47 +4079,51 @@ msgstr "命令" msgid "Playbook" msgstr "Playbook" -#: ops/const.py:43 +#: ops/const.py:40 +msgid "Upload File" +msgstr "上传" + +#: ops/const.py:44 msgid "Privileged Only" msgstr "仅限特权账号" -#: ops/const.py:44 +#: ops/const.py:45 msgid "Privileged First" msgstr "特权账号优先" -#: ops/const.py:50 ops/const.py:60 +#: ops/const.py:51 ops/const.py:61 msgid "Powershell" msgstr "PowerShell" -#: ops/const.py:51 ops/const.py:61 +#: ops/const.py:52 ops/const.py:62 msgid "Python" msgstr "Python" -#: ops/const.py:52 ops/const.py:62 +#: ops/const.py:53 ops/const.py:63 msgid "MySQL" msgstr "MySQL" -#: ops/const.py:53 ops/const.py:64 +#: ops/const.py:54 ops/const.py:65 msgid "PostgreSQL" msgstr "PostgreSQL" -#: ops/const.py:54 ops/const.py:65 +#: ops/const.py:55 ops/const.py:66 msgid "SQLServer" msgstr "SQLServer" -#: ops/const.py:55 ops/const.py:67 +#: ops/const.py:56 ops/const.py:68 msgid "Raw" msgstr "Raw" -#: ops/const.py:63 +#: ops/const.py:64 msgid "MariaDB" msgstr "MariaDB" -#: ops/const.py:66 +#: ops/const.py:67 msgid "Oracle" msgstr "Oracle" -#: ops/const.py:73 +#: ops/const.py:74 msgid "Timeout" msgstr "超时" @@ -4249,7 +4257,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material 类型" -#: ops/models/job.py:557 +#: ops/models/job.py:565 msgid "Job Execution" msgstr "作业执行" @@ -4293,15 +4301,15 @@ msgstr "CPU 使用率超过 {max_threshold}: => {value}" msgid "Run after save" msgstr "保存后执行" -#: ops/serializers/job.py:53 +#: ops/serializers/job.py:63 msgid "Job type" msgstr "任务类型" -#: ops/serializers/job.py:56 terminal/serializers/session.py:53 +#: ops/serializers/job.py:66 terminal/serializers/session.py:53 msgid "Is finished" msgstr "是否完成" -#: ops/serializers/job.py:57 +#: ops/serializers/job.py:67 msgid "Time cost" msgstr "花费时间" @@ -4516,7 +4524,8 @@ msgstr "授权账号" msgid "today" msgstr "今天" -#: perms/notifications.py:12 settings/serializers/feature.py:106 +#: perms/notifications.py:12 perms/notifications.py:44 +#: settings/serializers/feature.py:106 msgid "day" msgstr "天" @@ -4545,20 +4554,6 @@ msgid "Send asset permission expired notification" msgstr "发送资产权限过期通知" #: perms/templates/perms/_msg_item_permissions_expire.html:7 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| " The following %(item_type)s will expire in %(count)s\n" -#| " " -msgid "" -"\n" -" The following %(item_type)s will expire in %(count)s days\n" -" " -msgstr "" -"\n" -" 以下 %(item_type)s 即将在 %(count)s 后过期\n" -" " - #: perms/templates/perms/_msg_permed_items_expire.html:7 #, python-format msgid "" @@ -8801,6 +8796,7 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" + #~ msgid "FeiShu query user failed" #~ msgstr "飞书查询用户失败" diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py index 4610ddfd2..47af111e5 100644 --- a/apps/ops/ansible/runner.py +++ b/apps/ops/ansible/runner.py @@ -1,8 +1,9 @@ import os import uuid - +import shutil import ansible_runner from django.conf import settings +from django.utils._os import safe_join from .callback import DefaultCallback from ..utils import get_ansible_log_verbosity @@ -85,6 +86,34 @@ class PlaybookRunner: return self.cb +class UploadFileRunner: + def __init__(self, inventory, job_id, dest_path, callback=None): + self.id = uuid.uuid4() + self.inventory = inventory + self.cb = DefaultCallback() + upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file') + self.src_paths = safe_join(upload_file_dir, str(job_id)) + self.dest_path = dest_path + + def run(self, verbosity=0, **kwargs): + verbosity = get_ansible_log_verbosity(verbosity) + ansible_runner.run( + host_pattern="*", + inventory=self.inventory, + module='copy', + module_args=f"src={self.src_paths}/ dest={self.dest_path}", + verbosity=verbosity, + event_handler=self.cb.event_handler, + status_handler=self.cb.status_handler, + **kwargs + ) + try: + shutil.rmtree(self.src_paths) + except OSError as e: + print(f"del upload tmp dir {self.src_paths} failed! {e}") + return self.cb + + class CommandRunner(AdHocRunner): def __init__(self, inventory, command, pattern='*', project_dir='/tmp/'): super().__init__(inventory, 'shell', command, pattern, project_dir) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index f2bf92cae..3bcb7393e 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -1,16 +1,22 @@ +import json +import os from django.conf import settings from django.db import transaction from django.db.models import Count -from django.db.transaction import atomic from django.shortcuts import get_object_or_404 +from django.utils._os import safe_join +from django.utils.translation import gettext_lazy as _ + +from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.views import APIView from assets.models import Asset +from common.const.http import POST from common.permissions import IsValidUser from ops.const import Types from ops.models import Job, JobExecution -from ops.serializers.job import JobSerializer, JobExecutionSerializer +from ops.serializers.job import JobSerializer, JobExecutionSerializer, FileSerializer __all__ = [ 'JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', @@ -24,6 +30,7 @@ from orgs.utils import tmp_to_org, get_current_org from accounts.models import Account from perms.models import PermNode from perms.utils import UserPermAssetUtil +from jumpserver.settings import get_file_md5 def set_task_to_serializer_data(serializer, task_id): @@ -91,6 +98,40 @@ class JobViewSet(OrgBulkModelViewSet): transaction.on_commit( lambda: run_ops_job_execution.apply_async((str(execution.id),), task_id=str(execution.id))) + @action(methods=[POST], detail=False, serializer_class=FileSerializer, permission_classes=[IsValidUser, ], + url_path='upload') + def upload(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + if not serializer.is_valid(): + msg = 'Upload data invalid: {}'.format(serializer.errors) + return Response({'msg': msg}, status=400) + uploaded_files = request.FILES.getlist('files') + job_id = request.data.get('job_id', '') + job = get_object_or_404(Job, pk=job_id) + job_args = json.loads(job.args) + src_path_info = [] + filename_set = set() + same_filenames = [] + upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file') + for uploaded_file in uploaded_files: + filename = uploaded_file.name + saved_path = safe_join(upload_file_dir, f'{job_id}/{filename}') + os.makedirs(os.path.dirname(saved_path), exist_ok=True) + with open(saved_path, 'wb+') as destination: + for chunk in uploaded_file.chunks(): + destination.write(chunk) + if filename in filename_set: + same_filenames.append(filename) + filename_set.add(filename) + src_path_info.append({'filename': filename, 'md5': get_file_md5(saved_path)}) + if same_filenames: + return Response({'msg': _("Duplicate file exists")}, status=400) + job_args['src_path_info'] = src_path_info + job.args = json.dumps(job_args) + job.save() + self.run_job(job, serializer) + return Response({'task_id': serializer.data.get('task_id')}, status=201) + class JobExecutionViewSet(OrgBulkModelViewSet): serializer_class = JobExecutionSerializer diff --git a/apps/ops/const.py b/apps/ops/const.py index 7fa636a0f..578697c48 100644 --- a/apps/ops/const.py +++ b/apps/ops/const.py @@ -37,6 +37,7 @@ class CreateMethods(models.TextChoices): class Types(models.TextChoices): adhoc = 'adhoc', _('Adhoc') playbook = 'playbook', _('Playbook') + upload_file = 'upload_file', _('Upload File') class RunasPolicies(models.TextChoices): diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 7271a3401..358c37136 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -23,7 +23,7 @@ from assets.models import Asset from assets.automations.base.manager import SSHTunnelManager from common.db.encoder import ModelJSONFieldEncoder from labels.mixins import LabeledMixin -from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException +from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException, UploadFileRunner from ops.mixin import PeriodTaskModelMixin from ops.variables import * from ops.const import Types, RunasPolicies, JobStatus, JobModules @@ -362,7 +362,7 @@ class JobExecution(JMSOrgBaseModel): static_variables = self.gather_static_variables() extra_vars.update(static_variables) - if self.current_job.type == 'adhoc': + if self.current_job.type == Types.adhoc: module, args = self.compile_shell() runner = AdHocRunner( @@ -374,10 +374,18 @@ class JobExecution(JMSOrgBaseModel): project_dir=self.private_dir, extra_vars=extra_vars, ) - elif self.current_job.type == 'playbook': + elif self.current_job.type == Types.playbook: runner = PlaybookRunner( self.inventory_path, self.current_job.playbook.entry ) + elif self.current_job.type == Types.upload_file: + job_id = self.current_job.id + args = json.loads(self.current_job.args) + dst_path = args.get('dst_path') + if dst_path: + runner = UploadFileRunner(self.inventory_path, job_id, dst_path) + else: + raise ValueError("dst_path is null") else: raise Exception("unsupported job type") return runner diff --git a/apps/ops/serializers/job.py b/apps/ops/serializers/job.py index d97a0929a..3bb91d463 100644 --- a/apps/ops/serializers/job.py +++ b/apps/ops/serializers/job.py @@ -21,9 +21,12 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT def to_internal_value(self, data): instant = data.get('instant', False) + job_type = data.get('type', '') + _uid = str(uuid.uuid4()).split('-')[-1] if instant: - _uid = str(uuid.uuid4()).split('-')[-1] data['name'] = f'job-{_uid}' + if job_type == 'upload_file': + data['name'] = f'upload_file-{_uid}' return super().to_internal_value(data) def get_request_user(self): @@ -44,10 +47,17 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT "use_parameter_define", "parameters_define", "timeout", "chdir", "comment", "summary", "is_periodic", "interval", "crontab", "nodes", - "run_after_save", + "run_after_save" ] +class FileSerializer(serializers.Serializer): + files = serializers.FileField(allow_empty_file=True) + + class Meta: + ref_name = "JobFileSerializer" + + class JobExecutionSerializer(BulkOrgResourceModelSerializer): creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) job_type = serializers.ReadOnlyField(label=_("Job type")) diff --git a/apps/perms/notifications.py b/apps/perms/notifications.py index b00c50f43..5c82c2589 100644 --- a/apps/perms/notifications.py +++ b/apps/perms/notifications.py @@ -41,7 +41,7 @@ class AssetPermsWillExpireForOrgAdminMsg(UserMessage): super().__init__(user) self.perms = perms self.org = org - self.day_count = _('today') if day_count == 0 else day_count + self.day_count = _('today') if day_count == 0 else day_count + _('day') def get_items_with_url(self): items_with_url = [] diff --git a/apps/perms/templates/perms/_msg_item_permissions_expire.html b/apps/perms/templates/perms/_msg_item_permissions_expire.html index 9a9dc8244..eba2e7e5b 100644 --- a/apps/perms/templates/perms/_msg_item_permissions_expire.html +++ b/apps/perms/templates/perms/_msg_item_permissions_expire.html @@ -5,7 +5,7 @@

{% blocktranslate %} - The following {{ item_type }} will expire in {{ count }} days + The following {{ item_type }} will expire in {{ count }} {% endblocktranslate %}