diff --git a/setup.cfg b/setup.cfg index 5c05951ad..aef6ff09f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 diff --git a/watcher/applier/default.py b/watcher/applier/default.py index 0cbd7cb7b..4d070ca6d 100644 --- a/watcher/applier/default.py +++ b/watcher/applier/default.py @@ -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) diff --git a/watcher/applier/execution/base.py b/watcher/applier/execution/base.py new file mode 100644 index 000000000..bb147bbe7 --- /dev/null +++ b/watcher/applier/execution/base.py @@ -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() diff --git a/watcher/applier/execution/default.py b/watcher/applier/execution/default.py new file mode 100644 index 000000000..43b18dcd7 --- /dev/null +++ b/watcher/applier/execution/default.py @@ -0,0 +1,57 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2015 b<>com +# +# Authors: Jean-Emile DARTOIS +# +# 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 diff --git a/watcher/applier/execution/executor.py b/watcher/applier/execution/executor.py deleted file mode 100644 index 16b977357..000000000 --- a/watcher/applier/execution/executor.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- encoding: utf-8 -*- -# Copyright (c) 2015 b<>com -# -# Authors: Jean-Emile DARTOIS -# -# 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 diff --git a/watcher/applier/mapping/__init__.py b/watcher/applier/mapping/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher/applier/mapping/base.py b/watcher/applier/mapping/base.py deleted file mode 100644 index 99f92e70b..000000000 --- a/watcher/applier/mapping/base.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- encoding: utf-8 -*- -# Copyright (c) 2015 b<>com -# -# Authors: Jean-Emile DARTOIS -# -# 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() diff --git a/watcher/applier/mapping/default.py b/watcher/applier/mapping/default.py deleted file mode 100644 index 65bd7b259..000000000 --- a/watcher/applier/mapping/default.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- encoding: utf-8 -*- -# Copyright (c) 2015 b<>com -# -# Authors: Jean-Emile DARTOIS -# -# 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() diff --git a/watcher/applier/messaging/events.py b/watcher/applier/messaging/events.py index 2e186bf21..eb6de5c74 100644 --- a/watcher/applier/messaging/events.py +++ b/watcher/applier/messaging/events.py @@ -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" diff --git a/watcher/applier/primitives/base.py b/watcher/applier/primitives/base.py index 64eadcc7a..16507ba11 100644 --- a/watcher/applier/primitives/base.py +++ b/watcher/applier/primitives/base.py @@ -18,17 +18,38 @@ # 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() diff --git a/watcher/applier/primitives/change_nova_service_state.py b/watcher/applier/primitives/change_nova_service_state.py index 6773a927c..c82a828f6 100644 --- a/watcher/applier/primitives/change_nova_service_state.py +++ b/watcher/applier/primitives/change_nova_service_state.py @@ -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: diff --git a/watcher/applier/primitives/factory.py b/watcher/applier/primitives/factory.py new file mode 100644 index 000000000..40c67d163 --- /dev/null +++ b/watcher/applier/primitives/factory.py @@ -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 diff --git a/watcher/applier/primitives/migration.py b/watcher/applier/primitives/migration.py index 34e54b8a6..17f07f974 100644 --- a/watcher/applier/primitives/migration.py +++ b/watcher/applier/primitives/migration.py @@ -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')) diff --git a/watcher/applier/primitives/nop.py b/watcher/applier/primitives/nop.py index 000dd2d3a..d0e3ee4ff 100644 --- a/watcher/applier/primitives/nop.py +++ b/watcher/applier/primitives/nop.py @@ -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 diff --git a/watcher/applier/primitives/power_state.py b/watcher/applier/primitives/power_state.py deleted file mode 100644 index 178a7c941..000000000 --- a/watcher/applier/primitives/power_state.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- encoding: utf-8 -*- -# Copyright (c) 2015 b<>com -# -# Authors: Jean-Emile DARTOIS -# -# 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 diff --git a/watcher/common/exception.py b/watcher/common/exception.py index 651e595a2..2eaa82413 100644 --- a/watcher/common/exception.py +++ b/watcher/common/exception.py @@ -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): diff --git a/watcher/decision_engine/model/model_root.py b/watcher/decision_engine/model/model_root.py index f9aaa49e8..7ae764328 100644 --- a/watcher/decision_engine/model/model_root.py +++ b/watcher/decision_engine/model/model_root.py @@ -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): diff --git a/watcher/decision_engine/planner/default.py b/watcher/decision_engine/planner/default.py index d372591d5..1d37ca426 100644 --- a/watcher/decision_engine/planner/default.py +++ b/watcher/decision_engine/planner/default.py @@ -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, diff --git a/watcher/decision_engine/planner/manager.py b/watcher/decision_engine/planner/manager.py index b266718e1..c7946f35e 100644 --- a/watcher/decision_engine/planner/manager.py +++ b/watcher/decision_engine/planner/manager.py @@ -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) diff --git a/watcher/decision_engine/strategy/strategies/outlet_temp_control.py b/watcher/decision_engine/strategy/strategies/outlet_temp_control.py index c8d62825d..00f9bf483 100644 --- a/watcher/decision_engine/strategy/strategies/outlet_temp_control.py +++ b/watcher/decision_engine/strategy/strategies/outlet_temp_control.py @@ -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 diff --git a/watcher/locale/fr/LC_MESSAGES/watcher.po b/watcher/locale/fr/LC_MESSAGES/watcher.po index 0fd8df22c..54ca4e7c6 100644 --- a/watcher/locale/fr/LC_MESSAGES/watcher.po +++ b/watcher/locale/fr/LC_MESSAGES/watcher.po @@ -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 \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 "" + diff --git a/watcher/locale/watcher.pot b/watcher/locale/watcher.pot index 719dbacf0..b51c39576 100644 --- a/watcher/locale/watcher.pot +++ b/watcher/locale/watcher.pot @@ -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 \n" "Language-Team: LANGUAGE \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 "" diff --git a/watcher/tests/applier/execution/test_action_plan_executor.py b/watcher/tests/applier/execution/test_default_action_plan_executor.py similarity index 65% rename from watcher/tests/applier/execution/test_action_plan_executor.py rename to watcher/tests/applier/execution/test_default_action_plan_executor.py index b503dd7ce..99588d70c 100644 --- a/watcher/tests/applier/execution/test_action_plan_executor.py +++ b/watcher/tests/applier/execution/test_default_action_plan_executor.py @@ -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) diff --git a/watcher/tests/applier/mapping/__init__.py b/watcher/tests/applier/mapping/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/watcher/tests/applier/mapping/test_action_mapper.py b/watcher/tests/applier/mapping/test_action_mapper.py deleted file mode 100644 index 4f97c2947..000000000 --- a/watcher/tests/applier/mapping/test_action_mapper.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- encoding: utf-8 -*- -# Copyright (c) 2015 b<>com -# -# Authors: Jean-Emile DARTOIS -# -# 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) diff --git a/watcher/tests/applier/mapping/test_default_action_mapper.py b/watcher/tests/applier/mapping/test_default_action_mapper.py deleted file mode 100644 index 4f97c2947..000000000 --- a/watcher/tests/applier/mapping/test_default_action_mapper.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- encoding: utf-8 -*- -# Copyright (c) 2015 b<>com -# -# Authors: Jean-Emile DARTOIS -# -# 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) diff --git a/watcher/tests/decision_engine/model/test_model.py b/watcher/tests/decision_engine/model/test_model.py index fb6e43bf7..8bbfce7af 100644 --- a/watcher/tests/decision_engine/model/test_model.py +++ b/watcher/tests/decision_engine/model/test_model.py @@ -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): diff --git a/watcher/tests/decision_engine/planner/test_default_planner_loader.py b/watcher/tests/decision_engine/planner/test_default_planner_loader.py index 955fbdcbf..1b9e9bd3c 100644 --- a/watcher/tests/decision_engine/planner/test_default_planner_loader.py +++ b/watcher/tests/decision_engine/planner/test_default_planner_loader.py @@ -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(): diff --git a/watcher/tests/decision_engine/planner/test_planner_manager.py b/watcher/tests/decision_engine/planner/test_planner_manager.py index 516502afb..845627237 100644 --- a/watcher/tests/decision_engine/planner/test_planner_manager.py +++ b/watcher/tests/decision_engine/planner/test_planner_manager.py @@ -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)