Skip to content

Commit

Permalink
[feature] Add Monitoring Checks section in Device Admin #53
Browse files Browse the repository at this point in the history
Closes #53
  • Loading branch information
nepython authored Jul 7, 2020
1 parent 6ad4bf2 commit 0900380
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 1 deletion.
38 changes: 38 additions & 0 deletions openwisp_monitoring/device/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import uuid

from django.contrib import admin
from django.contrib.contenttypes.admin import GenericStackedInline
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
from django.contrib.contenttypes.models import ContentType
from django.db.models import TextField
from django.forms import Textarea
from django.urls import reverse
from django.utils.html import format_html
from django.utils.safestring import mark_safe
Expand All @@ -9,13 +14,44 @@

from openwisp_controller.config.admin import DeviceAdmin as BaseDeviceAdmin
from openwisp_controller.config.models import Device
from openwisp_utils.admin import TimeReadonlyAdminMixin

from ..monitoring.admin import MetricAdmin
from . import settings as app_settings

DeviceData = load_model('device_monitoring', 'DeviceData')
DeviceMonitoring = load_model('device_monitoring', 'DeviceMonitoring')
Chart = load_model('monitoring', 'Chart')
Check = load_model('check', 'Check')


class CheckInlineFormSet(BaseGenericInlineFormSet):
def full_clean(self):
for form in self.forms:
obj = form.instance
if not obj.content_type or not obj.object_id:
setattr(
form.instance,
self.ct_field.get_attname(),
ContentType.objects.get_for_model(self.instance).pk,
)
setattr(form.instance, self.ct_fk_field.get_attname(), self.instance.pk)
super().full_clean()


class CheckInline(TimeReadonlyAdminMixin, GenericStackedInline):
model = Check
extra = 0
formset = CheckInlineFormSet
fieldsets = [
(
None,
{'fields': ('name', 'check', 'active', 'params', 'created', 'modified',)},
),
]
formfield_overrides = {
TextField: {'widget': Textarea(attrs={'rows': 3, 'cols': 40})},
}


class DeviceAdmin(BaseDeviceAdmin):
Expand Down Expand Up @@ -54,6 +90,8 @@ def get_form(self, request, obj=None, **kwargs):
return super().get_form(request, obj, **kwargs)


DeviceAdmin.inlines.append(CheckInline)

DeviceAdmin.Media.js += MetricAdmin.Media.js + ('monitoring/js/percircle.js',)
DeviceAdmin.Media.css['all'] += (
'monitoring/css/percircle.css',
Expand Down
45 changes: 44 additions & 1 deletion openwisp_monitoring/device/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.forms import generic_inlineformset_factory
from django.urls import reverse
from swapper import load_model
from django.utils.timezone import now
from swapper import get_model_name, load_model

from ...check.settings import CHECK_CLASSES
from ..admin import CheckInline, CheckInlineFormSet
from . import DeviceMonitoringTestCase

Chart = load_model('monitoring', 'Chart')
Metric = load_model('monitoring', 'Metric')
DeviceData = load_model('device_monitoring', 'DeviceData')
Check = load_model('check', 'Check')


class TestAdmin(DeviceMonitoringTestCase):
Expand All @@ -21,14 +26,20 @@ def _login_admin(self):

def test_device_admin(self):
dd = self.create_test_adata()
check = Check.objects.create(
name='Ping check', check=CHECK_CLASSES[0][0], content_object=dd, params={},
)
url = reverse('admin:config_device_change', args=[dd.pk])
self._login_admin()
r = self.client.get(url)
self.assertContains(r, '<h2>Status</h2>')
self.assertContains(r, '<h2>Charts</h2>')
self.assertContains(r, '<h2>Checks</h2>')
self.assertContains(r, 'Storage')
self.assertContains(r, 'CPU')
self.assertContains(r, 'RAM status')
self.assertContains(r, check.name)
self.assertContains(r, check.params)

def test_no_device_data(self):
d = self._create_device(organization=self._create_org())
Expand Down Expand Up @@ -64,3 +75,35 @@ def test_uuid_bug(self):
self._login_admin()
r = self.client.get(url)
self.assertContains(r, '<h2>Status</h2>')

def test_check_inline_formset(self):
d = self._create_device(organization=self._create_org())
check_inline_formset = generic_inlineformset_factory(
model=Check, form=CheckInline.form, formset=CheckInlineFormSet
)
# model_name changes if swapped
model_name = get_model_name('check', 'Check').lower().replace('.', '-')
ct = f'{model_name}-content_type-object_id'
data = {
f'{ct}-TOTAL_FORMS': '1',
f'{ct}-INITIAL_FORMS': '0',
f'{ct}-MAX_NUM_FORMS': '0',
f'{ct}-0-name': 'Ping Check',
f'{ct}-0-check': CHECK_CLASSES[0][0],
f'{ct}-0-params': '{}',
f'{ct}-0-active': True,
f'{ct}-0-created': now(),
f'{ct}-0-modified': now(),
}
formset = check_inline_formset(data)
formset.instance = d
self.assertTrue(formset.is_valid())
self.assertEqual(formset.errors, [{}])
self.assertEqual(formset.non_form_errors(), [])
form = formset.forms[0]
form.cleaned_data = data
form.save(commit=True)
self.assertEqual(Check.objects.count(), 1)
c = Check.objects.first()
self.assertEqual(c.name, 'Ping Check')
self.assertEqual(c.content_object, d)

0 comments on commit 0900380

Please sign in to comment.