Merge "Enabled config parameters to plugins"
This commit is contained in:
@@ -23,18 +23,26 @@ import abc
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from watcher.common import clients
|
from watcher.common import clients
|
||||||
|
from watcher.common.loader import loadable
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class BaseAction(object):
|
class BaseAction(loadable.Loadable):
|
||||||
# NOTE(jed) by convention we decided
|
# NOTE(jed) by convention we decided
|
||||||
# that the attribute "resource_id" is the unique id of
|
# that the attribute "resource_id" is the unique id of
|
||||||
# the resource to which the Action applies to allow us to use it in the
|
# the resource to which the Action applies to allow us to use it in the
|
||||||
# watcher dashboard and will be nested in input_parameters
|
# watcher dashboard and will be nested in input_parameters
|
||||||
RESOURCE_ID = 'resource_id'
|
RESOURCE_ID = 'resource_id'
|
||||||
|
|
||||||
def __init__(self, osc=None):
|
def __init__(self, config, osc=None):
|
||||||
""":param osc: an OpenStackClients instance"""
|
"""Constructor
|
||||||
|
|
||||||
|
:param config: A mapping containing the configuration of this action
|
||||||
|
:type config: dict
|
||||||
|
:param osc: an OpenStackClients instance, defaults to None
|
||||||
|
:type osc: :py:class:`~.OpenStackClients` instance, optional
|
||||||
|
"""
|
||||||
|
super(BaseAction, self).__init__(config)
|
||||||
self._input_parameters = {}
|
self._input_parameters = {}
|
||||||
self._osc = osc
|
self._osc = osc
|
||||||
|
|
||||||
@@ -56,6 +64,15 @@ class BaseAction(object):
|
|||||||
def resource_id(self):
|
def resource_id(self):
|
||||||
return self.input_parameters[self.RESOURCE_ID]
|
return self.input_parameters[self.RESOURCE_ID]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
"""Defines the configuration options to be associated to this loadable
|
||||||
|
|
||||||
|
:return: A list of configuration options relative to this Loadable
|
||||||
|
:rtype: list of :class:`oslo_config.cfg.Opt` instances
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def execute(self):
|
def execute(self):
|
||||||
"""Executes the main logic of the action
|
"""Executes the main logic of the action
|
||||||
|
|||||||
@@ -23,18 +23,38 @@ import six
|
|||||||
from watcher.applier.actions import factory
|
from watcher.applier.actions import factory
|
||||||
from watcher.applier.messaging import event_types
|
from watcher.applier.messaging import event_types
|
||||||
from watcher.common import clients
|
from watcher.common import clients
|
||||||
|
from watcher.common.loader import loadable
|
||||||
from watcher.common.messaging.events import event
|
from watcher.common.messaging.events import event
|
||||||
from watcher import objects
|
from watcher import objects
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class BaseWorkFlowEngine(object):
|
class BaseWorkFlowEngine(loadable.Loadable):
|
||||||
def __init__(self, context=None, applier_manager=None):
|
|
||||||
|
def __init__(self, config, context=None, applier_manager=None):
|
||||||
|
"""Constructor
|
||||||
|
|
||||||
|
:param config: A mapping containing the configuration of this
|
||||||
|
workflow engine
|
||||||
|
:type config: dict
|
||||||
|
:param osc: an OpenStackClients object, defaults to None
|
||||||
|
:type osc: :py:class:`~.OpenStackClients` instance, optional
|
||||||
|
"""
|
||||||
|
super(BaseWorkFlowEngine, self).__init__(config)
|
||||||
self._context = context
|
self._context = context
|
||||||
self._applier_manager = applier_manager
|
self._applier_manager = applier_manager
|
||||||
self._action_factory = factory.ActionFactory()
|
self._action_factory = factory.ActionFactory()
|
||||||
self._osc = None
|
self._osc = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
"""Defines the configuration options to be associated to this loadable
|
||||||
|
|
||||||
|
:return: A list of configuration options relative to this Loadable
|
||||||
|
:rtype: list of :class:`oslo_config.cfg.Opt` instances
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def context(self):
|
def context(self):
|
||||||
return self._context
|
return self._context
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright (c) 2015 b<>com
|
# Copyright (c) 2016 b<>com
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -16,33 +16,82 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from stevedore.driver import DriverManager
|
from stevedore import driver as drivermanager
|
||||||
from stevedore import ExtensionManager
|
from stevedore import extension as extensionmanager
|
||||||
|
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.common.loader.base import BaseLoader
|
from watcher.common.loader import base
|
||||||
|
from watcher.common import utils
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DefaultLoader(BaseLoader):
|
class DefaultLoader(base.BaseLoader):
|
||||||
def __init__(self, namespace):
|
|
||||||
|
def __init__(self, namespace, conf=cfg.CONF):
|
||||||
|
"""Entry point loader for Watcher using Stevedore
|
||||||
|
|
||||||
|
:param namespace: namespace of the entry point(s) to load or list
|
||||||
|
:type namespace: str
|
||||||
|
:param conf: ConfigOpts instance, defaults to cfg.CONF
|
||||||
|
"""
|
||||||
super(DefaultLoader, self).__init__()
|
super(DefaultLoader, self).__init__()
|
||||||
self.namespace = namespace
|
self.namespace = namespace
|
||||||
|
self.conf = conf
|
||||||
|
|
||||||
def load(self, name, **kwargs):
|
def load(self, name, **kwargs):
|
||||||
try:
|
try:
|
||||||
LOG.debug("Loading in namespace %s => %s ", self.namespace, name)
|
LOG.debug("Loading in namespace %s => %s ", self.namespace, name)
|
||||||
driver_manager = DriverManager(namespace=self.namespace,
|
driver_manager = drivermanager.DriverManager(
|
||||||
name=name)
|
namespace=self.namespace,
|
||||||
loaded = driver_manager.driver
|
name=name,
|
||||||
|
invoke_on_load=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
driver_cls = driver_manager.driver
|
||||||
|
config = self._load_plugin_config(name, driver_cls)
|
||||||
|
|
||||||
|
driver = driver_cls(config, **kwargs)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise exception.LoadingError(name=name)
|
raise exception.LoadingError(name=name)
|
||||||
|
|
||||||
return loaded(**kwargs)
|
return driver
|
||||||
|
|
||||||
|
def _reload_config(self):
|
||||||
|
self.conf()
|
||||||
|
|
||||||
|
def get_entry_name(self, name):
|
||||||
|
return ".".join([self.namespace, name])
|
||||||
|
|
||||||
|
def _load_plugin_config(self, name, driver_cls):
|
||||||
|
"""Load the config of the plugin"""
|
||||||
|
config = utils.Struct()
|
||||||
|
config_opts = driver_cls.get_config_opts()
|
||||||
|
|
||||||
|
if not config_opts:
|
||||||
|
return config
|
||||||
|
|
||||||
|
group_name = self.get_entry_name(name)
|
||||||
|
self.conf.register_opts(config_opts, group=group_name)
|
||||||
|
|
||||||
|
# Finalise the opt import by re-checking the configuration
|
||||||
|
# against the provided config files
|
||||||
|
self._reload_config()
|
||||||
|
|
||||||
|
config_group = self.conf.get(group_name)
|
||||||
|
if not config_group:
|
||||||
|
raise exception.LoadingError(name=name)
|
||||||
|
|
||||||
|
config.update({
|
||||||
|
name: value for name, value in config_group.items()
|
||||||
|
})
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
def list_available(self):
|
def list_available(self):
|
||||||
extension_manager = ExtensionManager(namespace=self.namespace)
|
extension_manager = extensionmanager.ExtensionManager(
|
||||||
|
namespace=self.namespace)
|
||||||
return {ext.name: ext.plugin for ext in extension_manager.extensions}
|
return {ext.name: ext.plugin for ext in extension_manager.extensions}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
# Copyright (c) 2015 b<>com
|
# Copyright (c) 2016 b<>com
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -13,16 +13,29 @@
|
|||||||
# implied.
|
# implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class FakeLoadable(object):
|
class Loadable(object):
|
||||||
@classmethod
|
"""Generic interface for dynamically loading a driver/entry point.
|
||||||
def namespace(cls):
|
|
||||||
return "TESTING"
|
This defines the contract in order to let the loader manager inject
|
||||||
|
the configuration parameters during the loading.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
self.config = config
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_name(cls):
|
@abc.abstractmethod
|
||||||
return 'fake'
|
def get_config_opts(cls):
|
||||||
|
"""Defines the configuration options to be associated to this loadable
|
||||||
|
|
||||||
|
:return: A list of configuration options relative to this Loadable
|
||||||
|
:rtype: list of :class:`oslo_config.cfg.Opt` instances
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
@@ -41,6 +41,29 @@ CONF.register_opts(UTILS_OPTS)
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Struct(dict):
|
||||||
|
"""Specialized dict where you access an item like an attribute
|
||||||
|
|
||||||
|
>>> struct = Struct()
|
||||||
|
>>> struct['a'] = 1
|
||||||
|
>>> struct.b = 2
|
||||||
|
>>> assert struct.a == 1
|
||||||
|
>>> assert struct['b'] == 2
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
try:
|
||||||
|
return self[name]
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
try:
|
||||||
|
self[name] = value
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
|
||||||
def safe_rstrip(value, chars=None):
|
def safe_rstrip(value, chars=None):
|
||||||
"""Removes trailing characters from a string if that does not make it empty
|
"""Removes trailing characters from a string if that does not make it empty
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,24 @@ See :doc:`../architecture` for more details on this component.
|
|||||||
import abc
|
import abc
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from watcher.common.loader import loadable
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class BasePlanner(object):
|
class BasePlanner(loadable.Loadable):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
"""Defines the configuration options to be associated to this loadable
|
||||||
|
|
||||||
|
:return: A list of configuration options relative to this Loadable
|
||||||
|
:rtype: list of :class:`oslo_config.cfg.Opt` instances
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def schedule(self, context, audit_uuid, solution):
|
def schedule(self, context, audit_uuid, solution):
|
||||||
"""The planner receives a solution to schedule
|
"""The planner receives a solution to schedule
|
||||||
|
|
||||||
:param solution: A solution provided by a strategy for scheduling
|
:param solution: A solution provided by a strategy for scheduling
|
||||||
:type solution: :py:class:`~.BaseSolution` subclass instance
|
:type solution: :py:class:`~.BaseSolution` subclass instance
|
||||||
@@ -60,7 +72,7 @@ class BasePlanner(object):
|
|||||||
:type audit_uuid: str
|
:type audit_uuid: str
|
||||||
:return: Action plan with an ordered sequence of actions such that all
|
:return: Action plan with an ordered sequence of actions such that all
|
||||||
security, dependency, and performance requirements are met.
|
security, dependency, and performance requirements are met.
|
||||||
:rtype: :py:class:`watcher.objects.action_plan.ActionPlan` instance
|
:rtype: :py:class:`watcher.objects.ActionPlan` instance
|
||||||
"""
|
"""
|
||||||
# example: directed acyclic graph
|
# example: directed acyclic graph
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from watcher.common.loader.default import DefaultLoader
|
from watcher.common.loader import default
|
||||||
|
|
||||||
|
|
||||||
class DefaultPlannerLoader(DefaultLoader):
|
class DefaultPlannerLoader(default.DefaultLoader):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(DefaultPlannerLoader, self).__init__(
|
super(DefaultPlannerLoader, self).__init__(
|
||||||
namespace='watcher_planners')
|
namespace='watcher_planners')
|
||||||
|
|||||||
@@ -41,20 +41,22 @@ import six
|
|||||||
|
|
||||||
from watcher._i18n import _
|
from watcher._i18n import _
|
||||||
from watcher.common import clients
|
from watcher.common import clients
|
||||||
|
from watcher.common.loader import loadable
|
||||||
from watcher.decision_engine.solution import default
|
from watcher.decision_engine.solution import default
|
||||||
from watcher.decision_engine.strategy.common import level
|
from watcher.decision_engine.strategy.common import level
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class BaseStrategy(object):
|
class BaseStrategy(loadable.Loadable):
|
||||||
"""A base class for all the strategies
|
"""A base class for all the strategies
|
||||||
|
|
||||||
A Strategy is an algorithm implementation which is able to find a
|
A Strategy is an algorithm implementation which is able to find a
|
||||||
Solution for a given Goal.
|
Solution for a given Goal.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, osc=None):
|
def __init__(self, config, osc=None):
|
||||||
""":param osc: an OpenStackClients instance"""
|
""":param osc: an OpenStackClients instance"""
|
||||||
|
super(BaseStrategy, self).__init__(config)
|
||||||
self._name = self.get_name()
|
self._name = self.get_name()
|
||||||
self._display_name = self.get_display_name()
|
self._display_name = self.get_display_name()
|
||||||
# default strategy level
|
# default strategy level
|
||||||
@@ -104,6 +106,15 @@ class BaseStrategy(object):
|
|||||||
# other services
|
# other services
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
"""Defines the configuration options to be associated to this loadable
|
||||||
|
|
||||||
|
:return: A list of configuration options relative to this Loadable
|
||||||
|
:rtype: list of :class:`oslo_config.cfg.Opt` instances
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def execute(self, original_model):
|
def execute(self, original_model):
|
||||||
"""Execute a strategy
|
"""Execute a strategy
|
||||||
|
|||||||
@@ -71,16 +71,14 @@ class BasicConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
MIGRATION = "migrate"
|
MIGRATION = "migrate"
|
||||||
CHANGE_NOVA_SERVICE_STATE = "change_nova_service_state"
|
CHANGE_NOVA_SERVICE_STATE = "change_nova_service_state"
|
||||||
|
|
||||||
def __init__(self, osc=None):
|
def __init__(self, config=None, osc=None):
|
||||||
"""Basic offline Consolidation using live migration
|
"""Basic offline Consolidation using live migration
|
||||||
|
|
||||||
:param name: The name of the strategy (Default: "basic")
|
:param config: A mapping containing the configuration of this strategy
|
||||||
:param description: The description of the strategy
|
:type config: dict
|
||||||
(Default: "Basic offline consolidation")
|
:param osc: :py:class:`~.OpenStackClients` instance
|
||||||
:param osc: An :py:class:`~watcher.common.clients.OpenStackClients`
|
|
||||||
instance
|
|
||||||
"""
|
"""
|
||||||
super(BasicConsolidation, self).__init__(osc)
|
super(BasicConsolidation, self).__init__(config, osc)
|
||||||
|
|
||||||
# set default value for the number of released nodes
|
# set default value for the number of released nodes
|
||||||
self.number_of_released_nodes = 0
|
self.number_of_released_nodes = 0
|
||||||
|
|||||||
@@ -48,8 +48,14 @@ class DummyStrategy(base.DummyBaseStrategy):
|
|||||||
NOP = "nop"
|
NOP = "nop"
|
||||||
SLEEP = "sleep"
|
SLEEP = "sleep"
|
||||||
|
|
||||||
def __init__(self, osc=None):
|
def __init__(self, config=None, osc=None):
|
||||||
super(DummyStrategy, self).__init__(osc)
|
"""Dummy Strategy implemented for demo and testing purposes
|
||||||
|
|
||||||
|
:param config: A mapping containing the configuration of this strategy
|
||||||
|
:type config: dict
|
||||||
|
:param osc: :py:class:`~.OpenStackClients` instance
|
||||||
|
"""
|
||||||
|
super(DummyStrategy, self).__init__(config, osc)
|
||||||
|
|
||||||
def execute(self, original_model):
|
def execute(self, original_model):
|
||||||
LOG.debug("Executing Dummy strategy")
|
LOG.debug("Executing Dummy strategy")
|
||||||
|
|||||||
@@ -78,14 +78,15 @@ class OutletTempControl(base.ThermalOptimizationBaseStrategy):
|
|||||||
|
|
||||||
MIGRATION = "migrate"
|
MIGRATION = "migrate"
|
||||||
|
|
||||||
def __init__(self, osc=None):
|
def __init__(self, config=None, osc=None):
|
||||||
"""Outlet temperature control using live migration
|
"""Outlet temperature control using live migration
|
||||||
|
|
||||||
:param name: the name of the strategy
|
:param config: A mapping containing the configuration of this strategy
|
||||||
:param description: a description of the strategy
|
:type config: dict
|
||||||
:param osc: an OpenStackClients object
|
:param osc: an OpenStackClients object, defaults to None
|
||||||
|
:type osc: :py:class:`~.OpenStackClients` instance, optional
|
||||||
"""
|
"""
|
||||||
super(OutletTempControl, self).__init__(osc)
|
super(OutletTempControl, self).__init__(config, osc)
|
||||||
# the migration plan will be triggered when the outlet temperature
|
# the migration plan will be triggered when the outlet temperature
|
||||||
# reaches threshold
|
# reaches threshold
|
||||||
# TODO(zhenzanz): Threshold should be configurable for each audit
|
# TODO(zhenzanz): Threshold should be configurable for each audit
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ class VMWorkloadConsolidation(base.ServerConsolidationBaseStrategy):
|
|||||||
https://github.com/openstack/watcher-specs/blob/master/specs/mitaka/implemented/zhaw-load-consolidation.rst
|
https://github.com/openstack/watcher-specs/blob/master/specs/mitaka/implemented/zhaw-load-consolidation.rst
|
||||||
""" # noqa
|
""" # noqa
|
||||||
|
|
||||||
def __init__(self, osc=None):
|
def __init__(self, config=None, osc=None):
|
||||||
super(VMWorkloadConsolidation, self).__init__(osc)
|
super(VMWorkloadConsolidation, self).__init__(config, osc)
|
||||||
self._ceilometer = None
|
self._ceilometer = None
|
||||||
self.number_of_migrations = 0
|
self.number_of_migrations = 0
|
||||||
self.number_of_released_hypervisors = 0
|
self.number_of_released_hypervisors = 0
|
||||||
|
|||||||
@@ -18,14 +18,27 @@
|
|||||||
from keystoneauth1 import loading as ka_loading
|
from keystoneauth1 import loading as ka_loading
|
||||||
|
|
||||||
import watcher.api.app
|
import watcher.api.app
|
||||||
|
from watcher.applier.actions.loading import default as action_loader
|
||||||
from watcher.applier import manager as applier_manager
|
from watcher.applier import manager as applier_manager
|
||||||
|
from watcher.applier.workflow_engine.loading import default as \
|
||||||
|
workflow_engine_loader
|
||||||
from watcher.common import clients
|
from watcher.common import clients
|
||||||
from watcher.decision_engine import manager as decision_engine_manger
|
from watcher.decision_engine import manager as decision_engine_manger
|
||||||
|
from watcher.decision_engine.planner.loading import default as planner_loader
|
||||||
from watcher.decision_engine.planner import manager as planner_manager
|
from watcher.decision_engine.planner import manager as planner_manager
|
||||||
|
from watcher.decision_engine.strategy.loading import default as strategy_loader
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN_LOADERS = (
|
||||||
|
action_loader.DefaultActionLoader,
|
||||||
|
planner_loader.DefaultPlannerLoader,
|
||||||
|
strategy_loader.DefaultStrategyLoader,
|
||||||
|
workflow_engine_loader.DefaultWorkFlowEngineLoader,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
def list_opts():
|
||||||
return [
|
watcher_opts = [
|
||||||
('api', watcher.api.app.API_SERVICE_OPTS),
|
('api', watcher.api.app.API_SERVICE_OPTS),
|
||||||
('watcher_decision_engine',
|
('watcher_decision_engine',
|
||||||
decision_engine_manger.WATCHER_DECISION_ENGINE_OPTS),
|
decision_engine_manger.WATCHER_DECISION_ENGINE_OPTS),
|
||||||
@@ -41,3 +54,22 @@ def list_opts():
|
|||||||
ka_loading.get_auth_plugin_conf_options('password') +
|
ka_loading.get_auth_plugin_conf_options('password') +
|
||||||
ka_loading.get_session_conf_options()))
|
ka_loading.get_session_conf_options()))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
watcher_opts += list_plugin_opts()
|
||||||
|
|
||||||
|
return watcher_opts
|
||||||
|
|
||||||
|
|
||||||
|
def list_plugin_opts():
|
||||||
|
plugins_opts = []
|
||||||
|
for plugin_loader_cls in PLUGIN_LOADERS:
|
||||||
|
plugin_loader = plugin_loader_cls()
|
||||||
|
plugins_map = plugin_loader.list_available()
|
||||||
|
|
||||||
|
for plugin_name, plugin_cls in plugins_map.items():
|
||||||
|
plugin_opts = plugin_cls.get_config_opts()
|
||||||
|
if plugin_opts:
|
||||||
|
plugins_opts.append(
|
||||||
|
(plugin_loader.get_entry_name(plugin_name), plugin_opts))
|
||||||
|
|
||||||
|
return plugins_opts
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ class TestChangeNovaServiceState(base.TestCase):
|
|||||||
baction.BaseAction.RESOURCE_ID: "compute-1",
|
baction.BaseAction.RESOURCE_ID: "compute-1",
|
||||||
"state": hstate.HypervisorState.ENABLED.value,
|
"state": hstate.HypervisorState.ENABLED.value,
|
||||||
}
|
}
|
||||||
self.action = change_nova_service_state.ChangeNovaServiceState()
|
self.action = change_nova_service_state.ChangeNovaServiceState(
|
||||||
|
mock.Mock())
|
||||||
self.action.input_parameters = self.input_parameters
|
self.action.input_parameters = self.input_parameters
|
||||||
|
|
||||||
def test_parameters_down(self):
|
def test_parameters_down(self):
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class TestMigration(base.TestCase):
|
|||||||
"dst_hypervisor": "hypervisor2-hostname",
|
"dst_hypervisor": "hypervisor2-hostname",
|
||||||
baction.BaseAction.RESOURCE_ID: self.INSTANCE_UUID,
|
baction.BaseAction.RESOURCE_ID: self.INSTANCE_UUID,
|
||||||
}
|
}
|
||||||
self.action = migration.Migrate()
|
self.action = migration.Migrate(mock.Mock())
|
||||||
self.action.input_parameters = self.input_parameters
|
self.action.input_parameters = self.input_parameters
|
||||||
|
|
||||||
self.input_parameters_cold = {
|
self.input_parameters_cold = {
|
||||||
@@ -67,7 +67,7 @@ class TestMigration(base.TestCase):
|
|||||||
"dst_hypervisor": "hypervisor2-hostname",
|
"dst_hypervisor": "hypervisor2-hostname",
|
||||||
baction.BaseAction.RESOURCE_ID: self.INSTANCE_UUID,
|
baction.BaseAction.RESOURCE_ID: self.INSTANCE_UUID,
|
||||||
}
|
}
|
||||||
self.action_cold = migration.Migrate()
|
self.action_cold = migration.Migrate(mock.Mock())
|
||||||
self.action_cold.input_parameters = self.input_parameters_cold
|
self.action_cold.input_parameters = self.input_parameters_cold
|
||||||
|
|
||||||
def test_parameters(self):
|
def test_parameters(self):
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import mock
|
||||||
import voluptuous
|
import voluptuous
|
||||||
|
|
||||||
from watcher.applier.actions import sleep
|
from watcher.applier.actions import sleep
|
||||||
@@ -23,7 +25,7 @@ from watcher.tests import base
|
|||||||
class TestSleep(base.TestCase):
|
class TestSleep(base.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestSleep, self).setUp()
|
super(TestSleep, self).setUp()
|
||||||
self.s = sleep.Sleep()
|
self.s = sleep.Sleep(mock.Mock())
|
||||||
|
|
||||||
def test_parameters_duration(self):
|
def test_parameters_duration(self):
|
||||||
self.s.input_parameters = {self.s.DURATION: 1.0}
|
self.s.input_parameters = {self.s.DURATION: 1.0}
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ import abc
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from stevedore import driver
|
|
||||||
from stevedore import extension
|
|
||||||
|
|
||||||
from watcher.applier.actions import base as abase
|
from watcher.applier.actions import base as abase
|
||||||
|
from watcher.applier.actions import factory
|
||||||
from watcher.applier.workflow_engine import default as tflow
|
from watcher.applier.workflow_engine import default as tflow
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.common import utils
|
from watcher.common import utils
|
||||||
@@ -31,6 +30,10 @@ from watcher import objects
|
|||||||
from watcher.tests.db import base
|
from watcher.tests.db import base
|
||||||
|
|
||||||
|
|
||||||
|
class ExpectedException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class FakeAction(abase.BaseAction):
|
class FakeAction(abase.BaseAction):
|
||||||
def schema(self):
|
def schema(self):
|
||||||
@@ -46,21 +49,14 @@ class FakeAction(abase.BaseAction):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
raise Exception()
|
raise ExpectedException()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def namespace(cls):
|
|
||||||
return "TESTING"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_name(cls):
|
|
||||||
return 'fake_action'
|
|
||||||
|
|
||||||
|
|
||||||
class TestDefaultWorkFlowEngine(base.DbTestCase):
|
class TestDefaultWorkFlowEngine(base.DbTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestDefaultWorkFlowEngine, self).setUp()
|
super(TestDefaultWorkFlowEngine, self).setUp()
|
||||||
self.engine = tflow.DefaultWorkFlowEngine(
|
self.engine = tflow.DefaultWorkFlowEngine(
|
||||||
|
config=mock.Mock(),
|
||||||
context=self.context,
|
context=self.context,
|
||||||
applier_manager=mock.MagicMock())
|
applier_manager=mock.MagicMock())
|
||||||
|
|
||||||
@@ -86,6 +82,7 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
|
|||||||
new_action = objects.Action(self.context, **action)
|
new_action = objects.Action(self.context, **action)
|
||||||
new_action.create(self.context)
|
new_action.create(self.context)
|
||||||
new_action.save()
|
new_action.save()
|
||||||
|
|
||||||
return new_action
|
return new_action
|
||||||
|
|
||||||
def check_action_state(self, action, expected_state):
|
def check_action_state(self, action, expected_state):
|
||||||
@@ -175,18 +172,13 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
|
|||||||
self.check_action_state(second, objects.action.State.SUCCEEDED)
|
self.check_action_state(second, objects.action.State.SUCCEEDED)
|
||||||
self.check_action_state(third, objects.action.State.FAILED)
|
self.check_action_state(third, objects.action.State.FAILED)
|
||||||
|
|
||||||
@mock.patch("watcher.common.loader.default.DriverManager")
|
@mock.patch.object(factory.ActionFactory, "make_action")
|
||||||
def test_execute_with_action_exception(self, m_driver):
|
def test_execute_with_action_exception(self, m_make_action):
|
||||||
m_driver.return_value = driver.DriverManager.make_test_instance(
|
actions = [self.create_action("fake_action", {}, None)]
|
||||||
extension=extension.Extension(name=FakeAction.get_name(),
|
m_make_action.return_value = FakeAction(mock.Mock())
|
||||||
entry_point="%s:%s" % (
|
|
||||||
FakeAction.__module__,
|
|
||||||
FakeAction.__name__),
|
|
||||||
plugin=FakeAction,
|
|
||||||
obj=None),
|
|
||||||
namespace=FakeAction.namespace())
|
|
||||||
actions = [self.create_action("dontcare", {}, None)]
|
|
||||||
|
|
||||||
self.assertRaises(exception.WorkflowExecutionException,
|
exc = self.assertRaises(exception.WorkflowExecutionException,
|
||||||
self.engine.execute, actions)
|
self.engine.execute, actions)
|
||||||
|
|
||||||
|
self.assertIsInstance(exc.kwargs['error'], ExpectedException)
|
||||||
self.check_action_state(actions[0], objects.action.State.FAILED)
|
self.check_action_state(actions[0], objects.action.State.FAILED)
|
||||||
|
|||||||
@@ -18,36 +18,88 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from oslotest.base import BaseTestCase
|
from oslo_config import cfg
|
||||||
from stevedore.driver import DriverManager
|
from stevedore import driver as drivermanager
|
||||||
from stevedore.extension import Extension
|
from stevedore.extension import Extension
|
||||||
|
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.common.loader.default import DefaultLoader
|
from watcher.common.loader import default
|
||||||
from watcher.tests.common.loader.FakeLoadable import FakeLoadable
|
from watcher.common.loader import loadable
|
||||||
|
from watcher.tests import base
|
||||||
|
|
||||||
|
|
||||||
class TestLoader(BaseTestCase):
|
class FakeLoadable(loadable.Loadable):
|
||||||
@mock.patch("watcher.common.loader.default.DriverManager")
|
|
||||||
def test_load_driver_no_opt(self, m_driver_manager):
|
|
||||||
m_driver_manager.return_value = DriverManager.make_test_instance(
|
|
||||||
extension=Extension(name=FakeLoadable.get_name(),
|
|
||||||
entry_point="%s:%s" % (
|
|
||||||
FakeLoadable.__module__,
|
|
||||||
FakeLoadable.__name__),
|
|
||||||
plugin=FakeLoadable,
|
|
||||||
obj=None),
|
|
||||||
namespace=FakeLoadable.namespace())
|
|
||||||
|
|
||||||
loader_manager = DefaultLoader(namespace='TESTING')
|
@classmethod
|
||||||
loaded_driver = loader_manager.load(name='fake')
|
def get_config_opts(cls):
|
||||||
|
return []
|
||||||
|
|
||||||
self.assertEqual(FakeLoadable.get_name(), loaded_driver.get_name())
|
|
||||||
|
|
||||||
@mock.patch("watcher.common.loader.default.DriverManager")
|
class FakeLoadableWithOpts(loadable.Loadable):
|
||||||
def test_load_driver_bad_plugin(self, m_driver_manager):
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
return [
|
||||||
|
cfg.StrOpt("test_opt", default="fake_with_opts"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestLoader(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestLoader, self).setUp()
|
||||||
|
|
||||||
|
def _fake_parse(self, *args, **kw):
|
||||||
|
return cfg.ConfigOpts._parse_cli_opts(cfg.CONF, [])
|
||||||
|
|
||||||
|
cfg.CONF._parse_cli_opts = _fake_parse
|
||||||
|
|
||||||
|
def test_load_loadable_no_opt(self):
|
||||||
|
fake_driver = drivermanager.DriverManager.make_test_instance(
|
||||||
|
extension=Extension(
|
||||||
|
name="fake",
|
||||||
|
entry_point="%s:%s" % (FakeLoadable.__module__,
|
||||||
|
FakeLoadable.__name__),
|
||||||
|
plugin=FakeLoadable,
|
||||||
|
obj=None),
|
||||||
|
namespace="TESTING")
|
||||||
|
|
||||||
|
loader_manager = default.DefaultLoader(namespace='TESTING')
|
||||||
|
with mock.patch.object(drivermanager,
|
||||||
|
"DriverManager") as m_driver_manager:
|
||||||
|
m_driver_manager.return_value = fake_driver
|
||||||
|
loaded_driver = loader_manager.load(name='fake')
|
||||||
|
|
||||||
|
self.assertIsInstance(loaded_driver, FakeLoadable)
|
||||||
|
|
||||||
|
@mock.patch("watcher.common.loader.default.drivermanager.DriverManager")
|
||||||
|
def test_load_loadable_bad_plugin(self, m_driver_manager):
|
||||||
m_driver_manager.side_effect = Exception()
|
m_driver_manager.side_effect = Exception()
|
||||||
|
|
||||||
loader_manager = DefaultLoader(namespace='TESTING')
|
loader_manager = default.DefaultLoader(namespace='TESTING')
|
||||||
self.assertRaises(exception.LoadingError, loader_manager.load,
|
self.assertRaises(exception.LoadingError, loader_manager.load,
|
||||||
name='bad_driver')
|
name='bad_driver')
|
||||||
|
|
||||||
|
def test_load_loadable_with_opts(self):
|
||||||
|
fake_driver = drivermanager.DriverManager.make_test_instance(
|
||||||
|
extension=Extension(
|
||||||
|
name="fake",
|
||||||
|
entry_point="%s:%s" % (FakeLoadableWithOpts.__module__,
|
||||||
|
FakeLoadableWithOpts.__name__),
|
||||||
|
plugin=FakeLoadableWithOpts,
|
||||||
|
obj=None),
|
||||||
|
namespace="TESTING")
|
||||||
|
|
||||||
|
loader_manager = default.DefaultLoader(namespace='TESTING')
|
||||||
|
with mock.patch.object(drivermanager,
|
||||||
|
"DriverManager") as m_driver_manager:
|
||||||
|
m_driver_manager.return_value = fake_driver
|
||||||
|
loaded_driver = loader_manager.load(name='fake')
|
||||||
|
|
||||||
|
self.assertIsInstance(loaded_driver, FakeLoadableWithOpts)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
"fake_with_opts", loaded_driver.config.get("test_opt"))
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
"fake_with_opts", loaded_driver.config.test_opt)
|
||||||
|
|||||||
@@ -14,8 +14,12 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from watcher.decision_engine.strategy.strategies import base as base_strategy
|
from watcher.decision_engine.strategy.strategies import base as base_strategy
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class FakeStrategy(base_strategy.BaseStrategy):
|
class FakeStrategy(base_strategy.BaseStrategy):
|
||||||
|
|
||||||
@@ -48,6 +52,10 @@ class FakeStrategy(base_strategy.BaseStrategy):
|
|||||||
def get_translatable_goal_display_name(cls):
|
def get_translatable_goal_display_name(cls):
|
||||||
return cls.GOAL_DISPLAY_NAME
|
return cls.GOAL_DISPLAY_NAME
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
return []
|
||||||
|
|
||||||
def execute(self, original_model):
|
def execute(self, original_model):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -58,6 +66,12 @@ class FakeDummy1Strategy1(FakeStrategy):
|
|||||||
NAME = "STRATEGY_1"
|
NAME = "STRATEGY_1"
|
||||||
DISPLAY_NAME = "Strategy 1"
|
DISPLAY_NAME = "Strategy 1"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_opts(cls):
|
||||||
|
return [
|
||||||
|
cfg.StrOpt('test_opt', help="Option used for testing."),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class FakeDummy1Strategy2(FakeStrategy):
|
class FakeDummy1Strategy2(FakeStrategy):
|
||||||
GOAL_NAME = "DUMMY_1"
|
GOAL_NAME = "DUMMY_1"
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class SolutionFakerSingleHyp(object):
|
|||||||
|
|
||||||
class TestActionScheduling(base.DbTestCase):
|
class TestActionScheduling(base.DbTestCase):
|
||||||
def test_schedule_actions(self):
|
def test_schedule_actions(self):
|
||||||
default_planner = pbase.DefaultPlanner()
|
default_planner = pbase.DefaultPlanner(mock.Mock())
|
||||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||||
solution = dsol.DefaultSolution()
|
solution = dsol.DefaultSolution()
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class TestActionScheduling(base.DbTestCase):
|
|||||||
self.assertEqual("migrate", actions[0].action_type)
|
self.assertEqual("migrate", actions[0].action_type)
|
||||||
|
|
||||||
def test_schedule_two_actions(self):
|
def test_schedule_two_actions(self):
|
||||||
default_planner = pbase.DefaultPlanner()
|
default_planner = pbase.DefaultPlanner(mock.Mock())
|
||||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||||
solution = dsol.DefaultSolution()
|
solution = dsol.DefaultSolution()
|
||||||
|
|
||||||
@@ -115,9 +115,10 @@ class TestActionScheduling(base.DbTestCase):
|
|||||||
|
|
||||||
|
|
||||||
class TestDefaultPlanner(base.DbTestCase):
|
class TestDefaultPlanner(base.DbTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestDefaultPlanner, self).setUp()
|
super(TestDefaultPlanner, self).setUp()
|
||||||
self.default_planner = pbase.DefaultPlanner()
|
self.default_planner = pbase.DefaultPlanner(mock.Mock())
|
||||||
obj_utils.create_test_audit_template(self.context)
|
obj_utils.create_test_audit_template(self.context)
|
||||||
|
|
||||||
p = mock.patch.object(db_api.BaseConnection, 'create_action_plan')
|
p = mock.patch.object(db_api.BaseConnection, 'create_action_plan')
|
||||||
|
|||||||
@@ -15,8 +15,9 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from mock import patch
|
import mock
|
||||||
from stevedore import extension
|
from stevedore import extension
|
||||||
|
|
||||||
from watcher.common import exception
|
from watcher.common import exception
|
||||||
from watcher.decision_engine.strategy.loading import default as default_loading
|
from watcher.decision_engine.strategy.loading import default as default_loading
|
||||||
from watcher.decision_engine.strategy.strategies import dummy_strategy
|
from watcher.decision_engine.strategy.strategies import dummy_strategy
|
||||||
@@ -25,38 +26,38 @@ from watcher.tests import base
|
|||||||
|
|
||||||
class TestDefaultStrategyLoader(base.TestCase):
|
class TestDefaultStrategyLoader(base.TestCase):
|
||||||
|
|
||||||
strategy_loader = default_loading.DefaultStrategyLoader()
|
|
||||||
|
|
||||||
def test_load_strategy_with_empty_model(self):
|
def test_load_strategy_with_empty_model(self):
|
||||||
self.assertRaises(
|
strategy_loader = default_loading.DefaultStrategyLoader()
|
||||||
exception.LoadingError, self.strategy_loader.load, None)
|
self.assertRaises(exception.LoadingError, strategy_loader.load, None)
|
||||||
|
|
||||||
def test_load_strategy_is_basic(self):
|
def test_load_strategy_is_basic(self):
|
||||||
|
strategy_loader = default_loading.DefaultStrategyLoader()
|
||||||
expected_strategy = 'basic'
|
expected_strategy = 'basic'
|
||||||
selected_strategy = self.strategy_loader.load(expected_strategy)
|
selected_strategy = strategy_loader.load(expected_strategy)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
selected_strategy.id,
|
selected_strategy.id,
|
||||||
expected_strategy,
|
expected_strategy,
|
||||||
'The default strategy should be basic')
|
'The default strategy should be basic')
|
||||||
|
|
||||||
@patch("watcher.common.loader.default.ExtensionManager")
|
def test_strategy_loader(self):
|
||||||
def test_strategy_loader(self, m_extension_manager):
|
|
||||||
dummy_strategy_name = "dummy"
|
dummy_strategy_name = "dummy"
|
||||||
# Set up the fake Stevedore extensions
|
# Set up the fake Stevedore extensions
|
||||||
m_extension_manager.return_value = extension.\
|
fake_extmanager_call = extension.ExtensionManager.make_test_instance(
|
||||||
ExtensionManager.make_test_instance(
|
extensions=[extension.Extension(
|
||||||
extensions=[extension.Extension(
|
name=dummy_strategy_name,
|
||||||
name=dummy_strategy_name,
|
entry_point="%s:%s" % (
|
||||||
entry_point="%s:%s" % (
|
dummy_strategy.DummyStrategy.__module__,
|
||||||
dummy_strategy.DummyStrategy.__module__,
|
dummy_strategy.DummyStrategy.__name__),
|
||||||
dummy_strategy.DummyStrategy.__name__),
|
plugin=dummy_strategy.DummyStrategy,
|
||||||
plugin=dummy_strategy.DummyStrategy,
|
obj=None,
|
||||||
obj=None,
|
)],
|
||||||
)],
|
namespace="watcher_strategies",
|
||||||
namespace="watcher_strategies",
|
)
|
||||||
)
|
|
||||||
strategy_loader = default_loading.DefaultStrategyLoader()
|
with mock.patch.object(extension, "ExtensionManager") as m_ext_manager:
|
||||||
loaded_strategy = strategy_loader.load("dummy")
|
m_ext_manager.return_value = fake_extmanager_call
|
||||||
|
strategy_loader = default_loading.DefaultStrategyLoader()
|
||||||
|
loaded_strategy = strategy_loader.load("dummy")
|
||||||
|
|
||||||
self.assertEqual("dummy", loaded_strategy.id)
|
self.assertEqual("dummy", loaded_strategy.id)
|
||||||
self.assertEqual("Dummy strategy", loaded_strategy.display_name)
|
self.assertEqual("Dummy strategy", loaded_strategy.display_name)
|
||||||
@@ -67,5 +68,5 @@ class TestDefaultStrategyLoader(base.TestCase):
|
|||||||
self.assertIsInstance(loaded_strategy, dummy_strategy.DummyStrategy)
|
self.assertIsInstance(loaded_strategy, dummy_strategy.DummyStrategy)
|
||||||
|
|
||||||
def test_endpoints(self):
|
def test_endpoints(self):
|
||||||
for endpoint in self.strategy_loader.list_available():
|
for endpoint in strategy_loader.list_available():
|
||||||
self.assertIsNotNone(self.strategy_loader.load(endpoint))
|
self.assertIsNotNone(self.strategy_loader.load(endpoint))
|
||||||
|
|||||||
@@ -14,18 +14,97 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from __future__ import absolute_import
|
import mock
|
||||||
from __future__ import unicode_literals
|
from stevedore import extension
|
||||||
|
|
||||||
import watcher.opts as opt
|
from watcher import opts
|
||||||
|
from watcher.tests import base
|
||||||
from watcher.tests.base import BaseTestCase
|
from watcher.tests.decision_engine import fake_strategies
|
||||||
|
|
||||||
|
|
||||||
class TestListOpts(BaseTestCase):
|
class TestListOpts(base.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestListOpts, self).setUp()
|
super(TestListOpts, self).setUp()
|
||||||
|
self.base_sections = [
|
||||||
|
'api', 'watcher_decision_engine', 'watcher_applier',
|
||||||
|
'watcher_planner', 'nova_client', 'glance_client',
|
||||||
|
'cinder_client', 'ceilometer_client', 'neutron_client',
|
||||||
|
'watcher_clients_auth']
|
||||||
|
|
||||||
def test_run_list_opts(self):
|
def test_run_list_opts(self):
|
||||||
result = opt.list_opts()
|
expected_sections = self.base_sections
|
||||||
|
|
||||||
|
result = opts.list_opts()
|
||||||
|
|
||||||
self.assertIsNotNone(result)
|
self.assertIsNotNone(result)
|
||||||
|
for section_name, options in result:
|
||||||
|
self.assertIn(section_name, expected_sections)
|
||||||
|
self.assertTrue(len(options))
|
||||||
|
|
||||||
|
def test_list_opts_no_opts(self):
|
||||||
|
expected_sections = self.base_sections
|
||||||
|
# Set up the fake Stevedore extensions
|
||||||
|
fake_extmanager_call = extension.ExtensionManager.make_test_instance(
|
||||||
|
extensions=[extension.Extension(
|
||||||
|
name=fake_strategies.FakeDummy1Strategy2.get_name(),
|
||||||
|
entry_point="%s:%s" % (
|
||||||
|
fake_strategies.FakeDummy1Strategy2.__module__,
|
||||||
|
fake_strategies.FakeDummy1Strategy2.__name__),
|
||||||
|
plugin=fake_strategies.FakeDummy1Strategy2,
|
||||||
|
obj=None,
|
||||||
|
)],
|
||||||
|
namespace="watcher_strategies",
|
||||||
|
)
|
||||||
|
|
||||||
|
def m_list_available(namespace):
|
||||||
|
if namespace == "watcher_strategies":
|
||||||
|
return fake_extmanager_call
|
||||||
|
else:
|
||||||
|
return extension.ExtensionManager.make_test_instance(
|
||||||
|
extensions=[], namespace=namespace)
|
||||||
|
|
||||||
|
with mock.patch.object(extension, "ExtensionManager") as m_ext_manager:
|
||||||
|
m_ext_manager.side_effect = m_list_available
|
||||||
|
result = opts.list_opts()
|
||||||
|
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
for section_name, options in result:
|
||||||
|
self.assertIn(section_name, expected_sections)
|
||||||
|
self.assertTrue(len(options))
|
||||||
|
|
||||||
|
def test_list_opts_with_opts(self):
|
||||||
|
expected_sections = self.base_sections + [
|
||||||
|
'watcher_strategies.STRATEGY_1']
|
||||||
|
# Set up the fake Stevedore extensions
|
||||||
|
fake_extmanager_call = extension.ExtensionManager.make_test_instance(
|
||||||
|
extensions=[extension.Extension(
|
||||||
|
name=fake_strategies.FakeDummy1Strategy1.get_name(),
|
||||||
|
entry_point="%s:%s" % (
|
||||||
|
fake_strategies.FakeDummy1Strategy1.__module__,
|
||||||
|
fake_strategies.FakeDummy1Strategy1.__name__),
|
||||||
|
plugin=fake_strategies.FakeDummy1Strategy1,
|
||||||
|
obj=None,
|
||||||
|
)],
|
||||||
|
namespace="watcher_strategies",
|
||||||
|
)
|
||||||
|
|
||||||
|
def m_list_available(namespace):
|
||||||
|
if namespace == "watcher_strategies":
|
||||||
|
return fake_extmanager_call
|
||||||
|
else:
|
||||||
|
return extension.ExtensionManager.make_test_instance(
|
||||||
|
extensions=[], namespace=namespace)
|
||||||
|
|
||||||
|
with mock.patch.object(extension, "ExtensionManager") as m_ext_manager:
|
||||||
|
m_ext_manager.side_effect = m_list_available
|
||||||
|
result = opts.list_opts()
|
||||||
|
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
for section_name, options in result:
|
||||||
|
self.assertIn(section_name, expected_sections)
|
||||||
|
self.assertTrue(len(options))
|
||||||
|
|
||||||
|
result_map = dict(result)
|
||||||
|
|
||||||
|
strategy_opts = result_map['watcher_strategies.STRATEGY_1']
|
||||||
|
self.assertEqual(['test_opt'], [opt.name for opt in strategy_opts])
|
||||||
|
|||||||
Reference in New Issue
Block a user