ktest: New features: noclean, dodie, poweroff on error and success
[linux-2.6.git] / tools / testing / ktest / ktest.pl
1 #!/usr/bin/perl -w
2
3 use strict;
4 use IPC::Open2;
5 use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
6 use FileHandle;
7
8 $#ARGV >= 0 || die "usage: autotest.pl config-file\n";
9
10 $| = 1;
11
12 my %opt;
13
14 #default opts
15 $opt{"NUM_BUILDS"}              = 5;
16 $opt{"DEFAULT_BUILD_TYPE"}      = "randconfig";
17 $opt{"MAKE_CMD"}                = "make";
18 $opt{"TIMEOUT"}                 = 50;
19 $opt{"TMP_DIR"}                 = "/tmp/autotest";
20 $opt{"SLEEP_TIME"}              = 60;   # sleep time between tests
21 $opt{"BUILD_NOCLEAN"}           = 0;
22 $opt{"POWEROFF_ON_ERROR"}       = 0;
23 $opt{"POWEROFF_ON_SUCCESS"}     = 0;
24
25 my $version;
26 my $install_mods;
27 my $grub_number;
28 my $target;
29 my $make;
30 my $noclean;
31
32 sub read_config {
33     my ($config) = @_;
34
35     open(IN, $config) || die "can't read file $config";
36
37     while (<IN>) {
38
39         # ignore blank lines and comments
40         next if (/^\s*$/ || /\s*\#/);
41
42         if (/^\s*(\S+)\s*=\s*(.*?)\s*$/) {
43             my $lvalue = $1;
44             my $rvalue = $2;
45
46             $opt{$lvalue} = $rvalue;
47         }
48     }
49
50     close(IN);
51 }
52
53 sub doprint {
54     print @_;
55
56     if (defined($opt{"LOG_FILE"})) {
57         open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}";
58         print OUT @_;
59         close(OUT);
60     }
61 }
62
63 sub dodie {
64     doprint "CRITICAL FAILURE... ", @_;
65
66     if ($opt{"POWEROFF_ON_ERROR"} && defined($opt{"POWER_OFF"})) {
67         doprint "POWERING OFF\n";
68         `$opt{"POWER_OFF"}`;
69     }
70     die @_;
71 }
72
73 sub run_command {
74     my ($command) = @_;
75     my $redirect = "";
76
77     if (defined($opt{"LOG_FILE"})) {
78         $redirect = " >> $opt{LOG_FILE} 2>&1";
79     }
80
81     doprint "$command ... ";
82     `$command $redirect`;
83
84     my $failed = $?;
85
86     if ($failed) {
87         doprint "FAILED!\n";
88     } else {
89         doprint "SUCCESS\n";
90     }
91
92     return $failed;
93 }
94
95 my $timeout = $opt{"TIMEOUT"};
96
97 sub wait_for_input
98 {
99     my ($fp, $time) = @_;
100     my $rin;
101     my $ready;
102     my $line;
103     my $ch;
104
105     if (!defined($time)) {
106         $time = $timeout;
107     }
108
109     $rin = '';
110     vec($rin, fileno($fp), 1) = 1;
111     $ready = select($rin, undef, undef, $time);
112
113     $line = "";
114
115     # try to read one char at a time
116     while (sysread $fp, $ch, 1) {
117         $line .= $ch;
118         last if ($ch eq "\n");
119     }
120
121     if (!length($line)) {
122         return undef;
123     }
124
125     return $line;
126 }
127
128 sub reboot {
129     run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'";
130 }
131
132 sub monitor {
133     my $flags;
134     my $booted = 0;
135     my $bug = 0;
136     my $pid;
137     my $doopen2 = 0;
138     my $skip_call_trace = 0;
139
140     if ($doopen2) {
141         $pid = open2(\*IN, \*OUT, $opt{CONSOLE});
142         if ($pid < 0) {
143             dodie "Failed to connect to the console";
144         }
145     } else {
146         $pid = open(IN, "$opt{CONSOLE} |");
147     }
148
149     $flags = fcntl(IN, F_GETFL, 0) or
150         dodie "Can't get flags for the socket: $!\n";
151
152     $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or
153         dodie "Can't set flags for the socket: $!\n";
154
155     my $line;
156     my $full_line = "";
157
158     doprint "Wait for monitor to settle down.\n";
159     # read the monitor and wait for the system to calm down
160     do {
161         $line = wait_for_input(\*IN, 5);
162     } while (defined($line));
163
164     reboot;
165
166     for (;;) {
167
168         $line = wait_for_input(\*IN);
169
170         last if (!defined($line));
171
172         doprint $line;
173
174         # we are not guaranteed to get a full line
175         $full_line .= $line;
176
177         if ($full_line =~ /login:/) {
178             $booted = 1;
179         }
180
181         if ($full_line =~ /\[ backtrace testing \]/) {
182             $skip_call_trace = 1;
183         }
184
185         if ($full_line =~ /call trace:/i) {
186             $bug = 1 if (!$skip_call_trace);
187         }
188
189         if ($full_line =~ /\[ end of backtrace testing \]/) {
190             $skip_call_trace = 0;
191         }
192
193         if ($full_line =~ /Kernel panic -/) {
194             $bug = 1;
195         }
196
197         if ($line =~ /\n/) {
198             $full_line = "";
199         }
200     }
201
202     doprint "kill child process $pid\n";
203     kill 2, $pid;
204
205     print "closing!\n";
206     close(IN);
207
208     if (!$booted) {
209         dodie "failed - never got a boot prompt.\n";
210     }
211
212     if ($bug) {
213         dodie "failed - got a bug report\n";
214     }
215 }
216
217 sub install {
218
219     if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") {
220         dodie "failed to copy image";
221     }
222
223     if ($install_mods) {
224         my $modlib = "/lib/modules/$version";
225         my $modtar = "autotest-mods.tar.bz2";
226
227         if (run_command "ssh $target rm -rf $modlib") {
228             dodie "failed to remove old mods: $modlib";
229         }
230
231         # would be nice if scp -r did not follow symbolic links
232         if (run_command "cd $opt{TMP_DIR}; tar -cjf $modtar lib/modules/$version") {
233             dodie "making tarball";
234         }
235
236         if (run_command "scp $opt{TMP_DIR}/$modtar $target:/tmp") {
237             dodie "failed to copy modules";
238         }
239
240         unlink "$opt{TMP_DIR}/$modtar";
241
242         if (run_command "ssh $target '(cd / && tar xf /tmp/$modtar)'") {
243             dodie "failed to tar modules";
244         }
245
246         run_command "ssh $target rm -f /tmp/$modtar";
247     }
248
249 }
250
251 sub build {
252     my ($type) = @_;
253     my $defconfig = "";
254     my $append = "";
255
256     # old config can ask questions
257     if ($type eq "oldconfig") {
258         $append = "yes ''|";
259         if (run_command "mv $opt{OUTPUT_DIR}/.config $opt{OUTPUT_DIR}/config_temp") {
260             dodie "moving .config";
261         }
262
263         if (!$noclean && run_command "$make mrproper") {
264             dodie "make mrproper";
265         }
266
267         if (run_command "mv $opt{OUTPUT_DIR}/config_temp $opt{OUTPUT_DIR}/.config") {
268             dodie "moving config_temp";
269         }
270
271     } elsif (!$noclean) {
272         unlink "$opt{OUTPUT_DIR}/.config";
273         if (run_command "$make mrproper") {
274             dodie "make mrproper";
275         }
276     }
277
278     # add something to distinguish this build
279     open(OUT, "> $opt{OUTPUT_DIR}/localversion") or dodie("Can't make localversion file");
280     print OUT "$opt{LOCALVERSION}\n";
281     close(OUT);
282
283     if (defined($opt{"MIN_CONFIG"})) {
284         $defconfig = "KCONFIG_ALLCONFIG=$opt{MIN_CONFIG}";
285     }
286
287     if (run_command "$defconfig $append $make $type") {
288         dodie "failed make config";
289     }
290
291     if (run_command "$make $opt{BUILD_OPTIONS}") {
292         dodie "failed build";
293     }
294 }
295
296 read_config $ARGV[0];
297
298 # mandatory configs
299 die "MACHINE not defined\n"             if (!defined($opt{"MACHINE"}));
300 die "SSH_USER not defined\n"            if (!defined($opt{"SSH_USER"}));
301 die "BUILD_DIR not defined\n"           if (!defined($opt{"BUILD_DIR"}));
302 die "OUTPUT_DIR not defined\n"          if (!defined($opt{"OUTPUT_DIR"}));
303 die "BUILD_TARGET not defined\n"        if (!defined($opt{"BUILD_TARGET"}));
304 die "POWER_CYCLE not defined\n"         if (!defined($opt{"POWER_CYCLE"}));
305 die "CONSOLE not defined\n"             if (!defined($opt{"CONSOLE"}));
306 die "LOCALVERSION not defined\n"        if (!defined($opt{"LOCALVERSION"}));
307 die "GRUB_MENU not defined\n"           if (!defined($opt{"GRUB_MENU"}));
308
309 chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}";
310
311 $target = "$opt{SSH_USER}\@$opt{MACHINE}";
312
313 doprint "\n\nSTARTING AUTOMATED TESTS\n";
314
315 doprint "Find grub menu ... ";
316 $grub_number = -1;
317 open(IN, "ssh $target cat /boot/grub/menu.lst |")
318     or die "unable to get menu.lst";
319 while (<IN>) {
320     if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) {
321         $grub_number++;
322         last;
323     } elsif (/^\s*title\s/) {
324         $grub_number++;
325     }
326 }
327 close(IN);
328 die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}"
329     if ($grub_number < 0);
330 doprint "$grub_number\n";
331
332 $make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}";
333
334 # First we need to do is the builds
335 for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) {
336     my $type = "BUILD_TYPE[$i]";
337
338     if (defined($opt{"BUILD_NOCLEAN[$i]"}) &&
339         $opt{"BUILD_NOCLEAN[$i]"} != 0) {
340         $noclean = 1;
341     } else {
342         $noclean = $opt{"BUILD_NOCLEAN"};
343     }
344
345     if (!defined($opt{$type})) {
346         $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"};
347     }
348
349     doprint "\n\n";
350     doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n";
351
352     if ($opt{$type} ne "nobuild") {
353         build $opt{$type};
354     }
355
356     # get the release name
357     doprint "$make kernelrelease ... ";
358     $version = `$make kernelrelease | tail -1`;
359     chomp($version);
360     doprint "$version\n";
361
362     # should we process modules?
363     $install_mods = 0;
364     open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file");
365     while (<IN>) {
366         if (/CONFIG_MODULES(=y)?/) {
367             $install_mods = 1 if (defined($1));
368             last;
369         }
370     }
371     close(IN);
372
373     if ($install_mods) {
374         if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") {
375             dodie "Failed to install modules";
376         }
377     } else {
378         doprint "No modules needed\n";
379     }
380
381     install;
382
383     monitor;
384
385     doprint "\n\n*******************************************\n";
386     doprint     "*******************************************\n";
387     doprint     "**            SUCCESS!!!!                **\n";
388     doprint     "*******************************************\n";
389     doprint     "*******************************************\n";
390
391     # try to reboot normally
392
393     if (run_command "ssh $target reboot") {
394         # nope? power cycle it.
395         run_command "$opt{POWER_CYCLE}";
396     }
397
398     sleep "$opt{SLEEP_TIME}";
399 }
400
401 if ($opt{"POWEROFF_ON_SUCCESS"}) {
402     if (run_command "ssh $target halt" && defined($opt{"POWER_OFF"})) {
403         # nope? the zap it!
404         run_command "$opt{POWER_OFF}";
405     }
406 }
407 exit 0;