# --------------------------------------------------------------------------- # Script: autoboottest.pl # # (c) 2000 Microsoft Corporation. All rights reserved. # # Purpose: This script is an example of a perl script in the NT postbuild # environment. # # Version: <1.00> () : () # <1.01> () : () #--------------------------------------------------------------------- # Set Package # package autoboottest; # Set the script name # $ENV{script_name} = 'autoboottest.pl'; # Set version # $VERSION = '1.00'; # Set required perl version # require 5.003; # Use section use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts"; use lib $ENV{RAZZLETOOLPATH}; use GetParams; use LocalEnvEx; use GetIniSetting; use cksku; use Logmsg; use strict; no strict 'vars'; # # Require section require Exporter; # # Global variable section # $selectedsku = undef; (@BuildArchs, @BuildTypes, @JoinDomain) = (); ($FirstBuildMachine, @ReleaseServers, $ReleaseServers, $ReleaseRemote, $BootTestMachines, $BuildNumber, $AutoRaise)=(); ($BuildBranch, $BuildArchs, $BuildTypes)=( $ENV{_BuildBranch}, $ENV{_BuildArch}, $ENV{_BuildType} ); # Assumption: BootTestDrive defined in Boot Test Machine remote. # Please reference BootTestDriveVarName in bootinit.cmd ($BootTestDriveVar, $PostBootTest, $UnAttend, $OpShellFolder) = ( "BootTestDrive", "PostBootTest", "unattend.txt", "OpShellFolder.pl" ); sub Main { # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # Begin Main code section # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # Return when you want to exit on error # my (%skus)=(); my (@BootTestMachines, $BuildName, $sku, $PostBootTestFile ) = (); # BUGBUG - need to remove if we turn on outside main # if not defined MAIN_BUILD_LAB_MACHINE goto End return if (!exists $ENV{MAIN_BUILD_LAB_MACHINE}); &initial; # for $BuildBranch, we can not change branch, because currently template limitation # for $lang. we can not change language, because currently template limitation # Initial GetIniSetting unless ( &GetIniSetting::CheckIniFile ) { errmsg( "Failed to find ini file ..." ); return; } # for @BuildArchs, now is only execute one time, because template limitation for $BuildArch (@BuildArchs) { $ENV{_BuildArch} = $BuildArch; %skus = &cksku::GetSkus($lang, $BuildArch); # for @BuildTypes, now is only execute one time, because template limitation for $BuildType (@BuildTypes) { $ENV{_BuildType} = $BuildType; # Get Build Name $BuildName = &GetLatestBuildName; next if (!defined $BuildName); $BuildNumber = (split(/\./, $BuildName))[0]; # Get build share $BuildShare = &GetBuildShare($BuildName); # Get Release Server @ReleaseServers = &GetReleaseServers; $ReleaseRemote = &GetReleaseRemote; # Get BuildMachine $FirstBuildMachine = &GetFirstBuildMachine($BuildBranch, $BuildArch, $BuildType); # Get Account @JoinDomain = split(/\s+/, &GetAccount); # Get AutoRaise $AutoRaise = &GetAutoRaise($BuildArch, $BuildType); for $sku (reverse(keys %skus)) { next if (defined ($selectedsku) && $sku ne $selectedsku); # Get Boot Test Machines @BootTestMachines = &GetBootTestMachine( $BuildArch, $BuildType, $sku ); $BootTestMachines = join(" ", map({$_ . "1"} @BootTestMachines)); for $BootTestMachine (@BootTestMachines) { # Show the boot test machine we working on logmsg("Start to install build on $BootTestMachine...."); # Write PostBootTestScript $PostBootTestFile = "$ENV{temp}\\${PostBootTest}_${BootTestMachine}.cmd"; &WritePostBootTestScript($PostBootTestFile, $BootTestMachines, $FirstBuildMachine, $BuildArch, $BuildType, @ReleaseServers ); &StartBootTest($BuildArch, $BuildShare, $sku, $PostBootTestFile, $BootTestMachine); } } } } # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # End Main code section # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ } # sub initial { @BuildArchs = splitcolon( $BuildArchs ); # x86 or amd64 or ia64 @BuildTypes = splitcolon( $BuildTypes ); # fre or chk } sub StartBootTest { my ($BuildArch, $BuildShare, $sku, $PostBootTestFile, $BootTestMachine) = @_; my $BootCmd; # For some reason there is no environment variable for i386 my $TargetArch = ($BuildArch eq "x86")?"i386":$BuildArch; my $UnattendFile = "$ENV{tmp}\\$UnAttend"; my $OpShellFolderFile = "$ENV{RazzleToolPath}\\postbuildscripts\\$OpShellFolder"; # Compose Boot Command $BootCmd = &ComposeBootCmd($TargetArch, $BuildShare, $sku); # Make the Unattend file &WriteUnattendFile($UnattendFile, $BootTestMachine, $BuildBranch); errmsg "Unable to create answer file." if (!-e $UnattendFile); # Issue Commands &IssueBootTestCommands($BuildArch, $BootTestMachine, $UnattendFile, $PostBootTestFile, $OpShellFolderFile, $BootCmd); } # # ComposeBootCmd($TargetArch, $BuildShare, $sku) # # purpose: Generate the boot command. Such as "winnt32 ....", so we can call issue command # sub ComposeBootCmd { my ($TargetArch, $BuildShare, $sku) = @_; my $BinSource = "$BuildShare\\$sku\\$TargetArch"; my $UnattendFile = "/Unattend:\%$BootTestDriveVar\%\\tools\\$UnAttend"; my $CmdOpt; if ($AutoRaise) { $CmdOpt = "/cmd:\"\%$BootTestDriveVar\%\\tools\\perl\\bin\\perl.exe " . "\%$BootTestDriveVar\%\\tools\\OpShellFolder.pl " . "CopyFileTo Startup \%$BootTestDriveVar\%\\tools\\PostBootTest.cmd\""; } # Set the boot command to issue # Have to remove winnt32 when testing on MP machines return "$BinSource\\winnt32.exe /#bvt:nosymbols /tempdrive:\%$BootTestDriveVar\% $UnattendFile $CmdOpt"; } # # WriteUnattendFile($UnattendFile, $BootTestMachine, $Branch) # # purpose: Generate Unattend File # sub WriteUnattendFile { my($UnattendFile, $BootTestMachine, $Branch) = @_; open(F, ">$UnattendFile"); print F <: with $ $UnattendFile =~ s/\\(\w)\:\\/\\$1\$\\/; $SourceToolDir =~ s/\\(\w)\:\\/\\$1\$\\/; $PostBootTestFile =~ s/\\(\w)\:\\/\\$1\$\\/; $OpShellFolderFile =~ s/\\(\w)\:\\/\\$1\$\\/; # Phase 1: Format the Boot test drive (details see below the 'code' part) # # Phase 2: Prepare reference files (such as unattend.txt, opshellfolder.pl, postboottest.cmd) my @Phase2 = &FormStrToArray( <$ENV{tmp}\\format_${lang}.txt"); print F "Format \%$BootTestDriveVar\% /X /FS:NTFS /Q\n"; print F "Y\n\n\n"; close(F); # Phase 1: Format the Boot test drive &MyRemoteCommand($BootTestMachine, "BootTestRemote", "Echo Beginning boot test"); &MyRemoteCommand($BootTestMachine, "BootTestRemote", "$ENV{tmp}\\format_${lang}.txt", 1); sleep 45; # Phase 2, Phase 3: for $cmd ( @Phase2, @Phase3) { &MyRemoteCommand($BootTestMachine, "BootTestRemote", $cmd); } # Phase 4: start setup for $cmd ( @Phase4) { &MyRemoteCommand($BootTestMachine, "BootTestRemote", $cmd); } } # # FormStrToArray(@strs) # Parameters: # @strs: multi-line string " This is a text.\n This is another line\n\t\tAnd, have tabs" # # purpose: Seperate the multi-line string into an array and format it # sub FormStrToArray { my (@strs)=@_; my (@result, $str, $line); for $str (@strs) { for $line (split(/\n/, $str)) { chomp $str; $line=~s/^\s+$//; $line=~s/\t/ /g; push @result, $line; } } return @result; } # # WritePostBootTestScript($postboottestfile, $boottestlist, $myprimary, $buildarch, $buildtype, @ReleaseServers) # Parameters: # $postboottestfile: such as "postboottest.cmd" # $boottestlist: such as "i32bt0011 i32bt0021" # $myprimary: such as "i32fre" # $buildarch: such as "x86" # $buildtype: such as "fre" # @ReleaseServers: such as ("intblds10", "intblds11" ) # # purpose: According the parameters, generate postboottestfile # sub WritePostBootTestScript { # Write a script on the fly to use on the boot test machine. The reason # This needs to be done on the fly is so that we can use sources and # and environment variables only available on the build machines. my ($PostBootTestFile, $BootTestList, $MyPrimary, $buildarch, $buildtype, @ReleaseServers ) = @_; my $langopt = ((defined $lang)&&(lc$lang ne "usa"))?"-l:$lang":""; my $ReleaseServers = join(" ", @ReleaseServers); `del /f $PostBootTestFile` if (-e $PostBootTestFile); open(F, ">$PostBootTestFile"); print F <nul REM 300 \%BootTestDrive\%\\tools\\sleep 3 net use \\\\\%\%a /d if errorlevel 1 call :LogMe "Error - \%\%a can not disconnect" ) ) ) call :LogMe "Move PostBootTest.cmd to desktop" \%$BootTestDriveVar\%\\tools\\perl\\bin\\perl \%$BootTestDriveVar\%\\tools\\OpShellFolder.pl CopyFileTo Desktop \%$BootTestDriveVar\%\\tools\\postboottest.cmd \%$BootTestDriveVar\%\\tools\\perl\\bin\\perl \%$BootTestDriveVar\%\\tools\\OpShellFolder.pl Delfile Startup postboottest.cmd call :LogMe "PostBootTest.cmd finished." goto :exit :logme echo \%1 echo \%1>>\%LOGFILE\% goto :EOF :exit endlocal PostBootTestFile close(F); logmsg "Failed to create postboottest.cmd" if (!-e $PostBootTestFile); return; } #################################### Reusable functions # # GetFirstBuildMachine($branch, $arch, $type) # # purpose: Get first primary build machine or first secondary build machine # if not defined primary from buildmachines.txt # sub GetFirstBuildMachine { # ($order, $branch, $arch, $type) = ("primary", @_); my $searchpattern = join(",", @_); my @BuildMachines; my ($machinename, $firstbuildmachine, $line); open(F, "$ENV{RazzleToolPath}\\BuildMachines.txt"); @BuildMachines = ; close(F); for $line (@BuildMachines) { $machinename = (split(/\,/, $line))[0]; if ($line =~ /$searchpattern/i) { return $machinename if ($line =~ /primary/i); $firstbuildmachine=$machinename if (!defined $firstbuildmachine); } } errmsg "Failed to find a build machine." if(!defined $firstbuildmachine); return $firstbuildmachine; } # # GetFirstBuildMachine($branch, $arch, $type) # # purpose: Get first primary build machine or first secondary build machine # if not defined primary from buildmachines.txt # sub GetAccount { my (@Request) = ("JoinDomain"); my( $JoinDomain ) = &GetIniSetting::GetSetting( @Request ); return $JoinDomain; } # # GetBuildPath($buildname, [$compname], [$releaseshare]) # # purpose: Get the fullpath share, so we can write file correctly. such as \\i32fre\release\jpn\1234 => \\i32fre\f$\release\jpn\1234 # sub GetBuildPath { my ($releaseshare) = @_; my ($computername, $sharename, $misc, $sharepath); # Get the computername, sharename, and remain paths if ($releaseshare =~ /^\\\\([^\\]+)\\([^\\]+)/) { ($computername, $sharename, $misc) = ($1, $2, $'); } elsif ($releaseshare =~ /^([^\\]+)/) { ($computername, $sharename, $misc) = ($ENV{computername}, $1, $'); } else { errmsg("Can not get correct build path ($releaseshare)"); return; } # Replace d: to d$ if applicable $sharename =~ s/\:/\$/; for my $line (`rmtshare \\\\$computername\\$sharename`) { $sharepath=$1 if ($line =~ /^Path\s+(.+)$/); } if (!defined $sharepath) { errmsg("Can not find the path of this share ($sharename)"); return; } # Remove d:\ to d: if applicable $sharepath =~ s/\\$//; # Replace d: to d$ if applicable $sharepath =~ s/\:/\$/; return "\\\\$computername\\$sharepath\\$misc"; } # # GetBuildShare($buildname, [$compname], [$releaseshare]) # # purpose: Get build share from computer ($compname). Default is $ENV{computername # sub GetBuildShare { my ($buildname, $compname, $releaseshare) = @_; # This setting is misnamed as it is actually used as both the directory # and share name my (@Request) = ("AlternateReleaseDir"); my ($inirelease) = &GetIniSetting::GetSetting( @Request ); $compname = $ENV{computername} if (!defined $compname); $releaseshare = $inirelease || 'release' if (!defined $releaseshare); my $BuildShare = (lc$lang eq "usa")?"\\\\$compname\\$releaseshare\\$buildname": "\\\\$compname\\$releaseshare\\$lang\\$buildname"; logmsg "No latest build to test - aborting." if (!defined $BuildShare); logmsg "No latest build could find - aborting." if (!-e $BuildShare); return $BuildShare; } # # GetReleaseServers # # purpose: Get release server from ini file # sub GetReleaseServers { my (@Request) = ("ReleaseServers", $ENV{_BuildArch} . $ENV{_BuildType}); my ($ReleaseServer) = &GetIniSetting::GetSetting( @Request ); my @ReleaseServers; @Request = ("PrimaryReleaseServer"); @ReleaseServers = &GetIniSetting::GetSetting( @Request ); # Collect Release Servers to @ReleaseServers # Avoid to include primary release server twice # # hardcode exclude ntburnlab08 push(@ReleaseServers, grep {!( # USA case ((lc$lang eq "usa") && (/ntburnlab08|intblds10/i)) || # Primary release server ((defined $ReleaseServers[0]) && (/\Q$ReleaseServers[0]\E/i))) } split(/\s+/, $ReleaseServer) ); errmsg "Could not find release servers to raise." if (!@ReleaseServers); return @ReleaseServers; } sub GetReleaseRemote { my (@Request) = ("AlternateReleaseRemote"); my( $ReleaseRemote ) = &GetIniSetting::GetSetting( @Request ); $ReleaseRemote = "release" if (!defined $ReleaseRemote); return $ReleaseRemote; } # # GetBootTestMachine($arch, $type, $sku) # # purpose: Get Boot Test Machine from ini file # sub GetBootTestMachine { my ($arch, $type, $sku) = @_; my (@Request) = ( "BootTestMachines", uc$arch . uc$type, $sku); return split(/\s+/, &GetIniSetting::GetSetting( @Request )); } # # GetLatestBuildName # # purpose: call getlatestrelease.cmd and handle its error # sub GetLatestBuildName { my $buildname = `$ENV{RazzleToolPath}\\PostBuildScripts\\GetLatestRelease.cmd -l:$lang`; chomp $buildname; if ($buildname =~ /none/i) { errmsg "No Binaries tree or latest release found - Aborting."; return; } return $buildname; } # # GetAutoRaise # # purpose: check the .ini file to see if we should try to auto raise or not # sub GetAutoRaise { my ($arch, $type) = @_; my (@Request) = ( "AutoRaise", uc$arch . uc$type); return (&GetIniSetting::GetSetting( @Request ) =~ /true/i); } # # MyRemoteCommand($Machine, $RemoteId, $cmd) # # purpose: Execute $cmd on remote console # sub MyRemoteCommand { my ($Machine, $RemoteId, $cmd, $echotype) = @_; $echotype = ($echotype eq "1")?"type":"echo"; $cmd = ($cmd=~/\S/)?"$echotype $cmd" : "${echotype}."; # Make sure this gets logged properly logmsg("Remote Command: '$cmd'"); # Wait 1 more second each time before each time we execute remote command sleep 1; return &MySystemCall("$cmd|remote /c $Machine $RemoteId >nul"); } # # MySystemCall($cmd) # # purpose: Execute $cmd thru system call # sub MySystemCall { my ($cmd) = @_; my $r = system($cmd); $r >>= 8; # Because remote command always return 4, we should ignore it. if (($r)&&($r ne 4)&&($r ne 0)) { errmsg "Failed ($r): $cmd"; return 0; } return 1; } # # splitcolon($str) # # purpose: split $str with delimiter ':' and return splited string # sub splitcolon { return grep {/\w+/} split(/\:/, $_[0]); } ################################## sub ValidateParams { # } # sub Usage { print < &GetParams ('-o', 'l:y:', '-p', 'lang selectedsku', @ARGV); # Include local environment extensions &LocalEnvEx::localenvex('initialize'); # Set lang from the environment $lang=$ENV{lang}; # Validate the option given as parameter. &ValidateParams; # Step 4: Call the main function &autoboottest::Main(); # End local environment extensions. &LocalEnvEx::localenvex('end'); } # ------------------------------------------------------------------------------------------- # Script: autoboottest.pl # Purpose: Template perl perl script for the NT postbuild environment # SD Location: %sdxroot%\tools\postbuildscripts # # (1) Code section description: # CmdMain - Developer code section. This is where your work gets done. # - Developer subs code section. This is where you write subs. # # (2) Reserved Variables - # $ENV{HELP} - Flag that specifies usage. # $ENV{lang} - The specified language. Defaults to USA. # $ENV{logfile} - The path and filename of the logs file. # $ENV{logfile_bak} - The path and filename of the logfile. # $ENV{errfile} - The path and filename of the error file. # $ENV{tmpfile} - The path and filename of the temp file. # $ENV{errors} - The scripts errorlevel. # $ENV{script_name} - The script name. # $ENV{_NTPostBld} - Abstracts the language from the files path that # postbuild operates on. # $ENV{_NTPostBld_Bak} - Reserved support var. # $ENV{_temp_bak} - Reserved support var. # # (3) Reserved Subs - # Usage - Use this sub to discribe the scripts usage. # ValidateParams - Use this sub to verify the parameters passed to the script. # # (4) Call other executables or command scripts by using: # system "foo.exe"; # Note that the executable/script you're calling with system must return a # non-zero value on errors to make the error checking mechanism work. # # Example # if (system("perl.exe foo.pl -l $lang")){ # errmsg("perl.exe foo.pl -l $lang failed."); # # If you need to terminate function's execution on this error # goto End; # } # # (5) Log non-error information by using: # logmsg ""; # and log error information by using: # errmsg ""; # # (6) Have your changes reviewed by a member of the US build team (ntbusa) and # by a member of the international build team (ntbintl). # # ------------------------------------------------------------------------------------------- =head1 NAME B - What this package for =head1 SYNOPSIS =head1 DESCRIPTION =head1 INSTANCES =head2 =head1 METHODS =head2 =head1 SEE ALSO =head1 AUTHOR > =cut 1;