import agent_util
from os import path
from datetime import datetime
import json
import logging
import sys
import speedtest
class DEMPlugin(agent_util.Plugin):
textkey = "dem"
label = "Digital Experience"
log = logging.getLogger(__name__)
@classmethod
def get_metadata(self, config):
status = agent_util.SUPPORTED
msg = None
dem_configured = True
if config.get('enabled', 'false').lower() != 'true':
status = agent_util.UNSUPPORTED
msg = 'DEM not configured'
dem_configured = False
if dem_configured:
info = config.get('wifi_results')
if not info:
status = agent_util.UNSUPPORTED
msg = 'No wifi info found'
metadata = {
'dem.agrCtlRSSI' : {
'label' : 'Wifi signal strength',
'options' : None,
'status' : status,
'error_msg' : msg,
'unit' : 'dBm'
},
'dem.lastTxRate' : {
'label' : 'Last transmission rate',
'options' : None,
'status' : status,
'error_msg' : msg,
'unit' : 'mbps/s'
},
'dem.agrCtlNoise' : {
'label' : 'Noise',
'options' : None,
'status' : status,
'error_msg' : msg,
'unit' : 'dBm'
},
'dem.MCS' : {
'label' : 'MCS Index',
'options' : None,
'status' : status,
'error_msg' : msg,
'unit' : 'index'
},
'dem.downloadspeed' :{
'label' : 'Download Speed',
'options' : None,
'status' : agent_util.SUPPORTED,
'error_msg' : None,
'unit' : 'Mbit/s'
}
}
speedtest_upload = agent_util.SUPPORTED
speedtest_msg = None
if config.get('speedtest_mode', 'speedtest').lower() != 'iperf3' or not dem_configured:
speedtest_upload = agent_util.UNSUPPORTED
speedtest_msg = 'Upload speed not supported'
metadata['dem.uploadspeed'] ={
'label' : 'Upload Speed',
'options' : None,
'status' : speedtest_upload,
'error_msg' : speedtest_msg,
'unit' : 'Mbit/s'
}
battery_status = agent_util.SUPPORTED
battery_msg = None
if not dem_configured:
battery_status = agent_util.UNSUPPORTED
battery_msg = 'DEM not configured'
else:
battery_pct = self.get_battery_remaining()
if battery_pct is None:
battery_status = agent_util.UNSUPPORTED
battery_msg = 'Battery metric unavailable'
metadata['dem.battery_percent_remaining'] ={
'label' : 'Battery Percent Remaining',
'options' : None,
'status' : battery_status,
'error_msg' : battery_msg,
'unit' : 'percent'
}
return metadata
def check(self, textkey, data, config):
if config.get('enabled', 'false').lower() != 'true':
return None
if textkey in ('dem.downloadspeed', 'dem.uploadspeed'):
return self._measureNetworkSpeed(textkey, config)
if 'dem.battery_percent_remaining' == textkey:
return self.get_battery_remaining()
try:
info = config.get('wifi_results')
key = textkey[len('dem.'):]
metric_value = info.get(key, None)
if metric_value is None:
raise Exception("Missing key {}".format(key))
return float(metric_value)
except:
self.log.exception('Wifi metrics:')
return None
def _run_iperf3_test(self, textkey, config):
speedtest_bin = '/usr/local/FortiMonitor/agent/latest/bin/iperf3'
try:
start_url = config.get(
'iperf3_start_url',
None
)
from iperf3 import Iperf3Runner
runner = Iperf3Runner(iperf3_bin=speedtest_bin, start_url=start_url)
result = None
if 'dem.downloadspeed' == textkey:
result = runner.get_download_speed()
elif 'dem.uploadspeed' == textkey:
result = runner.get_upload_speed()
return float(result / (1000 * 1000))
except:
self.log.exception('Iperf3 error:')
def _measureNetworkSpeed(self, textkey, config):
if config.get('speedtest_mode', 'speedtest').lower() == 'iperf3':
return self._run_iperf3_test(textkey, config)
if 'dem.uploadspeed' == textkey:
raise Exception('Service does not support upload speed')
try:
start = datetime.now()
s = speedtest.Speedtest()
s.get_best_server()
k = s.download()
rv = float(k / 1000000)
self.log.info('Download speed {} in {:.2f}'.format(
rv,
(datetime.now() - start).total_seconds()
))
return rv
except Exception as ste:
self.log.error('Speedtest exception: {}'.format(ste))
return None
@classmethod
def get_battery_remaining(self):
try:
sp = agent_util.which('system_profiler')
power_tk = 'SPPowerDataType'
from subprocess import Popen, PIPE
d = Popen([sp, '-json', power_tk], stdout=PIPE)
data = json.loads(d.stdout.read())
for d in data[power_tk]:
if d.get('_name', '') == 'spbattery_information':
mv = d.get('sppower_battery_charge_info', None)
if not mv:
return None
return float(mv['sppower_battery_state_of_charge'])
return None
except Exception as e:
self.log.error('Battery exception: {}'.format(e))
return None