blob: a982c36d05cca174c41f9219b5a1a7718fff97ca [file] [log] [blame]
Aaron Kruglikoveb0ae4e2015-11-10 19:16:16 -08001#!/usr/bin/env python
2"""
Thiago Santos9eb23c12016-09-20 14:03:34 -03003usage: onos-gen-partitions [-h] [-s PARTITION_SIZE] [-n NUM_PARTITIONS]
4 [filename] [node_ip [node_ip ...]]
Aaron Kruglikoveb0ae4e2015-11-10 19:16:16 -08005
Thiago Santos9eb23c12016-09-20 14:03:34 -03006Generate the partitions json file given a list of IPs or from the $OC*
7environment variables.
8
9positional arguments:
10 filename File to write output to. If none is provided, output
11 is written to stdout.
12 node_ip IP Address(es) of the node(s) in the cluster. If no
13 IPs are given, will use the $OC* environment
14 variables. NOTE: these arguemnts are only processed
15 after the filename argument.
16
17optional arguments:
18 -h, --help show this help message and exit
19 -s PARTITION_SIZE, --partition-size PARTITION_SIZE
20 Number of nodes per partition. Note that partition
21 sizes smaller than 3 are not fault tolerant. Defaults
22 to 3.
23 -n NUM_PARTITIONS, --num-partitions NUM_PARTITIONS
24 Number of partitions. Defaults to the number of nodes
25 in the cluster.
Aaron Kruglikoveb0ae4e2015-11-10 19:16:16 -080026"""
Brian O'Connor6e192432015-02-26 15:17:23 -080027
28from os import environ
Thiago Santos9eb23c12016-09-20 14:03:34 -030029from collections import deque
30import argparse
Brian O'Connor6e192432015-02-26 15:17:23 -080031import re
32import json
Thomas Vachuskab56b9172015-11-24 11:24:23 -080033import hashlib
Brian O'Connor6e192432015-02-26 15:17:23 -080034
Aaron Kruglikoveb0ae4e2015-11-10 19:16:16 -080035convert = lambda text: int(text) if text.isdigit() else text.lower()
36alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
Brian O'Connor6e192432015-02-26 15:17:23 -080037
38def get_OC_vars():
39 vars = []
40 for var in environ:
41 if re.match(r"OC[0-9]+", var):
42 vars.append(var)
43 return sorted(vars, key=alphanum_key)
44
Ray Milkeyff18b6e2017-07-13 08:34:19 -070045def get_nodes(ips=None, default_port=9876):
46 node = lambda id, ip, port : { 'id': id, 'ip': ip, 'port': port }
47 result = []
48 if not ips:
49 ips = [ environ[v] for v in get_OC_vars() ]
50 for ip_string in ips:
51 address_tuple = ip_string.split(":")
52 if len(address_tuple) == 3:
53 id=address_tuple[0]
54 ip=address_tuple[1]
55 port=int(address_tuple[2])
56 else:
57 id=ip_string
58 ip=ip_string
59 port=default_port
60 result.append(node(id, ip, port))
61 return result
Brian O'Connor6e192432015-02-26 15:17:23 -080062
Thiago Santos9eb23c12016-09-20 14:03:34 -030063def generate_partitions(nodes, k, n):
Brian O'Connor6e192432015-02-26 15:17:23 -080064 l = deque(nodes)
Madan Jampaniec1df022015-10-13 21:23:03 -070065 perms = []
Thiago Santos9eb23c12016-09-20 14:03:34 -030066 for i in range(1, n+1):
Madan Jampaniec1df022015-10-13 21:23:03 -070067 part = {
Madan Jampaniab7e7cd2016-01-14 14:02:32 -080068 'id': i,
Madan Jampaniec1df022015-10-13 21:23:03 -070069 'members': list(l)[:k]
70 }
71 perms.append(part)
Brian O'Connor6e192432015-02-26 15:17:23 -080072 l.rotate(-1)
Madan Jampaniec1df022015-10-13 21:23:03 -070073 return perms
Brian O'Connor6e192432015-02-26 15:17:23 -080074
75if __name__ == '__main__':
Thiago Santos9eb23c12016-09-20 14:03:34 -030076 parser = argparse.ArgumentParser(
77 description="Generate the partitions json file given a list of IPs or from the $OC* environment variables.")
78 parser.add_argument(
79 '-s', '--partition-size', type=int, default=3,
80 help="Number of nodes per partition. Note that partition sizes smaller than 3 are not fault tolerant. Defaults to 3." )
81 parser.add_argument(
82 '-n', '--num-partitions', type=int,
83 help="Number of partitions. Defaults to the number of nodes in the cluster." )
84 # TODO: make filename and nodes independent. This will break backwards compatibility with existing usage.
85 parser.add_argument(
86 'filename', metavar='filename', type=str, nargs='?',
87 help='File to write output to. If none is provided, output is written to stdout.')
88 parser.add_argument(
89 'nodes', metavar='node_ip', type=str, nargs='*',
90 help='IP Address(es) of the node(s) in the cluster. If no IPs are given, ' +
91 'will use the $OC* environment variables. NOTE: these arguemnts' +
92 ' are only processed after the filename argument.')
93
94 args = parser.parse_args()
95 filename = args.filename
96 partition_size = args.partition_size
97 nodes = get_nodes(args.nodes)
98 num_partitions = args.num_partitions
99 if not num_partitions:
100 num_partitions = len(nodes)
101
102 partitions = generate_partitions([v.get('id') for v in nodes], partition_size, num_partitions)
Brian O'Connorfb8a2752016-05-16 16:48:23 -0700103 m = hashlib.sha256()
Aaron Kruglikoveb0ae4e2015-11-10 19:16:16 -0800104 for node in nodes:
Brian O'Connorfb8a2752016-05-16 16:48:23 -0700105 m.update(node['ip'])
106 name = int(m.hexdigest()[:8], base=16) # 32-bit int based on SHA256 digest
Madan Jampaniec1df022015-10-13 21:23:03 -0700107 data = {
Aaron Kruglikoveb0ae4e2015-11-10 19:16:16 -0800108 'name': name,
Brian O'Connor6e192432015-02-26 15:17:23 -0800109 'nodes': nodes,
Aaron Kruglikoveb0ae4e2015-11-10 19:16:16 -0800110 'partitions': partitions
Brian O'Connor6e192432015-02-26 15:17:23 -0800111 }
112 output = json.dumps(data, indent=4)
113
Thiago Santos9eb23c12016-09-20 14:03:34 -0300114 if filename:
Brian O'Connor6e192432015-02-26 15:17:23 -0800115 with open(filename, 'w') as f:
116 f.write(output)
117 else:
118 print output