mirror of https://github.com/jumpserver/jumpserver
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.
136 lines
4.6 KiB
136 lines
4.6 KiB
import logging |
|
import os.path |
|
import uuid |
|
|
|
from django.conf import settings |
|
from django.db import models |
|
from django.utils import timezone |
|
from django.utils.translation import gettext_lazy as _ |
|
|
|
from orgs.mixins.models import JMSOrgBaseModel |
|
from ..ansible.inventory import JMSInventory |
|
from ..mixin import PeriodTaskModelMixin |
|
|
|
|
|
class BaseAnsibleJob(PeriodTaskModelMixin, JMSOrgBaseModel): |
|
owner = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True) |
|
assets = models.ManyToManyField('assets.Asset', verbose_name=_("Assets")) |
|
account = models.CharField(max_length=128, default='root', verbose_name=_('Account')) |
|
account_policy = models.CharField(max_length=128, default='root', verbose_name=_('Account policy')) |
|
last_execution = models.ForeignKey('BaseAnsibleExecution', verbose_name=_("Last execution"), |
|
on_delete=models.SET_NULL, null=True) |
|
date_last_run = models.DateTimeField(null=True, verbose_name=_('Date last run')) |
|
|
|
class Meta: |
|
abstract = True |
|
|
|
@property |
|
def inventory(self): |
|
inv = JMSInventory(self.assets.all(), self.account, self.account_policy) |
|
return inv |
|
|
|
def get_register_task(self): |
|
raise NotImplementedError |
|
|
|
def to_json(self): |
|
raise NotImplementedError |
|
|
|
def create_execution(self): |
|
execution = self.executions.create() |
|
return execution |
|
|
|
def run(self, *args, **kwargs): |
|
execution = self.create_execution() |
|
return execution.start() |
|
|
|
|
|
class BaseAnsibleExecution(models.Model): |
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4) |
|
status = models.CharField(max_length=16, verbose_name=_('Status'), default='running') |
|
task = models.ForeignKey(BaseAnsibleJob, on_delete=models.CASCADE, related_name='executions', null=True) |
|
result = models.JSONField(blank=True, null=True, verbose_name=_('Result')) |
|
summary = models.JSONField(default=dict, verbose_name=_('Summary')) |
|
creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True) |
|
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created')) |
|
date_start = models.DateTimeField(null=True, verbose_name=_('Date start'), db_index=True) |
|
date_finished = models.DateTimeField(null=True, verbose_name=_("Date finished")) |
|
|
|
class Meta: |
|
abstract = True |
|
ordering = ["-date_start"] |
|
|
|
def __str__(self): |
|
return str(self.id) |
|
|
|
@property |
|
def private_dir(self): |
|
uniq = self.date_created.strftime('%Y%m%d_%H%M%S') + '_' + self.short_id |
|
return os.path.join(settings.ANSIBLE_DIR, self.task.name, uniq) |
|
|
|
@property |
|
def inventory_path(self): |
|
return os.path.join(self.private_dir, 'inventory', 'hosts') |
|
|
|
def get_runner(self): |
|
raise NotImplementedError |
|
|
|
def finish_task(self): |
|
self.date_finished = timezone.now() |
|
self.save(update_fields=['result', 'status', 'summary', 'date_finished']) |
|
self.update_task() |
|
|
|
def set_error(self, error): |
|
this = self.__class__.objects.get(id=self.id) # 重新获取一次,避免数据库超时连接超时 |
|
this.status = 'failed' |
|
this.summary['error'] = str(error) |
|
this.finish_task() |
|
|
|
def set_result(self, cb): |
|
status_mapper = { |
|
'successful': 'success', |
|
} |
|
this = self.__class__.objects.get(id=self.id) |
|
this.status = status_mapper.get(cb.status, cb.status) |
|
this.summary = cb.summary |
|
this.result = cb.result |
|
this.finish_task() |
|
print("Finished") |
|
|
|
def update_task(self): |
|
self.task.last_execution = self |
|
self.task.date_last_run = timezone.now() |
|
self.task.save(update_fields=['last_execution', 'date_last_run']) |
|
|
|
def start(self, **kwargs): |
|
runner = self.get_runner() |
|
try: |
|
cb = runner.run(**kwargs) |
|
self.set_result(cb) |
|
return cb |
|
except Exception as e: |
|
logging.error(e, exc_info=True) |
|
self.set_error(e) |
|
|
|
@property |
|
def is_finished(self): |
|
return self.status in ['success', 'failed'] |
|
|
|
@property |
|
def is_success(self): |
|
return self.status == 'success' |
|
|
|
@property |
|
def short_id(self): |
|
return str(self.id).split('-')[-1] |
|
|
|
@property |
|
def timedelta(self): |
|
if self.date_start and self.date_finished: |
|
return self.date_finished - self.date_start |
|
return None |
|
|
|
@property |
|
def time_cost(self): |
|
if self.date_finished and self.date_start: |
|
return (self.date_finished - self.date_start).total_seconds() |
|
return None
|
|
|