Implement ansible driver 79/29779/7
authorwu.zhihui <wu.zhihui1@zte.com.cn>
Sat, 4 Mar 2017 12:33:22 +0000 (20:33 +0800)
committerwu.zhihui <wu.zhihui1@zte.com.cn>
Wed, 8 Mar 2017 10:03:15 +0000 (18:03 +0800)
- According to inputs parameters, setup test environment. If failed
during setupping, qtip will exited. If successful, execute metric
tests.
- Parameters(optional):
* keypair: the keypair to login/execute commands to the remote
  hosts. They can be automatically generated.
* hostfile: a inventory file. If not give, it can be
  automatically generated via installer.
* args: the parameters passed to playbook
- Use ansible python api to trigger ansible-playbook.

Unit test will be in a new patch.

Change-Id: I7470d348308f7cb6cb669bcc49063cf0f4da2111
Signed-off-by: wu.zhihui <wu.zhihui1@zte.com.cn>
qtip/driver/ansible.py [deleted file]
qtip/driver/ansible_api.py [new file with mode: 0644]
qtip/driver/ansible_driver.py [new file with mode: 0644]

diff --git a/qtip/driver/ansible.py b/qtip/driver/ansible.py
deleted file mode 100644 (file)
index cd17625..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 ZTE Corp and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-from qtip.driver.base import BaseDriver
-
-
-class AnsibleDriver(BaseDriver):
-    """driver for running performance tests with Ansible"""
diff --git a/qtip/driver/ansible_api.py b/qtip/driver/ansible_api.py
new file mode 100644 (file)
index 0000000..5c5baff
--- /dev/null
@@ -0,0 +1,58 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corp and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from collections import namedtuple
+
+from ansible.executor.playbook_executor import PlaybookExecutor
+from ansible.inventory import Inventory
+from ansible.parsing.dataloader import DataLoader
+from ansible.vars import VariableManager
+
+
+class AnsibleApi(object):
+
+    def __init__(self):
+        self.variable_manager = VariableManager()
+        self.loader = DataLoader()
+        self.passwords = {}
+        self.playbook_executor = None
+
+    def execute_playbook(self, playbook_path, hosts_file=None,
+                         key_file=None, extra_vars=None):
+        inventory = Inventory(loader=self.loader,
+                              variable_manager=self.variable_manager,
+                              host_list=hosts_file)
+        Options = namedtuple('Options',
+                             ['listtags', 'listtasks', 'listhosts', 'syntax',
+                              'connection', 'module_path', 'forks', 'remote_user',
+                              'private_key_file', 'ssh_common_args', 'ssh_extra_args',
+                              'sftp_extra_args', 'scp_extra_args', 'become',
+                              'become_method', 'become_user', 'verbosity', 'check'])
+        options = Options(listtags=False, listtasks=False, listhosts=False,
+                          syntax=False, connection='ssh', module_path=None,
+                          forks=100, remote_user='root', private_key_file=key_file,
+                          ssh_common_args=None, ssh_extra_args=None, sftp_extra_args=None,
+                          scp_extra_args=None, become=None, become_method=None,
+                          become_user='root', verbosity=None, check=False)
+        self.variable_manager.extra_vars = extra_vars
+
+        self.playbook_executor = PlaybookExecutor(playbooks=[playbook_path],
+                                                  inventory=inventory,
+                                                  variable_manager=self.variable_manager,
+                                                  loader=self.loader,
+                                                  options=options,
+                                                  passwords=self.passwords)
+        return self.playbook_executor.run()
+
+    def get_detail_playbook_stats(self):
+        if self.playbook_executor:
+            stats = self.playbook_executor._tqm._stats
+            return map(lambda x: (x, stats.summarize(x)), stats.processed.keys())
+        else:
+            return None
diff --git a/qtip/driver/ansible_driver.py b/qtip/driver/ansible_driver.py
new file mode 100644 (file)
index 0000000..1cd7918
--- /dev/null
@@ -0,0 +1,85 @@
+##############################################################################
+# Copyright (c) 2017 ZTE Corp and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from collections import defaultdict
+from os import path
+
+from qtip.driver.ansible_api import AnsibleApi
+from qtip.util.env import AnsibleEnvSetup
+from qtip.util.logger import QtipLogger
+
+logger = QtipLogger('ansible_driver').get
+PLAYBOOK_DIR = path.join(path.dirname(__file__), 'playbook')
+
+
+class AnsibleDriver(object):
+    """driver for running performance tests with Ansible"""
+
+    def __init__(self, config={}):
+        self.config = config
+        self.env = AnsibleEnvSetup()
+        self.env_setup_flag = False
+
+    @staticmethod
+    def merge_two_dicts(x, y):
+        '''
+        It is from http://stackoverflow.com/questions/38987/
+        how-can-i-merge-two-python-dictionaries-in-a-single-expression
+        '''
+        z = x.copy()
+        z.update(y)
+        return z
+
+    def pre_run(self):
+        if self.env_setup_flag:
+            logger.info("Already setup environment......")
+        else:
+            logger.info("Starting to setup test environment...")
+            self.env.setup(self.config)
+            self.env_setup_flag = True
+            logger("Done!")
+
+    def run(self, metric_list, **kwargs):
+        if 'args' in self.config:
+            extra_vars = self.merge_two_dicts(kwargs, self.config['args'])
+        else:
+            extra_vars = kwargs
+        logger.info("extra_var: {0}".format(extra_vars))
+
+        # TODO zhihui: will add a new property named "tool" for metrics, hardcode it now.
+        tool_to_metrics = defaultdict(list)
+        for metric in metric_list:
+            if metric in ['dhrystone', 'whetstone']:
+                tool_to_metrics['unixbench'].append(metric)
+                extra_vars[metric] = True
+            elif metric == 'ssl':
+                tool_to_metrics['openssl'].append(metric)
+            else:
+                tool_to_metrics[metric].append(metric)
+
+        ansible_api = AnsibleApi()
+        map(lambda tool: self._run_metric(ansible_api, tool,
+                                          tool_to_metrics[tool], extra_vars),
+            tool_to_metrics)
+
+    def _run_metric(self, ansible_api, tool, metrics, extra_vars):
+        logger.info('Using {0} to measure metrics {1}'.format(tool, metrics))
+
+        for metric in metrics:
+            extra_vars[metric] = True
+
+        logger.debug("extra_vars: {0}".format(extra_vars))
+
+        for item in ['setup', 'run', 'clean']:
+            pbook = "{0}/{1}/{2}.yaml".format(PLAYBOOK_DIR, tool, item)
+            logger.debug("Start to run {0}".format(pbook))
+            ansible_api.execute_playbook(pbook, self.env.hostfile,
+                                         self.env.keypair['private'], extra_vars)
+            playbook_stat = ansible_api.get_detail_playbook_stats()
+            logger.debug("playbook_stat: {0}".format(playbook_stat))