diff --git a/Readme.md b/Readme.md index 2e90cc3..2a91c15 100644 --- a/Readme.md +++ b/Readme.md @@ -2,11 +2,11 @@ This repo contains scripts to generate network models (in JSON) and waku configu ## generate_network.py -generate_network.py generates one network and per-node configuration files. The tool is configurable with specified number of nodes, topics, network types, node types. Use with Python3. Comment out the `#draw(fname, H)` line to visualise the generated graph. +generate_network.py generates one network and per-node configuration files. The tool is configurable with specified number of nodes, topics, network types, node types and number of subnets. Use with Python3. Comment out the `#draw(fname, H)` line to visualise the generated graph. > usage: $./generate_network --help ## batch_gen.sh -batch_gen.sh can generate given number of Waku networks and outputs them to a directory. Please make sure that the output directory does not exists; both relative and absolute paths work. The Wakunode parameters are generated at random; edit the MIN and MAX for finer control. The script requires bc & /dev/urandom.
+batch_gen.sh can generate given number of Waku networks and outputs them to a directory. Please make sure that the output directory does NOT exist; both relative and absolute paths work. The Wakunode parameters are generated at random; edit the MIN and MAX for finer control. The script requires bc & /dev/urandom.
> usage: $./batch_gen.sh <#number of networks needed>
diff --git a/batch_gen.sh b/batch_gen.sh index 3ea571b..bc0ffdf 100755 --- a/batch_gen.sh +++ b/batch_gen.sh @@ -30,8 +30,8 @@ mkdir -p $path echo "Ok, will generate $nfiles networks & put them under '$path'." -nwtype="NEWMANWATTSSTROGATZ" -nodetype="DESKTOP" +nwtype="newmanwattsstrogatz" +nodetype="desktop" for i in $(seq $nfiles) @@ -40,8 +40,11 @@ do n=$((RANDOM+1)) getrand t=$((RANDOM+1)) + getrand + s=`expr $((RANDOM+1)) % $n` + dirname="$path/$i/Waku" mkdir "$path/$i" - echo "Generating ./generate_network.py --dirname $dirname --num-nodes $n --num-topics $t --nw-type $nwtype --node-type $nodetype --num-partitions 1 ...." - $(./generate_network.py --dirname $dirname --num-nodes $n --num-topics $t --nw-type $nwtype --node-type $nodetype --num-partitions 1) + echo "Generating ./generate_network.py --dirname $dirname --num-nodes $n --num-topics $t --nw-type $nwtype --node-type $nodetype --num-partitions 1 --num-subnets $s ...." + $(./generate_network.py --dirname $dirname --num-nodes $n --num-topics $t --nw-type $nwtype --node-type $nodetype --num-partitions 1 --num-subnets $s) done diff --git a/generate_network.py b/generate_network.py index c40045d..595ef27 100755 --- a/generate_network.py +++ b/generate_network.py @@ -35,7 +35,8 @@ class networkType(Enum): NW_DATA_FNAME = "network_data.json" -PREFIX = "waku_" +NODE_PREFIX = "waku" +SUBNET_PREFIX = "subnetwork" ### I/O related fns ############################################################## @@ -53,7 +54,7 @@ def write_toml(dirname, node_name, toml): f.write(toml) -# Draw the network and output the image to a file +# Draw the network and output the image to a file; does not account for subnets yet def draw(dirname, H): nx.draw(H, pos=nx.kamada_kawai_layout(H), with_labels=True) fname = os.path.join(dirname, NW_DATA_FNAME) @@ -68,7 +69,8 @@ def read_json(fname): return nx.node_link_graph(jdata) -def exists_and_nonempty(dirname): +# check if the required dir can be created +def exists_or_nonempty(dirname): if not os.path.exists(dirname): return False elif not os.path.isfile(dirname) and os.listdir(dirname): @@ -150,18 +152,39 @@ networkTypeSwitch = { # Generate the network from nw type -def generate_network(n, nw_type): - return postprocess_network(networkTypeSwitch.get(nw_type)(n)) +def generate_network(n, network_type): + return postprocess_network(networkTypeSwitch.get(network_type)(n)) # Label the generated network with prefix def postprocess_network(G): G = nx.Graph(G) # prune out parallel/multi edges G.remove_edges_from(nx.selfloop_edges(G)) # remove the self-loops - mapping = {i: f"{PREFIX}{i}" for i in range(len(G))} + mapping = {i: f"{NODE_PREFIX}_{i}" for i in range(len(G))} return nx.relabel_nodes(G, mapping) # label the nodes +def generate_subnets(G, num_subnets): + n = len(G.nodes) + if num_subnets == n: # if num_subnets == size of the network + return {f"{NODE_PREFIX}_{i}": f"{SUBNET_PREFIX}_{i}" for i in range(n)} + + lst = list(range(n)) + random.shuffle(lst) + offsets = sorted(random.sample(range(0, n), num_subnets - 1)) + offsets.append(n-1) + + start = 0 + subnets = {} + subnet_id = 0 + for end in offsets: + for i in range(start, end+1): + subnets[f"{NODE_PREFIX}_{lst[i]}"] = f"{SUBNET_PREFIX}_{subnet_id}" + start = end + subnet_id += 1 + return subnets + + ### file format related fns ########################################################### #Generate per node toml configs def generate_toml(topics, node_type=nodeType.DESKTOP): @@ -170,40 +193,47 @@ def generate_toml(topics, node_type=nodeType.DESKTOP): # Generates network-wide json and per-node toml and writes them -def generate_and_write_files(dirname, num_topics, H): +def generate_and_write_files(dirname, num_topics, num_subnets, G): topics = generate_topics(num_topics) + subnets = generate_subnets(G, num_subnets) json_dump = {} - for node in H.nodes: + for node in G.nodes: write_toml(dirname, node, generate_toml(topics)) # per node toml json_dump[node] = {} json_dump[node]["static-nodes"] = [] - for edge in H.edges(node): + for edge in G.edges(node): json_dump[node]["static-nodes"].append(edge[1]) + json_dump[node][SUBNET_PREFIX] = subnets[node] write_json(dirname, json_dump) # network wide json ### the main ########################################################################## def main( dirname: str = "WakuNetwork", num_nodes: int = 4, num_topics: int = 1, - nw_type: networkType = networkType.NEWMANWATTSSTROGATZ.value, + network_type: networkType = networkType.NEWMANWATTSSTROGATZ.value, node_type: nodeType = nodeType.DESKTOP.value, + num_subnets: int = -1, num_partitions: int = 1): + # sanity checks if num_partitions > 1: - print(f"--num-partitions {num_partitions}, Sorry, we do not yet support partitions") - sys.exit(1) + raise ValueError(f"--num-partitions {num_partitions}, Sorry, we do not yet support partitions") + if num_subnets > num_nodes: + raise ValueError(f"num_subnets must be <= num_nodes: num_subnets={num_subnets}, num_nodes={num_nodes}") + if num_subnets == -1: + num_subnets = num_nodes # Generate the network - G = generate_network(num_nodes, nw_type) + G = generate_network(num_nodes, network_type) # Refuse to overwrite non-empty dirs - if exists_and_nonempty(dirname) : + if exists_or_nonempty(dirname) : sys.exit(1) os.makedirs(dirname, exist_ok=True) # Generate file format specific data structs and write the files; optionally, draw the network - generate_and_write_files(dirname, num_topics, G) - draw(dirname, G) + generate_and_write_files(dirname, num_topics, num_subnets, G) + #draw(dirname, G) if __name__ == "__main__":