119119from decimal import Decimal
120120import jc .utils
121121from copy import deepcopy
122+ from jc .exceptions import ParseError
122123
123124
124125class info ():
@@ -164,7 +165,7 @@ class info():
164165SOFTWARE.
165166'''
166167
167- RE_HEADER = re .compile (r'(\S+)\s+\((\d+\.\d+\.\d+\.\d+|[0-9a-fA-F:]+)\)' )
168+ RE_HEADER = re .compile (r'traceroute6? to (\S+)\s+\((\d+\.\d+\.\d+\.\d+|[0-9a-fA-F:]+)\)' )
168169RE_PROBE_NAME_IP = re .compile (r'(\S+)\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[0-9a-fA-F:]+)\)+' )
169170RE_PROBE_IP_ONLY = re .compile (r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+([^\(])' )
170171RE_PROBE_IPV6_ONLY = re .compile (r'(([a-f0-9]*:)+[a-f0-9]+)' )
@@ -291,8 +292,23 @@ def _get_probes(hop_string: str):
291292 return probes
292293
293294
294- def _loads (data ):
295- lines = data .splitlines ()
295+ def _loads (data : str , quiet : bool ):
296+ lines = []
297+
298+ # remove any warning lines
299+ for data_line in data .splitlines ():
300+ if 'traceroute: Warning: ' not in data_line and 'traceroute6: Warning: ' not in data_line :
301+ lines .append (data_line )
302+ else :
303+ continue
304+
305+ # check if header row exists, otherwise add a dummy header
306+ if not lines [0 ].startswith ('traceroute to ' ) and not lines [0 ].startswith ('traceroute6 to ' ):
307+ lines [:0 ] = ['traceroute to <<_>> (<<_>>), 30 hops max, 60 byte packets' ]
308+
309+ # print warning to STDERR
310+ if not quiet :
311+ jc .utils .warning_message (['No header row found. For destination info redirect STDERR to STDOUT' ])
296312
297313 # Get headers
298314 match_dest = RE_HEADER .search (lines [0 ])
@@ -330,11 +346,28 @@ def _loads(data):
330346 return traceroute
331347
332348
333- class ParseError (Exception ):
334- pass
349+ ########################################################################################
335350
336351
337- ########################################################################################
352+ def _serialize_hop (hop : _Hop ):
353+ hop_obj = {}
354+ hop_obj ['hop' ] = str (hop .idx )
355+ probe_list = []
356+
357+ if hop .probes :
358+ for probe in hop .probes :
359+ probe_obj = {
360+ 'annotation' : probe .annotation ,
361+ 'asn' : None if probe .asn is None else str (probe .asn ),
362+ 'ip' : probe .ip ,
363+ 'name' : probe .name ,
364+ 'rtt' : None if probe .rtt is None else str (probe .rtt )
365+ }
366+ probe_list .append (probe_obj )
367+
368+ hop_obj ['probes' ] = probe_list
369+
370+ return hop_obj
338371
339372
340373def _process (proc_data ):
@@ -349,26 +382,20 @@ def _process(proc_data):
349382
350383 Dictionary. Structured to conform to the schema.
351384 """
352- int_list = {'hop' , 'asn' }
385+ int_list = {'hop' , 'asn' , 'max_hops' , 'data_bytes' }
353386 float_list = {'rtt' }
354387
355- if 'hops' in proc_data :
356- for entry in proc_data ['hops' ]:
357- for key in entry :
358- if key in int_list :
359- entry [key ] = jc .utils .convert_to_int (entry [key ])
360-
361- if key in float_list :
362- entry [key ] = jc .utils .convert_to_float (entry [key ])
388+ for entry in proc_data .get ('hops' , []):
389+ _process (entry )
390+ for entry in proc_data .get ('probes' , []):
391+ _process (entry )
363392
364- if 'probes' in entry :
365- for item in entry ['probes' ]:
366- for key in item :
367- if key in int_list :
368- item [key ] = jc .utils .convert_to_int (item [key ])
393+ for key in proc_data :
394+ if key in int_list :
395+ proc_data [key ] = jc .utils .convert_to_int (proc_data [key ])
369396
370- if key in float_list :
371- item [key ] = jc .utils .convert_to_float (item [key ])
397+ if key in float_list :
398+ proc_data [key ] = jc .utils .convert_to_float (proc_data [key ])
372399
373400 return proc_data
374401
@@ -393,48 +420,11 @@ def parse(data, raw=False, quiet=False):
393420 raw_output = {}
394421
395422 if jc .utils .has_data (data ):
396-
397- # remove any warning lines
398- new_data = []
399- for data_line in data .splitlines ():
400- if 'traceroute: Warning: ' not in data_line and 'traceroute6: Warning: ' not in data_line :
401- new_data .append (data_line )
402- else :
403- continue
404-
405- # check if header row exists, otherwise add a dummy header
406- if not new_data [0 ].startswith ('traceroute to ' ) and not new_data [0 ].startswith ('traceroute6 to ' ):
407- new_data [:0 ] = ['traceroute to <<_>> (<<_>>), 30 hops max, 60 byte packets' ]
408-
409- # print warning to STDERR
410- if not quiet :
411- jc .utils .warning_message (['No header row found. For destination info redirect STDERR to STDOUT' ])
412-
413- data = '\n ' .join (new_data )
414-
415- tr = _loads (data )
416- hops = tr .hops
423+ tr = _loads (data , quiet )
417424 hops_list = []
418425
419- if hops :
420- for hop in hops :
421- hop_obj = {}
422- hop_obj ['hop' ] = str (hop .idx )
423- probe_list = []
424-
425- if hop .probes :
426- for probe in hop .probes :
427- probe_obj = {
428- 'annotation' : probe .annotation ,
429- 'asn' : None if probe .asn is None else str (probe .asn ),
430- 'ip' : probe .ip ,
431- 'name' : probe .name ,
432- 'rtt' : None if probe .rtt is None else str (probe .rtt )
433- }
434- probe_list .append (probe_obj )
435-
436- hop_obj ['probes' ] = probe_list
437- hops_list .append (hop_obj )
426+ for hop in tr .hops :
427+ hops_list .append (_serialize_hop (hop ))
438428
439429 raw_output = {
440430 'destination_ip' : tr .dest_ip ,
0 commit comments