1+ from argparse import ArgumentParser
2+
3+ from pavilion import output
4+ from pavilion .errors import TestSeriesError
5+ from pavilion .config import PavConfig
6+ from pavilion .series .series import TestSeries
7+ from pavilion .schedulers .config import parse_node_range
8+ from .base_classes import Command
9+
10+
11+ class BisectCommand (Command ):
12+ """Identify bad nodes by using a test to perform a binary search."""
13+
14+ def __init__ (self ):
15+ super ().__init__ (
16+ 'bisect' ,
17+ 'Perform a bisection search on a given set of nodes using a particular test.' ,
18+ short_help = "Perform a bisection search."
19+ )
20+
21+ def _setup_arguments (self , parser : ArgumentParser ) -> None :
22+ """Set up the parser arguments."""
23+
24+ parser .add_argument ("test_name" , type = str ,
25+ help = "The name of the test to use to bisect the nodes." )
26+ parser .add_argument ("nodes" , type = str , default = "" ,
27+ help = "The set of nodes with which to start the search." )
28+ parser .add_argument (
29+ '-p' , '--platform' , action = 'store' ,
30+ help = 'The platform to configure this test for. If not '
31+ 'specified, the current platform as denoted by the sys '
32+ 'plugin \' platform\' is used.' )
33+ parser .add_argument (
34+ '-H' , '--host' , action = 'store' ,
35+ help = 'The host to configure this test for. If not specified, the '
36+ 'current host as denoted by the sys plugin \' sys_host\' is '
37+ 'used. Host configurations are overlayed on operating system '
38+ 'configurations.' )
39+ parser .add_argument (
40+ '-n' , '--name' , action = 'store' , default = ''
41+ )
42+ parser .add_argument (
43+ '-m' , '--mode' , action = 'append' , dest = 'modes' , default = [],
44+ help = 'Mode configurations to overlay on the host configuration for '
45+ 'each test. These are overlayed in the order given.' )
46+ parser .add_argument (
47+ '-c' , dest = 'overrides' , action = 'append' , default = [],
48+ help = 'Overrides for specific configuration options. These are '
49+ 'gathered used as a final set of overrides before the '
50+ 'configs are resolved. They should take the form '
51+ '\' key=value\' , where key is the dot separated key name, '
52+ 'and value is a json object. Example: `-c schedule.nodes=23`' )
53+
54+ def run (pav_cfg : PavConfig , args : Namespace ) -> int :
55+ """Run the bisection search."""
56+
57+ try :
58+ nodes = self ._parse_nodes (args .nodes )
59+ except ValueError :
60+ output .fprint (self .errfile , f"Error parsing node list { args .nodes } ." )
61+
62+ return 1
63+
64+ # 1. Split the set of nodes in half.
65+ num_nodes = len (nodes )
66+ first_half = nodes [:num_nodes ]
67+ second_half = nodes [num_nodes :]
68+
69+ # 2. Run the test on each set of nodes
70+ series_cfg = generate_series_config (
71+ name = "bisect" ,
72+ modes = args .modes ,
73+ platform = args .platform ,
74+ host = args .host ,
75+ overrides = args .overrides ,
76+ ignore_errors = args .ignore_errors ,
77+ )
78+
79+ series_obj = TestSeries (pav_cfg , series_cfg = series_cfg , outfile = self .outfile )
80+ testset_name = cmd_utils .get_testset_name (pav_cfg , [args .test_name ], [])
81+
82+ series_obj .add_test_set_config (
83+ testset_name ,
84+ [args .test_name ],
85+ modes = args .modes ,
86+ )
87+
88+ self .last_series = series_obj
89+
90+ try :
91+ series_obj .run (
92+ rebuild = args .rebuild ,
93+ local_builds_only = local_builds_only ,
94+ log_results = log_results )
95+ self .last_tests = list (series_obj .tests .values ())
96+ except TestSeriesError as err :
97+ self .last_tests = list (series_obj .tests .values ())
98+ output .fprint (self .errfile , err , color = output .RED )
99+
100+ return 2
101+
102+ # 3. Wait for tests to finish
103+ series_obj .wait ()
104+
105+ # 4. Choose test with failed nodes and repeat
106+
107+ return 0
108+
109+ @staticmethod
110+ def _parse_nodes (nodes : str ) -> List [str ]:
111+ """Parse a list of nodes into individual nodes."""
112+
113+ nodes = []
114+
115+ ranges = nodes .split ("," )
116+
117+ for rng in ranges :
118+ nodes .append (parse_node_range (rng ))
119+
120+ return nodes
0 commit comments