From f8b52ec5839c804be2f866393462f85f530160d9 Mon Sep 17 00:00:00 2001 From: JingLu5 Date: Tue, 28 Jun 2016 14:26:08 +0800 Subject: Add plugin Command Change-Id: I8976ddf3dd43813ee38051dc4b0030265b85c3ef Signed-off-by: JingLu5 --- plugin/sample_config.yaml | 12 ++ samples/plugintest.yaml | 22 +++ .../benchmark/scenarios/compute/test_plugintest.py | 60 ++++++++ tests/unit/cmd/commands/test_plugin.py | 83 +++++++++++ .../benchmark/scenarios/compute/plugintest.py | 54 +++++++ yardstick/cmd/cli.py | 4 +- yardstick/cmd/commands/plugin.py | 156 +++++++++++++++++++++ yardstick/resources/script/install/sample.bash | 18 +++ yardstick/resources/script/remove/sample.bash | 16 +++ 9 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 plugin/sample_config.yaml create mode 100644 samples/plugintest.yaml create mode 100644 tests/unit/benchmark/scenarios/compute/test_plugintest.py create mode 100644 tests/unit/cmd/commands/test_plugin.py create mode 100644 yardstick/benchmark/scenarios/compute/plugintest.py create mode 100644 yardstick/cmd/commands/plugin.py create mode 100644 yardstick/resources/script/install/sample.bash create mode 100644 yardstick/resources/script/remove/sample.bash diff --git a/plugin/sample_config.yaml b/plugin/sample_config.yaml new file mode 100644 index 000000000..39d04e3af --- /dev/null +++ b/plugin/sample_config.yaml @@ -0,0 +1,12 @@ +--- +# Sample plugin configration file + +schema: "yardstick:plugin:0.1" + +plugins: + name: sample + +deployment: + ip: 10.1.0.50 + user: root + password: root diff --git a/samples/plugintest.yaml b/samples/plugintest.yaml new file mode 100644 index 000000000..dc3b29d63 --- /dev/null +++ b/samples/plugintest.yaml @@ -0,0 +1,22 @@ +--- +# Sample benchmark task config file +# Used for testing sample plugin + +schema: "yardstick:task:0.1" + +scenarios: +- + type: PluginTest + options: + + nodes: + host1: node1.LF + + runner: + type: Iteration + iterations: 1 + +context: + type: Node + name: LF + file: /root/yardstick/etc/yardstick/nodes/compass_sclab_virtual/pod.yaml diff --git a/tests/unit/benchmark/scenarios/compute/test_plugintest.py b/tests/unit/benchmark/scenarios/compute/test_plugintest.py new file mode 100644 index 000000000..94f52738c --- /dev/null +++ b/tests/unit/benchmark/scenarios/compute/test_plugintest.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd 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 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.plugintest.PluginTest + +import mock +import json +import unittest +import os + +from yardstick.benchmark.scenarios.compute import plugintest + + +@mock.patch('yardstick.benchmark.scenarios.compute.plugintest.ssh') +class PluginTestTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'nodes': { + 'host1': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key", + 'password': "root" + }, + } + } + + self.result = {} + + def test_sample_successful_setup(self, mock_ssh): + s = plugintest.PluginTest({}, self.ctx) + mock_ssh.SSH().execute.return_value = (0, '', '') + + s.setup() + self.assertIsNotNone(s.client) + self.assertTrue(s.setup_done) + + def test_sample_successful(self, mock_ssh): + s = plugintest.PluginTest({}, self.ctx) + + sample_output = '{"Test Output": "Hello world!"}' + mock_ssh.SSH().execute.return_value = (0, sample_output, '') + s.run(self.result) + expected_result = json.loads(sample_output) + self.assertEqual(self.result, expected_result) + + def test_sample_unsuccessful_script_error(self, mock_ssh): + s = plugintest.PluginTest({}, self.ctx) + + mock_ssh.SSH().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, s.run, self.result) diff --git a/tests/unit/cmd/commands/test_plugin.py b/tests/unit/cmd/commands/test_plugin.py new file mode 100644 index 000000000..2e823fdae --- /dev/null +++ b/tests/unit/cmd/commands/test_plugin.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd 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 +############################################################################## + +# Unittest for yardstick.cmd.commands.plugin + +import mock +import unittest + +from yardstick.cmd.commands import plugin + + +class Arg(object): + def __init__(self): + self.input_file = ('plugin/sample_config.yaml',) + + +@mock.patch('yardstick.cmd.commands.plugin.ssh') +class pluginCommandsTestCase(unittest.TestCase): + + def setUp(self): + self.result = {} + + def test_do_install(self, mock_ssh): + p = plugin.PluginCommands() + mock_ssh.SSH().execute.return_value = (0, '', '') + input_file = Arg() + p.do_install(input_file) + expected_result = {} + self.assertEqual(self.result, expected_result) + + def test_do_remove(self, mock_ssh): + p = plugin.PluginCommands() + mock_ssh.SSH().execute.return_value = (0, '', '') + input_file = Arg() + p.do_remove(input_file) + expected_result = {} + self.assertEqual(self.result, expected_result) + + def test_install_setup_run(self, mock_ssh): + p = plugin.PluginCommands() + mock_ssh.SSH().execute.return_value = (0, '', '') + plugins = { + "name": "sample" + } + deployment = { + "ip": "10.1.0.50", + "user": "root", + "password": "root" + } + plugin_name = plugins.get("name") + p._install_setup(plugin_name, deployment) + self.assertIsNotNone(p.client) + + p._run(plugin_name) + expected_result = {} + self.assertEqual(self.result, expected_result) + + def test_remove_setup_run(self, mock_ssh): + p = plugin.PluginCommands() + mock_ssh.SSH().execute.return_value = (0, '', '') + plugins = { + "name": "sample" + } + deployment = { + "ip": "10.1.0.50", + "user": "root", + "password": "root" + } + plugin_name = plugins.get("name") + p._remove_setup(plugin_name, deployment) + self.assertIsNotNone(p.client) + + p._run(plugin_name) + expected_result = {} + self.assertEqual(self.result, expected_result) diff --git a/yardstick/benchmark/scenarios/compute/plugintest.py b/yardstick/benchmark/scenarios/compute/plugintest.py new file mode 100644 index 000000000..e41fb8399 --- /dev/null +++ b/yardstick/benchmark/scenarios/compute/plugintest.py @@ -0,0 +1,54 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd 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 +############################################################################## +import logging +import json + +import yardstick.ssh as ssh +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class PluginTest(base.Scenario): + """Sample scenario file for testing sample plugin""" + __scenario_type__ = "PluginTest" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.setup_done = False + + def setup(self): + """scenario setup""" + + nodes = self.context_cfg['nodes'] + node = nodes.get('host1', None) + host_user = node.get('user', 'ubuntu') + host_ip = node.get('ip', None) + host_pwd = node.get('password', 'root') + LOG.debug("user:%s, host:%s", host_user, host_ip) + self.client = ssh.SSH(host_user, host_ip, password=host_pwd) + self.client.wait(timeout=600) + + self.setup_done = True + + def run(self, result): + """execute the benchmark""" + + if not self.setup_done: + self.setup() + + cmd = "sudo bash test.sh" + + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + result.update(json.loads(stdout)) diff --git a/yardstick/cmd/cli.py b/yardstick/cmd/cli.py index a7ba04f57..dd74836cb 100644 --- a/yardstick/cmd/cli.py +++ b/yardstick/cmd/cli.py @@ -23,6 +23,7 @@ from yardstick.cmd.commands import task from yardstick.cmd.commands import runner from yardstick.cmd.commands import scenario from yardstick.cmd.commands import testcase +from yardstick.cmd.commands import plugin CONF = cfg.CONF cli_opts = [ @@ -60,7 +61,8 @@ class YardstickCLI(): 'task': task.TaskCommands, 'runner': runner.RunnerCommands, 'scenario': scenario.ScenarioCommands, - 'testcase': testcase.TestcaseCommands + 'testcase': testcase.TestcaseCommands, + 'plugin': plugin.PluginCommands } def __init__(self): diff --git a/yardstick/cmd/commands/plugin.py b/yardstick/cmd/commands/plugin.py new file mode 100644 index 000000000..e65c818fa --- /dev/null +++ b/yardstick/cmd/commands/plugin.py @@ -0,0 +1,156 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd 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 +############################################################################## + +""" Handler for yardstick command 'plugin' """ + +import sys +import yaml +import time +import logging +import pkg_resources +import yardstick.ssh as ssh + +from yardstick.common.utils import cliargs +from yardstick.common.task_template import TaskTemplate + +LOG = logging.getLogger(__name__) + + +class PluginCommands(object): + '''Plugin commands. + + Set of commands to manage plugins. + ''' + + @cliargs("input_file", type=str, help="path to plugin configuration file", + nargs=1) + def do_install(self, args): + '''Install a plugin.''' + + total_start_time = time.time() + parser = PluginParser(args.input_file[0]) + + plugins, deployment = parser.parse_plugin() + plugin_name = plugins.get("name") + print("Installing plugin: %s" % plugin_name) + + self._install_setup(plugin_name, deployment) + + self._run(plugin_name) + + total_end_time = time.time() + LOG.info("total finished in %d secs", + total_end_time - total_start_time) + + print("Done, exiting") + + def do_remove(self, args): + '''Remove a plugin.''' + + total_start_time = time.time() + parser = PluginParser(args.input_file[0]) + + plugins, deployment = parser.parse_plugin() + plugin_name = plugins.get("name") + print("Remove plugin: %s" % plugin_name) + + self._remove_setup(plugin_name, deployment) + + self._run(plugin_name) + + total_end_time = time.time() + LOG.info("total finished in %d secs", + total_end_time - total_start_time) + + print("Done, exiting") + + def _install_setup(self, plugin_name, deployment): + '''Deployment environment setup''' + target_script = plugin_name + ".bash" + self.script = pkg_resources.resource_filename( + 'yardstick.resources', 'script/install/' + target_script) + + deployment_user = deployment.get("user") + deployment_ip = deployment.get("ip") + + deployment_password = deployment.get("password") + LOG.debug("user:%s, host:%s", deployment_user, deployment_ip) + self.client = ssh.SSH(deployment_user, deployment_ip, + password=deployment_password) + self.client.wait(timeout=600) + + # copy script to host + cmd = "cat > ~/%s.sh" % plugin_name + self.client.run(cmd, stdin=open(self.script, 'rb')) + + def _remove_setup(self, plugin_name, deployment): + '''Deployment environment setup''' + target_script = plugin_name + ".bash" + self.script = pkg_resources.resource_filename( + 'yardstick.resources', 'script/remove/' + target_script) + + deployment_user = deployment.get("user") + deployment_ip = deployment.get("ip") + + deployment_password = deployment.get("password") + LOG.debug("user:%s, host:%s", deployment_user, deployment_ip) + self.client = ssh.SSH(deployment_user, deployment_ip, + password=deployment_password) + self.client.wait(timeout=600) + + # copy script to host + cmd = "cat > ~/%s.sh" % plugin_name + self.client.run(cmd, stdin=open(self.script, 'rb')) + + def _run(self, plugin_name): + '''Run installation script ''' + cmd = "sudo bash %s" % plugin_name + ".sh" + + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + + +class PluginParser(object): + '''Parser for plugin configration files in yaml format''' + def __init__(self, path): + self.path = path + + def parse_plugin(self): + '''parses the plugin file and return a plugins instance + and a deployment instance + ''' + + print "Parsing plugin config:", self.path + + try: + kw = {} + with open(self.path) as f: + try: + input_plugin = f.read() + rendered_plugin = TaskTemplate.render(input_plugin, **kw) + except Exception as e: + print(("Failed to render template:\n%(plugin)s\n%(err)s\n") + % {"plugin": input_plugin, "err": e}) + raise e + print(("Input plugin is:\n%s\n") % rendered_plugin) + + cfg = yaml.load(rendered_plugin) + except IOError as ioerror: + sys.exit(ioerror) + + self._check_schema(cfg["schema"], "plugin") + + return cfg["plugins"], cfg["deployment"] + + def _check_schema(self, cfg_schema, schema_type): + '''Check if configration file is using the correct schema type''' + + if cfg_schema != "yardstick:" + schema_type + ":0.1": + sys.exit("error: file %s has unknown schema %s" % (self.path, + cfg_schema)) diff --git a/yardstick/resources/script/install/sample.bash b/yardstick/resources/script/install/sample.bash new file mode 100644 index 000000000..21eb14680 --- /dev/null +++ b/yardstick/resources/script/install/sample.bash @@ -0,0 +1,18 @@ +#!/bin/bash + +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd 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 +############################################################################## + +# Sample plugin installation script + +set -e + +cat > test.sh <