Skip to content

Commit

Permalink
[feature] Added check for WiFi Clients
Browse files Browse the repository at this point in the history
  • Loading branch information
pandafy committed Dec 12, 2024
1 parent 13285e3 commit 958bd6d
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 16 deletions.
9 changes: 9 additions & 0 deletions openwisp_monitoring/check/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,12 @@ def _connect_signals(self):
sender=load_model('config', 'Device'),
dispatch_uid='auto_iperf3_check',
)

if app_settings.AUTO_WIFI_CLIENT_CHECK:
from .base.models import auto_wifi_client_check_receiver

post_save.connect(
auto_wifi_client_check_receiver,
sender=load_model('config', 'Device'),
dispatch_uid='auto_wifi_clients_check',
)
19 changes: 19 additions & 0 deletions openwisp_monitoring/check/base/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
auto_create_config_check,
auto_create_iperf3_check,
auto_create_ping,
auto_create_wifi_client_check,
)
from openwisp_utils.base import TimeStampedEditableModel

Expand Down Expand Up @@ -160,3 +161,21 @@ def auto_iperf3_check_receiver(sender, instance, created, **kwargs):
object_id=str(instance.pk),
)
)


def auto_wifi_client_check_receiver(sender, instance, created, **kwargs):
"""Implements OPENWISP_MONITORING_AUTO_WIFI_CLIENT_CHECK.
The creation step is executed in the background.
"""
# we need to skip this otherwise this task will be executed
# every time the configuration is requested via checksum
if not created:
return
transaction_on_commit(
lambda: auto_create_wifi_client_check.delay(
model=sender.__name__.lower(),
app_label=sender._meta.app_label,
object_id=str(instance.pk),
)
)
1 change: 1 addition & 0 deletions openwisp_monitoring/check/classes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .config_applied import ConfigApplied # noqa
from .iperf3 import Iperf3 # noqa
from .ping import Ping # noqa
from .wifi_client import WifiClient # noqa
52 changes: 52 additions & 0 deletions openwisp_monitoring/check/classes/wifi_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from django.utils import timezone
from swapper import load_model

from ...db import timeseries_db
from .. import settings as app_settings
from .base import BaseCheck

AlertSettings = load_model('monitoring', 'AlertSettings')


class WifiClient(BaseCheck):
def check(self, store=True):
values = timeseries_db.read(
key='wifi_clients',
fields='COUNT(DISTINCT(clients))',
tags={
'content_type': self.related_object._meta.label_lower,
'object_id': str(self.related_object.pk),
},
since=int(
(
timezone.localtime()
- timezone.timedelta(
minutes=app_settings.WIFI_CLIENT_CHECK_INTERVAL
)
).timestamp()
),
)
if not values:
result = 0
else:
result = values[0]['count']
if store:
self.store_result(result)
return result

def store_result(self, result):
max_metric = self._get_metric('max_wifi_clients')
max_metric.write(result)
min_metric = self._get_metric('min_wifi_clients')
min_metric.write(result)

def _get_metric(self, configuration):
metric, created = self._get_or_create_metric(configuration=configuration)
if created:
self._create_alert_setting(metric)
return metric

def _create_alert_setting(self, metric):
alert_s = AlertSettings(metric=metric)
alert_s.full_clean()
alert_s.save()
8 changes: 8 additions & 0 deletions openwisp_monitoring/check/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
('openwisp_monitoring.check.classes.Ping', 'Ping'),
('openwisp_monitoring.check.classes.ConfigApplied', 'Configuration Applied'),
('openwisp_monitoring.check.classes.Iperf3', 'Iperf3'),
('openwisp_monitoring.check.classes.WifiClient', 'Wifi Client'),
),
)
AUTO_PING = get_settings_value('AUTO_PING', True)
Expand All @@ -19,6 +20,13 @@
getattr(settings, 'OPENWISP_CONTROLLER_MANAGEMENT_IP_ONLY', True),
)
PING_CHECK_CONFIG = get_settings_value('PING_CHECK_CONFIG', {})
AUTO_WIFI_CLIENT_CHECK = get_settings_value('AUTO_WIFI_CLIENT_CHECK', False)
WIFI_CLIENT_CHECK_SNOOZE_SCHEDULE = get_settings_value(
'WIFI_CLIENT_CHECK_SNOOZE_SCHEDULE', []
)
WIFI_CLIENT_CHECK_INTERVAL = int(
get_settings_value('WIFI_CLIENT_CHECK_INTERVAL', 5)
) # in minutes
AUTO_IPERF3 = get_settings_value('AUTO_IPERF3', False)
IPERF3_CHECK_CONFIG = get_settings_value('IPERF3_CHECK_CONFIG', {})
IPERF3_CHECK_LOCK_EXPIRE = get_settings_value(
Expand Down
52 changes: 51 additions & 1 deletion openwisp_monitoring/check/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.utils import timezone
from swapper import load_model

from openwisp_utils.tasks import OpenwispCeleryTask

from .settings import CHECKS_LIST
from .settings import CHECKS_LIST, WIFI_CLIENT_CHECK_SNOOZE_SCHEDULE

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -52,6 +53,26 @@ def run_checks(checks=None):
perform_check.delay(check['id'])


@shared_task(time_limit=2 * 60 * 60)
def run_wifi_client_checks():
if WIFI_CLIENT_CHECK_SNOOZE_SCHEDULE:
today = timezone.localdate()
# Format as MM-DD
today_month_day = today.strftime("%m-%d")
for start_date, end_date in WIFI_CLIENT_CHECK_SNOOZE_SCHEDULE:
# Check if the date range wraps around the new year
if start_date <= end_date:
# Normal range within the same year
if start_date <= today_month_day <= end_date:
return
else:
# Wrap-around range spanning across years
if today_month_day >= start_date or today_month_day <= end_date:
return

run_checks(checks=['openwisp_monitoring.check.classes.WifiClient'])


@shared_task(time_limit=30 * 60)
def perform_check(uuid):
"""Performs check with specified uuid.
Expand Down Expand Up @@ -150,3 +171,32 @@ def auto_create_iperf3_check(
)
check.full_clean()
check.save()


@shared_task(base=OpenwispCeleryTask)
def auto_create_wifi_client_check(
model, app_label, object_id, check_model=None, content_type_model=None
):
"""Implements the auto creation of the wifi_clients check.
Called by the
openwisp_monitoring.check.models.auto_wifi_client_check_receiver.
"""
Check = check_model or get_check_model()
check_path = 'openwisp_monitoring.check.classes.WifiClient'
has_check = Check.objects.filter(
object_id=object_id, content_type__model='device', check_type=check_path
).exists()
# create new check only if necessary
if has_check:
return
content_type_model = content_type_model or ContentType
ct = content_type_model.objects.get_by_natural_key(app_label=app_label, model=model)
check = Check(
name='Wifi Client',
check_type=check_path,
content_type=ct,
object_id=object_id,
)
check.full_clean()
check.save()
27 changes: 27 additions & 0 deletions openwisp_monitoring/check/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
from django.db.models.signals import post_save
from swapper import load_model

from ..base.models import auto_wifi_client_check_receiver

Device = load_model('config', 'Device')

_FPING_REACHABLE = (
'',
bytes(
Expand All @@ -10,3 +17,23 @@
'',
bytes('192.168.255.255 : xmt/rcv/%loss = 3/0/100%', encoding='utf8'),
)


class AutoWifiClientCheck(object):
@classmethod
def setUpClass(cls):
super().setUpClass()
post_save.connect(
auto_wifi_client_check_receiver,
sender=Device,
dispatch_uid='auto_wifi_clients_check',
)

@classmethod
def tearDownClass(cls):
super().tearDownClass()
post_save.disconnect(
auto_wifi_client_check_receiver,
sender=Device,
dispatch_uid='auto_wifi_clients_check',
)
Loading

0 comments on commit 958bd6d

Please sign in to comment.