mirror of
https://github.com/optim-enterprises-bv/openlan-cgw.git
synced 2025-10-30 17:57:47 +00:00
Add initial infrastructure for sending / receiving messages using websocket bindings for client sim. Add initial handle-reboot-simulation test, that both tests CGW's ability to sink down a request, as well as test it's behavior whenever simulated device's been rebooted. Signed-off-by: Oleksandr Mazur <oleksandr.mazur@plvision.eu>
232 lines
8.2 KiB
Python
232 lines
8.2 KiB
Python
from dataclasses import dataclass
|
|
from typing import List, Tuple
|
|
import copy
|
|
import json
|
|
import uuid
|
|
|
|
|
|
class MacRange:
|
|
"""
|
|
Return an object that produces a sequence of MAC addresses from
|
|
START (inclusive) to END (inclusive). START and END are exctracted
|
|
from the input string if it is in the format
|
|
"11:22:AA:BB:00:00-11:22:AA:BB:00:05" (where START=11:22:AA:BB:00:00,
|
|
END=11:22:AA:BB:00:05, and the total amount of MACs in the range is 6).
|
|
|
|
Examples (all of these are identical):
|
|
|
|
00:00:00:00:XX:XX
|
|
00:00:00:00:00:00-00:00:00:00:FF:FF
|
|
00:00:00:00:00:00^65536
|
|
|
|
Raises ValueError
|
|
"""
|
|
def __init__(self, input: str = "XX:XX:XX:XX:XX:XX") -> None:
|
|
input = input.replace("-", ":", 5)
|
|
|
|
self.__base_as_num, self.__len = self.__parse_input(input.upper())
|
|
self.__idx = 0
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self) -> str:
|
|
if self.__idx >= len(self):
|
|
self.__idx = 0
|
|
raise StopIteration()
|
|
mac = self.num2mac(self.__base_as_num + self.__idx)
|
|
self.__idx += 1
|
|
return mac
|
|
|
|
def __len__(self) -> int:
|
|
return self.__len
|
|
|
|
def __str__(self) -> str:
|
|
return f"MacRange[start={self.base}, " \
|
|
f"end={self.num2mac(self.__base_as_num + len(self) - 1)}]"
|
|
|
|
def __repr__(self) -> str:
|
|
return f"MacRange('{self.base}^{len(self)}')"
|
|
|
|
@property
|
|
def base(self) -> str:
|
|
return self.num2mac(self.__base_as_num)
|
|
|
|
@staticmethod
|
|
def mac2num(mac: str) -> int:
|
|
mac = mac.replace(":", "", 5)
|
|
mac = mac.replace("-", "", 5)
|
|
return int(mac, base=16)
|
|
|
|
@staticmethod
|
|
def num2mac(mac: int) -> str:
|
|
hex = f"{mac:012X}"
|
|
return ":".join([a+b for a, b in zip(hex[::2], hex[1::2])])
|
|
|
|
def __parse_input(self, input: str) -> Tuple[int, int]:
|
|
if "X" in input:
|
|
string = f"{input.replace('X', '0')}-{input.replace('X', 'F')}"
|
|
else:
|
|
string = input
|
|
if "-" in string:
|
|
start, end = string.split("-")
|
|
start, end = self.mac2num(start), self.mac2num(end)
|
|
if start > end:
|
|
raise ValueError(f"Invalid MAC range {start}-{end}")
|
|
return start, end - start + 1
|
|
if "^" in string:
|
|
base, count = string.split("^")
|
|
return self.mac2num(base), int(count)
|
|
return self.mac2num(input), 1
|
|
|
|
class UCentralConfigRequest:
|
|
TEMPLATE_FILE_AP_BASIC = "./kafka_data/cfg_ap_basic.json"
|
|
TEMPLATE_FILE_AP_BASIC_INVALID = "./kafka_data/cfg_ap_basic_invalid.json"
|
|
TEMPLATE_FILE_SWITCH_BASIC = "./kafka_data/cfg_switch_basic.json"
|
|
TEMPLATE_FILE_SWITCH_BASIC_INVALID = "./kafka_data/cfg_switch_basic_invalid.json"
|
|
|
|
@staticmethod
|
|
def parse_uuid(uuid_val = None) -> str:
|
|
if uuid_val is None:
|
|
return str(1)
|
|
|
|
return str(uuid_val)
|
|
|
|
def __init__(self) -> None:
|
|
with open(self.TEMPLATE_FILE_AP_BASIC) as f:
|
|
self.ap_basic = f.read()
|
|
with open(self.TEMPLATE_FILE_AP_BASIC_INVALID) as f:
|
|
self.ap_basic_invalid = f.read()
|
|
with open(self.TEMPLATE_FILE_SWITCH_BASIC) as f:
|
|
self.switch_basic = f.read()
|
|
with open(self.TEMPLATE_FILE_SWITCH_BASIC_INVALID) as f:
|
|
self.switch_basic_invalid = f.read()
|
|
|
|
def get_ap_basic_cfg(self, mac: str, uuid_val = None):
|
|
req = copy.deepcopy(self.ap_basic);
|
|
req = req.replace("MAC_PLACEHOLDER", mac)
|
|
req = req.replace("UUID_PLACEHOLDER", UCentralConfigRequest.parse_uuid(uuid_val))
|
|
return req
|
|
|
|
def get_ap_basic_invalid_cfg(self, mac: str, uuid_val = None):
|
|
req = copy.deepcopy(self.ap_basic_invalid);
|
|
req = req.replace("MAC_PLACEHOLDER", mac)
|
|
req = req.replace("UUID_PLACEHOLDER", UCentralConfigRequest.parse_uuid(uuid_val))
|
|
return req
|
|
|
|
def get_switch_basic_cfg(self, mac: str, uuid_val = None):
|
|
req = copy.deepcopy(self.switch_basic);
|
|
req = req.replace("MAC_PLACEHOLDER", mac)
|
|
req = req.replace("UUID_PLACEHOLDER", UCentralConfigRequest.parse_uuid(uuid_val))
|
|
return req
|
|
|
|
def get_switch_basic_invalid_cfg(self, mac: str, uuid_val = None):
|
|
req = copy.deepcopy(self.switch_basic_invalid);
|
|
req = req.replace("MAC_PLACEHOLDER", mac)
|
|
req = req.replace("UUID_PLACEHOLDER", UCentralConfigRequest.parse_uuid(uuid_val))
|
|
return req
|
|
|
|
class Message:
|
|
TEMPLATE_FILE = "./kafka_data/message_template.json"
|
|
GROUP_ADD = "add_group"
|
|
GROUP_ADD_TO_SHARD = "add_group_to_shard"
|
|
GROUP_DEL = "del_group"
|
|
DEV_TO_GROUP = "add_to_group"
|
|
DEV_FROM_GROUP = "del_from_group"
|
|
TO_DEVICE = "message_infra"
|
|
GROUP_ID = "infra_group_id"
|
|
GROUP_NAME = "infra_name"
|
|
SHARD_ID = "infra_shard_id"
|
|
DEV_LIST = "infra_group_infras"
|
|
MAC = "mac"
|
|
DATA = "msg"
|
|
MSG_UUID = "uuid"
|
|
|
|
def __init__(self) -> None:
|
|
with open(self.TEMPLATE_FILE) as f:
|
|
self.templates = json.loads(f.read())
|
|
|
|
@staticmethod
|
|
def parse_uuid(uuid_val = None) -> str:
|
|
if uuid_val is None:
|
|
return str(uuid.uuid1())
|
|
|
|
return str(uuid.UUID(int=uuid_val))
|
|
|
|
def group_create(self, id: str, name: str, uuid_val: int = None) -> bytes:
|
|
msg = copy.copy(self.templates[self.GROUP_ADD])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.GROUP_NAME] = name
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
def group_create_to_shard(self, id: str, shard_id: int, name: str, uuid_val: int = None) -> bytes:
|
|
msg = copy.copy(self.templates[self.GROUP_ADD_TO_SHARD])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.SHARD_ID] = shard_id
|
|
msg[self.GROUP_NAME] = name
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
def group_delete(self, id: str, uuid_val: int = None) -> bytes:
|
|
msg = copy.copy(self.templates[self.GROUP_DEL])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
def add_dev_to_group(self, id: str, mac_range: MacRange, uuid_val: int = None) -> bytes:
|
|
msg = copy.copy(self.templates[self.DEV_TO_GROUP])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.DEV_LIST] = list(mac_range)
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
def remove_dev_from_group(self, id: str, mac_range: MacRange, uuid_val: int = None) -> bytes:
|
|
msg = copy.copy(self.templates[self.DEV_FROM_GROUP])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.DEV_LIST] = list(mac_range)
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
def add_devices_to_group(self, id: str, mac_list: list, uuid_val: int = None) -> bytes:
|
|
msg = copy.copy(self.templates[self.DEV_TO_GROUP])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.DEV_LIST] = mac_list
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
def remove_devices_from_group(self, id: str, mac_list: list, uuid_val: int = None) -> bytes:
|
|
msg = copy.copy(self.templates[self.DEV_FROM_GROUP])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.DEV_LIST] = mac_list
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
def to_device(self, id: str, mac: str, data, sequence: int = 0, uuid_val: int = None):
|
|
msg = copy.copy(self.templates[self.TO_DEVICE])
|
|
msg[self.GROUP_ID] = id
|
|
msg[self.MAC] = mac
|
|
if type(data) is dict:
|
|
msg[self.DATA] = data
|
|
else:
|
|
msg[self.DATA] = {"data": data}
|
|
#msg[self.MSG_UUID] = str(uuid.uuid1(node=MacRange.mac2num(mac), clock_seq=sequence))
|
|
msg[self.MSG_UUID] = Message.parse_uuid(uuid_val)
|
|
return json.dumps(msg).encode('utf-8')
|
|
|
|
|
|
@dataclass
|
|
class Args:
|
|
add_groups: List[Tuple[str, int, str]]
|
|
del_groups: List[str]
|
|
assign_to_group: List[Tuple[str, MacRange]]
|
|
remove_from_group: List[Tuple[str, MacRange]]
|
|
topic: str
|
|
db: str
|
|
message: dict
|
|
count: int
|
|
time_to_send_s: float
|
|
interval_s: float
|
|
group_id: int
|
|
send_to_macs: MacRange
|