aodh notifier

Change-Id: I025f58bfb898d6252fa61153de4866b741b26464
This commit is contained in:
Idan Hefetz 2016-03-16 08:43:18 +00:00
parent 211afaf4a1
commit c832df92bf
27 changed files with 692 additions and 52 deletions

View File

@ -182,6 +182,7 @@ function start_vitrage {
fi
run_process vitrage-graph "$VITRAGE_BIN_DIR/vitrage-graph --config-file $VITRAGE_CONF"
run_process vitrage-notifier "$VITRAGE_BIN_DIR/vitrage-notifier --config-file $VITRAGE_CONF"
}
# stop_vitrage() - Stop running processes
@ -191,7 +192,7 @@ function stop_vitrage {
restart_apache_server
fi
# Kill the vitrage screen windows
for serv in vitrage-api vitrage-graph; do
for serv in vitrage-api vitrage-graph vitrage-notifier; do
stop_process $serv
done
}

View File

@ -3,6 +3,8 @@
enable_service vitrage-api
# Graph
enable_service vitrage-graph
# Notifier
enable_service vitrage-notifier
# Default directories

View File

@ -0,0 +1,67 @@
===============================
Vitrage Notifier plugins - AODH
===============================
Overview
========
The Evaluator performs root cause analysis on the Vitrage Graph and may determine that an alarm should be created, deleted or otherwise updated.
Other components are notified of such changes by the Vitrage Notifier service. Among others, Vitrage Notifier is responsible for handling Aodh Alarms.
This document describes the implementation of Vitrage Notifier infrastructure and specifically notifying Aodh on Vitrage alarms.
Design
======
::
+------------------+ +--------+
| Vitrage | | Message| +------------------+
| Evaluator ---------->| Bus --------> Vitrage Notifier |
+------------------+ | | +------------------+
| | | | |
| | +---|------+ | |
| | | Aodh |-|-+ |
| | | notifier | |-|--+
| | +--| plugin | | |
| | | +----------+ | |
+--------+ | +-----------+ |
| +------------+
+------------------+ |
| Aodh <-----------------------+
+------------------+
...
Evaluator bus notifications
---------------------------
Vitrage Evaluator will use the **vitrage.evaluator** message bus topic, and will post messages as follows:
- message of type **vitrage.deduce_alarm.activate** :
* name - is the alarm name in vitrage
* severity - is the alarm severity
* affected_resource_id - is the openstack id of the resource on which the alarm was raised
- **vitrage.deduce_alarm.deactivate**
* id - is the alarm id
Notifier
========
- Is a new running service
- Receives notifications from the message bus
- Holds instances of all the plugins
- Upon a received notification, calls 'notify(msg)' for all the plugins
- Each plugin is responsible of how and whether to process the notification
Aodh Plugin
===========
Vitrage alarms should be reflected as possible in Aodh. the aodh plugin has ceilometer client by which it can send rest calls to aodh
Handle vitrage.deduce_alarm.activate:
-------------------------------------
Create an event alarm with the specified severity, where the alarm name is vitrage_alarm_name+resource_id so to be unique
Handle vitrage.deduce_alarm.deactivate:
---------------------------------------
delete an event alarm with the specified id

View File

@ -26,11 +26,15 @@ setup-hooks =
console_scripts =
vitrage-api = vitrage.cmd.api:main
vitrage-graph = vitrage.cmd.graph:main
vitrage-notifier = vitrage.cmd.notifier:main
oslo.config.opts =
vitrage = vitrage.opts:list_opts
plugins = vitrage.opts:plugins_opts
keystoneauth1.plugin =
password-vitrage-legacy = vitrage.keystone_client:LegacyVitrageKeystoneLoader
vitrage.transformers =
nova.instance = vitrage.entity_graph.transformer.nova_transformer.instance_transformer.InstanceTransformer
nova.host = vitrage.entity_graph.transformer.nova_transformer.host_transformer.HostTransformer

35
vitrage/clients.py Normal file
View File

@ -0,0 +1,35 @@
# Copyright 2016 - Nokia
#
# 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 ceilometerclient import client as cm_client
from oslo_log import log
from vitrage import keystone_client
LOG = log.getLogger(__name__)
def ceilometer_client(conf):
"""Get an instance of ceilometer client"""
auth_config = conf.service_credentials
try:
client = cm_client.get_client(
version=2,
session=keystone_client.get_session(conf),
region_name=auth_config.region_name,
interface=auth_config.interface,
)
LOG.info('Ceilometer client created')
return client
except Exception as e:
LOG.exception('Create Ceilometer client - Got Exception: %s', e)

30
vitrage/cmd/notifier.py Normal file
View File

@ -0,0 +1,30 @@
# Copyright 2016 - Nokia
#
# 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_service import service as os_service
import sys
from vitrage.notifier.service import VitrageNotifierService
from vitrage import service
def main():
conf = service.prepare_service()
launcher = os_service.ServiceLauncher(conf)
launcher.launch_service(VitrageNotifierService(conf))
launcher.wait()
if __name__ == "__main__":
sys.exit(main())

View File

@ -81,3 +81,8 @@ class EventAction(object):
DELETE_RELATIONSHIP = 'delete_relationship'
UPDATE_RELATIONSHIP = 'update_relationship'
END_MESSAGE = 'end_message'
class NotifierEventTypes(object):
ACTIVATE_ALARM_EVENT = 'vitrage.deduced_alarm.activate'
DEACTIVATE_ALARM_EVENT = 'vitrage.deduced_alarm.deactivate'

View File

@ -62,14 +62,12 @@ class EntityGraph(NXGraph):
def mark_vertex_as_deleted(self, vertex):
"""Marks the vertex as is deleted, and updates deletion timestamp"""
# TODO(Alexey): change the update_vertex so it will raise a trigger
vertex[VProps.IS_DELETED] = True
vertex[VProps.UPDATE_TIMESTAMP] = str(utcnow())
self.update_vertex(vertex)
def mark_edge_as_deleted(self, edge):
"""Marks the edge as is deleted, and updates delete timestamp"""
# TODO(Alexey): change the update_edge so it will raise a trigger
edge[EProps.IS_DELETED] = True
edge[EProps.UPDATE_TIMESTAMP] = str(utcnow())
self.update_edge(edge)

View File

@ -26,37 +26,31 @@ LOG = log.getLogger(__name__)
class VitrageGraphService(os_service.Service):
def __init__(self, cfg, event_queue, entity_graph, initialization_status):
def __init__(self, conf, event_queue, entity_graph, initialization_status):
super(VitrageGraphService, self).__init__()
self.queue = event_queue
self.cfg = cfg
self.processor = proc.Processor(self.cfg,
initialization_status,
e_graph=entity_graph)
self.scenario_repo = ScenarioRepository(cfg)
self.evaluator = ScenarioEvaluator(entity_graph,
self.scenario_repo,
event_queue)
self.conf = conf
self.scenario_repo = ScenarioRepository(conf)
self.processor = proc.Processor(
conf,
initialization_status,
e_graph=entity_graph)
self.evaluator = ScenarioEvaluator(
conf,
entity_graph,
self.scenario_repo,
event_queue)
def start(self):
LOG.info("Start VitrageGraphService")
LOG.info("Vitrage Graph Service - Starting...")
super(VitrageGraphService, self).start()
self.tg.add_timer(1.0, self._process_event_non_blocking)
LOG.info("Finish start VitrageGraphService")
LOG.info("Vitrage Graph Service - Started!")
def stop(self, graceful=False):
LOG.info("Stop VitrageGraphService")
# TODO(Alexey): check if we need this command here
self.tg.stop_timers()
super(VitrageGraphService, self).stop()
LOG.info("Finish stop VitrageGraphService")
LOG.info("Vitrage Graph Service - Stopping...")
super(VitrageGraphService, self).stop(graceful)
LOG.info("Vitrage Graph Service - Stopped!")
def _process_events(self):
while True:

View File

@ -21,4 +21,9 @@ OPTS = [
default='/etc/vitrage/templates',
help='A path for the templates used by the evaluator'
),
cfg.StrOpt('notifier_topic',
default='vitrage.evaluator',
help='The topic that vitrage-evaluator uses for alarm '
'notifications messages.'),
]

View File

@ -41,9 +41,10 @@ LOG = logging.getLogger(__name__)
class ActionExecutor(object):
def __init__(self, event_queue):
def __init__(self, event_queue, notifier=None):
self.event_queue = event_queue
self.action_recipes = ActionExecutor._register_action_recipes()
self.notifier = notifier
self.action_step_defs = {
ADD_VERTEX: self.add_vertex,
@ -105,7 +106,10 @@ class ActionExecutor(object):
self.event_queue.put(event)
def notify(self, params):
pass
if self.notifier:
event_type = params['event_type']
del params['event_type']
self.notifier.notify(event_type, params)
@staticmethod
def _add_default_properties(event):

View File

@ -12,12 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
from vitrage.common.constants import NotifierEventTypes
from vitrage.common.constants import VertexProperties as VProps
from vitrage.evaluator.actions.recipes.action_steps import ADD_VERTEX
from vitrage.evaluator.actions.recipes.action_steps import NOTIFY
from vitrage.evaluator.actions.recipes.action_steps import REMOVE_VERTEX
from vitrage.evaluator.actions.recipes import base
from vitrage.evaluator.actions.recipes.base import ActionStepWrapper
from vitrage.evaluator.template_fields import TemplateFields as TFields
from vitrage.synchronizer.plugins.base.alarm.properties \
import AlarmProperties as AlarmProps
@ -31,7 +33,9 @@ class RaiseAlarm(base.Recipe):
params[VProps.STATE] = AlarmProps.ALARM_ACTIVE_STATE
add_vertex_step = ActionStepWrapper(ADD_VERTEX, params)
notify_step = RaiseAlarm._get_notify_step()
notify_step = RaiseAlarm._get_notify_step(
action_spec,
NotifierEventTypes.ACTIVATE_ALARM_EVENT)
return [add_vertex_step, notify_step]
@ -42,15 +46,22 @@ class RaiseAlarm(base.Recipe):
params[VProps.STATE] = AlarmProps.ALARM_INACTIVE_STATE
remove_vertex_step = ActionStepWrapper(REMOVE_VERTEX, params)
notify_step = RaiseAlarm._get_notify_step()
notify_step = RaiseAlarm._get_notify_step(
action_spec,
NotifierEventTypes.DEACTIVATE_ALARM_EVENT)
return [remove_vertex_step, notify_step]
@staticmethod
def _get_notify_step():
def _get_notify_step(action_spec, event_type):
# TODO(lhartal): add params
return ActionStepWrapper(NOTIFY, {})
notify_params = {
'affected_resource_id': action_spec.targets[TFields.TARGET],
'name': action_spec.properties[TFields.ALARM_NAME],
'event_type': event_type,
}
notify_step = ActionStepWrapper(NOTIFY, notify_params)
return notify_step
@staticmethod
def _get_vertex_params(action_spec):

View File

@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from vitrage.common.constants import VertexProperties as VProps
from vitrage.evaluator.actions.recipes.action_steps import NOTIFY
from vitrage.evaluator.actions.recipes.action_steps import UPDATE_VERTEX
from vitrage.evaluator.actions.recipes import base
from vitrage.evaluator.actions.recipes.base import ActionStepWrapper
@ -28,9 +27,7 @@ class SetState(base.Recipe):
action_spec.targets[TFields.TARGET],
action_spec.properties[TFields.STATE])
notify_step = SetState._get_notify_step()
return [update_vertex_step, notify_step]
return [update_vertex_step]
@staticmethod
def get_undo_recipe(action_spec):
@ -39,9 +36,7 @@ class SetState(base.Recipe):
action_spec.targets[TFields.TARGET],
None)
notify_step = SetState._get_notify_step()
return [update_vertex_step, notify_step]
return [update_vertex_step]
@staticmethod
def _get_update_vertex_step(target_id, vitrage_state):
@ -54,9 +49,3 @@ class SetState(base.Recipe):
update_vertex_params)
return update_vertex_step
@staticmethod
def _get_notify_step():
# TODO(lhartal): add params
return ActionStepWrapper(NOTIFY, {})

View File

@ -25,6 +25,7 @@ from vitrage.graph.algo_driver.algorithm import Mapping
from vitrage.graph import create_algorithm
from vitrage.graph import create_graph
from vitrage.graph.driver import Vertex
from vitrage.messaging import VitrageNotifier
LOG = log.getLogger(__name__)
@ -32,11 +33,14 @@ LOG = log.getLogger(__name__)
class ScenarioEvaluator(object):
def __init__(self, entity_graph, scenario_repo, event_queue):
def __init__(self, conf, entity_graph, scenario_repo, event_queue):
self._entity_graph = entity_graph
self._graph_algs = create_algorithm(entity_graph)
self._scenario_repo = scenario_repo
self._action_executor = ActionExecutor(event_queue)
self._notifier = VitrageNotifier(conf,
'vitrage.evaluator',
conf.evaluator.notifier_topic)
self._action_executor = ActionExecutor(event_queue, self._notifier)
self._entity_graph.subscribe(self.process_event)
self.enabled = True

170
vitrage/keystone_client.py Normal file
View File

@ -0,0 +1,170 @@
#
# Copyright 2015 eNovance <licensing@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from keystoneauth1 import exceptions as ka_exception
from keystoneauth1 import identity as ka_identity
from keystoneauth1 import loading as ka_loading
from keystoneclient.v3 import client as ks_client_v3
from oslo_config import cfg
from oslo_log import log
LOG = log.getLogger(__name__)
CFG_GROUP = "service_credentials"
def get_session(conf, requests_session=None):
"""Get a vitrage service credentials auth session."""
auth_plugin = ka_loading.load_auth_from_conf_options(conf, CFG_GROUP)
session = ka_loading.load_session_from_conf_options(
conf, CFG_GROUP, auth=auth_plugin, session=requests_session
)
return session
def get_client(conf, trust_id=None, requests_session=None):
"""Return a client for keystone v3 endpoint, optionally using a trust."""
session = get_session(conf, requests_session=requests_session)
return ks_client_v3.Client(session=session, trust_id=trust_id)
def get_service_catalog(client):
return client.session.auth.get_access(client.session).service_catalog
def get_auth_token(client):
return client.session.auth.get_access(client.session).auth_token
def get_client_on_behalf_user(conf, auth_plugin, trust_id=None,
requests_session=None):
"""Return a client for keystone v3 endpoint, optionally using a trust."""
session = ka_loading.load_session_from_conf_options(
conf, CFG_GROUP, auth=auth_plugin, session=requests_session
)
return ks_client_v3.Client(session=session, trust_id=trust_id)
def create_trust_id(conf, trustor_user_id, trustor_project_id, roles,
auth_plugin):
"""Create a new trust using the vitrage service user."""
admin_client = get_client(conf)
trustee_user_id = admin_client.auth_ref.user_id
client = get_client_on_behalf_user(conf, auth_plugin=auth_plugin)
trust = client.trusts.create(trustor_user=trustor_user_id,
trustee_user=trustee_user_id,
project=trustor_project_id,
impersonation=True,
role_names=roles)
return trust.id
def delete_trust_id(conf, trust_id, auth_plugin):
"""Delete a trust previously setup for the vitrage user."""
client = get_client_on_behalf_user(conf, auth_plugin=auth_plugin)
try:
client.trusts.delete(trust_id)
except ka_exception.NotFound:
pass
OPTS = [
cfg.StrOpt('region-name',
default=os.environ.get('OS_REGION_NAME'),
deprecated_name="os-region-name",
help='Region name to use for OpenStack service endpoints.'),
cfg.StrOpt('interface',
default=os.environ.get(
'OS_INTERFACE', os.environ.get('OS_ENDPOINT_TYPE',
'public')),
deprecated_name="os-endpoint-type",
choices=('public', 'internal', 'admin', 'auth', 'publicURL',
'internalURL', 'adminURL'),
help='Type of endpoint in Identity service catalog to use for '
'communication with OpenStack services.'),
]
def register_keystoneauth_opts(conf):
ka_loading.register_auth_conf_options(conf, CFG_GROUP)
ka_loading.register_session_conf_options(
conf, CFG_GROUP,
deprecated_opts={'cacert': [
cfg.DeprecatedOpt('os-cacert', group=CFG_GROUP),
cfg.DeprecatedOpt('os-cacert', group="DEFAULT")]
})
conf.set_default("auth_type", default="password-vitrage-legacy",
group=CFG_GROUP)
def setup_keystoneauth(conf):
if conf[CFG_GROUP].auth_type == "password-vitrage-legacy":
LOG.warn("Value 'password-vitrage-legacy' for '[%s]/auth_type' "
"is deprecated. And will be removed in Vitrage 2.0. "
"Use 'password' instead.",
CFG_GROUP)
ka_loading.load_auth_from_conf_options(conf, CFG_GROUP)
class LegacyVitrageKeystoneLoader(ka_loading.BaseLoader):
@property
def plugin_class(self):
return ka_identity.V2Password
def get_options(self):
options = super(LegacyVitrageKeystoneLoader, self).get_options()
options.extend([
ka_loading.Opt(
'os-username',
default=os.environ.get('OS_USERNAME', 'vitrage'),
help='User name to use for OpenStack service access.'),
ka_loading.Opt(
'os-password',
secret=True,
default=os.environ.get('OS_PASSWORD', 'admin'),
help='Password to use for OpenStack service access.'),
ka_loading.Opt(
'os-tenant-id',
default=os.environ.get('OS_TENANT_ID', ''),
help='Tenant ID to use for OpenStack service access.'),
ka_loading.Opt(
'os-tenant-name',
default=os.environ.get('OS_TENANT_NAME', 'admin'),
help='Tenant name to use for OpenStack service access.'),
ka_loading.Opt(
'os-auth-url',
default=os.environ.get('OS_AUTH_URL',
'http://localhost:5000/v2.0'),
help='Auth URL to use for OpenStack service access.'),
])
return options
def load_from_options(self, **kwargs):
options_map = {
'os_auth_url': 'auth_url',
'os_username': 'username',
'os_password': 'password',
'os_tenant_name': 'tenant_name',
'os_tenant_id': 'tenant_id',
}
identity_kwargs = dict((options_map[o.dest],
kwargs.get(o.dest) or o.default)
for o in self.get_options()
if o.dest in options_map)
return self.plugin_class(**identity_kwargs)

77
vitrage/messaging.py Normal file
View File

@ -0,0 +1,77 @@
# Copyright 2016 - Nokia
#
# 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
import oslo_messaging
# from oslo_messaging import serializer as oslo_serializer
LOG = log.getLogger(__name__)
DEFAULT_URL = "__default__"
TRANSPORTS = {}
# _SERIALIZER = oslo_serializer.JsonPayloadSerializer()
def setup():
# Set the default exchange under which topics are scoped
oslo_messaging.set_transport_defaults('vitrage')
def get_transport(conf, url=None, optional=False, cache=True):
"""Initialise the oslo_messaging layer."""
global TRANSPORTS, DEFAULT_URL
cache_key = url or DEFAULT_URL
transport = TRANSPORTS.get(cache_key)
if not transport or not cache:
try:
transport = oslo_messaging.get_transport(conf, url)
except oslo_messaging.InvalidTransportURL as e:
if not optional or e.url:
# NOTE(sileht): oslo_messaging is configured but unloadable
# so reraise the exception
raise
return None
else:
if cache:
TRANSPORTS[cache_key] = transport
return transport
def get_notification_listener(transport, targets, endpoints,
allow_requeue=False):
"""Return a configured oslo_messaging notification listener."""
return oslo_messaging.get_notification_listener(
transport, targets, endpoints, executor='threading',
allow_requeue=allow_requeue)
class VitrageNotifier(object):
"""Allows writing to message bus"""
def __init__(self, conf, publisher_id, topic):
transport = get_transport(conf)
self.notifier = oslo_messaging.Notifier(
transport,
driver='messagingv2',
publisher_id=publisher_id,
topic=topic)
def notify(self, event_type, data):
LOG.info('notify : ' + event_type + ' ' + str(data))
if self.notifier:
try:
self.notifier.info({}, event_type, data)
except Exception as e:
LOG.exception('Notifier cannot notify - %e', e)
else:
LOG.error('Notifier cannot notify')

View File

@ -0,0 +1,15 @@
# Copyright 2016 - Nokia
#
# 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.
pass

View File

@ -0,0 +1,15 @@
# Copyright 2016 - Nokia
#
# 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.
pass

View File

@ -0,0 +1,15 @@
# Copyright 2016 - Nokia
#
# 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.
pass

View File

@ -0,0 +1,95 @@
# Copyright 2016 - Nokia
#
# 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 random
import string
from oslo_log import log as logging
from vitrage import clients
from vitrage.common.constants import NotifierEventTypes
from vitrage.notifier.plugins.base import NotifierBase
LOG = logging.getLogger(__name__)
def aodh_alarm_name_generator(name, unique=None, size=6,
chars=string.ascii_uppercase + string.digits):
if unique:
return name.join(['_', unique])
else:
unique = ''.join(random.choice(chars) for _ in range(size))
return name.join(['_', unique])
class AodhNotifier(NotifierBase):
def __init__(self, conf):
super(AodhNotifier, self).__init__(conf)
self.client = clients.ceilometer_client(conf)
def process_event(self, data, event_type):
if event_type == NotifierEventTypes.DEACTIVATE_ALARM_EVENT:
self._deactivate_aodh_alarm(data)
elif event_type == NotifierEventTypes.ACTIVATE_ALARM_EVENT:
self._activate_aodh_alarm(data)
def _activate_aodh_alarm(self, data):
LOG.info('### Activate aodh alarm')
# alarm_name = aodh_alarm_name_generator(
# data.get(VProps.NAME),
# data.get('affected_resource_id'))
# query = [dict(
# field='resource_id',
# type='string',
# op='eq',
# value=data.get('affected_resource_id'))]
# severity = data.get(VProps.SEVERITY)
# try:
# alarm = self.client.alarms.create(
# name=alarm_name,
# description='Vitrage deduced alarm',
# query=query,
# severity=severity,
# state='alarm',
# type='event',
# event_rule={"event_type": '*'})
# LOG.info('Aodh Alarm created: ' + str(alarm))
# except Exception as e:
# LOG.exception('Failed to create Aodh Alarm, Got Exception: %s',e)
# name
# description
# type' : event or threshold
# threshold_rule
# event_rule
# state': ok, alarm, insufficient data
# severity': moderate, critical, low
# enabled
# alarm_actions
# ok_actions
# insufficient_data_actions
# repeat_actions
# project_id
# user_id
# time_constraints
def _deactivate_aodh_alarm(self, data):
LOG.info('### Deactivate aodh alarm')
# try:
# alarm = self.client.alarms.update(
# alarm_id=data.get(VProps.ID),
# state='ok')
# LOG.info('Aodh Alarm deactivated ' + str(alarm))
# except Exception as e:
# LOG.exception('Failed to update Aodh Alarm, Got Exception: %s',e)

View File

@ -0,0 +1,27 @@
# Copyright 2016 - Nokia
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class NotifierBase(object):
def __init__(self, conf):
self.conf = conf
@abc.abstractmethod
def process_event(self, data, event_type):
pass

View File

@ -0,0 +1,64 @@
# Copyright 2016 - Nokia
#
# 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
import oslo_messaging
from oslo_service import service as os_service
from vitrage import messaging
from vitrage.notifier.plugins.aodh.aodh_notifier import AodhNotifier
LOG = log.getLogger(__name__)
class VitrageNotifierService(os_service.Service):
def __init__(self, conf):
super(VitrageNotifierService, self).__init__()
self.conf = conf
self.notifiers = [AodhNotifier(conf)]
transport = messaging.get_transport(conf)
target = oslo_messaging.Target(topic=conf.evaluator.notifier_topic)
self.listener = messaging.get_notification_listener(
transport, [target],
[VitrageEventEndpoint(self.notifiers)])
def start(self):
LOG.info("Vitrage Notifier Service - Starting...")
super(VitrageNotifierService, self).start()
self.listener.start()
LOG.info("Vitrage Notifier Service - Started!")
def stop(self, graceful=False):
LOG.info("Vitrage Notifier Service - Stopping...")
self.listener.stop()
self.listener.wait()
super(VitrageNotifierService, self).stop(graceful)
LOG.info("Vitrage Notifier Service - Stopped!")
class VitrageEventEndpoint(object):
def __init__(self, notifiers):
self.notifiers = notifiers
def info(self, ctxt, publisher_id, event_type, payload, metadata):
"""Endpoint for alarm notifications"""
LOG.info('Vitrage Event Info: publisher_id %s', publisher_id)
LOG.info('Vitrage Event Info: event_type %s', event_type)
LOG.info('Vitrage Event Info: metadata %s', metadata)
LOG.info('Vitrage Event Info: payload %s', payload)
for plugin in self.notifiers:
plugin.process_event(payload, event_type)

View File

@ -21,6 +21,7 @@ from oslo_utils import importutils
import vitrage.api
import vitrage.entity_graph.consistency
import vitrage.evaluator
import vitrage.keystone_client
import vitrage.rpc
import vitrage.synchronizer
import vitrage.synchronizer.plugins
@ -36,6 +37,7 @@ def list_opts():
('synchronizer_plugins', vitrage.synchronizer.plugins.OPTS),
('consistency', vitrage.entity_graph.consistency.OPTS),
('entity_graph', vitrage.entity_graph.OPTS),
('service_credentials', vitrage.keystone_client.OPTS),
('DEFAULT', vitrage.rpc.OPTS)
]

View File

@ -18,6 +18,8 @@ from oslo_log import log
from oslo_policy import opts as policy_opts
from oslo_utils import importutils
from vitrage import keystone_client
from vitrage import messaging
from vitrage import opts
PLUGINS_PATH = 'vitrage.synchronizer.plugins.'
@ -40,9 +42,13 @@ def prepare_service(args=None, default_opts=None, conf=None):
for plugin_name in conf.synchronizer_plugins.plugin_type:
load_plugin(conf, plugin_name)
keystone_client.register_keystoneauth_opts(conf)
conf(args, project='vitrage', validate_default_values=True)
keystone_client.setup_keystoneauth(conf)
log.setup(conf, 'vitrage')
conf.log_opt_values(LOG, logging.DEBUG)
messaging.setup()
return conf

View File

@ -30,7 +30,6 @@ class TestScenarioEvaluator(TestEntityGraphFunctionalBase):
@classmethod
def setUpClass(cls):
cls.conf = cfg.ConfigOpts()
cls.conf.register_opts(cls.PROCESSOR_OPTS, group='entity_graph')
cls.conf.register_opts(cls.EVALUATOR_OPTS, group='evaluator')
@ -44,7 +43,8 @@ class TestScenarioEvaluator(TestEntityGraphFunctionalBase):
# Test Setup
processor = self._create_processor_with_graph(self.conf)
event_queue = multiprocessing.Queue()
ScenarioEvaluator(processor.entity_graph,
ScenarioEvaluator(self.conf,
processor.entity_graph,
self.scenario_repository,
event_queue)

View File

@ -37,7 +37,12 @@ class TestEntityGraphUnitBase(base.BaseTest):
EVALUATOR_OPTS = [
cfg.StrOpt('templates_dir',
default=utils.get_resources_dir() + '/evaluator_templates',
)]
),
cfg.StrOpt('notifier_topic',
default='vitrage.evaluator',
help='The topic that vitrage-evaluator uses for alarm '
'notifications messages.'),
]
PLUGINS_OPTS = [
cfg.ListOpt('plugin_type',

View File

@ -41,7 +41,7 @@ class SetStateRecipeTest(base.BaseTest):
action_steps = SetState.get_do_recipe(action_spec)
# Test Assertions
self.assertEqual(2, len(action_steps))
self.assertEqual(1, len(action_steps))
self.assertEqual(UPDATE_VERTEX, action_steps[0].type)
update_vertex_step_params = action_steps[0].params