perf: add applets deployment

pull/9075/head
Eric 2022-11-14 18:48:21 +08:00
parent 8e1312e8ce
commit d554e92d02
8 changed files with 278 additions and 181 deletions

View File

@ -2,16 +2,15 @@ from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from common.permissions import IsServiceAccount
from common.drf.api import JMSModelViewSet from common.drf.api import JMSModelViewSet
from common.permissions import IsServiceAccount
from orgs.utils import tmp_to_builtin_org from orgs.utils import tmp_to_builtin_org
from terminal.models import AppletHost, AppletHostDeployment
from terminal.serializers import ( from terminal.serializers import (
AppletHostSerializer, AppletHostDeploymentSerializer, AppletHostSerializer, AppletHostDeploymentSerializer,
AppletHostStartupSerializer AppletHostStartupSerializer, AppletHostDeployAppletSerializer
) )
from terminal.models import AppletHost, AppletHostDeployment from terminal.tasks import run_applet_host_deployment, run_applet_host_deployment_install_applet
from terminal.tasks import run_applet_host_deployment
__all__ = ['AppletHostViewSet', 'AppletHostDeploymentViewSet'] __all__ = ['AppletHostViewSet', 'AppletHostDeploymentViewSet']
@ -41,6 +40,9 @@ class AppletHostViewSet(JMSModelViewSet):
class AppletHostDeploymentViewSet(viewsets.ModelViewSet): class AppletHostDeploymentViewSet(viewsets.ModelViewSet):
serializer_class = AppletHostDeploymentSerializer serializer_class = AppletHostDeploymentSerializer
queryset = AppletHostDeployment.objects.all() queryset = AppletHostDeployment.objects.all()
rbac_perms = (
('applets', 'terminal.view_AppletHostDeployment'),
)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
@ -49,3 +51,11 @@ class AppletHostDeploymentViewSet(viewsets.ModelViewSet):
task = run_applet_host_deployment.delay(instance.id) task = run_applet_host_deployment.delay(instance.id)
return Response({'task': str(task.id)}, status=201) 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)

View File

@ -1,22 +1,23 @@
import os
import datetime import datetime
import shutil import os
import yaml import yaml
from django.utils import timezone
from django.conf import settings 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.db.utils import safe_db_connection
from common.utils import get_logger
from ops.ansible import PlaybookRunner, JMSInventory from ops.ansible import PlaybookRunner, JMSInventory
from terminal.models import Applet, AppletHostDeployment
logger = get_logger(__name__) logger = get_logger(__name__)
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
class DeployAppletHostManager: class DeployAppletHostManager:
def __init__(self, deployment): def __init__(self, deployment: AppletHostDeployment, applet: Applet = None):
self.deployment = deployment self.deployment = deployment
self.applet = applet
self.run_dir = self.get_run_dir() self.run_dir = self.get_run_dir()
@staticmethod @staticmethod
@ -25,29 +26,56 @@ class DeployAppletHostManager:
now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
return os.path.join(base, now) return os.path.join(base, now)
def generate_playbook(self): def run(self, **kwargs):
playbook_src = os.path.join(CURRENT_DIR, "playbook.yml") self._run(self._run_initial_deploy, **kwargs)
base_site_url = settings.BASE_SITE_URL
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 bootstrap_token = settings.BOOTSTRAP_TOKEN
host_id = str(self.deployment.host.id) host_id = str(self.deployment.host.id)
if not base_site_url: if not site_url:
base_site_url = "http://localhost:8080" site_url = "http://localhost:8080"
with open(playbook_src) as f: options = self.deployment.host.deploy_options
plays = yaml.safe_load(f)
def handler(plays):
for play in plays: for play in plays:
play["vars"].update(self.deployment.host.deploy_options) play["vars"].update(options)
play["vars"]["DownloadHost"] = base_site_url + "/download" play["vars"]["DownloadHost"] = site_url + "/download"
play["vars"]["CORE_HOST"] = base_site_url play["vars"]["CORE_HOST"] = site_url
play["vars"]["BOOTSTRAP_TOKEN"] = bootstrap_token play["vars"]["BOOTSTRAP_TOKEN"] = bootstrap_token
play["vars"]["HOST_ID"] = host_id play["vars"]["HOST_ID"] = host_id
play["vars"]["HOST_NAME"] = self.deployment.host.name play["vars"]["HOST_NAME"] = self.deployment.host.name
playbook_dir = os.path.join(self.run_dir, "playbook") return self._generate_playbook("playbook.yml", handler)
playbook_dst = os.path.join(playbook_dir, "main.yml")
os.makedirs(playbook_dir, exist_ok=True) def generate_install_all_playbook(self):
with open(playbook_dst, "w") as f: return self._generate_playbook("install_all.yml")
yaml.safe_dump(plays, f)
return playbook_dst 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): def generate_inventory(self):
inventory = JMSInventory( inventory = JMSInventory(
@ -58,18 +86,31 @@ class DeployAppletHostManager:
inventory.write_to_file(inventory_path) inventory.write_to_file(inventory_path)
return 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() inventory = self.generate_inventory()
playbook = self.generate_playbook() playbook = generate_playbook()
runner = PlaybookRunner( runner = PlaybookRunner(
inventory=inventory, playbook=playbook, project_dir=self.run_dir inventory=inventory, playbook=playbook, project_dir=self.run_dir
) )
return runner.run(**kwargs) return runner.run(**kwargs)
def run(self, **kwargs): def _run(self, cb_func: callable, **kwargs):
try: try:
self.deployment.date_start = timezone.now() self.deployment.date_start = timezone.now()
cb = self._run(**kwargs) cb = cb_func(**kwargs)
self.deployment.status = cb.status self.deployment.status = cb.status
except Exception as e: except Exception as e:
logger.error("Error: {}".format(e)) logger.error("Error: {}".format(e))

View File

@ -0,0 +1,8 @@
---
- hosts: all
tasks:
- name: Install all applets
ansible.windows.win_shell:
"tinkerd install all"

View File

@ -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'

View File

@ -3,7 +3,6 @@
- hosts: all - hosts: all
vars: vars:
DownloadHost: https://demo.jumpserver.org/download DownloadHost: https://demo.jumpserver.org/download
Initial: 0
HOST_NAME: test HOST_NAME: test
HOST_ID: 00000000-0000-0000-0000-000000000000 HOST_ID: 00000000-0000-0000-0000-000000000000
CORE_HOST: https://demo.jumpserver.org CORE_HOST: https://demo.jumpserver.org

View File

@ -110,3 +110,13 @@ class AppletHostDeployment(JMSBaseModel):
from ...automations.deploy_applet_host import DeployAppletHostManager from ...automations.deploy_applet_host import DeployAppletHostManager
manager = DeployAppletHostManager(self) manager = DeployAppletHostManager(self)
manager.run(**kwargs) 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)

View File

@ -1,19 +1,18 @@
from rest_framework import serializers
from django.utils.translation import gettext_lazy as _ 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.models import Platform, Account
from assets.serializers import HostSerializer 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 .applet import AppletSerializer
from .. import const from .. import const
from ..models import AppletHost, AppletHostDeployment
__all__ = [ __all__ = [
'AppletHostSerializer', 'AppletHostDeploymentSerializer', 'AppletHostSerializer', 'AppletHostDeploymentSerializer',
'AppletHostAccountSerializer', 'AppletHostAppletReportSerializer', 'AppletHostAccountSerializer', 'AppletHostAppletReportSerializer',
'AppletHostStartupSerializer', 'AppletHostStartupSerializer', 'AppletHostDeployAppletSerializer'
] ]
@ -29,7 +28,8 @@ class DeployOptionsSerializer(serializers.Serializer):
RDS_Licensing = serializers.BooleanField(default=False, label=_("RDS Licensing")) 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_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_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_MaxDisconnectionTime = serializers.IntegerField(default=60000, label=_("RDS Max Disconnection Time"))
RDS_RemoteAppLogoffTimeLimit = serializers.IntegerField(default=0, label=_("RDS Remote App Logoff Time Limit")) 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 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 AppletHostAccountSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Account model = Account

View File

@ -1,26 +1,25 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import datetime
import os import os
import subprocess import subprocess
import datetime
from celery import shared_task from celery import shared_task
from celery.utils.log import get_task_logger from celery.utils.log import get_task_logger
from django.utils import timezone
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.utils import timezone
from common.utils import get_log_keep_day from common.utils import get_log_keep_day
from ops.celery.decorator import ( from ops.celery.decorator import (
register_as_period_task, after_app_ready_start, register_as_period_task, after_app_ready_start,
after_app_shutdown_clean_periodic after_app_shutdown_clean_periodic
) )
from .models import (
Status, Session, Command, Task, AppletHost,
AppletHostDeployment
)
from orgs.utils import tmp_to_builtin_org from orgs.utils import tmp_to_builtin_org
from .backends import server_replay_storage from .backends import server_replay_storage
from .models import (
Status, Session, Command, Task, AppletHostDeployment
)
from .utils import find_session_replay_local from .utils import find_session_replay_local
CACHE_REFRESH_INTERVAL = 10 CACHE_REFRESH_INTERVAL = 10
@ -114,3 +113,10 @@ def run_applet_host_deployment(did):
with tmp_to_builtin_org(system=1): with tmp_to_builtin_org(system=1):
deployment = AppletHostDeployment.objects.get(id=did) deployment = AppletHostDeployment.objects.get(id=did)
deployment.start() 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)