aodh notifier
Change-Id: I025f58bfb898d6252fa61153de4866b741b26464
This commit is contained in:
parent
211afaf4a1
commit
c832df92bf
@ -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
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
enable_service vitrage-api
|
||||
# Graph
|
||||
enable_service vitrage-graph
|
||||
# Notifier
|
||||
enable_service vitrage-notifier
|
||||
|
||||
|
||||
# Default directories
|
||||
|
67
doc/source/notifier-aodh-plugin.rst
Normal file
67
doc/source/notifier-aodh-plugin.rst
Normal 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
|
@ -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
35
vitrage/clients.py
Normal 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
30
vitrage/cmd/notifier.py
Normal 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())
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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.'),
|
||||
]
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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, {})
|
||||
|
@ -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
170
vitrage/keystone_client.py
Normal 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
77
vitrage/messaging.py
Normal 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')
|
15
vitrage/notifier/__init__.py
Normal file
15
vitrage/notifier/__init__.py
Normal 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
|
15
vitrage/notifier/plugins/__init__.py
Normal file
15
vitrage/notifier/plugins/__init__.py
Normal 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
|
15
vitrage/notifier/plugins/aodh/__init__.py
Normal file
15
vitrage/notifier/plugins/aodh/__init__.py
Normal 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
|
95
vitrage/notifier/plugins/aodh/aodh_notifier.py
Normal file
95
vitrage/notifier/plugins/aodh/aodh_notifier.py
Normal 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)
|
27
vitrage/notifier/plugins/base.py
Normal file
27
vitrage/notifier/plugins/base.py
Normal 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
|
64
vitrage/notifier/service.py
Normal file
64
vitrage/notifier/service.py
Normal 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)
|
@ -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)
|
||||
]
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user