diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b683f9d..ff99d533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Add Unicore UM980/UM982 support (rtcm3 mode). #351 - Detect Gnss receiver firmware version during receiver detection. #428 - GUI -> Logs: Non-zipped files can be convert to rinex. #348 + - GUI -> Settings: Display network informations. ### Changed - Faster Septentrio Mosaic-X5 detection ### Fixed diff --git a/web_app/network_infos.py b/web_app/network_infos.py new file mode 100644 index 00000000..c2840da8 --- /dev/null +++ b/web_app/network_infos.py @@ -0,0 +1,105 @@ +#!/usr/bin/python +""" Module to get up network interfaces with their ip address and connection name + These informations are then displayed inside the RTKBase web GUI. +""" + +import logging +import psutil +import nmcli +import argparse + +logging.basicConfig(format='%(levelname)s: %(message)s') +log = logging.getLogger(__name__) +log.setLevel('ERROR') + +nmcli.disable_use_sudo() + + +def get_conn_name(device): + """ + Get the connection name for the device + (e.g. Wired Connection 1) + + Parameter: + device(str): the network device name + Return: + str: connection name + """ + try: + device_infos = nmcli.device.show(device) + return device_infos["GENERAL.CONNECTION"] + except nmcli.NotExistException: + log.debug("No connection name for {}".format(device)) + return None + +def get_up_if(): + """ + Get the up network interface + + Return: + list: up interfaces + """ + #filtering available interface + if_stats = psutil.net_if_stats() + if_stats.pop('lo') + log.debug(if_stats) + if_stats = {k:v for (k,v) in if_stats.items() if if_stats[k].isup} # keep only up interface + if_stats = {k:v for (k,v) in if_stats.items() if if_stats[k].speed > 0 or 'pointopoint' in if_stats[k].flags} # keep only interface with speed > 0 + if_stats = {k:v for (k,v) in if_stats.items() if not k.startswith('docker')} # remove docker interface + return if_stats + +def get_interfaces_infos(): + """ + Get all up network interfaces with their ip v4/v6 addresses + and the connection name. + It returns a list of dict + e.g. [{'device': 'end0', 'ipv4': ['192.168.1.135'], 'ipv6': [], 'conn_name': 'Wired connection 1'}] + + Return: + list: all up network interfaces as dict + """ + up_interface = get_up_if() + log.debug("up interfaces: {}".format(up_interface)) + #then filter psutil.net_if_addrs() + if_addrs = psutil.net_if_addrs() + up_interface = {k:if_addrs[k] for (k,v) in up_interface.items() if if_addrs.get(k)} + #then filter to keep only AF_INET and AF_INET6 + for k,v in up_interface.items(): + up_interface[k] = [ x for x in v if x.family.name == 'AF_INET' or x.family.name == 'AF_INET6'] + #then filter ipv6 link local address + for k,v in up_interface.items(): + up_interface[k] = [ x for x in v if not x.address.startswith('fe80')] + + interfaces_infos = [] + for k,v in up_interface.items(): + device_info = {"device" : k} + ipv4 = [] + ipv6 = [] + for part in v: + if part.family.name == 'AF_INET6': + ipv6.append(part.address) + elif part.family.name == 'AF_INET': + ipv4.append(part.address) + log.debug("{} : {} : {}".format(k, part.family.name, part.address)) + device_info["ipv4"] = ipv4 if len(ipv4) > 0 else None + device_info["ipv6"] = ipv6 if len(ipv6) > 0 else None + conn_name = get_conn_name(k) + if conn_name: + device_info["conn_name"] = conn_name + interfaces_infos.append(device_info) + return interfaces_infos + +def arg_parse(): + """ Parse the command line you use to launch the script """ + + parser= argparse.ArgumentParser(prog='network_infos', description="Module to get up network interfaces with their ip address and connection name") + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument("-d", "--debug", action='store_true') + args = parser.parse_args() + return args + +if __name__ == "__main__": + args = arg_parse() + if args.debug: + log.setLevel('DEBUG') + print(get_interfaces_infos()) diff --git a/web_app/server.py b/web_app/server.py index 383c45f3..7eb91410 100755 --- a/web_app/server.py +++ b/web_app/server.py @@ -45,6 +45,7 @@ from RTKLIB import RTKLIB from ServiceController import ServiceController from RTKBaseConfigManager import RTKBaseConfigManager +import network_infos #print("Installing all required packages") #provisioner.provision_reach() @@ -180,6 +181,7 @@ def manager(): socketio.emit("services status", json.dumps(services_status), namespace="/test") #print("service status", services_status) + interfaces_infos = network_infos.get_interfaces_infos() volume_usage = get_volume_usage() sys_infos = {"cpu_temp" : cpu_temp, "max_cpu_temp" : max_cpu_temp, @@ -187,7 +189,8 @@ def manager(): "volume_free" : round(volume_usage.free / 10E8, 2), "volume_used" : round(volume_usage.used / 10E8, 2), "volume_total" : round(volume_usage.total / 10E8, 2), - "volume_percent_used" : volume_usage.percent} + "volume_percent_used" : volume_usage.percent, + "network_infos" : interfaces_infos} socketio.emit("sys_informations", json.dumps(sys_infos), namespace="/test") if rtk.sleep_count > rtkcv_standby_delay and rtk.state != "inactive" or \ diff --git a/web_app/static/settings.js b/web_app/static/settings.js index d27ca24c..27739b67 100644 --- a/web_app/static/settings.js +++ b/web_app/static/settings.js @@ -593,7 +593,31 @@ $(document).ready(function () { } else { volumeSpaceElt.style.color = "#212529"; } + + var networkElt = document.getElementById("network_infos"); + networkElt.innerHTML = createNetworkInterfacesList(sysInfos["network_infos"]); }) + + function createNetworkInterfacesList(interfaces) { + let html = '