diff options
author | lihuan <lihuansse@tongji.edu.cn> | 2016-07-06 17:29:00 +0800 |
---|---|---|
committer | lihuan <lihuansse@tongji.edu.cn> | 2016-07-08 15:01:02 +0800 |
commit | 1eb35349d2bedacc75aff28b0883995794682395 (patch) | |
tree | dd402671a0dc52d8cf7a28385159693037689ec0 | |
parent | 55af4f473d60838b57b14670423717b7f2e901ea (diff) |
Creating Director and related codes.
Add a new scenario type 'ScenarioGeneral' that support orchestrating general HA test scenarios.
Director, ActionPlayer and RollbackPlayer are uesed to execute the test scenario (or test flow).
JIRA: YARDSTICK-288
Change-Id: Ied2ccd4712f3c3efde6771bfa4538c1e9e137c11
Signed-off-by: lihuan <lihuansse@tongji.edu.cn>
7 files changed, 441 insertions, 1 deletions
diff --git a/tests/unit/benchmark/scenarios/availability/test_director.py b/tests/unit/benchmark/scenarios/availability/test_director.py new file mode 100644 index 000000000..887ddd631 --- /dev/null +++ b/tests/unit/benchmark/scenarios/availability/test_director.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# 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.availability.director + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.director import Director +from yardstick.benchmark.scenarios.availability import actionplayers + + +@mock.patch('yardstick.benchmark.scenarios.availability.director.basemonitor') +@mock.patch('yardstick.benchmark.scenarios.availability.director.baseattacker') +@mock.patch('yardstick.benchmark.scenarios.availability.director.baseoperation') +@mock.patch('yardstick.benchmark.scenarios.availability.director.baseresultchecker') +class DirectorTestCase(unittest.TestCase): + + def setUp(self): + self.scenario_cfg = { + 'type': "general_scenario", + 'options': { + 'attackers':[{ + 'fault_type': "general-attacker", + 'key': "kill-process"}], + 'monitors': [{ + 'monitor_type': "general-monitor", + 'key': "service_status"}], + 'operations': [{ + 'operation_type': 'general-operation', + 'key' : 'service_status'}], + 'resultCheckers': [{ + 'checker_type': 'general-result-checker', + 'key' : 'process-checker',}], + 'steps':[ + { + 'actionKey': "service_status", + 'actionType': "operation", + 'index': 1}, + { + 'actionKey': "kill-process", + 'actionType': "attacker", + 'index': 2}, + { + 'actionKey': "process-checker", + 'actionType': "resultchecker", + 'index': 3}, + { + 'actionKey': "service_status", + 'actionType': "monitor", + 'index': 4}, + ] + } + } + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.ctx = {"nodes": {"node1": host}} + + def test_director_all_successful(self, mock_checer, mock_opertion, mock_attacker, mock_monitor): + ins = Director(self.scenario_cfg, self.ctx) + opertion_action = ins.createActionPlayer("operation", "service_status") + attacker_action = ins.createActionPlayer("attacker", "kill-process") + checker_action = ins.createActionPlayer("resultchecker", "process-checker") + monitor_action = ins.createActionPlayer("monitor", "service_status") + + opertion_rollback = ins.createActionRollbacker("operation", "service_status") + attacker_rollback = ins.createActionRollbacker("attacker", "kill-process") + ins.executionSteps.append(opertion_rollback) + ins.executionSteps.append(attacker_rollback) + + opertion_action.action() + attacker_action.action() + checker_action.action() + monitor_action.action() + + attacker_rollback.rollback() + opertion_rollback.rollback() + + ins.stopMonitors() + ins.verify() + ins.knockoff() + + def test_director_get_wrong_item(self, mock_checer, mock_opertion, mock_attacker, mock_monitor): + ins = Director(self.scenario_cfg, self.ctx) + ins.createActionPlayer("wrong_type", "wrong_key") + ins.createActionRollbacker("wrong_type", "wrong_key") + + + + + + diff --git a/tests/unit/benchmark/scenarios/availability/test_scenario_general.py b/tests/unit/benchmark/scenarios/availability/test_scenario_general.py new file mode 100644 index 000000000..c17edea45 --- /dev/null +++ b/tests/unit/benchmark/scenarios/availability/test_scenario_general.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# 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.availability.scenario_general + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.scenario_general import ScenarioGeneral + + +@mock.patch('yardstick.benchmark.scenarios.availability.scenario_general.Director') +class ScenarioGeneralTestCase(unittest.TestCase): + + def setUp(self): + self.scenario_cfg = { + 'type': "general_scenario", + 'options': { + 'attackers':[{ + 'fault_type': "general-attacker", + 'key': "kill-process"}], + 'monitors': [{ + 'monitor_type': "general-monitor", + 'key': "service_status"}], + 'steps':[ + { + 'actionKey': "kill-process", + 'actionType': "attacker", + 'index': 1}, + { + 'actionKey': "service_status", + 'actionType': "monitor", + 'index': 2}] + } + } + + def test_scenario_general_all_successful(self, mock_director): + ins = ScenarioGeneral(self.scenario_cfg, None) + ins.setup() + ins.run(None) + ins.teardown() + + def test_scenario_general_exception(self, mock_director): + ins = ScenarioGeneral(self.scenario_cfg, None) + mock_obj = mock.Mock() + mock_obj.createActionPlayer.side_effect = KeyError('Wrong') + ins.director = mock_obj + ins.run(None) + ins.teardown() + + def test_scenario_general_case_fail(self, mock_director): + ins = ScenarioGeneral(self.scenario_cfg, None) + mock_obj = mock.Mock() + mock_obj.verify.return_value = False + ins.director = mock_obj + ins.run(None) + ins.teardown()
\ No newline at end of file diff --git a/yardstick/benchmark/scenarios/availability/__init__.py b/yardstick/benchmark/scenarios/availability/__init__.py index fdad0fe95..c3b3aae30 100755 --- a/yardstick/benchmark/scenarios/availability/__init__.py +++ b/yardstick/benchmark/scenarios/availability/__init__.py @@ -12,7 +12,6 @@ class ActionType: ATTACKER = "attacker" MONITOR = "monitor" RESULTCHECKER = "resultchecker" - RESULTCOMPARER = "comparer" OPERATION = "operation" diff --git a/yardstick/benchmark/scenarios/availability/actionplayers.py b/yardstick/benchmark/scenarios/availability/actionplayers.py new file mode 100644 index 000000000..420626413 --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/actionplayers.py @@ -0,0 +1,54 @@ +############################################################################## +# Copyright (c) 2016 Juan Qiu and others +# juan_ qiu@tongji.edu.cn +# 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 +############################################################################## + + +class ActionPlayer(object): + """ + Abstract the action functions of attacker, + monitor, operation, resultchecker and mybe others in future + """ + + def action(self): + pass + + +class AttackerPlayer(ActionPlayer): + + def __init__(self, attacker): + self.underlyingAttacker = attacker + + def action(self): + self.underlyingAttacker.inject_fault() + + +class OperationPlayer(ActionPlayer): + + def __init__(self, operation): + self.underlyingOperation = operation + + def action(self): + self.underlyingOperation.run() + + +class MonitorPlayer(ActionPlayer): + + def __init__(self, monitor): + self.underlyingmonitor = monitor + + def action(self): + self.underlyingmonitor.start_monitor() + + +class ResultCheckerPlayer(ActionPlayer): + + def __init__(self, resultChecker): + self.underlyingresultChecker = resultChecker + + def action(self): + self.underlyingresultChecker.verify() diff --git a/yardstick/benchmark/scenarios/availability/actionrollbackers.py b/yardstick/benchmark/scenarios/availability/actionrollbackers.py new file mode 100644 index 000000000..4b732a10c --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/actionrollbackers.py @@ -0,0 +1,45 @@ +############################################################################## +# Copyright (c) 2016 Juan Qiu and others +# juan_ qiu@tongji.edu.cn +# 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 + +LOG = logging.getLogger(__name__) + + +class ActionRollbacker(object): + """ + Abstract the rollback functions of attacker, operation + and mybe others in future + """ + + def rollback(self): + pass + + +class AttackerRollbacker(ActionRollbacker): + + def __init__(self, attacker): + self.underlyingAttacker = attacker + + def rollback(self): + LOG.debug( + "\033[93m recovering attacker %s \033[0m" + % (self.underlyingAttacker.key)) + self.underlyingAttacker.recover() + + +class OperationRollbacker(ActionRollbacker): + + def __init__(self, operation): + self.underlyingOperation = operation + + def rollback(self): + LOG.debug( + "\033[93m rollback operation %s \033[0m" + % (self.underlyingOperation.key)) + self.underlyingOperation.rollback() diff --git a/yardstick/benchmark/scenarios/availability/director.py b/yardstick/benchmark/scenarios/availability/director.py new file mode 100644 index 000000000..267933dd0 --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/director.py @@ -0,0 +1,106 @@ +############################################################################## +# Copyright (c) 2016 Juan Qiu and others +# juan_ qiu@tongji.edu.cn +# 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 + +from yardstick.benchmark.scenarios.availability.monitor import basemonitor +from yardstick.benchmark.scenarios.availability.attacker import baseattacker +from yardstick.benchmark.scenarios.availability.operation import baseoperation +from yardstick.benchmark.scenarios.availability.result_checker \ + import baseresultchecker +from yardstick.benchmark.scenarios.availability import ActionType +from yardstick.benchmark.scenarios.availability import actionplayers +from yardstick.benchmark.scenarios.availability import actionrollbackers + +LOG = logging.getLogger(__name__) + + +class Director(object): + """ + Director is used to direct a test scenaio + including the creation of action players, test result verification + and rollback of actions. + """ + + def __init__(self, scenario_cfg, context_cfg): + + # A stack store Rollbacker that will be called after + # all actionplayers finish. + self.executionSteps = [] + + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + nodes = self.context_cfg.get("nodes", None) + # setup attackers + if "attackers" in self.scenario_cfg["options"]: + LOG.debug("start init attackers...") + attacker_cfgs = self.scenario_cfg["options"]["attackers"] + self.attackerMgr = baseattacker.AttackerMgr() + self.attackerMgr.init_attackers(attacker_cfgs, nodes) + # setup monitors + if "monitors" in self.scenario_cfg["options"]: + LOG.debug("start init monitors...") + monitor_cfgs = self.scenario_cfg["options"]["monitors"] + self.monitorMgr = basemonitor.MonitorMgr() + self.monitorMgr.init_monitors(monitor_cfgs, nodes) + # setup operations + if "operations" in self.scenario_cfg["options"]: + LOG.debug("start init operations...") + operation_cfgs = self.scenario_cfg["options"]["operations"] + self.operationMgr = baseoperation.OperationMgr() + self.operationMgr.init_operations(operation_cfgs, nodes) + # setup result checker + if "resultCheckers" in self.scenario_cfg["options"]: + LOG.debug("start init resultCheckers...") + result_check_cfgs = self.scenario_cfg["options"]["resultCheckers"] + self.resultCheckerMgr = baseresultchecker.ResultCheckerMgr() + self.resultCheckerMgr.init_ResultChecker(result_check_cfgs, nodes) + + def createActionPlayer(self, type, key): + LOG.debug( + "the type of current action is %s, the key is %s" % (type, key)) + if type == ActionType.ATTACKER: + return actionplayers.AttackerPlayer(self.attackerMgr[key]) + if type == ActionType.MONITOR: + return actionplayers.MonitorPlayer(self.monitorMgr[key]) + if type == ActionType.RESULTCHECKER: + return actionplayers.ResultCheckerPlayer( + self.resultCheckerMgr[key]) + if type == ActionType.OPERATION: + return actionplayers.OperationPlayer(self.operationMgr[key]) + LOG.debug("something run when creatactionplayer") + + def createActionRollbacker(self, type, key): + LOG.debug( + "the type of current action is %s, the key is %s" % (type, key)) + if type == ActionType.ATTACKER: + return actionrollbackers.AttackerRollbacker(self.attackerMgr[key]) + if type == ActionType.OPERATION: + return actionrollbackers.OperationRollbacker( + self.operationMgr[key]) + LOG.debug("no rollbacker created for %s" % (key)) + + def verify(self): + result = True + if hasattr(self, 'monitorMgr'): + result &= self.monitorMgr.verify_SLA() + if hasattr(self, 'resultCheckerMgr'): + result &= self.resultCheckerMgr.verify() + if result: + LOG.debug("monitors are passed") + return result + + def stopMonitors(self): + if "monitors" in self.scenario_cfg["options"]: + self.monitorMgr.wait_monitors() + + def knockoff(self): + LOG.debug("knock off ....") + while self.executionSteps: + singleStep = self.executionSteps.pop() + singleStep.rollback() diff --git a/yardstick/benchmark/scenarios/availability/scenario_general.py b/yardstick/benchmark/scenarios/availability/scenario_general.py new file mode 100644 index 000000000..0a128aa09 --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/scenario_general.py @@ -0,0 +1,68 @@ +############################################################################## +# Copyright (c) 2016 Juan Qiu and others +# juan_ qiu@tongji.edu.cn +# 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 traceback + +from yardstick.benchmark.scenarios import base +from yardstick.benchmark.scenarios.availability.director import Director + +LOG = logging.getLogger(__name__) + + +class ScenarioGeneral(base.Scenario): + """Support orchestrating general HA test scenarios.""" + + __scenario_type__ = "GeneralHA" + + def __init__(self, scenario_cfg, context_cfg): + LOG.debug( + "scenario_cfg:%s context_cfg:%s" % (scenario_cfg, context_cfg)) + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + + def setup(self): + self.director = Director(self.scenario_cfg, self.context_cfg) + + def run(self, args): + steps = self.scenario_cfg["options"]["steps"] + orderedSteps = sorted(steps, key=lambda x: x['index']) + for step in orderedSteps: + LOG.debug( + "\033[94m running step: {0} .... \033[0m" + .format(orderedSteps.index(step)+1)) + try: + actionPlayer = self.director.createActionPlayer( + step['actionType'], step['actionKey']) + actionPlayer.action() + actionRollbacker = self.director.createActionRollbacker( + step['actionType'], step['actionKey']) + if actionRollbacker: + self.director.executionSteps.append(actionRollbacker) + except Exception, e: + LOG.debug(e.message) + traceback.print_exc() + LOG.debug( + "\033[91m exception when running step: {0} .... \033[0m" + .format(orderedSteps.index(step))) + break + finally: + pass + + self.director.stopMonitors() + if self.director.verify(): + LOG.debug( + "\033[92m congratulations, " + "the test cases scenario is pass! \033[0m") + else: + LOG.debug( + "\033[91m aoh,the test cases scenario failed," + "please check the detail debug information! \033[0m") + + def teardown(self): + self.director.knockoff() |