github.com/canonical/ubuntu-image@v0.0.0-20240430122802-2202fe98b290/tests/lib/external/snapd-testing-tools/utils/log-analyzer (about)

     1  #!/bin/bash
     2  
     3  show_help() {
     4      echo "usage: log-analyzer list-failed-tasks <EXEC-PARAM> <PARSED-LOG>"
     5      echo "       log-analyzer list-executed-tasks <EXEC-PARAM> <PARSED-LOG>"
     6      echo "       log-analyzer list-successful-tasks <EXEC-PARAM> <PARSED-LOG>"
     7      echo "       log-analyzer list-aborted-tasks <EXEC-PARAM> <PARSED-LOG>"
     8      echo "       log-analyzer list-all-tasks <EXEC-PARAM>"
     9      echo "       log-analyzer list-reexecute-tasks <EXEC-PARAM> <PARSED-LOG>"
    10      echo ""
    11      echo "The log analyzer is an utility wchi provides useful information about a spread"
    12      echo "execution. The main functionality of the analyzer utility is to determine which tests"
    13      echo "have to be re-executed, being able to include the tests that have been aborted, even"
    14      echo "when those tests are not included in the test results."
    15      echo "The log analyzer uses as input the spread expression that was used to run the tests,"
    16      echo "this expression determines which are all the tests to be considered. The second input"
    17      echo "is the output of the log-parser utility, which generates a json file including all the"
    18      echo "information extracted from the raw spread log"
    19      echo ""
    20      echo "Available options:"
    21      echo "  -h --help   show this help message."
    22      echo ""
    23      echo "COMMANDS:"
    24      echo "  list-failed-tasks        list the tasks that failed during execute"
    25      echo "  list-executed-tasks      list the tasks that were executed"
    26      echo "  list-successful-tasks    list the successful tasks"
    27      echo "  list-aborted-tasks       list the aborted tasks (needs spread to be installed)"
    28      echo "  list-all-tasks           list all the tasks"
    29      echo "  list-reexecute-tasks     list the tasks to re-execute to complete (includes aborted and failed tasks)"
    30      echo ""
    31      echo "PARSED-LOG: This is the output generated by the log-parser tool"
    32      echo "EXEC-PARAM: this is the parameter used to run spread (something like this BACKEND:SYSTEM:SUITE)"
    33      echo ""
    34  }
    35  
    36  _check_log() {
    37      local log="$1"
    38  
    39      if [ -z "$log" ]; then
    40          echo "log.analyzer: the log file cannot be empty"
    41          exit 1
    42      elif [ ! -f "$log" ]; then
    43          echo "log.analyzer: the log file $log does not exist"
    44          exit 1
    45      fi
    46  }
    47  
    48  _list_failed() {
    49      local level="$1"
    50      local stage="$2"
    51      local log="$3"
    52  
    53      if [ -z "$level" ]; then
    54          echo "log.analyzer: the first parameter cannot be empty"
    55          exit 1
    56      elif [ ! "$level" = 'task' ] && [ ! "$level" = 'suite' ] && [ ! "$level" = 'project' ]; then
    57          echo "log.analyzer: the first parameter has to be: task, suite or project"
    58          exit 1
    59      fi
    60  
    61      if [ -z "$stage" ]; then
    62          echo "log.analyzer: the second parameter cannot be empty"
    63          exit 1
    64      elif [ ! "$stage" = 'prepare' ] && [ ! "$stage" = 'restore' ]; then
    65          echo "log.analyzer: the second parameter has to be: prepare or restore"
    66          exit 1
    67      fi
    68      _check_log "$log"
    69  
    70      jq -r ".[] | select( .type == \"result\") | select( .result_type == \"Failed\")  | select(.level == \"$level\") | select(.stage == \"$stage\")  | .detail.lines[]" "$log" | cut -d '-' -f2- | xargs
    71  }
    72  
    73  _merge_tasks_lists() {
    74      # Returns the list1 + the tasks in list2 which are not included in list1
    75      local list1="$1"
    76      local list2="$2"
    77      local merged_list="$1"
    78      local list1_file
    79  
    80      list1_file=$(mktemp list1.XXXXXX)
    81      for elem in $list1; do
    82          echo "$elem" >> "$list1_file"
    83      done
    84  
    85      # shellcheck disable=SC2086
    86      for elem2 in $list2; do
    87          if ! grep -Fxq "$elem2" "$list1_file"; then
    88              merged_list="$merged_list $elem2"
    89          fi
    90      done
    91      rm "$list1_file"
    92      echo "$merged_list"
    93  }
    94  
    95  _diff_tasks_lists() {
    96      # Returns the list1 - the tasks in list2
    97      local list1="$1"
    98      local list2="$2"
    99      local diff_list list2_file
   100  
   101      diff_list=""
   102      list2_file=$(mktemp list2.XXXXXX)
   103      for elem in $list2; do
   104          echo "$elem" >> "$list2_file"
   105      done
   106  
   107      # shellcheck disable=SC2086
   108      for elem1 in $list1; do
   109          if ! grep -Fxq "$elem1" "$list2_file"; then
   110              diff_list="$diff_list $elem1"
   111          fi
   112      done 
   113      rm "$list2_file"
   114      echo "$diff_list"
   115  }
   116  
   117  _intersection_tasks_lists() {
   118      # Returns the tasks in list1 which are also in the list2
   119      local list1="$1"
   120      local list2="$2"
   121      local both_list list2_file
   122  
   123      both_list=""
   124      list2_file=$(mktemp list2.XXXXXX)
   125      for elem in $list2; do
   126          echo "$elem" >> "$list2_file"
   127      done
   128      for elem in $list1; do
   129          # -F tells grep to look for fixed strings, not regexps
   130          if grep -Fxq "$elem" "$list2_file"; then
   131              both_list="$both_list $elem"
   132          fi
   133      done
   134      rm "$list2_file"
   135      echo "$both_list"
   136  }
   137  
   138  list_all_tasks() {
   139      local exec_exp="$1"
   140      exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
   141      if ! command -v spread >/dev/null; then
   142          echo "log.analyzer: spread tool is not installed, exiting..."
   143          exit 1
   144      fi
   145  
   146      # shellcheck disable=SC2086
   147      spread -list $exec_exp
   148  }
   149  
   150  _list_executed_and_failed_tasks() {
   151      local exec_exp="$1"
   152      local log="$2"
   153  
   154      if ! command -v spread >/dev/null; then
   155          echo "log.analyzer: spread tool is not installed, exiting..."
   156          exit 1
   157      fi
   158      _check_log "$log"
   159  
   160      local failed_tasks failed_tasks_restore failed_tasks_prepare exec_and_failed_tasks
   161      failed_tasks="$(list_failed_tasks "$exec_exp" "$log")"
   162      failed_tasks_prepare="$(_list_failed task prepare "$log")"
   163      failed_tasks_restore="$(_list_failed task restore "$log")"
   164  
   165      exec_and_failed_tasks="$(_merge_tasks_lists "$failed_tasks" "$failed_tasks_restore")"
   166      _diff_tasks_lists "$exec_and_failed_tasks" "$failed_tasks_prepare"
   167  }
   168  
   169  list_failed_tasks() {
   170      local exec_exp="$1"
   171      local log="$2"
   172  
   173      if [ -z "$exec_exp" ]; then
   174          echo "log.analyzer: execution expression for spread cannot be empty"
   175          exit 1
   176      fi
   177      exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
   178      _check_log "$log"
   179  
   180      local all_tasks failed_tasks
   181      all_tasks="$(list_all_tasks "$exec_exp")"
   182      failed_tasks="$(jq -r '.[] | select( .type == "result") | select( .result_type == "Failed")  | select(.level == "tasks") | .detail.lines[]' "$log" | cut -d '-' -f2- | xargs)"
   183      _intersection_tasks_lists "$failed_tasks" "$all_tasks"
   184  }
   185  
   186  list_reexecute_tasks() {
   187      local exec_exp="$1"
   188      local log="$2"
   189  
   190      if [ -z "$exec_exp" ]; then
   191          echo "log.analyzer: execution expression for spread cannot be empty"
   192          exit 1
   193      fi
   194      exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
   195      _check_log "$log"
   196  
   197      local aborted_tasks exec_and_failed_tasks all_tasks reexec_tasks
   198      aborted_tasks="$(list_aborted_tasks "$exec_exp" "$log")"
   199      all_tasks="$(list_all_tasks "$exec_exp")"
   200      exec_and_failed_tasks="$(_list_executed_and_failed_tasks "$exec_exp" "$log")"
   201  
   202      # Remove the tasks which are not in the filter from the executed and failed
   203      exec_and_failed_tasks="$(_intersection_tasks_lists "$exec_and_failed_tasks" "$all_tasks")"
   204      reexec_tasks="$(_merge_tasks_lists "$aborted_tasks" "$exec_and_failed_tasks")"
   205  
   206      # In case all the tests are failed or aborted, then the execution expression is used to reexecute
   207      if [ "$(echo "$reexec_tasks" | wc -w)" = "$(echo "$all_tasks" | wc -w)" ]; then
   208          echo "$exec_exp"
   209          return 
   210      fi
   211  
   212      # When all the tests were successful, then no tests need to be reexecuted
   213      if [ "$(echo "$reexec_tasks" | wc -w)" = 0 ]; then
   214          return 
   215      fi
   216      echo "$reexec_tasks"
   217  }
   218  
   219  list_successful_tasks() {
   220      local exec_exp="$1"
   221      local log="$2"
   222  
   223      if [ -z "$exec_exp" ]; then
   224          echo "log.analyzer: execution expression for spread cannot be empty"
   225          exit 1
   226      fi
   227      exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
   228      _check_log "$log"
   229  
   230      local all_tasks executed_tasks failed_tasks_restore failed_tasks
   231      all_tasks="$(list_all_tasks "$exec_exp")"
   232      executed_tasks="$(list_executed_tasks "$exec_exp" "$log")"
   233      executed_tasks="$(_intersection_tasks_lists "$executed_tasks" "$all_tasks")"
   234      failed_tasks="$(list_failed_tasks "$exec_exp" "$log")"
   235      failed_tasks_restore="$(_list_failed task restore "$log")"
   236  
   237      if [ -n "$failed_tasks_restore" ]; then
   238          failed_tasks="$(_merge_tasks_lists "$failed_tasks" "$failed_tasks_restore")"
   239      fi
   240  
   241      if [ -n "$failed_tasks" ]; then
   242          executed_tasks="$(_diff_tasks_lists "$executed_tasks" "$failed_tasks")"
   243      fi    
   244      
   245      echo "$executed_tasks"
   246  }
   247  
   248  list_executed_tasks() {
   249      local exec_exp="$1"
   250      local log="$2"
   251  
   252      if [ -z "$exec_exp" ]; then
   253          echo "log.analyzer: execution expression for spread cannot be empty"
   254          exit 1
   255      fi
   256      exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
   257      _check_log "$log"
   258  
   259      local all_tasks executed_tasks
   260      all_tasks="$(list_all_tasks "$exec_exp")"
   261      executed_tasks="$(jq -r '.[] | select( .type == "action") | select( .verb == "Executing") | .task' "$log")"
   262      _intersection_tasks_lists "$executed_tasks" "$all_tasks"
   263  }
   264  
   265  list_aborted_tasks() {
   266      local exec_exp="$1"
   267      local log="$2"
   268  
   269      if [ -z "$exec_exp" ]; then
   270          echo "log.analyzer: execution expression for spread cannot be empty"
   271          exit 1
   272      fi
   273      exec_exp="$(echo "$exec_exp" | tr ',' ' ')"
   274      _check_log "$log"
   275  
   276      local all_tasks executed_tasks failed_tasks_prepare failed_tasks_restore failed_tasks
   277      all_tasks="$(list_all_tasks "$exec_exp")"
   278      executed_tasks="$(list_executed_tasks "$exec_exp" "$log")"
   279      failed_tasks="$(list_failed_tasks "$exec_exp" "$log")"
   280      failed_tasks_prepare="$(_list_failed task prepare "$log")"
   281      failed_tasks_restore="$(_list_failed task restore "$log")"
   282  
   283      # In case no tasks for the expression, the aborted list is empty
   284      if [ -z "$all_tasks" ]; then
   285          return
   286      fi
   287  
   288      # In case no tasks are successfully executed, all the tasks - the failed ones are the aborted
   289      if [ -z "$executed_tasks" ]; then
   290          exec_and_failed_tasks="$(_list_executed_and_failed_tasks "$exec_exp" "$log")"
   291          _diff_tasks_lists "$all_tasks" "$exec_and_failed_tasks"
   292          return
   293      fi
   294      
   295      # In other cases the aborted tasks are all the tasks - the executed - the that failed 
   296      _diff_tasks_lists "$all_tasks" "$executed_tasks"
   297  }
   298  
   299  main() {
   300      if [ $# -eq 0 ]; then
   301          show_help
   302          exit 0
   303      fi
   304  
   305      local subcommand="$1"
   306      local action=
   307      while [ $# -gt 0 ]; do
   308          case "$1" in
   309              -h|--help)
   310                  show_help
   311                  exit 0
   312                  ;;
   313              *)
   314                  action=$(echo "$subcommand" | tr '-' '_')
   315                  shift
   316                  break
   317                  ;;
   318          esac
   319      done
   320  
   321      if [ -z "$(declare -f "$action")" ]; then
   322          echo "log.analyzer: no such command: $subcommand" >&2
   323          show_help
   324          exit 1
   325      fi
   326  
   327      "$action" "$@"
   328  }
   329  
   330  main "$@"