diff --git a/src/ralph/admin/filters.py b/src/ralph/admin/filters.py index 86c8930249..2538a72d5b 100644 --- a/src/ralph/admin/filters.py +++ b/src/ralph/admin/filters.py @@ -215,6 +215,24 @@ def choices(self, cl): } +class VulnerabilitesByPatchDeadline(DateListFilter): + + def queryset(self, request, queryset): + from ralph.security.models import SecurityScan + if any(self.value()): + filters = {} + date_start, date_end = self.value() + if date_start: + filters['vulnerabilities__patch_deadline__gte'] = date_start + if date_end: + filters['vulnerabilities__patch_deadline__lte'] = date_end + base_objects_ids = SecurityScan.objects.filter( + **filters + ).values_list('base_object_id', flat=True) + queryset = queryset.filter(id__in=base_objects_ids) + return queryset + + class NumberListFilter(DateListFilter): """Renders filter form with decimal field.""" diff --git a/src/ralph/data_center/admin.py b/src/ralph/data_center/admin.py index 6f25e3ff97..3e84df02bf 100644 --- a/src/ralph/data_center/admin.py +++ b/src/ralph/data_center/admin.py @@ -19,7 +19,8 @@ MacAddressFilter, RelatedAutocompleteFieldListFilter, TagsListFilter, - TreeRelatedAutocompleteFilterWithDescendants + TreeRelatedAutocompleteFilterWithDescendants, + VulnerabilitesByPatchDeadline ) from ralph.admin.helpers import generate_html_link from ralph.admin.m2m import RalphTabularM2MInline @@ -350,6 +351,7 @@ class DataCenterAssetAdmin( 'rack__server_room__data_center', 'position', 'property_of', LiquidatedStatusFilter, IPFilter, TagsListFilter, 'fibrechannelcard_set__wwn', + ('securityscan__vulnerabilities__patch_deadline', VulnerabilitesByPatchDeadline), # noqa ( 'securityscan__vulnerabilities', filters.RelatedAutocompleteFieldListFilter @@ -562,6 +564,7 @@ class DCHostAdmin(ScanStatusInChangeListMixin, RalphAdmin): ('content_type', DCHostTypeListFilter), MacAddressFilter, IPFilter, + ('securityscan__vulnerabilities__patch_deadline', VulnerabilitesByPatchDeadline), # noqa ( 'securityscan__vulnerabilities', filters.RelatedAutocompleteFieldListFilter diff --git a/src/ralph/data_center/tests/test_view.py b/src/ralph/data_center/tests/test_view.py index 537cc45bff..6387ceacfc 100644 --- a/src/ralph/data_center/tests/test_view.py +++ b/src/ralph/data_center/tests/test_view.py @@ -1,3 +1,6 @@ +from datetime import datetime, timedelta +from urllib.parse import urlencode + from django.core.urlresolvers import reverse from django.test import TestCase @@ -6,7 +9,10 @@ DataCenterAssetFullFactory ) from ralph.security.models import ScanStatus -from ralph.security.tests.factories import SecurityScanFactory +from ralph.security.tests.factories import ( + SecurityScanFactory, + VulnerabilityFactory +) from ralph.tests.mixins import ClientMixin from ralph.virtual.tests.factories import ( CloudHostFullFactory, @@ -152,3 +158,67 @@ def test_listing_show_failed_icon_when_scan_error(self): reverse('admin:data_center_dchost_changelist'), ) self.assertContains(result, "Scan failed") + + +class DCHostFilterByPatchDeadline(ClientMixin, TestCase): + def setUp(self): + self.login_as_user() + self.asset_no_vuls = DataCenterAssetFullFactory( + rack__name='Rack #1', + rack__server_room__name='SR1', + rack__server_room__data_center__name='DC1', + ) + self.scan_no_vuls = SecurityScanFactory( + base_object=self.asset_no_vuls.baseobject_ptr, vulnerabilities=[], + ) + + self.today = datetime.now() + self.yesterday = self.today + timedelta(days=-1) + self.tomorrow = self.today + timedelta(days=1) + + self.asset_with_today_vul = DataCenterAssetFullFactory( + rack__name='Rack #1', + rack__server_room__name='SR1', + rack__server_room__data_center__name='DC1', + ) + self.scan_with_vuls2 = SecurityScanFactory( + base_object=self.asset_with_today_vul.baseobject_ptr, + vulnerabilities=[ + VulnerabilityFactory( + patch_deadline=self.today, + ) + ] + ) + + self.asset_vuls2 = DataCenterAssetFullFactory( + rack__name='Rack #1', + rack__server_room__name='SR1', + rack__server_room__data_center__name='DC1', + ) + self.scan_with_vuls2 = SecurityScanFactory( + base_object=self.asset_vuls2.baseobject_ptr, + vulnerabilities=[ + VulnerabilityFactory( + patch_deadline=self.today + timedelta(days=30) + ) + ] + ) + + def test_patch_deadline_filters_hosts(self): + FORMAT = '%Y-%m-%d' + url = ( + '?'.join([ + reverse('admin:data_center_dchost_changelist',), + urlencode({ + 'securityscan__vulnerabilities__patch_deadline__start': self.yesterday.strftime(FORMAT), # noqa + 'securityscan__vulnerabilities__patch_deadline__end': self.tomorrow.strftime(FORMAT), # noqa + }) + ]) + ) + + response = self.client.get(url, follow=True) + + self.assertEqual( + int(response.context_data['object_id']), + self.asset_with_today_vul.id, + ) diff --git a/src/ralph/virtual/admin.py b/src/ralph/virtual/admin.py index 0732b4aa69..9d94891582 100644 --- a/src/ralph/virtual/admin.py +++ b/src/ralph/virtual/admin.py @@ -11,7 +11,8 @@ MacAddressFilter, RelatedAutocompleteFieldListFilter, TagsListFilter, - TreeRelatedAutocompleteFilterWithDescendants + TreeRelatedAutocompleteFilterWithDescendants, + VulnerabilitesByPatchDeadline ) from ralph.assets.models.components import Ethernet from ralph.assets.views import ComponentsAdminView, RalphDetailViewAdmin @@ -98,6 +99,7 @@ class VirtualServerAdmin( BaseObjectHostnameFilter, 'sn', 'service_env', IPFilter, 'parent', TagsListFilter, MacAddressFilter, ('configuration_path__module', TreeRelatedAutocompleteFilterWithDescendants), # noqa + ('securityscan__vulnerabilities__patch_deadline', VulnerabilitesByPatchDeadline), # noqa ( 'securityscan__vulnerabilities', RelatedAutocompleteFieldListFilter ), @@ -215,6 +217,7 @@ class CloudHostAdmin( list_filter = [ BaseObjectHostnameFilter, 'cloudprovider', 'service_env', 'cloudflavor', TagsListFilter, + ('securityscan__vulnerabilities__patch_deadline', VulnerabilitesByPatchDeadline), # noqa ( 'securityscan__vulnerabilities', RelatedAutocompleteFieldListFilter ),