Merge "Integration with oslo.context"
This commit is contained in:
commit
b23949f442
|
@ -2,8 +2,7 @@
|
|||
name = monasca_api
|
||||
|
||||
[pipeline:main]
|
||||
# Add validator in the pipeline so the metrics messages can be validated.
|
||||
pipeline = auth keystonecontext api
|
||||
pipeline = request_id auth api
|
||||
|
||||
[app:api]
|
||||
paste.app_factory = monasca_api.api.server:launch
|
||||
|
@ -11,8 +10,8 @@ paste.app_factory = monasca_api.api.server:launch
|
|||
[filter:auth]
|
||||
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
|
||||
|
||||
[filter:keystonecontext]
|
||||
paste.filter_factory = monasca_api.middleware.keystone_context_filter:filter_factory
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware.request_id:RequestId.factory
|
||||
|
||||
[server:main]
|
||||
use = egg:gunicorn#main
|
||||
|
|
|
@ -5,11 +5,10 @@ keys = root, sqlalchemy, kafka
|
|||
keys = console, file
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
keys = context
|
||||
|
||||
[logger_root]
|
||||
level = DEBUG
|
||||
formatter = default
|
||||
handlers = console, file
|
||||
|
||||
[logger_sqlalchemy]
|
||||
|
@ -18,14 +17,12 @@ qualname = sqlalchemy.engine
|
|||
# "level = DEBUG" logs SQL queries and results.
|
||||
# "level = WARN" logs neither. (Recommended for production systems.)
|
||||
level = DEBUG
|
||||
formatter = default
|
||||
handlers = console, file
|
||||
propagate=0
|
||||
|
||||
[logger_kafka]
|
||||
qualname = kafka
|
||||
level = DEBUG
|
||||
formatter = default
|
||||
handlers = console, file
|
||||
propagate = 0
|
||||
|
||||
|
@ -33,14 +30,14 @@ propagate = 0
|
|||
class = logging.StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = DEBUG
|
||||
formatter = generic
|
||||
formatter = context
|
||||
|
||||
[handler_file]
|
||||
class = logging.handlers.RotatingFileHandler
|
||||
level = DEBUG
|
||||
formatter = generic
|
||||
formatter = context
|
||||
# store up to 5*100MB of logs
|
||||
args = ('/var/log/monasca/api/monasca-api.log', 'a', 104857600, 5)
|
||||
|
||||
[formatter_generic]
|
||||
format = %(asctime)s %(levelname)s [%(name)s][%(threadName)s] %(message)s
|
||||
[formatter_context]
|
||||
class = oslo_log.formatters.ContextFormatter
|
|
@ -2,8 +2,7 @@
|
|||
name = monasca_api
|
||||
|
||||
[pipeline:main]
|
||||
# Add validator in the pipeline so the metrics messages can be validated.
|
||||
pipeline = auth keystonecontext api
|
||||
pipeline = request_id auth api
|
||||
|
||||
[app:api]
|
||||
paste.app_factory = monasca_api.api.server:launch
|
||||
|
@ -11,8 +10,8 @@ paste.app_factory = monasca_api.api.server:launch
|
|||
[filter:auth]
|
||||
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
|
||||
|
||||
[filter:keystonecontext]
|
||||
paste.filter_factory = monasca_api.middleware.keystone_context_filter:filter_factory
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware.request_id:RequestId.factory
|
||||
|
||||
[server:main]
|
||||
use = egg:gunicorn#main
|
||||
|
|
|
@ -5,11 +5,10 @@ keys = root, sqlalchemy, kafka
|
|||
keys = console, file
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
keys = context
|
||||
|
||||
[logger_root]
|
||||
level = DEBUG
|
||||
formatter = default
|
||||
handlers = console, file
|
||||
|
||||
[logger_sqlalchemy]
|
||||
|
@ -18,14 +17,12 @@ qualname = sqlalchemy.engine
|
|||
# "level = DEBUG" logs SQL queries and results.
|
||||
# "level = WARN" logs neither. (Recommended for production systems.)
|
||||
level = DEBUG
|
||||
formatter = default
|
||||
handlers = console, file
|
||||
propagate=0
|
||||
|
||||
[logger_kafka]
|
||||
qualname = kafka
|
||||
level = DEBUG
|
||||
formatter = default
|
||||
handlers = console, file
|
||||
propagate = 0
|
||||
|
||||
|
@ -33,14 +30,14 @@ propagate = 0
|
|||
class = logging.StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = DEBUG
|
||||
formatter = generic
|
||||
formatter = context
|
||||
|
||||
[handler_file]
|
||||
class = logging.handlers.RotatingFileHandler
|
||||
level = DEBUG
|
||||
formatter = generic
|
||||
formatter = context
|
||||
# store up to 5*100MB of logs
|
||||
args = ('/var/log/monasca/api/monasca-api.log', 'a', 104857600, 5)
|
||||
|
||||
[formatter_generic]
|
||||
format = %(asctime)s %(levelname)s [%(name)s][%(threadName)s] %(message)s
|
||||
[formatter_context]
|
||||
class = oslo_log.formatters.ContextFormatter
|
|
@ -0,0 +1,109 @@
|
|||
# Copyright 2016 FUJITSU LIMITED
|
||||
#
|
||||
# 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 falcon
|
||||
|
||||
from oslo_context import context
|
||||
|
||||
from monasca_api.common.repositories import constants
|
||||
from monasca_api.v2.common import exceptions
|
||||
|
||||
_TENANT_ID_PARAM = 'tenant_id'
|
||||
"""Name of the query-param pointing at project-id (tenant-id)"""
|
||||
|
||||
|
||||
class Request(falcon.Request):
|
||||
"""Variation of falcon.Request with context
|
||||
|
||||
Following class enhances :py:class:`falcon.Request` with
|
||||
:py:class:`context.RequestContext`.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, env, options=None):
|
||||
super(Request, self).__init__(env, options)
|
||||
self.context = context.RequestContext.from_environ(self.env)
|
||||
|
||||
@property
|
||||
def project_id(self):
|
||||
"""Returns project-id (tenant-id)
|
||||
|
||||
:return: project-id
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
return self.context.tenant
|
||||
|
||||
@property
|
||||
def cross_project_id(self):
|
||||
"""Returns project-id (tenant-id) found in query params.
|
||||
|
||||
This particular project-id is later on identified as
|
||||
cross-project-id
|
||||
|
||||
:return: project-id
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
return self.get_param(_TENANT_ID_PARAM, required=False)
|
||||
|
||||
@property
|
||||
def user_id(self):
|
||||
"""Returns user-id
|
||||
|
||||
:return: user-id
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
return self.context.user
|
||||
|
||||
@property
|
||||
def roles(self):
|
||||
"""Returns roles associated with user
|
||||
|
||||
:return: user's roles
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
return self.context.roles
|
||||
|
||||
@property
|
||||
def limit(self):
|
||||
"""Returns LIMIT query param value.
|
||||
|
||||
'limit' is not required query param.
|
||||
In case it is not found, py:data:'.constants.PAGE_LIMIT'
|
||||
value is returned.
|
||||
|
||||
:return: value of 'limit' query param or default value
|
||||
:rtype: int
|
||||
:raise exceptions.HTTPUnprocessableEntityError: if limit is not valid integer
|
||||
|
||||
"""
|
||||
limit = self.get_param('limit', required=False, default=None)
|
||||
if limit is not None:
|
||||
if limit.isdigit():
|
||||
limit = int(limit)
|
||||
if limit > constants.PAGE_LIMIT:
|
||||
return constants.PAGE_LIMIT
|
||||
else:
|
||||
return limit
|
||||
else:
|
||||
err_msg = 'Limit parameter must be a positive integer'
|
||||
raise exceptions.HTTPUnprocessableEntityError('Invalid limit', err_msg)
|
||||
else:
|
||||
return constants.PAGE_LIMIT
|
||||
|
||||
def __repr__(self):
|
||||
return '%s, context=%s' % (self.path, self.context)
|
|
@ -22,6 +22,8 @@ from oslo_config import cfg
|
|||
from oslo_log import log
|
||||
import paste.deploy
|
||||
|
||||
from monasca_api.api.core import request
|
||||
|
||||
dispatcher_opts = [cfg.StrOpt('versions', default=None,
|
||||
help='Versions'),
|
||||
cfg.StrOpt('version_2_0', default=None,
|
||||
|
@ -66,7 +68,7 @@ def launch(conf, config_file="/etc/monasca/api-config.conf"):
|
|||
default_config_files=[config_file])
|
||||
log.setup(cfg.CONF, 'monasca_api')
|
||||
|
||||
app = falcon.API()
|
||||
app = falcon.API(request_type=request.Request)
|
||||
|
||||
versions = simport.load(cfg.CONF.dispatcher.versions)()
|
||||
app.add_route("/", versions)
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
# Copyright (c) 2015 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""RequestContext: context for requests that persist through monasca."""
|
||||
|
||||
import uuid
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_utils import timeutils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class RequestContext(object):
|
||||
"""Security context and request information.
|
||||
|
||||
Represents the user taking a given action within the system.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, user_id, project_id, domain_id=None, domain_name=None,
|
||||
roles=None, timestamp=None, request_id=None,
|
||||
auth_token=None, user_name=None, project_name=None,
|
||||
service_catalog=None, user_auth_plugin=None, **kwargs):
|
||||
"""Creates the Keystone Context. Supports additional parameters:
|
||||
|
||||
:param user_auth_plugin:
|
||||
The auth plugin for the current request's authentication data.
|
||||
:param kwargs:
|
||||
Extra arguments that might be present
|
||||
"""
|
||||
if kwargs:
|
||||
LOG.warning(
|
||||
'Arguments dropped when creating context: %s') % str(kwargs)
|
||||
|
||||
self._roles = roles or []
|
||||
self.timestamp = timeutils.utcnow()
|
||||
|
||||
if not request_id:
|
||||
request_id = self.generate_request_id()
|
||||
self._request_id = request_id
|
||||
self._auth_token = auth_token
|
||||
|
||||
self._service_catalog = service_catalog
|
||||
|
||||
self._domain_id = domain_id
|
||||
self._domain_name = domain_name
|
||||
|
||||
self._user_id = user_id
|
||||
self._user_name = user_name
|
||||
|
||||
self._project_id = project_id
|
||||
self._project_name = project_name
|
||||
|
||||
self._user_auth_plugin = user_auth_plugin
|
||||
|
||||
def to_dict(self):
|
||||
return {'user_id': self._user_id,
|
||||
'project_id': self._project_id,
|
||||
'domain_id': self._domain_id,
|
||||
'domain_name': self._domain_name,
|
||||
'roles': self._roles,
|
||||
'timestamp': timeutils.strtime(self._timestamp),
|
||||
'request_id': self._request_id,
|
||||
'auth_token': self._auth_token,
|
||||
'user_name': self._user_name,
|
||||
'service_catalog': self._service_catalog,
|
||||
'project_name': self._project_name,
|
||||
'user': self._user}
|
||||
|
||||
def generate_request_id(self):
|
||||
return b'req-' + str(uuid.uuid4()).encode('ascii')
|
|
@ -1,49 +0,0 @@
|
|||
# Copyright 2013 IBM Corp
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class Inspector(object):
|
||||
"""The middleware that logs the request body and header
|
||||
|
||||
This is a middleware for debug purposes. To enable this middleware, add
|
||||
the following lines into the configuration file, for example:
|
||||
|
||||
[pipeline:main]
|
||||
pipeline = inspector api
|
||||
|
||||
[filter:inspector]
|
||||
use = egg: monasca_api_server#inspector
|
||||
"""
|
||||
def __init__(self, app, conf):
|
||||
"""Inspect each request
|
||||
|
||||
:param app: OptionParser to use. If not sent one will be created.
|
||||
:param conf: Override sys.argv; used in testing
|
||||
"""
|
||||
self.app = app
|
||||
self.conf = conf
|
||||
print('Inspect config:', self.conf)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
|
||||
print('environ is ', environ)
|
||||
return self.app(environ, start_response)
|
||||
|
||||
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
|
||||
def login_filter(app):
|
||||
return Inspector(app, local_conf)
|
||||
|
||||
return login_filter
|
|
@ -1,109 +0,0 @@
|
|||
# Copyright (c) 2015 OpenStack Foundation
|
||||
#
|
||||
# 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 falcon
|
||||
from oslo_log import log
|
||||
from oslo_middleware import request_id
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from monasca_api.middleware import context
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
def validator_filter(app):
|
||||
return KeystoneContextFilter(app, local_conf)
|
||||
|
||||
return validator_filter
|
||||
|
||||
|
||||
class KeystoneContextFilter(object):
|
||||
"""Make a request context from keystone headers."""
|
||||
|
||||
def __init__(self, app, conf):
|
||||
self._app = app
|
||||
self._conf = conf
|
||||
|
||||
def __call__(self, env, start_response):
|
||||
|
||||
LOG.debug("Creating Keystone Context Object.")
|
||||
|
||||
user_id = env.get('HTTP_X_USER_ID', env.get('HTTP_X_USER'))
|
||||
if user_id is None:
|
||||
msg = "Neither X_USER_ID nor X_USER found in request"
|
||||
LOG.error(msg)
|
||||
raise falcon.HTTPUnauthorized(title='Forbidden', description=msg)
|
||||
|
||||
roles = self._get_roles(env)
|
||||
|
||||
project_id = env.get('HTTP_X_PROJECT_ID')
|
||||
project_name = env.get('HTTP_X_PROJECT_NAME')
|
||||
|
||||
domain_id = env.get('HTTP_X_DOMAIN_ID')
|
||||
domain_name = env.get('HTTP_X_DOMAIN_NAME')
|
||||
|
||||
user_name = env.get('HTTP_X_USER_NAME')
|
||||
|
||||
req_id = env.get(request_id.ENV_REQUEST_ID)
|
||||
|
||||
# Get the auth token
|
||||
auth_token = env.get('HTTP_X_AUTH_TOKEN',
|
||||
env.get('HTTP_X_STORAGE_TOKEN'))
|
||||
|
||||
service_catalog = None
|
||||
if env.get('HTTP_X_SERVICE_CATALOG') is not None:
|
||||
try:
|
||||
catalog_header = env.get('HTTP_X_SERVICE_CATALOG')
|
||||
service_catalog = jsonutils.loads(catalog_header)
|
||||
except ValueError:
|
||||
msg = "Invalid service catalog json."
|
||||
LOG.error(msg)
|
||||
raise falcon.HTTPInternalServerError(msg)
|
||||
|
||||
# NOTE(jamielennox): This is a full auth plugin set by auth_token
|
||||
# middleware in newer versions.
|
||||
user_auth_plugin = env.get('keystone.token_auth')
|
||||
|
||||
# Build a context
|
||||
ctx = context.RequestContext(user_id,
|
||||
project_id,
|
||||
user_name=user_name,
|
||||
project_name=project_name,
|
||||
domain_id=domain_id,
|
||||
domain_name=domain_name,
|
||||
roles=roles,
|
||||
auth_token=auth_token,
|
||||
service_catalog=service_catalog,
|
||||
request_id=req_id,
|
||||
user_auth_plugin=user_auth_plugin)
|
||||
|
||||
env['monasca.context'] = ctx
|
||||
|
||||
LOG.debug("Keystone Context successfully created.")
|
||||
|
||||
return self._app(env, start_response)
|
||||
|
||||
def _get_roles(self, env):
|
||||
"""Get the list of roles."""
|
||||
|
||||
if 'HTTP_X_ROLES' in env:
|
||||
roles = env.get('HTTP_X_ROLES', '')
|
||||
else:
|
||||
# Fallback to deprecated role header:
|
||||
roles = env.get('HTTP_X_ROLE', '')
|
||||
if roles:
|
||||
LOG.warning(
|
||||
'Sourcing roles from deprecated X-Role HTTP header')
|
||||
return [r.strip() for r in roles.split(',')]
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright 2014 Hewlett-Packard
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class MockAuthFilter(object):
|
||||
"""Authorization filter.
|
||||
|
||||
This authorization filter doesn't do any authentication, it just copies the
|
||||
auth token to the tenant ID and supplies the 'admin' role and is meant for
|
||||
testing purposes only.
|
||||
"""
|
||||
|
||||
def __init__(self, app, conf):
|
||||
self.app = app
|
||||
self.conf = conf
|
||||
|
||||
def __call__(self, env, start_response):
|
||||
env['HTTP_X_TENANT_ID'] = env['HTTP_X_AUTH_TOKEN']
|
||||
env['HTTP_X_ROLES'] = 'admin'
|
||||
return self.app(env, start_response)
|
||||
|
||||
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
def validator_filter(app):
|
||||
return MockAuthFilter(app, local_conf)
|
||||
return validator_filter
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2015 kornicameister@gmail.com
|
||||
# Copyright 2015 FUJITSU LIMITED
|
||||
#
|
||||
# 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 falcon
|
||||
|
||||
from monasca_api.api.core import request
|
||||
|
||||
|
||||
class MockedAPI(falcon.API):
|
||||
"""MockedAPI
|
||||
|
||||
Subclasses :py:class:`falcon.API` in order to overwrite
|
||||
request_type property with custom :py:class:`request.Request`
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(MockedAPI, self).__init__(
|
||||
media_type=falcon.DEFAULT_MEDIA_TYPE,
|
||||
request_type=request.Request,
|
||||
response_type=falcon.Response,
|
||||
middleware=None,
|
||||
router=None
|
||||
)
|
|
@ -24,10 +24,10 @@ import fixtures
|
|||
import testtools.matchers as matchers
|
||||
|
||||
from monasca_api.common.repositories.model import sub_alarm_definition
|
||||
from monasca_api.tests import base
|
||||
from monasca_api.v2.reference import alarm_definitions
|
||||
from monasca_api.v2.reference import alarms
|
||||
|
||||
import oslo_config
|
||||
import oslo_config.fixture
|
||||
import oslotest.base as oslotest
|
||||
|
||||
|
@ -159,6 +159,8 @@ class RESTResponseEquals(object):
|
|||
|
||||
class AlarmTestBase(falcon.testing.TestBase, oslotest.BaseTestCase):
|
||||
|
||||
api_class = base.MockedAPI
|
||||
|
||||
def setUp(self):
|
||||
super(AlarmTestBase, self).setUp()
|
||||
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
# Copyright 2016 FUJITSU LIMITED
|
||||
#
|
||||
# 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 mock import mock
|
||||
from oslo_config import fixture as oo_cfg
|
||||
from oslo_context import fixture as oo_ctx
|
||||
|
||||
from falcon import testing
|
||||
|
||||
from monasca_api.api.core import request
|
||||
from monasca_api.v2.common import exceptions
|
||||
|
||||
|
||||
class TestRequest(testing.TestBase):
|
||||
|
||||
def test_use_context_from_request(self):
|
||||
req = request.Request(
|
||||
testing.create_environ(
|
||||
path='/',
|
||||
headers={
|
||||
'X_AUTH_TOKEN': '111',
|
||||
'X_USER_ID': '222',
|
||||
'X_PROJECT_ID': '333',
|
||||
'X_ROLES': 'terminator,predator'
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual('111', req.context.auth_token)
|
||||
self.assertEqual('222', req.user_id)
|
||||
self.assertEqual('333', req.project_id)
|
||||
self.assertEqual(['terminator', 'predator'], req.roles)
|
||||
|
||||
|
||||
class TestRequestLimit(testing.TestBase):
|
||||
def setUp(self):
|
||||
super(TestRequestLimit, self).setUp()
|
||||
self.useFixture(oo_cfg.Config())
|
||||
self.useFixture(oo_ctx.ClearRequestContext())
|
||||
|
||||
def test_valid_limit(self):
|
||||
expected_limit = 10
|
||||
req = request.Request(
|
||||
testing.create_environ(
|
||||
path='/',
|
||||
query_string='limit=%d' % expected_limit,
|
||||
headers={
|
||||
'X_AUTH_TOKEN': '111',
|
||||
'X_USER_ID': '222',
|
||||
'X_PROJECT_ID': '333',
|
||||
'X_ROLES': 'terminator,predator'
|
||||
}
|
||||
)
|
||||
)
|
||||
self.assertEqual(expected_limit, req.limit)
|
||||
|
||||
def test_invalid_limit(self):
|
||||
req = request.Request(
|
||||
testing.create_environ(
|
||||
path='/',
|
||||
query_string='limit=abc',
|
||||
headers={
|
||||
'X_AUTH_TOKEN': '111',
|
||||
'X_USER_ID': '222',
|
||||
'X_PROJECT_ID': '333',
|
||||
'X_ROLES': 'terminator,predator'
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# note(trebskit) assertRaises fails to call property
|
||||
# so we need the actual function
|
||||
def property_wrapper():
|
||||
return req.limit
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.HTTPUnprocessableEntityError,
|
||||
property_wrapper
|
||||
)
|
||||
|
||||
@mock.patch('monasca_api.common.repositories.constants.PAGE_LIMIT')
|
||||
def test_default_limit(self, page_limit):
|
||||
req = request.Request(
|
||||
testing.create_environ(
|
||||
path='/',
|
||||
headers={
|
||||
'X_AUTH_TOKEN': '111',
|
||||
'X_USER_ID': '222',
|
||||
'X_PROJECT_ID': '333',
|
||||
'X_ROLES': 'terminator,predator'
|
||||
}
|
||||
)
|
||||
)
|
||||
self.assertEqual(page_limit, req.limit)
|
|
@ -140,31 +140,31 @@ class TestValueMetaValidation(unittest.TestCase):
|
|||
class TestRoleValidation(unittest.TestCase):
|
||||
|
||||
def test_role_valid(self):
|
||||
req_roles = 'role0,rOlE1'
|
||||
req_roles = 'role0', 'rOlE1'
|
||||
authorized_roles = ['RolE1', 'Role2']
|
||||
|
||||
req = mock.Mock()
|
||||
req.get_header.return_value = req_roles
|
||||
req.roles = req_roles
|
||||
|
||||
helpers.validate_authorization(req, authorized_roles)
|
||||
|
||||
def test_role_invalid(self):
|
||||
req_roles = 'role2 ,role3'
|
||||
authorized_roles = ['role0', 'role1', 'role2']
|
||||
req_roles = 'role2', 'role3'
|
||||
authorized_roles = ['role0', 'role1']
|
||||
|
||||
req = mock.Mock()
|
||||
req.get_header.return_value = req_roles
|
||||
req.roles = req_roles
|
||||
|
||||
self.assertRaises(
|
||||
falcon.HTTPUnauthorized,
|
||||
helpers.validate_authorization, req, authorized_roles)
|
||||
|
||||
def test_empty_role_header(self):
|
||||
req_roles = ''
|
||||
req_roles = []
|
||||
authorized_roles = ['Role1', 'Role2']
|
||||
|
||||
req = mock.Mock()
|
||||
req.get_header.return_value = req_roles
|
||||
req.roles = req_roles
|
||||
|
||||
self.assertRaises(
|
||||
falcon.HTTPUnauthorized,
|
||||
|
@ -175,7 +175,7 @@ class TestRoleValidation(unittest.TestCase):
|
|||
authorized_roles = ['Role1', 'Role2']
|
||||
|
||||
req = mock.Mock()
|
||||
req.get_header.return_value = req_roles
|
||||
req.roles = req_roles
|
||||
|
||||
self.assertRaises(
|
||||
falcon.HTTPUnauthorized,
|
||||
|
|
|
@ -60,7 +60,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
|
||||
self._validate_alarm_definition(alarm_definition)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = get_query_alarm_definition_name(alarm_definition)
|
||||
expression = get_query_alarm_definition_expression(alarm_definition)
|
||||
description = get_query_alarm_definition_description(alarm_definition)
|
||||
|
@ -72,7 +71,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
alarm_definition)
|
||||
ok_actions = get_query_ok_actions(alarm_definition)
|
||||
|
||||
result = self._alarm_definition_create(tenant_id, name, expression,
|
||||
result = self._alarm_definition_create(req.project_id, name, expression,
|
||||
description, severity, match_by,
|
||||
alarm_actions,
|
||||
undetermined_actions,
|
||||
|
@ -85,7 +84,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
def on_get(self, req, res, alarm_definition_id=None):
|
||||
if alarm_definition_id is None:
|
||||
helpers.validate_authorization(req, self._get_alarmdefs_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
severity = helpers.get_query_param(req, "severity", default_val=None)
|
||||
|
@ -108,19 +106,18 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
except Exception:
|
||||
raise HTTPUnprocessableEntityError('Unprocessable Entity',
|
||||
'Offset value {} must be an integer'.format(offset))
|
||||
limit = helpers.get_limit(req)
|
||||
|
||||
result = self._alarm_definition_list(tenant_id, name, dimensions, severity,
|
||||
req.uri, sort_by, offset, limit)
|
||||
result = self._alarm_definition_list(req.project_id, name,
|
||||
dimensions, severity,
|
||||
req.uri, sort_by,
|
||||
offset, req.limit)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
else:
|
||||
helpers.validate_authorization(req, self._get_alarmdefs_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
result = self._alarm_definition_show(tenant_id,
|
||||
result = self._alarm_definition_show(req.project_id,
|
||||
alarm_definition_id)
|
||||
|
||||
helpers.add_links_to_resource(result,
|
||||
|
@ -137,8 +134,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
|
||||
self._validate_alarm_definition(alarm_definition, require_all=True)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
name = get_query_alarm_definition_name(alarm_definition)
|
||||
expression = get_query_alarm_definition_expression(alarm_definition)
|
||||
actions_enabled = (
|
||||
|
@ -151,7 +146,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
match_by = get_query_alarm_definition_match_by(alarm_definition)
|
||||
severity = get_query_alarm_definition_severity(alarm_definition)
|
||||
|
||||
result = self._alarm_definition_update_or_patch(tenant_id,
|
||||
result = self._alarm_definition_update_or_patch(req.project_id,
|
||||
alarm_definition_id,
|
||||
name,
|
||||
expression,
|
||||
|
@ -174,8 +169,6 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
|
||||
alarm_definition = helpers.read_json_msg_body(req)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
# Optional args
|
||||
name = get_query_alarm_definition_name(alarm_definition,
|
||||
return_none=True)
|
||||
|
@ -197,7 +190,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
severity = get_query_alarm_definition_severity(alarm_definition,
|
||||
return_none=True)
|
||||
|
||||
result = self._alarm_definition_update_or_patch(tenant_id,
|
||||
result = self._alarm_definition_update_or_patch(req.project_id,
|
||||
alarm_definition_id,
|
||||
name,
|
||||
expression,
|
||||
|
@ -217,8 +210,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
|||
def on_delete(self, req, res, alarm_definition_id):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
self._alarm_definition_delete(tenant_id, alarm_definition_id)
|
||||
self._alarm_definition_delete(req.project_id, alarm_definition_id)
|
||||
res.status = falcon.HTTP_204
|
||||
|
||||
def _validate_name_not_conflicting(self, tenant_id, name, expected_id=None):
|
||||
|
|
|
@ -53,8 +53,6 @@ class Alarms(alarms_api_v2.AlarmsV2API,
|
|||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
alarm = helpers.read_http_resource(req)
|
||||
schema_alarm.validate(alarm)
|
||||
|
||||
|
@ -69,10 +67,10 @@ class Alarms(alarms_api_v2.AlarmsV2API,
|
|||
raise HTTPUnprocessableEntityError('Unprocessable Entity',
|
||||
"Field 'link' is required")
|
||||
|
||||
self._alarm_update(tenant_id, alarm_id, alarm['state'],
|
||||
self._alarm_update(req.project_id, alarm_id, alarm['state'],
|
||||
alarm['lifecycle_state'], alarm['link'])
|
||||
|
||||
result = self._alarm_show(req.uri, tenant_id, alarm_id)
|
||||
result = self._alarm_show(req.uri, req.project_id, alarm_id)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
@ -81,12 +79,10 @@ class Alarms(alarms_api_v2.AlarmsV2API,
|
|||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
alarm = helpers.read_http_resource(req)
|
||||
schema_alarm.validate(alarm)
|
||||
|
||||
old_alarm = self._alarms_repo.get_alarm(tenant_id, alarm_id)[0]
|
||||
old_alarm = self._alarms_repo.get_alarm(req.project_id, alarm_id)[0]
|
||||
|
||||
# if a field is not present or is None, replace it with the old value
|
||||
if 'state' not in alarm or not alarm['state']:
|
||||
|
@ -96,10 +92,10 @@ class Alarms(alarms_api_v2.AlarmsV2API,
|
|||
if 'link' not in alarm or alarm['link'] is None:
|
||||
alarm['link'] = old_alarm['link']
|
||||
|
||||
self._alarm_patch(tenant_id, alarm_id, alarm['state'],
|
||||
self._alarm_patch(req.project_id, alarm_id, alarm['state'],
|
||||
alarm['lifecycle_state'], alarm['link'])
|
||||
|
||||
result = self._alarm_show(req.uri, tenant_id, alarm_id)
|
||||
result = self._alarm_show(req.uri, req.project_id, alarm_id)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
@ -108,15 +104,12 @@ class Alarms(alarms_api_v2.AlarmsV2API,
|
|||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
self._alarm_delete(tenant_id, alarm_id)
|
||||
self._alarm_delete(req.project_id, alarm_id)
|
||||
|
||||
res.status = falcon.HTTP_204
|
||||
|
||||
def on_get(self, req, res, alarm_id=None):
|
||||
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
if alarm_id is None:
|
||||
query_parms = falcon.uri.parse_query_string(req.query_string)
|
||||
|
@ -155,16 +148,15 @@ class Alarms(alarms_api_v2.AlarmsV2API,
|
|||
raise HTTPUnprocessableEntityError("Unprocessable Entity",
|
||||
"Offset value {} must be an integer".format(offset))
|
||||
|
||||
limit = helpers.get_limit(req)
|
||||
|
||||
result = self._alarm_list(req.uri, tenant_id, query_parms, offset,
|
||||
limit)
|
||||
result = self._alarm_list(req.uri, req.project_id,
|
||||
query_parms, offset,
|
||||
req.limit)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
else:
|
||||
result = self._alarm_show(req.uri, tenant_id, alarm_id)
|
||||
result = self._alarm_show(req.uri, req.project_id, alarm_id)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
@ -399,7 +391,6 @@ class AlarmsCount(alarms_api_v2.AlarmsCountV2API, alarming.Alarming):
|
|||
|
||||
def on_get(self, req, res):
|
||||
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
query_parms = falcon.uri.parse_query_string(req.query_string)
|
||||
|
||||
if 'state' in query_parms:
|
||||
|
@ -426,9 +417,7 @@ class AlarmsCount(alarms_api_v2.AlarmsCountV2API, alarming.Alarming):
|
|||
raise HTTPUnprocessableEntityError("Unprocessable Entity",
|
||||
"Offset must be a valid integer, was {}".format(offset))
|
||||
|
||||
limit = helpers.get_limit(req)
|
||||
|
||||
result = self._alarms_count(req.uri, tenant_id, query_parms, offset, limit)
|
||||
result = self._alarms_count(req.uri, req.project_id, query_parms, offset, req.limit)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
@ -507,28 +496,25 @@ class AlarmsStateHistory(alarms_api_v2.AlarmsStateHistoryV2API,
|
|||
|
||||
if alarm_id is None:
|
||||
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
start_timestamp = helpers.get_query_starttime_timestamp(req, False)
|
||||
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
|
||||
query_parms = falcon.uri.parse_query_string(req.query_string)
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
|
||||
result = self._alarm_history_list(tenant_id, start_timestamp,
|
||||
result = self._alarm_history_list(req.project_id, start_timestamp,
|
||||
end_timestamp, query_parms,
|
||||
req.uri, offset, limit)
|
||||
req.uri, offset, req.limit)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
else:
|
||||
helpers.validate_authorization(req, self._get_alarms_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
|
||||
result = self._alarm_history(tenant_id, [alarm_id], req.uri,
|
||||
offset, limit)
|
||||
result = self._alarm_history(req.project_id, [alarm_id],
|
||||
req.uri, offset,
|
||||
req.limit)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
|
|
@ -20,9 +20,9 @@ import falcon
|
|||
from oslo_log import log
|
||||
from oslo_utils import timeutils
|
||||
import simplejson
|
||||
import six
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from monasca_api.common.repositories import constants
|
||||
from monasca_api.v2.common.exceptions import HTTPUnprocessableEntityError
|
||||
from monasca_api.v2.common.schemas import dimensions_schema
|
||||
from monasca_api.v2.common.schemas import exceptions as schemas_exceptions
|
||||
|
@ -54,24 +54,6 @@ def validate_json_content_type(req):
|
|||
'application/json')
|
||||
|
||||
|
||||
def is_in_role(req, authorized_roles):
|
||||
"""Is one or more of the X-ROLES in the supplied authorized_roles.
|
||||
|
||||
:param req: HTTP request object. Must contain "X-ROLES" in the HTTP
|
||||
request header.
|
||||
:param authorized_roles: List of authorized roles to check against.
|
||||
:return: Returns True if in the list of authorized roles, otherwise False.
|
||||
"""
|
||||
str_roles = req.get_header('X-ROLES')
|
||||
if str_roles is None:
|
||||
return False
|
||||
roles = str_roles.lower().split(',')
|
||||
for role in roles:
|
||||
if role in authorized_roles:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def validate_authorization(req, authorized_roles):
|
||||
"""Validates whether one or more X-ROLES in the HTTP header is authorized.
|
||||
|
||||
|
@ -85,15 +67,16 @@ def validate_authorization(req, authorized_roles):
|
|||
:param authorized_roles: List of authorized roles to check against.
|
||||
:raises falcon.HTTPUnauthorized
|
||||
"""
|
||||
str_roles = req.get_header('X-ROLES')
|
||||
roles = req.roles
|
||||
challenge = 'Token'
|
||||
if str_roles is None:
|
||||
if not roles:
|
||||
raise falcon.HTTPUnauthorized('Forbidden',
|
||||
'Tenant does not have any roles',
|
||||
challenge)
|
||||
roles = str_roles.lower().split(',')
|
||||
roles = roles.split(',') if isinstance(roles, six.string_types) else roles
|
||||
authorized_roles_lower = [r.lower() for r in authorized_roles]
|
||||
for role in roles:
|
||||
role = role.lower()
|
||||
if role in authorized_roles_lower:
|
||||
return
|
||||
raise falcon.HTTPUnauthorized('Forbidden',
|
||||
|
@ -102,14 +85,6 @@ def validate_authorization(req, authorized_roles):
|
|||
challenge)
|
||||
|
||||
|
||||
def get_tenant_id(req):
|
||||
"""Returns the tenant ID in the HTTP request header.
|
||||
|
||||
:param req: HTTP request object.
|
||||
"""
|
||||
return req.get_header('X-TENANT-ID')
|
||||
|
||||
|
||||
def get_x_tenant_or_tenant_id(req, delegate_authorized_roles):
|
||||
"""Evaluates whether the tenant ID or cross tenant ID should be returned.
|
||||
|
||||
|
@ -118,12 +93,12 @@ def get_x_tenant_or_tenant_id(req, delegate_authorized_roles):
|
|||
delegate privileges.
|
||||
:returns: Returns the cross tenant or tenant ID.
|
||||
"""
|
||||
if is_in_role(req, delegate_authorized_roles):
|
||||
if any(x in set(delegate_authorized_roles) for x in req.roles):
|
||||
params = falcon.uri.parse_query_string(req.query_string)
|
||||
if 'tenant_id' in params:
|
||||
tenant_id = params['tenant_id']
|
||||
return tenant_id
|
||||
return get_tenant_id(req)
|
||||
return req.project_id
|
||||
|
||||
|
||||
def get_query_param(req, param_name, required=False, default_val=None):
|
||||
|
@ -769,21 +744,3 @@ def dumpit_utf8(thingy):
|
|||
|
||||
def str_2_bool(s):
|
||||
return s.lower() in ("true")
|
||||
|
||||
|
||||
def get_limit(req):
|
||||
limit = get_query_param(req, 'limit')
|
||||
|
||||
if limit:
|
||||
if limit.isdigit():
|
||||
limit = int(limit)
|
||||
if limit > constants.PAGE_LIMIT:
|
||||
return constants.PAGE_LIMIT
|
||||
else:
|
||||
return limit
|
||||
else:
|
||||
raise HTTPUnprocessableEntityError("Invalid limit",
|
||||
"Limit parameter must "
|
||||
"be a positive integer")
|
||||
else:
|
||||
return constants.PAGE_LIMIT
|
||||
|
|
|
@ -131,18 +131,17 @@ class Metrics(metrics_api_v2.MetricsV2API):
|
|||
|
||||
def on_get(self, req, res):
|
||||
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req)
|
||||
helpers.validate_query_name(name)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
helpers.validate_query_dimensions(dimensions)
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
start_timestamp = helpers.get_query_starttime_timestamp(req, False)
|
||||
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
|
||||
helpers.validate_start_end_timestamps(start_timestamp, end_timestamp)
|
||||
result = self._list_metrics(tenant_id, name, dimensions,
|
||||
req.uri, offset, limit,
|
||||
result = self._list_metrics(req.project_id, name,
|
||||
dimensions, req.uri,
|
||||
offset, req.limit,
|
||||
start_timestamp, end_timestamp)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
@ -171,7 +170,6 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API):
|
|||
|
||||
def on_get(self, req, res):
|
||||
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req, True)
|
||||
helpers.validate_query_name(name)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
|
@ -180,14 +178,13 @@ class MetricsMeasurements(metrics_api_v2.MetricsMeasurementsV2API):
|
|||
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
|
||||
helpers.validate_start_end_timestamps(start_timestamp, end_timestamp)
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
merge_metrics_flag = get_merge_metrics_flag(req)
|
||||
group_by = helpers.get_query_group_by(req)
|
||||
|
||||
result = self._measurement_list(tenant_id, name, dimensions,
|
||||
result = self._measurement_list(req.project_id, name, dimensions,
|
||||
start_timestamp, end_timestamp,
|
||||
req.uri, offset,
|
||||
limit, merge_metrics_flag,
|
||||
req.limit, merge_metrics_flag,
|
||||
group_by)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
|
@ -230,7 +227,6 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API):
|
|||
|
||||
def on_get(self, req, res):
|
||||
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req, True)
|
||||
helpers.validate_query_name(name)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
|
@ -241,14 +237,13 @@ class MetricsStatistics(metrics_api_v2.MetricsStatisticsV2API):
|
|||
statistics = helpers.get_query_statistics(req)
|
||||
period = helpers.get_query_period(req)
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
merge_metrics_flag = get_merge_metrics_flag(req)
|
||||
group_by = helpers.get_query_group_by(req)
|
||||
|
||||
result = self._metric_statistics(tenant_id, name, dimensions,
|
||||
result = self._metric_statistics(req.project_id, name, dimensions,
|
||||
start_timestamp, end_timestamp,
|
||||
statistics, period, req.uri,
|
||||
offset, limit, merge_metrics_flag,
|
||||
offset, req.limit, merge_metrics_flag,
|
||||
group_by)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
|
@ -292,13 +287,11 @@ class MetricsNames(metrics_api_v2.MetricsNamesV2API):
|
|||
|
||||
def on_get(self, req, res):
|
||||
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
helpers.validate_query_dimensions(dimensions)
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
result = self._list_metric_names(tenant_id, dimensions,
|
||||
req.uri, offset, limit)
|
||||
result = self._list_metric_names(req.project_id, dimensions,
|
||||
req.uri, offset, req.limit)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
|
@ -331,14 +324,12 @@ class DimensionValues(metrics_api_v2.DimensionValuesV2API):
|
|||
|
||||
def on_get(self, req, res):
|
||||
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
metric_name = helpers.get_query_param(req, 'metric_name')
|
||||
dimension_name = helpers.get_query_param(req, 'dimension_name',
|
||||
required=True)
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
result = self._dimension_values(tenant_id, req.uri, metric_name,
|
||||
dimension_name, offset, limit)
|
||||
result = self._dimension_values(req.project_id, req.uri, metric_name,
|
||||
dimension_name, offset, req.limit)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
|
@ -372,12 +363,10 @@ class DimensionNames(metrics_api_v2.DimensionNamesV2API):
|
|||
|
||||
def on_get(self, req, res):
|
||||
helpers.validate_authorization(req, self._get_metrics_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
metric_name = helpers.get_query_param(req, 'metric_name')
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
result = self._dimension_names(tenant_id, req.uri, metric_name,
|
||||
offset, limit)
|
||||
result = self._dimension_names(req.project_id, req.uri, metric_name,
|
||||
offset, req.limit)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
|
|
|
@ -202,8 +202,7 @@ class Notifications(notifications_api_v2.NotificationsV2API):
|
|||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
notification = helpers.read_http_resource(req)
|
||||
self._parse_and_validate_notification(notification)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
result = self._create_notification(tenant_id, notification, req.uri)
|
||||
result = self._create_notification(req.project_id, notification, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_201
|
||||
|
||||
|
@ -211,7 +210,6 @@ class Notifications(notifications_api_v2.NotificationsV2API):
|
|||
if notification_method_id is None:
|
||||
helpers.validate_authorization(req,
|
||||
self._get_notifications_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
sort_by = helpers.get_query_param(req, 'sort_by', default_val=None)
|
||||
if sort_by is not None:
|
||||
if isinstance(sort_by, basestring):
|
||||
|
@ -223,16 +221,14 @@ class Notifications(notifications_api_v2.NotificationsV2API):
|
|||
validation.validate_sort_by(sort_by, allowed_sort_by)
|
||||
|
||||
offset = helpers.get_query_param(req, 'offset')
|
||||
limit = helpers.get_limit(req)
|
||||
result = self._list_notifications(tenant_id, req.uri, sort_by,
|
||||
offset, limit)
|
||||
result = self._list_notifications(req.project_id, req.uri, sort_by,
|
||||
offset, req.limit)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
else:
|
||||
helpers.validate_authorization(req,
|
||||
self._get_notifications_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
result = self._list_notification(tenant_id,
|
||||
result = self._list_notification(req.project_id,
|
||||
notification_method_id,
|
||||
req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
|
@ -240,8 +236,7 @@ class Notifications(notifications_api_v2.NotificationsV2API):
|
|||
|
||||
def on_delete(self, req, res, notification_method_id):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
self._delete_notification(tenant_id, notification_method_id)
|
||||
self._delete_notification(req.project_id, notification_method_id)
|
||||
res.status = falcon.HTTP_204
|
||||
|
||||
def on_put(self, req, res, notification_method_id):
|
||||
|
@ -249,8 +244,7 @@ class Notifications(notifications_api_v2.NotificationsV2API):
|
|||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
notification = helpers.read_http_resource(req)
|
||||
self._parse_and_validate_notification(notification, require_all=True)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
result = self._update_notification(notification_method_id, tenant_id,
|
||||
result = self._update_notification(notification_method_id, req.project_id,
|
||||
notification, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
@ -259,10 +253,9 @@ class Notifications(notifications_api_v2.NotificationsV2API):
|
|||
helpers.validate_json_content_type(req)
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
notification = helpers.read_http_resource(req)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
self._patch_get_notification(tenant_id, notification_method_id, notification)
|
||||
self._patch_get_notification(req.project_id, notification_method_id, notification)
|
||||
self._parse_and_validate_notification(notification, require_all=True)
|
||||
result = self._update_notification(notification_method_id, tenant_id,
|
||||
result = self._update_notification(notification_method_id, req.project_id,
|
||||
notification, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
|
|
@ -37,8 +37,7 @@ class NotificationsType(notificationstype_api_v2.NotificationsTypeV2API):
|
|||
|
||||
# This is to provide consistency. Pagination is not really supported here as there
|
||||
# are not that many rows
|
||||
limit = helpers.get_limit(req)
|
||||
result = self._list_notifications(req.uri, limit)
|
||||
result = self._list_notifications(req.uri, req.limit)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
|
||||
oslo.context>=2.9.0 # Apache-2.0
|
||||
oslo.log>=3.11.0 # Apache-2.0
|
||||
oslo.middleware>=3.0.0 # Apache-2.0
|
||||
oslo.serialization>=1.10.0 # Apache-2.0
|
||||
|
|
Loading…
Reference in New Issue