API: Add stats host filter for 'gt >' and 'lt <'

This provides the ability to search for hosts based on their stats.

For example:

- return hosts with no changes: /api/v1/hosts?changed__lt=1
- return hosts with failures: /api/v1/hosts?failed__gt=0
- return hosts with unreachable tasks: /api/v1/hosts?unreachable__gt=0

Change-Id: Iccd74c3e419fdd52b8305505335f57d599eec1ee
This commit is contained in:
David Moreau Simard
2020-07-22 18:41:11 -04:00
parent 96e26dc650
commit 2827d43ac4
3 changed files with 43 additions and 0 deletions

View File

@@ -126,6 +126,18 @@ class HostFilter(BaseFilter):
playbook = django_filters.NumberFilter(field_name="playbook__id", lookup_expr="exact")
name = django_filters.CharFilter(field_name="name", lookup_expr="icontains")
# For example: /api/v1/hosts/failed__gt=0 to return hosts with 1 failure or more
changed__gt = django_filters.NumberFilter(field_name="changed", lookup_expr="gt")
changed__lt = django_filters.NumberFilter(field_name="changed", lookup_expr="lt")
failed__gt = django_filters.NumberFilter(field_name="failed", lookup_expr="gt")
failed__lt = django_filters.NumberFilter(field_name="failed", lookup_expr="lt")
ok__gt = django_filters.NumberFilter(field_name="ok", lookup_expr="gt")
ok__lt = django_filters.NumberFilter(field_name="ok", lookup_expr="lt")
skipped__gt = django_filters.NumberFilter(field_name="skipped", lookup_expr="gt")
skipped__lt = django_filters.NumberFilter(field_name="skipped", lookup_expr="lt")
unreachable__gt = django_filters.NumberFilter(field_name="unreachable", lookup_expr="gt")
unreachable__lt = django_filters.NumberFilter(field_name="unreachable", lookup_expr="lt")
# fmt: off
order = django_filters.OrderingFilter(
fields=(

View File

@@ -101,6 +101,11 @@ class HostFactory(factory.DjangoModelFactory):
facts = utils.compressed_obj(HOST_FACTS)
name = "hostname"
playbook = factory.SubFactory(PlaybookFactory)
changed = 0
failed = 0
ok = 0
skipped = 0
unreachable = 0
class ResultFactory(factory.DjangoModelFactory):

View File

@@ -120,6 +120,32 @@ class HostTestCase(APITestCase):
self.assertEqual(1, len(request.data["results"]))
self.assertEqual(host.name, request.data["results"][0]["name"])
def test_get_hosts_by_stats(self):
# Create two hosts with different stats
first_host = factories.HostFactory(name="first_host", changed=2, failed=2, ok=2, skipped=2, unreachable=2)
second_host = factories.HostFactory(name="second_host", changed=0, failed=0, ok=0, skipped=0, unreachable=0)
# There must be two distinct hosts
request = self.client.get("/api/v1/hosts")
self.assertEqual(2, request.data["count"])
self.assertEqual(2, len(request.data["results"]))
statuses = ["changed", "failed", "ok", "skipped", "unreachable"]
# Searching for > should only return the first host
for status in statuses:
request = self.client.get("/api/v1/hosts?%s__gt=1" % status)
self.assertEqual(1, request.data["count"])
self.assertEqual(1, len(request.data["results"]))
self.assertEqual(first_host.id, request.data["results"][0]["id"])
# Searching for < should only return the second host
for status in statuses:
request = self.client.get("/api/v1/hosts?%s__lt=1" % status)
self.assertEqual(1, request.data["count"])
self.assertEqual(1, len(request.data["results"]))
self.assertEqual(second_host.id, request.data["results"][0]["id"])
def test_get_host_by_date(self):
host = factories.HostFactory()