blob: f5184f292503462b8c00c443435d75a57eef34fd [file] [log] [blame]
#!/usr/bin/env python
"""
usage: onos-gen-partitions [-h] [-s PARTITION_SIZE] [-n NUM_PARTITIONS]
[filename] [node_ip [node_ip ...]]
Generate the partitions json file given a list of IPs or from the $OC*
environment variables.
positional arguments:
filename File to write output to. If none is provided, output
is written to stdout.
node_ip IP Address(es) of the node(s) in the cluster. If no
IPs are given, will use the $OC* environment
variables. NOTE: these arguemnts are only processed
after the filename argument.
optional arguments:
-h, --help show this help message and exit
-s PARTITION_SIZE, --partition-size PARTITION_SIZE
Number of nodes per partition. Note that partition
sizes smaller than 3 are not fault tolerant. Defaults
to 3.
-n NUM_PARTITIONS, --num-partitions NUM_PARTITIONS
Number of partitions. Defaults to the number of nodes
in the cluster.
"""
from os import environ
from collections import deque
import argparse
import re
import json
import hashlib
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
def get_OC_vars():
vars = []
for var in environ:
if re.match(r"OC[0-9]+", var):
vars.append(var)
return sorted(vars, key=alphanum_key)
def get_nodes(ips=None, port=9876):
node = lambda k: { 'id': k, 'ip': k, 'port': port }
if not ips:
ips = [ environ[v] for v in get_OC_vars() ]
return [ node(v) for v in ips ]
def generate_partitions(nodes, k, n):
l = deque(nodes)
perms = []
for i in range(1, n+1):
part = {
'id': i,
'members': list(l)[:k]
}
perms.append(part)
l.rotate(-1)
return perms
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="Generate the partitions json file given a list of IPs or from the $OC* environment variables.")
parser.add_argument(
'-s', '--partition-size', type=int, default=3,
help="Number of nodes per partition. Note that partition sizes smaller than 3 are not fault tolerant. Defaults to 3." )
parser.add_argument(
'-n', '--num-partitions', type=int,
help="Number of partitions. Defaults to the number of nodes in the cluster." )
# TODO: make filename and nodes independent. This will break backwards compatibility with existing usage.
parser.add_argument(
'filename', metavar='filename', type=str, nargs='?',
help='File to write output to. If none is provided, output is written to stdout.')
parser.add_argument(
'nodes', metavar='node_ip', type=str, nargs='*',
help='IP Address(es) of the node(s) in the cluster. If no IPs are given, ' +
'will use the $OC* environment variables. NOTE: these arguemnts' +
' are only processed after the filename argument.')
args = parser.parse_args()
filename = args.filename
partition_size = args.partition_size
nodes = get_nodes(args.nodes)
num_partitions = args.num_partitions
if not num_partitions:
num_partitions = len(nodes)
partitions = generate_partitions([v.get('id') for v in nodes], partition_size, num_partitions)
m = hashlib.sha256()
for node in nodes:
m.update(node['ip'])
name = int(m.hexdigest()[:8], base=16) # 32-bit int based on SHA256 digest
data = {
'name': name,
'nodes': nodes,
'partitions': partitions
}
output = json.dumps(data, indent=4)
if filename:
with open(filename, 'w') as f:
f.write(output)
else:
print output