Update to Python3
[functest.git] / functest / opnfv_tests / openstack / rally / rally.py
index ecffd3f..674c742 100644 (file)
@@ -11,7 +11,9 @@
 """Rally testcases implementation."""
 
 from __future__ import division
+from __future__ import print_function
 
+import fileinput
 import json
 import logging
 import os
@@ -28,7 +30,6 @@ from xtesting.core import testcase
 import yaml
 
 from functest.core import singlevm
-from functest.opnfv_tests.openstack.tempest import conf_utils
 from functest.utils import config
 from functest.utils import env
 
@@ -38,10 +39,13 @@ LOGGER = logging.getLogger(__name__)
 class RallyBase(singlevm.VmReady2):
     """Base class form Rally testcases implementation."""
 
-    # pylint: disable=too-many-instance-attributes
+    # pylint: disable=too-many-instance-attributes, too-many-public-methods
     TESTS = ['authenticate', 'glance', 'cinder', 'gnocchi', 'heat',
              'keystone', 'neutron', 'nova', 'quotas']
 
+    RALLY_CONF_PATH = "/etc/rally/rally.conf"
+    RALLY_AARCH64_PATCH_PATH = pkg_resources.resource_filename(
+        'functest', 'ci/rally_aarch64_patch.conf')
     RALLY_DIR = pkg_resources.resource_filename(
         'functest', 'opnfv_tests/openstack/rally')
     RALLY_SCENARIO_DIR = pkg_resources.resource_filename(
@@ -54,6 +58,8 @@ class RallyBase(singlevm.VmReady2):
     TENANTS_AMOUNT = 3
     ITERATIONS_AMOUNT = 10
     CONCURRENCY = 4
+    VOLUME_VERSION = 3
+    VOLUME_SERVICE_TYPE = "volumev3"
     BLACKLIST_FILE = os.path.join(RALLY_DIR, "blacklist.yaml")
     TASK_DIR = os.path.join(getattr(config.CONF, 'dir_rally_data'), 'task')
     TEMP_DIR = os.path.join(TASK_DIR, 'var')
@@ -109,6 +115,9 @@ class RallyBase(singlevm.VmReady2):
         task_args['iterations'] = self.ITERATIONS_AMOUNT
         task_args['concurrency'] = self.CONCURRENCY
         task_args['smoke'] = self.smoke
+        task_args['volume_version'] = self.VOLUME_VERSION
+        task_args['volume_service_type'] = self.VOLUME_SERVICE_TYPE
+        task_args['block_migration'] = env.get("BLOCK_MIGRATION").lower()
 
         if self.ext_net:
             task_args['floating_network'] = str(self.ext_net.name)
@@ -145,6 +154,57 @@ class RallyBase(singlevm.VmReady2):
         self.apply_blacklist(scenario_file_name, test_file_name)
         return test_file_name
 
+    @staticmethod
+    def get_verifier_deployment_id():
+        """
+        Returns deployment id for active Rally deployment
+        """
+        cmd = ("rally deployment list | awk '/" +
+               getattr(config.CONF, 'rally_deployment_name') +
+               "/ {print $2}'")
+        proc = subprocess.Popen(cmd, shell=True,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        deployment_uuid = proc.stdout.readline().rstrip()
+        return deployment_uuid.decode()
+
+    @staticmethod
+    def create_rally_deployment(environ=None):
+        """Create new rally deployment"""
+        # set the architecture to default
+        pod_arch = env.get("POD_ARCH")
+        arch_filter = ['aarch64']
+
+        if pod_arch and pod_arch in arch_filter:
+            LOGGER.info("Apply aarch64 specific to rally config...")
+            with open(RallyBase.RALLY_AARCH64_PATCH_PATH, "r") as pfile:
+                rally_patch_conf = pfile.read()
+
+            for line in fileinput.input(RallyBase.RALLY_CONF_PATH):
+                print(line, end=' ')
+                if "cirros|testvm" in line:
+                    print(rally_patch_conf)
+
+        LOGGER.info("Creating Rally environment...")
+        try:
+            cmd = ['rally', 'deployment', 'destroy',
+                   '--deployment',
+                   str(getattr(config.CONF, 'rally_deployment_name'))]
+            output = subprocess.check_output(cmd)
+            LOGGER.info("%s\n%s", " ".join(cmd), output)
+        except subprocess.CalledProcessError:
+            pass
+
+        cmd = ['rally', 'deployment', 'create', '--fromenv',
+               '--name', str(getattr(config.CONF, 'rally_deployment_name'))]
+        output = subprocess.check_output(cmd, env=environ)
+        LOGGER.info("%s\n%s", " ".join(cmd), output)
+
+        cmd = ['rally', 'deployment', 'check']
+        output = subprocess.check_output(cmd)
+        LOGGER.info("%s\n%s", " ".join(cmd), output)
+        return RallyBase.get_verifier_deployment_id()
+
     @staticmethod
     def update_keystone_default_role(rally_conf='/etc/rally/rally.conf'):
         """Set keystone_default_role in rally.conf"""
@@ -155,7 +215,7 @@ class RallyBase(singlevm.VmReady2):
                 rconfig.add_section('openstack')
             rconfig.set(
                 'openstack', 'keystone_default_role', env.get("NEW_USER_ROLE"))
-            with open(rally_conf, 'wb') as config_file:
+            with open(rally_conf, 'w') as config_file:
                 rconfig.write(config_file)
 
     @staticmethod
@@ -166,7 +226,7 @@ class RallyBase(singlevm.VmReady2):
             rconfig.read(rally_conf)
             if rconfig.has_option('openstack', 'keystone_default_role'):
                 rconfig.remove_option('openstack', 'keystone_default_role')
-            with open(rally_conf, 'wb') as config_file:
+            with open(rally_conf, 'w') as config_file:
                 rconfig.write(config_file)
 
     @staticmethod
@@ -180,7 +240,7 @@ class RallyBase(singlevm.VmReady2):
         taskid_re = re.compile('^Task +(.*): started$')
         for line in cmd_raw.splitlines(True):
             line = line.strip()
-            match = taskid_re.match(line)
+            match = taskid_re.match(line.decode())
             if match:
                 return match.group(1)
         return None
@@ -268,6 +328,8 @@ class RallyBase(singlevm.VmReady2):
             with open(RallyBase.BLACKLIST_FILE, 'r') as black_list_file:
                 black_list_yaml = yaml.safe_load(black_list_file)
 
+            if env.get('BLOCK_MIGRATION').lower() == 'true':
+                func_list.append("block_migration")
             if not self._migration_supported():
                 func_list.append("no_migration")
             if not self._network_trunk_supported():
@@ -378,10 +440,13 @@ class RallyBase(singlevm.VmReady2):
         nb_tests = 0
         nb_success = 0
         overall_duration = 0.0
+        success = []
+        failures = []
 
         rally_report = json.loads(json_raw)
         for task in rally_report.get('tasks'):
             for subtask in task.get('subtasks'):
+                has_errors = False
                 for workload in subtask.get('workloads'):
                     if workload.get('full_duration'):
                         overall_duration += workload.get('full_duration')
@@ -392,11 +457,20 @@ class RallyBase(singlevm.VmReady2):
                     for result in workload.get('data'):
                         if not result.get('error'):
                             nb_success += 1
+                        else:
+                            has_errors = True
+
+                if has_errors:
+                    failures.append(subtask['title'])
+                else:
+                    success.append(subtask['title'])
 
         scenario_summary = {'test_name': test_name,
                             'overall_duration': overall_duration,
                             'nb_tests': nb_tests,
                             'nb_success': nb_success,
+                            'success': success,
+                            'failures': failures,
                             'task_status': self.task_succeed(json_raw)}
         self.summary.append(scenario_summary)
 
@@ -493,7 +567,9 @@ class RallyBase(singlevm.VmReady2):
             payload.append({'module': item['test_name'],
                             'details': {'duration': item['overall_duration'],
                                         'nb tests': item['nb_tests'],
-                                        'success': success_str}})
+                                        'success rate': success_str,
+                                        'success': item['success'],
+                                        'failures': item['failures']}})
 
         total_duration_str = time.strftime("%H:%M:%S",
                                            time.gmtime(total_duration))
@@ -516,8 +592,9 @@ class RallyBase(singlevm.VmReady2):
                                     'nb success': success_rate}})
         self.details = payload
 
-    def generate_html_report(self):
-        """Save all task reports as single HTML
+    @staticmethod
+    def export_task(file_name, export_type="html"):
+        """Export all task results (e.g. html or xunit report)
 
         Raises:
             subprocess.CalledProcessError: if Rally doesn't return 0
@@ -525,9 +602,26 @@ class RallyBase(singlevm.VmReady2):
         Returns:
             None
         """
-        cmd = ["rally", "task", "report", "--deployment",
+        cmd = ["rally", "task", "export", "--type", export_type,
+               "--deployment",
                str(getattr(config.CONF, 'rally_deployment_name')),
-               "--out", "{}/{}.html".format(self.results_dir, self.case_name)]
+               "--to", file_name]
+        LOGGER.debug('running command: %s', cmd)
+        output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+        LOGGER.info("%s\n%s", " ".join(cmd), output)
+
+    @staticmethod
+    def verify_report(file_name, uuid, export_type="html"):
+        """Generate the verifier report (e.g. html or xunit report)
+
+        Raises:
+            subprocess.CalledProcessError: if Rally doesn't return 0
+
+        Returns:
+            None
+        """
+        cmd = ["rally", "verify", "report", "--type", export_type,
+               "--uuid", uuid, "--to", file_name]
         LOGGER.debug('running command: %s', cmd)
         output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
         LOGGER.info("%s\n%s", " ".join(cmd), output)
@@ -553,25 +647,18 @@ class RallyBase(singlevm.VmReady2):
         try:
             assert super(RallyBase, self).run(
                 **kwargs) == testcase.TestCase.EX_OK
-            environ = dict(
-                os.environ,
-                OS_USERNAME=self.project.user.name,
-                OS_PROJECT_NAME=self.project.project.name,
-                OS_PROJECT_ID=self.project.project.id,
-                OS_PASSWORD=self.project.password)
-            try:
-                del environ['OS_TENANT_NAME']
-                del environ['OS_TENANT_ID']
-            except Exception:  # pylint: disable=broad-except
-                pass
-            conf_utils.create_rally_deployment(environ=environ)
+            self.create_rally_deployment(environ=self.project.get_environ())
             self.prepare_run(**kwargs)
             self.run_tests(**kwargs)
             self._generate_report()
-            self.generate_html_report()
+            self.export_task(
+                "{}/{}.html".format(self.results_dir, self.case_name))
+            self.export_task(
+                "{}/{}.xml".format(self.results_dir, self.case_name),
+                export_type="junit-xml")
             res = testcase.TestCase.EX_OK
-        except Exception as exc:   # pylint: disable=broad-except
-            LOGGER.error('Error with run: %s', exc)
+        except Exception:   # pylint: disable=broad-except
+            LOGGER.exception('Error with run:')
             self.result = 0
             res = testcase.TestCase.EX_RUN_ERROR
         self.stop_time = time.time()