From f1de4ff6fc5b58b794a4415deb4c47215eefed53 Mon Sep 17 00:00:00 2001 From: Kapil Bansal Date: Sun, 9 May 2021 19:41:55 +0530 Subject: [PATCH] [refactor] Split functions in multiple files --- address.lua => interface_functions.lua | 78 +++++++++ miscellaneous_functions.lua | 82 +++++++++ neighbors_functions.lua | 2 + netjson-monitoring.lua | 231 +------------------------ 4 files changed, 170 insertions(+), 223 deletions(-) rename address.lua => interface_functions.lua (52%) create mode 100644 miscellaneous_functions.lua diff --git a/address.lua b/interface_functions.lua similarity index 52% rename from address.lua rename to interface_functions.lua index c779a48..9cca0f7 100644 --- a/address.lua +++ b/interface_functions.lua @@ -1,3 +1,5 @@ +#!/usr/bin/env lua + utils = require('utils') nixio = require('nixio') ubus_lib = require('ubus') @@ -10,6 +12,56 @@ ubus = ubus_lib.connect() interface_data = ubus:call('network.interface', 'dump', {}) nixio_data = nixio.getifaddrs() +specialized_interfaces = { + modemmanager = function(name, interface) + local modem = uci_cursor.get('network', interface['interface'], 'device') + local info = {} + + local general = io.popen('mmcli --output-json -m '..modem):read("*a") + if general and pcall(function () general = cjson.decode(general) end) then + general = general.modem + + if not utils.is_table_empty(general['3gpp']) then + info.imei = general['3gpp'].imei + info.operator_name = general['3gpp']['operator-name'] + info.operator_code = general['3gpp']['operator-code'] + end + + if not utils.is_table_empty(general.generic) then + info.manufacturer = general.generic.manufacturer + info.model = general.generic.model + info.connection_status = general.generic.state + info.power_status = general.generic['power-state'] + end + end + + local signal = io.popen('mmcli --output-json -m '..modem..' --signal-get'):read() + if signal and pcall(function () signal = cjson.decode(signal) end) then + -- only send data if not empty to avoid generating too much traffic + if not utils.is_table_empty(signal.modem) and not utils.is_table_empty(signal.modem.signal) then + -- omit refresh rate + signal.modem.signal.refresh = nil + info.signal = {} + -- collect section only if not empty + for section_key, section_values in pairs(signal.modem.signal) do + for key, value in pairs(section_values) do + if value ~= '--' then + -- convert to number + section_values[key] = tonumber(value) + -- store in info + if utils.is_table_empty(info[section_key]) then + info.signal[section_key] = section_values + end + end + end + end + end + end + + return {type='modem-manager', mobile=info} + end +} + function find_default_gateway(routes) for i = 1, #routes do if routes[i].target == '0.0.0.0' then @@ -99,4 +151,30 @@ function get_addresses(name) return addresses end +function get_interface_info(name, netjson_interface) + info = { + dns_search = nil, + dns_servers = nil + } + for _, interface in pairs(interface_data['interface']) do + if interface['l3_device'] == name then + if next(interface['dns-search']) then + info.dns_search = interface['dns-search'] + end + if next(interface['dns-server']) then + info.dns_servers = interface['dns-server'] + end + if netjson_interface.type == 'bridge' then + info.stp = uci_cursor.get('network', interface['interface'], 'stp') == '1' + end + -- collect specialized info if available + local specialized_info = specialized_interfaces[interface.proto] + if specialized_info then + info.specialized = specialized_info(name, interface) + end + end + end + return info +end + print(get_addresses('eth2')) diff --git a/miscellaneous_functions.lua b/miscellaneous_functions.lua new file mode 100644 index 0000000..7a389c2 --- /dev/null +++ b/miscellaneous_functions.lua @@ -0,0 +1,82 @@ +#!/usr/bin/env lua + +utils = require('utils') +uci = require('uci') +uci_cursor = uci.cursor() + + +function parse_hostapd_clients(clients) + local data = {} + for mac, properties in pairs(clients) do + properties.mac = mac + table.insert(data, properties) + end + return data +end + +function parse_iwinfo_clients(clients) + local data = {} + for i, p in pairs(clients) do + client = {} + client.ht = p.rx.ht + client.mac = p.mac + client.authorized = p.authorized + client.vht = p.rx.vht + client.wmm = p.wme + client.mfp = p.mfp + client.auth = p.authenticated + client.signal = p.signal + client.noise = p.noise + table.insert(data, client) + end + return data +end + +function parse_disk_usage() + file = io.popen('df') + disk_usage_info = {} + for line in file:lines() do + if line:sub(1, 10) ~= 'Filesystem' then + filesystem, size, used, available, percent, location = + line:match('(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)') + if filesystem ~= 'tmpfs' and not string.match(filesystem, 'overlayfs') then + percent = percent:gsub('%W', '') + -- available, size and used are in KiB + table.insert(disk_usage_info, { + filesystem = filesystem, + available_bytes = tonumber(available) * 1024, + size_bytes = tonumber(size) * 1024, + used_bytes = tonumber(used) * 1024, + used_percent = tonumber(percent), + mount_point = location + }) + end + end + end + file:close() + return disk_usage_info +end + +function get_cpus() + processors = io.popen('cat /proc/cpuinfo | grep -c processor') + cpus = tonumber(processors:read('*a')) + processors:close() + return cpus +end + +function get_vpn_interfaces() + -- only openvpn supported for now + local items = uci_cursor:get_all('openvpn') + local vpn_interfaces = {} + + if utils.is_table_empty(items) then + return {} + end + + for name, config in pairs(items) do + if config and config.dev then + vpn_interfaces[config.dev] = true + end + end + return vpn_interfaces +end diff --git a/neighbors_functions.lua b/neighbors_functions.lua index c019047..3203dea 100644 --- a/neighbors_functions.lua +++ b/neighbors_functions.lua @@ -1,3 +1,5 @@ +#!/usr/bin/env lua + local cjson = require('cjson') netjson = {} diff --git a/netjson-monitoring.lua b/netjson-monitoring.lua index 079b27b..89bb322 100644 --- a/netjson-monitoring.lua +++ b/netjson-monitoring.lua @@ -8,6 +8,8 @@ nixio = require('nixio') uci = require('uci') uci_cursor = uci.cursor() +interface_functions = require('interface_functions') +miscellaneous_functions = require('miscellaneous_functions') neighbors_functions = require('neighbors_functions') utils = require('utils') @@ -48,36 +50,9 @@ function get_dhcp_leases() return leases end -function parse_hostapd_clients(clients) - local data = {} - for mac, properties in pairs(clients) do - properties.mac = mac - table.insert(data, properties) - end - return data -end - -function parse_iwinfo_clients(clients) - local data = {} - for i, p in pairs(clients) do - client = {} - client.ht = p.rx.ht - client.mac = p.mac - client.authorized = p.authorized - client.vht = p.rx.vht - client.wmm = p.wme - client.mfp = p.mfp - client.auth = p.authenticated - client.signal = p.signal - client.noise = p.noise - table.insert(data, client) - end - return data -end - -- takes ubus wireless.status clients output and converts it to NetJSON function netjson_clients(clients, is_mesh) - return (is_mesh and parse_iwinfo_clients(clients) or parse_hostapd_clients(clients)) + return (is_mesh and miscellaneous_functions.parse_iwinfo_clients(clients) or miscellaneous_functions.parse_hostapd_clients(clients)) end ubus = ubus_lib.connect() @@ -100,54 +75,6 @@ loadavg_output = io.popen('cat /proc/loadavg'):read() loadavg_output = utils.split(loadavg_output, ' ') load_average = {tonumber(loadavg_output[1]), tonumber(loadavg_output[2]), tonumber(loadavg_output[3])} -function parse_disk_usage() - file = io.popen('df') - disk_usage_info = {} - for line in file:lines() do - if line:sub(1, 10) ~= 'Filesystem' then - filesystem, size, used, available, percent, location = - line:match('(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)') - if filesystem ~= 'tmpfs' and not string.match(filesystem, 'overlayfs') then - percent = percent:gsub('%W', '') - -- available, size and used are in KiB - table.insert(disk_usage_info, { - filesystem = filesystem, - available_bytes = tonumber(available) * 1024, - size_bytes = tonumber(size) * 1024, - used_bytes = tonumber(used) * 1024, - used_percent = tonumber(percent), - mount_point = location - }) - end - end - end - file:close() - return disk_usage_info -end - -function get_cpus() - processors = io.popen('cat /proc/cpuinfo | grep -c processor') - cpus = tonumber(processors:read('*a')) - processors:close() - return cpus -end - -function get_vpn_interfaces() - -- only openvpn supported for now - local items = uci_cursor:get_all('openvpn') - local vpn_interfaces = {} - - if utils.is_table_empty(items) then - return {} - end - - for name, config in pairs(items) do - if config and config.dev then - vpn_interfaces[config.dev] = true - end - end - return vpn_interfaces -end -- init netjson data structure netjson = { @@ -161,8 +88,8 @@ netjson = { load = load_average, memory = system_info.memory, swap = system_info.swap, - cpus = get_cpus(), - disk = parse_disk_usage() + cpus = miscellaneous_functions.get_cpus(), + disk = miscellaneous_functions.parse_disk_usage() } } @@ -193,154 +120,12 @@ end -- collect device data network_status = ubus:call('network.device', 'status', {}) wireless_status = ubus:call('network.wireless', 'status', {}) -interface_data = ubus:call('network.interface', 'dump', {}) -nixio_data = nixio.getifaddrs() -vpn_interfaces = get_vpn_interfaces() +vpn_interfaces = miscellaneous_functions.get_vpn_interfaces() wireless_interfaces = {} interfaces = {} dns_servers = {} dns_search = {} -specialized_interfaces = { - modemmanager = function(name, interface) - local modem = uci_cursor.get('network', interface['interface'], 'device') - local info = {} - - local general = io.popen('mmcli --output-json -m '..modem):read("*a") - if general and pcall(function () general = cjson.decode(general) end) then - general = general.modem - - if not utils.is_table_empty(general['3gpp']) then - info.imei = general['3gpp'].imei - info.operator_name = general['3gpp']['operator-name'] - info.operator_code = general['3gpp']['operator-code'] - end - - if not utils.is_table_empty(general.generic) then - info.manufacturer = general.generic.manufacturer - info.model = general.generic.model - info.connection_status = general.generic.state - info.power_status = general.generic['power-state'] - end - end - - local signal = io.popen('mmcli --output-json -m '..modem..' --signal-get'):read() - if signal and pcall(function () signal = cjson.decode(signal) end) then - -- only send data if not empty to avoid generating too much traffic - if not utils.is_table_empty(signal.modem) and not utils.is_table_empty(signal.modem.signal) then - -- omit refresh rate - signal.modem.signal.refresh = nil - info.signal = {} - -- collect section only if not empty - for section_key, section_values in pairs(signal.modem.signal) do - for key, value in pairs(section_values) do - if value ~= '--' then - -- convert to number - section_values[key] = tonumber(value) - -- store in info - if utils.is_table_empty(info[section_key]) then - info.signal[section_key] = section_values - end - end - end - end - end - end - - return {type='modem-manager', mobile=info} - end -} - -function get_interface_info(name, netjson_interface) - info = { - dns_search = nil, - dns_servers = nil - } - for _, interface in pairs(interface_data['interface']) do - if interface['l3_device'] == name then - if next(interface['dns-search']) then - info.dns_search = interface['dns-search'] - end - if next(interface['dns-server']) then - info.dns_servers = interface['dns-server'] - end - if netjson_interface.type == 'bridge' then - info.stp = uci_cursor.get('network', interface['interface'], 'stp') == '1' - end - -- collect specialized info if available - local specialized_info = specialized_interfaces[interface.proto] - if specialized_info then - info.specialized = specialized_info(name, interface) - end - end - end - return info -end - --- collect interface addresses -function get_addresses(name) - addresses = {} - interface_list = interface_data['interface'] - addresses_list = {} - for _, interface in pairs(interface_list) do - if interface['l3_device'] == name then - proto = interface['proto'] - if proto == 'dhcpv6' then - proto = 'dhcp' - end - for _, address in pairs(interface['ipv4-address']) do - table.insert(addresses_list, address['address']) - new_address = new_address_array(address, interface, 'ipv4') - table.insert(addresses, new_address) - end - for _, address in pairs(interface['ipv6-address']) do - table.insert(addresses_list, address['address']) - new_address = new_address_array(address, interface, 'ipv6') - table.insert(addresses, new_address) - end - end - end - for i = 1, #nixio_data do - if nixio_data[i].name == name then - if not is_excluded(name) then - family = nixio_data[i].family - addr = nixio_data[i].addr - if family == 'inet' then - family = 'ipv4' - -- Since we don't already know this from the dump, we can - -- consider this dynamically assigned, this is the case for - -- example for OpenVPN interfaces, which get their address - -- from the DHCP server embedded in OpenVPN - proto = 'dhcp' - elseif family == 'inet6' then - family = 'ipv6' - if utils.starts_with(addr, 'fe80') then - proto = 'static' - else - ula = uci_cursor.get('network', 'globals', 'ula_prefix') - ula_prefix = utils.split(ula, '::')[1] - if utils.starts_with(addr, ula_prefix) then - proto = 'static' - else - proto = 'dhcp' - end - end - end - if family == 'ipv4' or family == 'ipv6' then - if not utils.has_value(addresses_list, addr) then - table.insert(addresses, { - address = addr, - mask = nixio_data[i].prefix, - proto = proto, - family = family - }) - end - end - end - end - end - return addresses -end -- collect relevant wireless interface stats -- (traffic and connected clients) @@ -440,11 +225,11 @@ for name, interface in pairs(network_status) do end netjson_interface.statistics = interface.statistics end - addresses = get_addresses(name) + addresses = interface_functions.get_addresses(name) if next(addresses) then netjson_interface.addresses = addresses end - info = get_interface_info(name, netjson_interface) + info = interface_functions.get_interface_info(name, netjson_interface) if info.stp ~= nil then netjson_interface.stp = info.stp end