blob: 81fb2e431058a668f428eb711eb8ab36929e87ca [file] [log] [blame]
Steven Rostedt2545eb62010-11-02 15:01:32 -04001#!/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
21
22my $version;
23my $install_mods;
24my $grub_number;
25my $target;
26my $make;
27
28sub read_config {
29 my ($config) = @_;
30
31 open(IN, $config) || die "can't read file $config";
32
33 while (<IN>) {
34
35 # ignore blank lines and comments
36 next if (/^\s*$/ || /\s*\#/);
37
38 if (/^\s*(\S+)\s*=\s*(.*?)\s*$/) {
39 my $lvalue = $1;
40 my $rvalue = $2;
41
42 $opt{$lvalue} = $rvalue;
43 }
44 }
45
46 close(IN);
47}
48
49sub doprint {
50 print @_;
51
52 if (defined($opt{"LOG_FILE"})) {
53 open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}";
54 print OUT @_;
55 close(OUT);
56 }
57}
58
59sub run_command {
60 my ($command) = @_;
61 my $redirect = "";
62
63 if (defined($opt{"LOG_FILE"})) {
64 $redirect = " >> $opt{LOG_FILE} 2>&1";
65 }
66
67 doprint "$command ... ";
68 `$command $redirect`;
69
70 my $failed = $?;
71
72 if ($failed) {
73 doprint "FAILED!\n";
74 } else {
75 doprint "SUCCESS\n";
76 }
77
78 return $failed;
79}
80
81my $timeout = $opt{"TIMEOUT"};
82
83sub wait_for_input
84{
85 my ($fp, $time) = @_;
86 my $rin;
87 my $ready;
88 my $line;
89 my $ch;
90
91 if (!defined($time)) {
92 $time = $timeout;
93 }
94
95 $rin = '';
96 vec($rin, fileno($fp), 1) = 1;
97 $ready = select($rin, undef, undef, $time);
98
99 $line = "";
100
101 # try to read one char at a time
102 while (sysread $fp, $ch, 1) {
103 $line .= $ch;
104 last if ($ch eq "\n");
105 }
106
107 if (!length($line)) {
108 return undef;
109 }
110
111 return $line;
112}
113
114sub reboot {
115 run_command "ssh $target '(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'";
116}
117
118sub monitor {
119 my $flags;
120 my $booted = 0;
121 my $bug = 0;
122 my $pid;
123 my $doopen2 = 0;
124
125 if ($doopen2) {
126 $pid = open2(\*IN, \*OUT, $opt{CONSOLE});
127 if ($pid < 0) {
128 die "Failed to connect to the console";
129 }
130 } else {
131 $pid = open(IN, "$opt{CONSOLE} |");
132 }
133
134 $flags = fcntl(IN, F_GETFL, 0) or
135 die "Can't get flags for the socket: $!\n";
136
137 $flags = fcntl(IN, F_SETFL, $flags | O_NONBLOCK) or
138 die "Can't set flags for the socket: $!\n";
139
140 my $line;
141 my $full_line = "";
142
143 doprint "Wait for monitor to settle down.\n";
144 # read the monitor and wait for the system to calm down
145 do {
146 $line = wait_for_input(\*IN, 5);
147 } while (defined($line));
148
149 reboot;
150
151 for (;;) {
152
153 $line = wait_for_input(\*IN);
154
155 last if (!defined($line));
156
157 doprint $line;
158
159 # we are not guaranteed to get a full line
160 $full_line .= $line;
161
162 if ($full_line =~ /login:/) {
163 $booted = 1;
164 }
165
166 if ($full_line =~ /call trace:/i) {
167 $bug = 1;
168 }
169
170 if ($line =~ /\n/) {
171 $full_line = "";
172 }
173 }
174
175 doprint "kill child process $pid\n";
176 kill 2, $pid;
177
178 print "closing!\n";
179 close(IN);
180
181 if (!$booted) {
182 die "failed - never got a boot prompt.\n";
183 }
184
185 if ($bug) {
186 die "failed - got a bug report\n";
187 }
188}
189
190sub install {
191
192 if (run_command "scp $opt{OUTPUT_DIR}/$opt{BUILD_TARGET} $target:$opt{TARGET_IMAGE}") {
193 die "failed to copy image";
194 }
195
196 if ($install_mods) {
197 my $modlib = "/lib/modules/$version";
198
199 if (run_command "ssh $target rm -rf $modlib") {
200 die "failed to remove old mods: $modlib";
201 }
202
203 if (run_command "scp -r $opt{TMP_DIR}/lib $target:/lib/modules/$version") {
204 die "failed to copy modules";
205 }
206 }
207
208}
209
210sub build {
211 my ($type) = @_;
212
213 unlink "$opt{OUTPUT_DIR}/.config";
214
215 run_command "$make mrproper";
216
217 # add something to distinguish this build
218 open(OUT, "> $opt{OUTPUT_DIR}/localversion") or die("Can't make localversion file");
219 print OUT "$opt{LOCALVERSION}\n";
220 close(OUT);
221
222 if (run_command "$make $opt{$type}") {
223 die "failed make config";
224 }
225
226 if (defined($opt{"MIN_CONFIG"})) {
227 run_command "cat $opt{MIN_CONFIG} >> $opt{OUTPUT_DIR}/.config";
228 run_command "yes '' | $make oldconfig";
229 }
230
231 if (run_command "$make $opt{BUILD_OPTIONS}") {
232 die "failed build";
233 }
234}
235
236read_config $ARGV[0];
237
238# mandatory configs
239die "MACHINE not defined\n" if (!defined($opt{"MACHINE"}));
240die "SSH_USER not defined\n" if (!defined($opt{"SSH_USER"}));
241die "BUILD_DIR not defined\n" if (!defined($opt{"BUILD_DIR"}));
242die "OUTPUT_DIR not defined\n" if (!defined($opt{"OUTPUT_DIR"}));
243die "BUILD_TARGET not defined\n" if (!defined($opt{"BUILD_TARGET"}));
244die "POWER_CYCLE not defined\n" if (!defined($opt{"POWER_CYCLE"}));
245die "CONSOLE not defined\n" if (!defined($opt{"CONSOLE"}));
246die "LOCALVERSION not defined\n" if (!defined($opt{"LOCALVERSION"}));
247die "GRUB_MENU not defined\n" if (!defined($opt{"GRUB_MENU"}));
248
249chdir $opt{"BUILD_DIR"} || die "can't change directory to $opt{BUILD_DIR}";
250
251$target = "$opt{SSH_USER}\@$opt{MACHINE}";
252
253doprint "\n\nSTARTING AUTOMATED TESTS\n";
254
255doprint "Find grub menu ... ";
256$grub_number = -1;
257open(IN, "ssh $target cat /boot/grub/menu.lst |")
258 or die "unable to get menu.lst";
259while (<IN>) {
260 if (/^\s*title\s+$opt{GRUB_MENU}\s*$/) {
261 $grub_number++;
262 last;
263 } elsif (/^\s*title\s/) {
264 $grub_number++;
265 }
266}
267close(IN);
268die "Could not find '$opt{GRUB_MENU}' in /boot/grub/menu on $opt{MACHINE}"
269 if ($grub_number < 0);
270doprint "$grub_number\n";
271
272$make = "$opt{MAKE_CMD} O=$opt{OUTPUT_DIR}";
273
274# First we need to do is the builds
275for (my $i = 1; $i <= $opt{"NUM_BUILDS"}; $i++) {
276 my $type = "BUILD_TYPE[$i]";
277
278 if (!defined($opt{$type})) {
279 $opt{$type} = $opt{"DEFAULT_BUILD_TYPE"};
280 }
281
282 doprint "\n\n";
283 doprint "RUNNING TEST $i of $opt{NUM_BUILDS} with option $opt{$type}\n\n";
284
285 if ($opt{$type} ne "nobuild") {
286 build $type;
287 }
288
289 # get the release name
290 doprint "$make kernelrelease ... ";
291 $version = `$make kernelrelease | tail -1`;
292 chomp($version);
293 doprint "$version\n";
294
295 # should we process modules?
296 $install_mods = 0;
297 open(IN, "$opt{OUTPUT_DIR}/.config") or die("Can't read config file");
298 while (<IN>) {
299 if (/CONFIG_MODULES(=y)?/) {
300 $install_mods = 1 if (defined($1));
301 last;
302 }
303 }
304 close(IN);
305
306 if ($install_mods) {
307 if (run_command "$make INSTALL_MOD_PATH=$opt{TMP_DIR} modules_install") {
308 die "Failed to install modules";
309 }
310 } else {
311 doprint "No modules needed\n";
312 }
313
314 install;
315
316 monitor;
317
318 doprint "\n\n*******************************************\n";
319 doprint "*******************************************\n";
320 doprint "** SUCCESS!!!! **\n";
321 doprint "*******************************************\n";
322 doprint "*******************************************\n";
323
324 # try to reboot normally
325
326 if (run_command "ssh $target reboot") {
327 # nope? power cycle it.
328 run_command "$opt{POWER_CYCLE}";
329 }
330
331 sleep "$opt{SLEEP_TIME}";
332}
333
334exit 0;