#!/usr/bin/env perl # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html use strict; use warnings; use Getopt::Long; use Data::Dumper; use Term::ANSIColor; use Time::Local; use File::Basename; use File::Path; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr'; } use lib "$::XCATROOT/lib/perl"; #--------------global attributes---------------- my $program_path = dirname(File::Spec->rel2abs(__FILE__)); my $program_name = basename($0); my $rootdir = "$program_path/../share/xcat/tools/autotest"; my $casedir = "$rootdir/testcase/"; $casedir = $ENV{'XCATTEST_CASEDIR'} if exists $ENV{'XCATTEST_CASEDIR'}; my $bundledir = "$rootdir/bundle/"; my $resultdir = "$rootdir/result/"; my $rst = 0; my $setup_env_by_config_file = 1; my $stop_to_keep_env = 0; #Array to save all cases planed to handle #these cases can be passed by option -b/-t/-c #if without value passed by option -b/-t/-c, for "show information" function, #the search scope is the all cases shipped by xcat_test package my @cases_to_be_run = (); #A hash to save the contect of config file #The structure of %config #config{object}{type}{name}{attr} #config{table}{name}{entry}{key} #config{script_prev}->[] #config{script_post}->[] #config{var}{varname} my %config = (); #Array of hash, to save the information of case my @cases = (); my %case_name_index_map; #when loading cases, NORUN means just load case without attribute replacement. #this is used for "show information" function my $NORUN = 0; my $RUN = 1; #----------global logs attributes--------------- my $running_log_fd = undef my $running_log_name = undef; my $failed_log_name = undef; my $performance_log_name = undef; my $xcatdebug = 0; $xcatdebug = 1 if exists $ENV{'XCATTEST_DEBUG'}; #--------------command line attrbutes-------------- my $needhelp = 0; my $configfile = undef; my $bundlelist = undef; my $caselist = undef; my $cmdlist = undef; my $list = undef; my $restore = 0; my $quiet = 0; my $search_expression = undef; my %label_map; my %case_label_map; my @label_order = (["xcat_install"], ["mn_only"], [ "sn_diskful", "sn_diskless" ], [ "flat_cn_diskful", "flat_cn_diskless", "hierarchy_cn_diskful", "hierarchy_cn_diskless" ], [ "cn_bmc_ready", "cn_os_ready" ], ["others"]); #-------------usage-------------------- $::USAGE = "Usage: To get help: $program_name -h To list the information about all cases shipped by xcat test package $program_name -l {caselist|caseinfo|casenum} To list the information about all bunldes shipped by xcat test package $program_name -l bundleinfo To list the information about label $program_name -l labelinfo To list the information about cases in specific bundles $program_name -l {caselist|caseinfo|casenum} -b To list the information about cases related to specific commands $program_name -l {caselist|caseinfo|casenum} -c To list the information about specific cases $program_name -l {caselist|caseinfo|casenum} -t To list cases information that satisfy specific filter expression $program_name -l {caselist|caseinfo|casenum} -s \"filter_expression\" For short, show the cases list can use below command $program_name -l -s \"filter_expression\" To list information about cases in specific bundles and satisfy specific filter expression at same time $program_name -l {caselist|caseinfo|casenum} -b -s \"filter_expression\" To run test cases in specific bundles $program_name [-f {configure_file|configure_file:System}] -b [-r] [-q] To run specific test cases $program_name [-f {configure_file|configure_file:System}] -t [-r] [-q] To run all cases related to specific commands $program_name [-f {configure_file|configure_file:System}] -c [-r] [-q] To run test cases that satisfy specific filter expression $program_name -s \"filter_expression\" Options: -h : Get $program_name usage information. -l : list specific information. The valid options are caselist,caseinfo,casenum,bundleinfo. For list case information, caselist is default value. -f : specify the configuration file. If 'System' tag is used, only [System] section in the configuration file will be used. If 'System' is not used all other sections of the configuration file will be used, like [Table], [Object], etc. -c : Comma separated list of command names to test. -t : Comma separated list of test case names to test. -b : Comma separated list of bundle names to test. If a bundle name is specified without an absolute path, such like / \$needhelp, "f=s" => \$configfile, "b=s" => \$bundlelist, "t=s" => \$caselist, "c=s" => \$cmdlist, "l:s" => \$list, "q" => \$quiet, "s=s" => \$search_expression, "r" => \$restore) ) { log_this($running_log_fd, "$::USAGE"); to_exit(1); } #$list="caselist" unless($list); if ($needhelp) { log_this($running_log_fd, "$::USAGE"); to_exit(0); } my $error; $rst = check_option_validity(\$error); if ($rst) { log_this($running_log_fd, "$error", "$::USAGE"); to_exit(1); } if($search_expression){ $rst = scan_existed_labels(\%case_label_map, \%label_map, \$error); if($rst) { log_this($running_log_fd, "$error"); to_exit(1); } } $rst = calculate_cases_to_be_run(\@cases_to_be_run, \$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } if ($xcatdebug) { print "----case to be run-----------------\n"; print Dumper \@cases_to_be_run; } if (defined $list) { $list="caselist" if ($list eq ""); if ($list eq "caselist") { if (@cases_to_be_run) { #list the cases indicated by option -b,-c,-t, -s foreach (@cases_to_be_run) { log_this($running_log_fd, "$_"); } } elsif (!@cases_to_be_run && $search_expression){ log_this($running_log_fd, "There is no cases match search expression $search_expression"); } else { #list the cases shipped by xcat test package $rst = load_case(\@cases_to_be_run, \@cases, \%case_name_index_map, \$error, $NORUN); if ($rst) { log_this($running_log_fd, "$error"); } foreach my $case (@cases) { log_this($running_log_fd, "$case->{name}"); } } } elsif ($list eq "caseinfo") { if (!@cases_to_be_run && $search_expression){ log_this($running_log_fd, "There is no cases match search expression $search_expression"); } else { $rst = show_case_info(\@cases_to_be_run, \$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } } } elsif ($list eq "casenum") { if (@cases_to_be_run || (!@cases_to_be_run && $search_expression)) { #list the case number indicated by option -b,-c,-t my $casenum = @cases_to_be_run; log_this($running_log_fd, "$casenum"); } else { #list the case number shipped by xcat test package $rst = load_case(\@cases_to_be_run, \@cases, \%case_name_index_map, \$error, $NORUN); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } my $casenum = @cases; log_this($running_log_fd, "$casenum"); } } elsif ($list eq "bundleinfo") { #list the bundle information shipped by xcat test package $rst = show_bundle_info(\$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } } elsif ($list eq "labelinfo"){ $rst = scan_existed_labels(\%case_label_map, \%label_map, \$error); my %label_conut; my %case_label_str_map; foreach my $case (keys %case_label_map){ if($case_label_map{$case}{label_str}){ $case_label_str_map{$case} = $case_label_map{$case}{label_str}; my @labels = split(",", $case_label_map{$case}{label_str}); foreach my $label (@labels){ if(!exists $label_conut{$label}){ $label_conut{$label}=1; }else{ $label_conut{$label}+=1; } } } } log_this($running_log_fd, "-------------------------------"); log_this($running_log_fd, "The labels of cases:"); log_this($running_log_fd, "-------------------------------"); print_table(\%case_label_str_map); my $label_total_num=keys %label_conut; log_this($running_log_fd, "\n-------------------------------"); log_this($running_log_fd, "There are $label_total_num different labels"); log_this($running_log_fd, "The number of cases of each label:"); log_this($running_log_fd, "-------------------------------"); print_table(\%label_conut); log_this($running_log_fd, "-------------------------------"); my $total_case = keys %case_label_map; my $case_with_label_num = keys %case_label_str_map; log_this($running_log_fd, "There are $total_case cases totaly, there are $case_with_label_num cases with label."); } to_exit(0); } unless (@cases_to_be_run) { log_this($running_log_fd, "There is no case to run, Please indicate case by option -b,-c,-t,-s"); to_exit(1); } if (defined($configfile) && ($configfile =~ /(.*):(System)/)) { $configfile = $1; $setup_env_by_config_file = 0; } log_this($running_log_fd, "xCAT automated test started at " . scalar(localtime())); if (defined($configfile)) { log_this($running_log_fd, "******************************"); log_this($running_log_fd, "loading Configure file"); log_this($running_log_fd, "******************************"); $rst = load_config_file($configfile, \%config, \$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } } else { $setup_env_by_config_file = 0; # Leverage environment variable to used in test case foreach (keys %ENV) { if (/^XCATTEST_/) { my @envname = split("_", $_, 2); $config{var}{ $envname[-1] } = $ENV{$_}; } } } if ($restore) { log_this($running_log_fd, "******************************"); log_this($running_log_fd, "Backup current xCAT database"); log_this($running_log_fd, "******************************"); $rst = bakup_current_env(\$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } } if ($setup_env_by_config_file) { log_this($running_log_fd, "******************************"); log_this($running_log_fd, "Initialize xCAT test environment by definition in configure file"); log_this($running_log_fd, "******************************"); $rst = setup_env_by_configure_file(\%config, \$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } } log_this($running_log_fd, "******************************"); log_this($running_log_fd, "To detect current test environment"); log_this($running_log_fd, "******************************"); $rst = detect_current_env(\%config, \$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } log_this($running_log_fd, "******************************"); log_this($running_log_fd, "loading test cases"); log_this($running_log_fd, "******************************"); $rst = load_case(\@cases_to_be_run, \@cases, \%case_name_index_map, \$error, $RUN); if ($rst && $rst < 2) { log_this($running_log_fd, "$error"); to_exit(1); } if ($xcatdebug) { print "=====Dumper loaded cases=======\n"; print Dumper \@cases; print "=====Dumper case_name_index_map=======================\n"; print Dumper \%case_name_index_map; print "=====Dumper cases to be run=====\n"; print Dumper \@cases_to_be_run; } unless (@cases_to_be_run) { to_exit(1); } log_this($running_log_fd, "******************************"); log_this($running_log_fd, "Start to run test cases"); log_this($running_log_fd, "******************************"); $rst = run_case(\@cases_to_be_run, \@cases, \%case_name_index_map, \$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } if ($restore) { log_this($running_log_fd, "******************************"); log_this($running_log_fd, "restore xCAT test evironment"); log_this($running_log_fd, "******************************"); $rst = restore_current_env(\$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } } log_this($running_log_fd, "xCAT automated test finished at " . scalar(localtime())); log_this($running_log_fd, "Please check results in the $resultdir, \nand see $failed_log_name file for failed cases."); #To generate performance report $rst = generate_performance_report($running_log_name, $performance_log_name, \$error); if ($rst) { log_this($running_log_fd, "$error"); to_exit(1); } log_this($running_log_fd, "see $performance_log_name file for time consumption"); if ($stop_to_keep_env) { to_exit(1); } else { to_exit(0); } #============================================================================================== # sub function implementation #============================================================================================== #-------------------------------------------------------- # Fuction name: log_this # Description: print message to log file and STDOUT. # Set '-q' option in command line, just print message to log file # Atrributes: $logdf: the file description of openning log file # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub log_this { my $logdf = shift; my $msg = join("\n", @_); #print message to STDOUT if (!$quiet) { if ($msg =~ /\[Pass\]/) { print color("green"), "$msg\n", color("reset"); } elsif ($msg =~ /\[Failed\]/) { print color("red"), "$msg\n", color("reset"); } else { print "$msg\n"; } } #record message to log file print $logdf "$msg\n"; return 0; } #-------------------------------------------------------- # Fuction name: pro_init # Description: Do program initialization # Atrributes: # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub pro_init { mkpath("$resultdir") unless (-d "$resultdir"); my $timestamp = `date +"%Y%m%d%H%M%S"`; chomp($timestamp); $running_log_name = "$resultdir/xcattest.log.$timestamp"; if (!open($running_log_fd, ">$running_log_name")) { print "Failed to generate running log file for $program_name: $!\n"; return 1; } $failed_log_name = "$resultdir/failedcases.$timestamp"; $performance_log_name = "$resultdir/performance.report.$timestamp"; return 0; } #-------------------------------------------------------- # Fuction name: calculate_cases_to_be_run # Description: calculate the case scope to be handle depending on option -b,-c,-t # Atrributes: # $cases_to_be_run_ref (output attribe) # The reference of array to save the cases to be handled # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #------------------------------------------------------- sub calculate_cases_to_be_run { my $cases_to_be_run_ref = shift; my $error_ref = shift; my $fd = undef; my @cases = (); $$error_ref = ""; if ($bundlelist) { my @bundles = split(",", $bundlelist); foreach my $bundle (@bundles) { #if $bundle doesn't include path information, find $bundle under $bundledir by default if ($bundle !~ /\//) { $bundle = "$bundledir/$bundle"; } if (!-e "$bundle") { $$error_ref = "There isn't file $bundle\n"; last; } if (!open($fd, "$bundle")) { $$error_ref = "Can't open file $bundle:$!\n"; last; } while (my $line = <$fd>) { chomp($line); $line =~ s/#.+//g if ($line =~ "#" && $line !~ "^#INCLUDE"); $line =~ s/^\s+|\s+$//g; next if ((length($line) == 0) || ($line =~ /^description\s*:\s*(.*)/)); push(@cases, $line); } close($fd); } return 1 if (length($$error_ref) != 0); #to handle "#INCLUDE:XXXXXXX#" line my $casetxt = join(',', @cases); for (; ;) { if ($casetxt =~ /#INCLUDE:[^#^\n]+#/) { $casetxt =~ s/#INCLUDE:([^#^\n]+)#/expend_include_file($1)/eg; } else { last; } } @cases = split(",", $casetxt); my @error = grep /INCLUDEBAD/, @cases; if (@error) { $$error_ref = join("\n", @error); return 1; } @{$cases_to_be_run_ref} = @cases; } elsif ($caselist) { @cases_to_be_run = split(",", $caselist); } elsif ($cmdlist) { my @cmds = split /,/, $cmdlist; my @files = (); my @cmdfiles = (); get_files_recursive("$casedir", \@files); for (my $countfile = 0 ; $countfile < @files ; $countfile++) { for (my $countcmd = 0 ; $countcmd < @cmds ; $countcmd++) { if ($files[$countfile] =~ m/\/$cmds[$countcmd]\/case/) { push(@cmdfiles, glob("$files[$countfile]")); } } } my $fd = undef; foreach my $file (@cmdfiles) { if (!open($fd, "<$file")) { $$error_ref = "can't open $file:$!"; return 1; } while (my $line = <$fd>) { $line =~ s/^\s+|#[^!].+|\s+$//g; #skip blank and comment lines next if (length($line) == 0 || ($line =~ /^\s*#/)); if ($line =~ /^start\s*:\s*(.*)/) { push @{$cases_to_be_run_ref}, $1; } } close($fd); } } if($search_expression){ my @rest_cases_to_be_run=(); $rst = filter_case_by_label($cases_to_be_run_ref,\%case_label_map,\@rest_cases_to_be_run, $error_ref); if ($rst) { return 1; } @$cases_to_be_run_ref=@rest_cases_to_be_run; } return 0; } #-------------------------------------------------------- # Fuction name: expend_include_file # Description: To support '#INCLUDE" label in bundle file, to expend the include file # Atrributes: $bundle (input attribute): the name of bundle # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub expend_include_file { my $bundle = shift; my $fd = undef; my $bundlepath = dirname($bundle); my @cases = (); if ($bundlepath eq ".") { $bundle = "$bundledir/$bundle"; } if (!-e "$bundle") { return "#INCLUDEBAD:cannot find $bundle#"; } if (!open($fd, "<$bundle")) { return "#INCLUDEBAD:cannot open $bundle $!#"; } while (my $line = <$fd>) { chomp($line); $line =~ s/#.+//g if ($line =~ "#" && $line !~ "^#INCLUDE"); $line =~ s/^\s+|\s+$//g; next if ((length($line) == 0) || ($line =~ /^description\s*:\s*(.*)/)); push(@cases, $line); } close($fd); return join(",", @cases); } #-------------------------------------------------------- # Fuction name: to_exit # Description: customize exit function, include clean up environment # Atrributes: # Retrun code: #-------------------------------------------------------- sub to_exit { my $exit_code = shift; close($running_log_fd) if (defined $running_log_fd); &runcmd("rm -rf /tmp/xCATdbbackup") if (-d "/tmp/xCATdbbackup"); exit $exit_code; } #-------------------------------------------------------- # Fuction name: check_option_validity # Description: check the validity of command line # Atrributes: # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub check_option_validity { my $error_ref = shift; if ($list) { my @vaild_list_method = (); if ($bundlelist || $caselist || $cmdlist || $search_expression) { @vaild_list_method = ("caselist", "caseinfo", "casenum"); } else { @vaild_list_method = ("caselist", "caseinfo", "casenum", "bundleinfo", "labelinfo"); } if (!(grep { /^$list$/ } @vaild_list_method)) { $$error_ref = "Unsupport list method for option l"; return 1; } } return 0; } #-------------------------------------------------------- # Fuction name: show_case_info # Description: to show case name and description # Atrributes: # $cases_to_be_run_ref (input attribe) # The reference of array to save the cases to be handled # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub show_case_info { my $cases_to_be_run_ref = shift; my $error_ref = shift; my @cases = (); my %case_name_index_map; my $rst = load_case($cases_to_be_run_ref, \@cases, \%case_name_index_map, $error_ref, $NORUN); if ($rst) { return 1; } #print Dumper \@cases; my %caseinfo; my $case_num = @{$cases_to_be_run_ref}; if ($case_num) { foreach my $case (@{$cases_to_be_run_ref}) { my $casedes = "without description"; $casedes = $cases[ $case_name_index_map{$case} ]->{description} if ($cases[ $case_name_index_map{$case} ]->{description}); $caseinfo{$case} = $casedes; } } else { foreach my $case (@cases) { my $casedes = "without description"; $casedes = $case->{description} if ($case->{description}); $caseinfo{ $case->{name} } = $casedes; } } print_table(\%caseinfo); return 0; } #-------------------------------------------------------- # Fuction name: show_bundle_info # Description: show bundle name and description # Atrributes: # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub show_bundle_info { my $error_ref = shift; my %bundleinfo; my $dd = undef; my @bundlefiles = (); if (!opendir(DIR, $bundledir)) { $$error_ref = "Can't open directory $bundledir: $!"; return 1; } my @files = readdir(DIR); foreach my $file (@files) { next if (-d $file); next if ($file =~ /^\./); push(@bundlefiles, $file); } closedir(DIR); foreach my $bundlefile (@bundlefiles) { $bundleinfo{$bundlefile} = "without description"; } my $fd = undef; foreach my $bundlefile (@bundlefiles) { if (!open($fd, "< $bundledir$bundlefile")) { $$error_ref = "Can't open bundle file $bundledir$bundlefile $!"; return 1; } while (my $line = <$fd>) { $line =~ s/^\s+|#.+|\s+$//g; if ($line =~ /^description\s*:\s*(.*)/) { $bundleinfo{$bundlefile} = $1; last; } } close($fd); } print_table(\%bundleinfo); return 0; } #-------------------------------------------------------- # Fuction name: load_config_file # Description: load config file indicated by option -f # Atrributes: # $configfile (input attribute) # The config file name # $config_ref (output attribute) # The reference of a hash to save the contect of config file # The hash %config is a global attribute. # The structure of %config: # config{object}{type}{name}{attr} # config{table}{name}{entry}{key} # config{script_prev}->[] # config{script_post}->[] # config{var}{varname} # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub load_config_file { my $configfile = shift; my $config_ref = shift; my $error_ref = shift; my $type = undef; my $sub_type = undef; my $name = undef; my $attr = undef; my $value = undef; my $c = 0; my $cmd = undef; my $mgt_name = undef; my $fd = undef; if (!open($fd, "$configfile")) { $$error_ref = "Error: can't open xCAT config file $configfile: $!"; return 1; } while (my $line = <$fd>) { $line =~ s/^\s+|#.+|\s+$//g; next if (length($line) == 0); #Table name can not contain "_" if ($line =~ /\[\s*(\w+)\_(\w+)\s*\]/) { $type = $1; $sub_type = $2; $name = undef; $c = 0; } elsif ($line =~ /\[\s*System|Custom\s*\]/) { $type = "Varible"; } elsif ($type eq "Table") { ##TABLE BLOCK## if ($line =~ /(\w+)\s*=\s*([\w\.\-]+)/) { $attr = $1; $value = $2; if ($name && ($$config_ref{table}{$sub_type}{$name}{__KEY__} ne $attr)) { $$config_ref{table}{$sub_type}{$name}{$attr} = $value; } else { $name = $value; $$config_ref{table}{$sub_type}{$name}{__KEY__} = $attr; } } } elsif ($type eq "Object") { ##OBJECT BLOCK## if ($line =~ /(\w+)\s*=\s*([:,\w\.\-\/]+)/) { $attr = $1; $value = $2; if ($attr eq "Name") { $name = $value; } elsif (!defined($name)) { $$error = "Please give name for Object section"; close($fd); return 1; } else { $$config_ref{object}{$sub_type}{$name}{$attr} = $value; } } } elsif ($type eq "Script") { ##SCRIPT_BLOCK## if ($sub_type eq "Prev") { $$config_ref{script_prev}->[$c] = $line; $c = $c + 1; } elsif ($sub_type eq "Post") { $$config_ref{script_post}->[$c] = $line; $c = $c + 1; } } elsif ($type eq "Varible") { ##NODE_BLOCK## if ($line =~ /(\w+)\s*=\s*([\w\.\-\+\/:]+)/) { $$config_ref{var}{$1} = $2; } } } if (exists $$config_ref{object}) { foreach my $type (keys %{ $$config_ref{object} }) { foreach my $name (keys %{ $$config_ref{object}{$type} }) { log_this($running_log_fd, "OBJECT:$name,TYPE:$type"); foreach my $attr (keys %{ $$config_ref{object}{$type}{$name} }) { log_this($running_log_fd, " $attr = $$config_ref{object}{$type}{$name}{$attr};"); } } } } if (exists $$config_ref{table}) { foreach my $type (keys %{ $$config_ref{table} }) { log_this($running_log_fd, "TABLE:$type"); foreach my $name (keys %{ $$config_ref{table}{$type} }) { log_this($running_log_fd, " $$config_ref{table}{$type}{$name}{__KEY__} = $name"); foreach my $attr (keys %{ $$config_ref{table}{$type}{$name} }) { if ($attr ne '__KEY__') { log_this($running_log_fd, " $attr = $$config_ref{table}{$type}{$name}{$attr}"); } } } } } if (exists $$config_ref{script_prev}) { log_this($running_log_fd, "Script_Prev:"); foreach $cmd (@{ $$config_ref{script_prev} }) { log_this($running_log_fd, " $cmd"); } } if (exists $$config_ref{script_post}) { log_this($running_log_fd, "Script_Post:"); foreach $cmd (@{ $$config_ref{script_post} }) { log_this($running_log_fd, " $cmd"); } } if (exists $$config_ref{var}) { log_this($running_log_fd, "Varible:"); foreach my $varname (keys %{ $$config_ref{var} }) { log_this($running_log_fd, " $varname = $$config_ref{var}{$varname}"); } } close($fd); return 0; } #-------------------------------------------------------- # Fuction name: load_case # Description: load test case # Atrributes: # $cases_to_be_run_ref (input attribe) # The reference of array to save the cases to be handled # $case_ref (input attribe) # The reference of a array of hash to save the contect of case # The array @cases is a global attribute. # The struture of @cases are: # $cases[index]->{name} # $cases[index]->{os} # $cases[index]->{arch} # $cases[index]->{hcp} # $cases[index]->{type} # $cases[index]->{stop} # $cases[index]->{description} # $cases[index]->{attribute} # $cases[index]->{cmd}->[index][index] # $cases[index]->{check}->[index][index] # $cases[index]->{cmdcheck}->[index][index] # $case_name_index_map_ref (input attribute) # The reference of a hash to save the mapping of test name ane its index in @cases # Due to there maybe is more than one implementation for one case # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # $run_case_flag (input attribute) # The flag of whether run these case nex. # 0 means no, just load case basic information, used by "searching informaiotn funtion of xcattest" # 1 means yes, load case basic information and parse the attribure at the sametime. used by "run case funtion of xcattest" # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub load_case { my $cases_to_be_run_ref = shift; my $case_ref = shift; my $case_name_index_map_ref = shift; my $error_ref = shift; my $run_case_flag = shift; #if @{$cases_to_be_run_ref} is empty, that means not indicate test case scope by command line option -b,-t,-c #load all cases shipped by xcat test package my $load_all_case_flag = 0; my $case_num = @{$cases_to_be_run_ref}; $load_all_case_flag = 1 if ($case_num == 0); @$case_ref = (); %$case_name_index_map_ref=(); my @files = (); get_files_recursive("$casedir", \@files); my $line; my $i = 0; my $j = -1; my $z = 0; my $m = 0; my $newcmdstart = 0; my $skip = 0; my $fd = undef; my %invalidcases; my %case_name_index_map_bak; foreach my $file (@files) { if (!open($fd, "<$file")) { $$error_ref = "Can't open $file: $!"; return 1; } while ($line = <$fd>) { if ($newcmdstart) { $line =~ s/\s+$//g; } else { $line =~ s/^\s+|#[^!].+|\s+$//g; } #skip blank and comment lines next if (length($line) == 0 || ($line =~ /^\s*#/)); if ($line =~ /^start\s*:\s*(.*)/) { my $name = $1; if ($load_all_case_flag) { if (is_valid_case_name($name)) { $skip = 0; $j = -1; $case_ref->[$i] = {}; $case_ref->[$i]->{name} = $name; $case_ref->[$i]->{filename} = $file; $case_ref->[$i]->{label} = 0; $case_ref->[$i]->{label_str} = ""; if (exists($$case_name_index_map_ref{"$name"})) { $case_name_index_map_bak{"$name"} = $$case_name_index_map_ref{"$name"}; } $$case_name_index_map_ref{"$name"} = $i; $newcmdstart = 0; } else { $skip = 1; push @{ $invalidcases{"invalidcasename"} }, $name; } } else { if (!(grep { /^$name$/ } @{$cases_to_be_run_ref})) { $skip = 1; next; } else { if (is_valid_case_name($name)) { $skip = 0; $j = -1; $case_ref->[$i] = {}; $case_ref->[$i]->{name} = $name; $case_ref->[$i]->{filename} = $file; $case_ref->[$i]->{label} = 0; $case_ref->[$i]->{label_str} = ""; if (exists($$case_name_index_map_ref{"$name"})) { $case_name_index_map_bak{"$name"} = $$case_name_index_map_ref{"$name"}; } $$case_name_index_map_ref{"$name"} = $i; $newcmdstart = 0; if (grep (/$case_ref->[$i]->{name}/, @{ $invalidcases{"noruncases"} })) { delete_item_from_array($case_ref->[$i]->{name}, $invalidcases{"noruncases"}); } } else { $skip = 1; push @{ $invalidcases{"invalidcasename"} }, $name; } } } } elsif ($line =~ /^os\s*:\s*(\w[\w\, ]+)/) { next if $skip; $case_ref->[$i]->{os} = $1; if ($run_case_flag) { #To judge whether need to skip the current case my @validoslist = split(",", $case_ref->[$i]->{os}); my @newvalidoslist = (); foreach my $validos (@validoslist) { if ($validos =~ /linux/i) { push(@newvalidoslist, ("rhel", "sles", "ubuntu")); } else { push(@newvalidoslist, $validos); } } my $currentos = get_current_os(); my $valid = 0; foreach my $os (@newvalidoslist) { if ($currentos =~ /$os/i) { $valid = 1; if (grep (/$case_ref->[$i]->{name}/, @{ $invalidcases{"noruncases"} })) { delete_item_from_array($case_ref->[$i]->{name}, $invalidcases{"noruncases"}); } last; } } unless ($valid) { #$skip = 1; if (exists($case_name_index_map_bak{ $case_ref->[$i]->{name} })) { $$case_name_index_map_ref{ $case_ref->[$i]->{name} } = $case_name_index_map_bak{ $case_ref->[$i]->{name} }; } else { delete $$case_name_index_map_ref{ $case_ref->[$i]->{name} }; if (!grep (/$case_ref->[$i]->{name}/, @{ $invalidcases{"noruncases"} })) { push @{ $invalidcases{"noruncases"} }, $case_ref->[$i]->{name}; } } } } $newcmdstart = 0; } elsif ($line =~ /^arch\s*:\s*(\w[\w\, ]+)/) { next if $skip; $case_ref->[$i]->{arch} = $1; if ($run_case_flag) { #To judge whether need to skip the current case my @vaild_archs_tmp = split(",", $case_ref->[$i]->{arch}); my @vaild_archs=(); foreach my $arch (@vaild_archs_tmp){ my $tmp_str=""; if ($arch =~ /ppc/i && $arch !~ /le|el/i) { $tmp_str = "ppc"; } elsif ($arch =~ /ppc/i && $arch =~ /le|el/i) { $tmp_str = "ppc64le"; } elsif ($arch =~ /x86/i) { $tmp_str = "x86"; } push @vaild_archs, $tmp_str; } my $env_arch = ""; if (exists($config{var}{ARCH})) { $env_arch = $config{var}{ARCH}; } else { $env_arch = `uname -m`; chomp($env_arch); } if ($env_arch =~ /ppc/i && $env_arch !~ /le|el/i) { $env_arch = "ppc"; } elsif ($env_arch =~ /ppc/i && $env_arch =~ /le|el/i) { $env_arch = "ppc64le"; } elsif ($env_arch =~ /x86/i) { $env_arch = "x86"; } my $valid = 0; foreach my $arch (@vaild_archs){ if ($arch eq $env_arch) { $valid = 1; if (grep (/$case_ref->[$i]->{name}/, @{ $invalidcases{"noruncases"} })) { delete_item_from_array($case_ref->[$i]->{name}, $invalidcases{"noruncases"}); } last; } } unless ($valid) { if (exists($case_name_index_map_bak{ $case_ref->[$i]->{name} })) { $$case_name_index_map_ref{ $case_ref->[$i]->{name} } = $case_name_index_map_bak{ $case_ref->[$i]->{name} }; } else { delete $$case_name_index_map_ref{ $case_ref->[$i]->{name} }; if (!grep (/$case_ref->[$i]->{name}/, @{ $invalidcases{"noruncases"} })) { push @{ $invalidcases{"noruncases"} }, $case_ref->[$i]->{name}; } } } } $newcmdstart = 0; } elsif ($line =~ /^hcp\s*:\s*(\w[\w\, ]+)/) { next if $skip; $case_ref->[$i]->{hcp} = $1; if ($run_case_flag) { #To judge whether need to skip the current case my $valid = 0; my @valid_hcps = split(",", $case_ref->[$i]->{hcp}); foreach my $hcp (@valid_hcps){ $hcp =~ s/^\s+|\s+$//g; if (exists($config{var}{HCP}) && ($hcp =~ /^$config{var}{HCP}$/i)) { $valid = 1; if (grep (/$case_ref->[$i]->{name}/, @{ $invalidcases{"noruncases"} })) { delete_item_from_array($case_ref->[$i]->{name}, $invalidcases{"noruncases"}); } last; } } unless ($valid) { if (exists($case_name_index_map_bak{ $case_ref->[$i]->{name} })) { $$case_name_index_map_ref{ $case_ref->[$i]->{name} } = $case_name_index_map_bak{ $case_ref->[$i]->{name} }; } else { delete $$case_name_index_map_ref{ $case_ref->[$i]->{name} }; if (!grep (/$case_ref->[$i]->{name}/, @{ $invalidcases{"noruncases"} })) { push @{ $invalidcases{"noruncases"} }, $case_ref->[$i]->{name}; } } } } $newcmdstart = 0; } elsif ($line =~ /^type\s*:\s*(\w[\w\,-]+)/) { next if $skip; $case_ref->[$i]->{type} = $1; $newcmdstart = 0; } elsif ($line =~ /^stop\s*:\s*(\w[\w\,]+)/) { next if $skip; $case_ref->[$i]->{stop} = $1; $newcmdstart = 0; } elsif ($line =~ /^description\s*:\s*(.+)/) { next if $skip; $case_ref->[$i]->{description} = $1; $newcmdstart = 0; } elsif ($line =~ /^attribute\s*:\s*(\w[\w\,]+)/) { next if $skip; $case_ref->[$i]->{attribute} = $1; $newcmdstart = 0; } elsif ($line =~ /^cmd\s*:\s*([\#\/\$\w].+)/) { next if $skip; $newcmdstart = 0; $j = $j + 1; $z = 0; $m = 0; if ($run_case_flag) { $case_ref->[$i]->{cmd}->[$j][$m] = getvar($1, \%config); if ($case_ref->[$i]->{cmd}->[$j][$m] =~ /miss attribute (.+)/) { update_miss_attr($case_ref->[$i]->{cmd}->[$j][$m], $case_ref->[$i]->{name}, \@{ $invalidcases{"missattr"} }); } } else { $case_ref->[$i]->{cmd}->[$j][$m] = $1; } $newcmdstart = 1; } elsif ($line =~ /^check\s*:\s*(\w.+)/) { next if $skip; if ($run_case_flag) { $case_ref->[$i]->{check}->[$j][$z] = getvar($1, \%config); if ($case_ref->[$i]->{check}->[$j][$z] =~ /miss attribute/) { update_miss_attr($case_ref->[$i]->{check}->[$j][$z], $case_ref->[$i]->{name}, \@{ $invalidcases{"missattr"} }); } } else { $case_ref->[$i]->{check}->[$j][$z] = $1; } $z = $z + 1; $newcmdstart = 0; } elsif ($line =~ /^cmdcheck\s*:\s*(\w.+)/) { next if $skip; if ($run_case_flag) { $case_ref->[$i]->{cmdcheck}->[$j][$z] = getvar($1, \%config); if ($case_ref->[$i]->{cmdcheck}->[$j][$z] =~ /miss attribute/) { update_miss_attr($case_ref->[$i]->{cmdcheck}->[$j][$z], $case_ref->[$i]->{name}, \@{ $invalidcases{"missattr"} }); } } else { $case_ref->[$i]->{cmdcheck}->[$j][$z] = $1; } $z = $z + 1; $newcmdstart = 0; } elsif ($line =~ /^end/) { next if $skip; $i = $i + 1; $newcmdstart = 0; } elsif ($newcmdstart) { ++$m; $case_ref->[$i]->{cmd}->[$j][$m] = $line; } } close($fd); } my @wrong_cases = (); my $caseerror = 0; if ($invalidcases{"invalidcasename"}) { #log_this($running_log_fd, "Case name invalid:", @{ $invalidcases{"invalidcasename"} }); $$error_ref = "Case name invalid: " . join(",", @{ $invalidcases{"invalidcasename"} }); push @wrong_cases, @{ $invalidcases{"invalidcasename"} }; $caseerror = 2; } if ($run_case_flag) { if ($invalidcases{"missattr"}) { log_this($running_log_fd, "Miss attribute:", @{ $invalidcases{"missattr"} }); #$$error_ref = "Miss attribute: " . join(",", @{ $invalidcases{"missattr"} }); foreach my $line (@{ $invalidcases{"missattr"} }) { my @name = split(" ", $line); if (!(grep /$name[0]/, @wrong_cases)) { push @wrong_cases, $name[0]; } } $caseerror = 2; } if ($invalidcases{"noruncases"} && @{ $invalidcases{"noruncases"} }) { log_this($running_log_fd, "Unsuitable current environment:", @{ $invalidcases{"noruncases"} }); push @wrong_cases, @{ $invalidcases{"noruncases"} }; $caseerror = 2; } # To filter unexisted cases my @unexisted_cases; foreach my $case (@{$cases_to_be_run_ref}) { if (!(grep { /^$case$/ } @wrong_cases) && !defined($$case_name_index_map_ref{$case})) { push @unexisted_cases, $case; } } if (@unexisted_cases) { log_this($running_log_fd, "Not existed:", @unexisted_cases); push @wrong_cases, @unexisted_cases; $caseerror = 2; } if ($caseerror) { my @new_cases_to_be_run = (); foreach my $c (@{$cases_to_be_run_ref}) { if (!(grep { /^$c$/ } @wrong_cases)) { push @new_cases_to_be_run, $c; } } @{$cases_to_be_run_ref} = @new_cases_to_be_run; } if (@{$cases_to_be_run_ref}) { log_this($running_log_fd, "To run:", @{$cases_to_be_run_ref}); } else { log_this($running_log_fd, "To run:", "There is no valid case to run"); } } return $caseerror; } #-------------------------------------------------------- # Fuction name: bakup_current_env # Description: back up the xcat db # Atrributes: # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub bakup_current_env { my $error_ref = shift; &runcmd("mkdir -p /tmp/xCATdbbackup"); &runcmd("dumpxCATdb -p /tmp/xCATdbbackup"); if ($::RUNCMD_RC != 0) { $$error_ref = "Fail to backup xCAT database"; &runcmd("rm -rf /tmp/xCATdbbackup"); return 1; } return 0; } #-------------------------------------------------------- # Fuction name: restore_current_env # Description: restore the xcat db # Atrributes: # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub restore_current_env { my $error_ref = shift; &runcmd("restorexCATdb -p /tmp/xCATdbbackup"); &runcmd("rm -rf /tmp/xCATdbbackup"); return 0; } #-------------------------------------------------------- # Fuction name: run_case # Description: # Atrributes: # $cases_to_be_run_ref (input attribe) # The reference of array to save the cases to be handled # $case_ref (input attribe) # The reference of a array of hash to save the contect of case # The array @cases is a global attribute. # Please refer load_case function's comment to get the struture of @cases # $case_name_index_map_ref (input attribute) # The reference of a hash to save the mapping of test name ane its index in @cases # Due to there maybe is more than one implementation for one case # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub run_case { my $cases_to_be_run_ref = shift; my $cases_ref = shift; my $case_name_index_map_ref = shift; my $error_ref = shift; my $total = 0; my $failnum = 0; my $failed_log_fd = undef; if (!open($failed_log_fd, ">$failed_log_name")) { $error_ref = "Failed to generate failed log file for $program_name: $!"; return 1; } foreach my $case (@{$cases_to_be_run_ref}) { my @caselog = (); my $failflag = 0; my $j = 0; ++$total; my $case_start_time = timelocal(localtime()); my $case_start_time_str = scalar(localtime()); log_this($running_log_fd, "------START::$cases_ref->[$case_name_index_map_ref->{$case}]->{name}::Time:$case_start_time_str------\n"); push @caselog, "------START::$cases_ref->[$case_name_index_map_ref->{$case}]->{name}::Time:$case_start_time_str------\n"; push @caselog, "FILENAME:$cases_ref->[$case_name_index_map_ref->{$case}]->{filename}\n"; foreach my $cmd (@{ $cases_ref->[ $case_name_index_map_ref->{$case} ]->{cmd} }) { my $runstart = timelocal(localtime()); my $runstartstr = scalar(localtime()); my $cmdlen = @{$cmd}; my @output = (); my $rc = 0; if ($cmdlen == 1) { #to run single line command log_this($running_log_fd, "RUN:$cmd->[0] [$runstartstr]"); $cmd->[0] = getfunc($cmd->[0]); @output = &runcmd($cmd->[0]); $rc = $::RUNCMD_RC; push(@caselog, "RUN:$cmd->[0] [$runstartstr]"); } else { #to run multiple lines command log_this($running_log_fd, "RUN: [$runstartstr]", @{$cmd}); @output = runscript($cmd); $rc = $::RUNCMD_RC; push(@caselog, ("RUN: [$runstartstr]", @{$cmd})); } my $runstop = timelocal(localtime()); my $diffduration = $runstop - $runstart; log_this($running_log_fd, ("ElapsedTime:$diffduration sec", "RETURN rc = $rc", "OUTPUT:", @output)); push(@caselog, ("ElapsedTime:$diffduration sec", "RETURN rc = $rc", "OUTPUT:", @output)); foreach my $check (@{ $cases_ref->[ $case_name_index_map_ref->{$case} ]->{check}->[$j] }) { last if ($failflag); if ($check =~ /rc\s*([=!]+)\s*(\d+)/) { my $lvalue = $rc; my $op = $1; my $rvalue = $2; if ((($op eq '!=') && ($lvalue == $rvalue)) || (($op eq '==') && ($lvalue != $rvalue))) { $failflag = 1; } if ($failflag) { log_this($running_log_fd, "CHECK:rc $op $rvalue\t[Failed]"); push(@caselog, "CHECK:rc $op $rvalue\t[Failed]"); last; } else { log_this($running_log_fd, "CHECK:rc $op $rvalue\t[Pass]"); push(@caselog, "CHECK:rc $op $rvalue\t[Pass]"); } } elsif ($check =~ /output\s*([=!~]+)\s*(\S.*)/ && $check !~ /output\s*([=!~])\1/) { my $lvalue = join("\n", @output); my $op = $1; my $rvalue = $2; $rvalue = getfunc($rvalue); if ((($op eq '=~') && ($lvalue !~ /$rvalue/)) || (($op eq '!~') && ($lvalue =~ /$rvalue/)) || (($op eq '==') && ($lvalue ne $rvalue)) || (($op eq '!=') && ($lvalue eq $rvalue))) { $failflag = 1; } if ($failflag) { log_this($running_log_fd, "CHECK:output $op $rvalue\t[Failed]"); push(@caselog, "CHECK:output $op $rvalue\t[Failed]"); last; } else { log_this($running_log_fd, "CHECK:output $op $rvalue\t[Pass]"); push(@caselog, "CHECK:output $op $rvalue\t[Pass]"); } } elsif ($check =~ /output\s*~~\s*(\S.*)/) { my $op = "~~"; #my $failflag = 1; my $rvalue = $1; $rvalue = getfunc($rvalue); my $num; if ($rvalue =~ /(\d+)/) { $num = $1; } $rvalue =~ s/(\d+)//; foreach my $line (@output) { chomp($line); if ($line =~ /$rvalue/) { if ($num =~ /^\d+$/) { my $max = $num * 1.1; my $min = $num * 0.9; $line =~ /.*:.*: (\d+) /; if ($1 < $max && $1 > $min) { $failflag = 0; last; } } else { next; } } } if ($failflag) { log_this($running_log_fd, "CHECK:output $op $rvalue\t[Failed]"); push(@caselog, "CHECK:output $op $rvalue\t[Failed]"); last; } else { log_this($running_log_fd, "CHECK:output $op $rvalue\t[Pass]"); push(@caselog, "CHECK:output $op $rvalue\t[Pass]"); } } } foreach my $cmdcheck (@{ $cases_ref->[ $case_name_index_map_ref->{$case} ]->{cmdcheck}->[$j] }) { if ($cmdcheck) { &runcmd($cmdcheck); $rc = $::RUNCMD_RC; if ($rc != 0) { $failflag = 1; log_this($running_log_fd, "CHECK:output $cmdcheck\t[Failed]"); push(@caselog, "CHECK:output $cmdcheck\t[Failed]"); } elsif ($rc == 0) { log_this($running_log_fd, "CHECK:output $cmdcheck\t[Pass]"); push(@caselog, "CHECK:output $cmdcheck\t[Pass]"); } } } $j = $j + 1; log_this($running_log_fd, " "); push(@caselog, " "); } my $case_end_time = timelocal(localtime()); my $case_end_time_str = scalar(localtime()); my $diff = $case_end_time - $case_start_time; if ($failflag) { log_this($running_log_fd, "------END::$cases_ref->[$case_name_index_map_ref->{$case}]->{name}::Failed::Time:$case_end_time_str ::Duration::$diff sec------"); push(@caselog, "------END::$cases_ref->[$case_name_index_map_ref->{$case}]->{name}::Failed::Time:$case_end_time_str ::Duration::$diff sec------"); } else { log_this($running_log_fd, "------END::$cases_ref->[$case_name_index_map_ref->{$case}]->{name}::Passed::Time:$case_end_time_str ::Duration::$diff sec------"); push(@caselog, "------END::$cases_ref->[$case_name_index_map_ref->{$case}]->{name}::Passed::Time:$case_end_time_str ::Duration::$diff sec------"); } if ($failflag) { ++$failnum; print $failed_log_fd (join("\n", @caselog), "\n"); if (defined($cases_ref->[ $case_name_index_map_ref->{$case} ]->{stop}) && ($cases_ref->[ $case_name_index_map_ref->{$case} ]->{stop} =~ /^yes$/)) { $stop_to_keep_env = 1; last; } } } log_this($running_log_fd, "------Total: $total , Failed: $failnum------\n"); close($failed_log_fd); return 0; } #-------------------------------------------------------- # Fuction name: setup_env_by_configure_file # Description: set up environment by the settings in config file # Atrributes: # $config_ref (input attribute) # The reference of global hash %config. # The structure of %config please refer to the comment of function load_config_file # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub setup_env_by_configure_file { my $config_ref = shift; my $error_ref = shift; my $cmd = undef; foreach $cmd (@{ $$config_ref{script_prev} }) { log_this($running_log_fd, "$cmd"); &runcmd($cmd); if ($::RUNCMD_RC != 0) { $$error_ref = "Fail to run $cmd"; return 1; } } if (exists $$config_ref{object}) { foreach my $type (keys %{ $$config_ref{object} }) { foreach my $name (keys %{ $$config_ref{object}{$type} }) { $cmd = "chdef -t $type -o $name"; foreach my $attr (keys %{ $$config_ref{object}{$type}{$name} }) { $cmd = $cmd . " $attr=$$config_ref{object}{$type}{$name}{$attr}"; } log_this($running_log_fd, "$cmd"); runcmd($cmd); if ($::RUNCMD_RC != 0) { $$error_ref = "Fail to run $cmd"; return 1; } } } } if (exists $$config_ref{table}) { foreach my $type (keys %{ $$config_ref{table} }) { foreach my $name (keys %{ $$config_ref{table}{$type} }) { $cmd = "chtab $$config_ref{table}{$type}{$name}{__KEY__}=$name"; foreach my $attr (keys %{ $$config_ref{table}{$type}{$name} }) { if ($attr ne '__KEY__') { $cmd = $cmd . " $type.$attr=$$config_ref{table}{$type}{$name}{$attr}"; } } log_this($running_log_fd, "$cmd"); &runcmd($cmd); if ($::RUNCMD_RC != 0) { $$error_ref = "Fail to run $cmd"; return 1; } } } } return 0; } #-------------------------------------------------------- # Fuction name: detect_current_env # Description: detect some important attribute in current environment, such as os, arch, hcp... # Atrributes: # $config_ref (input attribute) # The reference of global hash %config. # The structure of %config please refer to the comment of function load_config_file # $error_ref (output attribe) # The reference of scalar to save the error message generated during running current function # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub detect_current_env { my $config_ref = shift; my $error_ref = shift; if (!exists $$config_ref{var}{OS}) { my @output = runcmd("uname"); $$config_ref{var}{OS} = lc($output[0]); log_this($running_log_fd, "Detecting: OS = $$config_ref{var}{OS}"); } else { $$config_ref{var}{OS} = lc($$config_ref{var}{OS}); } if (!exists $$config_ref{var}{ARCH}) { if (!exists $$config_ref{var}{CN}) { $$config_ref{var}{ARCH} = "Unknown"; log_this($running_log_fd, "Warning: No compute node defined, can't get ARCH of compute node"); } else { $$config_ref{var}{ARCH} = getnodeattr($$config_ref{var}{CN}, "arch"); if ($$config_ref{var}{ARCH} =~ /le|el/i) { $$config_ref{var}{ARCH} = 'ppc64le'; } elsif ($$config_ref{var}{ARCH} =~ /ppc/i) { $$config_ref{var}{ARCH} = 'ppc'; } elsif ($$config_ref{var}{ARCH} =~ /86/i) { $$config_ref{var}{ARCH} = 'x86'; } log_this($running_log_fd, "Detecting: ARCH = $$config_ref{var}{ARCH}"); } } if (!exists $$config_ref{var}{HCP}) { if (!exists $$config_ref{var}{CN}) { $$config_ref{var}{HCP} = "Unknown"; log_this($running_log_fd, "Warning: No compute node defined, can't get HCP TYPE of compute node"); } else { $$config_ref{var}{HCP} = getnodeattr($$config_ref{var}{CN}, "mgt"); log_this($running_log_fd, "Detecting: HCP = $$config_ref{var}{HCP}"); } } return 0; } #-------------------------------------------------------- # Fuction name: runcmd # Description: run a command after 'cmd' label in one case # Atrributes: # Retrun code: # $::RUNCMD_RC : the return code of command # @$outref : the output of command #-------------------------------------------------------- sub runcmd { my ($cmd) = @_; my $rc = 0; $::RUNCMD_RC = 0; my $outref = []; @$outref = `$cmd 2>&1`; if ($?) { $rc = $?; $rc = $rc >> 8; $::RUNCMD_RC = $rc; } chomp(@$outref); return @$outref; } #-------------------------------------------------------- # Fuction name: runscript # Description: run a script after 'cmd' label in one case # Atrributes: # $script_ref (input attribute) # the reference of array where save every line of script # Retrun code: # $::RUNCMD_RC : the return code of command # @$outref : the output of command #-------------------------------------------------------- sub runscript { my $script_ref = shift; my $time = time(); my $tmpdir = "/tmp/xCATautotest$time/"; my $tmpfile = "$tmpdir/script"; my $rf; mkpath("$tmpdir"); open($rf, ">$tmpfile"); foreach my $line (@$script_ref) { $line = getfunc($line); print $rf "$line\n"; } close($rf); chmod 0755, "$tmpfile"; my @output = runcmd("$tmpfile"); unlink("$tmpfile"); rmdir("$tmpdir"); return @output; } #-------------------------------------------------------- # Fuction name: getnodeattr # Description: get the value of node attribute from current environment # Atrributes: # Retrun code: #-------------------------------------------------------- sub getnodeattr { my $node = shift; my $attr = shift; my $maxtry = 40; return getobjectattr('node', $node, $attr, $maxtry); } #-------------------------------------------------------- # Fuction name: getobjectattr # Description: get the value of object attribute from current DB # Atrributes: # Retrun code: #-------------------------------------------------------- sub getobjectattr { my $objtype = shift; my $objname = shift; my $attr = shift; my $maxtry = shift; $maxtry = 1 unless defined($maxtry) and $maxtry > 0; foreach my $try (0 .. $maxtry) { my @output = runcmd("lsdef -t $objtype -o $objname -i $attr"); if ($::RUNCMD_RC == 0) { foreach my $line (@output) { if ($line =~ /$attr=(\S.+)/) { return $1; } } } } return "Unknown"; } #-------------------------------------------------------- # Fuction name: get_files_recursive # Description: Search all file in one directory recursively # Atrributes: # $dir (input attribute) # The target scan directory # $files_path_ref (output attribute) # the reference of array where save all vaild files under $dir # Retrun code: #-------------------------------------------------------- sub get_files_recursive { my $dir = shift; my $files_path_ref = shift; my $fd = undef; opendir($fd, $dir); for (; ;) { my $direntry = readdir($fd); last unless (defined($direntry)); next if ($direntry =~ m/^\.\w*/); next if ($direntry eq '..'); my $target = "$dir/$direntry"; if (-d $target) { get_files_recursive($target, $files_path_ref); } else { push(@{$files_path_ref}, glob("$target\n")); } } closedir($fd); } #-------------------------------------------------------- # Fuction name: is_valid_case_name # Description: to check if a case name is valid # Atrributes: $casename (input atrribute): the target case name # Retrun code: 0 Success 1 Failed #-------------------------------------------------------- sub is_valid_case_name { my $casename = shift; if ($casename =~ /[^a-zA-Z0-9_-]/) { return 0; } else { return 1; } } #------------------------------------------------------- # Fuction name: get_current_os # Description: # Atrributes: # Retrun code: #-------------------------------------------------------- sub get_current_os { if (-f "/etc/redhat-release") { return "rhels"; } elsif (-f "/etc/SuSE-release") { return "sles"; } elsif (-f "/etc/lsb-release") { return "ubuntu"; } else { return "aix"; } } #-------------------------------------------------------- # Fuction name: generate_performance_report # Description: # Atrributes: # Retrun code: #-------------------------------------------------------- sub generate_performance_report { my $runninglog = shift; my $performancelog = shift; my $error_ref = shift; my $performance_log_fd = undef; if (!open($performance_log_fd, ">$performancelog")) { $$error_ref = "Failed to create performance log $performancelog: $!"; return 1; } print $performance_log_fd "Testcase Duration\n"; print $performance_log_fd "------------------------------------------------------------------------------\n"; my @output = runcmd("cat $runninglog"); my $cmdlineflag = 0; my $line; foreach my $line (@output) { if ($line =~ /^------START:/) { $line =~ s/------//g; print $performance_log_fd "$line\n"; } elsif ($line =~ /^RUN:/) { $cmdlineflag = 1; print $performance_log_fd "$line;\n"; } elsif ($line =~ /^ElapsedTime:/) { $cmdlineflag = 0; print $performance_log_fd "$line\n"; } elsif ($cmdlineflag) { print $performance_log_fd "$line\n"; } elsif ($line =~ /^------END:/) { $line =~ s/------//g; print $performance_log_fd "$line\n\n"; } } close($performance_log_fd); return 0; } #-------------------------------------------------------- # Fuction name: getvar # Description: replace specific attribute in case definition by the value set in config file # Atrributes: # Retrun code: #-------------------------------------------------------- sub getvar { my $str = shift; my $config_ref = shift; while ($str =~ /\$\$(\w+)/) { my $varname = $1; if (exists($$config_ref{var}{$varname})) { $str =~ s/\$\$$varname/$$config_ref{var}{$varname}/g; } else { return "miss attribute $varname"; } } return $str; } #-------------------------------------------------------- # Fuction name: getfunc # Description: replace specific funciton in case definition # Atrributes: # Retrun code: #-------------------------------------------------------- sub getfunc { my $str = shift; while ($str =~ /__(\w+)\(([\s\,\w\$\-\.]*)\)__/) { my $func = $1; my $parameter = $2; my $value = undef; my @para = (); #log_this("parameter is $parameter,fun is $func"); chomp($parameter); @para = split /\s*,\s*/, $parameter; if ($func eq "GETNODEATTR") { $value = getnodeattr($para[0], $para[1]); if ($value eq "Unknown") { $value = ''; } } elsif ($func eq "GETOBJECTATTR") { $value = getobjectattr(@para); if ($value eq "Unknown") { $value = ''; } } elsif ($func eq "INC") { $value = $para[0] + 1; } elsif ($func eq "GETTABLEVALUE") { $value = gettablevalue(@para); } $str =~ s/__$func\($parameter\)__/$value/g; } return $str; } #-------------------------------------------------------- # Fuction name: gettablevalue # Description: get specific table value from current environment # Atrributes: # Retrun code: #-------------------------------------------------------- sub gettablevalue { my ($keyname, $key, $colname, $table) = @_; my @output = runcmd("gettab $keyname=$key $table\.$colname"); return $output[0]; } #-------------------------------------------------------- # Fuction name: print_table # Description: print a hash table in good format # Atrributes: # Retrun code: #-------------------------------------------------------- sub print_table { my $msg_ref = shift; my $desiredwidth = 120; my $screenwidth = (`tput cols` + 0); my $finallen = ($screenwidth > $desiredwidth ? $desiredwidth : $screenwidth); my $maxkeylen = 0; foreach my $key (keys %{$msg_ref}) { my $keylen = length($key); $maxkeylen = $keylen if ($keylen > $maxkeylen); } $maxkeylen += 4; foreach my $key (sort { $a cmp $b } keys %{$msg_ref}) { my @desc = split(" ", $msg_ref->{$key}); my $str = ""; my @formatdesc = (); foreach my $word (@desc) { if (length($str) + length($word) > $finallen - $maxkeylen) { $str =~ s/([^\s]+)\s$/$1/g; push @formatdesc, $str; $str = ""; } $str .= $word . " "; } $str =~ s/([^\s]+)\s$/$1/g; push @formatdesc, $str; print color("green"), "$key", color("reset"); my $space = " " x ($maxkeylen - length($key)); print "$space$formatdesc[0]\n"; delete $formatdesc[0]; $space = " " x $maxkeylen; foreach my $line (@formatdesc) { print "$space$line\n" if (length($line)); } } return 0; } sub update_miss_attr { my $org_str = shift; my $case_name = shift; my $miss_attr_arr_ref = shift; my $insert_flag = 0; my $index = 0; foreach my $str (@{$miss_attr_arr_ref}) { my @words = split(" ", $str); my @org_words = split(" ", $org_str); if ($case_name eq "$words[0]") { if (!(grep { /^$org_words[2]$/ } @words)) { $miss_attr_arr_ref->[$index] .= " $org_words[2]"; } $insert_flag = 1; last; } ++$index; } unless ($insert_flag) { push @{$miss_attr_arr_ref}, "$case_name $org_str"; } } sub delete_item_from_array { my $item = shift; my $array_ref = shift; my @tmp_arr = (); foreach (@$array_ref) { push @tmp_arr, $_ unless ($_ eq $item); } @$array_ref = @tmp_arr; } sub filter_case_by_label { my $cases_to_be_run_ref = shift; my $case_label_map_ref = shift; my $rest_cases_to_be_run_ref = shift; my $error_ref = shift; my @filters = (); my $rst = parse_filters(\@filters, $error_ref); if($rst){ return 1; } #print Dumper \@filters; my @targetcases = (); foreach my $case (keys %{$case_label_map_ref}) { if(@$cases_to_be_run_ref){ next unless(grep { /^$case$/ } @$cases_to_be_run_ref); } foreach my $f (@filters) { my $hit = 1; foreach my $c (@$f) { if (($c > 0) and (($case_label_map_ref->{$case}->{label} & $c) == 0)) { $hit = 0; last; } elsif (($c < 0) and (($case_label_map_ref->{$case}->{label} & ($c * -1)) != 0)) { $hit = 0; last; } } push @targetcases, $case if ($hit and !(grep { /^$case$/ } @targetcases)); } } #print Dumper \@targetcases; for (my $i = 0 ; $i <= $#label_order ; $i++) { foreach my $l (@{ $label_order[$i] }) { foreach my $c (@targetcases) { push @{$rest_cases_to_be_run_ref}, $c if (defined ($label_map{$l}) && (($case_label_map_ref->{$c}->{label} & $label_map{$l}) != 0)); } } } return 0; } sub parse_filters{ my $filters_ref=shift; my $error_ref=shift; my $index = 0; my @undefined_labels; my @exps = split('\|', $search_expression); foreach my $e (@exps) { $e =~ s/\+/ /g; $e =~ s/\-/ -/g; my @tags = split(' ', $e); foreach my $t (@tags) { if ($t =~ /^-(.+)/) { my $tmpt = $1; unless (grep { /^$tmpt$/ } keys(%label_map)) { push @undefined_labels, $tmpt; next; } push @{ $filters_ref->[$index] }, ($label_map{$tmpt} * -1); } else { unless (grep { /^$t$/ } keys(%label_map)) { push @undefined_labels, $t; next; } push @{ $filters_ref->[$index] }, $label_map{$t}; } } $index += 1; } # if (@undefined_labels) { # $$error_ref = "Label \"" . join(",", @undefined_labels) . "\" are not exist. Existed labels are: " . join(" ", keys(%label_map)); # return 1; # } return 0; } sub scan_existed_labels { my $case_label_map_ref = shift; my $label_map_ref = shift; my $error_ref = shift; my $label_value = 1; my @files = (); my @cmdfiles = (); get_files_recursive("$casedir", \@files); foreach my $file (@files) { my @output = runcmd("grep -E \"^start:|^hcp:|^os:|^arch:|^label:\" $file"); my $current_case_name = ""; foreach my $line (@output) { $line =~ s/^\s+|#[^!].+|\s+$//g; #skip blank and comment lines next if (length($line) == 0 || ($line =~ /^\s*#/)); my @labels = (); if ($line =~ /^start\s*:\s*(.*)/) { $current_case_name = $1; $case_label_map_ref->{$current_case_name}->{label} = 0; $case_label_map_ref->{$current_case_name}->{label_str} = ""; } elsif ($line =~ /^os\s*:\s*(\w[\w\, ]+)/) { my @oss = split(",", $1); foreach my $os (@oss) { $os =~ s/^\s+|\s+$//g; push @labels, "os=$os"; } } elsif ($line =~ /^arch\s*:\s*(\w[\w\, ]+)/) { my @archs = split(",", $1); foreach my $arch (@archs) { $arch =~ s/^\s+|\s+$//g; push @labels, "arch=$arch"; } } elsif ($line =~ /^hcp\s*:\s*(\w[\w\, ]+)/) { my @hcps = split(",", $1); foreach my $hcp (@hcps) { $hcp =~ s/^\s+|\s+$//g; push @labels, "hcp=$hcp"; } } elsif ($line =~ /^label\s*:\s*(.+)/) { my @tmp = split(",", $1); foreach my $l (@tmp){ $l =~ s/^\s+|\s+$//g; push @labels, $l; } } if($current_case_name){ foreach my $t (@labels) { unless (exists $label_map_ref->{$t}) { $label_map_ref->{$t} = $label_value; $label_value *= 2; } $case_label_map_ref->{$current_case_name}->{label} |= $label_map_ref->{$t}; $case_label_map_ref->{$current_case_name}->{label_str} .= "$t,"; } } } } return 0; }