mirror of
https://github.com/ansible/awx.git
synced 2026-02-26 23:46:05 -03:30
[constructed-inventory] Backlink events to real hosts and summaries to both hosts (#13718)
* Backlink events to real hosts and summaries to both hosts * Prevent error when original host is deleted during job run * No duplicate entries, review suggestion from Rick * Change word tense in help text, dict style adjustments From code review Co-authored-by: Rick Elrod <rick@elrod.me> * Back out new variable for constructed host id --------- Co-authored-by: Rick Elrod <rick@elrod.me>
This commit is contained in:
committed by
Rick Elrod
parent
b88d9f4731
commit
3f5a4cb6f1
@@ -158,6 +158,7 @@ SUMMARIZABLE_FK_FIELDS = {
|
|||||||
'kind',
|
'kind',
|
||||||
),
|
),
|
||||||
'host': DEFAULT_SUMMARY_FIELDS,
|
'host': DEFAULT_SUMMARY_FIELDS,
|
||||||
|
'constructed_host': DEFAULT_SUMMARY_FIELDS,
|
||||||
'group': DEFAULT_SUMMARY_FIELDS,
|
'group': DEFAULT_SUMMARY_FIELDS,
|
||||||
'default_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
|
'default_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
|
||||||
'execution_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
|
'execution_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
|
||||||
@@ -1903,6 +1904,10 @@ class HostSerializer(BaseSerializerWithVariables):
|
|||||||
group_list = [{'id': g.id, 'name': g.name} for g in obj.groups.all().order_by('id')[:5]]
|
group_list = [{'id': g.id, 'name': g.name} for g in obj.groups.all().order_by('id')[:5]]
|
||||||
group_cnt = obj.groups.count()
|
group_cnt = obj.groups.count()
|
||||||
d.setdefault('groups', {'count': group_cnt, 'results': group_list})
|
d.setdefault('groups', {'count': group_cnt, 'results': group_list})
|
||||||
|
if obj.inventory.kind == 'constructed':
|
||||||
|
summaries_qs = obj.constructed_host_summaries
|
||||||
|
else:
|
||||||
|
summaries_qs = obj.job_host_summaries
|
||||||
d.setdefault(
|
d.setdefault(
|
||||||
'recent_jobs',
|
'recent_jobs',
|
||||||
[
|
[
|
||||||
@@ -1913,7 +1918,7 @@ class HostSerializer(BaseSerializerWithVariables):
|
|||||||
'status': j.job.status,
|
'status': j.job.status,
|
||||||
'finished': j.job.finished,
|
'finished': j.job.finished,
|
||||||
}
|
}
|
||||||
for j in obj.job_host_summaries.select_related('job__job_template').order_by('-created').defer('job__extra_vars', 'job__artifacts')[:5]
|
for j in summaries_qs.select_related('job__job_template').order_by('-created').defer('job__extra_vars', 'job__artifacts')[:5]
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
return d
|
return d
|
||||||
@@ -4140,6 +4145,7 @@ class JobHostSummarySerializer(BaseSerializer):
|
|||||||
'-description',
|
'-description',
|
||||||
'job',
|
'job',
|
||||||
'host',
|
'host',
|
||||||
|
'constructed_host',
|
||||||
'host_name',
|
'host_name',
|
||||||
'changed',
|
'changed',
|
||||||
'dark',
|
'dark',
|
||||||
|
|||||||
@@ -122,4 +122,17 @@ class Migration(migrations.Migration):
|
|||||||
help_text='This field is deprecated and will be removed in a future release. Regex where only matching hosts will be imported.',
|
help_text='This field is deprecated and will be removed in a future release. Regex where only matching hosts will be imported.',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='jobhostsummary',
|
||||||
|
name='constructed_host',
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default=None,
|
||||||
|
editable=False,
|
||||||
|
help_text='Only for jobs run against constructed inventories, this links to the host inside the constructed inventory.',
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name='constructed_host_summaries',
|
||||||
|
to='main.host',
|
||||||
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from collections import defaultdict
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import models, DatabaseError
|
from django.db import models, DatabaseError
|
||||||
|
from django.db.models.functions import Cast
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
from django.utils.text import Truncator
|
from django.utils.text import Truncator
|
||||||
from django.utils.timezone import utc, now
|
from django.utils.timezone import utc, now
|
||||||
@@ -538,23 +539,36 @@ class JobEvent(BasePlaybookEvent):
|
|||||||
|
|
||||||
from awx.main.models import Host, JobHostSummary # circular import
|
from awx.main.models import Host, JobHostSummary # circular import
|
||||||
|
|
||||||
all_hosts = Host.objects.filter(pk__in=self.host_map.values()).only('id', 'name')
|
if self.job.inventory.kind == 'constructed':
|
||||||
|
all_hosts = Host.objects.filter(id__in=self.job.inventory.hosts.values_list(Cast('instance_id', output_field=models.IntegerField()))).only(
|
||||||
|
'id', 'name'
|
||||||
|
)
|
||||||
|
constructed_host_map = self.host_map
|
||||||
|
host_map = {host.name: host.id for host in all_hosts}
|
||||||
|
else:
|
||||||
|
all_hosts = Host.objects.filter(pk__in=self.host_map.values()).only('id', 'name')
|
||||||
|
constructed_host_map = {}
|
||||||
|
host_map = self.host_map
|
||||||
|
|
||||||
existing_host_ids = set(h.id for h in all_hosts)
|
existing_host_ids = set(h.id for h in all_hosts)
|
||||||
|
|
||||||
summaries = dict()
|
summaries = dict()
|
||||||
updated_hosts_list = list()
|
updated_hosts_list = list()
|
||||||
for host in hostnames:
|
for host in hostnames:
|
||||||
updated_hosts_list.append(host.lower())
|
updated_hosts_list.append(host.lower())
|
||||||
host_id = self.host_map.get(host, None)
|
host_id = host_map.get(host)
|
||||||
if host_id not in existing_host_ids:
|
if host_id not in existing_host_ids:
|
||||||
host_id = None
|
host_id = None
|
||||||
|
constructed_host_id = constructed_host_map.get(host)
|
||||||
host_stats = {}
|
host_stats = {}
|
||||||
for stat in ('changed', 'dark', 'failures', 'ignored', 'ok', 'processed', 'rescued', 'skipped'):
|
for stat in ('changed', 'dark', 'failures', 'ignored', 'ok', 'processed', 'rescued', 'skipped'):
|
||||||
try:
|
try:
|
||||||
host_stats[stat] = self.event_data.get(stat, {}).get(host, 0)
|
host_stats[stat] = self.event_data.get(stat, {}).get(host, 0)
|
||||||
except AttributeError: # in case event_data[stat] isn't a dict.
|
except AttributeError: # in case event_data[stat] isn't a dict.
|
||||||
pass
|
pass
|
||||||
summary = JobHostSummary(created=now(), modified=now(), job_id=job.id, host_id=host_id, host_name=host, **host_stats)
|
summary = JobHostSummary(
|
||||||
|
created=now(), modified=now(), job_id=job.id, host_id=host_id, constructed_host_id=constructed_host_id, host_name=host, **host_stats
|
||||||
|
)
|
||||||
summary.failed = bool(summary.dark or summary.failures)
|
summary.failed = bool(summary.dark or summary.failures)
|
||||||
summaries[(host_id, host)] = summary
|
summaries[(host_id, host)] = summary
|
||||||
|
|
||||||
|
|||||||
@@ -569,12 +569,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
|||||||
default=None,
|
default=None,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
)
|
)
|
||||||
hosts = models.ManyToManyField(
|
hosts = models.ManyToManyField('Host', related_name='jobs', editable=False, through='JobHostSummary', through_fields=('job', 'host'))
|
||||||
'Host',
|
|
||||||
related_name='jobs',
|
|
||||||
editable=False,
|
|
||||||
through='JobHostSummary',
|
|
||||||
)
|
|
||||||
artifacts = JSONBlob(
|
artifacts = JSONBlob(
|
||||||
default=dict,
|
default=dict,
|
||||||
blank=True,
|
blank=True,
|
||||||
@@ -1059,6 +1054,15 @@ class JobHostSummary(CreatedModifiedModel):
|
|||||||
editable=False,
|
editable=False,
|
||||||
)
|
)
|
||||||
host = models.ForeignKey('Host', related_name='job_host_summaries', null=True, default=None, on_delete=models.SET_NULL, editable=False)
|
host = models.ForeignKey('Host', related_name='job_host_summaries', null=True, default=None, on_delete=models.SET_NULL, editable=False)
|
||||||
|
constructed_host = models.ForeignKey(
|
||||||
|
'Host',
|
||||||
|
related_name='constructed_host_summaries',
|
||||||
|
null=True,
|
||||||
|
default=None,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
editable=False,
|
||||||
|
help_text='Only for jobs run against constructed inventories, this links to the host inside the constructed inventory.',
|
||||||
|
)
|
||||||
|
|
||||||
host_name = models.CharField(
|
host_name = models.CharField(
|
||||||
max_length=1024,
|
max_length=1024,
|
||||||
|
|||||||
Reference in New Issue
Block a user