From a595d2331df4b85046208c6e13d3933597b7d149 Mon Sep 17 00:00:00 2001
From: chenjiankun <chenjiankun1@huawei.com>
Date: Mon, 13 Feb 2017 11:24:20 +0000
Subject: Context improvement: add support for configing node environment

JIRA: YARDSTICK-556

Currently we do can not config the node environment before heat create a
stack.
But in lots of scene, we need to config node environment before heat.
So I add support for it.

Change-Id: Iac1b74dc780eb40e6ab2c9cf04ed14e2b8f91ca8
Signed-off-by: chenjiankun <chenjiankun1@huawei.com>
---
 tests/unit/benchmark/contexts/test_node.py | 96 ++++++++++++++++++++++++++++++
 yardstick/benchmark/contexts/node.py       | 82 +++++++++++++++++++++++--
 yardstick/benchmark/core/task.py           |  4 +-
 3 files changed, 174 insertions(+), 8 deletions(-)

diff --git a/tests/unit/benchmark/contexts/test_node.py b/tests/unit/benchmark/contexts/test_node.py
index 64fe4a566..53a8ffa93 100644
--- a/tests/unit/benchmark/contexts/test_node.py
+++ b/tests/unit/benchmark/contexts/test_node.py
@@ -14,6 +14,7 @@
 from __future__ import absolute_import
 import os
 import unittest
+import mock
 
 from yardstick.benchmark.contexts import node
 
@@ -123,3 +124,98 @@ class NodeContextTestCase(unittest.TestCase):
         curr_path = os.path.dirname(os.path.abspath(__file__))
         file_path = os.path.join(curr_path, filename)
         return file_path
+
+    prefix = 'yardstick.benchmark.contexts.node'
+
+    @mock.patch('{}.NodeContext._execute_script'.format(prefix))
+    def test_deploy(self, execute_script_mock):
+        obj = node.NodeContext()
+        obj.env = {
+            'setup': [
+                {'node5': {}}
+            ]
+        }
+        obj.deploy()
+        self.assertTrue(execute_script_mock.called)
+
+    @mock.patch('{}.NodeContext._execute_script'.format(prefix))
+    def test_undeploy(self, execute_script_mock):
+        obj = node.NodeContext()
+        obj.env = {
+            'teardown': [
+                {'node5': {}}
+            ]
+        }
+        obj.undeploy()
+        self.assertTrue(execute_script_mock.called)
+
+    @mock.patch('{}.ssh.SSH._put_file_shell'.format(prefix))
+    @mock.patch('{}.ssh.SSH.execute'.format(prefix))
+    def test_execute_remote_script(self, execute_mock, put_file_mock):
+        obj = node.NodeContext()
+        obj.env = {'prefix': 'yardstick.benchmark.scenarios.compute'}
+        node_name_args = 'node5'
+        obj.nodes = [{
+            'name': node_name_args,
+            'user': 'ubuntu',
+            'ip': '10.10.10.10',
+            'pwd': 'ubuntu',
+        }]
+
+        info = {'script': 'computecapacity.bash'}
+        execute_mock.return_value = (0, '', '')
+        obj._execute_remote_script('node5', info)
+
+        self.assertTrue(put_file_mock.called)
+        self.assertTrue(execute_mock.called)
+
+    @mock.patch('{}.NodeContext._execute_local_script'.format(prefix))
+    def test_execute_script_local(self, local_execute_mock):
+        node_name = 'local'
+        info = {}
+        node.NodeContext()._execute_script(node_name, info)
+        self.assertTrue(local_execute_mock.called)
+
+    @mock.patch('{}.NodeContext._execute_remote_script'.format(prefix))
+    def test_execute_script_remote(self, remote_execute_mock):
+        node_name = 'node5'
+        info = {}
+        node.NodeContext()._execute_script(node_name, info)
+        self.assertTrue(remote_execute_mock.called)
+
+    def test_get_script(self):
+        script_args = 'hello.bash'
+        info_args = {
+            'script': script_args
+        }
+        script, options = node.NodeContext()._get_script(info_args)
+        self.assertEqual(script_args, script)
+        self.assertEqual('', options)
+
+    def test_node_info(self):
+        node_name_args = 'node5'
+        obj = node.NodeContext()
+        obj.nodes = [{'name': node_name_args, 'check': node_name_args}]
+        node_info = obj._get_node_info(node_name_args)
+        self.assertEqual(node_info.get('check'), node_name_args)
+
+    @mock.patch('{}.ssh.SSH.wait'.format(prefix))
+    def test_get_client(self, wait_mock):
+        node_name_args = 'node5'
+        obj = node.NodeContext()
+        obj.nodes = [{
+            'name': node_name_args,
+            'user': 'ubuntu',
+            'ip': '10.10.10.10',
+            'pwd': 'ubuntu',
+        }]
+        obj._get_client(node_name_args)
+        self.assertTrue(wait_mock.called)
+
+
+def main():
+    unittest.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/yardstick/benchmark/contexts/node.py b/yardstick/benchmark/contexts/node.py
index c7ed3b3f1..6fa9aa99a 100644
--- a/yardstick/benchmark/contexts/node.py
+++ b/yardstick/benchmark/contexts/node.py
@@ -8,14 +8,18 @@
 ##############################################################################
 
 from __future__ import absolute_import
-import logging
 import errno
+import subprocess
 import os
 import collections
+import logging
+
 import yaml
+import pkg_resources
 
+from yardstick import ssh
 from yardstick.benchmark.contexts.base import Context
-from yardstick.definitions import YARDSTICK_ROOT_PATH
+from yardstick.common.constants import YARDSTICK_ROOT_PATH
 
 LOG = logging.getLogger(__name__)
 
@@ -32,6 +36,7 @@ class NodeContext(Context):
         self.controllers = []
         self.computes = []
         self.baremetals = []
+        self.env = {}
         super(NodeContext, self).__init__()
 
     def read_config_file(self):
@@ -69,13 +74,20 @@ class NodeContext(Context):
         LOG.debug("Computes: %r", self.computes)
         LOG.debug("BareMetals: %r", self.baremetals)
 
+        self.env = attrs.get('env', {})
+        LOG.debug("Env: %r", self.env)
+
     def deploy(self):
-        """don't need to deploy"""
-        pass
+        setups = self.env.get('setup', [])
+        for setup in setups:
+            for host, info in setup.items():
+                self._execute_script(host, info)
 
     def undeploy(self):
-        """don't need to undeploy"""
-        pass
+        teardowns = self.env.get('teardown', [])
+        for teardown in teardowns:
+            for host, info in teardown.items():
+                self._execute_script(host, info)
 
     def _get_server(self, attr_name):
         """lookup server info by name from context
@@ -106,3 +118,61 @@ class NodeContext(Context):
 
         node["name"] = attr_name
         return node
+
+    def _execute_script(self, node_name, info):
+        if node_name == 'local':
+            self._execute_local_script(info)
+        else:
+            self._execute_remote_script(node_name, info)
+
+    def _execute_remote_script(self, node_name, info):
+        prefix = self.env.get('prefix', '')
+        script, options = self._get_script(info)
+
+        script_file = pkg_resources.resource_filename(prefix, script)
+
+        self._get_client(node_name)
+        self.client._put_file_shell(script_file, '~/{}'.format(script))
+
+        cmd = 'sudo bash {} {}'.format(script, options)
+        status, stdout, stderr = self.client.execute(cmd)
+        if status:
+            raise RuntimeError(stderr)
+
+    def _execute_local_script(self, info):
+        script, options = self._get_script(info)
+        script = os.path.join(YARDSTICK_ROOT_PATH, script)
+        cmd = ['bash', script, options]
+
+        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        LOG.debug('\n%s', p.communicate()[0])
+
+    def _get_script(self, info):
+        return info.get('script'), info.get('options', '')
+
+    def _get_client(self, node_name):
+        node = self._get_node_info(node_name.strip())
+
+        if node is None:
+            raise SystemExit('No such node')
+
+        user = node.get('user', 'ubuntu')
+        ssh_port = node.get("ssh_port", ssh.DEFAULT_PORT)
+        ip = node.get('ip')
+        pwd = node.get('password')
+        key_fname = node.get('key_filename', '/root/.ssh/id_rsa')
+
+        if pwd is not None:
+            LOG.debug("Log in via pw, user:%s, host:%s, password:%s",
+                      user, ip, pwd)
+            self.client = ssh.SSH(user, ip, password=pwd, port=ssh_port)
+        else:
+            LOG.debug("Log in via key, user:%s, host:%s, key_filename:%s",
+                      user, ip, key_fname)
+            self.client = ssh.SSH(user, ip, key_filename=key_fname,
+                                  port=ssh_port)
+
+        self.client.wait(timeout=600)
+
+    def _get_node_info(self, name):
+        return next((n for n in self.nodes if n['name'].strip() == name))
diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py
index 87d70f42f..bc1328624 100644
--- a/yardstick/benchmark/core/task.py
+++ b/yardstick/benchmark/core/task.py
@@ -85,7 +85,7 @@ class Task(object):     # pragma: no cover
                 # (hide it for exit handler)
                 Context.list = []
             else:
-                for context in Context.list:
+                for context in Context.list[::-1]:
                     context.undeploy()
                 Context.list = []
             one_task_end_time = time.time()
@@ -348,7 +348,7 @@ def atexit_handler():
 
     if len(Context.list) > 0:
         print("Undeploying all contexts")
-        for context in Context.list:
+        for context in Context.list[::-1]:
             context.undeploy()
 
 
-- 
cgit