From 8db1b3ea411b60914f4272dbbb37195b793b6031 Mon Sep 17 00:00:00 2001 From: rabi Date: Fri, 27 Oct 2017 12:57:24 +0530 Subject: [PATCH] Remove stack watch service This removes the rpc api and related code. Change-Id: Ib89bcc3ff6a542f49467e2ad6c7e2a716a0dc2b4 Partial-Bug: #1743707 --- heat/api/aws/exception.py | 1 - heat/cmd/engine.py | 4 - heat/common/config.py | 3 + heat/common/exception.py | 5 - heat/engine/resource.py | 3 - heat/engine/resources/openstack/aodh/alarm.py | 25 - .../resources/openstack/heat/cloud_watch.py | 182 +--- heat/engine/service.py | 143 --- heat/engine/service_stack_watch.py | 109 -- heat/engine/watchrule.py | 396 ------- heat/objects/watch_data.py | 60 -- heat/objects/watch_rule.py | 87 -- heat/rpc/api.py | 60 -- heat/rpc/client.py | 54 - heat/tests/engine/service/test_stack_watch.py | 270 ----- heat/tests/openstack/aodh/test_alarm.py | 60 +- heat/tests/openstack/heat/test_cloudwatch.py | 120 --- heat/tests/openstack/heat/test_cw_alarm.py | 161 --- heat/tests/test_engine_service_stack_watch.py | 118 --- heat/tests/test_metadata_refresh.py | 1 - heat/tests/test_rpc_client.py | 17 - heat/tests/test_signal.py | 7 +- heat/tests/test_watch.py | 978 ------------------ 23 files changed, 12 insertions(+), 2852 deletions(-) delete mode 100644 heat/engine/service_stack_watch.py delete mode 100644 heat/engine/watchrule.py delete mode 100644 heat/objects/watch_data.py delete mode 100644 heat/objects/watch_rule.py delete mode 100644 heat/tests/engine/service/test_stack_watch.py delete mode 100644 heat/tests/openstack/heat/test_cloudwatch.py delete mode 100644 heat/tests/openstack/heat/test_cw_alarm.py delete mode 100644 heat/tests/test_engine_service_stack_watch.py delete mode 100644 heat/tests/test_watch.py diff --git a/heat/api/aws/exception.py b/heat/api/aws/exception.py index b5f592d35d..d92c244783 100644 --- a/heat/api/aws/exception.py +++ b/heat/api/aws/exception.py @@ -298,7 +298,6 @@ def map_remote_error(ex): 'ResourceActionNotSupported', 'ResourceNotFound', 'ResourceNotAvailable', - 'WatchRuleNotFound', 'StackValidationFailed', 'InvalidSchemaError', 'InvalidTemplateReference', diff --git a/heat/cmd/engine.py b/heat/cmd/engine.py index 19e8f7d9bf..81860fcf9b 100644 --- a/heat/cmd/engine.py +++ b/heat/cmd/engine.py @@ -74,10 +74,6 @@ def launch_engine(setup_logging=True): launcher = service.launch(cfg.CONF, srv, workers=workers, restart_method='mutate') - if cfg.CONF.enable_cloud_watch_lite: - # We create the periodic tasks here, which mean they are created - # only in the parent process when num_engine_workers>1 is specified - srv.create_periodic_tasks() return launcher diff --git a/heat/common/config.py b/heat/common/config.py index 63ec0e6457..471a4cddcc 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -182,6 +182,9 @@ engine_opts = [ ' for stack locking.')), cfg.BoolOpt('enable_cloud_watch_lite', default=False, + deprecated_for_removal=True, + deprecated_reason='Heat CloudWatch Service has been removed.', + deprecated_since='10.0.0', help=_('Enable the legacy OS::Heat::CWLiteAlarm resource.')), cfg.BoolOpt('enable_stack_abandon', default=False, diff --git a/heat/common/exception.py b/heat/common/exception.py index f42c9e07f6..cf6acb5ce2 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -304,11 +304,6 @@ class ClientNotAvailable(HeatException): msg_fmt = _("The client (%(client_name)s) is not available.") -class WatchRuleNotFound(EntityNotFound): - """Keep this for AWS compatibility.""" - msg_fmt = _("The Watch Rule (%(watch_name)s) could not be found.") - - class ResourceFailure(HeatExceptionWithPath): def __init__(self, exception_or_error, resource, action=None): self.resource = resource diff --git a/heat/engine/resource.py b/heat/engine/resource.py index bf67a3fec3..833a19886c 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -2449,9 +2449,6 @@ class Resource(status.ResourceStatus): # this is from Ceilometer. auto = '%(previous)s to %(current)s (%(reason)s)' % details return 'alarm state changed from %s' % auto - elif 'state' in details: - # this is from watchrule - return 'alarm state changed to %(state)s' % details return 'Unknown' diff --git a/heat/engine/resources/openstack/aodh/alarm.py b/heat/engine/resources/openstack/aodh/alarm.py index ca6bb4e4c2..c3fbe10236 100644 --- a/heat/engine/resources/openstack/aodh/alarm.py +++ b/heat/engine/resources/openstack/aodh/alarm.py @@ -13,14 +13,12 @@ import six -from heat.common import exception from heat.common.i18n import _ from heat.engine import constraints from heat.engine import properties from heat.engine.resources import alarm_base from heat.engine.resources.openstack.heat import none_resource from heat.engine import support -from heat.engine import watchrule class AodhAlarm(alarm_base.BaseAlarm): @@ -178,17 +176,6 @@ class AodhAlarm(alarm_base.BaseAlarm): alarm = self.client().alarm.create(props) self.resource_id_set(alarm['alarm_id']) - # the watchrule below is for backwards compatibility. - # 1) so we don't create watch tasks unnecessarily - # 2) to support CW stats post, we will redirect the request - # to ceilometer. - wr = watchrule.WatchRule(context=self.context, - watch_name=self.physical_resource_name(), - rule=dict(self.properties), - stack_id=self.stack.id) - wr.state = wr.CEILOMETER_CONTROLLED - wr.store() - def handle_update(self, json_snippet, tmpl_diff, prop_diff): if prop_diff: new_props = json_snippet.properties(self.properties_schema, @@ -209,19 +196,7 @@ class AodhAlarm(alarm_base.BaseAlarm): return record_reality - def handle_delete(self): - try: - wr = watchrule.WatchRule.load( - self.context, watch_name=self.physical_resource_name()) - wr.destroy() - except exception.EntityNotFound: - pass - - return super(AodhAlarm, self).handle_delete() - def handle_check(self): - watch_name = self.physical_resource_name() - watchrule.WatchRule.load(self.context, watch_name=watch_name) self.client().alarm.get(self.resource_id) diff --git a/heat/engine/resources/openstack/heat/cloud_watch.py b/heat/engine/resources/openstack/heat/cloud_watch.py index 8a227a742a..26ed62f139 100644 --- a/heat/engine/resources/openstack/heat/cloud_watch.py +++ b/heat/engine/resources/openstack/heat/cloud_watch.py @@ -13,193 +13,23 @@ from oslo_config import cfg -from heat.common import exception from heat.common.i18n import _ -from heat.engine import constraints -from heat.engine import properties -from heat.engine import resource +from heat.engine.resources.openstack.heat import none_resource from heat.engine import support -from heat.engine import watchrule -class CloudWatchAlarm(resource.Resource): - PROPERTIES = ( - COMPARISON_OPERATOR, ALARM_DESCRIPTION, EVALUATION_PERIODS, - METRIC_NAME, NAMESPACE, PERIOD, STATISTIC, ALARM_ACTIONS, - OKACTIONS, DIMENSIONS, INSUFFICIENT_DATA_ACTIONS, THRESHOLD, - UNITS, - ) = ( - 'ComparisonOperator', 'AlarmDescription', 'EvaluationPeriods', - 'MetricName', 'Namespace', 'Period', 'Statistic', 'AlarmActions', - 'OKActions', 'Dimensions', 'InsufficientDataActions', 'Threshold', - 'Units', - ) - - properties_schema = { - COMPARISON_OPERATOR: properties.Schema( - properties.Schema.STRING, - _('Operator used to compare the specified Statistic with ' - 'Threshold.'), - constraints=[ - constraints.AllowedValues(['GreaterThanOrEqualToThreshold', - 'GreaterThanThreshold', - 'LessThanThreshold', - 'LessThanOrEqualToThreshold']), - ], - required=True, - update_allowed=True - ), - ALARM_DESCRIPTION: properties.Schema( - properties.Schema.STRING, - _('Description for the alarm.'), - update_allowed=True - ), - EVALUATION_PERIODS: properties.Schema( - properties.Schema.STRING, - _('Number of periods to evaluate over.'), - required=True, - update_allowed=True - ), - METRIC_NAME: properties.Schema( - properties.Schema.STRING, - _('Metric name watched by the alarm.'), - required=True - ), - NAMESPACE: properties.Schema( - properties.Schema.STRING, - _('Namespace for the metric.'), - required=True - ), - PERIOD: properties.Schema( - properties.Schema.STRING, - _('Period (seconds) to evaluate over.'), - required=True, - update_allowed=True - ), - STATISTIC: properties.Schema( - properties.Schema.STRING, - _('Metric statistic to evaluate.'), - constraints=[ - constraints.AllowedValues(['SampleCount', 'Average', 'Sum', - 'Minimum', 'Maximum']), - ], - required=True, - update_allowed=True - ), - ALARM_ACTIONS: properties.Schema( - properties.Schema.LIST, - _('A list of actions to execute when state transitions to alarm.'), - update_allowed=True - ), - OKACTIONS: properties.Schema( - properties.Schema.LIST, - _('A list of actions to execute when state transitions to ok.'), - update_allowed=True - ), - DIMENSIONS: properties.Schema( - properties.Schema.LIST, - _('A list of dimensions (arbitrary name/value pairs) associated ' - 'with the metric.') - ), - INSUFFICIENT_DATA_ACTIONS: properties.Schema( - properties.Schema.LIST, - _('A list of actions to execute when state transitions to ' - 'insufficient-data.'), - update_allowed=True - ), - THRESHOLD: properties.Schema( - properties.Schema.STRING, - _('Threshold to evaluate against.'), - required=True, - update_allowed=True - ), - UNITS: properties.Schema( - properties.Schema.STRING, - _('Unit for the metric.'), - constraints=[ - constraints.AllowedValues(['Seconds', 'Microseconds', - 'Milliseconds', 'Bytes', - 'Kilobytes', 'Megabytes', - 'Gigabytes', 'Terabytes', 'Bits', - 'Kilobits', 'Megabits', - 'Gigabits', 'Terabits', 'Percent', - 'Count', 'Bytes/Second', - 'Kilobytes/Second', - 'Megabytes/Second', - 'Gigabytes/Second', - 'Terabytes/Second', 'Bits/Second', - 'Kilobits/Second', - 'Megabits/Second', - 'Gigabits/Second', - 'Terabits/Second', 'Count/Second', - None]), - ], - update_allowed=True - ), - } - - strict_dependency = False - +class CloudWatchAlarm(none_resource.NoneResource): support_status = support.SupportStatus( status=support.HIDDEN, - message=_('OS::Heat::CWLiteAlarm is deprecated, ' - 'use OS::Aodh::Alarm instead.'), + message=_('OS::Heat::CWLiteAlarm resource has been removed ' + 'since version 10.0.0. Existing stacks can still ' + 'use it, where it would do nothing for update/delete.'), version='5.0.0', previous_status=support.SupportStatus( status=support.DEPRECATED, - version='2014.2' - ) + version='2014.2') ) - def handle_create(self): - wr = watchrule.WatchRule(context=self.context, - watch_name=self.physical_resource_name(), - rule=dict(self.properties), - stack_id=self.stack.id) - wr.store() - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - # If Properties has changed, update self.properties, so we - # get the new values during any subsequent adjustment - if prop_diff: - self.properties = json_snippet.properties(self.properties_schema, - self.context) - loader = watchrule.WatchRule.load - wr = loader(self.context, - watch_name=self.physical_resource_name()) - - wr.rule = dict(self.properties) - wr.store() - - def handle_delete(self): - try: - wr = watchrule.WatchRule.load( - self.context, watch_name=self.physical_resource_name()) - wr.destroy() - except exception.EntityNotFound: - pass - - def handle_suspend(self): - wr = watchrule.WatchRule.load(self.context, - watch_name=self.physical_resource_name()) - wr.state_set(wr.SUSPENDED) - - def handle_resume(self): - wr = watchrule.WatchRule.load(self.context, - watch_name=self.physical_resource_name()) - # Just set to NODATA, which will be re-evaluated next periodic task - wr.state_set(wr.NODATA) - - def handle_check(self): - watch_name = self.physical_resource_name() - watchrule.WatchRule.load(self.context, watch_name=watch_name) - - def get_reference_id(self): - return self.physical_resource_name_or_FnGetRefId() - - def physical_resource_name(self): - return '%s-%s' % (self.stack.name, self.name) - def resource_mapping(): cfg.CONF.import_opt('enable_cloud_watch_lite', 'heat.common.config') diff --git a/heat/engine/service.py b/heat/engine/service.py index 595a8b0661..bfb1ae1502 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -15,7 +15,6 @@ import collections import datetime import functools import itertools -import os import pydoc import socket @@ -52,22 +51,18 @@ from heat.engine import parameter_groups from heat.engine import properties from heat.engine import resources from heat.engine import service_software_config -from heat.engine import service_stack_watch from heat.engine import stack as parser from heat.engine import stack_lock from heat.engine import stk_defn from heat.engine import support from heat.engine import template as templatem from heat.engine import update -from heat.engine import watchrule from heat.engine import worker from heat.objects import event as event_object from heat.objects import resource as resource_objects from heat.objects import service as service_objects from heat.objects import snapshot as snapshot_object from heat.objects import stack as stack_object -from heat.objects import watch_data -from heat.objects import watch_rule from heat.rpc import api as rpc_api from heat.rpc import worker_api as rpc_worker_api @@ -322,7 +317,6 @@ class EngineService(service.ServiceBase): # The following are initialized here, but assigned in start() which # happens after the fork when spawning multiple worker processes - self.stack_watch = None self.listener = None self.worker_service = None self.engine_id = None @@ -341,35 +335,6 @@ class EngineService(service.ServiceBase): 'Please keep the same if you do not want to ' 'delegate subset roles when upgrading.') - def create_periodic_tasks(self): - LOG.debug("Starting periodic watch tasks pid=%s", os.getpid()) - # Note with multiple workers, the parent process hasn't called start() - # so we need to create a ThreadGroupManager here for the periodic tasks - if self.thread_group_mgr is None: - self.thread_group_mgr = ThreadGroupManager() - self.stack_watch = service_stack_watch.StackWatch( - self.thread_group_mgr) - - def create_watch_tasks(): - while True: - try: - # Create a periodic_watcher_task per-stack - admin_context = context.get_admin_context() - stacks = stack_object.Stack.get_all( - admin_context, - show_hidden=True) - for s in stacks: - self.stack_watch.start_watch_task(s.id, admin_context) - LOG.info("Watch tasks created") - return - except Exception as e: - LOG.error("Watch task creation attempt failed, %s", e) - eventlet.sleep(5) - - if self.manage_thread_grp is None: - self.manage_thread_grp = threadgroup.ThreadGroup() - self.manage_thread_grp.add_thread(create_watch_tasks) - def start(self): self.engine_id = service_utils.generate_engine_id() if self.thread_group_mgr is None: @@ -819,14 +784,6 @@ class EngineService(service.ServiceBase): elif stack.status != stack.FAILED: stack.create(msg_queue=msg_queue) - if (stack.action in (stack.CREATE, stack.ADOPT) - and stack.status == stack.COMPLETE): - if self.stack_watch: - # Schedule a periodic watcher task for this stack - self.stack_watch.start_watch_task(stack.id, cnxt) - else: - LOG.info("Stack create failed, status %s", stack.status) - convergence = cfg.CONF.convergence_engine stack = self._parse_template_and_validate_stack( @@ -2173,106 +2130,6 @@ class EngineService(service.ServiceBase): data = snapshot_object.Snapshot.get_all(cnxt, s.id) return [api.format_snapshot(snapshot) for snapshot in data] - @context.request_context - def create_watch_data(self, cnxt, watch_name, stats_data): - """Creates data for CloudWatch and WaitConditions. - - This could be used by CloudWatch and WaitConditions - and treat HA service events like any other CloudWatch. - """ - def get_matching_watches(): - if watch_name: - yield watchrule.WatchRule.load(cnxt, watch_name) - else: - for wr in watch_rule.WatchRule.get_all(cnxt): - if watchrule.rule_can_use_sample(wr, stats_data): - yield watchrule.WatchRule.load(cnxt, watch=wr) - - rule_run = False - for rule in get_matching_watches(): - rule.create_watch_data(stats_data) - rule_run = True - - if not rule_run: - if watch_name is None: - watch_name = 'Unknown' - raise exception.EntityNotFound(entity='Watch Rule', - name=watch_name) - - return stats_data - - @context.request_context - def show_watch(self, cnxt, watch_name): - """Return the attributes of one watch/alarm. - - :param cnxt: RPC context. - :param watch_name: Name of the watch you want to see, or None to see - all. - """ - if watch_name: - wrn = [watch_name] - else: - try: - wrn = [w.name for w in watch_rule.WatchRule.get_all(cnxt)] - except Exception as ex: - LOG.warning('show_watch (all) db error %s', ex) - return - - wrs = [watchrule.WatchRule.load(cnxt, w) for w in wrn] - result = [api.format_watch(w) for w in wrs] - return result - - @context.request_context - def show_watch_metric(self, cnxt, metric_namespace=None, metric_name=None): - """Return the datapoints for a metric. - - :param cnxt: RPC context. - :param metric_namespace: Name of the namespace you want to see, or None - to see all. - :param metric_name: Name of the metric you want to see, or None to see - all. - """ - - # DB API and schema does not yet allow us to easily query by - # namespace/metric, but we will want this at some point - # for now, the API can query all metric data and filter locally - if metric_namespace is not None or metric_name is not None: - LOG.error("Filtering by namespace/metric not yet supported") - return - - try: - wds = watch_data.WatchData.get_all(cnxt) - rule_names = { - r.id: r.name for r in watch_rule.WatchRule.get_all(cnxt) - } - except Exception as ex: - LOG.warning('show_metric (all) db error %s', ex) - return - - result = [api.format_watch_data(w, rule_names) for w in wds] - return result - - @context.request_context - def set_watch_state(self, cnxt, watch_name, state): - """Temporarily set the state of a given watch. - - :param cnxt: RPC context. - :param watch_name: Name of the watch. - :param state: State (must be one defined in WatchRule class. - """ - wr = watchrule.WatchRule.load(cnxt, watch_name) - if wr.state == rpc_api.WATCH_STATE_CEILOMETER_CONTROLLED: - return - actions = wr.set_watch_state(state) - for action in actions: - self.thread_group_mgr.start(wr.stack_id, action) - - # Return the watch with the state overridden to indicate success - # We do not update the timestamps as we are not modifying the DB - result = api.format_watch(wr) - result[rpc_api.WATCH_STATE_VALUE] = state - return result - @context.request_context def show_software_config(self, cnxt, config_id): return self.software_config.show_software_config(cnxt, config_id) diff --git a/heat/engine/service_stack_watch.py b/heat/engine/service_stack_watch.py deleted file mode 100644 index 9e60f2e691..0000000000 --- a/heat/engine/service_stack_watch.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# 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 as logging -from oslo_utils import timeutils - -from heat.common import context -from heat.engine import stack -from heat.engine import stk_defn -from heat.engine import watchrule -from heat.objects import stack as stack_object -from heat.objects import watch_rule as watch_rule_object -from heat.rpc import api as rpc_api - -LOG = logging.getLogger(__name__) - - -class StackWatch(object): - def __init__(self, thread_group_mgr): - self.thread_group_mgr = thread_group_mgr - - def start_watch_task(self, stack_id, cnxt): - - def stack_has_a_watchrule(sid): - wrs = watch_rule_object.WatchRule.get_all_by_stack(cnxt, sid) - - now = timeutils.utcnow() - start_watch_thread = False - for wr in wrs: - # reset the last_evaluated so we don't fire off alarms when - # the engine has not been running. - watch_rule_object.WatchRule.update_by_id( - cnxt, wr.id, - {'last_evaluated': now}) - - if wr.state != rpc_api.WATCH_STATE_CEILOMETER_CONTROLLED: - start_watch_thread = True - - children = stack_object.Stack.get_all_by_owner_id(cnxt, sid) - for child in children: - if stack_has_a_watchrule(child.id): - start_watch_thread = True - - return start_watch_thread - - if stack_has_a_watchrule(stack_id): - self.thread_group_mgr.add_timer( - stack_id, - self.periodic_watcher_task, - sid=stack_id) - - def check_stack_watches(self, sid): - # Use admin_context for stack_get to defeat tenant - # scoping otherwise we fail to retrieve the stack - LOG.debug("Periodic watcher task for stack %s", sid) - admin_context = context.get_admin_context() - db_stack = stack_object.Stack.get_by_id(admin_context, - sid) - if not db_stack: - LOG.error("Unable to retrieve stack %s for periodic task", sid) - return - stk = stack.Stack.load(admin_context, stack=db_stack, - use_stored_context=True) - - # recurse into any nested stacks. - children = stack_object.Stack.get_all_by_owner_id(admin_context, sid) - for child in children: - self.check_stack_watches(child.id) - - # Get all watchrules for this stack and evaluate them - try: - wrs = watch_rule_object.WatchRule.get_all_by_stack(admin_context, - sid) - except Exception as ex: - LOG.warning('periodic_task db error watch rule removed? %s', ex) - return - - def run_alarm_action(stk, actions, details): - for action in actions: - action(details=details) - for res in stk._explicit_dependencies(): - res.metadata_update() - stk_defn.update_resource_data(stk.defn, res.name, - res.node_data()) - - for wr in wrs: - rule = watchrule.WatchRule.load(stk.context, watch=wr) - actions = rule.evaluate() - if actions: - self.thread_group_mgr.start(sid, run_alarm_action, stk, - actions, rule.get_details()) - - def periodic_watcher_task(self, sid): - """Evaluate all watch-rules defined for stack ID. - - Periodic task, created for each stack, triggers watch-rule evaluation - for all rules defined for the stack sid = stack ID. - """ - self.check_stack_watches(sid) diff --git a/heat/engine/watchrule.py b/heat/engine/watchrule.py deleted file mode 100644 index 6f72b8676e..0000000000 --- a/heat/engine/watchrule.py +++ /dev/null @@ -1,396 +0,0 @@ -# -# 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 datetime - -from oslo_log import log as logging -from oslo_utils import timeutils - -from heat.common import exception -from heat.common.i18n import _ -from heat.engine import stack -from heat.engine import timestamp -from heat.objects import stack as stack_object -from heat.objects import watch_data as watch_data_objects -from heat.objects import watch_rule as watch_rule_objects -from heat.rpc import api as rpc_api - -LOG = logging.getLogger(__name__) - - -class WatchRule(object): - WATCH_STATES = ( - ALARM, - NORMAL, - NODATA, - SUSPENDED, - CEILOMETER_CONTROLLED, - ) = ( - rpc_api.WATCH_STATE_ALARM, - rpc_api.WATCH_STATE_OK, - rpc_api.WATCH_STATE_NODATA, - rpc_api.WATCH_STATE_SUSPENDED, - rpc_api.WATCH_STATE_CEILOMETER_CONTROLLED, - ) - ACTION_MAP = {ALARM: 'AlarmActions', - NORMAL: 'OKActions', - NODATA: 'InsufficientDataActions'} - - created_at = timestamp.Timestamp(watch_rule_objects.WatchRule.get_by_id, - 'created_at') - updated_at = timestamp.Timestamp(watch_rule_objects.WatchRule.get_by_id, - 'updated_at') - - def __init__(self, context, watch_name, rule, stack_id=None, - state=NODATA, wid=None, watch_data=None, - last_evaluated=None): - - self.context = context - self.now = timeutils.utcnow() - self.name = watch_name - self.state = state - self.rule = rule - self.stack_id = stack_id - period = 0 - if 'Period' in rule: - period = int(rule['Period']) - elif 'period' in rule: - period = int(rule['period']) - self.timeperiod = datetime.timedelta(seconds=period) - self.id = wid - self.watch_data = watch_data or [] - self.last_evaluated = last_evaluated or timeutils.utcnow() - - @classmethod - def load(cls, context, watch_name=None, watch=None): - """Load the watchrule object. - - The object can be loaded either from the DB by name or from an existing - DB object. - """ - if watch is None: - try: - watch = watch_rule_objects.WatchRule.get_by_name(context, - watch_name) - except Exception as ex: - LOG.warning('WatchRule.load (%(watch_name)s) db error %(ex)s', - {'watch_name': watch_name, 'ex': ex}) - if watch is None: - raise exception.EntityNotFound(entity='Watch Rule', - name=watch_name) - else: - return cls(context=context, - watch_name=watch.name, - rule=watch.rule, - stack_id=watch.stack_id, - state=watch.state, - wid=watch.id, - watch_data=watch.watch_data, - last_evaluated=watch.last_evaluated) - - def store(self): - """Store the watchrule in the database and return its ID. - - If self.id is set, we update the existing rule. - """ - - wr_values = { - 'name': self.name, - 'rule': self.rule, - 'state': self.state, - 'stack_id': self.stack_id - } - - if self.id is None: - wr = watch_rule_objects.WatchRule.create(self.context, wr_values) - self.id = wr.id - else: - watch_rule_objects.WatchRule.update_by_id(self.context, self.id, - wr_values) - - def destroy(self): - """Delete the watchrule from the database.""" - if self.id is not None: - watch_rule_objects.WatchRule.delete(self.context, self.id) - - def do_data_cmp(self, data, threshold): - op = self.rule['ComparisonOperator'] - if op == 'GreaterThanThreshold': - return data > threshold - elif op == 'GreaterThanOrEqualToThreshold': - return data >= threshold - elif op == 'LessThanThreshold': - return data < threshold - elif op == 'LessThanOrEqualToThreshold': - return data <= threshold - else: - return False - - def do_Maximum(self): - data = 0 - have_data = False - for d in self.watch_data: - if d.created_at < self.now - self.timeperiod: - continue - if not have_data: - data = float(d.data[self.rule['MetricName']]['Value']) - have_data = True - if float(d.data[self.rule['MetricName']]['Value']) > data: - data = float(d.data[self.rule['MetricName']]['Value']) - - if not have_data: - return self.NODATA - - if self.do_data_cmp(data, - float(self.rule['Threshold'])): - return self.ALARM - else: - return self.NORMAL - - def do_Minimum(self): - data = 0 - have_data = False - for d in self.watch_data: - if d.created_at < self.now - self.timeperiod: - continue - if not have_data: - data = float(d.data[self.rule['MetricName']]['Value']) - have_data = True - elif float(d.data[self.rule['MetricName']]['Value']) < data: - data = float(d.data[self.rule['MetricName']]['Value']) - - if not have_data: - return self.NODATA - - if self.do_data_cmp(data, - float(self.rule['Threshold'])): - return self.ALARM - else: - return self.NORMAL - - def do_SampleCount(self): - """Count all samples within the specified period.""" - data = 0 - for d in self.watch_data: - if d.created_at < self.now - self.timeperiod: - continue - data = data + 1 - - if self.do_data_cmp(data, - float(self.rule['Threshold'])): - return self.ALARM - else: - return self.NORMAL - - def do_Average(self): - data = 0 - samples = 0 - for d in self.watch_data: - if d.created_at < self.now - self.timeperiod: - continue - samples = samples + 1 - data = data + float(d.data[self.rule['MetricName']]['Value']) - - if samples == 0: - return self.NODATA - - data = data / samples - if self.do_data_cmp(data, - float(self.rule['Threshold'])): - return self.ALARM - else: - return self.NORMAL - - def do_Sum(self): - data = 0 - for d in self.watch_data: - if d.created_at < self.now - self.timeperiod: - LOG.debug('ignoring %s', str(d.data)) - continue - data = data + float(d.data[self.rule['MetricName']]['Value']) - - if self.do_data_cmp(data, - float(self.rule['Threshold'])): - return self.ALARM - else: - return self.NORMAL - - def get_alarm_state(self): - fn = getattr(self, 'do_%s' % self.rule['Statistic']) - return fn() - - def evaluate(self): - if self.state in [self.CEILOMETER_CONTROLLED, self.SUSPENDED]: - return [] - # has enough time progressed to run the rule - self.now = timeutils.utcnow() - if self.now < (self.last_evaluated + self.timeperiod): - return [] - return self.run_rule() - - def get_details(self): - return {'alarm': self.name, - 'state': self.state} - - def run_rule(self): - new_state = self.get_alarm_state() - actions = self.rule_actions(new_state) - self.state = new_state - - self.last_evaluated = self.now - self.store() - return actions - - def rule_actions(self, new_state): - LOG.info('WATCH: stack:%(stack)s, watch_name:%(watch_name)s, ' - 'new_state:%(new_state)s', {'stack': self.stack_id, - 'watch_name': self.name, - 'new_state': new_state}) - actions = [] - if self.ACTION_MAP[new_state] not in self.rule: - LOG.info('no action for new state %s', new_state) - else: - s = stack_object.Stack.get_by_id( - self.context, - self.stack_id) - stk = stack.Stack.load(self.context, stack=s) - if (stk.action != stk.DELETE - and stk.status == stk.COMPLETE): - for refid in self.rule[self.ACTION_MAP[new_state]]: - actions.append(stk.resource_by_refid(refid).signal) - else: - LOG.warning("Could not process watch state %s for stack", - new_state) - return actions - - def _to_ceilometer(self, data): - clients = self.context.clients - sample = {} - sample['counter_type'] = 'gauge' - - for k, d in iter(data.items()): - if k == 'Namespace': - continue - sample['counter_name'] = k - sample['counter_volume'] = d['Value'] - sample['counter_unit'] = d['Unit'] - dims = d.get('Dimensions', {}) - if isinstance(dims, list): - dims = dims[0] - sample['resource_metadata'] = dims - sample['resource_id'] = dims.get('InstanceId') - LOG.debug('new sample:%(k)s data:%(sample)s', { - 'k': k, 'sample': sample}) - clients.client('ceilometer').samples.create(**sample) - - def create_watch_data(self, data): - if self.state == self.CEILOMETER_CONTROLLED: - # this is a short term measure for those that have cfn-push-stats - # within their templates, but want to use Ceilometer alarms. - - self._to_ceilometer(data) - return - - if self.state == self.SUSPENDED: - LOG.debug('Ignoring metric data for %s, SUSPENDED state', - self.name) - return [] - - if self.rule['MetricName'] not in data: - # Our simplified cloudwatch implementation only expects a single - # Metric associated with each alarm, but some cfn-push-stats - # options, e.g --haproxy try to push multiple metrics when we - # actually only care about one (the one we're alarming on) - # so just ignore any data which doesn't contain MetricName - LOG.debug('Ignoring metric data (only accept %(metric)s) ' - ': %(data)s' % {'metric': self.rule['MetricName'], - 'data': data}) - return - - watch_data = { - 'data': data, - 'watch_rule_id': self.id - } - wd = watch_data_objects.WatchData.create(self.context, watch_data) - LOG.debug('new watch:%(name)s data:%(data)s' - % {'name': self.name, 'data': str(wd.data)}) - - def state_set(self, state): - """Persistently store the watch state.""" - if state not in self.WATCH_STATES: - raise ValueError(_("Invalid watch state %s") % state) - - self.state = state - self.store() - - def set_watch_state(self, state): - """Temporarily set the watch state. - - :returns: list of functions to be scheduled in the stack ThreadGroup - for the specified state. - """ - - if state not in self.WATCH_STATES: - raise ValueError(_('Unknown watch state %s') % state) - - actions = [] - if state != self.state: - actions = self.rule_actions(state) - if actions: - LOG.debug("Overriding state %(self_state)s for watch " - "%(name)s with %(state)s" - % {'self_state': self.state, 'name': self.name, - 'state': state}) - else: - LOG.warning("Unable to override state %(state)s for " - "watch %(name)s", {'state': self.state, - 'name': self.name}) - return actions - - -def rule_can_use_sample(wr, stats_data): - def match_dimesions(rule, data): - for k, v in iter(rule.items()): - if k not in data: - return False - elif v != data[k]: - return False - return True - - if wr.state == WatchRule.SUSPENDED: - return False - if wr.state == WatchRule.CEILOMETER_CONTROLLED: - metric = wr.rule['meter_name'] - rule_dims = {} - for k, v in iter(wr.rule.get('matching_metadata', {}).items()): - name = k.split('.')[-1] - rule_dims[name] = v - else: - metric = wr.rule['MetricName'] - rule_dims = dict((d['Name'], d['Value']) - for d in wr.rule.get('Dimensions', [])) - - if metric not in stats_data: - return False - - for k, v in iter(stats_data.items()): - if k == 'Namespace': - continue - if k == metric: - data_dims = v.get('Dimensions', {}) - if isinstance(data_dims, list): - data_dims = data_dims[0] - if match_dimesions(rule_dims, data_dims): - return True - return False diff --git a/heat/objects/watch_data.py b/heat/objects/watch_data.py deleted file mode 100644 index e972f337f7..0000000000 --- a/heat/objects/watch_data.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2014 Intel Corp. -# -# 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. - -"""WatchData object.""" - -from oslo_versionedobjects import base -from oslo_versionedobjects import fields - -from heat.db.sqlalchemy import api as db_api -from heat.objects import base as heat_base -from heat.objects import fields as heat_fields - - -class WatchData( - heat_base.HeatObject, - base.VersionedObjectDictCompat, -): - - fields = { - 'id': fields.IntegerField(), - 'data': heat_fields.JsonField(nullable=True), - 'watch_rule_id': fields.StringField(), - 'created_at': fields.DateTimeField(read_only=True), - 'updated_at': fields.DateTimeField(nullable=True), - } - - @staticmethod - def _from_db_object(context, rule, db_data): - for field in rule.fields: - rule[field] = db_data[field] - rule._context = context - rule.obj_reset_changes() - return rule - - @classmethod - def create(cls, context, values): - db_data = db_api.watch_data_create(context, values) - return cls._from_db_object(context, cls(), db_data) - - @classmethod - def get_all(cls, context): - return [cls._from_db_object(context, cls(), db_data) - for db_data in db_api.watch_data_get_all(context)] - - @classmethod - def get_all_by_watch_rule_id(cls, context, watch_rule_id): - return (cls._from_db_object(context, cls(), db_data) - for db_data in db_api.watch_data_get_all_by_watch_rule_id( - context, watch_rule_id)) diff --git a/heat/objects/watch_rule.py b/heat/objects/watch_rule.py deleted file mode 100644 index 36ff0c27d0..0000000000 --- a/heat/objects/watch_rule.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2014 Intel Corp. -# -# 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. - -"""WatchRule object.""" - -from oslo_versionedobjects import base -from oslo_versionedobjects import fields - -from heat.db.sqlalchemy import api as db_api -from heat.objects import base as heat_base -from heat.objects import fields as heat_fields -from heat.objects import watch_data - - -class WatchRule( - heat_base.HeatObject, - base.VersionedObjectDictCompat, -): - - fields = { - 'id': fields.IntegerField(), - 'name': fields.StringField(nullable=True), - 'rule': heat_fields.JsonField(nullable=True), - 'state': fields.StringField(nullable=True), - 'last_evaluated': fields.DateTimeField(nullable=True), - 'stack_id': fields.StringField(), - 'watch_data': fields.ListOfObjectsField(watch_data.WatchData), - 'created_at': fields.DateTimeField(read_only=True), - 'updated_at': fields.DateTimeField(nullable=True), - } - - @staticmethod - def _from_db_object(context, rule, db_rule): - for field in rule.fields: - if field == 'watch_data': - rule[field] = watch_data.WatchData.get_all_by_watch_rule_id( - context, db_rule['id']) - else: - rule[field] = db_rule[field] - rule._context = context - rule.obj_reset_changes() - return rule - - @classmethod - def get_by_id(cls, context, rule_id): - db_rule = db_api.watch_rule_get(context, rule_id) - return cls._from_db_object(context, cls(), db_rule) - - @classmethod - def get_by_name(cls, context, watch_rule_name): - db_rule = db_api.watch_rule_get_by_name(context, watch_rule_name) - return cls._from_db_object(context, cls(), db_rule) - - @classmethod - def get_all(cls, context): - return [cls._from_db_object(context, cls(), db_rule) - for db_rule in db_api.watch_rule_get_all(context)] - - @classmethod - def get_all_by_stack(cls, context, stack_id): - return [cls._from_db_object(context, cls(), db_rule) - for db_rule in db_api.watch_rule_get_all_by_stack(context, - stack_id)] - - @classmethod - def update_by_id(cls, context, watch_id, values): - db_api.watch_rule_update(context, watch_id, values) - - @classmethod - def create(cls, context, values): - return cls._from_db_object(context, cls(), - db_api.watch_rule_create(context, values)) - - @classmethod - def delete(cls, context, watch_id): - db_api.watch_rule_delete(context, watch_id) diff --git a/heat/rpc/api.py b/heat/rpc/api.py index 90cdc81776..1763718eb9 100644 --- a/heat/rpc/api.py +++ b/heat/rpc/api.py @@ -127,66 +127,6 @@ NOTIFY_KEYS = ( STACK_TAGS, ) -# This is the representation of a watch we expose to the API via RPC -WATCH_KEYS = ( - WATCH_ACTIONS_ENABLED, WATCH_ALARM_ACTIONS, WATCH_TOPIC, - WATCH_UPDATED_TIME, WATCH_DESCRIPTION, WATCH_NAME, - WATCH_COMPARISON, WATCH_DIMENSIONS, WATCH_PERIODS, - WATCH_INSUFFICIENT_ACTIONS, WATCH_METRIC_NAME, WATCH_NAMESPACE, - WATCH_OK_ACTIONS, WATCH_PERIOD, WATCH_STATE_REASON, - WATCH_STATE_REASON_DATA, WATCH_STATE_UPDATED_TIME, WATCH_STATE_VALUE, - WATCH_STATISTIC, WATCH_THRESHOLD, WATCH_UNIT, WATCH_STACK_ID, -) = ( - 'actions_enabled', 'actions', 'topic', - 'updated_time', 'description', 'name', - 'comparison', 'dimensions', 'periods', - 'insufficient_actions', 'metric_name', 'namespace', - 'ok_actions', 'period', 'state_reason', - 'state_reason_data', 'state_updated_time', 'state_value', - 'statistic', 'threshold', 'unit', 'stack_id', -) - -# Alternate representation of a watch rule to align with DB format -# FIXME : These align with AWS naming for compatibility with the -# current cfn-push-stats & metadata server, fix when we've ported -# cfn-push-stats to use the Cloudwatch server and/or moved metric -# collection into ceilometer, these should just be WATCH_KEYS -# or each field should be stored separately in the DB watch_data -# table if we stick to storing watch data in the heat DB -WATCH_RULE_KEYS = ( - RULE_ACTIONS_ENABLED, RULE_ALARM_ACTIONS, RULE_TOPIC, - RULE_UPDATED_TIME, RULE_DESCRIPTION, RULE_NAME, - RULE_COMPARISON, RULE_DIMENSIONS, RULE_PERIODS, - RULE_INSUFFICIENT_ACTIONS, RULE_METRIC_NAME, RULE_NAMESPACE, - RULE_OK_ACTIONS, RULE_PERIOD, RULE_STATE_REASON, - RULE_STATE_REASON_DATA, RULE_STATE_UPDATED_TIME, RULE_STATE_VALUE, - RULE_STATISTIC, RULE_THRESHOLD, RULE_UNIT, RULE_STACK_NAME, -) = ( - 'ActionsEnabled', 'AlarmActions', 'AlarmArn', - 'AlarmConfigurationUpdatedTimestamp', 'AlarmDescription', 'AlarmName', - 'ComparisonOperator', 'Dimensions', 'EvaluationPeriods', - 'InsufficientDataActions', 'MetricName', 'Namespace', - 'OKActions', 'Period', 'StateReason', - 'StateReasonData', 'StateUpdatedTimestamp', 'StateValue', - 'Statistic', 'Threshold', 'Unit', 'StackName', -) - -WATCH_STATES = ( - WATCH_STATE_OK, WATCH_STATE_ALARM, WATCH_STATE_NODATA, - WATCH_STATE_SUSPENDED, WATCH_STATE_CEILOMETER_CONTROLLED -) = ( - 'NORMAL', 'ALARM', 'NODATA', - 'SUSPENDED', 'CEILOMETER_CONTROLLED' -) - -WATCH_DATA_KEYS = ( - WATCH_DATA_ALARM, WATCH_DATA_METRIC, WATCH_DATA_TIME, - WATCH_DATA_NAMESPACE, WATCH_DATA -) = ( - 'watch_name', 'metric_name', 'timestamp', - 'namespace', 'data' -) - VALIDATE_PARAM_KEYS = ( PARAM_TYPE, PARAM_DEFAULT, PARAM_NO_ECHO, PARAM_ALLOWED_VALUES, PARAM_ALLOWED_PATTERN, PARAM_MAX_LENGTH, diff --git a/heat/rpc/client.py b/heat/rpc/client.py index d5b4b677be..2a6cc23c84 100644 --- a/heat/rpc/client.py +++ b/heat/rpc/client.py @@ -676,60 +676,6 @@ class EngineClient(object): resource_status_reason=resource_status_reason), version='1.26') - def create_watch_data(self, ctxt, watch_name, stats_data): - """Creates data for CloudWatch and WaitConditions. - - This could be used by CloudWatch and WaitConditions and treat HA - service events like any other CloudWatch. - - :param ctxt: RPC context. - :param watch_name: Name of the watch/alarm - :param stats_data: The data to post. - """ - return self.call(ctxt, self.make_msg('create_watch_data', - watch_name=watch_name, - stats_data=stats_data)) - - def show_watch(self, ctxt, watch_name): - """Returns the attributes of one watch/alarm. - - The show_watch method returns the attributes of one watch - or all watches if no watch_name is passed. - - :param ctxt: RPC context. - :param watch_name: Name of the watch/alarm you want to see, - or None to see all - """ - return self.call(ctxt, self.make_msg('show_watch', - watch_name=watch_name)) - - def show_watch_metric(self, ctxt, metric_namespace=None, metric_name=None): - """Returns the datapoints for a metric. - - The show_watch_metric method returns the datapoints associated - with a specified metric, or all metrics if no metric_name is passed. - - :param ctxt: RPC context. - :param metric_namespace: Name of the namespace you want to see, - or None to see all - :param metric_name: Name of the metric you want to see, - or None to see all - """ - return self.call(ctxt, self.make_msg('show_watch_metric', - metric_namespace=metric_namespace, - metric_name=metric_name)) - - def set_watch_state(self, ctxt, watch_name, state): - """Temporarily set the state of a given watch. - - :param ctxt: RPC context. - :param watch_name: Name of the watch - :param state: State (must be one defined in WatchRule class) - """ - return self.call(ctxt, self.make_msg('set_watch_state', - watch_name=watch_name, - state=state)) - def get_revision(self, ctxt): return self.call(ctxt, self.make_msg('get_revision')) diff --git a/heat/tests/engine/service/test_stack_watch.py b/heat/tests/engine/service/test_stack_watch.py deleted file mode 100644 index 3f1d97dfa2..0000000000 --- a/heat/tests/engine/service/test_stack_watch.py +++ /dev/null @@ -1,270 +0,0 @@ -# -# 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 oslo_messaging.rpc import dispatcher - -from heat.common import exception -from heat.engine import service -from heat.engine import service_stack_watch -from heat.engine import stack -from heat.engine import watchrule -from heat.objects import stack as stack_object -from heat.objects import watch_data as watch_data_object -from heat.objects import watch_rule as watch_rule_object -from heat.rpc import api as rpc_api -from heat.tests import common -from heat.tests.engine import tools -from heat.tests import utils - - -class StackWatchTest(common.HeatTestCase): - - def setUp(self): - super(StackWatchTest, self).setUp() - - self.ctx = utils.dummy_context(tenant_id='stack_watch_test_tenant') - self.eng = service.EngineService('a-host', 'a-topic') - # self.eng.engine_id = 'engine-fake-uuid' - - def _create_periodic_tasks(self): - self.eng.create_periodic_tasks() - self.eng.manage_thread_grp.wait() - - @mock.patch.object(service_stack_watch.StackWatch, 'start_watch_task') - @mock.patch.object(stack_object.Stack, 'get_all') - @mock.patch.object(service.service.Service, 'start') - def test_start_watches_all_stacks(self, mock_super_start, mock_get_all, - start_watch_task): - s1 = mock.Mock(id=1) - s2 = mock.Mock(id=2) - mock_get_all.return_value = [s1, s2] - start_watch_task.return_value = None - - self.eng.thread_group_mgr = None - self._create_periodic_tasks() - - mock_get_all.assert_called_once_with(mock.ANY, - show_hidden=True) - calls = start_watch_task.call_args_list - self.assertEqual(2, start_watch_task.call_count) - self.assertIn(mock.call(1, mock.ANY), calls) - self.assertIn(mock.call(2, mock.ANY), calls) - - @tools.stack_context('service_show_watch_test_stack', False) - def test_show_watch(self): - # Insert two dummy watch rules into the DB - rule = {u'EvaluationPeriods': u'1', - u'AlarmActions': [u'WebServerRestartPolicy'], - u'AlarmDescription': u'Restart the WikiDatabase', - u'Namespace': u'system/linux', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'ServiceFailure'} - self.wr = [] - self.wr.append(watchrule.WatchRule(context=self.ctx, - watch_name='show_watch_1', - rule=rule, - watch_data=[], - stack_id=self.stack.id, - state='NORMAL')) - self.wr[0].store() - - self.wr.append(watchrule.WatchRule(context=self.ctx, - watch_name='show_watch_2', - rule=rule, - watch_data=[], - stack_id=self.stack.id, - state='NORMAL')) - self.wr[1].store() - - # watch_name=None should return all watches - result = self.eng.show_watch(self.ctx, watch_name=None) - result_names = [r.get('name') for r in result] - self.assertIn('show_watch_1', result_names) - self.assertIn('show_watch_2', result_names) - - result = self.eng.show_watch(self.ctx, watch_name="show_watch_1") - self.assertEqual(1, len(result)) - self.assertIn('name', result[0]) - self.assertEqual('show_watch_1', result[0]['name']) - - result = self.eng.show_watch(self.ctx, watch_name="show_watch_2") - self.assertEqual(1, len(result)) - self.assertIn('name', result[0]) - self.assertEqual('show_watch_2', result[0]['name']) - - ex = self.assertRaises(dispatcher.ExpectedException, - self.eng.show_watch, - self.ctx, watch_name="nonexistent") - self.assertEqual(exception.EntityNotFound, ex.exc_info[0]) - - # Check the response has all keys defined in the engine API - for key in rpc_api.WATCH_KEYS: - self.assertIn(key, result[0]) - - @tools.stack_context('service_show_watch_metric_test_stack', False) - def test_show_watch_metric(self): - # Insert dummy watch rule into the DB - rule = {u'EvaluationPeriods': u'1', - u'AlarmActions': [u'WebServerRestartPolicy'], - u'AlarmDescription': u'Restart the WikiDatabase', - u'Namespace': u'system/linux', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'ServiceFailure'} - self.wr = watchrule.WatchRule(context=self.ctx, - watch_name='show_watch_metric_1', - rule=rule, - watch_data=[], - stack_id=self.stack.id, - state='NORMAL') - self.wr.store() - - # And add a metric datapoint - watch = watch_rule_object.WatchRule.get_by_name(self.ctx, - 'show_watch_metric_1') - self.assertIsNotNone(watch) - values = {'watch_rule_id': watch.id, - 'data': {u'Namespace': u'system/linux', - u'ServiceFailure': { - u'Units': u'Counter', u'Value': 1}}} - watch_data_object.WatchData.create(self.ctx, values) - - # Check there is one result returned - result = self.eng.show_watch_metric(self.ctx, - metric_namespace=None, - metric_name=None) - self.assertEqual(1, len(result)) - - # Create another metric datapoint and check we get two - watch_data_object.WatchData.create(self.ctx, values) - result = self.eng.show_watch_metric(self.ctx, - metric_namespace=None, - metric_name=None) - self.assertEqual(2, len(result)) - - # Check the response has all keys defined in the engine API - for key in rpc_api.WATCH_DATA_KEYS: - self.assertIn(key, result[0]) - - @tools.stack_context('service_show_watch_state_test_stack') - @mock.patch.object(stack.Stack, 'resource_by_refid') - def test_set_watch_state(self, mock_ref): - self._create_periodic_tasks() - # Insert dummy watch rule into the DB - rule = {u'EvaluationPeriods': u'1', - u'AlarmActions': [u'WebServerRestartPolicy'], - u'AlarmDescription': u'Restart the WikiDatabase', - u'Namespace': u'system/linux', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'ServiceFailure'} - self.wr = watchrule.WatchRule(context=self.ctx, - watch_name='OverrideAlarm', - rule=rule, - watch_data=[], - stack_id=self.stack.id, - state='NORMAL') - self.wr.store() - - class DummyAction(object): - def signal(self): - return "dummyfoo" - - dummy_action = DummyAction() - mock_ref.return_value = dummy_action - - # Replace the real stack threadgroup with a dummy one, so we can - # check the function returned on ALARM is correctly scheduled - dtg = tools.DummyThreadGroup() - self.eng.thread_group_mgr.groups[self.stack.id] = dtg - - state = watchrule.WatchRule.NODATA - result = self.eng.set_watch_state(self.ctx, - watch_name="OverrideAlarm", - state=state) - self.assertEqual(state, result[rpc_api.WATCH_STATE_VALUE]) - self.assertEqual( - [], self.eng.thread_group_mgr.groups[self.stack.id].threads) - - state = watchrule.WatchRule.NORMAL - result = self.eng.set_watch_state(self.ctx, - watch_name="OverrideAlarm", - state=state) - self.assertEqual(state, result[rpc_api.WATCH_STATE_VALUE]) - self.assertEqual( - [], self.eng.thread_group_mgr.groups[self.stack.id].threads) - - state = watchrule.WatchRule.ALARM - result = self.eng.set_watch_state(self.ctx, - watch_name="OverrideAlarm", - state=state) - self.assertEqual(state, result[rpc_api.WATCH_STATE_VALUE]) - self.assertEqual( - [dummy_action.signal], - self.eng.thread_group_mgr.groups[self.stack.id].threads) - - mock_ref.assert_called_once_with('WebServerRestartPolicy') - - @tools.stack_context('service_show_watch_state_badstate_test_stack') - @mock.patch.object(watchrule.WatchRule, 'set_watch_state') - def test_set_watch_state_badstate(self, mock_set): - mock_set.side_effect = ValueError - # Insert dummy watch rule into the DB - rule = {u'EvaluationPeriods': u'1', - u'AlarmActions': [u'WebServerRestartPolicy'], - u'AlarmDescription': u'Restart the WikiDatabase', - u'Namespace': u'system/linux', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'ServiceFailure'} - self.wr = watchrule.WatchRule(context=self.ctx, - watch_name='OverrideAlarm2', - rule=rule, - watch_data=[], - stack_id=self.stack.id, - state='NORMAL') - self.wr.store() - - for state in ["HGJHGJHG", "1234", "!\\*(&%"]: - self.assertRaises(ValueError, - self.eng.set_watch_state, - self.ctx, watch_name="OverrideAlarm2", - state=state) - - calls = [mock.call("HGJHGJHG"), - mock.call("1234"), - mock.call("!\\*(&%")] - mock_set.assert_has_calls(calls) - - @mock.patch.object(watchrule.WatchRule, 'load') - def test_set_watch_state_noexist(self, mock_load): - state = watchrule.WatchRule.ALARM # State valid - mock_load.side_effect = exception.EntityNotFound(entity='Watch Rule', - name='test') - - ex = self.assertRaises(dispatcher.ExpectedException, - self.eng.set_watch_state, - self.ctx, watch_name="nonexistent", - state=state) - self.assertEqual(exception.EntityNotFound, ex.exc_info[0]) - mock_load.assert_called_once_with(self.ctx, "nonexistent") diff --git a/heat/tests/openstack/aodh/test_alarm.py b/heat/tests/openstack/aodh/test_alarm.py index 746ac37887..23ab45d966 100644 --- a/heat/tests/openstack/aodh/test_alarm.py +++ b/heat/tests/openstack/aodh/test_alarm.py @@ -26,7 +26,6 @@ from heat.engine import rsrc_defn from heat.engine import scheduler from heat.engine import stack as parser from heat.engine import template as tmpl -from heat.engine import watchrule from heat.tests import common from heat.tests import utils @@ -404,48 +403,6 @@ class AodhAlarmTest(common.HeatTestCase): 'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack) self.assertIsNone(rsrc.validate()) - def test_delete_watchrule_destroy(self): - t = template_format.parse(alarm_template) - - test_stack = self.create_stack(template=json.dumps(t)) - rsrc = test_stack['MEMAlarmHigh'] - - wr = mock.MagicMock() - self.patchobject(watchrule.WatchRule, 'load', return_value=wr) - wr.destroy.return_value = None - - self.patchobject(aodh.AodhClientPlugin, 'client', - return_value=self.fa) - self.patchobject(self.fa.alarm, 'delete') - rsrc.resource_id = '12345' - - self.assertEqual('12345', rsrc.handle_delete()) - self.assertEqual(1, wr.destroy.call_count) - # check that super method has been called and execute deleting - self.assertEqual(1, self.fa.alarm.delete.call_count) - - def test_delete_no_watchrule(self): - t = template_format.parse(alarm_template) - - test_stack = self.create_stack(template=json.dumps(t)) - rsrc = test_stack['MEMAlarmHigh'] - - wr = mock.MagicMock() - self.patchobject(watchrule.WatchRule, 'load', - side_effect=[exception.EntityNotFound( - entity='Watch Rule', name='test')]) - wr.destroy.return_value = None - - self.patchobject(aodh.AodhClientPlugin, 'client', - return_value=self.fa) - self.patchobject(self.fa.alarm, 'delete') - rsrc.resource_id = '12345' - - self.assertEqual('12345', rsrc.handle_delete()) - self.assertEqual(0, wr.destroy.call_count) - # check that super method has been called and execute deleting - self.assertEqual(1, self.fa.alarm.delete.call_count) - def _prepare_resource(self, for_check=True): snippet = template_format.parse(not_string_alarm_template) self.stack = utils.parse_stack(snippet) @@ -457,25 +414,12 @@ class AodhAlarmTest(common.HeatTestCase): res.client().alarm.get.return_value = mock_alarm return res - @mock.patch.object(alarm.watchrule.WatchRule, 'load') - def test_check(self, mock_load): + def test_check(self): res = self._prepare_resource() scheduler.TaskRunner(res.check)() self.assertEqual((res.CHECK, res.COMPLETE), res.state) - @mock.patch.object(alarm.watchrule.WatchRule, 'load') - def test_check_watchrule_failure(self, mock_load): - res = self._prepare_resource() - exc = alarm.exception.EntityNotFound(entity='Watch Rule', name='Boom') - mock_load.side_effect = exc - - self.assertRaises(exception.ResourceFailure, - scheduler.TaskRunner(res.check)) - self.assertEqual((res.CHECK, res.FAILED), res.state) - self.assertIn('Boom', res.status_reason) - - @mock.patch.object(alarm.watchrule.WatchRule, 'load') - def test_check_alarm_failure(self, mock_load): + def test_check_alarm_failure(self): res = self._prepare_resource() res.client().alarm.get.side_effect = Exception('Boom') diff --git a/heat/tests/openstack/heat/test_cloudwatch.py b/heat/tests/openstack/heat/test_cloudwatch.py deleted file mode 100644 index d93a43e47c..0000000000 --- a/heat/tests/openstack/heat/test_cloudwatch.py +++ /dev/null @@ -1,120 +0,0 @@ -# -# 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 heat.common import exception -from heat.common import template_format -from heat.engine import resource -from heat.engine import resources -from heat.engine.resources.openstack.heat import cloud_watch -from heat.engine import scheduler -from heat.engine import watchrule -from heat.tests import common -from heat.tests import utils - - -AWS_CloudWatch_Alarm = ''' -HeatTemplateFormatVersion: '2012-12-12' -Description: Template which tests alarms -Resources: - test_me: - Type: AWS::CloudWatch::Alarm - Properties: - MetricName: cpu_util - Namespace: AWS/EC2 - Statistic: Average - Period: '60' - EvaluationPeriods: '1' - Threshold: '50' - ComparisonOperator: GreaterThanThreshold -''' - - -class CloudWatchAlarmTest(common.HeatTestCase): - - def setUp(self): - super(CloudWatchAlarmTest, self).setUp() - - def clear_register_class(): - env = resources.global_env() - env.registry._registry.pop('CWLiteAlarmForTest') - - self.ctx = utils.dummy_context() - resource._register_class('CWLiteAlarmForTest', - cloud_watch.CloudWatchAlarm) - self.addCleanup(clear_register_class) - - def parse_stack(self): - t = template_format.parse(AWS_CloudWatch_Alarm) - env = {'resource_registry': { - 'AWS::CloudWatch::Alarm': 'CWLiteAlarmForTest' - }} - self.stack = utils.parse_stack(t, params=env) - return self.stack - - def test_resource_create_good(self): - s = self.parse_stack() - self.assertIsNone(scheduler.TaskRunner(s['test_me'].create)()) - - def test_resource_create_failed(self): - s = self.parse_stack() - with mock.patch.object(watchrule.WatchRule, 'store') as bad_store: - bad_store.side_effect = KeyError('any random failure') - task_func = scheduler.TaskRunner(s['test_me'].create) - self.assertRaises(exception.ResourceFailure, task_func) - - def test_resource_delete_good(self): - s = self.parse_stack() - self.assertIsNone(scheduler.TaskRunner(s['test_me'].create)()) - self.assertIsNone(scheduler.TaskRunner(s['test_me'].delete)()) - - def test_resource_delete_notfound(self): - # if a resource is not found, handle_delete() should not raise - # an exception. - s = self.parse_stack() - self.assertIsNone(scheduler.TaskRunner(s['test_me'].create)()) - res_name = self.stack['test_me'].physical_resource_name() - self.wr = watchrule.WatchRule.load(self.ctx, - watch_name=res_name) - - with mock.patch.object(watchrule.WatchRule, 'destroy') as bad_destroy: - watch_exc = exception.EntityNotFound(entity='Watch Rule', - name='test') - bad_destroy.side_effect = watch_exc - self.assertIsNone(scheduler.TaskRunner(s['test_me'].delete)()) - - def _get_watch_rule(self): - stack = self.parse_stack() - res = stack['test_me'] - res.state_set(res.CREATE, res.COMPLETE) - return res - - @mock.patch.object(cloud_watch.watchrule.WatchRule, 'load') - def test_check(self, mock_lock): - res = self._get_watch_rule() - - scheduler.TaskRunner(res.check)() - self.assertEqual((res.CHECK, res.COMPLETE), res.state) - - @mock.patch.object(cloud_watch.watchrule.WatchRule, 'load') - def test_check_fail(self, mock_load): - res = self._get_watch_rule() - exc = cloud_watch.exception.EntityNotFound(entity='Watch Rule', - name='Boom') - mock_load.side_effect = exc - - self.assertRaises(exception.ResourceFailure, - scheduler.TaskRunner(res.check)) - self.assertEqual((res.CHECK, res.FAILED), res.state) - self.assertIn('Boom', res.status_reason) diff --git a/heat/tests/openstack/heat/test_cw_alarm.py b/heat/tests/openstack/heat/test_cw_alarm.py deleted file mode 100644 index 3ae4f57d63..0000000000 --- a/heat/tests/openstack/heat/test_cw_alarm.py +++ /dev/null @@ -1,161 +0,0 @@ -# -# 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 copy - -from heat.common import template_format -from heat.engine import resource -from heat.engine.resources.openstack.heat import cloud_watch -from heat.engine import rsrc_defn -from heat.engine import scheduler -from heat.engine import watchrule -from heat.tests import common -from heat.tests import utils - - -alarm_template = ''' -{ - "AWSTemplateFormatVersion" : "2010-09-09", - "Description" : "Alarm Test", - "Parameters" : {}, - "Resources" : { - "MEMAlarmHigh": { - "Type": "AWS::CloudWatch::Alarm", - "Properties": { - "AlarmDescription": "Scale-up if MEM > 50% for 1 minute", - "MetricName": "MemoryUtilization", - "Namespace": "system/linux", - "Statistic": "Average", - "Period": "60", - "EvaluationPeriods": "1", - "Threshold": "50", - "AlarmActions": [], - "Dimensions": [], - "ComparisonOperator": "GreaterThanThreshold" - } - } - } -} -''' - - -class CloudWatchAlarmTest(common.HeatTestCase): - - def create_alarm(self, t, stack, resource_name): - resource_defns = stack.t.resource_definitions(stack) - rsrc = cloud_watch.CloudWatchAlarm(resource_name, - resource_defns[resource_name], - stack) - self.assertIsNone(rsrc.validate()) - scheduler.TaskRunner(rsrc.create)() - self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) - return rsrc - - def test_mem_alarm_high_update_no_replace(self): - """Test case for updating the alarm with updatable properties. - - Make sure that we can change the updatable properties - without replacing the Alarm rsrc. - """ - t = template_format.parse(alarm_template) - - # short circuit the alarm's references - properties = t['Resources']['MEMAlarmHigh']['Properties'] - properties['AlarmActions'] = ['a'] - properties['Dimensions'] = [{'a': 'v'}] - - stack = utils.parse_stack(t) - # the watch rule needs a valid stack_id - stack.store() - - self.m.ReplayAll() - rsrc = self.create_alarm(t, stack, 'MEMAlarmHigh') - props = copy.copy(rsrc.properties.data) - props.update({ - 'ComparisonOperator': 'LessThanThreshold', - 'AlarmDescription': 'fruity', - 'EvaluationPeriods': '2', - 'Period': '90', - 'Statistic': 'Maximum', - 'Threshold': '39', - }) - snippet = rsrc_defn.ResourceDefinition(rsrc.name, - rsrc.type(), - props) - - scheduler.TaskRunner(rsrc.update, snippet)() - - scheduler.TaskRunner(rsrc.delete)() - self.m.VerifyAll() - - def test_mem_alarm_high_update_replace(self): - """Test case for replacing the alarm with non-updatable properties. - - Make sure that the Alarm resource IS replaced when non-update-able - properties are changed. - """ - t = template_format.parse(alarm_template) - - # short circuit the alarm's references - properties = t['Resources']['MEMAlarmHigh']['Properties'] - properties['AlarmActions'] = ['a'] - properties['Dimensions'] = [{'a': 'v'}] - - stack = utils.parse_stack(t) - # the watch rule needs a valid stack_id - stack.store() - - self.m.ReplayAll() - rsrc = self.create_alarm(t, stack, 'MEMAlarmHigh') - props = copy.copy(rsrc.properties.data) - props['MetricName'] = 'temp' - snippet = rsrc_defn.ResourceDefinition(rsrc.name, - rsrc.type(), - props) - - updater = scheduler.TaskRunner(rsrc.update, snippet) - self.assertRaises(resource.UpdateReplace, updater) - - scheduler.TaskRunner(rsrc.delete)() - self.m.VerifyAll() - - def test_suspend_resume(self): - t = template_format.parse(alarm_template) - stack_name = "test_cw_alarm_sus_res_stack" - stack = utils.parse_stack(t, stack_name=stack_name) - # the watch rule needs a valid stack_id - stack.store() - - self.m.ReplayAll() - rsrc = self.create_alarm(t, stack, 'MEMAlarmHigh') - scheduler.TaskRunner(rsrc.suspend)() - self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state) - - self.ctx = utils.dummy_context() - - wr = watchrule.WatchRule.load( - self.ctx, watch_name="%s-MEMAlarmHigh" % stack_name) - - self.assertEqual(watchrule.WatchRule.SUSPENDED, wr.state) - - scheduler.TaskRunner(rsrc.resume)() - self.assertEqual((rsrc.RESUME, rsrc.COMPLETE), rsrc.state) - - wr = watchrule.WatchRule.load( - self.ctx, watch_name="%s-MEMAlarmHigh" % stack_name) - - self.assertEqual(watchrule.WatchRule.NODATA, wr.state) - - scheduler.TaskRunner(rsrc.delete)() - self.m.VerifyAll() diff --git a/heat/tests/test_engine_service_stack_watch.py b/heat/tests/test_engine_service_stack_watch.py deleted file mode 100644 index 3ff191609a..0000000000 --- a/heat/tests/test_engine_service_stack_watch.py +++ /dev/null @@ -1,118 +0,0 @@ -# -# 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 heat.engine import service_stack_watch -from heat.rpc import api as rpc_api -from heat.tests import common -from heat.tests import utils - - -class StackServiceWatcherTest(common.HeatTestCase): - - def setUp(self): - super(StackServiceWatcherTest, self).setUp() - self.ctx = utils.dummy_context(tenant_id='stack_service_test_tenant') - - @mock.patch.object(service_stack_watch.stack_object.Stack, - 'get_all_by_owner_id') - @mock.patch.object(service_stack_watch.watch_rule_object.WatchRule, - 'get_all_by_stack') - @mock.patch.object(service_stack_watch.watch_rule_object.WatchRule, - 'update_by_id') - def test_periodic_watch_task_not_created(self, watch_rule_update, - watch_rule_get_all_by_stack, - stack_get_all_by_owner_id): - """Test case for not creating periodic task for cloud watch lite alarm. - - If there is no cloud watch lite alarm, then don't create a periodic - task for it. - """ - stack_id = 83 - watch_rule_get_all_by_stack.return_value = [] - stack_get_all_by_owner_id.return_value = [] - tg = mock.Mock() - sw = service_stack_watch.StackWatch(tg) - sw.start_watch_task(stack_id, self.ctx) - - # assert that add_timer is NOT called. - self.assertEqual([], tg.add_timer.call_args_list) - - @mock.patch.object(service_stack_watch.stack_object.Stack, - 'get_all_by_owner_id') - @mock.patch.object(service_stack_watch.watch_rule_object.WatchRule, - 'get_all_by_stack') - @mock.patch.object(service_stack_watch.watch_rule_object.WatchRule, - 'update_by_id') - def test_periodic_watch_task_created(self, watch_rule_update, - watch_rule_get_all_by_stack, - stack_get_all_by_owner_id): - """Test case for creating periodic task for cloud watch lite alarm. - - If there is no cloud watch lite alarm, then DO create a periodic task - for it. - """ - stack_id = 86 - wr1 = mock.Mock() - wr1.id = 4 - wr1.state = rpc_api.WATCH_STATE_NODATA - - watch_rule_get_all_by_stack.return_value = [wr1] - stack_get_all_by_owner_id.return_value = [] - tg = mock.Mock() - sw = service_stack_watch.StackWatch(tg) - sw.start_watch_task(stack_id, self.ctx) - - # assert that add_timer IS called. - self.assertEqual([mock.call(stack_id, sw.periodic_watcher_task, - sid=stack_id)], - tg.add_timer.call_args_list) - - @mock.patch.object(service_stack_watch.stack_object.Stack, - 'get_all_by_owner_id') - @mock.patch.object(service_stack_watch.watch_rule_object.WatchRule, - 'get_all_by_stack') - @mock.patch.object(service_stack_watch.watch_rule_object.WatchRule, - 'update_by_id') - def test_periodic_watch_task_created_nested(self, watch_rule_update, - watch_rule_get_all_by_stack, - stack_get_all_by_owner_id): - stack_id = 90 - - def my_wr_get(cnxt, sid): - if sid == stack_id: - return [] - wr1 = mock.Mock() - wr1.id = 4 - wr1.state = rpc_api.WATCH_STATE_NODATA - return [wr1] - - watch_rule_get_all_by_stack.side_effect = my_wr_get - - def my_nested_get(cnxt, sid): - if sid == stack_id: - nested_stack = mock.Mock() - nested_stack.id = 55 - return [nested_stack] - return [] - - stack_get_all_by_owner_id.side_effect = my_nested_get - tg = mock.Mock() - sw = service_stack_watch.StackWatch(tg) - sw.start_watch_task(stack_id, self.ctx) - - # assert that add_timer IS called. - self.assertEqual([mock.call(stack_id, sw.periodic_watcher_task, - sid=stack_id)], - tg.add_timer.call_args_list) diff --git a/heat/tests/test_metadata_refresh.py b/heat/tests/test_metadata_refresh.py index 1741fe28ac..b638f3d366 100644 --- a/heat/tests/test_metadata_refresh.py +++ b/heat/tests/test_metadata_refresh.py @@ -219,7 +219,6 @@ class WaitConditionMetadataUpdateTest(common.HeatTestCase): def setUp(self): super(WaitConditionMetadataUpdateTest, self).setUp() self.man = service.EngineService('a-host', 'a-topic') - self.man.create_periodic_tasks() @mock.patch.object(nova.NovaClientPlugin, 'find_flavor_by_name_or_id') @mock.patch.object(glance.GlanceClientPlugin, 'find_image_by_name_or_id') diff --git a/heat/tests/test_rpc_client.py b/heat/tests/test_rpc_client.py index bb077e908e..7aa7fc3518 100644 --- a/heat/tests/test_rpc_client.py +++ b/heat/tests/test_rpc_client.py @@ -301,23 +301,6 @@ class EngineRpcAPITestCase(common.HeatTestCase): details={u'wordpress': []}, sync_call=True) - def test_create_watch_data(self): - self._test_engine_api('create_watch_data', 'call', - watch_name='watch1', - stats_data={}) - - def test_show_watch(self): - self._test_engine_api('show_watch', 'call', - watch_name='watch1') - - def test_show_watch_metric(self): - self._test_engine_api('show_watch_metric', 'call', - metric_namespace=None, metric_name=None) - - def test_set_watch_state(self): - self._test_engine_api('set_watch_state', 'call', - watch_name='watch1', state="xyz") - def test_list_software_configs(self): self._test_engine_api('list_software_configs', 'call', limit=mock.ANY, marker=mock.ANY) diff --git a/heat/tests/test_signal.py b/heat/tests/test_signal.py index ec4f17e102..87e54c9b76 100644 --- a/heat/tests/test_signal.py +++ b/heat/tests/test_signal.py @@ -537,9 +537,6 @@ class SignalTest(common.HeatTestCase): 'previous': 'SUCCESS'} ceilo_expected = 'alarm state changed from SUCCESS to foo (apples)' - watch_details = {'state': 'go_for_it'} - watch_expected = 'alarm state changed to go_for_it' - str_details = 'a string details' str_expected = str_details @@ -547,13 +544,11 @@ class SignalTest(common.HeatTestCase): none_expected = 'No signal details provided' # Test - for test_d in (ceilo_details, watch_details, str_details, - none_details): + for test_d in (ceilo_details, str_details, none_details): rsrc.signal(details=test_d) # Verify mock_add.assert_any_call('SIGNAL', 'COMPLETE', ceilo_expected) - mock_add.assert_any_call('SIGNAL', 'COMPLETE', watch_expected) mock_add.assert_any_call('SIGNAL', 'COMPLETE', str_expected) mock_add.assert_any_call('SIGNAL', 'COMPLETE', none_expected) diff --git a/heat/tests/test_watch.py b/heat/tests/test_watch.py deleted file mode 100644 index f22ca4639a..0000000000 --- a/heat/tests/test_watch.py +++ /dev/null @@ -1,978 +0,0 @@ -# -# 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 datetime - -import mock -from oslo_utils import timeutils - -from heat.common import exception -from heat.engine import stack -from heat.engine import template -from heat.engine import watchrule -from heat.objects import watch_rule -from heat.tests import common -from heat.tests import utils - - -class WatchData(object): - def __init__(self, data, created_at): - self.created_at = created_at - self.data = {'test_metric': {'Value': data, - 'Unit': 'Count'}} - - -class DummyAction(object): - signal = "DummyAction" - - -class WatchRuleTest(common.HeatTestCase): - stack_id = None - - def setUp(self): - super(WatchRuleTest, self).setUp() - - self.username = 'watchrule_test_user' - self.ctx = utils.dummy_context() - self.ctx.auth_token = 'abcd1234' - - self._setup_database() - - def _setup_database(self): - if self.stack_id is not None: - return - # Create a dummy stack in the DB as WatchRule instances - # must be associated with a stack - empty_tmpl = {'HeatTemplateFormatVersion': '2012-12-12'} - tmpl = template.Template(empty_tmpl) - stack_name = 'dummystack' - dummy_stack = stack.Stack(self.ctx, stack_name, tmpl) - dummy_stack.state_set(dummy_stack.CREATE, dummy_stack.COMPLETE, - 'Testing') - dummy_stack.store() - - self.stack_id = dummy_stack.id - - def _setup_action_mocks(self, mock_get_resource, now, - action_expected=True): - """Setup stubs for the action tests.""" - timeutils.set_time_override(now) - self.addCleanup(timeutils.clear_time_override) - - if action_expected: - dummy_action = DummyAction() - mock_get_resource.return_value = dummy_action - - def test_minimum(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'Minimum', - 'ComparisonOperator': 'LessThanOrEqualToThreshold', - 'Threshold': '50'} - - now = timeutils.utcnow() - last = now - datetime.timedelta(seconds=320) - data = [WatchData(77, now - datetime.timedelta(seconds=100))] - - # Test 1 - Values greater than 0 are normal - data.append(WatchData(53, now - datetime.timedelta(seconds=150))) - wr = watchrule.WatchRule(self.ctx, - 'testwatch', - rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - new_state = wr.get_alarm_state() - self.assertEqual('NORMAL', new_state) - - # Test 2 - data.append(WatchData(25, now - datetime.timedelta(seconds=250))) - wr = watchrule.WatchRule(self.ctx, - 'testwatch', - rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - new_state = wr.get_alarm_state() - self.assertEqual('ALARM', new_state) - - def test_maximum(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - last = now - datetime.timedelta(seconds=320) - data = [WatchData(7, now - datetime.timedelta(seconds=100))] - - # Test 1 - values less than 30 are normal - data.append(WatchData(23, now - datetime.timedelta(seconds=150))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('NORMAL', new_state) - - # Test 2 - data.append(WatchData(35, now - datetime.timedelta(seconds=150))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('ALARM', new_state) - - def test_samplecount(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'SampleCount', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '3'} - - now = timeutils.utcnow() - last = now - datetime.timedelta(seconds=320) - data = [WatchData(1, now - datetime.timedelta(seconds=100))] - - # Test 1 - 2 samples is normal - data.append(WatchData(1, now - datetime.timedelta(seconds=150))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('NORMAL', new_state) - - # Test 2 - 3 samples is an alarm - data.append(WatchData(1, now - datetime.timedelta(seconds=200))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('ALARM', new_state) - - # Test 3 - 3 samples (one old) is normal - data.pop(0) - data.append(WatchData(1, now - datetime.timedelta(seconds=400))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('NORMAL', new_state) - - def test_sum(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'Sum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '100'} - - now = timeutils.utcnow() - last = now - datetime.timedelta(seconds=320) - data = [WatchData(17, now - datetime.timedelta(seconds=100))] - - # Test 1 - values less than 40 are normal - data.append(WatchData(23, now - datetime.timedelta(seconds=150))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('NORMAL', new_state) - - # Test 2 - sum greater than 100 is an alarm - data.append(WatchData(85, now - datetime.timedelta(seconds=150))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('ALARM', new_state) - - def test_average(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'Average', - 'ComparisonOperator': 'GreaterThanThreshold', - 'Threshold': '100'} - - now = timeutils.utcnow() - last = now - datetime.timedelta(seconds=320) - data = [WatchData(117, now - datetime.timedelta(seconds=100))] - - # Test 1 - data.append(WatchData(23, now - datetime.timedelta(seconds=150))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('NORMAL', new_state) - - # Test 2 - data.append(WatchData(195, now - datetime.timedelta(seconds=250))) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=data, - stack_id=self.stack_id, - last_evaluated=last) - wr.now = now - new_state = wr.get_alarm_state() - self.assertEqual('ALARM', new_state) - - def test_load(self): - # Setup - # Insert two dummy watch rules into the DB - rule = {u'EvaluationPeriods': u'1', - u'AlarmActions': [u'WebServerRestartPolicy'], - u'AlarmDescription': u'Restart the WikiDatabase', - u'Namespace': u'system/linux', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'ServiceFailure'} - rules = [] - rules.append(watchrule.WatchRule(context=self.ctx, - watch_name='HttpFailureAlarm', - rule=rule, - watch_data=[], - stack_id=self.stack_id, - state='NORMAL')) - rules[0].store() - - rules.append(watchrule.WatchRule(context=self.ctx, - watch_name='AnotherWatch', - rule=rule, - watch_data=[], - stack_id=self.stack_id, - state='NORMAL')) - rules[1].store() - - # Test - for wn in ('HttpFailureAlarm', 'AnotherWatch'): - wr = watchrule.WatchRule.load(self.ctx, wn) - self.assertIsInstance(wr, watchrule.WatchRule) - self.assertEqual(wn, wr.name) - self.assertEqual('NORMAL', wr.state) - self.assertEqual(rule, wr.rule) - self.assertEqual(datetime.timedelta(seconds=int(rule['Period'])), - wr.timeperiod) - - def test_store(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmActions': [u'WebServerRestartPolicy'], - u'AlarmDescription': u'Restart the WikiDatabase', - u'Namespace': u'system/linux', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'ServiceFailure'} - - # Test - wr = watchrule.WatchRule(context=self.ctx, watch_name='storetest', - stack_id=self.stack_id, rule=rule) - wr.store() - - # Verify - dbwr = watch_rule.WatchRule.get_by_name(self.ctx, 'storetest') - self.assertIsNotNone(dbwr) - self.assertEqual('storetest', dbwr.name) - self.assertEqual(watchrule.WatchRule.NODATA, dbwr.state) - self.assertEqual(rule, dbwr.rule) - - def test_evaluate(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - timeutils.set_time_override(now) - self.addCleanup(timeutils.clear_time_override) - - # Test 1 - It's not time to evaluate, so should stay NODATA - last = now - datetime.timedelta(seconds=299) - data = WatchData(25, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - - actions = wr.evaluate() - self.assertEqual('NODATA', wr.state) - self.assertEqual([], actions) - - # Test 2 - now - last == Period, so should set NORMAL - last = now - datetime.timedelta(seconds=300) - data = WatchData(25, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - - actions = wr.evaluate() - self.assertEqual('NORMAL', wr.state) - self.assertEqual(now, wr.last_evaluated) - self.assertEqual([], actions) - - # Test 3 - Now data breaches Threshold, so should set ALARM - last = now - datetime.timedelta(seconds=300) - data = WatchData(35, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - - actions = wr.evaluate() - self.assertEqual('ALARM', wr.state) - self.assertEqual(now, wr.last_evaluated) - self.assertEqual([], actions) - - def test_evaluate_suspend(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - timeutils.set_time_override(now) - self.addCleanup(timeutils.clear_time_override) - - last = now - datetime.timedelta(seconds=300) - data = WatchData(35, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - wr.state_set(wr.SUSPENDED) - - # Test - actions = wr.evaluate() - self.assertEqual(wr.SUSPENDED, wr.state) - self.assertEqual([], actions) - - def test_evaluate_ceilometer_controlled(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - timeutils.set_time_override(now) - self.addCleanup(timeutils.clear_time_override) - - last = now - datetime.timedelta(seconds=300) - data = WatchData(35, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - wr.state_set(wr.CEILOMETER_CONTROLLED) - - # Test - actions = wr.evaluate() - self.assertEqual(wr.CEILOMETER_CONTROLLED, wr.state) - self.assertEqual([], actions) - - @mock.patch('heat.engine.stack.Stack.resource_by_refid') - def test_rule_actions_alarm_normal(self, mock_get_resource): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'AlarmActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - self._setup_action_mocks(mock_get_resource, now, - action_expected=False) - - # Set data so rule evaluates to NORMAL state - last = now - datetime.timedelta(seconds=300) - data = WatchData(25, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - actions = wr.evaluate() - self.assertEqual('NORMAL', wr.state) - self.assertEqual([], actions) - self.assertEqual(0, mock_get_resource.call_count) - - @mock.patch('heat.engine.stack.Stack.resource_by_refid') - def test_rule_actions_alarm_alarm(self, mock_get_resource): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'AlarmActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - self._setup_action_mocks(mock_get_resource, now) - - # Set data so rule evaluates to ALARM state - last = now - datetime.timedelta(seconds=300) - data = WatchData(35, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - actions = wr.evaluate() - self.assertEqual('ALARM', wr.state) - self.assertEqual(['DummyAction'], actions) - - # re-set last_evaluated so the rule will be evaluated again. - last = now - datetime.timedelta(seconds=300) - wr.last_evaluated = last - actions = wr.evaluate() - self.assertEqual('ALARM', wr.state) - self.assertEqual(['DummyAction'], actions) - self.assertGreater(mock_get_resource.call_count, 0) - - @mock.patch('heat.engine.stack.Stack.resource_by_refid') - def test_rule_actions_alarm_two_actions(self, mock_get_resource): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'AlarmActions': ['DummyAction', 'AnotherDummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - self._setup_action_mocks(mock_get_resource, now) - - # Set data so rule evaluates to ALARM state - last = now - datetime.timedelta(seconds=300) - data = WatchData(35, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - actions = wr.evaluate() - self.assertEqual('ALARM', wr.state) - self.assertEqual(['DummyAction', 'DummyAction'], actions) - self.assertGreater(mock_get_resource.call_count, 0) - - @mock.patch('heat.engine.stack.Stack.resource_by_refid') - def test_rule_actions_ok_alarm(self, mock_get_resource): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'OKActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - self._setup_action_mocks(mock_get_resource, now, action_expected=False) - - # On creation the rule evaluates to NODATA state - last = now - datetime.timedelta(seconds=300) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - actions = wr.evaluate() - self.assertEqual('NODATA', wr.state) - self.assertEqual([], actions) - - # Move time forward and add data below threshold so we transition from - # ALARM -> NORMAL, so evaluate() should output a 'DummyAction' - now = now + datetime.timedelta(seconds=300) - self._setup_action_mocks(mock_get_resource, now) - - data = WatchData(25, now - datetime.timedelta(seconds=150)) - wr.watch_data = [data] - - actions = wr.evaluate() - self.assertEqual('NORMAL', wr.state) - self.assertEqual(['DummyAction'], actions) - self.assertGreater(mock_get_resource.call_count, 0) - - @mock.patch('heat.engine.stack.Stack.resource_by_refid') - def test_rule_actions_nodata(self, mock_get_resource): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'InsufficientDataActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - self._setup_action_mocks(mock_get_resource, now, action_expected=False) - - # Set data so rule evaluates to ALARM state - last = now - datetime.timedelta(seconds=300) - data = WatchData(35, now - datetime.timedelta(seconds=150)) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[data], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - actions = wr.evaluate() - self.assertEqual('ALARM', wr.state) - self.assertEqual([], actions) - - # Move time forward and don't add data so we transition from - # ALARM -> NODATA, so evaluate() should output a 'DummyAction' - now = now + datetime.timedelta(seconds=300) - self._setup_action_mocks(mock_get_resource, now) - - actions = wr.evaluate() - self.assertEqual('NODATA', wr.state) - self.assertEqual(['DummyAction'], actions) - self.assertGreater(mock_get_resource.call_count, 0) - - @mock.patch('heat.engine.stack.Stack.resource_by_refid') - def test_to_ceilometer(self, mock_get_resource): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'CreateDataMetric'} - testdata = {u'CreateDataMetric': {"Unit": "Counter", "Value": "1"}} - - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule) - wr.store() - - mock_ceilometer_client = mock.MagicMock() - - self.ctx._clients = mock.MagicMock() - self.ctx._clients.client.return_value = mock_ceilometer_client - - # Test - wr._to_ceilometer(testdata) - - # Verify - self.assertEqual(1, mock_ceilometer_client.samples.create.call_count) - create_kw_args = mock_ceilometer_client.samples.create.call_args[1] - expected = { - 'counter_type': 'gauge', - 'counter_name': 'CreateDataMetric', - 'counter_volume': '1', - 'counter_unit': 'Counter', - 'resource_metadata': {}, - 'resource_id': None, - } - self.assertEqual(expected, create_kw_args) - - def test_create_watch_data(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'CreateDataMetric'} - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule) - - wr.store() - - # Test - data = {u'CreateDataMetric': {"Unit": "Counter", - "Value": "1", - "Dimensions": []}} - wr.create_watch_data(data) - - # Verify - obj_wr = watch_rule.WatchRule.get_by_name(self.ctx, 'create_data_test') - obj_wds = [wd for wd in obj_wr.watch_data] - self.assertEqual(data, obj_wds[0].data) - - # Note, would be good to write another datapoint and check it - # but sqlite seems to not interpret the backreference correctly - # so dbwr.watch_data is always a list containing only the latest - # datapoint. In non-test use on mysql this is not the case, we - # correctly get a list of all datapoints where watch_rule_id == - # watch_rule.id, so leave it as a single-datapoint test for now. - - def test_create_watch_data_suspended(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'MetricName': u'CreateDataMetric'} - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule, - state=watchrule.WatchRule.SUSPENDED) - - wr.store() - - # Test - data = {u'CreateDataMetric': {"Unit": "Counter", - "Value": "1", - "Dimensions": []}} - wr.create_watch_data(data) - - # Verify - obj_wr = watch_rule.WatchRule.get_by_name(self.ctx, 'create_data_test') - obj_wds = [wd for wd in obj_wr.watch_data] - self.assertEqual([], obj_wds) - - def test_create_watch_data_match(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'Dimensions': [{u'Name': 'AutoScalingGroupName', - u'Value': 'group_x'}], - u'MetricName': u'CreateDataMetric'} - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule) - wr.store() - - # Test - data = {u'CreateDataMetric': {"Unit": "Counter", - "Value": "1", - "Dimensions": [{u'AutoScalingGroupName': - u'group_x'}]}} - self.assertTrue(watchrule.rule_can_use_sample(wr, data)) - - def test_create_watch_data_match_2(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'Dimensions': [{u'Name': 'AutoScalingGroupName', - u'Value': 'group_x'}], - u'MetricName': u'CreateDataMetric'} - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule) - wr.store() - - # Test - data = {u'not_interesting': {"Unit": "Counter", - "Value": "1", - "Dimensions": [ - {u'AutoScalingGroupName': - u'group_x'}]}, - u'CreateDataMetric': {"Unit": "Counter", - "Value": "1", - "Dimensions": [ - {u'AutoScalingGroupName': - u'group_x'}]}} - self.assertTrue(watchrule.rule_can_use_sample(wr, data)) - - def test_create_watch_data_match_3(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'Dimensions': [{u'Name': 'AutoScalingGroupName', - u'Value': 'group_x'}], - u'MetricName': u'CreateDataMetric'} - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule) - wr.store() - - # Test - data = {u'CreateDataMetric': {"Unit": "Counter", - "Value": "1", - "Dimensions": [ - {u'AutoScalingGroupName': - u'group_x'}]}} - self.assertTrue(watchrule.rule_can_use_sample(wr, data)) - - def test_create_watch_data_not_match_metric(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'Dimensions': [{u'Name': 'AutoScalingGroupName', - u'Value': 'group_x'}], - u'MetricName': u'CreateDataMetric'} - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule) - wr.store() - - # Test - data = {u'not_this': {"Unit": "Counter", - "Value": "1", - "Dimensions": [ - {u'AutoScalingGroupName': - u'group_x'}]}, - u'nor_this': {"Unit": "Counter", - "Value": "1", - "Dimensions": [ - {u'AutoScalingGroupName': - u'group_x'}]}} - self.assertFalse(watchrule.rule_can_use_sample(wr, data)) - - def test_create_watch_data_not_match_dimensions(self): - # Setup - rule = {u'EvaluationPeriods': u'1', - u'AlarmDescription': u'test alarm', - u'Period': u'300', - u'ComparisonOperator': u'GreaterThanThreshold', - u'Statistic': u'SampleCount', - u'Threshold': u'2', - u'Dimensions': [{u'Name': 'AutoScalingGroupName', - u'Value': 'group_x'}], - u'MetricName': u'CreateDataMetric'} - wr = watchrule.WatchRule(context=self.ctx, - watch_name='create_data_test', - stack_id=self.stack_id, - rule=rule) - wr.store() - - # Test - data = {u'CreateDataMetric': {"Unit": "Counter", - "Value": "1", - "Dimensions": [ - {u'wrong_key': - u'group_x'}]}} - self.assertFalse(watchrule.rule_can_use_sample(wr, data)) - - def test_destroy(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'AlarmActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - last = timeutils.utcnow() - wr = watchrule.WatchRule(context=self.ctx, - watch_name='testwatch_destroy', - rule=rule, - watch_data=[], - stack_id=self.stack_id, - last_evaluated=last) - - wr.store() - - # Sanity Check - check = watchrule.WatchRule.load(context=self.ctx, - watch_name='testwatch_destroy') - self.assertIsInstance(check, watchrule.WatchRule) - - # Test - wr.destroy() - ex = self.assertRaises(exception.EntityNotFound, - watchrule.WatchRule.load, - context=self.ctx, - watch_name='testwatch_destroy') - self.assertEqual('Watch Rule', ex.kwargs.get('entity')) - - def test_state_set(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'AlarmActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - last = timeutils.utcnow() - watcher = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch_set_state", - rule=rule, - watch_data=[], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - watcher.state_set(watcher.SUSPENDED) - - # Verify - self.assertEqual(watcher.SUSPENDED, watcher.state) - check = watchrule.WatchRule.load(context=self.ctx, - watch_name='testwatch_set_state') - self.assertEqual(watchrule.WatchRule.SUSPENDED, check.state) - - @mock.patch('heat.engine.stack.Stack.resource_by_refid') - def test_set_watch_state(self, mock_get_resource): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'AlarmActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - self._setup_action_mocks(mock_get_resource, now) - - # Set data so rule evaluates to ALARM state - last = now - datetime.timedelta(seconds=200) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - actions = wr.set_watch_state(watchrule.WatchRule.NODATA) - self.assertEqual([], actions) - - actions = wr.set_watch_state(watchrule.WatchRule.NORMAL) - self.assertEqual([], actions) - - actions = wr.set_watch_state(watchrule.WatchRule.ALARM) - self.assertEqual(['DummyAction'], actions) - self.assertGreater(mock_get_resource.call_count, 0) - - def test_set_watch_state_invalid(self): - # Setup - rule = {'EvaluationPeriods': '1', - 'MetricName': 'test_metric', - 'AlarmActions': ['DummyAction'], - 'Period': '300', - 'Statistic': 'Maximum', - 'ComparisonOperator': 'GreaterThanOrEqualToThreshold', - 'Threshold': '30'} - - now = timeutils.utcnow() - - last = now - datetime.timedelta(seconds=200) - wr = watchrule.WatchRule(context=self.ctx, - watch_name="testwatch", - rule=rule, - watch_data=[], - stack_id=self.stack_id, - last_evaluated=last) - - # Test - self.assertRaises(ValueError, wr.set_watch_state, None) - self.assertRaises(ValueError, wr.set_watch_state, "BADSTATE")