text: #!/usr/bin/env python3
  # OpenPOWER Automated Test Project
  #
  # Contributors Listed Below - COPYRIGHT 2015,2017
  # [+] International Business Machines Corp.
  #
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  # implied. See the License for the specific language governing
  # permissions and limitations under the License.
  
  # This test runs FWTS and munges the JSON report output into
  # python unittest.TestCase objects, so we get the individual
  # failure/successes into the TestResult output (e.g. junit XML)
  
  '''
  FWTS
  ----
  
  Run the Canonical FirmWare Test Suite (FWTS, see
  https://wiki.ubuntu.com/FirmwareTestSuite for details).
  
  Largely, if we can implement a test as part of FWTS, we should do it there
  as it's easy to run and packaged.
  '''
  
  import time
  import subprocess
  import re
  import sys
  import os
  
  import OpTestConfiguration
  import unittest
  from common.OpTestSystem import OpSystemState
  from common.Exceptions import CommandFailed
  import common.OpTestMambo as OpTestMambo
  import common.OpTestQemu as OpTestQemu
  
  import json
  
  import logging
  import OpTestLogger
  log = OpTestLogger.optest_logger_glob.get_logger(__name__)
  
  
  class FWTSCommandFailed(unittest.TestCase):
      FAIL = None
  
      def runTest(self):
          self.assertEqual(self.FAIL, None, str(self.FAIL))
  
  
  class FWTSCommandSkipped(unittest.TestCase):
      SKIP = None
  
      def runTest(self):
          raise unittest.SkipTest("QEMU/Mambo running so skipping FWTS tests")
  
  
  class FWTSVersion(unittest.TestCase):
      MAJOR = None
      MINOR = None
  
      def version_check(self):
          if self.MAJOR is None and self.MINOR is None:
              self.skipTest("Test not meant to be run this way.")
          return (int(self.MAJOR) == 17 and int(self.MINOR) >= 1) or int(self.MAJOR) > 17
  
      def runTest(self):
          self.assertTrue(self.version_check(),
                          'FWTS must be at least Version 17.01')
  
  
  class FWTSTest(unittest.TestCase):
      SUBTEST_RESULT = None
      CENTAURS_PRESENT = True
      IS_FSP_SYSTEM = False
      FWTS_MAJOR_VERSION = None
      FWTS_MINOR_VERSION = None
  
      def runTest(self):
          if self.SUBTEST_RESULT is None:
              self.skipTest("Test not meant to be run this way.")
  
          DTC_FLASH_INVALID_LENGTH = 'dtc reports warnings from device tree:'\
              'Warning (reg_format): "reg" property in /ibm,opal/flash@0 has '\
              'invalid length (8 bytes) (#address-cells == 0, #size-cells == 0)'\
              '\n'
          if self.SUBTEST_RESULT.get('log_text') == DTC_FLASH_INVALID_LENGTH:
              self.skipTest('/ibm,opal/flash@0 known warning')
  
          # Some FWTS verions barfed (incorrectly) on missing nodes
          # in the device tree. If we spot this, skip the test
          # this work-around should be removed when the FWTS version readily
          # available from the archives no longer has this problem
          if not (self.SUBTEST_RESULT.get('failure_label') == 'None'):
              log_text = self.SUBTEST_RESULT.get('log_text')
              if re.match('Property of "(status|manufacturer-id|part-number|'
                          'serial-number)" for '
                          '"/sys/firmware/devicetree/base/memory-buffer',
                          log_text):
                  self.skipTest("FWTS bug: Incorrect Missing '(status|"
                                "manufacturer-id|part-number|serial-number)' "
                                "property in memory-buffer/dimm")
              if re.match('property "serial-number" contains unprintable '
                          'characters', log_text):
                  self.skipTest("FWTS bug: DIMM VPD has binary serial number")
              # The following is a guess based on when
              # https://lists.ubuntu.com/archives/fwts-devel/2018-April/010318.html
              # will be merged
              if self.FWTS_MAJOR_VERSION <= 18:
                  if self.FWTS_MAJOR_VERSION < 18 or self.FWTS_MINOR_VERSION < 5:
                      if re.match('CPUFreqClaimedMax',
                                  self.SUBTEST_RESULT.get('failure_label')):
                          self.skipTest("Bug in FWTS r.e. boost frequencies, "
                                        "fixed sometime after April 2018")
  
              # On FSP machines, memory-buffers (centaurs) aren't present in DT
              # and FWTS 17.03 (at least) expects them to be, so skip those
              # failures
              if not self.CENTAURS_PRESENT and re.match(
                      'No MEM devices \(memory-buffer', log_text):
                  self.skipTest("FWTS assumes Centaurs present on FSP systems")
  
              if self.IS_FSP_SYSTEM and re.match(
                      'Property of "(board-info|part-number|serial-number|vendor'
                      '|ibm,slot-location-code)" for '
                      '"/sys/firmware/devicetree/base/xscom@.*" was not able to '
                      'be retrieved. Check the installation for the CPU device '
                      'config for missing nodes in the device tree if you '
                      'expect CPU devices', log_text):
                  self.skipTest("FWTS assumes some nodes present on FSP systems "
                                "which aren't")
  
              if re.match('Attempt was made to stop the opal-prd.service but '
                          'was not successful', log_text):
                  self.skipTest("FWTS bug: prd did actually stop, and there's "
                                "something strange with FWTS")
  
              if re.match('OPAL "/ibm,firmware-versions" firmware version from '
                          'device tree node "open-power" was not found',
                          log_text):
                  self.skipTest("FWTS known issue: 'open-power' version no "
                                "longer required")
  
              # We currently guess that all these are going to be merged for
              # FWTS 18.05 :)
              # To be extra cautious, allowing them to fail for all of 18.XX
              # though.
              if self.FWTS_MAJOR_VERSION <= 18:
                  if re.match('CPUPstateLimitsTestFail',
                              self.SUBTEST_RESULT.get('failure_label')):
                      self.skipTest("FWTS known issue: "
                                    "https://lists.ubuntu.com/archives/fwts-devel/2018-April/010315.html")
  
                  if re.match('DeviceTreeBaseDTCWarnings', self.SUBTEST_RESULT.get('failure_label')):
                      self.skipTest(
                          "FWTS known issue: https://lists.ubuntu.com/archives/fwts-devel/2018-April/010326.html")
  
                  if re.match('Property of "(board-info|vendor|'
                              'ibm,slot-location-code)" for '
                              '"/sys/firmware/devicetree/base/xscom.*" was not '
                              'able to be retrieved. Check the installation for '
                              'the CPU device config for missing nodes in the '
                              'device tree if you expect CPU devices.',
                              log_text):
                      self.skipTest(
                          "FWTS/firmware known issue: https://lists.ubuntu.com/archives/fwts-devel/2018-April/010329.html")
  
                  if re.match('No MEM DIMM devices \(memory-buffer\) were found '
                              'in "/sys/firmware/devicetree/base" with a status '
                              'of "okay" or "ok".  This is unexpected so please '
                              'check your system setup for issues.', log_text):
                      self.skipTest(
                          "FWTS/firmware known issue: https://lists.ubuntu.com/archives/fwts-devel/2018-April/010330.html")
  
          self.assertEqual(self.SUBTEST_RESULT.get('failure_label'), 'None',
                           self.SUBTEST_RESULT)
  
  
  class FWTS(unittest.TestSuite):
      '''
      Run the FWTS tests, importing the test results into `op-test`.
  
      This is implemented as a TestSuite rather than a TestCase so we can add
      multiple TestCase results to the `op-test` test results.
      '''
  
      def add_fwts_results(self, major_version, minor_version):
          host = self.cv_HOST
          try:
              fwtsjson = host.host_run_command(
                  "PATH=/usr/local/bin:$PATH fwts -q -r stdout --log-type=json")
          except CommandFailed as cf:
              # FWTS will have exit code of 1 if any test fails,
              # we want to ignore that and parse the output.
              fwtsjson = cf.output
              if cf.exitcode not in [0, 1]:
                  command_failed = FWTSCommandFailed()
                  command_failed.FAIL = cf
                  self.real_fwts_suite.addTest(command_failed)
  
          r = json.loads('\n'.join(fwtsjson), encoding='latin-1')
          tests = []
          for fwts in r['fwts']:
              for k in fwts:
                  if k == "tests":
                      tests = fwts[k]
  
          for test_container in tests:
              for tr in test_container:
                  js_suite = test_container[tr][0]
                  js_subtests = test_container[tr][1]
                  suite = unittest.TestSuite()
  
                  for sts in js_subtests:
                      if sts == "subtests":
                          for subtest in js_subtests[sts]:
                              for st_info in subtest['subtest']:
                                  if not st_info.get('subtest_results'):
                                      continue
                                  for st_r in st_info.get('subtest_results'):
                                      t = FWTSTest()
                                      t.SUBTEST_RESULT = st_r
                                      t.CENTAURS_PRESENT = self.centaurs_present
                                      t.FWTS_MAJOR_VERSION = major_version
                                      t.FWTS_MINOR_VERSION = minor_version
                                      if self.bmc_type == 'FSP':
                                          t.IS_FSP_SYSTEM = True
                                      suite.addTest(t)
                  self.real_fwts_suite.addTest(suite)
  
      def run(self, result):
          conf = OpTestConfiguration.conf
          self.cv_HOST = conf.host()
          self.cv_SYSTEM = conf.system()
          self.bmc_type = conf.args.bmc_type
          self.real_fwts_suite = unittest.TestSuite()
          try:
              self.cv_SYSTEM.goto_state(OpSystemState.OS)
          except Exception as e:
              if (isinstance(self.cv_SYSTEM.console, OpTestQemu.QemuConsole)) \
                      or (isinstance(self.cv_SYSTEM.console, OpTestMambo.MamboConsole)):
                  m = FWTSCommandSkipped()
                  m.SKIP = e
                  self.real_fwts_suite.addTest(m)
                  self.real_fwts_suite.run(result)
                  return
              # In the event of something going wrong during IPL,
              # We need to catch that here as we're abusing UnitTest
              # TestSuite infra and we don't have the catch-all that
              # a TestCase provides.
              f = FWTSCommandFailed()
              f.FAIL = e
              self.real_fwts_suite.addTest(f)
              self.real_fwts_suite.run(result)
              return
          self.centaurs_present = self.cv_SYSTEM.has_centaurs_in_dt()
          host = self.cv_HOST
  
          fwts_version = None
          try:
              fwts_version = host.host_run_command(
                  "PATH=/usr/local/bin:$PATH fwts --version")
              log.debug("fwts_version={}".format(fwts_version))
          except CommandFailed as cf:
              command_failed = FWTSCommandFailed()
              command_failed.FAIL = cf
              self.real_fwts_suite.addTest(command_failed)
  
          if fwts_version:
              # We want to ensure we're at least at version 17.01
              # which means we need to parse this:
              # fwts, Version V17.01.00, 2017-01-19 04:20:38
              v = re.search("fwts, Version V(\d+)\.(\d+)", ''.join(fwts_version))
              major, minor = v.group(1), v.group(2)
  
              checkver = FWTSVersion()
              checkver.MAJOR = major
              checkver.MINOR = minor
              self.real_fwts_suite.addTest(checkver)
  
              if checkver.version_check():
                  self.add_fwts_results(int(major), int(minor))
  
          self.real_fwts_suite.run(result)
  