From d554e92d022e5807db01139c51f0d9d04bdaba39 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 14 Nov 2022 18:48:21 +0800 Subject: [PATCH] perf: add applets deployment --- apps/terminal/api/applet/host.py | 20 +- .../deploy_applet_host/__init__.py | 99 +++++-- .../deploy_applet_host/install_all.yml | 8 + .../deploy_applet_host/install_applet.yml | 11 + .../deploy_applet_host/playbook.yml | 265 +++++++++--------- apps/terminal/models/applet/host.py | 10 + apps/terminal/serializers/applet_host.py | 26 +- apps/terminal/tasks.py | 20 +- 8 files changed, 278 insertions(+), 181 deletions(-) create mode 100644 apps/terminal/automations/deploy_applet_host/install_all.yml create mode 100644 apps/terminal/automations/deploy_applet_host/install_applet.yml diff --git a/apps/terminal/api/applet/host.py b/apps/terminal/api/applet/host.py index e5fb1c754..72321cc37 100644 --- a/apps/terminal/api/applet/host.py +++ b/apps/terminal/api/applet/host.py @@ -2,16 +2,15 @@ from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response -from common.permissions import IsServiceAccount from common.drf.api import JMSModelViewSet +from common.permissions import IsServiceAccount from orgs.utils import tmp_to_builtin_org +from terminal.models import AppletHost, AppletHostDeployment from terminal.serializers import ( AppletHostSerializer, AppletHostDeploymentSerializer, - AppletHostStartupSerializer + AppletHostStartupSerializer, AppletHostDeployAppletSerializer ) -from terminal.models import AppletHost, AppletHostDeployment -from terminal.tasks import run_applet_host_deployment - +from terminal.tasks import run_applet_host_deployment, run_applet_host_deployment_install_applet __all__ = ['AppletHostViewSet', 'AppletHostDeploymentViewSet'] @@ -41,6 +40,9 @@ class AppletHostViewSet(JMSModelViewSet): class AppletHostDeploymentViewSet(viewsets.ModelViewSet): serializer_class = AppletHostDeploymentSerializer queryset = AppletHostDeployment.objects.all() + rbac_perms = ( + ('applets', 'terminal.view_AppletHostDeployment'), + ) def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) @@ -49,3 +51,11 @@ class AppletHostDeploymentViewSet(viewsets.ModelViewSet): task = run_applet_host_deployment.delay(instance.id) return Response({'task': str(task.id)}, status=201) + @action(methods=['post'], detail=False, serializer_class=AppletHostDeployAppletSerializer) + def applets(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + applet_id = serializer.validated_data.get('applet_id') + instance = serializer.save() + task = run_applet_host_deployment_install_applet.delay(instance.id, applet_id) + return Response({'task': str(task.id)}, status=201) diff --git a/apps/terminal/automations/deploy_applet_host/__init__.py b/apps/terminal/automations/deploy_applet_host/__init__.py index 2946a6410..be5847aa7 100644 --- a/apps/terminal/automations/deploy_applet_host/__init__.py +++ b/apps/terminal/automations/deploy_applet_host/__init__.py @@ -1,22 +1,23 @@ -import os import datetime -import shutil +import os import yaml -from django.utils import timezone from django.conf import settings +from django.utils import timezone -from common.utils import get_logger from common.db.utils import safe_db_connection +from common.utils import get_logger from ops.ansible import PlaybookRunner, JMSInventory +from terminal.models import Applet, AppletHostDeployment logger = get_logger(__name__) CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) class DeployAppletHostManager: - def __init__(self, deployment): + def __init__(self, deployment: AppletHostDeployment, applet: Applet = None): self.deployment = deployment + self.applet = applet self.run_dir = self.get_run_dir() @staticmethod @@ -25,29 +26,56 @@ class DeployAppletHostManager: now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") return os.path.join(base, now) - def generate_playbook(self): - playbook_src = os.path.join(CURRENT_DIR, "playbook.yml") - base_site_url = settings.BASE_SITE_URL + def run(self, **kwargs): + self._run(self._run_initial_deploy, **kwargs) + + def install_applet(self, **kwargs): + self._run(self._run_install_applet, **kwargs) + + def _run_initial_deploy(self, **kwargs): + playbook = self.generate_initial_playbook + return self._run_playbook(playbook, **kwargs) + + def _run_install_applet(self, **kwargs): + if self.applet: + generate_playbook = self.generate_install_applet_playbook + else: + generate_playbook = self.generate_install_all_playbook + return self._run_playbook(generate_playbook, **kwargs) + + def generate_initial_playbook(self): + site_url = settings.SITE_URL bootstrap_token = settings.BOOTSTRAP_TOKEN host_id = str(self.deployment.host.id) - if not base_site_url: - base_site_url = "http://localhost:8080" - with open(playbook_src) as f: - plays = yaml.safe_load(f) - for play in plays: - play["vars"].update(self.deployment.host.deploy_options) - play["vars"]["DownloadHost"] = base_site_url + "/download" - play["vars"]["CORE_HOST"] = base_site_url - play["vars"]["BOOTSTRAP_TOKEN"] = bootstrap_token - play["vars"]["HOST_ID"] = host_id - play["vars"]["HOST_NAME"] = self.deployment.host.name + if not site_url: + site_url = "http://localhost:8080" + options = self.deployment.host.deploy_options - playbook_dir = os.path.join(self.run_dir, "playbook") - playbook_dst = os.path.join(playbook_dir, "main.yml") - os.makedirs(playbook_dir, exist_ok=True) - with open(playbook_dst, "w") as f: - yaml.safe_dump(plays, f) - return playbook_dst + def handler(plays): + for play in plays: + play["vars"].update(options) + play["vars"]["DownloadHost"] = site_url + "/download" + play["vars"]["CORE_HOST"] = site_url + play["vars"]["BOOTSTRAP_TOKEN"] = bootstrap_token + play["vars"]["HOST_ID"] = host_id + play["vars"]["HOST_NAME"] = self.deployment.host.name + + return self._generate_playbook("playbook.yml", handler) + + def generate_install_all_playbook(self): + return self._generate_playbook("install_all.yml") + + def generate_install_applet_playbook(self): + applet_name = self.applet.name + options = self.deployment.host.deploy_options + + def handler(plays): + for play in plays: + play["vars"].update(options) + play["vars"]["applet_name"] = applet_name + return plays + + return self._generate_playbook("install_applet.yml", handler) def generate_inventory(self): inventory = JMSInventory( @@ -58,18 +86,31 @@ class DeployAppletHostManager: inventory.write_to_file(inventory_path) return inventory_path - def _run(self, **kwargs): + def _generate_playbook(self, playbook_template_name, plays_handler: callable = None): + playbook_src = os.path.join(CURRENT_DIR, playbook_template_name) + with open(playbook_src) as f: + plays = yaml.safe_load(f) + if plays_handler: + plays = plays_handler(plays) + playbook_dir = os.path.join(self.run_dir, "playbook") + playbook_dst = os.path.join(playbook_dir, "main.yml") + os.makedirs(playbook_dir, exist_ok=True) + with open(playbook_dst, "w") as f: + yaml.safe_dump(plays, f) + return playbook_dst + + def _run_playbook(self, generate_playbook: callable, **kwargs): inventory = self.generate_inventory() - playbook = self.generate_playbook() + playbook = generate_playbook() runner = PlaybookRunner( inventory=inventory, playbook=playbook, project_dir=self.run_dir ) return runner.run(**kwargs) - def run(self, **kwargs): + def _run(self, cb_func: callable, **kwargs): try: self.deployment.date_start = timezone.now() - cb = self._run(**kwargs) + cb = cb_func(**kwargs) self.deployment.status = cb.status except Exception as e: logger.error("Error: {}".format(e)) diff --git a/apps/terminal/automations/deploy_applet_host/install_all.yml b/apps/terminal/automations/deploy_applet_host/install_all.yml new file mode 100644 index 000000000..bf3da06b4 --- /dev/null +++ b/apps/terminal/automations/deploy_applet_host/install_all.yml @@ -0,0 +1,8 @@ +--- + +- hosts: all + + tasks: + - name: Install all applets + ansible.windows.win_shell: + "tinkerd install all" diff --git a/apps/terminal/automations/deploy_applet_host/install_applet.yml b/apps/terminal/automations/deploy_applet_host/install_applet.yml new file mode 100644 index 000000000..5c216773f --- /dev/null +++ b/apps/terminal/automations/deploy_applet_host/install_applet.yml @@ -0,0 +1,11 @@ +--- + +- hosts: all + vars: + applet_name: chrome + + tasks: + - name: Install applet + ansible.windows.win_shell: + "tinkerd install --name {{ applet_name }}" + when: applet_name != 'all' diff --git a/apps/terminal/automations/deploy_applet_host/playbook.yml b/apps/terminal/automations/deploy_applet_host/playbook.yml index 4f652fcbf..3fec8999c 100644 --- a/apps/terminal/automations/deploy_applet_host/playbook.yml +++ b/apps/terminal/automations/deploy_applet_host/playbook.yml @@ -3,7 +3,6 @@ - hosts: all vars: DownloadHost: https://demo.jumpserver.org/download - Initial: 0 HOST_NAME: test HOST_ID: 00000000-0000-0000-0000-000000000000 CORE_HOST: https://demo.jumpserver.org @@ -17,166 +16,166 @@ TinkerInstaller: Tinker_Installer_v0.0.1.exe tasks: - - name: Install RDS-Licensing (RDS) - ansible.windows.win_feature: - name: RDS-Licensing - state: present - include_management_tools: yes - when: RDS_Licensing + - name: Install RDS-Licensing (RDS) + ansible.windows.win_feature: + name: RDS-Licensing + state: present + include_management_tools: yes + when: RDS_Licensing - - name: Install RDS-RD-Server (RDS) - ansible.windows.win_feature: - name: RDS-RD-Server - state: present - include_management_tools: yes - register: rds_install + - name: Install RDS-RD-Server (RDS) + ansible.windows.win_feature: + name: RDS-RD-Server + state: present + include_management_tools: yes + register: rds_install - - name: Download JumpServer Tinker installer (jumpserver) - ansible.windows.win_get_url: + - name: Download JumpServer Tinker installer (jumpserver) + ansible.windows.win_get_url: url: "{{ DownloadHost }}/{{ TinkerInstaller }}" dest: "{{ ansible_env.TEMP }}\\{{ TinkerInstaller }}" - - name: Install JumpServer Tinker (jumpserver) - ansible.windows.win_package: - path: "{{ ansible_env.TEMP }}\\{{ TinkerInstaller }}" - arguments: - - /VERYSILENT - - /SUPPRESSMSGBOXES - - /NORESTART - state: present + - name: Install JumpServer Tinker (jumpserver) + ansible.windows.win_package: + path: "{{ ansible_env.TEMP }}\\{{ TinkerInstaller }}" + arguments: + - /VERYSILENT + - /SUPPRESSMSGBOXES + - /NORESTART + state: present - - name: Set remote-server on the global system path (remote-server) - ansible.windows.win_path: - elements: - - '%USERPROFILE%\AppData\Local\Programs\Tinker\' - scope: user + - name: Set remote-server on the global system path (remote-server) + ansible.windows.win_path: + elements: + - '%USERPROFILE%\AppData\Local\Programs\Tinker\' + scope: user - - name: Download python-3.10.8 - ansible.windows.win_get_url: - url: "{{ DownloadHost }}/python-3.10.8-amd64.exe" - dest: "{{ ansible_env.TEMP }}\\python-3.10.8-amd64.exe" + - name: Download python-3.10.8 + ansible.windows.win_get_url: + url: "{{ DownloadHost }}/python-3.10.8-amd64.exe" + dest: "{{ ansible_env.TEMP }}\\python-3.10.8-amd64.exe" - - name: Install the python-3.10.8 - ansible.windows.win_package: - path: "{{ ansible_env.TEMP }}\\python-3.10.8-amd64.exe" - product_id: '{371d0d73-d418-4ffe-b280-58c3e7987525}' - arguments: - - /quiet - - InstallAllUsers=1 - - PrependPath=1 - - Include_test=0 - - Include_launcher=0 - state: present - register: win_install_python + - name: Install the python-3.10.8 + ansible.windows.win_package: + path: "{{ ansible_env.TEMP }}\\python-3.10.8-amd64.exe" + product_id: '{371d0d73-d418-4ffe-b280-58c3e7987525}' + arguments: + - /quiet + - InstallAllUsers=1 + - PrependPath=1 + - Include_test=0 + - Include_launcher=0 + state: present + register: win_install_python - - name: Reboot if installing requires it - ansible.windows.win_reboot: - post_reboot_delay: 10 - test_command: whoami - when: rds_install.reboot_required or win_install_python.reboot_required + - name: Reboot if installing requires it + ansible.windows.win_reboot: + post_reboot_delay: 10 + test_command: whoami + when: rds_install.reboot_required or win_install_python.reboot_required - - name: Set RDS LicenseServer (regedit) - ansible.windows.win_regedit: - path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services - name: LicenseServers - data: "{{ RDS_LicenseServer }}" - type: string + - name: Set RDS LicenseServer (regedit) + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services + name: LicenseServers + data: "{{ RDS_LicenseServer }}" + type: string - - name: Set RDS LicensingMode (regedit) - ansible.windows.win_regedit: - path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services - name: LicensingMode - data: "{{ RDS_LicensingMode }}" - type: dword + - name: Set RDS LicensingMode (regedit) + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services + name: LicensingMode + data: "{{ RDS_LicensingMode }}" + type: dword - - name: Set RDS fSingleSessionPerUser (regedit) - ansible.windows.win_regedit: - path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services - name: fSingleSessionPerUser - data: "{{ RDS_fSingleSessionPerUser }}" - type: dword + - name: Set RDS fSingleSessionPerUser (regedit) + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services + name: fSingleSessionPerUser + data: "{{ RDS_fSingleSessionPerUser }}" + type: dword - - name: Set RDS MaxDisconnectionTime (regedit) - ansible.windows.win_regedit: - path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services - name: MaxDisconnectionTime - data: "{{ RDS_MaxDisconnectionTime }}" - type: dword - when: RDS_MaxDisconnectionTime >= 60000 + - name: Set RDS MaxDisconnectionTime (regedit) + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services + name: MaxDisconnectionTime + data: "{{ RDS_MaxDisconnectionTime }}" + type: dword + when: RDS_MaxDisconnectionTime >= 60000 - - name: Set RDS RemoteAppLogoffTimeLimit (regedit) - ansible.windows.win_regedit: - path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services - name: RemoteAppLogoffTimeLimit - data: "{{ RDS_RemoteAppLogoffTimeLimit }}" - type: dword + - name: Set RDS RemoteAppLogoffTimeLimit (regedit) + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services + name: RemoteAppLogoffTimeLimit + data: "{{ RDS_RemoteAppLogoffTimeLimit }}" + type: dword - - name: Download pip packages - ansible.windows.win_get_url: - url: "{{ DownloadHost }}/pip_packages_v0.0.1.zip" - dest: "{{ ansible_env.TEMP }}\\pip_packages_v0.0.1.zip" + - name: Download pip packages + ansible.windows.win_get_url: + url: "{{ DownloadHost }}/pip_packages_v0.0.1.zip" + dest: "{{ ansible_env.TEMP }}\\pip_packages_v0.0.1.zip" - - name: Unzip pip_packages - community.windows.win_unzip: - src: "{{ ansible_env.TEMP }}\\pip_packages_v0.0.1.zip" - dest: "{{ ansible_env.TEMP }}" + - name: Unzip pip_packages + community.windows.win_unzip: + src: "{{ ansible_env.TEMP }}\\pip_packages_v0.0.1.zip" + dest: "{{ ansible_env.TEMP }}" - - name: Install python requirements offline - ansible.windows.win_shell: > + - name: Install python requirements offline + ansible.windows.win_shell: > pip install -r '{{ ansible_env.TEMP }}\pip_packages_v0.0.1\requirements.txt' --no-index --find-links='{{ ansible_env.TEMP }}\pip_packages_v0.0.1' - - name: Download chromedriver (chrome) - ansible.windows.win_get_url: - url: "{{ DownloadHost }}/chromedriver_win32.107.zip" - dest: "{{ ansible_env.TEMP }}\\chromedriver_win32.107.zip" + - name: Download chromedriver (chrome) + ansible.windows.win_get_url: + url: "{{ DownloadHost }}/chromedriver_win32.107.zip" + dest: "{{ ansible_env.TEMP }}\\chromedriver_win32.107.zip" - - name: Unzip chromedriver (chrome) - community.windows.win_unzip: - src: "{{ ansible_env.TEMP }}\\chromedriver_win32.107.zip" - dest: C:\Program Files\JumpServer\drivers + - name: Unzip chromedriver (chrome) + community.windows.win_unzip: + src: "{{ ansible_env.TEMP }}\\chromedriver_win32.107.zip" + dest: C:\Program Files\JumpServer\drivers - - name: Set chromedriver on the global system path (chrome) - ansible.windows.win_path: - elements: - - 'C:\Program Files\JumpServer\drivers' + - name: Set chromedriver on the global system path (chrome) + ansible.windows.win_path: + elements: + - 'C:\Program Files\JumpServer\drivers' - - name: Download chrome msi package (chrome) - ansible.windows.win_get_url: - url: "{{ DownloadHost }}/googlechromestandaloneenterprise64.msi" - dest: "{{ ansible_env.TEMP }}\\googlechromestandaloneenterprise64.msi" + - name: Download chrome msi package (chrome) + ansible.windows.win_get_url: + url: "{{ DownloadHost }}/googlechromestandaloneenterprise64.msi" + dest: "{{ ansible_env.TEMP }}\\googlechromestandaloneenterprise64.msi" - - name: Install chrome (chrome) - ansible.windows.win_package: - path: "{{ ansible_env.TEMP }}\\googlechromestandaloneenterprise64.msi" - state: present - arguments: - - /quiet + - name: Install chrome (chrome) + ansible.windows.win_package: + path: "{{ ansible_env.TEMP }}\\googlechromestandaloneenterprise64.msi" + state: present + arguments: + - /quiet - - name: Generate tinkerd component config - ansible.windows.win_shell: - "tinkerd config --hostname {{ HOST_NAME }} --core_host {{ CORE_HOST }} + - name: Generate tinkerd component config + ansible.windows.win_shell: + "tinkerd config --hostname {{ HOST_NAME }} --core_host {{ CORE_HOST }} --token {{ BOOTSTRAP_TOKEN }} --host_id {{ HOST_ID }}" - - name: Install tinkerd service - ansible.windows.win_shell: - "tinkerd service install" + - name: Install tinkerd service + ansible.windows.win_shell: + "tinkerd service install" - - name: Start tinkerd service - ansible.windows.win_shell: - "tinkerd service start" + - name: Start tinkerd service + ansible.windows.win_shell: + "tinkerd service start" - - name: Wait Tinker api health - ansible.windows.win_uri: - url: http://localhost:6068/api/health/ - status_code: 200 - method: GET - register: _result - until: _result.status_code == 200 - retries: 30 - delay: 5 + - name: Wait Tinker api health + ansible.windows.win_uri: + url: http://localhost:6068/api/health/ + status_code: 200 + method: GET + register: _result + until: _result.status_code == 200 + retries: 30 + delay: 5 - - name: Sync all remote applets - ansible.windows.win_shell: - "tinkerd install all" + - name: Sync all remote applets + ansible.windows.win_shell: + "tinkerd install all" diff --git a/apps/terminal/models/applet/host.py b/apps/terminal/models/applet/host.py index 990e366df..3e510a3e3 100644 --- a/apps/terminal/models/applet/host.py +++ b/apps/terminal/models/applet/host.py @@ -110,3 +110,13 @@ class AppletHostDeployment(JMSBaseModel): from ...automations.deploy_applet_host import DeployAppletHostManager manager = DeployAppletHostManager(self) manager.run(**kwargs) + + def install_applet(self, applet_id, **kwargs): + from ...automations.deploy_applet_host import DeployAppletHostManager + from .applet import Applet + if applet_id: + applet = Applet.objects.get(id=applet_id) + else: + applet = None + manager = DeployAppletHostManager(self, applet=applet) + manager.install_applet(**kwargs) diff --git a/apps/terminal/serializers/applet_host.py b/apps/terminal/serializers/applet_host.py index b94d615c8..2a291da73 100644 --- a/apps/terminal/serializers/applet_host.py +++ b/apps/terminal/serializers/applet_host.py @@ -1,19 +1,18 @@ -from rest_framework import serializers from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers -from common.validators import ProjectUniqueValidator -from common.drf.fields import ObjectRelatedField, LabeledChoiceField from assets.models import Platform, Account from assets.serializers import HostSerializer -from ..models import AppletHost, AppletHostDeployment, Applet +from common.drf.fields import LabeledChoiceField +from common.validators import ProjectUniqueValidator from .applet import AppletSerializer from .. import const - +from ..models import AppletHost, AppletHostDeployment __all__ = [ 'AppletHostSerializer', 'AppletHostDeploymentSerializer', 'AppletHostAccountSerializer', 'AppletHostAppletReportSerializer', - 'AppletHostStartupSerializer', + 'AppletHostStartupSerializer', 'AppletHostDeployAppletSerializer' ] @@ -29,7 +28,8 @@ class DeployOptionsSerializer(serializers.Serializer): RDS_Licensing = serializers.BooleanField(default=False, label=_("RDS Licensing")) RDS_LicenseServer = serializers.CharField(default='127.0.0.1', label=_('RDS License Server'), max_length=1024) RDS_LicensingMode = serializers.ChoiceField(choices=LICENSE_MODE_CHOICES, default=4, label=_('RDS Licensing Mode')) - RDS_fSingleSessionPerUser = serializers.ChoiceField(choices=SESSION_PER_USER, default=1, label=_("RDS fSingleSessionPerUser")) + RDS_fSingleSessionPerUser = serializers.ChoiceField(choices=SESSION_PER_USER, default=1, + label=_("RDS fSingleSessionPerUser")) RDS_MaxDisconnectionTime = serializers.IntegerField(default=60000, label=_("RDS Max Disconnection Time")) RDS_RemoteAppLogoffTimeLimit = serializers.IntegerField(default=0, label=_("RDS Remote App Logoff Time Limit")) @@ -94,6 +94,18 @@ class AppletHostDeploymentSerializer(serializers.ModelSerializer): fields = fields_mini + ['comment'] + read_only_fields +class AppletHostDeployAppletSerializer(AppletHostDeploymentSerializer): + applet_id = serializers.UUIDField(write_only=True, allow_null=True, required=False) + + class Meta(AppletHostDeploymentSerializer.Meta): + fields = AppletHostDeploymentSerializer.Meta.fields + ['applet_id'] + + def create(self, validated_data): + applet_id = validated_data.pop('applet_id', None) + deployment = super().create(validated_data) + return deployment + + class AppletHostAccountSerializer(serializers.ModelSerializer): class Meta: model = Account diff --git a/apps/terminal/tasks.py b/apps/terminal/tasks.py index 396369d16..4e67d1fc7 100644 --- a/apps/terminal/tasks.py +++ b/apps/terminal/tasks.py @@ -1,26 +1,25 @@ # -*- coding: utf-8 -*- # +import datetime import os import subprocess -import datetime from celery import shared_task from celery.utils.log import get_task_logger -from django.utils import timezone from django.core.files.storage import default_storage +from django.utils import timezone from common.utils import get_log_keep_day from ops.celery.decorator import ( register_as_period_task, after_app_ready_start, after_app_shutdown_clean_periodic ) -from .models import ( - Status, Session, Command, Task, AppletHost, - AppletHostDeployment -) from orgs.utils import tmp_to_builtin_org from .backends import server_replay_storage +from .models import ( + Status, Session, Command, Task, AppletHostDeployment +) from .utils import find_session_replay_local CACHE_REFRESH_INTERVAL = 10 @@ -57,7 +56,7 @@ def clean_orphan_session(): @shared_task -@register_as_period_task(interval=3600*24) +@register_as_period_task(interval=3600 * 24) @after_app_ready_start @after_app_shutdown_clean_periodic def clean_expired_session_period(): @@ -114,3 +113,10 @@ def run_applet_host_deployment(did): with tmp_to_builtin_org(system=1): deployment = AppletHostDeployment.objects.get(id=did) deployment.start() + + +@shared_task +def run_applet_host_deployment_install_applet(did, applet_id): + with tmp_to_builtin_org(system=1): + deployment = AppletHostDeployment.objects.get(id=did) + deployment.install_applet(applet_id)