Skip to content

Commit

Permalink
Revert "previous commit failed to account for return code"
Browse files Browse the repository at this point in the history
This reverts commit 2a0fcdc.
  • Loading branch information
clutch2sft committed Oct 27, 2023
1 parent 6c3ae7d commit e4625e2
Showing 1 changed file with 187 additions and 187 deletions.
374 changes: 187 additions & 187 deletions openwisp_monitoring/check/classes/ping.py
Original file line number Diff line number Diff line change
@@ -1,187 +1,187 @@
import subprocess

from django.core.exceptions import ValidationError
from jsonschema import draft7_format_checker, validate
from jsonschema.exceptions import ValidationError as SchemaError
from swapper import load_model

from openwisp_utils.utils import deep_merge_dicts

from ... import settings as monitoring_settings
from .. import settings as app_settings
from ..exceptions import OperationalError
from .base import BaseCheck

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

DEFAULT_PING_CHECK_CONFIG = {
'count': {
'type': 'integer',
'default': 5,
'minimum': 2,
# chosen to avoid slowing down the queue
'maximum': 20,
},
'interval': {
'type': 'integer',
'default': 25,
'minimum': 10,
# chosen to avoid slowing down the queue
'maximum': 1000,
},
'bytes': {'type': 'integer', 'default': 56, 'minimum': 12, 'maximum': 65508},
'timeout': {
'type': 'integer',
'default': 800,
'minimum': 5,
# arbitrary chosen to avoid slowing down the queue
'maximum': 1500,
},
}


def get_ping_schema():
schema = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'type': 'object',
'additionalProperties': False,
}
schema['properties'] = deep_merge_dicts(
DEFAULT_PING_CHECK_CONFIG, app_settings.PING_CHECK_CONFIG
)
return schema


class Ping(BaseCheck):
schema = get_ping_schema()

def validate_params(self):
try:
validate(self.params, self.schema, format_checker=draft7_format_checker)
except SchemaError as e:
message = 'Invalid param'
path = '/'.join(e.path)
if path:
message = '{0} in "{1}"'.format(message, path)
message = '{0}: {1}'.format(message, e.message)
raise ValidationError({'params': message}) from e

def check(self, store=True):
count = self._get_param('count')
interval = self._get_param('interval')
bytes_ = self._get_param('bytes')
timeout = self._get_param('timeout')
ip = self._get_ip()
# if the device has no available IP
if not ip:
monitoring = self.related_object.monitoring
# device not known yet, ignore
if monitoring.status == 'unknown':
return
# device is known, simulate down
result = {'reachable': 0, 'loss': 100.0}
if store:
self.store_result(result)
return result
command = [
'fping',
'-e', # show elapsed (round-trip) time of packets
'-c', str(count), # count of pings to send to each target
'-p', str(interval), # interval between sending pings (in ms)
'-b', str(bytes_), # amount of ping data to send
'-t', str(timeout), # individual target initial timeout (in ms)
'-q',
ip
]
stdout, stderr, returncode = self._command(command)
# Check the return code and raise an exception if it's not zero
if returncode not in [0, 1]:
message = f'fping command failed with return code {returncode}:\n\n{stderr.decode("utf8")}'
raise OperationalError(message)
# fpings shows statistics on stderr
output = stderr.decode('utf8')
try:
parts = output.split('=')
if len(parts) > 2:
min, avg, max = parts[-1].strip().split('/')
i = -2
else:
i = -1
sent, received, loss = parts[i].strip().split(',')[0].split('/')
loss = float(loss.strip('%'))
except (IndexError, ValueError) as e:
message = 'Unrecognized fping output:\n\n{0}'.format(output)
raise OperationalError(message) from e
result = {'reachable': int(loss < 100), 'loss': loss}
if result['reachable']:
result.update(
{'rtt_min': float(min), 'rtt_avg': float(avg), 'rtt_max': float(max)}
)
if store:
self.store_result(result)
return result

def store_result(self, result):
"""
store result in the DB
"""
metric = self._get_metric()
copied = result.copy()
reachable = copied.pop('reachable')
metric.write(reachable, extra_values=copied)

def _get_param(self, param):
"""
Gets specified param or its default value according to the schema
"""
return self.params.get(param, self.schema['properties'][param]['default'])

def _get_ip(self):
"""
Figures out ip to use or fails raising OperationalError
"""
device = self.related_object
ip = device.management_ip
if not ip and not app_settings.MANAGEMENT_IP_ONLY:
ip = device.last_ip
return ip

def _command(self, command):
"""
Executes command (easier to mock)
"""
try:
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
return stdout, stderr, p.returncode
except Exception as e:
return None, str(e), 99

def _get_metric(self):
"""
Gets or creates metric
"""
metric, created = self._get_or_create_metric()
if created:
self._create_alert_settings(metric)
self._create_charts(metric)
return metric

def _create_alert_settings(self, metric):
alert_settings = AlertSettings(metric=metric)
alert_settings.full_clean()
alert_settings.save()

def _create_charts(self, metric):
"""
Creates device charts if necessary
"""
charts = ['uptime', 'packet_loss', 'rtt']
for chart in charts:
if chart not in monitoring_settings.AUTO_CHARTS:
continue
chart = Chart(metric=metric, configuration=chart)
chart.full_clean()
chart.save()
import subprocess

from django.core.exceptions import ValidationError
from jsonschema import draft7_format_checker, validate
from jsonschema.exceptions import ValidationError as SchemaError
from swapper import load_model

from openwisp_utils.utils import deep_merge_dicts

from ... import settings as monitoring_settings
from .. import settings as app_settings
from ..exceptions import OperationalError
from .base import BaseCheck

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

DEFAULT_PING_CHECK_CONFIG = {
'count': {
'type': 'integer',
'default': 5,
'minimum': 2,
# chosen to avoid slowing down the queue
'maximum': 20,
},
'interval': {
'type': 'integer',
'default': 25,
'minimum': 10,
# chosen to avoid slowing down the queue
'maximum': 1000,
},
'bytes': {'type': 'integer', 'default': 56, 'minimum': 12, 'maximum': 65508},
'timeout': {
'type': 'integer',
'default': 800,
'minimum': 5,
# arbitrary chosen to avoid slowing down the queue
'maximum': 1500,
},
}


def get_ping_schema():
schema = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'type': 'object',
'additionalProperties': False,
}
schema['properties'] = deep_merge_dicts(
DEFAULT_PING_CHECK_CONFIG, app_settings.PING_CHECK_CONFIG
)
return schema


class Ping(BaseCheck):
schema = get_ping_schema()

def validate_params(self):
try:
validate(self.params, self.schema, format_checker=draft7_format_checker)
except SchemaError as e:
message = 'Invalid param'
path = '/'.join(e.path)
if path:
message = '{0} in "{1}"'.format(message, path)
message = '{0}: {1}'.format(message, e.message)
raise ValidationError({'params': message}) from e

def check(self, store=True):
count = self._get_param('count')
interval = self._get_param('interval')
bytes_ = self._get_param('bytes')
timeout = self._get_param('timeout')
ip = self._get_ip()
# if the device has no available IP
if not ip:
monitoring = self.related_object.monitoring
# device not known yet, ignore
if monitoring.status == 'unknown':
return
# device is known, simulate down
result = {'reachable': 0, 'loss': 100.0}
if store:
self.store_result(result)
return result
command = [
'fping',
'-e', # show elapsed (round-trip) time of packets
'-c', str(count), # count of pings to send to each target
'-p', str(interval), # interval between sending pings (in ms)
'-b', str(bytes_), # amount of ping data to send
'-t', str(timeout), # individual target initial timeout (in ms)
'-q',
ip
]
stdout, stderr, returncode = self._command(command)
# Check the return code and raise an exception if it's not zero
if returncode != 0:
message = f'fping command failed with return code {returncode}:\n\n{stderr.decode("utf8")}'
raise OperationalError(message)
# fpings shows statistics on stderr
output = stderr.decode('utf8')
try:
parts = output.split('=')
if len(parts) > 2:
min, avg, max = parts[-1].strip().split('/')
i = -2
else:
i = -1
sent, received, loss = parts[i].strip().split(',')[0].split('/')
loss = float(loss.strip('%'))
except (IndexError, ValueError) as e:
message = 'Unrecognized fping output:\n\n{0}'.format(output)
raise OperationalError(message) from e
result = {'reachable': int(loss < 100), 'loss': loss}
if result['reachable']:
result.update(
{'rtt_min': float(min), 'rtt_avg': float(avg), 'rtt_max': float(max)}
)
if store:
self.store_result(result)
return result

def store_result(self, result):
"""
store result in the DB
"""
metric = self._get_metric()
copied = result.copy()
reachable = copied.pop('reachable')
metric.write(reachable, extra_values=copied)

def _get_param(self, param):
"""
Gets specified param or its default value according to the schema
"""
return self.params.get(param, self.schema['properties'][param]['default'])

def _get_ip(self):
"""
Figures out ip to use or fails raising OperationalError
"""
device = self.related_object
ip = device.management_ip
if not ip and not app_settings.MANAGEMENT_IP_ONLY:
ip = device.last_ip
return ip

def _command(self, command):
"""
Executes command (easier to mock)
"""
try:
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
return stdout, stderr, p.returncode
except Exception as e:
return None, str(e), 99

def _get_metric(self):
"""
Gets or creates metric
"""
metric, created = self._get_or_create_metric()
if created:
self._create_alert_settings(metric)
self._create_charts(metric)
return metric

def _create_alert_settings(self, metric):
alert_settings = AlertSettings(metric=metric)
alert_settings.full_clean()
alert_settings.save()

def _create_charts(self, metric):
"""
Creates device charts if necessary
"""
charts = ['uptime', 'packet_loss', 'rtt']
for chart in charts:
if chart not in monitoring_settings.AUTO_CHARTS:
continue
chart = Chart(metric=metric, configuration=chart)
chart.full_clean()
chart.save()

0 comments on commit e4625e2

Please sign in to comment.