Merge "Included the aodh sender class in the common one"

This commit is contained in:
Jenkins 2017-07-17 01:58:11 +00:00 committed by Gerrit Code Review
commit 0aa39651ee
5 changed files with 168 additions and 381 deletions

View File

@ -16,7 +16,7 @@
from __future__ import unicode_literals
from collectd_ceilometer.aodh.sender import Sender
from collectd_ceilometer.aodh import sender as aodh_sender
import datetime
import logging
@ -30,7 +30,7 @@ class Notifier(object):
def __init__(self, meters, config):
"""Initialize Notifier."""
self._meters = meters
self._sender = Sender()
self._sender = aodh_sender.Sender()
self._config = config
def notify(self, vl, data):
@ -58,4 +58,6 @@ class Notifier(object):
def _send_data(self, metername, severity, resource_id, alarm_severity):
"""Send data to Aodh."""
LOGGER.debug('Sending alarm for %s', metername)
self._sender.send(metername, severity, resource_id, alarm_severity)
self._sender.send(metername, None, severity=severity,
resource_id=resource_id,
alarm_severity=alarm_severity)

View File

@ -17,17 +17,14 @@
from __future__ import division
from __future__ import unicode_literals
import collectd_ceilometer
from collectd_ceilometer.common.keystone_light import ClientV3
from collectd_ceilometer.common.keystone_light import KeystoneException
from collectd_ceilometer.common.settings import Config
import json
import logging
import requests
from requests.exceptions import RequestException
import six
import threading
import collectd_ceilometer
from collectd_ceilometer.common import sender as common_sender
from collectd_ceilometer.common.settings import Config
LOGGER = logging.getLogger(__name__)
ROOT_LOGGER = logging.getLogger(collectd_ceilometer.__name__)
@ -38,151 +35,77 @@ HTTP_UNAUTHORIZED = 401
HTTP_NOT_FOUND = 404
class Sender(object):
class Sender(common_sender.Sender):
"""Sends the JSON serialized data to Aodh."""
def __init__(self):
"""Create the Sender instance.
The cofiguration must be initialized before the object is created.
The configuration must be initialized before the object is created.
"""
self._url_base = None
self._keystone = None
self._auth_token = None
self._auth_lock = threading.Lock()
self._failed_auth = False
super(Sender, self).__init__()
self._alarm_ids = {}
def _authenticate(self):
"""Authenticate and renew the authentication token."""
# if auth_token is available, just return it
if self._auth_token is not None:
return self._auth_token
def _on_authenticated(self):
# get the uri of service endpoint for an alarm state update
endpoint = self._get_endpoint("aodh")
# aquire the authentication lock
with self._auth_lock:
# re-check the auth_token as another thread could set it
if self._auth_token is not None:
return self._auth_token
self._url_base = "{}/v2/alarms/%s/state".format(endpoint)
LOGGER.debug('Authenticating request')
# pylint: disable=broad-except
try:
# create a keystone client if it doesn't exist
if self._keystone is None:
cfg = Config.instance()
self._keystone = ClientV3(
auth_url=cfg.OS_AUTH_URL,
username=cfg.OS_USERNAME,
password=cfg.OS_PASSWORD,
tenant_name=cfg.OS_TENANT_NAME
)
# store the authentication token
self._auth_token = self._keystone.auth_token
# get the uri of service endpoint
endpoint = self._get_endpoint("aodh")
self._url_base = "{}/v2/alarms/%s/state".format(endpoint)
LOGGER.info('Authenticating request - success')
self._failed_auth = False
except KeystoneException as exc:
log_level = logging.DEBUG
if not self._failed_auth:
log_level = logging.ERROR
LOGGER.error(
'Suspending error logs until successful auth'
)
LOGGER.log(log_level, 'Authentication error: %s',
six.text_type(exc),
exc_info=0)
if exc.response:
LOGGER.debug('Response: %s', exc.response)
self._auth_token = None
self._failed_auth = True
return self._auth_token
def send(self, metername, severity, resource_id, alarm_severity):
"""Send the payload to Aodh.
severity: is retrieved from the collectd notification itself, it
defines how severely a threshold is broken. Changes everytime
a notification is generated for a specific meter.
alarm_severity: is a variable used to define the severity of the aodh
alarm that will be created. Defined when the alarm is
created and doesn't change, it defines how severe the
situation is if that alarm is triggered.
"""
# get the auth_token
auth_token = self._authenticate()
LOGGER.info('Auth_token: %s',
auth_token,
)
# if auth_token is not set, there is nothing to do
if auth_token is None:
LOGGER.debug('Unable to send data. Not authenticated')
return
if self._url_base is None:
LOGGER.debug(
'Unable to send data. Missing endpoint from ident server')
return
# Create alarm name
def _create_request_url(self, metername, **kwargs):
"""Create the request url for an alarm update."""
severity = kwargs['severity']
resource_id = kwargs['resource_id']
alarm_severity = kwargs['alarm_severity']
alarm_name = self._get_alarm_name(metername, resource_id)
alarm_id = self._get_alarm_id(alarm_name,
severity, metername, alarm_severity)
payload = self._get_alarm_payload(**kwargs)
# Update or create this alarm
result = self._update_or_create_alarm(alarm_name, auth_token, severity,
metername, alarm_severity)
# Create a url if an alarm already exists
if alarm_id is not None:
url = self._url_base % (alarm_id)
try:
self._perform_request(url, payload, self._auth_token, "put")
except requests.exceptions.HTTPError as exc:
# This is an error and it has to be forwarded
self._handle_http_error(exc, metername, payload,
self._auth_token, **kwargs)
if result is None:
return
return None
LOGGER.info('Result: %s %s',
six.text_type(result.status_code),
result.text)
# if the request failed due to an auth error
if result.status_code == HTTP_UNAUTHORIZED:
# reset the auth token in order to force the subsequent
# _authenticate() call to renew it
# Here, it can happen that the token is reset right after
# another thread has finished the authentication and thus
# the authentication may be performed twice
self._auth_token = None
# renew the authentication token
auth_token = self._authenticate()
if auth_token is not None:
result = self._update_or_create_alarm(alarm_name, auth_token,
severity, metername,
alarm_severity)
if result.status_code == HTTP_NOT_FOUND:
LOGGER.debug("Received 404 error when submitting %s notification, \
creating a new alarm",
def _handle_http_error(self, exc, metername,
payload, auth_token, **kwargs):
"""Handle and log a http error request."""
severity = kwargs['severity']
resource_id = kwargs['resource_id']
alarm_name = self._get_alarm_name(metername, resource_id)
response = exc.response
if response.status_code == common_sender.Sender.HTTP_NOT_FOUND:
LOGGER.debug("Received 404 error when submitting %s update, \
updating a new alarm",
alarm_name)
# check for and/or get alarm_id
result = self._update_or_create_alarm(alarm_name, auth_token,
severity, metername,
alarm_severity)
# get alarm id for
alarm_id = self._get_alarm_id(alarm_name, severity, metername)
if result.status_code == HTTP_CREATED:
LOGGER.debug('Result: %s', HTTP_CREATED)
LOGGER.info('alarmname: %s, alarm_id: %s', alarm_name, alarm_id)
# Set a new url for the request
url = self._url_base % (alarm_id)
# Get the responses for the alarm
result = self._perform_request(url, payload, auth_token, "put")
if result.status_code == common_sender.Sender.HTTP_CREATED:
LOGGER.debug('Result: %s', common_sender.Sender.HTTP_CREATED)
else:
LOGGER.info('Result: %s %s',
result.status_code,
result.text)
else:
LOGGER.info('Result: %s %s',
result.status_code,
result.text)
raise exc
def _get_endpoint(self, service):
"""Get the uri of service endpoint."""
@ -191,23 +114,24 @@ class Sender(object):
Config.instance().CEILOMETER_URL_TYPE)
return endpoint
def _update_or_create_alarm(self, alarm_name, auth_token,
severity, metername, alarm_severity):
def _get_alarm_id(self, alarm_name, severity, metername, alarm_severity):
# check for an alarm and update
try:
alarm_id = self._get_alarm_id(alarm_name)
result = self._update_alarm(alarm_id, severity, auth_token)
return self._alarm_ids[alarm_name]
# or create a new alarm
except KeyError as ke:
LOGGER.warn(ke)
LOGGER.warn('No known ID for %s', alarm_name)
endpoint = self._get_endpoint("aodh")
LOGGER.warn('No known ID for %s', alarm_name)
result, self._alarm_ids[alarm_name] = \
alarm_id = \
self._create_alarm(endpoint, severity,
metername, alarm_name, alarm_severity)
return result
if alarm_id is not None:
# Add alarm ids/names to relevant dictionaries/lists
self._alarm_ids[alarm_name] = alarm_id
return None
def _create_alarm(self, endpoint, severity, metername,
alarm_name, alarm_severity):
@ -222,14 +146,10 @@ class Sender(object):
'event_rule': rule,
})
result = self._perform_post_request(url, payload, self._auth_token)
result = self._perform_request(url, payload, self._auth_token, "post")
alarm_id = json.loads(result.text)['alarm_id']
LOGGER.debug("alarm_id=%s", alarm_id)
return result, alarm_id
def _get_alarm_id(self, alarm_name):
"""Try and return an alarm_id for an collectd notification"""
return self._alarm_ids[alarm_name]
return alarm_id
def _get_alarm_state(self, severity):
"""Get the state of the alarm."""
@ -246,66 +166,8 @@ class Sender(object):
alarm_name = metername + "(" + resource_id + ")"
return alarm_name
def _update_alarm(self, alarm_id, severity, auth_token):
"""Perform the alarm update."""
url = self._url_base % (alarm_id)
# create the payload and update the state of the alarm
def _get_alarm_payload(self, **kwargs):
"""Get the payload for the update/post request of the alarm."""
severity = kwargs['severity']
payload = json.dumps(self._get_alarm_state(severity))
return self._perform_update_request(url, auth_token, payload)
@classmethod
def _perform_post_request(cls, url, payload, auth_token):
"""Perform the POST request."""
LOGGER.debug('Performing request to %s', url)
# request headers
headers = {'X-Auth-Token': auth_token,
'Content-type': 'application/json'}
# perform request and return its result
response = None
try:
LOGGER.debug(
"Performing request to: %s with data=%s and headers=%s",
url, payload, headers)
response = requests.post(
url, data=payload, headers=headers,
timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.))
LOGGER.info('Response: %s: %s',
response.status_code, response.text
)
except RequestException as exc:
LOGGER.error('aodh request error: %s', six.text_type(exc))
finally:
LOGGER.debug(
"Returning response from _perform_post_request(): %s",
response.status_code)
return response
@classmethod
def _perform_update_request(cls, url, auth_token, payload):
"""Perform the PUT/update request."""
LOGGER.debug('Performing request to %s', url)
# request headers
headers = {'X-Auth-Token': auth_token,
'Content-type': 'application/json'}
# perform request and return its result
response = None
try:
LOGGER.debug(
"Performing request to: %s with data=%s and headers=%s",
url, payload, headers)
response = requests.put(
url, data=payload, headers=headers,
timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.))
LOGGER.info('Response: %s: %s',
response.status_code, response.text
)
except RequestException as exc:
LOGGER.error('aodh request error: %s', six.text_type(exc))
finally:
LOGGER.debug(
'Returning response from _perform_update_request(): %s',
response.status_code)
return response
return payload

View File

@ -124,7 +124,7 @@ class Sender(object):
raise exc
def send(self, metername, payload, **kwargs):
"""Send the payload to Ceilometer/Gnocchi"""
"""Send the payload to Ceilometer/Gnocchi/Aodh"""
# get the auth_token
auth_token = self._authenticate()
@ -175,18 +175,22 @@ class Sender(object):
auth_token, **kwargs)
@classmethod
def _perform_request(cls, url, payload, auth_token):
"""Perform the POST request"""
def _perform_request(cls, url, payload, auth_token, req_type="post"):
"""Perform the POST/PUT request."""
LOGGER.debug('Performing request to %s', url)
# request headers
headers = {'X-Auth-Token': auth_token,
'Content-type': 'application/json'}
# perform request and return its result
response = requests.post(
url, data=payload, headers=headers,
timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.))
if req_type == "put":
response = requests.put(
url, data=payload, headers=headers,
timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.))
else:
response = requests.post(
url, data=payload, headers=headers,
timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.))
# Raises exception if there was an error
try:

View File

@ -25,9 +25,10 @@ import six
import unittest
from collectd_ceilometer.aodh import plugin
from collectd_ceilometer.aodh import sender
from collectd_ceilometer.aodh import sender as aodh_sender
from collectd_ceilometer.common.keystone_light import KeystoneException
from collectd_ceilometer.common.meters import base
from collectd_ceilometer.common import sender as common_sender
from collectd_ceilometer.common import settings
Logger = logging.getLoggerClass()
@ -93,21 +94,20 @@ def config_module(
def config_severities(severities):
"""Create a mocked collectd config node having severities for alarms."""
children = [config_value('ALARM_SEVERITY', key, value)
for key, value in six.iteritems(severities)]
return config_node('ALARM_SEVERITIES', children)
def config_node(key, children, value=None):
"""Create a mocked collectd config node having given children and value."""
"Create a mocked collectd config node having given children and value"
return mock.create_autospec(
spec=MockedConfig, spec_set=True, instance=True,
children=tuple(children), key=key, values=(value,))
def config_value(key, *values):
"""Create a mocked collectd config node having given multiple values."""
"Create a mocked collectd config node having given multiple values"
return mock.create_autospec(
spec=MockedConfig, spec_set=True, instance=True,
children=tuple(), key=key, values=values)
@ -118,17 +118,14 @@ class MockedConfig(object):
@abc.abstractproperty
def children(self):
"""Mocked children method."""
pass
@abc.abstractproperty
def key(self):
"""Mocked key method."""
pass
@abc.abstractproperty
def values(self):
"""Mocked values method."""
pass
@ -162,7 +159,6 @@ class TestPlugin(unittest.TestCase):
@property
def default_values(self):
"""Default configuration values."""
return dict(
BATCH_SIZE=1,
OS_AUTH_URL='https://test-auth.url.tld/test',
@ -198,89 +194,37 @@ class TestPlugin(unittest.TestCase):
collectd.register_notification.assert_called_once_with(instance.notify)
collectd.register_shutdown.assert_called_once_with(instance.shutdown)
@mock.patch.object(sender.Sender, '_update_or_create_alarm', autospec=True)
@mock.patch.object(sender.Sender, '_get_alarm_name', autospec=True)
@mock.patch.object(base, 'Meter', autospec=True)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock.patch.object(aodh_sender.Sender, '_get_alarm_id', autospec=True)
@mock.patch.object(aodh_sender.Sender, '_get_alarm_state', autospec=True)
@mock.patch.object(requests, 'put', spec=callable)
@mock.patch.object(common_sender, 'ClientV3', autospec=True)
@mock_collectd()
@mock_config()
@mock_value()
def test_update_or_create_alarm(self, data, config, collectd,
ClientV3, meter,
_get_alarm_name, _update_or_create_alarm):
"""Test the update/create alarm function"""
auth_client = ClientV3.return_value
auth_client.get_service_endpoint.return_value = \
'https://test-aodh.tld'
_update_or_create_alarm.return_value = requests.Response()
# init sender instance
instance = sender.Sender()
_get_alarm_name.return_value = 'my-alarm'
meter_name = meter.meter_name.return_value
severity = meter.collectd_severity.return_value
resource_id = meter.resource_id.return_value
alarm_severity = meter.alarm_severity.return_value
# send the values
instance.send(meter_name, severity, resource_id, alarm_severity)
# check that the function is called
_update_or_create_alarm.assert_called_once_with(
instance, 'my-alarm', auth_client.auth_token,
severity, meter_name, alarm_severity)
# reset function
_update_or_create_alarm.reset_mock()
# run test again for failed attempt
_update_or_create_alarm.return_value = None
instance.send(meter_name, severity, resource_id, alarm_severity)
# and values that have been sent
_update_or_create_alarm.assert_called_once_with(
instance, 'my-alarm', auth_client.auth_token,
severity, meter_name, alarm_severity)
# reset post method
_update_or_create_alarm.reset_mock()
@mock.patch.object(sender.Sender, '_get_alarm_id', autospec=True)
@mock.patch.object(requests, 'put', spec=callable)
@mock.patch.object(base, 'Meter', autospec=True)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock_collectd()
@mock_config()
def test_update_alarm(self, config, collectd, ClientV3,
meter, put, _get_alarm_id):
def test_update_alarm(self, data, config, collectd, ClientV3,
put, _get_alarm_state, _get_alarm_id):
"""Test the update alarm function.
Set-up: create a sender object and get an alarm-id for it
Test: update an alarm when there is an alarm-id and when there isn't
Set-up: get an alarm-id for some notification values to be sent
Test: perform an update request
Expected behaviour:
- If alarm-id present a put/update request is called
- If alarm-id is present a put request is performed
"""
auth_client = ClientV3.return_value
auth_client.get_service_endpoint.return_value = \
'https://test-aodh.tld'
# init instance
instance = sender.Sender()
instance = plugin.Plugin(collectd=collectd, config=config)
# init values to send
_get_alarm_id.return_value = 'my-alarm-id'
metername = meter.meter_name.return_value
severity = meter.collectd_severity.return_value
rid = meter.resource_id.return_value
alarm_severity = meter.alarm_severity.return_value
_get_alarm_state.return_value = 'insufficient data'
# send the values
instance.send(metername, severity, rid, alarm_severity)
# notify aodh of the update
instance.notify(data)
# update the alarm
# update the alarm with a put request
put.assert_called_once_with(
'https://test-aodh.tld' +
'/v2/alarms/my-alarm-id/state',
@ -292,45 +236,43 @@ class TestPlugin(unittest.TestCase):
# reset method
put.reset_mock()
@mock.patch.object(sender.Sender, '_create_alarm', autospec=True)
@mock.patch.object(sender.Sender, '_get_alarm_id', autospec=True)
@mock.patch.object(aodh_sender.Sender, '_create_alarm', autospec=True)
@mock.patch.object(aodh_sender.Sender, '_get_alarm_id', autospec=True)
@mock.patch.object(requests, 'put', spec=callable)
@mock.patch.object(base, 'Meter', autospec=True)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock.patch.object(common_sender, 'ClientV3', autospec=True)
@mock_collectd()
@mock_config()
def test_alarm_not_updated(self, config, collectd, ClientV3,
meter, put, _get_alarm_id, _create_alarm):
"""Test if an alarm is created, hence it will not be updated
@mock_value()
def test_update_alarm_no_id(self, data, config, collectd, ClientV3,
put, _get_alarm_id, _create_alarm):
"""Test if the is no alarm id the alarm won't be updated.
Set-up: create a sender object and create an alarm
Test: alarm won't be updated if one is created
Set-up: create a client and an instance to send an update to
throw a side-effect when looking for an id
Test: send a notification for a new alarm
Expected behaviour:
- No alarm exists alarm-id throws a KeyError and a put/update request
isn't called
- if an alarm is create an update request is not performed
"""
# init instance
instance = sender.Sender()
auth_client = ClientV3.return_value
auth_client.get_service_endpoint.return_value = \
'https://test-aodh.tld'
instance = plugin.Plugin(collectd=collectd, config=config)
# init values to send
_get_alarm_id.return_value = None
_get_alarm_id.side_effect = KeyError()
_create_alarm.return_value = requests.Response(), 'my-alarm-id'
metername = meter.meter_name.return_value
severity = meter.collectd_severity.return_value
rid = meter.resource_id.return_value
alarm_severity = meter.alarm_severity.return_value
_create_alarm.return_value = 'my-alarm-id'
# send the values again
instance.send(metername, severity, rid, alarm_severity)
# try and perform an update without an id
instance.notify(data)
put.assert_not_called()
put.reset_mock()
@mock.patch.object(requests, 'put', spec=callable)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock.patch.object(sender, 'LOGGER', autospec=True)
@mock.patch.object(common_sender, 'ClientV3', autospec=True)
@mock.patch.object(common_sender, 'LOGGER', autospec=True)
@mock_collectd()
@mock_config()
@mock_value()
@ -368,7 +310,7 @@ class TestPlugin(unittest.TestCase):
Expected-behaviour: returned state value should equal 'ok'
and won't equal 'alarm' or insufficient data'
"""
instance = sender.Sender()
instance = aodh_sender.Sender()
# run test for moderate severity
severity.return_value = 'low'
@ -389,7 +331,7 @@ class TestPlugin(unittest.TestCase):
Expected-behaviour: returned state value should equal 'alarm'
and won't equal 'ok' or insufficient data'
"""
instance = sender.Sender()
instance = aodh_sender.Sender()
# run test for moderate severity
severity.return_value = 'moderate'
@ -410,7 +352,7 @@ class TestPlugin(unittest.TestCase):
Expected-behaviour: returned state value should equal 'alarm'
and won't equal 'ok' or 'insufficient data'
"""
instance = sender.Sender()
instance = aodh_sender.Sender()
# run test for moderate severity
severity.return_value = 'critical'
@ -422,8 +364,8 @@ class TestPlugin(unittest.TestCase):
self.assertNotEqual(instance._get_alarm_state('critical'),
'insufficient data')
@mock.patch.object(sender.Sender, '_perform_post_request', spec=callable)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock.patch.object(common_sender.Sender, '_perform_request', spec=callable)
@mock.patch.object(common_sender, 'ClientV3', autospec=True)
@mock_collectd()
@mock_config()
@mock_value()
@ -439,16 +381,15 @@ class TestPlugin(unittest.TestCase):
# the value
self.assertRaises(requests.RequestException, instance.notify, data)
@mock.patch.object(sender.Sender, '_update_or_create_alarm', autospec=True)
@mock.patch.object(sender.Sender, '_get_alarm_name', autospec=True)
@mock.patch.object(base, 'Meter', autospec=True)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock.patch.object(aodh_sender.Sender, '_get_alarm_state', autospec=True)
@mock.patch.object(aodh_sender.Sender, '_get_alarm_id', autospec=True)
@mock.patch.object(requests, 'put', spec=callable)
@mock.patch.object(common_sender, 'ClientV3', autospec=True)
@mock_collectd()
@mock_config()
@mock_value()
def test_reauthentication(self, data, config, collectd,
ClientV3, meter, _get_alarm_name,
_update_or_create_alarm):
ClientV3, put, _get_alarm_id, _get_alarm_state):
"""Test re-authentication for update request."""
# response returned on success
@ -459,62 +400,32 @@ class TestPlugin(unittest.TestCase):
response_unauthorized = requests.Response()
response_unauthorized.status_code = requests.codes["UNAUTHORIZED"]
_update_or_create_alarm.return_value = response_ok
# set-up client
client = ClientV3.return_value
client.auth_token = 'Test auth token'
client.get_service_endpoint.return_value = \
'https://test-aodh.tld'
# init instance attempt to update/create alarm
instance = sender.Sender()
instance = plugin.Plugin(collectd=collectd, config=config)
alarm_name = _get_alarm_name.return_value
meter_name = meter.meter_name.return_value
severity = meter.collectd_severity.return_value
resource_id = meter.resource_id.return_value
alarm_severity = meter.alarm_severity.return_value
put.return_value = response_ok
_get_alarm_id.return_value = 'my-alarm-id'
_get_alarm_state.return_value = 'insufficient data'
# send the data
instance.send(meter_name, severity, resource_id, alarm_severity)
# send notification to aodh
instance.notify(data)
_update_or_create_alarm.assert_called_once_with(
instance, alarm_name, client.auth_token,
severity, meter_name, alarm_severity)
# put/update is called
put.assert_called_once_with(
'https://test-aodh.tld' +
'/v2/alarms/my-alarm-id/state',
data='"insufficient data"',
headers={u'Content-type': 'application/json',
u'X-Auth-Token': 'Test auth token'},
timeout=1.0)
# de-assert the request
_update_or_create_alarm.reset_mock()
# response returned on success
response_ok = requests.Response()
response_ok.status_code = requests.codes["OK"]
# response returned on failure
response_unauthorized = requests.Response()
response_unauthorized.status_code = requests.codes["UNAUTHORIZED"]
_update_or_create_alarm.return_value = response_ok
client = ClientV3.return_value
client.auth_token = 'Test auth token'
# send the data
instance.send(meter_name, severity, resource_id, alarm_severity)
_update_or_create_alarm.assert_called_once_with(
instance, alarm_name, client.auth_token,
severity, meter_name, alarm_severity)
# update/create response is unauthorized -> new token needs
# to be acquired
_update_or_create_alarm.side_effect = [response_unauthorized,
response_ok]
# set a new auth token
client.auth_token = 'New test auth token'
# send the data again
instance.send(meter_name, severity, resource_id, alarm_severity)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock.patch.object(common_sender, 'ClientV3', autospec=True)
@mock.patch.object(plugin, 'Notifier', autospec=True)
@mock.patch.object(plugin, 'LOGGER', autospec=True)
@mock_collectd()
@ -531,7 +442,7 @@ class TestPlugin(unittest.TestCase):
self.assertRaises(ValueError, instance.notify, data)
@mock.patch.object(sender, 'ClientV3', autospec=True)
@mock.patch.object(common_sender, 'ClientV3', autospec=True)
@mock.patch.object(plugin, 'LOGGER', autospec=True)
@mock_collectd()
@mock_config()
@ -545,7 +456,7 @@ class TestPlugin(unittest.TestCase):
@mock.patch.object(settings, 'LOGGER', autospec=True)
def test_user_severities(self, LOGGER):
"""Test if a user enters a severity for a specific meter.
"""Test if a user enters a severity for a specific meter
Set-up: Create a node with some user defined severities
Configure the node
@ -572,7 +483,7 @@ class TestPlugin(unittest.TestCase):
@mock.patch.object(settings, 'LOGGER', autospec=True)
def test_user_severities_invalid(self, LOGGER):
"""Test invalid user defined severities.
"""Test invalid user defined severities
Set-up: Configure the node with one defined severity
Set a configuration to have 3 entries instead of the 2
@ -583,6 +494,7 @@ class TestPlugin(unittest.TestCase):
Log will be written that severities were
incorrectly configured
"""
node = config_module(values=self.default_values,
severities=dict(age='low'))
# make some alarm severity entry invalid
@ -601,7 +513,7 @@ class TestPlugin(unittest.TestCase):
@mock.patch.object(settings, 'LOGGER', autospec=True)
def test_user_severities_invalid_node(self, LOGGER):
"""Test invalid node with severities configuration.
"""Test invalid node with severities configuration
Set-up: Set up a configuration node with a severity defined
Configure the node with an incorrect module title
@ -609,6 +521,7 @@ class TestPlugin(unittest.TestCase):
Expected-behaviour: Error will be recorded in the log
Severity configuration will return None
"""
node = config_module(values=self.default_values,
severities=dict(age='moderate'))
# make some alarm severity entry invalid
@ -625,7 +538,7 @@ class TestPlugin(unittest.TestCase):
self.assertEqual(config.alarm_severity('age'), 'moderate')
def test_read_alarm_severities(self):
"""Test reading in user defined alarm severities method.
"""Test reading in user defined alarm severities method
Set-up: Set up a node configured with a severities dictionary defined
Test: Read the node for the ALARM_SEVERITY configuration

View File

@ -0,0 +1,6 @@
---
Fixes:
- |
Fixes 'bug 1668210 https://bugs.launchpad.net/collectd-ceilometer-plugin/+bug/1668210',
refactored the aodh sender and notifier classes so that the
collectd-aodh-plugin makes use of the common sender class.