1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 08:21:15 +03:00

Merge pull request #2065 from jakemcdermott/2058

add host_status, play, and task counts to job details
This commit is contained in:
Jake McDermott 2018-06-07 12:59:11 -04:00 committed by GitHub
commit b1f36572c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 2 deletions

View File

@ -9,7 +9,7 @@ import operator
import re
import six
import urllib
from collections import OrderedDict
from collections import defaultdict, OrderedDict
from datetime import timedelta
# OAuth2
@ -3144,6 +3144,48 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
return summary_fields
class JobDetailSerializer(JobSerializer):
host_status_counts = serializers.SerializerMethodField(
help_text=_('A count of hosts uniquely assigned to each status.'),
)
playbook_counts = serializers.SerializerMethodField(
help_text=_('A count of all plays and tasks for the job run.'),
)
class Meta:
model = Job
fields = ('*', 'host_status_counts', 'playbook_counts',)
def get_playbook_counts(self, obj):
task_count = obj.job_events.filter(event='playbook_on_task_start').count()
play_count = obj.job_events.filter(event='playbook_on_play_start').count()
data = {'play_count': play_count, 'task_count': task_count}
return data
def get_host_status_counts(self, obj):
try:
event_data = obj.job_events.only('event_data').get(event='playbook_on_stats').event_data
except JobEvent.DoesNotExist:
event_data = {}
host_status = {}
host_status_keys = ['skipped', 'ok', 'changed', 'failures', 'dark']
for key in host_status_keys:
for host in event_data.get(key, {}):
host_status[host] = key
host_status_counts = defaultdict(lambda: 0)
for value in host_status.values():
host_status_counts[value] += 1
return host_status_counts
class JobCancelSerializer(BaseSerializer):
can_cancel = serializers.BooleanField(read_only=True)

View File

@ -4080,7 +4080,7 @@ class JobDetail(UnifiedJobDeletionMixin, RetrieveUpdateDestroyAPIView):
model = Job
metadata_class = JobTypeMetadata
serializer_class = JobSerializer
serializer_class = JobDetailSerializer
def update(self, request, *args, **kwargs):
obj = self.get_object()

View File

@ -1,4 +1,5 @@
# Python
from collections import namedtuple
import pytest
import mock
import json
@ -7,6 +8,7 @@ from six.moves import xrange
# AWX
from awx.api.serializers import (
JobDetailSerializer,
JobSerializer,
JobOptionsSerializer,
)
@ -14,6 +16,7 @@ from awx.api.serializers import (
from awx.main.models import (
Label,
Job,
JobEvent,
)
@ -53,6 +56,7 @@ def jobs(mocker):
@mock.patch('awx.api.serializers.UnifiedJobTemplateSerializer.get_related', lambda x,y: {})
@mock.patch('awx.api.serializers.JobOptionsSerializer.get_related', lambda x,y: {})
class TestJobSerializerGetRelated():
@pytest.mark.parametrize("related_resource_name", [
'job_events',
'relaunch',
@ -76,6 +80,7 @@ class TestJobSerializerGetRelated():
@mock.patch('awx.api.serializers.BaseSerializer.to_representation', lambda self,obj: {
'extra_vars': obj.extra_vars})
class TestJobSerializerSubstitution():
def test_survey_password_hide(self, mocker):
job = mocker.MagicMock(**{
'display_extra_vars.return_value': '{\"secret_key\": \"$encrypted$\"}',
@ -90,6 +95,7 @@ class TestJobSerializerSubstitution():
@mock.patch('awx.api.serializers.BaseSerializer.get_summary_fields', lambda x,y: {})
class TestJobOptionsSerializerGetSummaryFields():
def test__summary_field_labels_10_max(self, mocker, job_template, labels):
job_template.labels.all = mocker.MagicMock(**{'return_value': labels})
@ -101,3 +107,45 @@ class TestJobOptionsSerializerGetSummaryFields():
def test_labels_exists(self, test_get_summary_fields, job_template):
test_get_summary_fields(JobOptionsSerializer, job_template, 'labels')
class TestJobDetailSerializerGetHostStatusCountFields(object):
def test_hosts_are_counted_once(self, job, mocker):
mock_event = JobEvent(**{
'event': 'playbook_on_stats',
'event_data': {
'skipped': {
'localhost': 2,
'fiz': 1,
},
'ok': {
'localhost': 1,
'foo': 2,
},
'changed': {
'localhost': 1,
'bar': 3,
},
'dark': {
'localhost': 2,
'fiz': 2,
}
}
})
mock_qs = namedtuple('mock_qs', ['get'])(mocker.MagicMock(return_value=mock_event))
job.job_events.only = mocker.MagicMock(return_value=mock_qs)
serializer = JobDetailSerializer()
host_status_counts = serializer.get_host_status_counts(job)
assert host_status_counts == {'ok': 1, 'changed': 1, 'dark': 2}
def test_host_status_counts_is_empty_dict_without_stats_event(self, job, mocker):
job.job_events = JobEvent.objects.none()
serializer = JobDetailSerializer()
host_status_counts = serializer.get_host_status_counts(job)
assert host_status_counts == {}