Skip to content

Commit

Permalink
Add wpa/hostapad status handling based in index
Browse files Browse the repository at this point in the history
- comms_status.py - status handling rework
- comms_nats_controller.py - refactoring status instance handling
- comms_settings.py - save/load fixes

Jira-Id: SCDI-43

Signed-off-by: Mika Joenpera <[email protected]>
  • Loading branch information
joenpera committed Oct 11, 2023
1 parent d7ace14 commit ca2fc96
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 93 deletions.
3 changes: 0 additions & 3 deletions common/scripts/mesh-11s_nats.sh
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,6 @@ main () {
_mesh_vif="${INDEX}_MESH_VIF"
wifidev="${!_mesh_vif}"

#_phy="${INDEX}_PHY"
#phyname="${!_phy}"

find_phy_interface "$wifidev"
phyname=$retval_phy

Expand Down
40 changes: 15 additions & 25 deletions modules/sc-mesh-secure-deployment/src/nats/comms_nats_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,15 @@ def __init__(self, server: str, port: str, interval: int = 1000):

self.comms_status = []
#TODO how many radios?
self.comms_status.append(comms_status.CommsStatus(self.main_logger.getChild("status 0")))
self.comms_status.append(comms_status.CommsStatus(self.main_logger.getChild("status 1")))
self.comms_status.append(comms_status.CommsStatus(self.main_logger.getChild("status 2")))
for i in range(0, 3):
self.comms_status.append(comms_status.CommsStatus(self.main_logger.getChild(f"status {str(i)}"), i))

self.settings = comms_settings.CommsSettings(self.comms_status,
self.main_logger.getChild("settings"))

for cstat in self.comms_status:
cstat.settings(self.settings)

self.command = comms_command.Command(server, port, self.comms_status,
self.main_logger.getChild("command"))
self.telemetry = MeshTelemetry(self.interval, self.main_logger.getChild("telemetry"))
Expand Down Expand Up @@ -215,29 +219,15 @@ async def message_handler(message):
await handle_settings_csa_post(ret)
else:
# Update status info
# TODO how many radios?
cc.comms_status[0].refresh_status()
cc.comms_status[1].refresh_status()
cc.comms_status[2].refresh_status()
_ = [item.refresh_status() for item in cc.comms_status]

response = {'status': ret, 'info': info,
'mesh_status': [cc.comms_status[0].mesh_status,
cc.comms_status[1].mesh_status,
cc.comms_status[2].mesh_status],
'mesh_cfg_status': [cc.comms_status[0].mesh_cfg_status,
cc.comms_status[1].mesh_cfg_status,
cc.comms_status[2].mesh_cfg_status],
'visualisation_active': [cc.comms_status[0].is_visualisation_active,
cc.comms_status[1].is_visualisation_active,
cc.comms_status[2].is_visualisation_active],
'mesh_radio_on': [cc.comms_status[0].is_mesh_radio_on,
cc.comms_status[1].is_mesh_radio_on,
cc.comms_status[2].is_mesh_radio_on],
'ap_radio_on': [cc.comms_status[0].is_ap_radio_on,
cc.comms_status[1].is_ap_radio_on,
cc.comms_status[2].is_ap_radio_on],
'security_status': [cc.comms_status[0].security_status,
cc.comms_status[1].security_status,
cc.comms_status[2].security_status]
'mesh_status': [item.mesh_status for item in cc.comms_status],
'mesh_cfg_status': [item.mesh_cfg_status for item in cc.comms_status],
'visualisation_active': [item.is_visualisation_active for item in cc.comms_status],
'mesh_radio_on': [item.is_mesh_radio_on for item in cc.comms_status],
'ap_radio_on': [item.is_ap_radio_on for item in cc.comms_status],
'security_status': [item.security_status for item in cc.comms_status]
}

if resp != "":
Expand Down
118 changes: 67 additions & 51 deletions modules/sc-mesh-secure-deployment/src/nats/src/comms_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from shlex import quote
import logging
import re
import os

try:
import comms_common as comms
Expand Down Expand Up @@ -48,6 +49,8 @@ def __init__(self, comms_status: [cs.CommsStatus, ...], logger):
self.csa_state: int = 0 # 0: not started, 1: stored, 2: triggered
self.csa_count: int = 0 # number of CSA triggered
self.device_amount: str = "0" # number of devices to trigger CSA from nats message
ret, info = self.__load_settings()
self.logger.debug("load settings: %s, %s", ret, info)

def validate_mesh_settings(self, index: int) -> (str, str):
"""
Expand Down Expand Up @@ -156,7 +159,7 @@ def handle_mesh_settings_csa(self, msg: str, path="/opt",
comms.STATUS.mesh_cfg_not_stored
self.logger.error("save settings failed: %s, %s", ret, info)
else:
ret, info = self.__save_settings(path, file)
ret, info = self.__save_settings(path, file, index)
self.logger.debug("save settings: %s, %s", ret, info)
self.csa_state = 1
self.csa_count = 0
Expand All @@ -179,7 +182,6 @@ def handle_mesh_settings(self, msg: str, path="/opt",
"""
try:
parameters_set = json.loads(msg)
print(parameters_set)
self.msversion = "nats"
self.api_version = int(parameters_set["api_version"])
self.role = quote(str(parameters_set["role"]))
Expand Down Expand Up @@ -253,7 +255,7 @@ def __save_settings(self, path: str, file: str, index: int) -> (str, str):
mesh_conf.write(f"id{str(index)}_MESH_VIF={quote(self.mesh_vif[index])}\n")
mesh_conf.write(f"id{str(index)}_PHY={quote(self.phy[index])}\n")
mesh_conf.write(f"id{str(index)}_BATMAN_IFACE={quote(self.batman_iface[index])}\n")
mesh_conf.write(f"BRIDGE=\"{self.bridge}\"\n")
mesh_conf.write(f"BRIDGE={self.bridge}\n")

except:
self.comms_status[index].mesh_cfg_status = \
Expand All @@ -265,65 +267,79 @@ def __save_settings(self, path: str, file: str, index: int) -> (str, str):
self.logger.debug("%s written", f"{str(index)}_{file}")
return "OK", "Mesh configuration stored"

def __read_configs(self, mesh_conf_lines):
def __read_configs(self, mesh_conf_lines) -> None:
pattern: str = r'(\w+)=(.*?)(?:\s*#.*)?$'
# Find all key-value pairs in the text
matches = re.findall(pattern, mesh_conf_lines, re.MULTILINE)
for match in matches:
print(f"{match[0]}={match[1]}")
if match[0] == "MODE":
self.mode = match[1]
elif match[0] == "IP":
self.ip_address = match[1]
elif match[0] == "MASK":
self.subnet = match[1]
elif match[0] == "MAC":
self.ap_mac = match[1]
elif match[0] == "KEY":
self.key = match[1]
elif match[0] == "ESSID":
self.ssid = match[1]
elif match[0] == "FREQ":
self.frequency = match[1]
elif match[0] == "FREQ_MCC":
self.frequency_mcc = match[1]
elif match[0] == "TXPOWER":
self.tx_power = match[1]
elif match[0] == "COUNTRY":
self.country = match[1]
elif match[0] == "ROUTING":
self.routing = match[1]
elif match[0] == "ROLE":
self.role = match[1]
elif match[0] == "PRIORITY":
self.priority = match[1]
elif match[0] == "MESH_VIF":
self.mesh_vif = match[1]
elif match[0] == "PHY":
self.phy = match[1]
elif match[0] == "MSVERSION":
self.msversion = match[1]
else:
self.logger.debug("unknown config parameter: %s", match[0])
if "id" in match[0]:
index = int(match[0].split("_")[0].replace("id", ""))
name_parts = match[0].split("_")[1:]
name = "_".join(name_parts)
if name == "MODE":
self.mode.append(match[1])
elif name == "IP":
self.ip_address.append(match[1])
elif name == "MASK":
self.subnet.append(match[1])
elif name == "MAC":
self.ap_mac.append(match[1])
elif name == "KEY":
self.key.append(match[1])
elif name == "ESSID":
self.ssid.append(match[1])
elif name == "FREQ":
self.frequency.append(match[1])
elif name == "FREQ_MCC":
self.frequency_mcc.append(match[1])
elif name == "TXPOWER":
self.tx_power.append(match[1])
elif name == "COUNTRY":
self.country.append(match[1])
elif name == "ROUTING":
self.routing.append(match[1])
elif name == "PRIORITY":
self.priority.append(match[1])
elif name == "MESH_VIF":
self.mesh_vif.append(match[1])
elif name == "PHY":
self.phy.append(match[1])
elif name == "BATMAN_IFACE":
self.batman_iface.append(match[1])
else:
self.logger.error("unknown config parameter: %s", name)
else: # global config without index
if match[0] == "MSVERSION":
self.msversion = match[1]
elif match[0] == "BRIDGE":
self.bridge = match[1]
elif match[0] == "ROLE":
self.role = match[1]
else:
pass #self.logger.error("unknown config parameter: %s", match[0])

def __load_settings(self) -> (str, str):
"""
Load mesh settings
return: OK, FAIL
"""
config_file_path: str = "/opt/mesh_default.conf"
mission_config_file_path: str = "/opt/mesh.conf"

try:
with open(mission_config_file_path, "r", encoding="utf-8") as mesh_conf:
mesh_conf_lines = mesh_conf.read()
self.__read_configs(mesh_conf_lines)
except FileNotFoundError:
try:
with open(config_file_path, "r", encoding="utf-8") as mesh_conf:
mesh_conf_lines = mesh_conf.read()
self.__read_configs(mesh_conf_lines)
except FileNotFoundError:
self.logger.error("not able to read mesh config files")
return "FAIL", "not able to read mesh config files"
for index in range(0, len(self.comms_status)):
file = f"/opt/{str(index)}_mesh.conf"
if os.path.exists(file):
self.logger.debug("mesh config file %s found", file)
with open(file, "r", encoding="utf-8") as mesh_conf:
mesh_conf_lines = mesh_conf.read()
self.__read_configs(mesh_conf_lines)
else:
self.logger.debug("mesh config file %s not found, loading default", file)
if index == 0:
with open(config_file_path, "r", encoding="utf-8") as mesh_conf:
mesh_conf_lines = mesh_conf.read()
self.__read_configs(mesh_conf_lines)
except:
self.logger.error("not able to read mesh config files")
return "FAIL", "not able to read mesh config files"
return "OK", "Mesh configuration loaded"
39 changes: 26 additions & 13 deletions modules/sc-mesh-secure-deployment/src/nats/src/comms_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@
import copy
import hashlib
import os
import logging
from .comms_common import STATUS


class CommsStatus: # pylint: disable=too-many-instance-attributes
class CommsStatus: # pylint: disable=too-many-instance-attributes
"""
Maintains mesh and radio statuses
"""

class WpaStatus:
"""
Maintains wpa_supplicant status
"""

def __init__(self):
self.interface: str = ""
self.bssid: str = ""
Expand Down Expand Up @@ -58,7 +60,7 @@ def __eq__(self, other):
self.pairwise_cipher == other.pairwise_cipher and \
self.group_cipher == other.group_cipher and \
self.key_mgmt == other.key_mgmt and \
self.wpa_state == other.wpa_state and\
self.wpa_state == other.wpa_state and \
self.address == other.address and \
self.uuid == other.uuid
return False
Expand All @@ -67,6 +69,7 @@ class HostapdStatus:
"""
Maintains hostapd status
"""

def __init__(self):
self.interface: str = ""
self.state: str = "DISABLED"
Expand Down Expand Up @@ -99,8 +102,8 @@ def __eq__(self, other):
self.ssid == other.ssid
return False

def __init__(self, logger, index):
self.__index = index
def __init__(self, logger: logging, index: int):
self.__index: int = index
self.__lock = threading.Lock()
self.__thread_running: bool = False
self.__logger = logger
Expand All @@ -116,7 +119,14 @@ def __init__(self, logger, index):
self.__is_visualisation_active: bool = False
self.__is_hash_file: bool = False
self.__is_ap_radio_on: bool = False
self.__settings = None
# Refresh status

def settings(self, value):
"""
Set mesh settings
"""
self.__settings = value
self.__update_status()

@property
Expand Down Expand Up @@ -210,7 +220,7 @@ def refresh_status(self):

return "OK", "Current status read"

def __update_status(self): # pylint: disable=too-many-branches
def __update_status(self): # pylint: disable=too-many-branches
"""
Get mesh configuration status and wpa_supplicant
status and update internal states accordingly.
Expand Down Expand Up @@ -287,14 +297,17 @@ def __update_status(self): # pylint: disable=too-many-branches
# Unlock thread
self.__lock.release()

@staticmethod
def __get_wpa_supplicant_pid() -> str:
def __get_wpa_supplicant_pid(self) -> str:
"""
Get wpa_supplicant process ID.
"""
# Run commands in pieces
ps_command = ["ps", "ax"]
grep_command = ["grep", "-E", r"wpa_supplicant\-11s"]
try:
grep_command = ["grep", "-E",
f"[w]pa_supplicant-11s_id{str(self.__index)}_{self.__settings.mesh_vif[self.__index]}.conf"]
except IndexError:
return ""
awk_command = ["awk", '{print $1}']

ps_process = subprocess.Popen(ps_command, stdout=subprocess.PIPE)
Expand All @@ -313,7 +326,7 @@ def __get_wpa_cli_status(self):
"""
Get wpa_supplicant states via wpa_cli tool.
"""
wpa_cli_command = ["wpa_cli", "status"]
wpa_cli_command = ["wpa_cli", "-i", f"{self.__settings.mesh_vif[self.__index]}" "status"]
proc = subprocess.Popen(wpa_cli_command,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = proc.communicate()
Expand Down Expand Up @@ -431,9 +444,9 @@ def __get_mission_cfg_status(self):
Checks whether mission config file exists and does it
match with hash file of previously applied settings.
"""
config_file_path = "/opt/mesh.conf"
pending_config_file_path = "/opt/mesh_stored.conf"
hash_file_path = "/opt/mesh.conf_hash"
config_file_path = f"/opt/{str(self.__index)}_mesh.conf"
pending_config_file_path = f"/opt/{str(self.__index)}_mesh_stored.conf"
hash_file_path = f"/opt/{str(self.__index)}_mesh.conf_hash"
old_mesh_cfg_status = self.__mesh_cfg_status
old_is_mission_cfg = self.__is_mission_cfg
try:
Expand Down
2 changes: 1 addition & 1 deletion modules/utils/docker/entrypoint_nats.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
source /opt/mesh-helper.sh

# sources mesh configuration and sets start_opts
source_configuration
source_configuration "id0"

if [ "$MSVERSION" != "nats" ]; then
if [ -f "/usr/local/bin/entrypoint.sh" ]; then
Expand Down

0 comments on commit ca2fc96

Please sign in to comment.