1+ from prometheus_client .parser import text_string_to_metric_families
2+ from urllib .parse import urlparse
3+ import requests
14import atexit
25import os
36import random
710import uuid
811from collections import defaultdict
912from test .perf import ping
10- from test .perf .utils import random_port
11- from test .perf .utils import read_and_parse_dot
13+ from test .perf .utils import random_port , net_check
14+ from test .perf .utils import read_and_parse_metrics
15+ from test .perf .utils import Conn
1216
1317import attr
1418import yaml
@@ -84,18 +88,21 @@ def _construct_run_command(self):
8488
8589 return st
8690
87- def start (self ):
88- try :
89- os .remove (f"graph_{ self .name } .dot" )
90- os .sync ()
91- except FileNotFoundError :
92- print (f"DIND'T FIND IT graph_{ self .name } .dot" )
91+ def start (self , wait_for_ports = True ):
9392 print (f"{ time .time ()} starting { self .name } ({ self .uuid } )" )
9493 op = subprocess .Popen (
9594 " " .join (self ._construct_run_command ()), shell = True , preexec_fn = os .setsid
9695 )
9796 procs [self .uuid ] = op
9897
98+ if wait_for_ports :
99+ self .wait_for_ports ()
100+
101+ def wait_for_ports (self ):
102+ wait_for (net_check , func_args = [self .port , self .hostname , True ], num_sec = 10 )
103+ if self .stats_enable :
104+ wait_for (net_check , func_args = [self .stats_port , self .hostname , True ], num_sec = 10 )
105+
99106 def stop (self ):
100107 print (f"{ time .time ()} killing { self .name } ({ self .uuid } )" )
101108 try :
@@ -105,22 +112,32 @@ def stop(self):
105112 procs [self .uuid ].wait ()
106113 print (f"Service was kill { procs [self .uuid ].returncode } " )
107114
108- def get_debug_dot (self ):
109- try :
110- with open (f"graph_{ self .name } .dot" ) as f :
111- dot_data = f .read ()
112- print (f"FILE FOUND: graph_{ self .name } .dot" )
113- return dot_data
114- except FileNotFoundError :
115- print (f"FILE NOT FOUND: graph_{ self .name } .dot" )
116- return ""
115+ @property
116+ def hostname (self ):
117+ return urlparse (self .listen ).hostname
118+
119+ @property
120+ def port (self ):
121+ return urlparse (self .listen ).port
122+
123+ def get_metrics (self ):
124+ stats = requests .get (f"http://{ self .hostname } :{ self .stats_port } /metrics" )
125+ metrics = {
126+ metric .name : metric
127+ for metric in text_string_to_metric_families (stats .text )
128+ }
129+ return metrics
130+
131+ def get_routes (self ):
132+ routes = self .get_metrics ()['routing_table_info' ].samples [0 ].labels ['edges' ]
133+ return read_and_parse_metrics (routes )
117134
118135 def validate_routes (self ):
119136 print (f"****====TRYING COMPARE { self .name } " )
120- dot1 = self .get_debug_dot ()
121- dot2 = self .topology .generate_dot ()
122- if dot1 and dot2 :
123- return self .topology .compare_dot ( dot1 , dot2 )
137+ node_routes = self .get_routes ()
138+ control_routes = self .topology .generate_routes ()
139+ if node_routes and control_routes :
140+ return self .topology .compare_routes ( node_routes , control_routes )
124141 else :
125142 return False
126143
@@ -175,7 +192,7 @@ def ping(self, count, peer=None, node_ping_name="ping_node"):
175192class PingNode (Node ):
176193 node_type = PING
177194
178- def start (self ):
195+ def start (self , * args ):
179196 return
180197
181198 def stop (self ):
@@ -184,10 +201,7 @@ def stop(self):
184201 def validate_routes (self ):
185202 return True
186203
187- def get_debug_dot (self ):
188- raise NotImplementedError
189-
190- def ping (self ):
204+ def ping (self , * args ):
191205 raise NotImplementedError
192206
193207 def create_from_config (config ):
@@ -300,26 +314,25 @@ def dump_yaml(self, filename=".last-topology.yaml"):
300314
301315 yaml .dump (data , f )
302316
303- def dump_dot (self , filename = ".last-topology-graph.dot" ):
304- with open (filename , "w" ) as f :
305- f .write (self .generate_dot ())
306-
307- def generate_dot (self ):
308- dot_data = "graph {"
317+ def generate_routes (self ):
318+ routes = set ()
309319 for node , node_data in self .nodes .items ():
310320 for conn in node_data .connections :
311- dot_data += f"{ node } -- { conn } ; "
312- dot_data += "}"
313- return dot_data
321+ routes .add (
322+ Conn (node_data .name , conn , 1 )
323+ )
324+ return routes
314325
315326 def start (self , wait = True ):
316327 self .dump_yaml ()
317- self .dump_dot ()
318328
319329 for k , node in self .nodes .items ():
320- node .start ()
330+ node .start (wait_for_ports = not wait )
321331
322332 if wait :
333+ print ("Waiting for nodes" )
334+ for _ , node in self .nodes .items ():
335+ node .wait_for_ports ()
323336 wait_for (self .validate_all_node_routes , delay = 6 , num_sec = 30 )
324337 # for name, node in self.nodes.items():
325338 # wait_for(lambda: node.validate_routes)
@@ -365,20 +378,15 @@ def validate_ping_results(results, threshold=0.1):
365378 return valid
366379
367380 @staticmethod
368- def compare_dot (dot1 , dot2 ):
369- try :
370- ds1 = read_and_parse_dot (dot1 )
371- ds2 = read_and_parse_dot (dot2 )
372- if ds1 != ds2 :
373- print (f"****====MATCH FAIL" )
374- print (ds1 )
375- print (ds2 )
376- return False
377- else :
378- print ("****====MATCH" )
379- return True
380- except ParseException :
381+ def compare_routes (route1 , route2 ):
382+ if route1 != route2 :
383+ print (f"****====MATCH FAIL" )
384+ print (route1 )
385+ print (route2 )
381386 return False
387+ else :
388+ print ("****====MATCH" )
389+ return True
382390
383391 def validate_all_node_routes (self ):
384392 return all (
0 commit comments