1954a0d61ae644f0b8a847fabd75094d68d9e06a
[integration/test.git] / tools / fastbgp / manage_play.py
1 """Utility for running several play.py utilities in parallel.
2
3 Only a subset of play.py functionality is fully supported.
4 Notably, listening play.py is not tested yet.
5
6 Only one hardcoded strategy is there to distinguish peers.
7 File play.py is to be present in current working directory.
8
9 It needs to be run with sudo-able user when you want to use ports below 1024
10 as --myip.
11 """
12
13 # Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
14 #
15 # This program and the accompanying materials are made available under the
16 # terms of the Eclipse Public License v1.0 which accompanies this distribution,
17 # and is available at http://www.eclipse.org/legal/epl-v10.html
18
19 __author__ = "Vratko Polak"
20 __copyright__ = "Copyright(c) 2015, Cisco Systems, Inc."
21 __license__ = "Eclipse Public License v1.0"
22 __email__ = "vrpolak@cisco.com"
23
24
25 import argparse
26 import ipaddr
27 import multiprocessing
28 import subprocess
29
30
31 def main():
32     """Use argparse to get arguments, return mgr_args object."""
33
34     parser = argparse.ArgumentParser()
35     # TODO: Should we use --argument-names-with-spaces?
36     str_help = "Autonomous System number use in the stream (current default as in ODL: 64496)."
37     parser.add_argument("--asnumber", default=64496, type=int, help=str_help)
38     # FIXME: We are acting as iBGP peer, we should mirror AS number from peer's open message.
39     str_help = "Amount of IP prefixes to generate. Negative number is taken as an overflown positive."
40     parser.add_argument("--amount", default="1", type=int, help=str_help)
41     str_help = "The first IPv4 prefix to announce, given as numeric IPv4 address."
42     parser.add_argument("--firstprefix", default="8.0.1.0", type=ipaddr.IPv4Address, help=str_help)
43     str_help = "If present, this tool will be listening for connection, instead of initiating it."
44     parser.add_argument("--listen", action="store_true", help=str_help)
45     str_help = "How many play.py utilities are to be started."
46     parser.add_argument("--multiplicity", default="1", type=int, help=str_help)
47     str_help = "Numeric IP Address to bind to and derive BGP ID from, for the first player."
48     parser.add_argument("--myip", default="0.0.0.0", type=ipaddr.IPv4Address, help=str_help)
49     str_help = "TCP port to bind to when listening or initiating connection."
50     parser.add_argument("--myport", default="0", type=int, help=str_help)
51     str_help = "The IP of the next hop to be placed into the update messages."
52     parser.add_argument("--nexthop", default="192.0.2.1", type=ipaddr.IPv4Address, dest="nexthop", help=str_help)
53     str_help = "Numeric IP Address to try to connect to. Currently no effect in listening mode."
54     parser.add_argument("--peerip", default="127.0.0.2", type=ipaddr.IPv4Address, help=str_help)
55     str_help = "TCP port to try to connect to. No effect in listening mode."
56     parser.add_argument("--peerport", default="179", type=int, help=str_help)
57     # TODO: The step between IP prefixes is currently hardcoded to 16. Should we make it configurable?
58     # Yes, the argument list above is sorted alphabetically.
59     mgr_args = parser.parse_args()
60     # TODO: Are sanity checks (such as asnumber>=0) required?
61
62     if mgr_args.multiplicity < 1:
63         print "Multiplicity", mgr_args.multiplicity, "is not positive."
64         raise SystemExit(1)
65     amount_left = mgr_args.amount
66     utils_left = mgr_args.multiplicity
67     prefix_current = mgr_args.firstprefix
68     myip_current = mgr_args.myip
69     processes = []
70     while 1:
71         amount_per_util = (amount_left - 1) / utils_left + 1  # round up
72         amount_left -= amount_per_util
73         utils_left -= 1
74         util_args = ["python", "play.py"]
75         util_args.extend(["--asnumber", str(mgr_args.asnumber)])
76         util_args.extend(["--amount", str(amount_per_util)])
77         util_args.extend(["--firstprefix", str(prefix_current)])
78         if mgr_args.listen:
79             util_args.append("--listen")
80         util_args.extend(["--myip", str(myip_current)])
81         util_args.extend(["--myport", str(mgr_args.myport)])
82         util_args.extend(["--nexthop", str(mgr_args.nexthop)])
83         util_args.extend(["--peerip", str(mgr_args.peerip)])
84         util_args.extend(["--peerport", str(mgr_args.peerport)])
85         process = multiprocessing.Process(target=subprocess.call, args=[util_args])
86         # No shell, inherited std* file descriptors, same process group, so gets SIGINT.
87         processes.append(process)
88         if not utils_left:
89             break
90         prefix_current += amount_per_util * 16
91         myip_current += 1
92     for process in processes:
93         process.start()
94     # Usually processes run until SIGINT, but if an error happens we want to exit quickly.
95     for process in processes:
96         process.join()
97
98
99 if __name__ == "__main__":
100     main()