ktest.pl 9.1 KB

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