60
60
import sys
61
61
import time
62
62
63
+ import overlay_survey .simulation as sim
64
+
65
+ # A SurveySimulation, if running in simulation mode, or None otherwise.
66
+ SIMULATION = None
67
+
68
+ def get_request (url , params = None ):
69
+ """ Make a GET request, or simulate one if running in simulation mode. """
70
+ if SIMULATION :
71
+ return SIMULATION .get (url = url , params = params )
72
+ else :
73
+ return requests .get (url = url , params = params )
63
74
64
75
def next_peer (direction_tag , node_info ):
65
76
if direction_tag in node_info and node_info [direction_tag ]:
@@ -140,7 +151,7 @@ def send_requests(peer_list, params, request_url):
140
151
# Submit `limit` queries roughly every ledger
141
152
for key in peer_list :
142
153
params ["node" ] = key
143
- requests . get (url = request_url , params = params )
154
+ get_request (url = request_url , params = params )
144
155
print ("Send request to %s" % key )
145
156
global request_count
146
157
request_count += 1
@@ -224,8 +235,8 @@ def get_tier1_stats(augmented_directed_graph):
224
235
225
236
def augment (args ):
226
237
graph = nx .read_graphml (args .graphmlInput )
227
- data = requests . get ("https://api.stellarbeat.io/v1/nodes" ).json ()
228
- transitive_quorum = requests . get (
238
+ data = get_request ("https://api.stellarbeat.io/v1/nodes" ).json ()
239
+ transitive_quorum = get_request (
229
240
"https://api.stellarbeat.io/v1/" ).json ()["transitiveQuorumSet" ]
230
241
231
242
for obj in data :
@@ -273,6 +284,13 @@ def run_survey(args):
273
284
"inboundPeers" : {},
274
285
"outboundPeers" : {}
275
286
})
287
+ if args .simulate :
288
+ global SIMULATION
289
+ try :
290
+ SIMULATION = sim .SurveySimulation (args .simGraph , args .simRoot )
291
+ except sim .SimulationError as e :
292
+ print (f"Error: { e } " )
293
+ sys .exit (1 )
276
294
277
295
url = args .node
278
296
@@ -285,7 +303,7 @@ def run_survey(args):
285
303
params = {'duration' : duration }
286
304
287
305
# reset survey
288
- requests . get (url = stop_survey )
306
+ get_request (url = stop_survey )
289
307
290
308
peer_list = set ()
291
309
if args .nodeList :
@@ -296,7 +314,7 @@ def run_survey(args):
296
314
297
315
peers_params = {'fullkeys' : "true" }
298
316
299
- peers = requests . get (url = peers , params = peers_params ).json ()[
317
+ peers = get_request (url = peers , params = peers_params ).json ()[
300
318
"authenticated_peers" ]
301
319
302
320
# seed initial peers off of /peers endpoint
@@ -307,9 +325,10 @@ def run_survey(args):
307
325
for peer in peers ["outbound" ]:
308
326
peer_list .add (peer ["id" ])
309
327
310
- self_name = requests .get (url + "/scp?limit=0&fullkeys=true" ).json ()["you" ]
328
+ scp_params = {'fullkeys' : "true" , 'limit' : 0 }
329
+ self_name = get_request (url + "/scp" , scp_params ).json ()["you" ]
311
330
graph .add_node (self_name ,
312
- version = requests . get (url + "/info" ).json ()["info" ]["build" ],
331
+ version = get_request (url + "/info" ).json ()["info" ]["build" ],
313
332
numTotalInboundPeers = len (peers ["inbound" ] or []),
314
333
numTotalOutboundPeers = len (peers ["outbound" ] or []))
315
334
@@ -328,7 +347,7 @@ def run_survey(args):
328
347
time .sleep (1 )
329
348
330
349
print ("Fetching survey result" )
331
- data = requests . get (url = survey_result ).json ()
350
+ data = get_request (url = survey_result ).json ()
332
351
print ("Done" )
333
352
334
353
if "topology" in data :
@@ -366,6 +385,12 @@ def run_survey(args):
366
385
print ("New peers: %s Retrying: %s" %
367
386
(new_peers , len (peer_list )- new_peers ))
368
387
388
+ # sanity check that simulation produced a graph isomorphic to the input
389
+ assert (not args .simulate or
390
+ nx .is_isomorphic (graph , nx .read_graphml (args .simGraph ))), \
391
+ ("Simulation produced a graph that is not isomorphic to the input "
392
+ "graph" )
393
+
369
394
if nx .is_empty (graph ):
370
395
print ("Graph is empty!" )
371
396
sys .exit (0 )
@@ -395,19 +420,8 @@ def flatten(args):
395
420
json .dump (output_graph , output_file )
396
421
sys .exit (0 )
397
422
398
-
399
- def main ():
400
- # construct the argument parse and parse the arguments
401
- argument_parser = argparse .ArgumentParser ()
402
- argument_parser .add_argument ("-gs" ,
403
- "--graphStats" ,
404
- help = "output file for graph stats" )
405
-
406
- subparsers = argument_parser .add_subparsers ()
407
-
408
- parser_survey = subparsers .add_parser ('survey' ,
409
- help = "run survey and "
410
- "analyze results" )
423
+ def init_parser_survey (parser_survey ):
424
+ """Initialize the `survey` subcommand"""
411
425
parser_survey .add_argument ("-n" ,
412
426
"--node" ,
413
427
required = True ,
@@ -429,6 +443,35 @@ def main():
429
443
help = "optional list of seed nodes" )
430
444
parser_survey .set_defaults (func = run_survey )
431
445
446
+ def main ():
447
+ # construct the argument parse and parse the arguments
448
+ argument_parser = argparse .ArgumentParser ()
449
+ argument_parser .add_argument ("-gs" ,
450
+ "--graphStats" ,
451
+ help = "output file for graph stats" )
452
+
453
+ subparsers = argument_parser .add_subparsers ()
454
+
455
+ parser_survey = subparsers .add_parser ('survey' ,
456
+ help = "run survey and "
457
+ "analyze results" )
458
+ parser_survey .set_defaults (simulate = False )
459
+ init_parser_survey (parser_survey )
460
+ parser_simulate = subparsers .add_parser ('simulate' ,
461
+ help = "simulate survey run" )
462
+ # `simulate` supports all arguments that `survey` does, plus some additional
463
+ # arguments for the simulation itself.
464
+ init_parser_survey (parser_simulate )
465
+ parser_simulate .add_argument ("-s" ,
466
+ "--simGraph" ,
467
+ required = True ,
468
+ help = "graphml file to simulate network from" )
469
+ parser_simulate .add_argument ("-r" ,
470
+ "--simRoot" ,
471
+ required = True ,
472
+ help = "node to start simulation from" )
473
+ parser_simulate .set_defaults (simulate = True )
474
+
432
475
parser_analyze = subparsers .add_parser ('analyze' ,
433
476
help = "write stats for "
434
477
"the graphml input graph" )
0 commit comments