heat/heat/engine/resources/alarm_base.py

269 lines
9.9 KiB
Python

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from heat.common.i18n import _
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine import support
from urllib import parse
COMMON_PROPERTIES = (
ALARM_ACTIONS, OK_ACTIONS, INSUFFICIENT_DATA_ACTIONS,
ALARM_QUEUES, OK_QUEUES, INSUFFICIENT_DATA_QUEUES,
REPEAT_ACTIONS, DESCRIPTION, ENABLED, TIME_CONSTRAINTS, SEVERITY,
) = (
'alarm_actions', 'ok_actions', 'insufficient_data_actions',
'alarm_queues', 'ok_queues', 'insufficient_data_queues',
'repeat_actions', 'description', 'enabled', 'time_constraints', 'severity',
)
INTERNAL_PROPERTIES = (ALARM_QUEUES, OK_QUEUES, INSUFFICIENT_DATA_QUEUES)
_TIME_CONSTRAINT_KEYS = (
NAME, START, DURATION, TIMEZONE, TIME_CONSTRAINT_DESCRIPTION,
) = (
'name', 'start', 'duration', 'timezone', 'description',
)
common_properties_schema = {
DESCRIPTION: properties.Schema(
properties.Schema.STRING,
_('Description for the alarm.'),
update_allowed=True
),
ENABLED: properties.Schema(
properties.Schema.BOOLEAN,
_('True if alarm evaluation/actioning is enabled.'),
default='true',
update_allowed=True
),
ALARM_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'alarm.'),
update_allowed=True
),
OK_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'ok.'),
update_allowed=True
),
INSUFFICIENT_DATA_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'insufficient-data.'),
update_allowed=True
),
ALARM_QUEUES: properties.Schema(
properties.Schema.LIST,
_('A list of Zaqar queues to post to when state transitions to '
'alarm.'),
support_status=support.SupportStatus(version='8.0.0'),
schema=properties.Schema(
properties.Schema.STRING,
constraints=[constraints.CustomConstraint('zaqar.queue')]
),
default=[],
update_allowed=True
),
OK_QUEUES: properties.Schema(
properties.Schema.LIST,
_('A list of Zaqar queues to post to when state transitions to '
'ok.'),
support_status=support.SupportStatus(version='8.0.0'),
schema=properties.Schema(
properties.Schema.STRING,
constraints=[constraints.CustomConstraint('zaqar.queue')]
),
default=[],
update_allowed=True
),
INSUFFICIENT_DATA_QUEUES: properties.Schema(
properties.Schema.LIST,
_('A list of Zaqar queues to post to when state transitions to '
'insufficient-data.'),
support_status=support.SupportStatus(version='8.0.0'),
schema=properties.Schema(
properties.Schema.STRING,
constraints=[constraints.CustomConstraint('zaqar.queue')]
),
default=[],
update_allowed=True
),
REPEAT_ACTIONS: properties.Schema(
properties.Schema.BOOLEAN,
_("False to trigger actions when the threshold is reached AND "
"the alarm's state has changed. By default, actions are called "
"each time the threshold is reached."),
default='true',
update_allowed=True
),
SEVERITY: properties.Schema(
properties.Schema.STRING,
_('Severity of the alarm.'),
default='low',
constraints=[
constraints.AllowedValues(['low', 'moderate', 'critical'])
],
update_allowed=True,
support_status=support.SupportStatus(version='5.0.0'),
),
TIME_CONSTRAINTS: properties.Schema(
properties.Schema.LIST,
_('Describe time constraints for the alarm. '
'Only evaluate the alarm if the time at evaluation '
'is within this time constraint. Start point(s) of '
'the constraint are specified with a cron expression, '
'whereas its duration is given in seconds.'
),
schema=properties.Schema(
properties.Schema.MAP,
schema={
NAME: properties.Schema(
properties.Schema.STRING,
_("Name for the time constraint."),
required=True
),
START: properties.Schema(
properties.Schema.STRING,
_("Start time for the time constraint. "
"A CRON expression property."),
constraints=[
constraints.CustomConstraint(
'cron_expression')
],
required=True
),
TIME_CONSTRAINT_DESCRIPTION: properties.Schema(
properties.Schema.STRING,
_("Description for the time constraint."),
),
DURATION: properties.Schema(
properties.Schema.INTEGER,
_("Duration for the time constraint."),
constraints=[
constraints.Range(min=0)
],
required=True
),
TIMEZONE: properties.Schema(
properties.Schema.STRING,
_("Timezone for the time constraint "
"(eg. 'Asia/Taipei', 'Europe/Amsterdam')."),
constraints=[
constraints.CustomConstraint('timezone')
],
)
}
),
support_status=support.SupportStatus(version='5.0.0'),
default=[],
)
}
NOVA_METERS = ['instance', 'memory', 'memory.usage', 'memory.resident',
'cpu', 'cpu_util', 'vcpus',
'disk.read.requests', 'disk.read.requests.rate',
'disk.write.requests', 'disk.write.requests.rate',
'disk.read.bytes', 'disk.read.bytes.rate',
'disk.write.bytes', 'disk.write.bytes.rate',
'disk.device.read.requests', 'disk.device.read.requests.rate',
'disk.device.write.requests', 'disk.device.write.requests.rate',
'disk.device.read.bytes', 'disk.device.read.bytes.rate',
'disk.device.write.bytes', 'disk.device.write.bytes.rate',
'disk.root.size', 'disk.ephemeral.size',
'network.incoming.bytes', 'network.incoming.bytes.rate',
'network.outgoing.bytes', 'network.outgoing.bytes.rate',
'network.incoming.packets', 'network.incoming.packets.rate',
'network.outgoing.packets', 'network.outgoing.packets.rate']
class BaseAlarm(resource.Resource):
"""Base Alarm Manager."""
default_client_name = 'aodh'
entity = 'alarm'
alarm_type = 'threshold'
QUERY_FACTOR_FIELDS = (
QF_FIELD, QF_OP, QF_VALUE, QF_TYPE,
) = (
'field', 'op', 'value', 'type',
)
QF_OP_VALS = constraints.AllowedValues(['le', 'ge', 'eq',
'lt', 'gt', 'ne'])
QF_TYPE_VALS = constraints.AllowedValues(['integer', 'float', 'string',
'boolean', 'datetime'])
def actions_to_urls(self, props):
kwargs = dict(props)
def get_urls(action_type, queue_type):
for act in kwargs.get(action_type) or []:
# if the action is a resource name
# we ask the destination resource for an alarm url.
# the template writer should really do this in the
# template if possible with:
# {Fn::GetAtt: ['MyAction', 'AlarmUrl']}
if act in self.stack:
yield self.stack[act].FnGetAtt('AlarmUrl')
elif act:
yield act
for queue in kwargs.pop(queue_type, []):
query = {'queue_name': queue}
yield 'trust+zaqar://?%s' % parse.urlencode(query)
action_props = {arg_types[0]: list(get_urls(*arg_types))
for arg_types in ((ALARM_ACTIONS, ALARM_QUEUES),
(OK_ACTIONS, OK_QUEUES),
(INSUFFICIENT_DATA_ACTIONS,
INSUFFICIENT_DATA_QUEUES))}
kwargs.update(action_props)
return kwargs
def _reformat_properties(self, props):
rule = {}
# Note that self.PROPERTIES includes only properties specific to the
# child class; BaseAlarm properties are not included.
for name in self.PROPERTIES:
if name in props:
rule[name] = props.pop(name)
if rule:
props['%s_rule' % self.alarm_type] = rule
return props
def handle_suspend(self):
if self.resource_id is not None:
alarm_update = {'enabled': False}
self.client().alarm.update(self.resource_id,
alarm_update)
def handle_resume(self):
if self.resource_id is not None:
alarm_update = {'enabled': True}
self.client().alarm.update(self.resource_id,
alarm_update)
def handle_check(self):
self.client().alarm.get(self.resource_id)