Monasca Ceilometer in sync with Ceilometer master

Following changes are necessary to keep monasca-ceilometer
in sync with Ceilometer upstream

* Remove api/health.py (since ceilometer v2 api was removed)
* Remove storage/impl_monasca.py (since ceilometer storage drivers were removed)
* Updated opts.py and service.py (to keep in sync with ceilometer master)
* removed api, storage driver tests
* updated /etc/ceilometer/ceilometer.conf with new [monasca] options

Change-Id: I044f3e512db52a128bac8d5c5dcac76549a3b1c2
This commit is contained in:
Ashwin Agate 2018-02-09 04:58:18 -08:00
parent e81be04ef7
commit 4a2c73685f
19 changed files with 67 additions and 3870 deletions

View File

@ -1,81 +0,0 @@
#
# (c) Copyright 2018 SUSE LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log
LOG = log.getLogger(__name__)
def filter_factory(global_config, **local_conf):
"""Returns a WSGI Filter."""
conf = global_config.copy()
conf.update(local_conf)
LOG.debug('Filter Factory')
def health_filter(app):
return HealthFilterApi(app, conf)
return health_filter
class HealthFilterApi(object):
def __init__(self, app, conf):
self.conf = conf
self.app = app
self.db = None
def __call__(self, environ, start_response):
"""Handle the incoming request and filters it.
Interjects the request and acts on it only if
it is related to the filter functionality, otherwise
it just let the request pass through to the app.
"""
LOG.debug('Health Check Filter')
if environ['PATH_INFO'].startswith('/v2/health'):
response_code = '204 No Content'
if environ['REQUEST_METHOD'] != 'HEAD':
response_code = '200 OK'
# Commenting out healthcheck behavior when request method
# is not of type HEAD.# As it creates load on monasca vertica
# FIXME: fix this when monasca creates get versions api in
# in Monascaclient. USe that instead
# try:
# if not self.db:
# self.db = storage.get_connection_from_config(cfg.CONF)
# meters = self.db.get_meters(unique=True, limit=1)
# if not meters:
# response_code = '503 Backend Unavailable'
# except Exception as e:
# response_code = '503 Backend Unavailable'
# LOG.warning('DB health check connection failed: %s', e)
# self.db = None
resp = MiniResp(response_code, environ)
start_response(response_code, resp.headers)
return resp.body
else:
return self.app(environ, start_response)
class MiniResp(object):
def __init__(self, message, env, headers=[]):
if env['REQUEST_METHOD'] == 'HEAD':
self.body = ['']
else:
self.body = [message]
self.headers = list(headers)

View File

@ -104,11 +104,17 @@ OPTS = [
'Monasca. These include metrics that failed to '
'publish on first attempt and failed metrics that'
' maxed out their retries.'),
# from impl_monasca
cfg.IntOpt('default_stats_period',
default=300,
help='Default period (in seconds) to use for querying stats '
'in case no period specified in the stats API call.'),
cfg.IntOpt('database_max_retries',
default=3,
help='Maximum number of retry attempts of connecting to '
'database failure.'),
cfg.IntOpt('database_retry_interval',
default=60,
help='Frequency of attempting a retry connecting to database'),
]

View File

@ -54,8 +54,8 @@ class Client(object):
def __init__(self, conf, parsed_url):
self.conf = conf
self._retry_interval = self.conf.database.retry_interval
self._max_retries = self.conf.database.max_retries or 1
self._retry_interval = self.conf.monasca.database_retry_interval
self._max_retries = self.conf.monasca.database_max_retries or 1
# enable monasca api pagination
self._enable_api_pagination = self.conf.monasca.enable_api_pagination
# NOTE(zqfan): There are many concurrency requests while using

View File

@ -18,25 +18,15 @@ import socket
from keystoneauth1 import loading
from oslo_config import cfg
import ceilometer.agent.manager
import ceilometer.api.app
import ceilometer.api.controllers.v2.root
import ceilometer.collector
import ceilometer.compute.discovery
import ceilometer.compute.virt.inspector
import ceilometer.compute.virt.libvirt.utils
import ceilometer.compute.virt.vmware.inspector
import ceilometer.compute.virt.xenapi.inspector
import ceilometer.dispatcher
import ceilometer.dispatcher.file
import ceilometer.dispatcher.gnocchi_opts
import ceilometer.dispatcher.http
import ceilometer.event.converter
import ceilometer.exchange_control
import ceilometer.hardware.discovery
import ceilometer.hardware.pollsters.generic
import ceilometer.image.discovery
import ceilometer.ipmi.notifications.ironic
import ceilometer.ipmi.platform.intel_node_manager
import ceilometer.ipmi.pollsters
import ceilometer.keystone_client
@ -48,11 +38,11 @@ import ceilometer.notification
import ceilometer.nova_client
import ceilometer.objectstore.rgw
import ceilometer.objectstore.swift
import ceilometer.pipeline
import ceilometer.pipeline.base
import ceilometer.polling.manager
import ceilometer.publisher.messaging
import ceilometer.publisher.utils
import ceilometer.sample
import ceilometer.storage
import ceilometer.utils
import ceilometer.volume.discovery
@ -82,22 +72,14 @@ def list_opts():
# This have been removed due to a recursive import issue
return [
('DEFAULT',
itertools.chain(ceilometer.agent.manager.OPTS,
ceilometer.api.app.OPTS,
itertools.chain(ceilometer.polling.manager.OPTS,
ceilometer.compute.virt.inspector.OPTS,
ceilometer.compute.virt.libvirt.utils.OPTS,
ceilometer.dispatcher.OPTS,
ceilometer.ipmi.notifications.ironic.OPTS,
ceilometer.nova_client.OPTS,
ceilometer.objectstore.swift.OPTS,
ceilometer.pipeline.OPTS,
ceilometer.pipeline.base.OPTS,
ceilometer.sample.OPTS,
ceilometer.utils.OPTS,
ceilometer.exchange_control.EXCHANGE_OPTS,
OPTS)),
('api', itertools.chain(ceilometer.api.app.API_OPTS,
ceilometer.api.controllers.v2.root.API_OPTS)),
('collector', ceilometer.collector.OPTS),
('compute', ceilometer.compute.discovery.OPTS),
('coordination', [
cfg.StrOpt(
@ -113,11 +95,28 @@ def list_opts():
help='Number of seconds between checks to see if group '
'membership has changed'),
]),
('database', ceilometer.storage.OPTS),
('dispatcher_file', ceilometer.dispatcher.file.OPTS),
('dispatcher_http', ceilometer.dispatcher.http.http_dispatcher_opts),
('dispatcher_gnocchi',
ceilometer.dispatcher.gnocchi_opts.dispatcher_opts),
('dispatcher_gnocchi', (
cfg.StrOpt(
'filter_project',
deprecated_for_removal=True,
default='gnocchi',
help='Gnocchi project used to filter out samples '
'generated by Gnocchi service activity'),
cfg.StrOpt(
'archive_policy',
deprecated_for_removal=True,
help='The archive policy to use when the dispatcher '
'create a new metric.'),
cfg.StrOpt(
'resources_definition_file',
deprecated_for_removal=True,
default='gnocchi_resources.yaml',
help=('The Yaml file that defines mapping between samples '
'and gnocchi resources/metrics')),
cfg.FloatOpt(
'request_timeout', default=6.05, min=0.0,
deprecated_for_removal=True,
help='Number of seconds before request to gnocchi times out'))),
('event', ceilometer.event.converter.OPTS),
('hardware', itertools.chain(
ceilometer.hardware.discovery.OPTS,
@ -130,7 +129,7 @@ def list_opts():
('notification',
itertools.chain(ceilometer.notification.OPTS,
ceilometer.notification.EXCHANGES_OPTS)),
('polling', ceilometer.agent.manager.POLLING_OPTS),
('polling', ceilometer.polling.manager.POLLING_OPTS),
('publisher', ceilometer.publisher.utils.OPTS),
('publisher_notifier', ceilometer.publisher.messaging.NOTIFIER_OPTS),
('rgw_admin_credentials', ceilometer.objectstore.rgw.CREDENTIAL_OPTS),

View File

@ -16,13 +16,10 @@
import sys
from oslo_config import cfg
from oslo_db import options as db_options
import oslo_i18n
from oslo_log import log
from oslo_policy import opts as policy_opts
# from oslo_reports import guru_meditation_report as gmr
from ceilometer.conf import defaults
from ceilometer import keystone_client
from ceilometer import messaging
from ceilometer import opts
@ -48,9 +45,6 @@ def prepare_service(argv=None, config_files=None, conf=None):
['futurist=INFO', 'neutronclient=INFO',
'keystoneclient=INFO'])
log.set_defaults(default_log_levels=log_levels)
defaults.set_cors_middleware_defaults()
policy_opts.set_defaults(conf)
db_options.set_defaults(conf)
conf(argv[1:], project='ceilometer', validate_default_values=True,
version=version.version_info.version_string(),
@ -62,11 +56,6 @@ def prepare_service(argv=None, config_files=None, conf=None):
utils.setup_root_helper(conf)
sample.setup(conf)
# NOTE(liusheng): guru cannot run with service under apache daemon, so when
# ceilometer-api running with mod_wsgi, the argv is [], we don't start
# guru.
if argv:
# gmr.TextGuruMeditation.setup_autorun(version)
pass
# gmr.TextGuruMeditation.setup_autorun(version)
messaging.setup()
return conf

File diff suppressed because it is too large Load Diff

View File

@ -1,185 +0,0 @@
#
# Copyright 2012 New Dream Network, LLC (DreamHost)
# (c) Copyright 2018 SUSE LLC
#
# 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.
"""Base classes for API tests.
"""
from oslo_policy import opts
import pecan
import pecan.testing
from ceilometer.api import rbac
from ceilometer import monasca_ceilometer_opts
from ceilometer import service
from ceilometer.tests import db as db_test_base
class FunctionalTest(db_test_base.TestBase):
"""Used for functional tests of Pecan controllers.
Used in case when you need to test your literal application and its
integration with the framework.
"""
PATH_PREFIX = ''
def setUp(self):
super(FunctionalTest, self).setUp()
self.CONF = service.prepare_service([], [])
self.CONF.register_opts(list(monasca_ceilometer_opts.OPTS),
'monasca')
self.setup_messaging(self.CONF)
opts.set_defaults(self.CONF)
self.CONF.set_override("policy_file",
self.path_get('etc/ceilometer/policy.json'),
group='oslo_policy')
self.CONF.set_override('gnocchi_is_enabled', False, group='api')
self.CONF.set_override('aodh_is_enabled', False, group='api')
self.CONF.set_override('panko_is_enabled', False, group='api')
self.app = self._make_app()
def _make_app(self, enable_acl=False):
self.config = {
'app': {
'root': 'ceilometer.api.controllers.root.RootController',
'modules': ['ceilometer.api'],
'enable_acl': enable_acl,
},
'wsme': {
'debug': True,
},
}
return pecan.testing.load_test_app(self.config,
conf=self.CONF)
def tearDown(self):
super(FunctionalTest, self).tearDown()
rbac.reset()
pecan.set_config({}, overwrite=True)
def put_json(self, path, params, expect_errors=False, headers=None,
extra_environ=None, status=None):
"""Sends simulated HTTP PUT request to Pecan test app.
:param path: url path of target service
:param params: content for wsgi.input of request
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param extra_environ: A dictionary of environ variables to send along
with the request
:param status: Expected status code of response
"""
return self.post_json(path=path, params=params,
expect_errors=expect_errors,
headers=headers, extra_environ=extra_environ,
status=status, method="put")
def post_json(self, path, params, expect_errors=False, headers=None,
method="post", extra_environ=None, status=None):
"""Sends simulated HTTP POST request to Pecan test app.
:param path: url path of target service
:param params: content for wsgi.input of request
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param method: Request method type. Appropriate method function call
should be used rather than passing attribute in.
:param extra_environ: A dictionary of environ variables to send along
with the request
:param status: Expected status code of response
"""
full_path = self.PATH_PREFIX + path
response = getattr(self.app, "%s_json" % method)(
str(full_path),
params=params,
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors
)
return response
def delete(self, path, expect_errors=False, headers=None,
extra_environ=None, status=None):
"""Sends simulated HTTP DELETE request to Pecan test app.
:param path: url path of target service
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param extra_environ: A dictionary of environ variables to send along
with the request
:param status: Expected status code of response
"""
full_path = self.PATH_PREFIX + path
response = self.app.delete(str(full_path),
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors)
return response
def get_json(self, path, expect_errors=False, headers=None,
extra_environ=None, q=None, groupby=None, status=None,
override_params=None, **params):
"""Sends simulated HTTP GET request to Pecan test app.
:param path: url path of target service
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param extra_environ: A dictionary of environ variables to send along
with the request
:param q: list of queries consisting of: field, value, op, and type
keys
:param groupby: list of fields to group by
:param status: Expected status code of response
:param override_params: literally encoded query param string
:param params: content for wsgi.input of request
"""
q = q or []
groupby = groupby or []
full_path = self.PATH_PREFIX + path
if override_params:
all_params = override_params
else:
query_params = {'q.field': [],
'q.value': [],
'q.op': [],
'q.type': [],
}
for query in q:
for name in ['field', 'op', 'value', 'type']:
query_params['q.%s' % name].append(query.get(name, ''))
all_params = {}
all_params.update(params)
if q:
all_params.update(query_params)
if groupby:
all_params.update({'groupby': groupby})
response = self.app.get(full_path,
params=all_params,
headers=headers,
extra_environ=extra_environ,
expect_errors=expect_errors,
status=status)
if not expect_errors:
response = response.json
return response

View File

@ -1,20 +0,0 @@
#
# Copyright 2012 New Dream Network, LLC (DreamHost)
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from ceilometer.tests.functional import api
class FunctionalTest(api.FunctionalTest):
PATH_PREFIX = '/v2'

View File

@ -1,270 +0,0 @@
#
# Copyright 2015 Hewlett Packard
# (c) Copyright 2018 SUSE LLC
#
# 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.
"""Test api with Monasca driver
"""
import fixtures
import mock
import pkg_resources
from oslo_config import cfg
from stevedore import driver
from stevedore import extension
from ceilometer import monasca_ceilometer_opts
from ceilometer import service
from ceilometer import storage
from ceilometer.tests import base as test_base
from oslo_policy import opts
import pecan
import pecan.testing
OPT_GROUP_NAME = 'keystone_authtoken'
cfg.CONF.import_group(OPT_GROUP_NAME, "keystonemiddleware.auth_token")
class TestApi(test_base.BaseTestCase):
# TODO(Unresolved comment from git review: Can we include CM-api test
# cases for get_samples in
# ceilometer/tests/api/v2/test_api_with_monasca_driver.py?)
def _get_driver_from_entry_point(self, entry_point, namespace):
ep = pkg_resources.EntryPoint.parse(entry_point)
a_driver = extension.Extension('con_driver', ep,
ep.load(require=False), None)
mgr = driver.DriverManager.make_test_instance(
a_driver, namespace=namespace
)
mgr._init_plugins([a_driver])
return mgr
def get_connection_with_mock_driver_manager(self, conf, url, namespace):
mgr = self._get_driver_from_entry_point(
entry_point='monasca = ceilometer.storage.impl_monasca:Connection',
namespace='ceilometer.metering.storage')
return mgr.driver(conf, url)
def get_publisher_with_mock_driver_manager(self, conf, url, namespace):
mgr = self._get_driver_from_entry_point(
entry_point='monasca = ceilometer.publisher.monclient:'
'MonascaPublisher',
namespace='ceilometer.sample.publisher')
return mgr.driver(conf, url)
def setUp(self):
super(TestApi, self).setUp()
self.PATH_PREFIX = '/v2'
self.CONF = service.prepare_service([], [])
self.CONF.register_opts(list(monasca_ceilometer_opts.OPTS),
'monasca')
self.setup_messaging(self.CONF)
opts.set_defaults(self.CONF)
self.CONF.set_override("policy_file",
self.path_get('etc/ceilometer/policy.json'),
group='oslo_policy')
self.CONF.import_opt('pipeline_cfg_file', 'ceilometer.pipeline')
self.CONF.set_override(
'pipeline_cfg_file',
self.path_get('etc/ceilometer/monasca_pipeline.yaml')
)
self.CONF.import_opt('monasca_mappings',
'ceilometer.publisher.monasca_data_filter',
group='monasca')
self.CONF.set_override(
'monasca_mappings',
self.path_get('etc/ceilometer/monasca_field_definitions.yaml'),
group='monasca'
)
with mock.patch("ceilometer.monasca_client.Client") as mock_client,\
mock.patch('ceilometer.storage.get_connection') as \
get_storage_conn, \
mock.patch('ceilometer.publisher.get_publisher') as get_pub:
get_storage_conn.side_effect = (
self.get_connection_with_mock_driver_manager)
get_pub.side_effect = self.get_publisher_with_mock_driver_manager
self.mock_mon_client = mock_client
self.conn = storage.get_connection(
self.CONF,
'monasca://127.0.0.1:8080',
'ceilometer.metering.storage')
self.useFixture(fixtures.MockPatch(
'ceilometer.storage.get_connection',
return_value=self.conn))
self.app = self._make_app()
def _make_app(self, enable_acl=False):
self.config = {
'app': {
'root': 'ceilometer.api.controllers.root.RootController',
'modules': ['ceilometer.api'],
'enable_acl': enable_acl,
},
'wsme': {
'debug': True,
},
}
return pecan.testing.load_test_app(self.config,
conf=self.CONF)
def get_json(self, path, expect_errors=False, headers=None,
extra_environ=None, q=None, groupby=None, status=None,
override_params=None, **params):
"""Sends simulated HTTP GET request to Pecan test app.
:param path: url path of target service
:param expect_errors: boolean value whether an error is expected based
on request
:param headers: A dictionary of headers to send along with the request
:param extra_environ: A dictionary of environ variables to send along
with the request
:param q: list of queries consisting of: field, value, op, and type
keys
:param groupby: list of fields to group by
:param status: Expected status code of response
:param override_params: literally encoded query param string
:param params: content for wsgi.input of request
"""
q = q or []
groupby = groupby or []
full_path = self.PATH_PREFIX + path
if override_params:
all_params = override_params
else:
query_params = {'q.field': [],
'q.value': [],
'q.op': [],
'q.type': [],
}
for query in q:
for name in ['field', 'op', 'value', 'type']:
query_params['q.%s' % name].append(query.get(name, ''))
all_params = {}
all_params.update(params)
if q:
all_params.update(query_params)
if groupby:
all_params.update({'groupby': groupby})
response = self.app.get(full_path,
params=all_params,
headers=headers,
extra_environ=extra_environ,
expect_errors=expect_errors,
status=status)
if not expect_errors:
response = response.json
return response
class TestListMeters(TestApi):
def setUp(self):
super(TestListMeters, self).setUp()
self.meter_payload = [{'name': 'm1',
'dimensions': {
'type': 'gauge',
'unit': 'any',
'resource_id': 'resource-1',
'project_id': 'project-1',
'user_id': 'user-1',
'source': 'source'}},
{'name': 'm2',
'dimensions': {
'type': 'delta',
'unit': 'any',
'resource_id': 'resource-1',
'project_id': 'project-1',
'user_id': 'user-1',
'source': 'source'}}]
def test_empty(self):
data = self.get_json('/meters')
self.assertEqual([], data)
def test_get_meters(self):
mnl_mock = self.mock_mon_client().metrics_list
mnl_mock.return_value = self.meter_payload
data = self.get_json('/meters')
self.assertEqual(True, mnl_mock.called)
self.assertEqual(2, mnl_mock.call_count,
"impl_monasca.py calls the metrics_list api twice.")
self.assertEqual(2, len(data))
(self.assertIn(meter['name'],
[payload.get('name') for payload in
self.meter_payload]) for meter in data)
def test_get_meters_query_with_project_resource(self):
"""Test meter name conversion for project-id and resource-id.
Previous versions of the monasca client did not do this conversion.
Pre-Newton expected:
'dimensions': {'project_id': u'project-1','resource_id': u'resource-1'}
Newton expected:
'dimensions': {'hostname': u'resource-1','project_id': u'project-1'}
"""
mnl_mock = self.mock_mon_client().metrics_list
mnl_mock.return_value = self.meter_payload
self.get_json('/meters',
q=[{'field': 'resource_id',
'value': 'resource-1'},
{'field': 'project_id',
'value': 'project-1'}])
self.assertEqual(True, mnl_mock.called)
self.assertEqual(4, mnl_mock.call_count,
"impl_monasca.py expected to make 4 calls to mock.")
# Note - previous versions of the api included a limit value
self.assertEqual(dict(dimensions=dict(hostname=u'resource-1',
project_id=u'project-1')),
mnl_mock.call_args[1])
def test_get_meters_query_with_user(self):
mnl_mock = self.mock_mon_client().metrics_list
mnl_mock.return_value = self.meter_payload
self.get_json('/meters',
q=[{'field': 'user_id',
'value': 'user-1'}])
self.assertEqual(True, mnl_mock.called)
self.assertEqual(2, mnl_mock.call_count,
"impl_monasca.py calls the metrics_list api twice.")
# Note - previous versions of the api included a limit value
self.assertEqual(dict(dimensions=dict(user_id=u'user-1')),
mnl_mock.call_args[1])
# TODO(joadavis) Test a bad query parameter
# Like using 'hostname' instead of 'resource_id'
# Expected result with bad parameter:
# webtest.app.AppError: Bad response: 400 Bad Request

View File

@ -1,601 +0,0 @@
#
# Copyright 2016 Hewlett Packard
# (c) Copyright 2018 SUSE LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import collections
import os
import fixtures
import mock
from oslo_utils import fileutils
from oslo_utils import timeutils
from oslotest import base
import six
import yaml
from ceilometer.ceilosca_mapping import ceilosca_mapping
from ceilometer.ceilosca_mapping.ceilosca_mapping import (
CeiloscaMappingDefinition)
from ceilometer.ceilosca_mapping.ceilosca_mapping import (
CeiloscaMappingDefinitionException)
from ceilometer.ceilosca_mapping.ceilosca_mapping import PipelineReader
from ceilometer import monasca_ceilometer_opts
from ceilometer import service
from ceilometer import storage
from ceilometer.storage import impl_monasca
from ceilometer.storage import models as storage_models
MONASCA_MEASUREMENT = {
"id": "fef26f9d27f8027ea44b940cf3626fc398f7edfb",
"name": "fake_metric",
"dimensions": {
"resource_id": "2fe6e3a9-9bdf-4c98-882c-a826cf0107a1",
"cloud_name": "helion-poc-hlm-003",
"component": "vm",
"control_plane": "control-plane-1",
"service": "compute",
"device": "tap3356676e-a5",
"tenant_id": "50ce24dd577c43879cede72b77224e2f",
"hostname": "hlm003-cp1-comp0003-mgmt",
"cluster": "compute",
"zone": "nova"
},
"columns": ["timestamp", "value", "value_meta"],
"measurements": [["2016-05-23T22:22:42.000Z", 54.0, {
"audit_period_ending": "None",
"audit_period_beginning": "None",
"host": "network.hlm003-cp1-c1-m2-mgmt",
"availability_zone": "None",
"event_type": "subnet.create.end",
"enable_dhcp": "true",
"gateway_ip": "10.43.0.1",
"ip_version": "4",
"cidr": "10.43.0.0/28"}]]
}
MONASCA_VALUE_META = {
'audit_period_beginning': 'None',
'audit_period_ending': 'None',
'availability_zone': 'None',
'cidr': '10.43.0.0/28',
'enable_dhcp': 'true',
'event_type': 'subnet.create.end',
'gateway_ip': '10.43.0.1',
'host': 'network.hlm003-cp1-c1-m2-mgmt',
'ip_version': '4'
}
class TestCeiloscaMapping(base.BaseTestCase):
pipeline_data = yaml.dump({
'sources': [{
'name': 'test_pipeline',
'interval': 1,
'meters': ['testbatch', 'testbatch2'],
'resources': ['alpha', 'beta', 'gamma', 'delta'],
'sinks': ['test_sink']}],
'sinks': [{
'name': 'test_sink',
'transformers': [],
'publishers': ["test"]}]
})
cfg = yaml.dump({
'meter_metric_map': [{
'user_id': '$.dimensions.user_id',
'name': 'fake_meter',
'resource_id': '$.dimensions.resource_id',
'region': 'NA',
'monasca_metric_name': 'fake_metric',
'source': 'NA',
'project_id': '$.dimensions.tenant_id',
'type': 'gauge',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}, {
'user_id': '$.dimensions.user_id',
'name': 'fake_meter2',
'resource_id': '$.dimensions.resource_id',
'region': 'NA',
'monasca_metric_name': 'fake_metric2',
'source': 'NA',
'project_id': '$.dimensions.project_id',
'type': 'delta',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}, {
'user_id': '$.dimensions.user_id',
'name': 'fake_meter3',
'resource_id': '$.dimensions.hostname',
'region': 'NA',
'monasca_metric_name': 'fake_metric3',
'source': 'NA',
'project_id': '$.dimensions.project_id',
'type': 'delta',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}
]
})
def setup_pipeline_file(self, pipeline_data):
if six.PY3:
pipeline_data = pipeline_data.encode('utf-8')
pipeline_cfg_file = fileutils.write_to_tempfile(content=pipeline_data,
prefix="pipeline",
suffix="yaml")
self.addCleanup(os.remove, pipeline_cfg_file)
return pipeline_cfg_file
def setup_ceilosca_mapping_def_file(self, cfg):
if six.PY3:
cfg = cfg.encode('utf-8')
ceilosca_mapping_file = fileutils.write_to_tempfile(
content=cfg, prefix='ceilosca_mapping', suffix='yaml')
self.addCleanup(os.remove, ceilosca_mapping_file)
return ceilosca_mapping_file
class TestGetPipelineReader(TestCeiloscaMapping):
def setUp(self):
super(TestGetPipelineReader, self).setUp()
self.CONF = service.prepare_service([], [])
self.CONF.register_opts(list(monasca_ceilometer_opts.OPTS),
'monasca')
def test_pipeline_reader(self):
pipeline_cfg_file = self.setup_pipeline_file(
self.pipeline_data)
self.CONF.set_override("pipeline_cfg_file", pipeline_cfg_file)
test_pipeline_reader = PipelineReader(self.CONF)
self.assertEqual(set(['testbatch', 'testbatch2']),
test_pipeline_reader.get_pipeline_meters()
)
class TestMappingDefinition(base.BaseTestCase):
def test_mapping_definition(self):
cfg = dict(name="network.outgoing.rate",
monasca_metric_name="vm.net.out_bytes_sec",
resource_id="$.dimensions.resource_id",
project_id="$.dimensions.tenant_id",
user_id="$.dimensions.user_id",
region="NA",
type="gauge",
unit="B/s",
source="NA",
resource_metadata="$.measurements[0][2]")
handler = CeiloscaMappingDefinition(cfg)
self.assertIsNone(handler.parse_fields("user_id", MONASCA_MEASUREMENT))
self.assertEqual("2fe6e3a9-9bdf-4c98-882c-a826cf0107a1",
handler.parse_fields("resource_id",
MONASCA_MEASUREMENT))
self.assertEqual("50ce24dd577c43879cede72b77224e2f",
handler.parse_fields("project_id",
MONASCA_MEASUREMENT))
self.assertEqual(MONASCA_VALUE_META,
handler.parse_fields("resource_metadata",
MONASCA_MEASUREMENT))
self.assertEqual("$.dimensions.tenant_id", handler.cfg["project_id"])
def test_config_required_missing_fields(self):
cfg = dict()
try:
CeiloscaMappingDefinition(cfg)
except CeiloscaMappingDefinitionException as e:
self.assertEqual("Required fields ["
"'name', 'monasca_metric_name', 'type', 'unit', "
"'source', 'resource_metadata', 'resource_id', "
"'project_id', 'user_id', 'region'] "
"not specified", e.message)
def test_bad_type_cfg_definition(self):
cfg = dict(name="fake_meter",
monasca_metric_name="fake_metric",
resource_id="$.dimensions.resource_id",
project_id="$.dimensions.tenant_id",
user_id="$.dimensions.user_id",
region="NA",
type="foo",
unit="B/s",
source="NA",
resource_metadata="$.measurements[0][2]")
try:
CeiloscaMappingDefinition(cfg)
except CeiloscaMappingDefinitionException as e:
self.assertEqual("Invalid type foo specified", e.message)
class TestMappedCeiloscaMetricProcessing(TestCeiloscaMapping):
def setUp(self):
super(TestMappedCeiloscaMetricProcessing, self).setUp()
self.CONF = service.prepare_service([], [])
self.CONF.register_opts(list(monasca_ceilometer_opts.OPTS),
'monasca')
def test_fallback_mapping_file_path(self):
self.useFixture(fixtures.MockPatchObject(self.CONF,
'find_file',
return_value=None))
fall_bak_path = ceilosca_mapping.get_config_file(self.CONF)
self.assertIn("ceilosca_mapping/data/ceilosca_mapping.yaml",
fall_bak_path)
@mock.patch('ceilometer.ceilosca_mapping.ceilosca_mapping.LOG')
def test_bad_meter_definition_skip(self, LOG):
cfg = yaml.dump({
'meter_metric_map': [{
'user_id': '$.dimensions.user_id',
'name': 'fake_meter',
'resource_id': '$.dimensions.resource_id',
'region': 'NA',
'monasca_metric_name': 'fake_metric',
'source': 'NA',
'project_id': '$.dimensions.tenant_id',
'type': 'gauge',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}, {
'user_id': '$.dimensions.user_id',
'name': 'fake_meter',
'resource_id': '$.dimensions.resource_id',
'region': 'NA',
'monasca_metric_name': 'fake_metric',
'source': 'NA',
'project_id': '$.dimensions.tenant_id',
'type': 'foo',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}]
})
ceilosca_mapping_file = self.setup_ceilosca_mapping_def_file(cfg)
self.CONF.set_override('ceilometer_monasca_metrics_mapping',
ceilosca_mapping_file, group='monasca')
data = ceilosca_mapping.setup_ceilosca_mapping_config(self.CONF)
meter_loaded = ceilosca_mapping.load_definitions(data)
self.assertEqual(1, len(meter_loaded))
LOG.error.assert_called_with(
"Error loading Ceilometer Monasca Mapping Definition : "
"Invalid type foo specified")
def test_list_of_meters_returned(self):
ceilosca_mapping_file = self.setup_ceilosca_mapping_def_file(self.cfg)
self.CONF.set_override('ceilometer_monasca_metrics_mapping',
ceilosca_mapping_file, group='monasca')
ceilosca_mapper = ceilosca_mapping\
.ProcessMappedCeiloscaMetric(self.CONF)
ceilosca_mapper.reinitialize(self.CONF)
self.assertItemsEqual(['fake_metric', 'fake_metric2', 'fake_metric3'],
ceilosca_mapper.get_list_monasca_metrics().keys()
)
def test_monasca_metric_name_map_ceilometer_meter(self):
cfg = yaml.dump({
'meter_metric_map': [{
'user_id': '$.dimensions.user_id',
'name': 'fake_meter',
'resource_id': '$.dimensions.resource_id',
'region': 'NA',
'monasca_metric_name': 'fake_metric',
'source': 'NA',
'project_id': '$.dimensions.tenant_id',
'type': 'gauge',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}]
})
ceilosca_mapping_file = self.setup_ceilosca_mapping_def_file(cfg)
self.CONF.set_override('ceilometer_monasca_metrics_mapping',
ceilosca_mapping_file, group='monasca')
ceilosca_mapper = ceilosca_mapping\
.ProcessMappedCeiloscaMetric(self.CONF)
ceilosca_mapper.reinitialize(self.CONF)
self.assertEqual('fake_metric',
ceilosca_mapper.get_monasca_metric_name('fake_meter')
)
self.assertEqual('$.dimensions.tenant_id',
ceilosca_mapper.
get_ceilosca_mapped_definition_key_val('fake_metric',
'project_id'))
# This Class will only test the driver for the mapped meteric
# Impl_Monasca Tests will be doing exhaustive tests for non mapped metrics
@mock.patch("ceilometer.storage.impl_monasca.MonascaDataFilter")
class TestMoanscaDriverForMappedMetrics(TestCeiloscaMapping):
Aggregate = collections.namedtuple("Aggregate", ['func', 'param'])
def setUp(self):
super(TestMoanscaDriverForMappedMetrics, self).setUp()
self.CONF = service.prepare_service([], [])
self.CONF.register_opts(list(monasca_ceilometer_opts.OPTS),
'monasca')
pipeline_cfg_file = self.setup_pipeline_file(self.pipeline_data)
self.CONF.set_override("pipeline_cfg_file", pipeline_cfg_file)
ceilosca_mapping_file = self.setup_ceilosca_mapping_def_file(self.cfg)
self.CONF.set_override('ceilometer_monasca_metrics_mapping',
ceilosca_mapping_file, group='monasca')
ceilosca_mapper = ceilosca_mapping\
.ProcessMappedCeiloscaMetric(self.CONF)
ceilosca_mapper.reinitialize(self.CONF)
def test_get_samples_for_mapped_meters(self, mdf_mock):
with mock.patch("ceilometer.monasca_client.Client") as mock_client:
conn = impl_monasca.Connection(self.CONF, "127.0.0.1:8080")
ml_mock = mock_client().measurements_list
# TODO(this test case needs more work)
ml_mock.return_value = ([MONASCA_MEASUREMENT])
sample_filter = storage.SampleFilter(
meter='fake_meter',
start_timestamp='2015-03-20T00:00:00Z')
results = list(conn.get_samples(sample_filter))
self.assertEqual(True, ml_mock.called)
self.assertEqual('fake_meter', results[0].counter_name)
self.assertEqual(54.0, results[0].counter_volume)
self.assertEqual('gauge', results[0].counter_type)
self.assertEqual('2fe6e3a9-9bdf-4c98-882c-a826cf0107a1',
results[0].resource_id
)
self.assertEqual(MONASCA_VALUE_META, results[0].resource_metadata)
self.assertEqual('50ce24dd577c43879cede72b77224e2f',
results[0].project_id,
)
self.assertEqual('B/s', results[0].counter_unit)
self.assertIsNone(results[0].user_id)
def test_get_meter_for_mapped_meters_non_uniq(self, mdf_mock):
data1 = (
[{u'dimensions': {u'datasource': u'ceilometer'},
u'id': u'2015-04-14T18:42:31Z',
u'name': u'meter-1'},
{u'dimensions': {u'datasource': u'ceilometer'},
u'id': u'2015-04-15T18:42:31Z',
u'name': u'meter-1'}])
data2 = (
[{u'dimensions': {u'datasource': u'ceilometer'},
u'id': u'2015-04-14T18:42:31Z',
u'name': u'meter-1'},
{u'dimensions': {u'datasource': u'ceilometer'},
u'id': u'2015-04-15T18:42:31Z',
u'name': u'meter-1'},
{u'id': u'fef26f9d27f8027ea44b940cf3626fc398f7edfb',
u'name': u'fake_metric',
u'dimensions': {
u'resource_id': u'2fe6e3a9-9bdf-4c98-882c-a826cf0107a1',
u'cloud_name': u'helion-poc-hlm-003',
u'component': u'vm',
u'control_plane': u'control-plane-1',
u'service': u'compute',
u'device': u'tap3356676e-a5',
u'tenant_id': u'50ce24dd577c43879cede72b77224e2f',
u'hostname': u'hlm003-cp1-comp0003-mgmt',
u'cluster': u'compute',
u'zone': u'nova'}
},
{u'dimensions': {},
u'id': u'2015-04-16T18:42:31Z',
u'name': u'testbatch'}])
with mock.patch("ceilometer.monasca_client.Client") as mock_client:
conn = impl_monasca.Connection(self.CONF, "127.0.0.1:8080")
metrics_list_mock = mock_client().metrics_list
metrics_list_mock.side_effect = [data1, data2]
kwargs = dict(limit=4)
results = list(conn.get_meters(**kwargs))
# result contains 2 records from data 1 since datasource
# = ceilometer, 2 records from data 2, 1 for pipeline
# meter but no datasource set to ceilometer and one for
# mapped meter
self.assertEqual(4, len(results))
self.assertEqual(True, metrics_list_mock.called)
self.assertEqual(2, metrics_list_mock.call_count)
def test_get_meter_for_mapped_meters_uniq(self, mdf_mock):
dummy_metric_names_mocked_return_value = (
[{"id": "015c995b1a770147f4ef18f5841ef566ab33521d",
"name": "network.delete"},
{"id": "335b5d569ad29dc61b3dc24609fad3619e947944",
"name": "subnet.update"}])
with mock.patch("ceilometer.monasca_client.Client") as mock_client:
conn = impl_monasca.Connection(self.CONF, "127.0.0.1:8080")
metric_names_list_mock = mock_client().metric_names_list
metric_names_list_mock.return_value = (
dummy_metric_names_mocked_return_value)
kwargs = dict(limit=4, unique=True)
results = list(conn.get_meters(**kwargs))
self.assertEqual(2, len(results))
self.assertEqual(True, metric_names_list_mock.called)
self.assertEqual(1, metric_names_list_mock.call_count)
def test_stats_list_mapped_meters(self, mock_mdf):
with mock.patch("ceilometer.monasca_client.Client") as mock_client:
conn = impl_monasca.Connection(self.CONF, "127.0.0.1:8080")
sl_mock = mock_client().statistics_list
sl_mock.return_value = [
{
'statistics':
[
['2014-10-24T12:12:12Z', 0.008],
['2014-10-24T12:52:12Z', 0.018]
],
'dimensions': {'unit': 'gb'},
'columns': ['timestamp', 'min']
}
]
sf = storage.SampleFilter()
sf.meter = "fake_meter"
aggregate = self.Aggregate(func="min", param=None)
sf.start_timestamp = timeutils.parse_isotime(
'2014-10-24T12:12:42').replace(tzinfo=None)
stats = list(conn.get_meter_statistics(sf, aggregate=[aggregate],
period=30))
self.assertEqual(2, len(stats))
self.assertEqual('B/s', stats[0].unit)
self.assertEqual('B/s', stats[1].unit)
self.assertEqual(0.008, stats[0].min)
self.assertEqual(0.018, stats[1].min)
self.assertEqual(30, stats[0].period)
self.assertEqual('2014-10-24T12:12:42',
stats[0].period_end.isoformat())
self.assertEqual('2014-10-24T12:52:42',
stats[1].period_end.isoformat())
self.assertIsNotNone(stats[0].as_dict().get('aggregate'))
self.assertEqual({u'min': 0.008}, stats[0].as_dict()['aggregate'])
def test_get_resources_for_mapped_meters(self, mock_mdf):
with mock.patch("ceilometer.monasca_client.Client") as mock_client:
conn = impl_monasca.Connection(self.CONF, "127.0.0.1:8080")
dummy_metric_names_mocked_return_value = (
[{"id": "015c995b1a770147f4ef18f5841ef566ab33521d",
"name": "fake_metric"},
{"id": "335b5d569ad29dc61b3dc24609fad3619e947944",
"name": "metric1"}])
mnl_mock = mock_client().metric_names_list
mnl_mock.return_value = (
dummy_metric_names_mocked_return_value)
dummy_get_resources_mocked_return_value = (
[{u'dimensions': {u'resource_id': u'abcd'},
u'measurements': [[u'2015-04-14T17:52:31Z', 1.0, {}],
[u'2015-04-15T17:52:31Z', 2.0, {}],
[u'2015-04-16T17:52:31Z', 3.0, {}]],
u'id': u'2015-04-14T18:42:31Z',
u'columns': [u'timestamp', u'value', u'value_meta'],
u'name': u'fake_metric'}])
ml_mock = mock_client().measurements_list
ml_mock.return_value = (
dummy_get_resources_mocked_return_value)
sample_filter = storage.SampleFilter(
meter='fake_meter', end_timestamp='2015-04-20T00:00:00Z')
resources = list(conn.get_resources(sample_filter, limit=2))
self.assertEqual(2, len(resources))
self.assertEqual(True, ml_mock.called)
self.assertEqual(1, ml_mock.call_count)
resources_without_limit = list(conn.get_resources(sample_filter))
self.assertEqual(3, len(resources_without_limit))
def test_stats_list_with_groupby_for_mapped_meters(self, mock_mdf):
with mock.patch("ceilometer.monasca_client.Client") as mock_client:
conn = impl_monasca.Connection(self.CONF, "127.0.0.1:8080")
sl_mock = mock_client().statistics_list
sl_mock.return_value = [
{
'statistics':
[
['2014-10-24T12:12:12Z', 0.008, 1.3, 3, 0.34],
['2014-10-24T12:20:12Z', 0.078, 1.25, 2, 0.21],
['2014-10-24T12:52:12Z', 0.018, 0.9, 4, 0.14]
],
'dimensions': {'hostname': '1234', 'unit': 'gb'},
'columns': ['timestamp', 'min', 'max', 'count', 'avg']
},
{
'statistics':
[
['2014-10-24T12:14:12Z', 0.45, 2.5, 2, 2.1],
['2014-10-24T12:20:12Z', 0.58, 3.2, 3, 3.4],
['2014-10-24T13:52:42Z', 1.67, 3.5, 1, 5.3]
],
'dimensions': {'hostname': '5678', 'unit': 'gb'},
'columns': ['timestamp', 'min', 'max', 'count', 'avg']
}]
sf = storage.SampleFilter()
sf.meter = "fake_meter3"
sf.start_timestamp = timeutils.parse_isotime(
'2014-10-24T12:12:42').replace(tzinfo=None)
groupby = ['resource_id']
stats = list(conn.get_meter_statistics(sf, period=30,
groupby=groupby))
self.assertEqual(2, len(stats))
for stat in stats:
self.assertIsNotNone(stat.groupby)
resource_id = stat.groupby.get('resource_id')
self.assertIn(resource_id, ['1234', '5678'])
if resource_id == '1234':
self.assertEqual(0.008, stat.min)
self.assertEqual(1.3, stat.max)
self.assertEqual(0.23, stat.avg)
self.assertEqual(9, stat.count)
self.assertEqual(30, stat.period)
self.assertEqual('2014-10-24T12:12:12',
stat.period_start.isoformat())
if resource_id == '5678':
self.assertEqual(0.45, stat.min)
self.assertEqual(3.5, stat.max)
self.assertEqual(3.6, stat.avg)
self.assertEqual(6, stat.count)
self.assertEqual(30, stat.period)
self.assertEqual('2014-10-24T13:52:42',
stat.period_end.isoformat())
def test_query_samples_for_mapped_meter(self, mock_mdf):
SAMPLES = [[
storage_models.Sample(
counter_name="fake_meter",
counter_type="gauge",
counter_unit="instance",
counter_volume=1,
project_id="123",
user_id="456",
resource_id="789",
resource_metadata={},
source="openstack",
recorded_at=timeutils.utcnow(),
timestamp=timeutils.utcnow(),
message_id="0",
message_signature='', )
]] * 2
samples = SAMPLES[:]
def _get_samples(*args, **kwargs):
return samples.pop()
with mock.patch("ceilometer.monasca_client.Client"):
conn = impl_monasca.Connection(self.CONF, "127.0.0.1:8080")
with mock.patch.object(conn, 'get_samples') as gsm:
gsm.side_effect = _get_samples
query = {'and': [{'=': {'counter_name': 'fake_meter'}},
{'or': [{'=': {"project_id": "123"}},
{'=': {"user_id": "456"}}]}]}
samples = conn.query_samples(query, None, 100)
self.assertEqual(2, len(samples))
self.assertEqual(2, gsm.call_count)
samples = SAMPLES[:]
query = {'and': [{'=': {'counter_name': 'fake_meter'}},
{'or': [{'=': {"project_id": "123"}},
{'>': {"counter_volume": 2}}]}]}
samples = conn.query_samples(query, None, 100)
self.assertEqual(1, len(samples))
self.assertEqual(4, gsm.call_count)

View File

@ -1,286 +0,0 @@
#
# Copyright 2016 Hewlett Packard
# (c) Copyright 2018 SUSE LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import fixtures
import mock
# from oslo_config import fixture as fixture_config
from oslo_utils import fileutils
from oslotest import base
import six
import yaml
from ceilometer.ceilosca_mapping import ceilometer_static_info_mapping
from ceilometer.ceilosca_mapping.ceilometer_static_info_mapping import (
CeilometerStaticMappingDefinition)
from ceilometer.ceilosca_mapping.ceilometer_static_info_mapping import (
CeilometerStaticMappingDefinitionException)
from ceilometer import monasca_ceilometer_opts
from ceilometer import service
from ceilometer.storage import impl_monasca
class TestStaticInfoBase(base.BaseTestCase):
pipeline_data = yaml.dump({
'sources': [{
'name': 'test_pipeline',
'interval': 1,
'meters': ['testbatch', 'testbatch2'],
'resources': ['alpha', 'beta', 'gamma', 'delta'],
'sinks': ['test_sink']}],
'sinks': [{
'name': 'test_sink',
'transformers': [],
'publishers': ["test"]}]
})
cfg = yaml.dump({
'meter_info_static_map': [{
'name': "disk.ephemeral.size",
'type': "gauge",
'unit': "GB"
}, {
'name': "image.delete",
'type': "delta",
'unit': "image"
}, {
'name': "image",
'type': "gauge",
'unit': "image"
}, {
'name': "disk.root.size",
'type': "gauge",
'unit': "GB"
}
]
})
ceilosca_cfg = yaml.dump({
'meter_metric_map': [{
'user_id': '$.dimensions.user_id',
'name': 'fake_meter',
'resource_id': '$.dimensions.resource_id',
'region': 'NA',
'monasca_metric_name': 'fake_metric',
'source': 'NA',
'project_id': '$.dimensions.tenant_id',
'type': 'gauge',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}, {
'user_id': '$.dimensions.user_id',
'name': 'fake_meter2',
'resource_id': '$.dimensions.resource_id',
'region': 'NA',
'monasca_metric_name': 'fake_metric2',
'source': 'NA',
'project_id': '$.dimensions.project_id',
'type': 'delta',
'resource_metadata': '$.measurements[0][2]',
'unit': 'B/s'
}]
})
def setup_static_mapping_def_file(self, cfg):
if six.PY3:
cfg = cfg.encode('utf-8')
ceilometer_static_info_mapping = fileutils.write_to_tempfile(
content=cfg, prefix='ceilometer_static_info_mapping', suffix='yaml'
)
self.addCleanup(os.remove, ceilometer_static_info_mapping)
return ceilometer_static_info_mapping
def setup_ceilosca_mapping_def_file(self, ceilosca_cfg):
if six.PY3:
ceilosca_cfg = ceilosca_cfg.encode('utf-8')
ceilosca_mapping_file = fileutils.write_to_tempfile(
content=ceilosca_cfg, prefix='ceilosca_mapping', suffix='yaml')
self.addCleanup(os.remove, ceilosca_mapping_file)
return ceilosca_mapping_file
def setup_pipeline_file(self, pipeline_data):
if six.PY3:
pipeline_data = pipeline_data.encode('utf-8')
pipeline_cfg_file = fileutils.write_to_tempfile(content=pipeline_data,
prefix="pipeline",
suffix="yaml")
self.addCleanup(os.remove, pipeline_cfg_file)
return pipeline_cfg_file
class TestStaticInfoDefinition(base.BaseTestCase):
def test_static_info_definition(self):
cfg = dict(name="image.delete",
type="delta",
unit="image")
handler = CeilometerStaticMappingDefinition(cfg)
self.assertEqual("delta", handler.cfg['type'])
self.assertEqual("image.delete", handler.cfg['name'])
self.assertEqual("image", handler.cfg['unit'])
def test_config_required_missing_fields(self):
cfg = dict()
try:
CeilometerStaticMappingDefinition(cfg)
except CeilometerStaticMappingDefinitionException as e:
self.assertEqual("Required fields ["
"'name', 'type', 'unit'] "
"not specified", e.message)
def test_bad_type_cfg_definition(self):
cfg = dict(name="fake_meter",
type="foo",
unit="B/s")
try:
CeilometerStaticMappingDefinition(cfg)
except CeilometerStaticMappingDefinitionException as e:
self.assertEqual("Invalid type foo specified", e.message)
class TestMappedCeilometerStaticInfoProcessing(TestStaticInfoBase):
def setUp(self):
super(TestMappedCeilometerStaticInfoProcessing, self).setUp()
# self.CONF = self.useFixture(fixture_config.Config()).conf
self.CONF = service.prepare_service([], [])
self.CONF.register_opts(list(monasca_ceilometer_opts.OPTS),
'monasca')
static_info_mapping_file = self.setup_static_mapping_def_file(self.cfg)
self.CONF.set_override('ceilometer_static_info_mapping',
static_info_mapping_file, group='monasca')
self.static_info_mapper = ceilometer_static_info_mapping\
.ProcessMappedCeilometerStaticInfo(self.CONF)
# self.CONF([], project='ceilometer', validate_default_values=True)
def test_fallback_mapping_file_path(self):
self.useFixture(fixtures.MockPatchObject(self.CONF,
'find_file',
return_value=None))
self.CONF.set_override('ceilometer_static_info_mapping',
' ', group='monasca')
self.static_info_mapper.reinitialize(self.CONF)
fall_bak_path = ceilometer_static_info_mapping.get_config_file(
self.CONF)
self.assertIn(
"ceilosca_mapping/data/ceilometer_static_info_mapping.yaml",
fall_bak_path)
@mock.patch(
'ceilometer.ceilosca_mapping.ceilometer_static_info_mapping.LOG')
def test_bad_mapping_definition_skip(self, LOG):
cfg = yaml.dump({
'meter_info_static_map': [{
'name': "disk.ephemeral.size",
'type': "gauge",
'unit': "GB"
}, {
'name': "image.delete",
'type': "delta",
'unit': "image"
}, {
'name': "image",
'type': "gauge",
'unit': "image"
}, {
'name': "disk.root.size",
'type': "foo",
'unit': "GB"
}]
})
static_info_mapping_file = self.setup_static_mapping_def_file(cfg)
self.CONF.set_override('ceilometer_static_info_mapping',
static_info_mapping_file, group='monasca')
data = ceilometer_static_info_mapping.\
setup_ceilometer_static_mapping_config(self.CONF)
meter_loaded = ceilometer_static_info_mapping.load_definitions(data)
self.assertEqual(3, len(meter_loaded))
LOG.error.assert_called_with(
"Error loading Ceilometer Static Mapping Definition : "
"Invalid type foo specified")
def test_list_of_meters_returned(self):
self.static_info_mapper.reinitialize(self.CONF)
self.assertItemsEqual(['disk.ephemeral.size', 'disk.root.size',
'image', 'image.delete'],
self.static_info_mapper.
get_list_supported_meters().
keys()
)
def test_static_info_of_ceilometer_meter(self):
cfg = yaml.dump({
'meter_info_static_map': [{
'name': "disk.ephemeral.size",
'type': "gauge",
'unit': "GB"
}]
})
static_info_mapping_file = self.setup_static_mapping_def_file(cfg)
self.CONF.set_override('ceilometer_static_info_mapping',
static_info_mapping_file, group='monasca')
self.static_info_mapper.reinitialize(self.CONF)
self.assertEqual('gauge',
self.static_info_mapper.get_meter_static_info_key_val(
'disk.ephemeral.size', 'type')
)
# This Class will only test the driver for the mapped static info
# Impl_Monasca Tests will be doing exhaustive tests for other test cases
@mock.patch("ceilometer.storage.impl_monasca.MonascaDataFilter")
class TestMoanscaDriverForMappedStaticInfo(TestStaticInfoBase):
def setUp(self):
super(TestMoanscaDriverForMappedStaticInfo, self).setUp()
# self.CONF = self.useFixture(fixture_config.Config()).conf
# self.CONF([], project='ceilometer', validate_default_values=True)
self.CONF = service.prepare_service([], [])
self.CONF.register_opts(list(monasca_ceilometer_opts.OPTS),
'monasca')
pipeline_cfg_file = self.setup_pipeline_file(self.pipeline_data)
self.CONF.set_override("pipeline_cfg_file", pipeline_cfg_file)
static_info_mapping_file = self.setup_static_mapping_def_file(self.cfg)
self.CONF.set_override('ceilometer_static_info_mapping',
static_info_mapping_file, group='monasca')
ceilosca_mapping_file = self.setup_ceilosca_mapping_def_file(
self.ceilosca_cfg)
self.CONF.set_override('ceilometer_monasca_metrics_mapping',
ceilosca_mapping_file, group='monasca')
self.static_info_mapper = ceilometer_static_info_mapping\
.ProcessMappedCeilometerStaticInfo(self.CONF)
self.static_info_mapper.reinitialize(self.CONF)
def test_get_statc_info_for_mapped_meters_uniq(self, mdf_mock):
dummy_metric_names_mocked_return_value = (
[{"id": "015c995b1a770147f4ef18f5841ef566ab33521d",
"name": "image"},
{"id": "335b5d569ad29dc61b3dc24609fad3619e947944",
"name": "fake_metric"}])
with mock.patch('ceilometer.monasca_client.Client') as mock_client:
conn = impl_monasca.Connection(self.CONF, '127.0.0.1:8080')
metric_names_list_mock = mock_client().metric_names_list
metric_names_list_mock.return_value = (
dummy_metric_names_mocked_return_value
)
kwargs = dict(limit=4,
unique=True)
results = list(conn.get_meters(**kwargs))
self.assertEqual(2, len(results))
self.assertEqual(True, metric_names_list_mock.called)
self.assertEqual(1, metric_names_list_mock.call_count)

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@ class TestMonascaClient(base.BaseTestCase):
'http://localhost:5000/v2.0',
'monasca')
self.CONF.set_override('max_retries', 0, 'database')
self.CONF.set_override('database_max_retries', 0, 'monasca')
self.mc = self._get_client()
def tearDown(self):
@ -111,8 +111,8 @@ class TestMonascaClient(base.BaseTestCase):
self.assertIsNotNone(True, conf.service_username)
def test_retry_on_key_error(self):
self.CONF.set_override('max_retries', 2, 'database')
self.CONF.set_override('retry_interval', 1, 'database')
self.CONF.set_override('database_max_retries', 2, 'monasca')
self.CONF.set_override('database_retry_interval', 1, 'monasca')
self.mc = self._get_client()
with mock.patch.object(
self.mc._mon_client.metrics, 'list',
@ -121,8 +121,8 @@ class TestMonascaClient(base.BaseTestCase):
self.assertEqual(2, mocked_metrics_list.call_count)
def test_no_retry_on_invalid_parameter(self):
self.CONF.set_override('max_retries', 2, 'database')
self.CONF.set_override('retry_interval', 1, 'database')
self.CONF.set_override('database_max_retries', 2, 'monasca')
self.CONF.set_override('database_retry_interval', 1, 'monasca')
self.mc = self._get_client()
def _check(exception):
@ -139,7 +139,8 @@ class TestMonascaClient(base.BaseTestCase):
def test_max_retris_not_too_much(self):
def _check(configured, expected):
self.CONF.set_override('max_retries', configured, 'database')
self.CONF.set_override('database_max_retries', configured,
'monasca')
self.mc = self._get_client()
self.assertEqual(expected, self.mc._max_retries)

View File

@ -14,7 +14,7 @@ rabbit_password = password
rabbit_hosts = 16.78.179.83
[service_credentials]
auth_url = http://192.168.10.6:5000
auth_url = http://192.168.10.6:5000/v3
region_name = RegionOne
password = secretservice
username = ceilometer
@ -22,18 +22,22 @@ project_name = service
project_domain_id = default
user_domain_id = default
auth_type = password
interface = internalURL
[keystone_authtoken]
signing_dir = /var/cache/ceilometer
auth_type = v3password
cafile = /opt/stack/data/ca-bundle.pem
auth_uri = http://16.78.179.83:5000
project_domain_id = default
project_name = service
user_domain_id = default
project_domain_name = Default
user_domain_name = Default
password = password
username = ceilometer
auth_url = http://16.78.179.83:35357
auth_plugin = password
region_name = RegionOne
[notification]
store_events = True
@ -43,4 +47,18 @@ disable_non_metric_meters = False
#metering_connection = mysql://root:password@127.0.0.1/ceilometer?charset=utf8
event_connection = mysql://root:password@127.0.0.1/ceilometer?charset=utf8
alarm_connection = mysql://root:password@127.0.0.1/ceilometer?charset=utf8
metering_connection = monasca://http://127.0.0.1:8070/v2.0
connection = monasca://http://127.0.0.1:8070/v2.0
[monasca]
enable_api_pagination = True
database_retry_interval = 5
database_max_retries = 5
service_auth_url = http://192.168.10.6:5000/v3
service_password = secretservice
service_username = ceilometer
service_interface = internalURL
service_auth_type = password
service_domain_name = Default
service_verify = /opt/stack/data/ca-bundle.pem
service_region_name = RegionOne
monasca_mappings = /etc/ceilometer/monasca_field_definitions.yaml

View File

@ -25,8 +25,7 @@ ceilosca_files = {
'monasca_client.py',
'monasca_ceilometer_opts.py',
'publisher/monasca_data_filter.py',
'publisher/monclient.py',
'storage/impl_monasca.py'
'publisher/monclient.py'
]
}

View File

@ -1,4 +1,4 @@
git+https://github.com/openstack/ceilometer.git@stable/pike#egg=ceilometer
git+https://github.com/openstack/ceilometer.git@master#egg=ceilometer
mock>=1.2
testrepository>=0.0.18
testscenarios>=0.4