Included the aodh sender class in the common one
- Combined commonalities from the aodh and common sender classes - Updated all of the relevant tests for each class - Included a reno Change-Id: I78fcdb075661e125b0e9f16bdee626cd811ad78e Closes-bug: #1668210
This commit is contained in:
parent
1437e06bf1
commit
2223d14005
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
6
releasenotes/notes/bug-1668210-5b8220bcc27a81ef.yaml
Normal file
6
releasenotes/notes/bug-1668210-5b8220bcc27a81ef.yaml
Normal 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.
|
Loading…
x
Reference in New Issue
Block a user