Commit a0312f68 authored by Neil Williams's avatar Neil Williams Committed by Remi Duraffort
Browse files

Fix visibility in pending_jobs_by_device_type

Exclude non-public TestJobs from the count and
exclude hidden device-types from the listing unless the
user authenticates and is a superuser.
Exclude device-types where all devices are retired.
Exclude device-types if display is set to false.

Change-Id: I0f885ac03d2dc6c45aeb1f92ada608d01278b3d3
parent 8d7afd66
......@@ -20,7 +20,8 @@ from lava_scheduler_app.views import (
)
from lava_scheduler_app.dbutils import (
device_type_summary,
testjob_submission
testjob_submission,
active_device_types,
)
from lava_scheduler_app.schema import (
validate_submission,
......@@ -711,7 +712,7 @@ class SchedulerAPI(ExposedAPI):
403, "Permission denied for user to put %s into online mode." % hostname
)
def pending_jobs_by_device_type(self):
def pending_jobs_by_device_type(self, all=False):
"""
Name
----
......@@ -720,10 +721,12 @@ class SchedulerAPI(ExposedAPI):
Description
-----------
Get number of pending jobs in each device type.
Private test jobs and hidden device types are
excluded, except for authenticated superusers.
Arguments
---------
None
`all`: boolean - include retired devices and undisplayed device-types in the listing.
Return value
------------
......@@ -736,18 +739,30 @@ class SchedulerAPI(ExposedAPI):
pending_jobs_by_device = {}
jobs_res = TestJob.objects.filter(state=TestJob.STATE_SUBMITTED) \
.values_list('requested_device_type_id')\
.annotate(pending_jobs=(Count('id')))
jobs_res = TestJob.objects.filter(state=TestJob.STATE_SUBMITTED)
jobs_res = jobs_res.exclude(requested_device_type_id__isnull=True)
if not self.user or not self.user.is_superuser:
jobs_res = jobs_res.filter(is_public=True)
jobs_res = jobs_res.values_list('requested_device_type_id')
jobs_res = jobs_res.annotate(pending_jobs=(Count('id')))
jobs = {}
jobs_hash = dict(jobs_res)
for job in jobs_hash:
if job:
jobs[job] = jobs_hash[job]
jobs[job] = jobs_hash[job]
pending_jobs_by_device.update(jobs)
# Get rest of the devices and put number of pending jobs as 0.
device_types = DeviceType.objects.values_list('name', flat=True)
if all:
device_types = DeviceType.objects.values_list('name', flat=True)
else:
device_types = active_device_types().values_list('name', flat=True)
if not self.user or not self.user.is_superuser:
device_types.filter(owners_only=False)
for device_type in device_types:
if device_type not in pending_jobs_by_device:
pending_jobs_by_device[device_type] = 0
......
......@@ -14,6 +14,7 @@ from nose.tools import nottest
from django.db.models import Q, Case, When, IntegerField, Sum
from lava_scheduler_app.models import (
Device,
DeviceType,
TestJob,
validate_job,
validate_device,
......@@ -141,6 +142,34 @@ def device_type_summary(visible=None):
return devices
def active_device_types():
"""
Filter the available device types to exclude
all device-types where ALL devices are in health RETIRED
without excluding device-types where only SOME devices are retired.
oneliner:
{device.device_type for device in Device.objects.filter(~Q(health=Device.HEALTH_RETIRED))}.union( \
{dt for dt in {device.device_type for device in Device.objects.filter(health=Device.HEALTH_RETIRED)} \
if list(Device.objects.filter(Q(device_type=dt), ~Q(health=Device.HEALTH_RETIRED)))})
Returns a RestrictedQuerySet of DeviceType objects.
"""
not_retired_devices = Device.objects.filter(
Q(device_type__display=True), ~Q(health=Device.HEALTH_RETIRED)).select_related('device_type')
retired_devices = Device.objects.filter(
Q(device_type__display=True), health=Device.HEALTH_RETIRED).select_related('device_type')
not_all_retired = set() # set of device_type.names where some devices of that device_type are retired but *not* all.
for device in retired_devices:
# identify device_types which can be added back because not all devices of that type are retired.
if list(Device.objects.filter(Q(device_type=device.device_type), ~Q(health=Device.HEALTH_RETIRED))):
not_all_retired.add(device.device_type.name)
# join the two sets as a union.
candidates = {device.device_type.name for device in not_retired_devices}.union(not_all_retired)
device_types = DeviceType.objects.filter(name__in=candidates)
return device_types
def load_devicetype_template(device_type_name, raw=False):
"""
Loads the bare device-type template as a python dictionary object for
......
......@@ -14,6 +14,7 @@ from lava_scheduler_app.models import (
from lava_scheduler_app.dbutils import (
load_devicetype_template,
invalid_template,
active_device_types,
)
from django_testscenarios.ubertest import TestCase
from django.contrib.auth.models import User
......@@ -97,6 +98,11 @@ class DeviceTypeTest(TestCaseWithFactory):
self.basedir = (os.path.abspath(os.path.join(
os.path.dirname(__file__))))
def tearDown(self):
super().tearDown()
Device.objects.all().delete()
DeviceType.objects.all().delete()
"""
Test loading of device-type information
"""
......@@ -205,3 +211,55 @@ class DeviceTypeTest(TestCaseWithFactory):
self.assertEqual('juno', device.get_extends())
self.assertFalse(bool(load_devicetype_template(device.device_type.name)))
self.assertFalse(invalid_template(device.device_type))
def test_active_device_types(self):
name = "beaglebone-black"
dt = DeviceType(name=name)
dt.save()
dt.refresh_from_db()
device = Device(device_type=dt, hostname='bbb-01', health=Device.HEALTH_GOOD)
device.save()
device = Device(device_type=dt, hostname='bbb-02', health=Device.HEALTH_RETIRED)
device.save()
name = "juno-r2"
dt = DeviceType(name=name)
dt.save()
dt.refresh_from_db()
device = Device(device_type=dt, hostname='juno-r2-01', health=Device.HEALTH_RETIRED)
device.save()
name = "juno"
dt = DeviceType(name=name)
dt.display = False
dt.save()
dt.refresh_from_db()
dt.refresh_from_db()
device = Device(device_type=dt, hostname='juno-01', health=Device.HEALTH_UNKNOWN)
device.save()
name = "qemu"
dt = DeviceType(name=name)
dt.save()
dt.refresh_from_db()
device = Device(device_type=dt, hostname='qemu-01', health=Device.HEALTH_GOOD)
device.save()
self.assertEqual(
{'bbb-01', 'bbb-02', 'juno-r2-01', 'qemu-01', 'juno-01'},
set(Device.objects.all().values_list('hostname', flat=True))
)
self.assertEqual(
{'beaglebone-black', 'juno', 'juno-r2', 'qemu'},
set(DeviceType.objects.values_list('name', flat=True))
)
# exclude juno-r2 because all devices of that device-type are retired.
# exclude juno because the device_type is set to not be displayed.
# include beaglebone-black because not all devices of that type are retired.
# include qemu because none of the devices of that type are retired.
self.assertEqual(
{'beaglebone-black', 'qemu'},
set(active_device_types().values_list('name', flat=True))
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment