jumpserver/apps/terminal/models/applet/host.py

141 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import os
from collections import defaultdict
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError
from simple_history.utils import bulk_create_with_history
from assets.models import Host
from common.db.models import JMSBaseModel
from common.utils import random_string
__all__ = ['AppletHost', 'AppletHostDeployment']
class AppletHost(Host):
deploy_options = models.JSONField(default=dict, verbose_name=_('Deploy options'))
inited = models.BooleanField(default=False, verbose_name=_('Inited'))
date_inited = models.DateTimeField(null=True, blank=True, verbose_name=_('Date inited'))
date_synced = models.DateTimeField(null=True, blank=True, verbose_name=_('Date synced'))
terminal = models.OneToOneField(
'terminal.Terminal', on_delete=models.PROTECT, null=True, blank=True,
related_name='applet_host', verbose_name=_('Terminal')
)
applets = models.ManyToManyField(
'Applet', verbose_name=_('Applet'),
through='AppletPublication', through_fields=('host', 'applet'),
)
LOCKING_ORG = '00000000-0000-0000-0000-000000000004'
class Meta:
verbose_name = _('Hosting')
def __str__(self):
return self.name
@property
def load(self):
if not self.terminal:
return 'offline'
return self.terminal.load
def check_terminal_binding(self, request):
request_terminal = getattr(request.user, 'terminal', None)
if not request_terminal:
raise ValidationError('Request user has no terminal')
self.date_synced = timezone.now()
if self.terminal == request_terminal:
self.save(update_fields=['date_synced'])
else:
self.terminal = request_terminal
self.save(update_fields=['terminal', 'date_synced'])
def check_applets_state(self, applets_value_list):
applets = self.applets.all()
name_version_mapper = {
value['name']: value['version']
for value in applets_value_list
}
status_applets = defaultdict(list)
for applet in applets:
if applet.name not in name_version_mapper:
status_applets['unpublished'].append(applet)
elif applet.version != name_version_mapper[applet.name]:
status_applets['not_match'].append(applet)
else:
status_applets['published'].append(applet)
for status, applets in status_applets.items():
self.publications.filter(applet__in=applets) \
.exclude(status=status) \
.update(status=status)
@staticmethod
def random_username():
return 'jms_' + random_string(8)
@staticmethod
def random_password():
return random_string(16, special_char=True)
def generate_accounts(self):
amount = int(os.getenv('TERMINAL_ACCOUNTS_AMOUNT', 100))
now_count = self.accounts.filter(privileged=False).count()
need = amount - now_count
accounts = []
account_model = self.accounts.model
for i in range(need):
username = self.random_username()
password = self.random_password()
account = account_model(
username=username, secret=password, name=username,
asset_id=self.id, secret_type='password', version=1,
org_id=self.LOCKING_ORG, is_active=False,
)
accounts.append(account)
bulk_create_with_history(accounts, account_model, batch_size=20)
class AppletHostDeployment(JMSBaseModel):
host = models.ForeignKey('AppletHost', on_delete=models.CASCADE, verbose_name=_('Hosting'))
initial = models.BooleanField(default=False, verbose_name=_('Initial'))
status = models.CharField(max_length=16, default='pending', verbose_name=_('Status'))
date_start = models.DateTimeField(null=True, verbose_name=_('Date start'), db_index=True)
date_finished = models.DateTimeField(null=True, verbose_name=_("Date finished"))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
task = models.UUIDField(null=True, verbose_name=_('Task'))
class Meta:
ordering = ('-date_start',)
def start(self, **kwargs):
# 重新初始化部署applet host 关联的终端需要删除
# 否则 tinker 会因终端注册名称相同,造成冲突,执行任务失败
if self.host.terminal:
terminal = self.host.terminal
self.host.terminal = None
self.host.save()
terminal.delete()
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)
def save_task(self, task):
self.task = task
self.save(update_fields=['task'])