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 committed Jul 15, 2020
1 parent 43739e6 commit 57b0ded
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 3 deletions.
38 changes: 38 additions & 0 deletions openwisp_monitoring/device/admin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
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
from django.utils.translation import gettext_lazy as _
from swapper import load_model

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

from ..monitoring.admin import MetricAdmin
from . import settings as app_settings
Expand All @@ -16,6 +22,36 @@
DeviceMonitoring = load_model('device_monitoring', 'DeviceMonitoring')
Chart = load_model('monitoring', 'Chart')
Device = load_model('config', 'Device')
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 @@ -56,6 +92,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,13 +1,18 @@
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')
User = get_user_model()
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)
1 change: 0 additions & 1 deletion openwisp_monitoring/device/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from paramiko.ssh_exception import NoValidConnectionsError
from swapper import load_model

from openwisp_controller.connection.models import Credentials, DeviceConnection
from openwisp_controller.connection.tests.base import CreateConnectionsMixin
from openwisp_utils.tests import catch_signal

Expand Down
1 change: 0 additions & 1 deletion openwisp_monitoring/monitoring/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@


class TestAdmin(TestMonitoringMixin, TestCase):

def _login_admin(self):
User = get_user_model()
u = User.objects.create_superuser('admin', 'admin', '[email protected]')
Expand Down

0 comments on commit 57b0ded

Please sign in to comment.