1717import os
1818import re
1919import webbrowser
20+ import hashlib
2021
2122from collections import OrderedDict
2223
4344
4445
4546class Command (BaseCommand ):
46-
4747 NAME = "measure"
4848
4949 DESCRIPTION = "Create a measurement and optionally wait for the results"
@@ -61,14 +61,12 @@ class Command(BaseCommand):
6161 )
6262
6363 def __init__ (self , * args , ** kwargs ):
64-
6564 self ._type = None
6665 self ._is_oneoff = True
6766
6867 BaseCommand .__init__ (self , * args , ** kwargs )
6968
7069 def _modify_parser_args (self , args ):
71-
7270 kinds = self .CREATION_CLASSES .keys ()
7371 error = (
7472 "Usage: ripe-atlas measure <{}> [options]\n "
@@ -90,7 +88,6 @@ def _modify_parser_args(self, args):
9088 return BaseCommand ._modify_parser_args (self , args )
9189
9290 def add_arguments (self ):
93-
9491 self .parser .add_argument (
9592 "--renderer" ,
9693 choices = Renderer .get_available (),
@@ -238,6 +235,43 @@ def add_arguments(self):
238235 help = "Exclude probes that are marked with these tags. "
239236 "Example: --exclude-tag=system-ipv6-works" ,
240237 )
238+ self .add_flag (
239+ parser = self .parser ,
240+ name = "auto-topup" ,
241+ default = conf ["specification" ]["auto_topup" ],
242+ help = "Automatic top-up measurements probes."
243+ "Applicable to periodic measurements only." ,
244+ )
245+ self .parser .add_argument (
246+ "--auto-topup-prb-days-off" ,
247+ type = ArgumentType .integer_range (1 , 30 ),
248+ default = conf ["specification" ]["auto_topup_prb_days_off" ],
249+ help = "Threshold in days to replace a disconnected probe."
250+ "Applicable to periodic measurements only."
251+ "Example: --auto-topup-prb-days-off=7" ,
252+ )
253+ self .parser .add_argument (
254+ "--auto-topup-prb-similarity" ,
255+ type = ArgumentType .float_range (minimum = 0 , maximum = 1 ),
256+ default = conf ["specification" ]["auto_topup_prb_similarity" ],
257+ help = "Minimum similarity for replacement probes"
258+ "Applicable to periodic measurements only."
259+ "Example: --auto-topup-prb-similarity=0.5" ,
260+ )
261+ self .parser .add_argument (
262+ "--target-update-hours" ,
263+ type = ArgumentType .integer_range (22 , 720 ),
264+ default = conf ["specification" ]["target_update_hours" ],
265+ help = "Number of hours to re-lookup a target DNS record."
266+ "Example: --target-update-hours=24" ,
267+ )
268+ self .parser .add_argument (
269+ "--aggregator-client-id" ,
270+ type = str ,
271+ default = conf ["specification" ]["aggregator_client_id" ],
272+ help = "Client ID for measurement aggregators."
273+ "The value is hashed on transmission." ,
274+ )
241275
242276 self .parser .add_argument (
243277 "--group-id" ,
@@ -280,7 +314,6 @@ def add_arguments(self):
280314 Renderer .add_arguments_for_available_renderers (self .parser )
281315
282316 def run (self ) -> None :
283-
284317 self ._account_for_selected_probes ()
285318
286319 if self .arguments .dry_run :
@@ -315,7 +348,6 @@ def run(self) -> None:
315348 self .stream (msm_id )
316349
317350 def dry_run (self ):
318-
319351 print (colourise ("\n Definitions:\n {}" .format ("=" * 80 ), "bold" ))
320352
321353 for param , val in self ._get_measurement_kwargs ().items ():
@@ -371,7 +403,6 @@ def stream(self, msm_id: int) -> None:
371403 self .ok ("Disconnected from stream" )
372404
373405 def clean_target (self ):
374-
375406 if not self .arguments .target :
376407 raise RipeAtlasToolsException (
377408 "You must specify a target for that kind of measurement"
@@ -389,7 +420,6 @@ def clean_description(self):
389420 )
390421
391422 def _get_measurement_kwargs (self ):
392-
393423 # This is kept apart from the r = {} because dns measurements don't
394424 # require a target attribute
395425 target = self .clean_target ()
@@ -404,6 +434,16 @@ def _get_measurement_kwargs(self):
404434 r ["interval" ] = self .arguments .interval
405435 self ._is_oneoff = False
406436 self .arguments .no_report = True
437+ # auto-topup is applicable only to periodic measurements
438+ if self .arguments .auto_topup is not None :
439+ r ["auto_topup" ] = self .arguments .auto_topup
440+ if self .arguments .auto_topup_prb_days_off is not None :
441+ r ["auto_topup_prb_days_off" ] = self .arguments .auto_topup_prb_days_off
442+ if self .arguments .auto_topup_prb_similarity is not None :
443+ r ["auto_topup_prb_similarity" ] = (
444+ self .arguments .auto_topup_prb_similarity
445+ )
446+
407447 elif not spec ["times" ]["one-off" ]:
408448 raise RipeAtlasToolsException (
409449 "Your configuration file appears to be setup to not create "
@@ -427,10 +467,17 @@ def _get_measurement_kwargs(self):
427467 if self .arguments .resolve_on_probe is not None :
428468 r ["resolve_on_probe" ] = self .arguments .resolve_on_probe
429469
470+ if self .arguments .target_update_hours :
471+ r ["target_update_hours" ] = self .arguments .target_update_hours
472+
473+ if self .arguments .aggregator_client_id :
474+ r ["aggregator_client_id" ] = hashlib .sha256 (
475+ self .arguments .aggregator_client_id .encode ("utf-8" )
476+ ).hexdigest ()
477+
430478 return r
431479
432480 def _get_source_kwargs (self ):
433-
434481 r = conf ["specification" ]["source" ]
435482
436483 r ["requested" ] = self .arguments .probes
@@ -505,7 +552,6 @@ def _account_for_selected_probes(self):
505552
506553 @staticmethod
507554 def _handle_api_error (response ):
508-
509555 message = "There was a problem communicating with the RIPE Atlas API."
510556
511557 if isinstance (response , dict ):
@@ -517,7 +563,6 @@ def _handle_api_error(response):
517563 "using:\n \n "
518564 " ripe-atlas configure --set authorisation.create=MY_API_KEY\n "
519565 )
520-
521566 message += f"\n \n { json .dumps (response , indent = 2 )} "
522567
523568 raise RipeAtlasToolsException (message )
0 commit comments