Commit | Line | Data |
---|---|---|
2545eb61 SR |
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 | |
5c42fc5b SR |
21 | $opt{"BUILD_NOCLEAN"} = 0; |
22 | $opt{"POWEROFF_ON_ERROR"} = 0; | |
23 | $opt{"POWEROFF_ON_SUCCESS"} = 0; | |
2545eb61 SR |
24 | |
25 | my $version; | |
26 | my $install_mods; | |
27 | my $grub_number; | |
28 | my $target; | |
29 | my $make; | |
5c42fc5b | 30 | my $noclean; |
2545eb61 SR |
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 | ||
5c42fc5b SR |
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 | ||
2545eb61 SR |
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; | |
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 | ||
217 | sub 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 | ||
251 | sub 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 | ||
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 | ||
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 |
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 | } | |
2545eb61 | 407 | exit 0; |