cloudpulse/cloudpulse/scenario/base.py

239 lines
8.4 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from cloudpulse.common import exception
from cloudpulse.common.plugin import discover
import itertools
def scenario(admin_only=False, operator=False, context=None):
"""Add extra fields to benchmark scenarios methods.
This method is used as decorator for the methods of benchmark scenarios
and it adds following extra fields to the methods.
'is_scenario' is set to True
'admin_only' is set to True if a scenario require admin endpoints
'operator' is set to True if the scenario is an operator scenario
"""
def wrapper(func):
func.is_scenario = True
func.admin_only = admin_only
func.operator = operator
func.context = context or {}
return func
return wrapper
class Scenario(object):
"""This is base class for any benchmark scenario.
You should create subclass of this class. And your test scenarios will
be auto discoverable and you will be able to specify it in test config.
"""
def __init__(self, context=None, admin_tests=None,
tenant_tests=None, operator_tests=None):
self._admin_tests = admin_tests
self.tenant_tests = tenant_tests
self.operator_tests = operator_tests
@staticmethod
def get_by_name(name):
"""Returns Scenario class by name."""
for scenario in discover.itersubclasses(Scenario):
if name == scenario.__name__:
return scenario
raise exception.NoSuchScenario(name=name)
@staticmethod
def is_scenario(cls, method_name):
"""Check whether a given method in scenario class is a scenario.
:param cls: scenario class
:param method_name: method name
:returns: True if the method is a benchmark scenario, False otherwise
"""
try:
getattr(cls, method_name)
except Exception:
return False
return Scenario.meta(cls, "is_scenario", method_name, default=False)
@staticmethod
def is_admin(cls, method_name):
"""Check whether a given method in scenario class is a scenario.
:param cls: scenario class
:param method_name: method name
:returns: True if the method is a benchmark scenario, False otherwise
"""
try:
getattr(cls, method_name)
except Exception:
return False
return Scenario.meta(cls, "admin_only", method_name, default=False)
@staticmethod
def is_operator(cls, method_name):
"""Check whether a given method in scenario class is a scenario.
:param cls: scenario class
:param method_name: method name
:returns: True if the method is a benchmark scenario, False otherwise
"""
try:
getattr(cls, method_name)
except Exception:
return False
return Scenario.meta(cls, "operator", method_name, default=False)
@classmethod
def list_operator_scenarios(scenario_cls):
"""Lists all the existing methods in the operator scenario classes.
Returns the method names in format <Class name>.<Method name>, which
is used in the test config.
:returns: List of strings
"""
scenario_classes = (list(discover.itersubclasses(scenario_cls)) +
[scenario_cls])
scenarios_list = [
["%s.%s" % (scenario.__name__, func)
for func in dir(scenario)
if Scenario.is_scenario(scenario, func)
and Scenario.is_operator(scenario, func)]
for scenario in scenario_classes
]
operator_scenarios = list(
itertools.chain.from_iterable(scenarios_list))
return operator_scenarios
@classmethod
def list_admin_scenarios(scenario_cls):
"""Lists all the existing methods in the operator scenario classes.
Returns the method names in format <Class name>.<Method name>, which
is used in the test config.
:returns: List of strings
"""
scenario_classes = (list(discover.itersubclasses(scenario_cls)) +
[scenario_cls])
scenarios_list = [
["%s.%s" % (scenario.__name__, func)
for func in dir(scenario)
if Scenario.is_scenario(scenario, func) and
Scenario.is_admin(scenario, func)]
for scenario in scenario_classes
]
scenarios_list_admin = list(
itertools.chain.from_iterable(scenarios_list))
return scenarios_list_admin
@classmethod
def list_tenant_scenarios(scenario_cls):
"""Lists all the existing methods in the operator scenario classes.
Returns the method names in format <Class name>.<Method name>, which
is used in the test config.
:returns: List of strings
"""
scenario_classes = (list(discover.itersubclasses(scenario_cls)) +
[scenario_cls])
scenarios_list = [
["%s.%s" % (scenario.__name__, func)
for func in dir(scenario)
if Scenario.is_scenario(scenario, func) and
not Scenario.is_admin(scenario, func) and
not Scenario.is_operator(scenario, func)]
for scenario in scenario_classes
]
tenant_scenarios = list(
itertools.chain.from_iterable(scenarios_list))
return tenant_scenarios
@classmethod
def list_all_scenarios(scenario_cls):
"""Lists all the existing methods in the operator scenario classes.
Returns the method names in format <Class name>.<Method name>, which
is used in the test config.
:returns: List of strings
"""
scenario_classes = (list(discover.itersubclasses(scenario_cls)) +
[scenario_cls])
scenarios_list = [
["%s.%s" % (scenario.__name__, func)
for func in dir(scenario) if Scenario.is_scenario(scenario, func)]
for scenario in scenario_classes
]
scenarios_list_flat = list(
itertools.chain.from_iterable(scenarios_list))
return scenarios_list_flat
@classmethod
def list_all_scenario_types(scenario_cls):
"""Lists all the tests in all scenarios."""
scenarios = scenario_cls.list_all_scenarios()
scenario_dict = {}
for scenario in scenarios:
testype, test = scenario.split(".")
if testype in scenario_dict:
scenario_dict[testype].append(test)
else:
scenario_dict[testype] = []
scenario_dict[testype].append(test)
return scenario_dict
@classmethod
def validate(cls, name, config, admin=None, users=None, task=None):
"""Semantic check of benchmark arguments."""
if cls.meta(name, "admin_only") is True:
print("Admin Access")
if cls.meta(name, "operator") is True:
print("Operator")
def setup(self, *args, **kwargs):
"""TODO:Implement setup and teardown"""
pass
def teardown(self, *args, **kwargs):
"""TODO:Implement setup and teardown"""
pass
@staticmethod
def meta(cls, attr_name, method_name=None, default=None):
"""Extract the named meta information out of the scenario name.
:param cls: Scenario (sub)class or string of form 'class.method'
:param attr_name: Name of method attribute holding meta information.
:param method_name: Name of method queried for meta information.
:param default: Value returned if no meta information is attached.
:returns: Meta value bound to method attribute or default.
"""
if isinstance(cls, str):
cls_name, method_name = cls.split(".", 1)
cls = Scenario.get_by_name(cls_name)
method = getattr(cls, method_name)
return getattr(method, attr_name, default)