From 0333bf2cbeac3483679256f0d184d8e0c11f1233 Mon Sep 17 00:00:00 2001 From: Stefal Date: Sun, 22 Dec 2024 21:10:31 +0100 Subject: [PATCH 1/6] WIP Show network info on Web UI --- web_app/network_infos.py | 58 +++++++++++++++++++++++++++++++++ web_app/server.py | 5 ++- web_app/static/settings.js | 24 ++++++++++++++ web_app/templates/settings.html | 9 +++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 web_app/network_infos.py diff --git a/web_app/network_infos.py b/web_app/network_infos.py new file mode 100644 index 00000000..0772cfc9 --- /dev/null +++ b/web_app/network_infos.py @@ -0,0 +1,58 @@ +#!/usr/bin/python + +import psutil +import nmcli +from dataclasses import dataclass + +nmcli.disable_use_sudo() + +def get_up_if(): + #filtering available interface + if_stats = psutil.net_if_stats() + if_stats.pop('lo') + 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} # 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_conn_name(device): + try: + device_infos = nmcli.device.show(device) + return device_infos["GENERAL.CONNECTION"] + except NotExistException: + return None + +def get_interfaces_infos(): + + #print(get_up_if()) + up_interface = get_up_if() + #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[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(): + #print('key: ', k) + #print('value: ', v) + 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) + #print("{} : {} : {}".format(k, part.family.name, part.address)) + device_info["ipv4"] = ipv4 + device_info["ipv6"] = ipv6 + conn_name = get_conn_name(k) + if conn_name: + device_info["conn_name"] = conn_name + interfaces_infos.append(device_info) + return interfaces_infos \ No newline at end of file diff --git a/web_app/server.py b/web_app/server.py index 6837fdb0..a3049536 100755 --- a/web_app/server.py +++ b/web_app/server.py @@ -44,6 +44,7 @@ from RTKLIB import RTKLIB from ServiceController import ServiceController from RTKBaseConfigManager import RTKBaseConfigManager +import network_infos #print("Installing all required packages") #provisioner.provision_reach() @@ -179,6 +180,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, @@ -186,7 +188,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 = '
' + interfaces.forEach(interface => { + html += `
${interface.device}`; + if (interface.conn_name) { + html += ` (${interface.conn_name})`; + } + html += '
'; + html += '
    '; + if (interface.ipv4) { + html += `
  • IPv4: ${interface.ipv4.join(' - ')}
  • `; + } + if (interface.ipv6) { + html += `
  • IPv6: ${interface.ipv6.join(' - ')}
  • `; + } + html += '
'; + }); + html += '
'; + return html; + } //source: https://stackoverflow.com/a/34270811 /** * Translates seconds into human readable format of seconds, minutes, hours, days, and years diff --git a/web_app/templates/settings.html b/web_app/templates/settings.html index c8f257b0..3ce8914a 100644 --- a/web_app/templates/settings.html +++ b/web_app/templates/settings.html @@ -586,6 +586,15 @@

System Settings:

+
+
+ Network: +
+
+ +
+
+
Settings: From dcefab2ef4a239dc45bd2a3166df03d2b378c750 Mon Sep 17 00:00:00 2001 From: Stefal Date: Mon, 23 Dec 2024 22:20:41 +0100 Subject: [PATCH 2/6] Add doctring --- web_app/network_infos.py | 59 +++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/web_app/network_infos.py b/web_app/network_infos.py index 0772cfc9..f8fb4be3 100644 --- a/web_app/network_infos.py +++ b/web_app/network_infos.py @@ -1,12 +1,43 @@ #!/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 -from dataclasses import dataclass + +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') @@ -15,17 +46,18 @@ def get_up_if(): if_stats = {k:v for (k,v) in if_stats.items() if not k.startswith('docker')} # remove docker interface return if_stats -def get_conn_name(device): - try: - device_infos = nmcli.device.show(device) - return device_infos["GENERAL.CONNECTION"] - except NotExistException: - return None - 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'}] - #print(get_up_if()) + 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[k]} @@ -38,8 +70,6 @@ def get_interfaces_infos(): interfaces_infos = [] for k,v in up_interface.items(): - #print('key: ', k) - #print('value: ', v) device_info = {"device" : k} ipv4 = [] ipv6 = [] @@ -48,11 +78,14 @@ def get_interfaces_infos(): ipv6.append(part.address) elif part.family.name == 'AF_INET': ipv4.append(part.address) - #print("{} : {} : {}".format(k, part.family.name, part.address)) + log.debug("{} : {} : {}".format(k, part.family.name, part.address)) device_info["ipv4"] = ipv4 device_info["ipv6"] = ipv6 conn_name = get_conn_name(k) if conn_name: device_info["conn_name"] = conn_name interfaces_infos.append(device_info) - return interfaces_infos \ No newline at end of file + return interfaces_infos + +if __name__ == "__main__": + print(get_interfaces_infos()) From 240f5733670da0bb3dce00c2c50a4daa03ee4eb8 Mon Sep 17 00:00:00 2001 From: Stefal Date: Mon, 23 Dec 2024 22:33:07 +0100 Subject: [PATCH 3/6] return None if no ip address exists --- web_app/network_infos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web_app/network_infos.py b/web_app/network_infos.py index f8fb4be3..a871773e 100644 --- a/web_app/network_infos.py +++ b/web_app/network_infos.py @@ -79,8 +79,8 @@ def get_interfaces_infos(): elif part.family.name == 'AF_INET': ipv4.append(part.address) log.debug("{} : {} : {}".format(k, part.family.name, part.address)) - device_info["ipv4"] = ipv4 - device_info["ipv6"] = ipv6 + 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 From b68685815efd218f86980804a74d1f435ea825a4 Mon Sep 17 00:00:00 2001 From: Stefal Date: Mon, 23 Dec 2024 22:39:00 +0100 Subject: [PATCH 4/6] add --debug argument --- web_app/network_infos.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/web_app/network_infos.py b/web_app/network_infos.py index a871773e..67c4f87c 100644 --- a/web_app/network_infos.py +++ b/web_app/network_infos.py @@ -6,6 +6,7 @@ import logging import psutil import nmcli +import argparse logging.basicConfig(format='%(levelname)s: %(message)s') log = logging.getLogger(__name__) @@ -87,5 +88,17 @@ def get_interfaces_infos(): 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()) From 7944fd18581c7d93713cc2f8242b4441768e2686 Mon Sep 17 00:00:00 2001 From: Stefal Date: Mon, 23 Dec 2024 23:09:19 +0100 Subject: [PATCH 5/6] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba43204a..c02d99a0 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 From 92a740b3089440afaea8dbf31c66d1259bcf9ecd Mon Sep 17 00:00:00 2001 From: Stefal Date: Tue, 24 Dec 2024 00:18:43 +0100 Subject: [PATCH 6/6] keep wireguard vpn link --- web_app/network_infos.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web_app/network_infos.py b/web_app/network_infos.py index 67c4f87c..c2840da8 100644 --- a/web_app/network_infos.py +++ b/web_app/network_infos.py @@ -42,8 +42,9 @@ def get_up_if(): #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} # keep only interface with speed > 0 + 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 @@ -61,7 +62,7 @@ def get_interfaces_infos(): 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[k]} + 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']