diff --git a/collectd_ceilometer/ceilometer/sender.py b/collectd_ceilometer/ceilometer/sender.py index a4315b7..5e28d1d 100644 --- a/collectd_ceilometer/ceilometer/sender.py +++ b/collectd_ceilometer/ceilometer/sender.py @@ -17,34 +17,15 @@ from __future__ import division from __future__ import unicode_literals import logging -import threading -import requests -import six - -from collectd_ceilometer.common.keystone_light import ClientV3 -from collectd_ceilometer.common.keystone_light import KeystoneException +from collectd_ceilometer.common import sender as common_sender from collectd_ceilometer.common.settings import Config LOGGER = logging.getLogger(__name__) -HTTP_CREATED = requests.codes['CREATED'] -HTTP_UNAUTHORIZED = requests.codes['UNAUTHORIZED'] -# Lookup dictionary for getting code status name from code status number -# this is the inverse mapping of requests.codes dictionary -STATUS_NAMES = { - status_code: status_name - for status_name, status_code in six.iteritems(requests.codes.__dict__)} - - -def get_status_name(status_code): - """Get an human friendly name for given status code""" - return STATUS_NAMES.get(status_code, str(status_code)) - - -class Sender(object): +class Sender(common_sender.Sender): """Sends the JSON serialized data to Ceilometer""" def __init__(self): @@ -52,143 +33,18 @@ class Sender(object): The cofinguration 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__() - def _authenticate(self): - """Authenticate and renew the authentication token""" + def _on_authenticated(self): + endpoint = self._keystone.get_service_endpoint( + "ceilometer", + Config.instance().CEILOMETER_URL_TYPE) - # if auth_token is available, just return it - if self._auth_token is not None: - return self._auth_token + self._url_base = "{}/v2/meters/%s".format(endpoint) + pass - # 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 + def _create_request_url(self, metername, **kwargs): + return self._url_base % metername - 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._keystone.get_service_endpoint( - "ceilometer", - Config.instance().CEILOMETER_URL_TYPE) - - self._url_base = "{}/v2/meters/%s".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, payload): - """Send the payload to Ceilometer""" - - # get the auth_token - auth_token = self._authenticate() - - # 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 request URL - url = self._url_base % metername - - # send the POST request - try: - return self._perform_request(url, payload, auth_token) - except requests.exceptions.HTTPError as exc: - response = exc.response - - # if the request failed due to an auth error - if response.status_code == HTTP_UNAUTHORIZED: - LOGGER.info('Renewing authentication.') - - # 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: - # and try to repost - return self._perform_request(url, payload, auth_token) - else: - # This is an error and it has to be forwarded - raise - - @staticmethod - def _perform_request(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 = requests.post( - url, data=payload, headers=headers, - timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.)) - - # Raises exception if there was an error - try: - response.raise_for_status() - # pylint: disable=broad-except - except Exception: - exc_info = 1 - raise - else: - exc_info = 0 - finally: - # Log out the result of the request for debugging purpose - LOGGER.debug( - 'Result: %s, %d, %r', - get_status_name(response.status_code), - response.status_code, response.text, exc_info=exc_info) - return response + def _handle_http_error(self, exc, metername, payload, auth_token, **kwargs): + raise exc diff --git a/collectd_ceilometer/ceilometer/writer.py b/collectd_ceilometer/ceilometer/writer.py index 949b559..38b0aad 100644 --- a/collectd_ceilometer/ceilometer/writer.py +++ b/collectd_ceilometer/ceilometer/writer.py @@ -15,7 +15,7 @@ from __future__ import unicode_literals -from collectd_ceilometer.ceilometer.sender import Sender +from collectd_ceilometer.ceilometer import sender as ceilometer_sender from collections import defaultdict from collections import namedtuple import json @@ -89,7 +89,7 @@ class Writer(object): def __init__(self, meters, config): self._meters = meters self._samples = SampleContainer() - self._sender = Sender() + self._sender = ceilometer_sender.Sender() self._config = config def write(self, vl, data): diff --git a/collectd_ceilometer/common/sender.py b/collectd_ceilometer/common/sender.py new file mode 100644 index 0000000..3c37501 --- /dev/null +++ b/collectd_ceilometer/common/sender.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- + +# 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. +"""Ceilometer collectd plugin implementation""" + +from __future__ import division +from __future__ import unicode_literals + +import logging +import threading + +import requests +import six + +from collectd_ceilometer.common.keystone_light import ClientV3 +from collectd_ceilometer.common.keystone_light import KeystoneException +from collectd_ceilometer.common.settings import Config + + +LOGGER = logging.getLogger(__name__) + + +# Lookup dictionary for getting code status name from code status number +# this is the inverse mapping of requests.codes dictionary +STATUS_NAMES = { + status_code: status_name + for status_name, status_code in six.iteritems(requests.codes.__dict__)} + + +def get_status_name(status_code): + """Get an human friendly name for given status code""" + return STATUS_NAMES.get(status_code, str(status_code)) + + +class Sender(object): + """Sends the JSON serialized data to Ceilometer""" + + HTTP_CREATED = requests.codes['CREATED'] + HTTP_UNAUTHORIZED = requests.codes['UNAUTHORIZED'] + HTTP_NOT_FOUND = requests.codes['NOT_FOUND'] + + def __init__(self): + """Create the Sender instance + + 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 + + def _on_authenticated(self): + pass + + 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 + + # 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 + + 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 + + self._on_authenticated() + + 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 _create_request_url(self, metername, *args, **kwargs): + return None + + def _handle_http_error(self, exc, metername, payload, auth_token): + raise exc + + def send(self, metername, payload, **kwargs): + """Send the payload to Ceilometer/Gnocchi""" + + # 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 request URL + url = self._create_request_url(metername, **kwargs) + if url is None: + LOGGER.debug("_create_request_url returned None, aborting send") + return + + # send the POST request + try: + return self._perform_request(url, payload, auth_token) + except requests.exceptions.HTTPError as exc: + response = exc.response + + # if the request failed due to an auth error + if response.status_code == Sender.HTTP_UNAUTHORIZED: + LOGGER.info('Renewing authentication.') + + # 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: + # and try to repost + return self._perform_request(url, payload, auth_token) + else: + # This is an error and it has to be forwarded + self._handle_http_error(exc, metername, payload, + auth_token, **kwargs) + + @classmethod + def _perform_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 = requests.post( + url, data=payload, headers=headers, + timeout=(Config.instance().CEILOMETER_TIMEOUT / 1000.)) + + # Raises exception if there was an error + try: + response.raise_for_status() + # pylint: disable=broad-except + except Exception: + exc_info = 1 + raise + else: + exc_info = 0 + finally: + # Log out the result of the request for debugging purpose + LOGGER.debug( + 'Result: %s, %d, %r', + get_status_name(response.status_code), + response.status_code, response.text, exc_info=exc_info) + return response diff --git a/collectd_ceilometer/gnocchi/sender.py b/collectd_ceilometer/gnocchi/sender.py index e48f824..355a989 100644 --- a/collectd_ceilometer/gnocchi/sender.py +++ b/collectd_ceilometer/gnocchi/sender.py @@ -17,28 +17,17 @@ 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 import sender as common_sender from collectd_ceilometer.common.settings import Config import json import logging -import requests -from requests.exceptions import RequestException -import six -import threading LOGGER = logging.getLogger(__name__) ROOT_LOGGER = logging.getLogger(collectd_ceilometer.__name__) -# HTTP status codes -HTTP_CREATED = 201 -HTTP_UNAUTHORIZED = 401 -HTTP_NOT_FOUND = 404 - - -class Sender(object): +class Sender(common_sender.Sender): """Sends the JSON serialized data to Gnocchi""" def __init__(self): @@ -46,124 +35,30 @@ class Sender(object): The cofinguration 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._meter_ids = {} - def _authenticate(self): - """Authenticate and renew the authentication token""" + def _on_authenticated(self): + # get the uri of service endpoint + endpoint = self._get_endpoint("gnocchi") - # if auth_token is available, just return it - if self._auth_token is not None: - return self._auth_token + self._url_base = "{}/v1/metric/%s/measures".format(endpoint) - # 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 - - 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("gnocchi") - - self._url_base = "{}/v1/metric/%s/measures".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, payload, unit): - """Send the payload to Gnocchi""" - - # 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 request URL + def _create_request_url(self, metername, **kwargs): + unit = kwargs['unit'] metric_id = self._get_metric_id(metername, unit) - url = self._url_base % (metric_id) + return self._url_base % (metric_id) - # send the POST request - result = self._perform_request(url, payload, auth_token) - - if result is None: - return - - 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: - # and try to repost - result = self._perform_request(url, payload, auth_token) - - if result.status_code == HTTP_NOT_FOUND: + def _handle_http_error(self, exc, metername, + payload, auth_token, **kwargs): + response = exc.response + if response.status_code == common_sender.Sender.HTTP_NOT_FOUND: LOGGER.debug("Received 404 error when submitting %s sample, \ creating a new metric", metername) # create metric (endpoint, metername) + unit = kwargs['unit'] metric_id = self._get_metric_id(metername, unit) LOGGER.info('metername: %s, meter_id: %s', metername, metric_id) @@ -172,13 +67,15 @@ class Sender(object): # TODO(emma-l-foley): Add error checking # Submit the sample result = self._perform_request(url, payload, auth_token) + 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) - if result.status_code == HTTP_CREATED: - LOGGER.debug('Result: %s', HTTP_CREATED) else: - LOGGER.info('Result: %s %s', - result.status_code, - result.text) + raise exc def _get_endpoint(self, service): # get the uri of service endpoint @@ -211,32 +108,3 @@ class Sender(object): metric_id = json.loads(result.text)['id'] LOGGER.debug("metric_id=%s", metric_id) return metric_id - - @classmethod - def _perform_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('gnocchi request error: %s', six.text_type(exc)) - finally: - LOGGER.debug('Returning response from _perform_request(): %s', - response.status_code) - return response diff --git a/collectd_ceilometer/gnocchi/writer.py b/collectd_ceilometer/gnocchi/writer.py index 07fe344..89da3ca 100644 --- a/collectd_ceilometer/gnocchi/writer.py +++ b/collectd_ceilometer/gnocchi/writer.py @@ -15,7 +15,7 @@ from __future__ import unicode_literals -from collectd_ceilometer.gnocchi.sender import Sender +from collectd_ceilometer.gnocchi import sender as gnocchi_sender from collections import defaultdict from collections import namedtuple import datetime @@ -82,7 +82,7 @@ class Writer(object): def __init__(self, meters, config): self._meters = meters self._samples = SampleContainer() - self._sender = Sender() + self._sender = gnocchi_sender.Sender() self._config = config def write(self, vl, data): @@ -136,4 +136,4 @@ class Writer(object): # gnocchi samples payload = json.dumps([sample.to_payload() for sample in to_send]) - self._sender.send(metername, payload, unit) + self._sender.send(metername, payload, unit=unit) diff --git a/collectd_ceilometer/tests/ceilometer/test_plugin.py b/collectd_ceilometer/tests/ceilometer/test_plugin.py index f2fc7f8..95b87a5 100644 --- a/collectd_ceilometer/tests/ceilometer/test_plugin.py +++ b/collectd_ceilometer/tests/ceilometer/test_plugin.py @@ -19,16 +19,15 @@ """ from collections import namedtuple + import logging +import mock import requests import unittest -import mock - from collectd_ceilometer.ceilometer import plugin -from collectd_ceilometer.ceilometer import sender from collectd_ceilometer.common import keystone_light - +from collectd_ceilometer.common import sender as common_sender from collectd_ceilometer.tests import match @@ -144,7 +143,7 @@ class TestPlugin(unittest.TestCase): collectd.register_shutdown.assert_called_once_with(instance.shutdown) @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock_collectd() @mock_config(BATCH_SIZE=2) @mock_value() @@ -155,7 +154,7 @@ class TestPlugin(unittest.TestCase): auth_client.get_service_endpoint.return_value =\ 'https://test-ceilometer.tld' - post.return_value.status_code = sender.HTTP_CREATED + post.return_value.status_code = common_sender.Sender.HTTP_CREATED post.return_value.text = 'Created' # init instance @@ -236,7 +235,7 @@ class TestPlugin(unittest.TestCase): timeout=1.0) @mock.patch.object(requests, 'post', spec=callable) - @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() @@ -259,8 +258,8 @@ class TestPlugin(unittest.TestCase): post.assert_not_called() @mock.patch.object(requests, 'post', 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() @@ -290,7 +289,7 @@ class TestPlugin(unittest.TestCase): post.assert_not_called() @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock_collectd() @mock_config() @mock_value() @@ -307,9 +306,9 @@ class TestPlugin(unittest.TestCase): # write the value self.assertRaises(requests.RequestException, instance.write, data) - @mock.patch.object(sender.Sender, '_perform_request', spec=callable) + @mock.patch.object(common_sender.Sender, '_perform_request', spec=callable) @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock_collectd() @mock_config() @mock_value() @@ -362,7 +361,7 @@ class TestPlugin(unittest.TestCase): ]) @mock.patch.object(requests, 'post', spec=callable) - @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() @@ -416,7 +415,7 @@ class TestPlugin(unittest.TestCase): timeout=1.0) @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock.patch.object(plugin, 'Writer', autospec=True) @mock.patch.object(plugin, 'LOGGER', autospec=True) @mock_collectd() diff --git a/collectd_ceilometer/tests/gnocchi/test_plugin.py b/collectd_ceilometer/tests/gnocchi/test_plugin.py index 0a46f34..f2df1e9 100644 --- a/collectd_ceilometer/tests/gnocchi/test_plugin.py +++ b/collectd_ceilometer/tests/gnocchi/test_plugin.py @@ -23,8 +23,9 @@ import requests import unittest from collectd_ceilometer.common.keystone_light import KeystoneException +from collectd_ceilometer.common import sender as common_sender from collectd_ceilometer.gnocchi import plugin -from collectd_ceilometer.gnocchi import sender +from collectd_ceilometer.gnocchi import sender as gnocchi_sender from collectd_ceilometer.tests import match @@ -138,9 +139,9 @@ class TestPlugin(unittest.TestCase): collectd.register_write.assert_called_once_with(instance.write) collectd.register_shutdown.assert_called_once_with(instance.shutdown) - @mock.patch.object(sender.Sender, '_get_metric_id', autospec=True) + @mock.patch.object(gnocchi_sender.Sender, '_get_metric_id', autospec=True) @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock_collectd() @mock_config(BATCH_SIZE=2) @mock_value() @@ -151,7 +152,7 @@ class TestPlugin(unittest.TestCase): auth_client.get_service_endpoint.return_value = \ 'https://test-gnocchi.tld' - post.return_value.status_code = sender.HTTP_CREATED + post.return_value.status_code = common_sender.Sender.HTTP_CREATED post.return_value.text = 'Created' get_metric_id.return_value = 'my-metric-id' @@ -218,8 +219,8 @@ class TestPlugin(unittest.TestCase): timeout=1.0) @mock.patch.object(requests, 'post', 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() @@ -249,8 +250,8 @@ class TestPlugin(unittest.TestCase): # no requests method has been called post.assert_not_called() - @mock.patch.object(sender.Sender, '_perform_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() @@ -267,9 +268,9 @@ class TestPlugin(unittest.TestCase): # write the value self.assertRaises(requests.RequestException, instance.write, data) - @mock.patch.object(sender.Sender, '_get_metric_id', autospec=True) + @mock.patch.object(gnocchi_sender.Sender, '_get_metric_id', autospec=True) @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock_collectd() @mock_config() @mock_value() @@ -327,7 +328,7 @@ class TestPlugin(unittest.TestCase): self.assertEqual(token, 'New test auth token') @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock.patch.object(plugin, 'Writer', autospec=True) @mock.patch.object(plugin, 'LOGGER', autospec=True) @mock_collectd() @@ -346,7 +347,7 @@ class TestPlugin(unittest.TestCase): self.assertRaises(ValueError, instance.write, data) @mock.patch.object(requests, 'post', spec=callable) - @mock.patch.object(sender, 'ClientV3', autospec=True) + @mock.patch.object(common_sender, 'ClientV3', autospec=True) @mock.patch.object(plugin, 'Writer', autospec=True) @mock.patch.object(plugin, 'LOGGER', autospec=True) @mock_collectd()