api: return 410 if only Gnocchi is enabled
If gnocchi dispatcher is enabled and not the database dispatcher. All the ceilometer API calls (except alarms) will return an empty result. To avoid misunderstanding of why a user got its data ingested by ceilometer without error but he can't retrieve them via ceilometer-api and should use the Gnocchi API. We explicitly tell that through the API by returning a 410. APIImpact DocImpact Change-Id: Ia947f354da73a9a98feaa8cc6c39a1791208613e
This commit is contained in:
parent
cfe81a584e
commit
b0f98cb767
@ -23,7 +23,8 @@ MEDIA_TYPE_XML = 'application/vnd.openstack.telemetry-%s+xml'
|
||||
|
||||
class RootController(object):
|
||||
|
||||
v2 = v2.V2Controller()
|
||||
def __init__(self):
|
||||
self.v2 = v2.V2Controller()
|
||||
|
||||
@pecan.expose('json')
|
||||
def index(self):
|
||||
|
@ -401,9 +401,3 @@ class QueryAlarmsController(rest.RestController):
|
||||
for s in conn.query_alarms(query.filter_expr,
|
||||
query.orderby,
|
||||
query.limit)]
|
||||
|
||||
|
||||
class QueryController(rest.RestController):
|
||||
|
||||
samples = QuerySamplesController()
|
||||
alarms = QueryAlarmsController()
|
||||
|
@ -18,6 +18,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystoneclient.openstack.common.apiclient import exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
import pecan
|
||||
|
||||
from ceilometer.api.controllers.v2 import alarms
|
||||
from ceilometer.api.controllers.v2 import capabilities
|
||||
from ceilometer.api.controllers.v2 import events
|
||||
@ -25,16 +30,97 @@ from ceilometer.api.controllers.v2 import meters
|
||||
from ceilometer.api.controllers.v2 import query
|
||||
from ceilometer.api.controllers.v2 import resources
|
||||
from ceilometer.api.controllers.v2 import samples
|
||||
from ceilometer.i18n import _LW
|
||||
from ceilometer import keystone_client
|
||||
|
||||
|
||||
API_OPTS = [
|
||||
cfg.BoolOpt('gnocchi_is_enabled',
|
||||
default=None,
|
||||
help=('Set True to disable resource/meter/sample URLs. '
|
||||
'Default autodetection by querying keystone.')),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(API_OPTS, group='api')
|
||||
cfg.CONF.import_opt('dispatcher', 'ceilometer.dispatcher')
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def gnocchi_abort():
|
||||
pecan.abort(410, ("This telemetry installation is configured to use "
|
||||
"Gnocchi. Please use the Gnocchi API available on "
|
||||
"the metric endpoint to retreive data."))
|
||||
|
||||
|
||||
class QueryController(object):
|
||||
def __init__(self, gnocchi_is_enabled=False):
|
||||
self.gnocchi_is_enabled = gnocchi_is_enabled
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, kind, *remainder):
|
||||
if kind == 'alarms':
|
||||
return query.QueryAlarmsController(), remainder
|
||||
elif kind == 'samples' and self.gnocchi_is_enabled:
|
||||
gnocchi_abort()
|
||||
elif kind == 'samples':
|
||||
return query.QuerySamplesController(), remainder
|
||||
else:
|
||||
pecan.abort(404)
|
||||
|
||||
|
||||
class V2Controller(object):
|
||||
"""Version 2 API controller root."""
|
||||
|
||||
resources = resources.ResourcesController()
|
||||
meters = meters.MetersController()
|
||||
samples = samples.SamplesController()
|
||||
alarms = alarms.AlarmsController()
|
||||
event_types = events.EventTypesController()
|
||||
events = events.EventsController()
|
||||
query = query.QueryController()
|
||||
capabilities = capabilities.CapabilitiesController()
|
||||
|
||||
def __init__(self):
|
||||
self._gnocchi_is_enabled = None
|
||||
|
||||
@property
|
||||
def gnocchi_is_enabled(self):
|
||||
if self._gnocchi_is_enabled is None:
|
||||
if cfg.CONF.api.gnocchi_is_enabled is not None:
|
||||
self._gnocchi_is_enabled = cfg.CONF.api.gnocchi_is_enabled
|
||||
|
||||
elif ("gnocchi" not in cfg.CONF.dispatcher
|
||||
or "database" in cfg.CONF.dispatcher):
|
||||
self._gnocchi_is_enabled = False
|
||||
else:
|
||||
try:
|
||||
ks = keystone_client.get_client()
|
||||
ks.service_catalog.url_for(service_type='metric')
|
||||
except exceptions.EndpointNotFound:
|
||||
self._gnocchi_is_enabled = False
|
||||
except exceptions.ClientException:
|
||||
LOG.warn(_LW("Can't connect to keystone, assuming gnocchi "
|
||||
"is disabled and retry later"))
|
||||
return False
|
||||
else:
|
||||
self._gnocchi_is_enabled = True
|
||||
LOG.warn(_LW("ceilometer-api started with gnocchi "
|
||||
"enabled. The resources/meters/samples "
|
||||
"URLs are disabled."))
|
||||
return self._gnocchi_is_enabled
|
||||
|
||||
@pecan.expose()
|
||||
def _lookup(self, kind, *remainder):
|
||||
if (kind in ['meters', 'resources', 'samples']
|
||||
and self.gnocchi_is_enabled):
|
||||
gnocchi_abort()
|
||||
elif kind == 'meters':
|
||||
return meters.MetersController(), remainder
|
||||
elif kind == 'resources':
|
||||
return resources.ResourcesController(), remainder
|
||||
elif kind == 'samples':
|
||||
return samples.SamplesController(), remainder
|
||||
elif kind == 'query':
|
||||
return QueryController(
|
||||
gnocchi_is_enabled=self.gnocchi_is_enabled,
|
||||
), remainder
|
||||
elif kind == 'alarms':
|
||||
return alarms.AlarmsController(), remainder
|
||||
else:
|
||||
pecan.abort(404)
|
||||
|
@ -25,6 +25,7 @@ from ceilometer.tests import db as db_test_base
|
||||
|
||||
OPT_GROUP_NAME = 'keystone_authtoken'
|
||||
cfg.CONF.import_group(OPT_GROUP_NAME, "keystonemiddleware.auth_token")
|
||||
cfg.CONF.import_group('api', 'ceilometer.api.controllers.v2.root')
|
||||
|
||||
|
||||
class FunctionalTest(db_test_base.TestBase):
|
||||
@ -47,6 +48,9 @@ class FunctionalTest(db_test_base.TestBase):
|
||||
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.app = self._make_app()
|
||||
|
||||
def _make_app(self, enable_acl=False):
|
||||
|
65
ceilometer/tests/api/v2/test_api_upgrade.py
Normal file
65
ceilometer/tests/api/v2/test_api_upgrade.py
Normal file
@ -0,0 +1,65 @@
|
||||
#
|
||||
# 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 keystoneclient.openstack.common.apiclient import exceptions
|
||||
import mock
|
||||
from oslotest import mockpatch
|
||||
|
||||
from ceilometer.tests.api import v2
|
||||
|
||||
|
||||
class TestAPIUpgradePath(v2.FunctionalTest):
|
||||
def _setup_osloconfig_options(self):
|
||||
self.CONF.set_override('gnocchi_is_enabled', True, group='api')
|
||||
|
||||
def _setup_keystone_mock(self):
|
||||
self.CONF.set_override('gnocchi_is_enabled', None, group='api')
|
||||
self.ks = mock.Mock()
|
||||
self.ks.service_catalog.url_for.side_effect = self._url_for
|
||||
self.useFixture(mockpatch.Patch(
|
||||
'ceilometer.keystone_client.get_client', return_value=self.ks))
|
||||
|
||||
@staticmethod
|
||||
def _url_for(service_type=None):
|
||||
if service_type == 'metric':
|
||||
return 'http://gnocchi/'
|
||||
raise exceptions.EndpointNotFound()
|
||||
|
||||
def _do_test_gnocchi_enabled_without_database_backend(self):
|
||||
self.CONF.set_override('dispatcher', 'gnocchi')
|
||||
for endpoint in ['meters', 'samples', 'resources']:
|
||||
response = self.app.get(self.PATH_PREFIX + '/' + endpoint,
|
||||
status=410)
|
||||
self.assertIn('Gnocchi API', response.body)
|
||||
|
||||
for endpoint in ['events', 'event_types']:
|
||||
self.app.get(self.PATH_PREFIX + '/' + endpoint,
|
||||
status=200)
|
||||
|
||||
response = self.post_json('/query/samples',
|
||||
params={
|
||||
"filter": '{"=": {"type": "creation"}}',
|
||||
"orderby": '[{"timestamp": "DESC"}]',
|
||||
"limit": 3
|
||||
}, status=410)
|
||||
self.assertIn('Gnocchi API', response.body)
|
||||
|
||||
def test_gnocchi_enabled_without_database_backend_keystone(self):
|
||||
self._setup_keystone_mock()
|
||||
self._do_test_gnocchi_enabled_without_database_backend()
|
||||
self.ks.service_catalog.url_for.assert_called_once_with(
|
||||
service_type="metric")
|
||||
|
||||
def test_gnocchi_enabled_without_database_backend_configoptions(self):
|
||||
self._setup_osloconfig_options()
|
||||
self._do_test_gnocchi_enabled_without_database_backend()
|
@ -77,6 +77,7 @@ class ConfigFixture(fixture.GabbiFixture):
|
||||
conf.set_override('alarm_connection', '', group='database')
|
||||
|
||||
conf.set_override('pecan_debug', True, group='api')
|
||||
conf.set_override('gnocchi_is_enabled', False, group='api')
|
||||
|
||||
conf.set_override('store_events', True, group='notification')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user