"""
    python config_slave.py 127.0.0.1 38000 38006 127.0.0.2 18999 18002

will generate 4 slave server configs accordingly. will be used in deployment automation to configure a cluster.
usage: python config_slave.py <host1> <port1> <port2> <host2> <port3> ...
"""
import argparse
import collections
import json
import os

FILE = "../../testnet/2/cluster_config_template.json"
if "QKC_CONFIG" in os.environ:
    FILE = os.environ["QKC_CONFIG"]


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "hostports",
        nargs="+",
        metavar="hostports",
        help="Host and ports for slave config",
    )
    args = parser.parse_args()

    abspath = os.path.abspath(__file__)
    dname = os.path.dirname(abspath)
    os.chdir(dname)

    ###############
    # parse hosts and ports to form a slave list
    ###############

    host_port_mapping = collections.defaultdict(list)
    last_host = None
    for host_or_port in args.hostports:  # type: str
        if not host_or_port.isdigit():  # host
            last_host = host_or_port
        else:  # port
            host_port_mapping[last_host].append(host_or_port)

    assert None not in host_port_mapping
    slave_num = sum(len(port_list) for port_list in host_port_mapping.values())
    # make sure number of slaves is power of 2
    assert slave_num > 0 and (slave_num & (slave_num - 1) == 0)

    slave_servers, i = [], 0
    for host, port_list in host_port_mapping.items():
        for port in port_list:
            s = {
                "HOST": host,
                "PORT": int(port),
                "ID": "S%d" % i,
                "CHAIN_MASK_LIST": [i | slave_num],
            }
            slave_servers.append(s)
            i += 1

    ###############
    # read config file and substitute with updated slave config
    ###############

    with open(FILE, "r+") as f:
        parsed_config = json.load(f)
        parsed_config["SLAVE_LIST"] = slave_servers
        f.seek(0)
        f.truncate()
        f.write(json.dumps(parsed_config, indent=4))


if __name__ == "__main__":
    main()

[((18, 13, 18, 38), 'argparse.ArgumentParser', 'argparse.ArgumentParser', ({}, {}), '()', False, 'import argparse\n'), ((27, 14, 27, 39), 'os.path.abspath', 'os.path.abspath', ({(27, 30, 27, 38): '__file__'}, {}), '(__file__)', False, 'import os\n'), ((28, 12, 28, 36), 'os.path.dirname', 'os.path.dirname', ({(28, 28, 28, 35): 'abspath'}, {}), '(abspath)', False, 'import os\n'), ((29, 4, 29, 19), 'os.chdir', 'os.chdir', ({(29, 13, 29, 18): 'dname'}, {}), '(dname)', False, 'import os\n'), ((35, 24, 35, 53), 'collections.defaultdict', 'collections.defaultdict', ({(35, 48, 35, 52): 'list'}, {}), '(list)', False, 'import collections\n'), ((65, 24, 65, 36), 'json.load', 'json.load', ({(65, 34, 65, 35): 'f'}, {}), '(f)', False, 'import json\n'), ((69, 16, 69, 51), 'json.dumps', 'json.dumps', (), '', False, 'import json\n')]