You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jumpserver/apps/terminal/tasks.py

214 lines
6.6 KiB

# -*- coding: utf-8 -*-
#
import datetime
from itertools import chain
from celery import shared_task
from celery.utils.log import get_task_logger
from django.core.files.storage import default_storage
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from common.storage.replay import ReplayStorageHandler
from ops.celery.decorator import (
register_as_period_task, after_app_ready_start)
from orgs.utils import tmp_to_builtin_org
from orgs.utils import tmp_to_root_org
from .backends import server_replay_storage
from .const import ReplayStorageType, CommandStorageType
from .models import (
Status, Session, Task, AppletHostDeployment,
AppletHost, ReplayStorage, CommandStorage
)
from .notifications import StorageConnectivityMessage
CACHE_REFRESH_INTERVAL = 10
RUNNING = False
logger = get_task_logger(__name__)
@shared_task(
verbose_name=_('Periodic delete terminal status'),
description=_("Unused")
)
@register_as_period_task(interval=3600)
@after_app_ready_start
def delete_terminal_status_period():
yesterday = timezone.now() - datetime.timedelta(days=7)
Status.objects.filter(date_created__lt=yesterday).delete()
@shared_task(
verbose_name=_('Clean orphan session'),
description=_(
"""
Check every 10 minutes for asset connection sessions that have been inactive for 3
minutes and mark these sessions as completed
"""
)
)
@register_as_period_task(interval=600)
@after_app_ready_start
@tmp_to_root_org()
def clean_orphan_session():
active_sessions = Session.objects.filter(is_finished=False)
for session in active_sessions:
# finished task
Task.objects.filter(args=str(session.id), is_finished=False).update(
is_finished=True, date_finished=timezone.now()
)
# finished session
if session.is_active():
continue
session.is_finished = True
session.date_end = timezone.now()
session.save()
@shared_task(
verbose_name=_('Upload session replay to external storage'),
description=_(
"""
If SERVER_REPLAY_STORAGE is configured in the config.txt, session commands and
recordings will be uploaded to external storage
"""
)
)
def upload_session_replay_to_external_storage(session_id):
logger.info(f'Start upload session to external storage: {session_id}')
session = Session.objects.filter(id=session_id).first()
if not session:
logger.error(f'Session db item not found: {session_id}')
return
replay_storage = ReplayStorageHandler(session)
local_path, url = replay_storage.find_local()
if not local_path:
logger.error(f'Session replay not found, may be upload error: {local_path}')
return
abs_path = default_storage.path(local_path)
remote_path = session.get_relative_path_by_local_path(abs_path)
ok, err = server_replay_storage.upload(abs_path, remote_path)
if not ok:
logger.error(f'Session replay upload to external error: {err}')
return
try:
default_storage.delete(local_path)
except:
pass
return
@shared_task(
verbose_name=_('Run applet host deployment'),
activity_callback=lambda self, did, *args, **kwargs: ([did],),
description=_(
"""
When deploying from the remote application publisher details page, and the 'Deploy'
button is clicked, this task will be executed
"""
)
)
def run_applet_host_deployment(did, install_applets):
with tmp_to_builtin_org(system=1):
deployment = AppletHostDeployment.objects.get(id=did)
deployment.start(install_applets=install_applets)
@shared_task(
verbose_name=_('Install applet'),
activity_callback=lambda self, ids, applet_id, *args, **kwargs: (ids,),
description=_(
"""
When the 'Deploy' button is clicked in the 'Remote Application' section of the remote
application publisher details page, this task will be executed
"""
)
)
def run_applet_host_deployment_install_applet(ids, applet_id):
with tmp_to_builtin_org(system=1):
for did in ids:
deployment = AppletHostDeployment.objects.get(id=did)
deployment.install_applet(applet_id)
@shared_task(
verbose_name=_('Uninstall applet'),
activity_callback=lambda self, ids, applet_id, *args, **kwargs: (ids,),
description=_(
"""
When the 'Uninstall' button is clicked in the 'Remote Application' section of the
remote application publisher details page, this task will be executed
"""
)
)
def run_applet_host_deployment_uninstall_applet(ids, applet_id):
with tmp_to_builtin_org(system=1):
for did in ids:
deployment = AppletHostDeployment.objects.get(id=did)
deployment.uninstall_applet(applet_id)
@shared_task(
verbose_name=_('Generate applet host accounts'),
activity_callback=lambda self, host_id, *args, **kwargs: ([host_id],),
description=_(
"""
When a remote publishing server is created and an account needs to be created
automatically, this task will be executed
"""
)
)
def applet_host_generate_accounts(host_id):
applet_host = AppletHost.objects.filter(id=host_id).first()
if not applet_host:
return
with tmp_to_builtin_org(system=1):
applet_host.generate_accounts()
@shared_task(
verbose_name=_('Check command replay storage connectivity'),
description=_(
"""
Check every day at midnight whether the external storage for commands and recordings
is accessible. If it is not accessible, send a notification to the recipients specified
in 'System Settings - Notifications - Subscription - Storage - Connectivity'
"""
)
)
@register_as_period_task(crontab='0 0 * * *')
@tmp_to_root_org()
def check_command_replay_storage_connectivity():
errors = []
replays = ReplayStorage.objects.exclude(
type__in=[ReplayStorageType.server, ReplayStorageType.null]
)
commands = CommandStorage.objects.exclude(
type__in=[CommandStorageType.server, CommandStorageType.null]
)
for instance in chain(replays, commands):
msg = None
try:
is_valid = instance.is_valid()
except Exception as e:
is_valid = False
msg = _("Test failure: {}".format(str(e)))
if is_valid:
continue
errors.append({
'msg': msg or _("Test failure: Account invalid"),
'type': instance.get_type_display(),
'name': instance.name
})
if not errors:
return
StorageConnectivityMessage(errors).publish_async()