ktest: New features: noclean, dodie, poweroff on error and success
[deliverable/linux.git] / tools / testing / ktest / ktest.pl
CommitLineData
2545eb61
SR
1#!/usr/bin/perl -w
2
3use strict;
4use IPC::Open2;
5use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
6use FileHandle;
7
8$#ARGV >= 0 || die "usage: autotest.pl config-file\n";
9
10$| = 1;
11
12my %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
5c42fc5b
SR
21$opt{"BUILD_NOCLEAN"} = 0;
22$opt{"POWEROFF_ON_ERROR"} = 0;
23$opt{"POWEROFF_ON_SUCCESS"} = 0;
2545eb61
SR
24
25my $version;
26my $install_mods;
27my $grub_number;
28my $target;
29my $make;
5c42fc5b 30my $noclean;
2545eb61
SR
31
32sub 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
53sub 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
5c42fc5b
SR
63sub 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
2545eb61
SR
73sub 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
95my $timeout = $opt{"TIMEOUT"};
96
97sub 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
128sub reboot {
129 run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'";
130}
131
132sub monitor {
133 my $flags;
134 my $booted = 0;
135 my $bug = 0;
136 my $pid;
137 my $doopen2 = 0;
5c42fc5b 138 my $skip_call_trace = 0;
2545eb61
SR
139
140 if ($doopen2) {
141 $pid = open2(\*IN, \*OUT, $opt{CONSOLE});
142 if ($pid < 0) {
5c42fc5b 143 dodie "Failed to connect to the console";
2545eb61
SR
144 }
145 } else {
146 $pid = open(IN, "$opt{CONSOLE} |");
147 }
148
149 $flags = fcntl(IN, F_GETFL, 0) or
5c42fc5b 150 dodie "Can't get flags for the socket: $!\n";
2545eb61
SR
151
152 $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or
5c42fc5b 153 dodie "Can't set flags for the socket: $!\n";
2545eb61
SR
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
5c42fc5b
SR
181 if ($full_line =~ /\[ backtrace testing \]/) {
182 $skip_call_trace = 1;
183 }
184
2545eb61 185 if ($full_line =~ /call trace:/i) {
5c42fc5b
SR
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 -/) {
2545eb61
SR
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) {
5c42fc5b 209 dodie "failed - never got a boot prompt.\n";
2545eb61
SR
210 }
211
212 if ($bug) {
5c42fc5b 213 dodie "failed - got a bug report\n";
2545eb61
SR
214 }
215}
216
217sub install {
218
219 if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") {
5c42fc5b 220 dodie "failed to copy image";
2545eb61
SR
221 }
222
223 if ($install_mods) {
224 my $modlib = "/lib/modules/$version";
5c42fc5b 225 my $modtar = "autotest-mods.tar.bz2";
2545eb61
SR
226
227 if (run_command "ssh $target rm -rf $modlib") {
5c42fc5b
SR
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";
2545eb61
SR
234 }
235
5c42fc5b
SR
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";
2545eb61 244 }
5c42fc5b
SR
245
246 run_command "ssh $target rm -f /tmp/$modtar";
2545eb61
SR
247 }
248
249}
250
251sub build {
252 my ($type) = @_;
5c42fc5b
SR
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 }
2545eb61 262
5c42fc5b
SR
263 if (!$noclean && run_command "$make mrproper") {
264 dodie "make mrproper";
265 }
2545eb61 266
5c42fc5b
SR
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 }
2545eb61
SR
277
278 # add something to distinguish this build
5c42fc5b 279 open(OUT, "> $opt{OUTPUT_DIR}/localversion") or dodie("Can't make localversion file");
2545eb61
SR
280 print OUT "$opt{LOCALVERSION}\n";
281 close(OUT);
282
5c42fc5b
SR
283 if (defined($opt{"MIN_CONFIG"})) {
284 $defconfig = "KCONFIG_ALLCONFIG=$opt{MIN_CONFIG}";
2545eb61
SR
285 }
286
5c42fc5b
SR
287 if (run_command "$defconfig $append $make $type") {
288 dodie "failed make config";
2545eb61
SR
289 }
290
291 if (run_command "$make $opt{BUILD_OPTIONS}") {
5c42fc5b 292 dodie "failed build";
2545eb61
SR
293 }
294}
295
296read_config $ARGV[0];
297
298# mandatory configs
299die "MACHINE not defined\n" if (!defined($opt{"MACHINE"}));
300die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"}));
301die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"}));
302die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"}));
303die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"}));
304die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"}));
305die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"}));
306die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"}));
307die "GRUB_MENU not defined\n" if (!defined($opt{"GRUB_MENU"}));
308
309chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}";
310
311$target = "$opt{SSH_USER}\@$opt{MACHINE}";
312
313doprint "\n\nSTARTING AUTOMATED TESTS\n";
314
315doprint "Find grub menu ... ";
316$grub_number = -1;
317open(IN, "ssh $target cat /boot/grub/menu.lst |")
318 or die "unable to get menu.lst";
319while (<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}
327close(IN);
328die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}"
329 if ($grub_number < 0);
330doprint "$grub_number\n";
331
332$make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}";
333
334# First we need to do is the builds
335for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) {
336 my $type = "BUILD_TYPE[$i]";
337
5c42fc5b
SR
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
2545eb61
SR
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") {
5c42fc5b 353 build $opt{$type};
2545eb61
SR
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;
5c42fc5b 364 open(IN, "$opt{OUTPUT_DIR}/.config") or dodie("Can't read config file");
2545eb61
SR
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") {
5c42fc5b 375 dodie "Failed to install modules";
2545eb61
SR
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
5c42fc5b
SR
401if ($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}
2545eb61 407exit 0;
This page took 0.050635 seconds and 5 git commands to generate.