Updated _get_alarm_id function in aodh plugin
- Request server for alarm_id before creating new one - Adding http get method to _perform_request of common Sender - Add unit tests for {aodh,common}/sender Change-Id: I48e83e87349048962c400a2660cd0c0252563ea0 Closes-bug: #1672296 Signed-off-by: Ivan Dyukov <i.dyukov@samsung.com>
This commit is contained in:
parent
d474b8ba68
commit
680a43d179
@ -25,6 +25,7 @@ import requests
|
||||
import collectd_ceilometer
|
||||
from collectd_ceilometer.common import sender as common_sender
|
||||
from collectd_ceilometer.common.settings import Config
|
||||
from collections import OrderedDict
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
ROOT_LOGGER = logging.getLogger(collectd_ceilometer.__name__)
|
||||
@ -114,24 +115,50 @@ class Sender(common_sender.Sender):
|
||||
Config.instance().CEILOMETER_URL_TYPE)
|
||||
return endpoint
|
||||
|
||||
def _get_remote_alarm_id(self, endpoint, alarm_name):
|
||||
"""Request alarm with given name."""
|
||||
url = "{}/v2/alarms".format(endpoint)
|
||||
# request id from a server. openstack service can
|
||||
# handle only predefined order of args: q.field=&q.op=&q.value=
|
||||
# in other cases it will fail
|
||||
params = OrderedDict([("q.field", "name"), ("q.op", "eq"),
|
||||
("q.value", alarm_name)])
|
||||
alarm_id = None
|
||||
try:
|
||||
result = self._perform_request(url, params, self._auth_token,
|
||||
req_type="get")
|
||||
except Exception as exc:
|
||||
LOGGER.warn('Invalid response from server for alarm:'
|
||||
' %s error = %s %s' % (alarm_name, type(exc), exc))
|
||||
return None
|
||||
|
||||
try:
|
||||
# parse response
|
||||
alarm_id = json.loads(result.text)[0]['alarm_id']
|
||||
except (KeyError, ValueError, IndexError):
|
||||
LOGGER.warn('NO alarm on the server: %s' % alarm_name)
|
||||
return alarm_id
|
||||
|
||||
def _get_alarm_id(self, alarm_name, severity, metername, alarm_severity):
|
||||
# check for an alarm and update
|
||||
try:
|
||||
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)
|
||||
LOGGER.warn('No ID in a cache for %s', alarm_name)
|
||||
|
||||
endpoint = self._get_endpoint("aodh")
|
||||
alarm_id = \
|
||||
self._create_alarm(endpoint, severity,
|
||||
metername, alarm_name, alarm_severity)
|
||||
alarm_id = self._get_remote_alarm_id(endpoint, alarm_name)
|
||||
# create new alarm
|
||||
if alarm_id is None:
|
||||
alarm_id = \
|
||||
self._create_alarm(endpoint, severity,
|
||||
metername, alarm_name, alarm_severity)
|
||||
if alarm_id is not None:
|
||||
# Add alarm ids/names to relevant dictionaries/lists
|
||||
self._alarm_ids[alarm_name] = alarm_id
|
||||
return None
|
||||
return alarm_id
|
||||
|
||||
def _create_alarm(self, endpoint, severity, metername,
|
||||
alarm_name, alarm_severity):
|
||||
|
@ -177,13 +177,18 @@ class Sender(object):
|
||||
@classmethod
|
||||
def _perform_request(cls, url, payload, auth_token, req_type="post"):
|
||||
"""Perform the POST/PUT request."""
|
||||
LOGGER.debug('Performing request to %s', url)
|
||||
LOGGER.debug('Performing request to %s, payload=%s, req_type = %s' %
|
||||
(url, payload, req_type))
|
||||
|
||||
# request headers
|
||||
headers = {'X-Auth-Token': auth_token,
|
||||
'Content-type': 'application/json'}
|
||||
# perform request and return its result
|
||||
if req_type == "put":
|
||||
if req_type == "get":
|
||||
response = requests.get(
|
||||
url, params=payload, headers=headers,
|
||||
timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.))
|
||||
elif req_type == "put":
|
||||
response = requests.put(
|
||||
url, data=payload, headers=headers,
|
||||
timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.))
|
||||
|
133
collectd_ceilometer/tests/aodh/test_sender.py
Normal file
133
collectd_ceilometer/tests/aodh/test_sender.py
Normal file
@ -0,0 +1,133 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2017 Intel Corporation.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Sender tests."""
|
||||
|
||||
import mock
|
||||
import requests
|
||||
import unittest
|
||||
|
||||
from collectd_ceilometer.aodh import sender as aodh_sender
|
||||
from collections import OrderedDict
|
||||
|
||||
valid_resp = '[{"alarm_actions": [], "event_rule": {"query": [],'\
|
||||
'"event_type": "events.gauge"}, "ok_actions": [],'\
|
||||
'"name": "alarm", "severity": "moderate",'\
|
||||
'"timestamp": "2017-08-22T06:22:46.790949", "enabled": true,'\
|
||||
'"alarm_id": "11af9327-8c3a-4120-8a74-bbc672c90f0a",'\
|
||||
'"time_constraints": [], "insufficient_data_actions": []}]'
|
||||
|
||||
valid_alarm_id = "valid_alarm_id"
|
||||
|
||||
|
||||
class response(object):
|
||||
|
||||
__attrs__ = [
|
||||
'_content', 'status_code', 'headers', 'url', 'history',
|
||||
'encoding', 'reason', 'cookies', 'elapsed', 'request', 'text'
|
||||
]
|
||||
|
||||
def __init__(self, text, code):
|
||||
self.text = text
|
||||
self.status_code = code
|
||||
|
||||
def raise_for_status(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestSender(unittest.TestCase):
|
||||
"""Test the Sender class."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestSender, self).setUp()
|
||||
self.sender = aodh_sender.Sender()
|
||||
|
||||
@mock.patch.object(aodh_sender.Sender, "_get_remote_alarm_id",
|
||||
autospec=True)
|
||||
@mock.patch.object(aodh_sender.Sender, "_get_endpoint", autospec=True)
|
||||
@mock.patch.object(aodh_sender.Sender, "_create_alarm", spec=callable)
|
||||
def test_get_alarm_id_no_local_alarm(self, _create_alarm, _get_endpoint,
|
||||
_get_remote_alarm_id):
|
||||
"""Test the behaviour when the alarm id doesn't exist locally.
|
||||
|
||||
Set-up:
|
||||
Test: call _get_alarm_id when no local alarm exists.
|
||||
Expected behaviour:
|
||||
* _get_remote_alarm_id is called
|
||||
* self._alarm_ids is updated
|
||||
"""
|
||||
_get_remote_alarm_id.return_value = valid_alarm_id
|
||||
|
||||
alarm_id = self.sender._get_alarm_id("alarm", "critical", "link status",
|
||||
"critical")
|
||||
|
||||
self.assertEqual(alarm_id, valid_alarm_id,
|
||||
"_get_remote_alarm_id is not called")
|
||||
_get_remote_alarm_id.assert_called_once_with(mock.ANY, mock.ANY,
|
||||
"alarm")
|
||||
_create_alarm.assert_not_called()
|
||||
_get_endpoint.assert_called_once_with(mock.ANY, "aodh")
|
||||
self.assertIn("alarm", self.sender._alarm_ids,
|
||||
"self._alarm_ids is not updated")
|
||||
|
||||
@mock.patch.object(aodh_sender.Sender, "_create_alarm", spec=callable)
|
||||
@mock.patch.object(requests, 'get', spec=callable)
|
||||
def test_get_remote_alarm_id(self, get, _create_alarm):
|
||||
"""Test behaviour of _get_remote_alarm_id
|
||||
|
||||
Set-up:
|
||||
Test: Call _get_remote_alarm_id with typical parameters
|
||||
Expected behaviour:
|
||||
* requests.get is called with correct args
|
||||
* Alarm ID is returned
|
||||
"""
|
||||
resp = response(valid_resp, 200)
|
||||
get.return_value = resp
|
||||
params = OrderedDict([(u"q.field", u"name"), (u"q.op", u"eq"),
|
||||
(u"q.value", u"alarm")])
|
||||
|
||||
alarm_id = self.sender._get_remote_alarm_id(u"endpoint", u"alarm")
|
||||
|
||||
self.assertEqual(alarm_id, "11af9327-8c3a-4120-8a74-bbc672c90f0a",
|
||||
"invalid alarm id")
|
||||
_create_alarm.assert_not_called()
|
||||
get.assert_called_once_with(u"endpoint/v2/alarms", params=params,
|
||||
headers=mock.ANY, timeout=mock.ANY)
|
||||
|
||||
@mock.patch.object(aodh_sender.Sender, "_get_endpoint", autospec=True)
|
||||
@mock.patch.object(aodh_sender.Sender, "_create_alarm", spec=callable)
|
||||
@mock.patch.object(requests, 'get', spec=callable)
|
||||
def test_get_alarm_id_not_found(self, get, _create_alarm, _get_endpoint):
|
||||
"""Test behaviour of _get_alarm_id when alarm does not exist
|
||||
|
||||
Set up:
|
||||
Test:
|
||||
* call _get_alarm_id
|
||||
* requests.get/sender._perform_request return an error
|
||||
Expected behaviour: _create_alarm is called
|
||||
"""
|
||||
resp = response("some invalid response", 404)
|
||||
get.return_value = resp
|
||||
_create_alarm.return_value = valid_alarm_id
|
||||
|
||||
alarm_id = self.sender._get_alarm_id("alarm", "critical", "link status",
|
||||
"critical")
|
||||
|
||||
_create_alarm.assert_called_once_with(
|
||||
mock.ANY, "critical", "link status", "alarm", "critical")
|
||||
_get_endpoint.assert_called_once_with(mock.ANY, "aodh")
|
||||
self.assertEqual(alarm_id, valid_alarm_id, "invalid alarm id")
|
68
collectd_ceilometer/tests/common/test_sender.py
Normal file
68
collectd_ceilometer/tests/common/test_sender.py
Normal file
@ -0,0 +1,68 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2017 Intel Corporation.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Sender tests."""
|
||||
|
||||
import mock
|
||||
import requests
|
||||
import unittest
|
||||
|
||||
from collectd_ceilometer.common import sender as common_sender
|
||||
|
||||
|
||||
class TestSender(unittest.TestCase):
|
||||
"""Test the Sender class."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestSender, self).setUp()
|
||||
self.sender = common_sender.Sender()
|
||||
|
||||
@mock.patch.object(requests, 'post')
|
||||
@mock.patch.object(requests, 'get')
|
||||
def test_perform_request_req_type_get(self, get, post):
|
||||
"""Test the behaviour when performing a get request
|
||||
|
||||
Set-up: None
|
||||
Test: call _perform_request with req_type="get"
|
||||
Expected behaviour:
|
||||
* requests.get is called with appropriate params
|
||||
* requests.post is not called
|
||||
"""
|
||||
self.sender._perform_request("my-url", "some payload",
|
||||
"some headers", req_type="get")
|
||||
|
||||
post.assert_not_called()
|
||||
get.assert_called_with("my-url", params="some payload",
|
||||
headers=mock.ANY, timeout=mock.ANY)
|
||||
|
||||
@mock.patch.object(requests, 'post')
|
||||
@mock.patch.object(requests, 'get')
|
||||
def test_perform_request_req_type_post(self, get, post):
|
||||
"""Test the behaviour when performing a post request
|
||||
|
||||
Set-up: None
|
||||
Test: call _perform_request with req_type="post"
|
||||
Expected behaviour:
|
||||
* requests.get is not called
|
||||
* requests.post is called with appropriate params
|
||||
"""
|
||||
self.sender._perform_request("my-url", "some payload",
|
||||
"some headers", req_type="post")
|
||||
|
||||
get.assert_not_called()
|
||||
post.assert_called_with("my-url", data="some payload",
|
||||
headers=mock.ANY, timeout=mock.ANY)
|
Loading…
Reference in New Issue
Block a user