mirror of https://github.com/jumpserver/jumpserver
feat: 增加跳过的主机统计, 增加每台主机执行情况api
parent
66bdc375df
commit
c14b97419d
|
@ -10,7 +10,7 @@ __all__ = ['JMSInventory']
|
|||
|
||||
class JMSInventory:
|
||||
def __init__(self, assets, account_policy='privileged_first',
|
||||
account_prefer='root,Administrator', host_callback=None):
|
||||
account_prefer='root,Administrator', host_callback=None, unique_host_name=False):
|
||||
"""
|
||||
:param assets:
|
||||
:param account_prefer: account username name if not set use account_policy
|
||||
|
@ -20,6 +20,8 @@ class JMSInventory:
|
|||
self.account_prefer = account_prefer
|
||||
self.account_policy = account_policy
|
||||
self.host_callback = host_callback
|
||||
self.exclude_hosts = {}
|
||||
self.unique_host_name = unique_host_name
|
||||
|
||||
@staticmethod
|
||||
def clean_assets(assets):
|
||||
|
@ -112,6 +114,9 @@ class JMSInventory:
|
|||
'secret': account.secret, 'secret_type': account.secret_type
|
||||
} if account else None
|
||||
}
|
||||
if self.unique_host_name:
|
||||
host['name'] += '({})'.format(asset.id)
|
||||
|
||||
if host['jms_account'] and asset.platform.type == 'oracle':
|
||||
host['jms_account']['mode'] = 'sysdba' if account.privileged else None
|
||||
|
||||
|
@ -194,7 +199,7 @@ class JMSInventory:
|
|||
print(_("Skip hosts below:"))
|
||||
for i, host in enumerate(exclude_hosts, start=1):
|
||||
print("{}: [{}] \t{}".format(i, host['name'], host['error']))
|
||||
|
||||
self.exclude_hosts[host['name']] = host['error']
|
||||
hosts = list(filter(lambda x: not x.get('error'), hosts))
|
||||
data = {'all': {'hosts': {}}}
|
||||
for host in hosts:
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from rest_framework.views import APIView
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.response import Response
|
||||
|
||||
from ops.api.base import SelfBulkModelViewSet
|
||||
from ops.models import Job, JobExecution
|
||||
from ops.serializers.job import JobSerializer, JobExecutionSerializer
|
||||
|
||||
__all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView']
|
||||
__all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', 'JobAssetDetail']
|
||||
|
||||
from ops.tasks import run_ops_job_execution
|
||||
from ops.variables import JMS_JOB_VARIABLE_HELP
|
||||
|
@ -73,3 +73,14 @@ class JobRunVariableHelpAPIView(APIView):
|
|||
|
||||
def get(self, request, **kwargs):
|
||||
return Response(data=JMS_JOB_VARIABLE_HELP)
|
||||
|
||||
|
||||
class JobAssetDetail(APIView):
|
||||
rbac_perms = ()
|
||||
permission_classes = ()
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
execution_id = request.query_params.get('execution_id')
|
||||
if execution_id:
|
||||
execution = get_object_or_404(JobExecution, id=execution_id)
|
||||
return Response(data=execution.assent_result_detail)
|
||||
|
|
|
@ -88,7 +88,7 @@ class Job(JMSBaseModel, PeriodTaskModelMixin):
|
|||
|
||||
@property
|
||||
def inventory(self):
|
||||
return JMSInventory(self.assets.all(), self.runas_policy, self.runas)
|
||||
return JMSInventory(self.assets.all(), self.runas_policy, self.runas, unique_host_name=True)
|
||||
|
||||
def create_execution(self):
|
||||
return self.executions.create()
|
||||
|
@ -110,6 +110,55 @@ class JobExecution(JMSBaseModel):
|
|||
date_start = models.DateTimeField(null=True, verbose_name=_('Date start'), db_index=True)
|
||||
date_finished = models.DateTimeField(null=True, verbose_name=_("Date finished"))
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
if self.is_finished and not self.summary.get('error', None):
|
||||
return {
|
||||
"ok": len(self.summary['ok']),
|
||||
"failed": len(self.summary['failures']) + len(self.summary['dark']),
|
||||
"excludes": len(self.summary['excludes']),
|
||||
"total": self.job.assets.count()
|
||||
}
|
||||
|
||||
@property
|
||||
def assent_result_detail(self):
|
||||
if self.is_finished and not self.summary.get('error', None):
|
||||
result = {
|
||||
"summary": self.count,
|
||||
"detail": [],
|
||||
}
|
||||
for asset in self.job.assets.all():
|
||||
asset_detail = {
|
||||
"name": asset.name,
|
||||
"status": "ok",
|
||||
"tasks": [],
|
||||
}
|
||||
host_name = "{}({})".format(asset.name, asset.id)
|
||||
if self.summary["excludes"].get(host_name, None):
|
||||
asset_detail.update({"status": "excludes"})
|
||||
result["detail"].append(asset_detail)
|
||||
break
|
||||
if self.result["dark"].get(host_name, None):
|
||||
asset_detail.update({"status": "failed"})
|
||||
for key, task in self.result["dark"][host_name].items():
|
||||
task_detail = {"name": key,
|
||||
"output": "{}{}".format(task.get("stdout", ""), task.get("stderr", ""))}
|
||||
asset_detail["tasks"].append(task_detail)
|
||||
if self.result["failures"].get(host_name, None):
|
||||
asset_detail.update({"status": "failed"})
|
||||
for key, task in self.result["failures"][host_name].items():
|
||||
task_detail = {"name": key,
|
||||
"output": "{}{}".format(task.get("stdout", ""), task.get("stderr", ""))}
|
||||
asset_detail["tasks"].append(task_detail)
|
||||
|
||||
if self.result["ok"].get(host_name, None):
|
||||
for key, task in self.result["ok"][host_name].items():
|
||||
task_detail = {"name": key,
|
||||
"output": "{}{}".format(task.get("stdout", ""), task.get("stderr", ""))}
|
||||
asset_detail["tasks"].append(task_detail)
|
||||
result["detail"].append(asset_detail)
|
||||
return result
|
||||
|
||||
@property
|
||||
def job_type(self):
|
||||
return self.job.type
|
||||
|
@ -124,6 +173,11 @@ class JobExecution(JMSBaseModel):
|
|||
def get_runner(self):
|
||||
inv = self.job.inventory
|
||||
inv.write_to_file(self.inventory_path)
|
||||
if len(inv.exclude_hosts) > 0:
|
||||
self.summary['excludes'] = inv.exclude_hosts
|
||||
self.result['excludes'] = inv.exclude_hosts
|
||||
self.save()
|
||||
|
||||
if isinstance(self.parameters, str):
|
||||
extra_vars = json.loads(self.parameters)
|
||||
else:
|
||||
|
@ -191,7 +245,7 @@ class JobExecution(JMSBaseModel):
|
|||
def set_error(self, error):
|
||||
this = self.__class__.objects.get(id=self.id) # 重新获取一次,避免数据库超时连接超时
|
||||
this.status = 'failed'
|
||||
this.summary['error'] = str(error)
|
||||
this.summary.update({'error': str(error)})
|
||||
this.finish_task()
|
||||
|
||||
def set_result(self, cb):
|
||||
|
@ -200,8 +254,8 @@ class JobExecution(JMSBaseModel):
|
|||
}
|
||||
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.summary.update(cb.summary)
|
||||
this.result.update(cb.result)
|
||||
this.finish_task()
|
||||
|
||||
def finish_task(self):
|
||||
|
|
|
@ -28,10 +28,13 @@ class JobSerializer(serializers.ModelSerializer, PeriodTaskSerializerMixin):
|
|||
class JobExecutionSerializer(serializers.ModelSerializer):
|
||||
creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
|
||||
job_type = serializers.ReadOnlyField(label=_("Job type"))
|
||||
count = serializers.ReadOnlyField(label=_("Count"))
|
||||
|
||||
class Meta:
|
||||
model = JobExecution
|
||||
read_only_fields = ["id", "task_id", "timedelta", "time_cost", 'is_finished', 'date_start', 'date_created',
|
||||
read_only_fields = ["id", "task_id", "timedelta", "count", "time_cost", 'is_finished', 'date_start',
|
||||
'date_finished',
|
||||
'date_created',
|
||||
'is_success', 'task_id', 'short_id', 'job_type', 'creator']
|
||||
fields = read_only_fields + [
|
||||
"job", "parameters"
|
||||
|
|
|
@ -24,7 +24,7 @@ router.register(r'task-executions', api.CeleryTaskExecutionViewSet, 'task-execut
|
|||
|
||||
urlpatterns = [
|
||||
path('variables/help/', api.JobRunVariableHelpAPIView.as_view(), name='variable-help'),
|
||||
|
||||
path('job-execution/asset-detail/', api.JobAssetDetail.as_view(), name='asset-detail'),
|
||||
path('ansible/job-execution/<uuid:pk>/log/', api.AnsibleTaskLogApi.as_view(), name='job-execution-log'),
|
||||
|
||||
path('celery/task/<uuid:name>/task-execution/<uuid:pk>/log/', api.CeleryTaskExecutionLogApi.as_view(),
|
||||
|
|
Loading…
Reference in New Issue