pref: 修改 applet host deploy

pull/9008/head
ibuler 2022-10-28 18:19:44 +08:00
parent 8f9eb64c8d
commit 12b74093e2
12 changed files with 140 additions and 54 deletions

View File

@ -9,8 +9,9 @@ __all__ = ['JMSInventory']
class JMSInventory:
def __init__(self, manager, assets=None, account_policy='smart',
account_prefer='root,administrator', host_callback=None):
def __init__(self, assets, account_policy='smart',
account_prefer='root,administrator',
host_callback=None):
"""
:param assets:
:param account_prefer: account username name if not set use account_policy
@ -79,10 +80,7 @@ class JMSInventory:
ssh_protocol_matched = list(filter(lambda x: x.name == 'ssh', protocols))
ssh_protocol = ssh_protocol_matched[0] if ssh_protocol_matched else None
host['ansible_host'] = asset.address
if asset.port == 0:
host['ansible_port'] = ssh_protocol.port if ssh_protocol else 22
else:
host['ansible_port'] = asset.port
host['ansible_port'] = ssh_protocol.port if ssh_protocol else 22
su_from = account.su_from
if platform.su_enabled and su_from:

View File

@ -99,7 +99,7 @@ class CeleryPeriodTaskViewSet(CommonApiMixin, viewsets.ModelViewSet):
class CeleryTaskViewSet(CommonApiMixin, viewsets.ReadOnlyModelViewSet):
queryset = CeleryTask.objects.filter(name__in=['ops.tasks.hello', 'ops.tasks.hello_error', 'ops.tasks.hello_random'])
queryset = CeleryTask.objects.all()
serializer_class = CeleryTaskSerializer
http_method_names = ('get', 'head', 'options',)

View File

@ -1,7 +1,7 @@
import os.path
import shutil
import zipfile
import yaml
import os.path
from django.core.files.storage import default_storage
from rest_framework import viewsets
@ -9,12 +9,16 @@ from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from terminal import serializers, models
from terminal import serializers
from terminal.models import AppletPublication, Applet
from terminal.serializers import AppletUploadSerializer
__all__ = ['AppletViewSet', 'AppletPublicationViewSet']
class AppletViewSet(viewsets.ModelViewSet):
queryset = models.Applet.objects.all()
queryset = Applet.objects.all()
serializer_class = serializers.AppletSerializer
rbac_perms = {
'upload': 'terminal.add_applet',
@ -67,7 +71,7 @@ class AppletViewSet(viewsets.ModelViewSet):
name = manifest['name']
update = request.query_params.get('update')
instance = models.Applet.objects.filter(name=name).first()
instance = Applet.objects.filter(name=name).first()
if instance and not update:
return Response({'error': 'Applet already exists: {}'.format(name)}, status=400)
@ -82,5 +86,5 @@ class AppletViewSet(viewsets.ModelViewSet):
class AppletPublicationViewSet(viewsets.ModelViewSet):
queryset = models.AppletPublication.objects.all()
queryset = AppletPublication.objects.all()
serializer_class = serializers.AppletPublicationSerializer

View File

@ -1,23 +1,35 @@
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from orgs.utils import tmp_to_builtin_org
from terminal import serializers, models
from terminal import serializers
from terminal.models import AppletHost, Applet
from terminal.tasks import run_applet_host_deployment
__all__ = ['AppletHostViewSet', 'AppletHostDeploymentViewSet']
__all__ = ['AppletHostViewSet']
class AppletHostViewSet(viewsets.ModelViewSet):
serializer_class = serializers.AppletHostSerializer
def get_queryset(self):
return models.AppletHost.objects.all()
return AppletHost.objects.all()
def dispatch(self, request, *args, **kwargs):
with tmp_to_builtin_org(system=1):
return super().dispatch(request, *args, **kwargs)
@action(methods=['post'], detail=True)
def deploy(self, request):
from terminal.automations.deploy_applet_host.manager import DeployAppletHostManager
manager = DeployAppletHostManager(self)
manager.run()
class AppletHostDeploymentViewSet(viewsets.ModelViewSet):
queryset = models.AppletHostDeployment.objects.all()
serializer_class = serializers.AppletHostDeploymentSerializer
@action(methods=['get'], detail=True, url_path='')
def not_published_applets(self, request, *args, **kwargs):
instance = self.get_object()
applets = Applet.objects.exclude(id__in=instance.applets.all())
serializer = serializers.AppletSerializer(applets, many=True)
return Response(serializer.data)

View File

@ -0,0 +1,43 @@
import os
import datetime
import shutil
from django.conf import settings
from ops.ansible import PlaybookRunner, JMSInventory
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
class DeployAppletHostManager:
def __init__(self, applet_host):
self.applet_host = applet_host
self.run_dir = self.get_run_dir()
@staticmethod
def get_run_dir():
base = os.path.join(settings.ANSIBLE_DIR, 'applet_host_deploy')
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')
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)
shutil.copy(playbook_src, playbook_dst)
return playbook_dst
def generate_inventory(self):
inventory = JMSInventory([self.applet_host], account_policy='privileged_only')
inventory_dir = os.path.join(self.run_dir, 'inventory')
inventory_path = os.path.join(inventory_dir, 'hosts.yml')
inventory.write_to_file(inventory_path)
return inventory_path
def run(self, **kwargs):
inventory = self.generate_inventory()
playbook = self.generate_playbook()
runner = PlaybookRunner(
inventory=inventory, playbook=playbook, project_dir=self.run_dir
)
return runner.run(**kwargs)

View File

@ -1,6 +1,6 @@
---
- hosts: windows
- hosts: all
vars:
- DownloadHost: https://demo.jumpserver.org/download
- RDS_Licensing: enabled

View File

@ -0,0 +1,42 @@
# Generated by Django 3.2.14 on 2022-10-28 07:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('terminal', '0054_auto_20221027_1125'),
]
operations = [
migrations.AddField(
model_name='applet',
name='hosts',
field=models.ManyToManyField(through='terminal.AppletPublication', to='terminal.AppletHost', verbose_name='Hosts'),
),
migrations.AddField(
model_name='applethost',
name='date_inited',
field=models.DateTimeField(blank=True, null=True, verbose_name='Date initialized'),
),
migrations.AddField(
model_name='applethost',
name='initialized',
field=models.BooleanField(default=False, verbose_name='Initialized'),
),
migrations.AlterField(
model_name='appletpublication',
name='applet',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='publications', to='terminal.applet', verbose_name='Applet'),
),
migrations.AlterField(
model_name='appletpublication',
name='host',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='publications', to='terminal.applethost', verbose_name='Host'),
),
migrations.DeleteModel(
name='AppletHostDeployment',
),
]

View File

@ -26,6 +26,7 @@ class Applet(JMSBaseModel):
protocols = models.JSONField(default=list, verbose_name=_('Protocol'))
tags = models.JSONField(default=list, verbose_name=_('Tags'))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
hosts = models.ManyToManyField(through_fields=('applet', 'host'), through='AppletPublication', to='AppletHost', verbose_name=_('Hosts'))
def __str__(self):
return self.name
@ -51,8 +52,8 @@ class Applet(JMSBaseModel):
class AppletPublication(JMSBaseModel):
applet = models.ForeignKey('Applet', on_delete=models.PROTECT, verbose_name=_('Applet'))
host = models.ForeignKey('AppletHost', on_delete=models.PROTECT, verbose_name=_('Host'))
applet = models.ForeignKey('Applet', on_delete=models.PROTECT, related_name='publications', verbose_name=_('Applet'))
host = models.ForeignKey('AppletHost', on_delete=models.PROTECT, related_name='publications', verbose_name=_('Host'))
status = models.CharField(max_length=16, verbose_name=_('Status'))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))

View File

@ -1,15 +1,17 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from common.db.models import JMSBaseModel
from assets.models import Host
from ops.ansible import PlaybookRunner, JMSInventory
__all__ = ['AppletHost', 'AppletHostDeployment']
__all__ = ['AppletHost']
class AppletHost(Host):
account_automation = models.BooleanField(default=False, verbose_name=_('Account automation'))
initialized = models.BooleanField(default=False, verbose_name=_('Initialized'))
date_inited = models.DateTimeField(null=True, blank=True, verbose_name=_('Date initialized'))
date_synced = models.DateTimeField(null=True, blank=True, verbose_name=_('Date synced'))
status = models.CharField(max_length=16, verbose_name=_('Status'))
applets = models.ManyToManyField(
@ -17,17 +19,11 @@ class AppletHost(Host):
through='AppletPublication', through_fields=('host', 'applet'),
)
def deploy(self):
inventory = JMSInventory([self])
playbook = PlaybookRunner(inventory, 'applets.yml')
playbook.run()
def __str__(self):
return self.name
class AppletHostDeployment(JMSBaseModel):
host = models.ForeignKey('AppletHost', on_delete=models.CASCADE, verbose_name=_('Hosting'))
status = models.CharField(max_length=16, verbose_name=_('Status'))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.host
def start(self):
pass

View File

@ -5,12 +5,12 @@ from common.drf.fields import ObjectRelatedField, LabeledChoiceField
from common.validators import ProjectUniqueValidator
from assets.models import Platform
from assets.serializers import HostSerializer
from ..models import Applet, AppletPublication, AppletHost, AppletHostDeployment
from ..models import Applet, AppletPublication, AppletHost
__all__ = [
'AppletSerializer', 'AppletPublicationSerializer',
'AppletHostSerializer', 'AppletHostDeploymentSerializer',
'AppletHostSerializer',
'AppletUploadSerializer'
]
@ -85,14 +85,3 @@ class AppletHostSerializer(HostSerializer):
validators.append(uniq_validator)
return validators
class AppletHostDeploymentSerializer(serializers.ModelSerializer):
host = ObjectRelatedField(queryset=AppletHost.objects.all())
class Meta:
model = AppletHostDeployment
fields_mini = ['id', 'host']
read_only_fields = ['date_created', 'date_updated']
fields = fields_mini + [
'status', 'comment',
] + read_only_fields

View File

@ -14,7 +14,7 @@ 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
from .models import Status, Session, Command, Task, AppletHost
from .backends import server_replay_storage
from .utils import find_session_replay_local
@ -99,3 +99,9 @@ def upload_session_replay_to_external_storage(session_id):
except:
pass
return
@shared_task
def run_applet_host_deployment(did):
host = AppletHost.objects.get(id=did)
host.deploy()

View File

@ -26,8 +26,7 @@ router.register(r'endpoints', api.EndpointViewSet, 'endpoint')
router.register(r'endpoint-rules', api.EndpointRuleViewSet, 'endpoint-rule')
router.register(r'applets', api.AppletViewSet, 'applet')
router.register(r'applet-hosts', api.AppletHostViewSet, 'applet-host')
router.register(r'applet-publication', api.AppletPublicationViewSet, 'applet-publication')
router.register(r'applet-host-deployment', api.AppletHostDeploymentViewSet, 'applet-host-deployment')
router.register(r'applet-publications', api.AppletPublicationViewSet, 'applet-publication')
urlpatterns = [
@ -46,10 +45,6 @@ urlpatterns = [
path('command-storages/<uuid:pk>/test-connective/', api.CommandStorageTestConnectiveApi.as_view(), name='command-storage-test-connective'),
# components
path('components/metrics/', api.ComponentsMetricsAPIView.as_view(), name='components-metrics'),
# v2: get session's replay
# path('v2/sessions/<uuid:pk>/replay/',
# api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}),
# name='session-replay-v2'),
]
old_version_urlpatterns = [