diff --git a/py-json/realm.py b/py-json/realm.py index 8b0823da..5a897465 100755 --- a/py-json/realm.py +++ b/py-json/realm.py @@ -16,12 +16,30 @@ class Realm(LFCliBase): self.lfclient_url = f"http://{lfclient_host}:{lfclient_port}" super().check_connect() + # Returns json response from webpage of all layer 3 cross connects def cx_list(self): response = super().json_get("/cx") return response - # Returns list of all stations with "sta" in their name + # Returns map of all stations with port+type == WIFI-STATION + def station_map(self): + response = super().json_get("/port/list?fields=_links,alias,device,port+type") + if (response is None) or ("interfaces" not in response): + pprint(response) + print("station_list: incomplete response, halting") + exit(1) + sta_map = {} + temp_map = LFUtils.portListToAliasMap(response) + for k,v in temp_map.items(): + if (v['port type'] == "WIFI-STA"): + sta_map[k] = v; + temp_map.clear() + del temp_map + del response + return sta_map + + # Returns list of all stations with port+type == WIFI-STATION def station_list(self): sta_list = [] response = super().json_get("/port/list?fields=_links,alias,device,port+type") @@ -29,11 +47,12 @@ class Realm(LFCliBase): print("station_list: incomplete response:") pprint(response) exit(1) + for x in range(len(response['interfaces'])): for k,v in response['interfaces'][x].items(): - if ("sta" in v['device']) or ("wlan" in v['device']): + if v['port type'] == "WIFI-STA": sta_list.append(response['interfaces'][x]) - + del response return sta_list # Returns list of all VAPs with "vap" in their name @@ -47,16 +66,39 @@ class Realm(LFCliBase): return sta_list + # removes port by eid/eidpn + def removeVlanByEid(self, eid): + if (eid is None) or ("" == eid): + raise ValueError(f"removeVlanByEid wants eid like 1.1.sta0 but given[{eid}]") + hunks = eid.split('.') + #print("- - - - - - - - - - - - - - - - -") + #pprint(hunks) + #pprint(self.lfclient_url) + #print("- - - - - - - - - - - - - - - - -") + if (len(hunks) > 3) or (len(hunks) < 2): + raise ValueError(f"removeVlanByEid wants eid like 1.1.sta0 but given[{eid}]") + elif len(hunks) == 3: + LFUtils.removePort(hunks[1], hunks[2], self.lfclient_url) + else: + LFUtils.removePort(hunks[0], hunks[1], self.lfclient_url) # Searches for ports that match a given pattern and returns a list of names def find_ports_like(self, pattern=""): - device_name_list = [] response = super().json_get("/port/list?fields=_links,alias,device,port+type") - for x in range(len(response['interfaces'])): - for k,v in response['interfaces'][x].items(): - if v['device'] != "NA": - device_name_list.append(v['device']) - matched_list = [] + alias_map = LFUtils.portListToAliasMap(response) + #pprint(alias_map) + prelim_map = {} + matched_map = {} + for name,record in alias_map.items(): + try: + #print("- prelim - - - - - - - - - - - - - - - - - - -") + #pprint(record) + if (record["port type"] == "WIFI-STA"): + prelim_map[name] = record + + except Exception as x: + super().error(x) + prefix = "" try: if pattern.find("+") > 0: @@ -64,18 +106,18 @@ class Realm(LFCliBase): if match.group(1): #print("name:", port_name, " Group 1: ",match.group(1)) prefix = match.group(1) - for port_name in device_name_list: - if port_name.find(prefix) == 0: - matched_list.append(port_name) + for port_eid,record in prelim_map.items(): + if port_eid.find(prefix) >= 0: + matched_map[port_eid] = record elif pattern.find("*") > 0: match = re.search(r"^([^\*]+)[\*]$", pattern) if match.group(1): prefix = match.group(1) #print("group 1: ",prefix) - for port_name in device_name_list: - if port_name.find(prefix) == 0: - matched_list.append(port_name) + for port_eid,record in prelim_map.items(): + if port_eid.find(prefix) >= 0: + matched_map[port_eid] = record elif pattern.find("[") > 0: match = re.search(r"^([^\[]+)\[(\d+)\.\.(\d+)\]$", pattern) @@ -84,28 +126,29 @@ class Realm(LFCliBase): #print("[group2]: ", match.group(2)) #print("[group3]: ", match.group(3)) prefix = match.group(1) - for port_name in device_name_list: - if port_name.find(prefix) == 0: - port_suf = port_name[len(prefix):] + for port_eid,record in prelim_map.items(): + if port_eid.find(prefix) >= 0: + port_suf = record["device"][len(prefix):] if (port_suf >= match.group(2)) and (port_suf <= match.group(3)): #print(f"{port_name}: suffix[{port_name}] between {match.group(2)}:{match.group(3)}") - matched_list.append(port_name) # wrong but better + matched_map[port_eid] = record except ValueError as e: super().error(e) - return matched_list + return matched_map def new_station_profile(self): - station_prof = StationProfile(self.lfclient_url) + station_prof = StationProfile(self.lfclient_url, debug=self.debugOn) return station_prof def new_cx_profile(self): - cx_prof = CXProfile(self.lfclient_url) + cx_prof = CXProfile(self.lfclient_url, debug=self.debugOn) return cx_prof class CXProfile: - def __init__(self, lfclient_host, lfclient_port): + def __init__(self, lfclient_host, lfclient_port, debug=False): self.lfclient_url = f"http://{lfclient_host}:{lfclient_port}/" + self.debug = debug self.post_data = [] # Adds post data for a cross-connect between eth1 and specified list of ports, appends to array @@ -186,7 +229,8 @@ class CXProfile: # profile.build(resource, radio, 64) # class StationProfile: - def __init__(self, lfclient_url, ssid="NA", ssid_pass="NA", security="open", start_id="", mode=0, up=True, dhcp=True): + def __init__(self, lfclient_url, ssid="NA", ssid_pass="NA", security="open", prefix="00000", mode=0, up=True, dhcp=True, debug=False): + self.debug = debug self.lfclient_url = lfclient_url self.ssid = ssid self.ssid_pass = ssid_pass @@ -196,6 +240,8 @@ class StationProfile: self.security = security self.COMMANDS = ["add_sta", "set_port"] self.desired_add_sta_flags = ["wpa2_enable", "80211u_enable", "create_admin_down"] + self.desired_add_sta_flags_mask = ["wpa2_enable", "80211u_enable", "create_admin_down"] + self.prefix = prefix self.add_sta_data = { "shelf": 1, "resource": 1, @@ -207,7 +253,8 @@ class StationProfile: "mac": "xx:xx:xx:xx:*:xx", "flags": 0, # (0x400 + 0x20000 + 0x1000000000) # create admin down } - self.desired_set_port_flags = ["down", "dhcp"] + self.desired_set_port_current_flags = ["if_down", "use_dhcp"] + self.desired_set_port_interest_flags = ["current_flags", "dhcp", "ifdown"] self.set_port_data = { "shelf": 1, "resource": 1, @@ -216,23 +263,69 @@ class StationProfile: "interest": 0, #(0x2 + 0x4000 + 0x800000) # current, dhcp, down, } - def set_param(self, cli_name, param_name, param_val): + def use_wpa2(self, on=False, ssid=None, passwd=None): + if on: + if (ssid is None) or ("" == ssid): + raise ValueError("use_wpa2: WPA2 requires ssid") + if (passwd is None) or ("" == passwd): + raise ValueError("use_wpa2: WPA2 requires passphrase or [BLANK]") + self.set_command_param("add_sta", "ssid", ssid) + self.set_command_param("add_sta", "key", passwd) + else: + self.set_command_flag("add_sta", "flags", "wpa2_enable", 0) + + def set_command_param(self, command_name, param_name, param_value): # we have to check what the param name is - if (cli_name is None) or (cli_name == ""): + if (command_name is None) or (command_name == ""): return if (param_name is None) or (param_name == ""): return - if cli_name not in self.COMMANDS: - print(f"Command name name [{cli_name}] not defined in {self.COMMANDS}") + if command_name not in self.COMMANDS: + super().error(f"Command name name [{command_name}] not defined in {self.COMMANDS}") return - if cli_name == "add_sta": - if (param_name not in add_sta.add_sta_flags) or (param_name not in add_sta.add_sta_modes): + if command_name == "add_sta": + self.add_sta_data[param_name] = param_value + elif command_name == "set_port": + self.set_port_data[param_name] = param_value + + def set_command_flag(self, command_name, param_name, value): + # we have to check what the param name is + if (command_name is None) or (command_name == ""): + return + if (param_name is None) or (param_name == ""): + return + if command_name not in self.COMMANDS: + print(f"Command name name [{command_name}] not defined in {self.COMMANDS}") + return + if command_name == "add_sta": + if (param_name not in add_sta.add_sta_flags) and (param_name not in add_sta.add_sta_modes): print(f"Parameter name [{param_name}] not defined in add_sta.py") + if self.debug: + pprint(add_sta.add_sta_flags) return - elif cli_name == "set_port": - if (param_name not in set_port.set_port_current_flags) or (param_name not in set_port.set_port_cmd_flags): + if (value == 1) and (param_name not in self.desired_add_sta_flags): + self.desired_add_sta_flags_flags.append(param_name) + elif value == 0: + self.desired_set_port_flags_flags.remove(param_name) + + elif command_name == "set_port": + if (param_name not in set_port.set_port_current_flags) and (param_name not in set_port.set_port_cmd_flags): print(f"Parameter name [{param_name}] not defined in set_port.py") + if self.debug: + pprint(set_port.set_port_cmd_flags) + pprint(set_port.set_port_current_flags) + pprint(set_port.set_port_interest_flags) return + if (value == 1) and (param_name not in self.desired_set_port_flags): + self.desired_set_port_flags_flags.append(param_name) + elif value == 0: + self.desired_set_port_flags_flags.remove(param_name) + + # use this for hinting station name; stations begin with 'sta', the + # stations created with a prefix '0100' indicate value 10100 + n with + # resulting substring(1,) applied; station 900 becomes 'sta1000' + def set_prefix(self, pref): + self.prefix = pref def add_named_flags(self, desired_list, command_ref): if desired_list is None: @@ -245,14 +338,29 @@ class StationProfile: result = 0 for name in desired_list: - if (name not in command_ref): - raise ValueError(f"flag {name} not in map") + if (name is None) or (name == ""): + continue + if name not in command_ref: + if self.debug: + pprint(command_ref) + raise ValueError("flag %s not in map" % name ) result += command_ref[name] return result + def format_sta_name(self, num): + wd = len(self.prefix) + v = int(self.prefix, 10) + if v > 0: + num += v + template = f"sta%0{wd}d" + name = template % num + #if self.debug: + # print(f"XXXXXXXXXXX {name} XXXXXXXXXXXXXXX") + return name + # Checks for errors in initialization values and creates specified number of stations using init parameters - def build(self, resource, radio, num_stations): + def build(self, resource, radio, num_stations, dry_run=False, debug=False): # try: # resource = resource_radio[0: resource_radio.index(".")] # name = resource_radio[resource_radio.index(".") + 1:] @@ -263,14 +371,45 @@ class StationProfile: # print(e) # create stations down, do set_port on them, then set stations up - self.add_sta_data["flags"] = self.add_named_flags(self.desired_add_sta_flags, add_sta.add_sta_flags) - self.add_sta_data["radio"] = radio - self.add_sta_data["resource"] = resource - lf_r = LFRequest.LFRequest(self.lfclient_url + "/cli-json/add_sta") + self.add_sta_data["flags"] = self.add_named_flags(self.desired_add_sta_flags, add_sta.add_sta_flags) + self.add_sta_data["flags_mask"] = self.add_named_flags(self.desired_add_sta_flags_mask, add_sta.add_sta_flags) + self.add_sta_data["radio"] = radio + self.add_sta_data["resource"] = resource + self.set_port_data["current_flags"] = self.add_named_flags(self.desired_set_port_current_flags, set_port.set_port_current_flags) + self.set_port_data["interest"] = self.add_named_flags(self.desired_set_port_interest_flags, set_port.set_port_interest_flags) + + add_sta_r = LFRequest.LFRequest(self.lfclient_url + "/cli-json/add_sta") + set_port_r = LFRequest.LFRequest(self.lfclient_url + "/cli-json/set_port") + sta_names = [] for num in range(num_stations): - self.add_sta_data["sta_name"] = f"sta{num:05}" - lf_r.addPostData(self.add_sta_data) - json_response = lf_r.jsonPost(True) + sta_name = self.format_sta_name(num) + self.add_sta_data["sta_name"] = sta_name + self.set_port_data["port"] = sta_name + sta_names.append(sta_name) + if debug: + print(f"- 381 - {sta_name}- - - - - - - - - - - - - - - - - - ") + pprint(self.add_sta_data) + pprint(self.set_port_data) + print("- ~381 - - - - - - - - - - - - - - - - - - - ") + if dry_run: + print("dry run") + continue + add_sta_r.addPostData(self.add_sta_data) + json_response = add_sta_r.jsonPost(debug) + set_port_r.addPostData(self.set_port_data) + json_response = set_port_r.jsonPost(debug) + time.sleep(0.03) + + LFUtils.waitUntilPortsAppear(resource, self.lfclient_url, sta_names) + # and set ports up + if dry_run: + return + for sta_name in sta_names: + req = LFUtils.portUpRequest(resource, sta_name, debug_on=False) + set_port_r.addPostData(req) + json_response = set_port_r.jsonPost(debug) + time.sleep(0.03) + print(f"created {num} stations") #