Merge "Add a dynamic loading of Actions handlers in the Watcher Applier"

This commit is contained in:
Jenkins
2016-01-15 15:44:56 +00:00
committed by Gerrit Code Review
29 changed files with 305 additions and 450 deletions

View File

@@ -46,6 +46,11 @@ watcher_strategies =
basic = watcher.decision_engine.strategy.strategies.basic_consolidation:BasicConsolidation
outlet_temp_control = watcher.decision_engine.strategy.strategies.outlet_temp_control:OutletTempControl
watcher_actions =
migrate = watcher.applier.primitives.migration:Migrate
nop = watcher.applier.primitives.nop:Nop
change_nova_service_state = watcher.applier.primitives.change_nova_service_state:ChangeNovaServiceState
watcher_planners =
default = watcher.decision_engine.planner.default:DefaultPlanner

View File

@@ -17,24 +17,23 @@
# limitations under the License.
#
from watcher.applier.base import BaseApplier
from watcher.applier.execution.executor import ActionPlanExecutor
from watcher.objects import Action
from watcher.objects import ActionPlan
from watcher.applier import base
from watcher.applier.execution import default
from watcher import objects
class DefaultApplier(BaseApplier):
class DefaultApplier(base.BaseApplier):
def __init__(self, manager_applier, context):
super(DefaultApplier, self).__init__()
self.manager_applier = manager_applier
self.context = context
self.executor = ActionPlanExecutor(manager_applier, context)
self.executor = default.DefaultActionPlanExecutor(manager_applier,
context)
def execute(self, action_plan_uuid):
action_plan = ActionPlan.get_by_uuid(self.context, action_plan_uuid)
action_plan = objects.ActionPlan.get_by_uuid(self.context,
action_plan_uuid)
# todo(jed) remove direct access to dbapi need filter in object
actions = Action.dbapi.get_action_list(self.context,
filters={
'action_plan_id':
action_plan.id})
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
return self.executor.execute(actions)

View File

@@ -0,0 +1,62 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
#
# 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.
#
import abc
import six
from watcher.applier.messaging import events
from watcher.applier.primitives import factory
from watcher.common.messaging.events import event
from watcher import objects
@six.add_metaclass(abc.ABCMeta)
class BaseActionPlanExecutor(object):
def __init__(self, manager_applier, context):
self._manager_applier = manager_applier
self._context = context
self._action_factory = factory.ActionFactory()
@property
def context(self):
return self._context
@property
def manager_applier(self):
return self._manager_applier
@property
def action_factory(self):
return self._action_factory
def notify(self, action, state):
db_action = objects.Action.get_by_uuid(self.context, action.uuid)
db_action.state = state
db_action.save()
ev = event.Event()
ev.type = events.Events.LAUNCH_ACTION
ev.data = {}
payload = {'action_uuid': action.uuid,
'action_state': state}
self.manager_applier.topic_status.publish_event(ev.type.name,
payload)
@abc.abstractmethod
def execute(self, actions):
raise NotImplementedError()

View File

@@ -0,0 +1,57 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 oslo_log import log
from watcher._i18n import _LE
from watcher.applier.execution import base
from watcher.applier.execution import deploy_phase
from watcher.objects import action_plan
LOG = log.getLogger(__name__)
class DefaultActionPlanExecutor(base.BaseActionPlanExecutor):
def __init__(self, manager_applier, context):
super(DefaultActionPlanExecutor, self).__init__(manager_applier,
context)
self.deploy = deploy_phase.DeployPhase(self)
def execute(self, actions):
for action in actions:
try:
self.notify(action, action_plan.Status.ONGOING)
loaded_action = self.action_factory.make_action(action)
result = self.deploy.execute_primitive(loaded_action)
if result is False:
self.notify(action, action_plan.Status.FAILED)
self.deploy.rollback()
return False
else:
self.deploy.populate(loaded_action)
self.notify(action, action_plan.Status.SUCCEEDED)
except Exception as e:
LOG.expection(e)
LOG.debug('The ActionPlanExecutor failed to execute the action'
' %s ', action)
LOG.error(_LE("Trigger a rollback"))
self.notify(action, action_plan.Status.FAILED)
self.deploy.rollback()
return False
return True

View File

@@ -1,76 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 oslo_log import log
from watcher.applier.execution.deploy_phase import DeployPhase
from watcher.applier.mapping.default import DefaultActionMapper
from watcher.applier.messaging.events import Events
from watcher.common.messaging.events.event import Event
from watcher.objects import Action
from watcher.objects.action_plan import Status
LOG = log.getLogger(__name__)
class ActionPlanExecutor(object):
def __init__(self, manager_applier, context):
self.manager_applier = manager_applier
self.context = context
self.deploy = DeployPhase(self)
self.mapper = DefaultActionMapper()
def get_primitive(self, action):
return self.mapper.build_primitive_from_action(action)
def notify(self, action, state):
db_action = Action.get_by_uuid(self.context, action.uuid)
db_action.state = state
db_action.save()
event = Event()
event.type = Events.LAUNCH_ACTION
event.data = {}
payload = {'action_uuid': action.uuid,
'action_state': state}
self.manager_applier.topic_status.publish_event(event.type.name,
payload)
def execute(self, actions):
for action in actions:
try:
self.notify(action, Status.ONGOING)
primitive = self.get_primitive(action)
result = self.deploy.execute_primitive(primitive)
if result is False:
self.notify(action, Status.FAILED)
self.deploy.rollback()
return False
else:
self.deploy.populate(primitive)
self.notify(action, Status.SUCCEEDED)
except Exception as e:
LOG.debug(
'The applier module failed to execute the action{0} with '
'the exception {1} '.format(
action,
unicode(e)))
LOG.error("Trigger a rollback")
self.notify(action, Status.FAILED)
self.deploy.rollback()
return False
return True

View File

@@ -1,33 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class BaseActionMapper(object):
@abc.abstractmethod
def build_primitive_from_action(self, action):
"""Transform an action to a primitive
:type action: watcher.decision_engine.action.BaseAction
:return: the associated Primitive
"""
raise NotImplementedError()

View File

@@ -1,47 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 watcher.applier.mapping.base import BaseActionMapper
from watcher.applier.primitives.change_nova_service_state import \
ChangeNovaServiceState
from watcher.applier.primitives.migration import Migrate
from watcher.applier.primitives.nop import Nop
from watcher.applier.primitives.power_state import ChangePowerState
from watcher.common.exception import ActionNotFound
from watcher.decision_engine.planner.default import Primitives
class DefaultActionMapper(BaseActionMapper):
def build_primitive_from_action(self, action):
if action.action_type == Primitives.COLD_MIGRATE.value:
return Migrate(action.applies_to, Primitives.COLD_MIGRATE,
action.src,
action.dst)
elif action.action_type == Primitives.LIVE_MIGRATE.value:
return Migrate(action.applies_to, Primitives.COLD_MIGRATE,
action.src,
action.dst)
elif action.action_type == Primitives.HYPERVISOR_STATE.value:
return ChangeNovaServiceState(action.applies_to, action.parameter)
elif action.action_type == Primitives.POWER_STATE.value:
return ChangePowerState()
elif action.action_type == Primitives.NOP.value:
return Nop()
else:
raise ActionNotFound()

View File

@@ -17,9 +17,9 @@
# limitations under the License.
#
from enum import Enum
import enum
class Events(Enum):
class Events(enum.Enum):
LAUNCH_ACTION_PLAN = "launch_action_plan"
LAUNCH_ACTION = "launch_action"

View File

@@ -33,17 +33,38 @@ the appropriate commands to Nova for this type of
import abc
import six
from watcher.applier.promise import Promise
from watcher.applier import promise
@six.add_metaclass(abc.ABCMeta)
class BasePrimitive(object):
@Promise
def __init__(self):
self._input_parameters = None
self._applies_to = None
@property
def input_parameters(self):
return self._input_parameters
@input_parameters.setter
def input_parameters(self, p):
self._input_parameters = p
@property
def applies_to(self):
return self._applies_to
@applies_to.setter
def applies_to(self, a):
self._applies_to = a
@promise.Promise
@abc.abstractmethod
def execute(self):
raise NotImplementedError()
@Promise
@promise.Promise
@abc.abstractmethod
def undo(self):
raise NotImplementedError()

View File

@@ -18,30 +18,21 @@
#
from oslo_config import cfg
from watcher._i18n import _
from watcher.applier.primitives.base import BasePrimitive
from watcher.applier.promise import Promise
from watcher.common.exception import IllegalArgumentException
from watcher.common.keystone import KeystoneClient
from watcher.common.nova import NovaClient
from watcher.decision_engine.model.hypervisor_state import HypervisorState
CONF = cfg.CONF
from watcher.applier.primitives import base
from watcher.applier import promise
from watcher.common import exception
from watcher.common import keystone as kclient
from watcher.common import nova as nclient
from watcher.decision_engine.model import hypervisor_state as hstate
class ChangeNovaServiceState(BasePrimitive):
def __init__(self, host, state):
"""This class allows us to change the state of nova-compute service.
:param host: the uuid of the host
:param state: (enabled/disabled)
"""
super(BasePrimitive, self).__init__()
self._host = host
self._state = state
class ChangeNovaServiceState(base.BasePrimitive):
def __init__(self):
"""This class allows us to change the state of nova-compute service."""
super(ChangeNovaServiceState, self).__init__()
self._host = self.applies_to
self._state = self.input_parameters.get('state')
@property
def host(self):
@@ -51,32 +42,32 @@ class ChangeNovaServiceState(BasePrimitive):
def state(self):
return self._state
@Promise
@promise.Promise
def execute(self):
target_state = None
if self.state == HypervisorState.OFFLINE.value:
if self.state == hstate.HypervisorState.OFFLINE.value:
target_state = False
elif self.status == HypervisorState.ONLINE.value:
elif self.status == hstate.HypervisorState.ONLINE.value:
target_state = True
return self.nova_manage_service(target_state)
@Promise
@promise.Promise
def undo(self):
target_state = None
if self.state == HypervisorState.OFFLINE.value:
if self.state == hstate.HypervisorState.OFFLINE.value:
target_state = True
elif self.state == HypervisorState.ONLINE.value:
elif self.state == hstate.HypervisorState.ONLINE.value:
target_state = False
return self.nova_manage_service(target_state)
def nova_manage_service(self, state):
if state is None:
raise IllegalArgumentException(
raise exception.IllegalArgumentException(
_("The target state is not defined"))
keystone = KeystoneClient()
wrapper = NovaClient(keystone.get_credentials(),
session=keystone.get_session())
keystone = kclient.KeystoneClient()
wrapper = nclient.NovaClient(keystone.get_credentials(),
session=keystone.get_session())
if state is True:
return wrapper.enable_service_nova_compute(self.host)
else:

View File

@@ -0,0 +1,36 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2016 b<>com
#
# 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 __future__ import unicode_literals
from oslo_log import log
from watcher.applier.primitives.loading import default
LOG = log.getLogger(__name__)
class ActionFactory(object):
def __init__(self):
self.action_loader = default.DefaultActionLoader()
def make_action(self, object_action):
LOG.debug("Creating instance of %s", object_action.action_type)
loaded_action = self.action_loader.load(name=object_action.action_type)
loaded_action.input_parameters = object_action.input_parameters
loaded_action.applies_to = object_action.applies_to
return loaded_action

View File

@@ -17,50 +17,38 @@
# limitations under the License.
#
from oslo_config import cfg
from watcher.applier.primitives.base import BasePrimitive
from watcher.applier.promise import Promise
from watcher.common.keystone import KeystoneClient
from watcher.common.nova import NovaClient
from watcher.decision_engine.planner.default import Primitives
CONF = cfg.CONF
from watcher.applier.primitives import base
from watcher.applier import promise
from watcher.common import exception
from watcher.common import keystone as kclient
from watcher.common import nova as nclient
class Migrate(BasePrimitive):
def __init__(self, vm_uuid=None,
migration_type=None,
source_hypervisor=None,
destination_hypervisor=None):
super(BasePrimitive, self).__init__()
self.instance_uuid = vm_uuid
self.migration_type = migration_type
self.source_hypervisor = source_hypervisor
self.destination_hypervisor = destination_hypervisor
class Migrate(base.BasePrimitive):
def __init__(self):
super(Migrate, self).__init__()
self.instance_uuid = self.applies_to
self.migration_type = self.input_parameters.get('migration_type')
def migrate(self, destination):
keystone = KeystoneClient()
wrapper = NovaClient(keystone.get_credentials(),
session=keystone.get_session())
keystone = kclient.KeystoneClient()
wrapper = nclient.NovaClient(keystone.get_credentials(),
session=keystone.get_session())
instance = wrapper.find_instance(self.instance_uuid)
if instance:
# todo(jed) remove Primitves
if self.migration_type is Primitives.COLD_MIGRATE:
if self.migration_type is 'live':
return wrapper.live_migrate_instance(
instance_id=self.instance_uuid,
dest_hostname=destination,
block_migration=True)
elif self.migration_type is Primitives.LIVE_MIGRATE:
return wrapper.live_migrate_instance(
instance_id=self.instance_uuid,
dest_hostname=destination,
block_migration=False)
instance_id=self.instance_uuid, dest_hostname=destination)
else:
raise exception.InvalidParameterValue(err=self.migration_type)
else:
raise exception.InstanceNotFound(name=self.instance_uuid)
@Promise
@promise.Promise
def execute(self):
return self.migrate(self.destination_hypervisor)
return self.migrate(self.input_parameters.get('dst_hypervisor_uuid'))
@Promise
@promise.Promise
def undo(self):
return self.migrate(self.source_hypervisor)
return self.migrate(self.input_parameters.get('src_hypervisor_uuid'))

View File

@@ -20,21 +20,22 @@
from oslo_log import log
from watcher.applier.primitives.base import BasePrimitive
from watcher.applier.promise import Promise
from watcher.applier.primitives import base
from watcher.applier import promise
LOG = log.getLogger(__name__)
class Nop(BasePrimitive):
class Nop(base.BasePrimitive):
@Promise
@promise.Promise
def execute(self):
LOG.debug("executing NOP command")
LOG.debug("executing action NOP message:%s ",
self.input_parameters.get('message'))
return True
@Promise
@promise.Promise
def undo(self):
LOG.debug("undo NOP command")
LOG.debug("undo action NOP")
return True

View File

@@ -1,32 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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 watcher.applier.primitives.base import BasePrimitive
from watcher.applier.promise import Promise
class ChangePowerState(BasePrimitive):
@Promise
def execute(self):
raise NotImplementedError # pragma:no cover
@Promise
def undo(self):
raise NotImplementedError # pragma:no cover

View File

@@ -291,12 +291,12 @@ class ClusterStateNotDefined(WatcherException):
# Model
class VMNotFound(WatcherException):
message = _("The VM could not be found")
class InstanceNotFound(WatcherException):
message = _("The instance '%(name)s' is not found")
class HypervisorNotFound(WatcherException):
message = _("The hypervisor could not be found")
message = _("The hypervisor is not found")
class LoadingError(WatcherException):

View File

@@ -66,7 +66,7 @@ class ModelRoot(object):
def get_vm_from_id(self, uuid):
if str(uuid) not in self._vms.keys():
raise exception.VMNotFound(uuid)
raise exception.InstanceNotFound(name=uuid)
return self._vms[str(uuid)]
def get_all_vms(self):

View File

@@ -17,9 +17,6 @@
# limitations under the License.
#
import json
import enum
from oslo_log import log
from watcher._i18n import _LW
@@ -30,14 +27,6 @@ from watcher import objects
LOG = log.getLogger(__name__)
class Primitives(enum.Enum):
LIVE_MIGRATE = 'MIGRATE'
COLD_MIGRATE = 'MIGRATE'
POWER_STATE = 'POWERSTATE'
HYPERVISOR_STATE = 'HYPERVISOR_STATE'
NOP = 'NOP'
class DefaultPlanner(base.BasePlanner):
priorities = {
'nop': 0,
@@ -56,7 +45,7 @@ class DefaultPlanner(base.BasePlanner):
'action_plan_id': int(action_plan_id),
'action_type': action_type,
'applies_to': applies_to,
'input_parameters': json.dumps(input_parameters),
'input_parameters': input_parameters,
'state': objects.action.Status.PENDING,
'alarm': None,
'next': None,

View File

@@ -49,5 +49,5 @@ class PlannerManager(object):
def load(self):
selected_planner = CONF.watcher_planner.planner
LOG.debug("Loading {0}".format(selected_planner))
LOG.debug("Loading %s", selected_planner)
return self.loader.load(name=selected_planner)

View File

@@ -149,8 +149,8 @@ class OutletTempControl(BaseStrategy):
LOG.info(_LE("VM not active, skipped: %s"),
vm.uuid)
continue
return (mig_src_hypervisor, vm)
except wexc.VMNotFound as e:
return mig_src_hypervisor, vm
except wexc.InstanceNotFound as e:
LOG.info("VM not found Error: %s" % e.message)
pass

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: python-watcher 0.21.1.dev32\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-01-14 14:51+0100\n"
"POT-Creation-Date: 2016-01-15 10:25+0100\n"
"PO-Revision-Date: 2015-12-11 15:42+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fr\n"
@@ -71,7 +71,11 @@ msgstr ""
msgid "Error parsing HTTP response: %s"
msgstr ""
#: watcher/applier/primitives/change_nova_service_state.py:75
#: watcher/applier/execution/default.py:52
msgid "Trigger a rollback"
msgstr ""
#: watcher/applier/primitives/change_nova_service_state.py:66
msgid "The target state is not defined"
msgstr ""
@@ -261,11 +265,12 @@ msgid "the cluster state is not defined"
msgstr ""
#: watcher/common/exception.py:295
msgid "The VM could not be found"
msgstr ""
#, python-format
msgid "The instance '%(name)s' is not found"
msgstr "L'instance '%(name)s' n'a pas été trouvée"
#: watcher/common/exception.py:299
msgid "The hypervisor could not be found"
msgid "The hypervisor is not found"
msgstr ""
#: watcher/common/exception.py:303
@@ -348,7 +353,7 @@ msgstr ""
msgid "'obj' argument type is not valid"
msgstr ""
#: watcher/decision_engine/planner/default.py:86
#: watcher/decision_engine/planner/default.py:75
msgid "The action plan is empty"
msgstr ""
@@ -536,3 +541,9 @@ msgstr ""
#~ msgid "The Meta-Action could not be found"
#~ msgstr ""
#~ msgid "The VM could not be found"
#~ msgstr ""
#~ msgid "The hypervisor could not be found"
#~ msgstr ""

View File

@@ -7,9 +7,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: python-watcher 0.22.1.dev16\n"
"Project-Id-Version: python-watcher 0.22.1.dev19\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-01-14 14:51+0100\n"
"POT-Creation-Date: 2016-01-15 10:25+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -70,7 +70,11 @@ msgstr ""
msgid "Error parsing HTTP response: %s"
msgstr ""
#: watcher/applier/primitives/change_nova_service_state.py:75
#: watcher/applier/execution/default.py:52
msgid "Trigger a rollback"
msgstr ""
#: watcher/applier/primitives/change_nova_service_state.py:66
msgid "The target state is not defined"
msgstr ""
@@ -259,11 +263,12 @@ msgid "the cluster state is not defined"
msgstr ""
#: watcher/common/exception.py:295
msgid "The VM could not be found"
#, python-format
msgid "The instance '%(name)s' is not found"
msgstr ""
#: watcher/common/exception.py:299
msgid "The hypervisor could not be found"
msgid "The hypervisor is not found"
msgstr ""
#: watcher/common/exception.py:303
@@ -346,7 +351,7 @@ msgstr ""
msgid "'obj' argument type is not valid"
msgstr ""
#: watcher/decision_engine/planner/default.py:86
#: watcher/decision_engine/planner/default.py:75
msgid "The action plan is empty"
msgstr ""

View File

@@ -18,21 +18,17 @@
#
import mock
from watcher.applier.execution.executor import ActionPlanExecutor
from watcher import objects
from watcher.applier.execution import default
from watcher.common import utils
from watcher.decision_engine.planner.default import Primitives
from watcher.objects.action import Action
from watcher.objects.action import Status
from watcher.tests.db.base import DbTestCase
from watcher import objects
from watcher.tests.db import base
class TestCommandExecutor(DbTestCase):
class TestDefaultActionPlanExecutor(base.DbTestCase):
def setUp(self):
super(TestCommandExecutor, self).setUp()
self.applier = mock.MagicMock()
self.executor = ActionPlanExecutor(self.applier, self.context)
super(TestDefaultActionPlanExecutor, self).setUp()
self.executor = default.DefaultActionPlanExecutor(mock.MagicMock(),
self.context)
def test_execute(self):
actions = mock.MagicMock()
@@ -44,19 +40,17 @@ class TestCommandExecutor(DbTestCase):
action = {
'uuid': utils.generate_uuid(),
'action_plan_id': 0,
'action_type': Primitives.NOP.value,
'action_type': "nop",
'applies_to': '',
'src': '',
'dst': '',
'parameter': '',
'description': '',
'state': Status.PENDING,
'input_parameters': {'state': 'OFFLINE'},
'state': objects.action.Status.PENDING,
'alarm': None,
'next': None,
}
new_action = objects.Action(self.context, **action)
new_action.create(self.context)
new_action.save()
actions.append(Action.get_by_uuid(self.context, action['uuid']))
actions.append(objects.Action.get_by_uuid(self.context,
action['uuid']))
result = self.executor.execute(actions)
self.assertEqual(result, True)

View File

@@ -1,59 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
import mock
from watcher.applier.mapping.default import DefaultActionMapper
from watcher.decision_engine.planner.default import Primitives
from watcher.tests import base
class TestDefaultActionMapper(base.TestCase):
def setUp(self):
super(TestDefaultActionMapper, self).setUp()
self.mapper = DefaultActionMapper()
def test_build_command_cold(self):
action = mock.MagicMock()
action.action_type = Primitives.COLD_MIGRATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_live(self):
action = mock.MagicMock()
action.action_type = Primitives.LIVE_MIGRATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_h_s(self):
action = mock.MagicMock()
action.action_type = Primitives.HYPERVISOR_STATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_p_s(self):
action = mock.MagicMock()
action.action_type = Primitives.POWER_STATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_exception_attribute(self):
action = mock.MagicMock
self.assertRaises(AttributeError,
self.mapper.build_primitive_from_action,
action)

View File

@@ -1,59 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
#
# 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.
#
import mock
from watcher.applier.mapping.default import DefaultActionMapper
from watcher.decision_engine.planner.default import Primitives
from watcher.tests import base
class TestDefaultActionMapper(base.TestCase):
def setUp(self):
super(TestDefaultActionMapper, self).setUp()
self.mapper = DefaultActionMapper()
def test_build_command_cold(self):
action = mock.MagicMock()
action.action_type = Primitives.COLD_MIGRATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_live(self):
action = mock.MagicMock()
action.action_type = Primitives.LIVE_MIGRATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_h_s(self):
action = mock.MagicMock()
action.action_type = Primitives.HYPERVISOR_STATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_p_s(self):
action = mock.MagicMock()
action.action_type = Primitives.POWER_STATE.value
cmd = self.mapper.build_primitive_from_action(action)
self.assertIsNotNone(cmd)
def test_build_command_exception_attribute(self):
action = mock.MagicMock
self.assertRaises(AttributeError,
self.mapper.build_primitive_from_action,
action)

View File

@@ -129,7 +129,7 @@ class TestModel(base.BaseTestCase):
def test_vm_from_id_raise(self):
fake_cluster = FakerModelCollector()
model = fake_cluster.generate_scenario_1()
self.assertRaises(exception.VMNotFound,
self.assertRaises(exception.InstanceNotFound,
model.get_vm_from_id, "valeur_qcq")
def test_assert_vm_raise(self):

View File

@@ -20,7 +20,9 @@ from watcher.tests import base
class TestDefaultPlannerLoader(base.TestCase):
loader = default.DefaultPlannerLoader()
def setUp(self):
super(TestDefaultPlannerLoader, self).setUp()
self.loader = default.DefaultPlannerLoader()
def test_endpoints(self):
for endpoint in self.loader.list_available():

View File

@@ -16,13 +16,13 @@
from oslo_config import cfg
from watcher.decision_engine.planner.default import DefaultPlanner
from watcher.decision_engine.planner.manager import PlannerManager
from watcher.decision_engine.planner import default
from watcher.decision_engine.planner import manager as planner
from watcher.tests import base
class TestPlannerManager(base.TestCase):
def test_load(self):
cfg.CONF.set_override('planner', "default", group='watcher_planner')
manager = PlannerManager()
self.assertIsInstance(manager.load(), DefaultPlanner)
manager = planner.PlannerManager()
self.assertIsInstance(manager.load(), default.DefaultPlanner)