Fix cross-tenant logs submission
* add 'delegate_roles' configuration option Change-Id: If4952b84536ef058d91f6ee2332076dc448d97bd
This commit is contained in:
parent
4b743bcc6c
commit
9bb918197e
|
@ -20,7 +20,9 @@ Create logs.
|
||||||
None.
|
None.
|
||||||
|
|
||||||
#### Query Parameters
|
#### Query Parameters
|
||||||
* tenant_id (string, optional, restricted) - Tenant ID to create log on behalf of. Usage of this query parameter requires the `monitoring-delegate` role.
|
* tenant_id (string, optional, restricted) - Tenant ID (project ID) to create
|
||||||
|
log on behalf of. Usage of this query parameter requires the role specified
|
||||||
|
in the configuration option `delegate_roles` .
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
JSON object which can have a maximum size of 5 MB. It consists of global
|
JSON object which can have a maximum size of 5 MB. It consists of global
|
||||||
|
@ -241,9 +243,6 @@ Example:
|
||||||
#### Path Parameters
|
#### Path Parameters
|
||||||
None.
|
None.
|
||||||
|
|
||||||
#### Query Parameters
|
|
||||||
* tenant_id (string, optional, restricted) - Tenant ID to create log on behalf of. Usage of this query parameter requires the `monitoring-delegate` role.
|
|
||||||
|
|
||||||
#### Request Body
|
#### Request Body
|
||||||
Consists of a single plain text message or a JSON object which can have a maximum length of 1048576 characters.
|
Consists of a single plain text message or a JSON object which can have a maximum length of 1048576 characters.
|
||||||
|
|
||||||
|
|
|
@ -53,3 +53,4 @@ kafka_topics = log
|
||||||
path = /v2.0/log,/v3.0/logs
|
path = /v2.0/log,/v3.0/logs
|
||||||
default_roles = user,domainuser,domainadmin,monasca-user
|
default_roles = user,domainuser,domainadmin,monasca-user
|
||||||
agent_roles = monasca-agent
|
agent_roles = monasca-agent
|
||||||
|
delegate_roles = admin
|
||||||
|
|
|
@ -21,8 +21,6 @@ from monasca_log_api.monitoring import metrics
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
MONITORING_DELEGATE_ROLE = 'monitoring-delegate'
|
|
||||||
|
|
||||||
|
|
||||||
class LogsApi(object):
|
class LogsApi(object):
|
||||||
"""Logs API.
|
"""Logs API.
|
||||||
|
|
|
@ -31,7 +31,11 @@ role_m_opts = [
|
||||||
default=None,
|
default=None,
|
||||||
help=('List of roles, that if set, mean that request '
|
help=('List of roles, that if set, mean that request '
|
||||||
'comes from agent, thus is authorized in the same '
|
'comes from agent, thus is authorized in the same '
|
||||||
'time'))
|
'time')),
|
||||||
|
cfg.ListOpt(name='delegate_roles',
|
||||||
|
default=['admin'],
|
||||||
|
help=('Roles that are allowed to POST logs on '
|
||||||
|
'behalf of another tenant (project)'))
|
||||||
]
|
]
|
||||||
role_m_group = cfg.OptGroup(name='roles_middleware', title='roles_middleware')
|
role_m_group = cfg.OptGroup(name='roles_middleware', title='roles_middleware')
|
||||||
|
|
||||||
|
@ -81,12 +85,15 @@ class RoleMiddleware(om.ConfigurableMiddleware):
|
||||||
path = /v2.0/log
|
path = /v2.0/log
|
||||||
default_roles = monasca-user
|
default_roles = monasca-user
|
||||||
agent_roles = monasca-log-agent
|
agent_roles = monasca-log-agent
|
||||||
|
delegate_roles = admin
|
||||||
|
|
||||||
Configuration explained:
|
Configuration explained:
|
||||||
|
|
||||||
* path (list) - path (or list of paths) middleware should be applied
|
* path (list) - path (or list of paths) middleware should be applied
|
||||||
* agent_roles (list) - list of roles that identifies tenant as an agent
|
* agent_roles (list) - list of roles that identifies tenant as an agent
|
||||||
* default_roles (list) - list of roles that should be authorized
|
* default_roles (list) - list of roles that should be authorized
|
||||||
|
* delegate_roles (list) - list of roles that are allowed to POST logs on
|
||||||
|
behalf of another tenant (project)
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
Being an agent means that tenant is automatically authorized.
|
Being an agent means that tenant is automatically authorized.
|
||||||
|
|
|
@ -20,7 +20,6 @@ from oslo_log import log
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from monasca_log_api.api import exceptions
|
from monasca_log_api.api import exceptions
|
||||||
from monasca_log_api.api import logs_api
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -214,10 +213,12 @@ def validate_payload_size(req):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_is_delegate(role):
|
def validate_is_delegate(roles):
|
||||||
if role:
|
delegate_roles = CONF.roles_middleware.delegate_roles
|
||||||
role = role.split(',') if isinstance(role, six.string_types) else role
|
if roles and delegate_roles:
|
||||||
return logs_api.MONITORING_DELEGATE_ROLE in role
|
roles = roles.split(',') if isinstance(roles, six.string_types) \
|
||||||
|
else roles
|
||||||
|
return any(x in set(delegate_roles) for x in roles)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -227,7 +228,7 @@ def validate_cross_tenant(tenant_id, cross_tenant_id, roles):
|
||||||
if cross_tenant_id:
|
if cross_tenant_id:
|
||||||
raise falcon.HTTPForbidden(
|
raise falcon.HTTPForbidden(
|
||||||
'Permission denied',
|
'Permission denied',
|
||||||
'Projects %s cannot POST cross tenant metrics' % tenant_id
|
'Projects %s cannot POST cross tenant logs' % tenant_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,8 @@ class Logs(logs_api.LogsApi):
|
||||||
self._logs_size_gauge.send(name=None,
|
self._logs_size_gauge.send(name=None,
|
||||||
value=int(req.content_length))
|
value=int(req.content_length))
|
||||||
|
|
||||||
tenant_id = (req.project_id if req.project_id
|
tenant_id = (req.cross_project_id if req.cross_project_id
|
||||||
else req.cross_project_id)
|
else req.project_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._processor.send_message(
|
self._processor.send_message(
|
||||||
|
|
|
@ -20,10 +20,11 @@ import unittest
|
||||||
|
|
||||||
from monasca_log_api.api import exceptions as log_api_exceptions
|
from monasca_log_api.api import exceptions as log_api_exceptions
|
||||||
from monasca_log_api.api import headers
|
from monasca_log_api.api import headers
|
||||||
from monasca_log_api.api import logs_api
|
|
||||||
from monasca_log_api.reference.v2 import logs
|
from monasca_log_api.reference.v2 import logs
|
||||||
from monasca_log_api.tests import base
|
from monasca_log_api.tests import base
|
||||||
|
|
||||||
|
ROLES = 'admin'
|
||||||
|
|
||||||
|
|
||||||
def _init_resource(test):
|
def _init_resource(test):
|
||||||
resource = logs.Logs()
|
resource = logs.Logs()
|
||||||
|
@ -121,7 +122,7 @@ class TestLogs(testing.TestBase):
|
||||||
'/log/single',
|
'/log/single',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: 'a:1',
|
headers.X_DIMENSIONS.name: 'a:1',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': '0'
|
'Content-Length': '0'
|
||||||
|
@ -147,7 +148,7 @@ class TestLogs(testing.TestBase):
|
||||||
method='POST',
|
method='POST',
|
||||||
query_string='tenant_id=1',
|
query_string='tenant_id=1',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: 'a:1',
|
headers.X_DIMENSIONS.name: 'a:1',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': '0'
|
'Content-Length': '0'
|
||||||
|
@ -169,7 +170,7 @@ class TestLogs(testing.TestBase):
|
||||||
'/log/single',
|
'/log/single',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: '',
|
headers.X_DIMENSIONS.name: '',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': '0'
|
'Content-Length': '0'
|
||||||
|
@ -187,7 +188,7 @@ class TestLogs(testing.TestBase):
|
||||||
'/log/single',
|
'/log/single',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: '',
|
headers.X_DIMENSIONS.name: '',
|
||||||
'Content-Type': 'video/3gpp',
|
'Content-Type': 'video/3gpp',
|
||||||
'Content-Length': '0'
|
'Content-Length': '0'
|
||||||
|
@ -208,7 +209,7 @@ class TestLogs(testing.TestBase):
|
||||||
'/log/single',
|
'/log/single',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: '',
|
headers.X_DIMENSIONS.name: '',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': str(content_length)
|
'Content-Length': str(content_length)
|
||||||
|
@ -229,7 +230,7 @@ class TestLogs(testing.TestBase):
|
||||||
'/log/single',
|
'/log/single',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: '',
|
headers.X_DIMENSIONS.name: '',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': str(content_length)
|
'Content-Length': str(content_length)
|
||||||
|
@ -250,7 +251,7 @@ class TestLogs(testing.TestBase):
|
||||||
'/log/single',
|
'/log/single',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: '',
|
headers.X_DIMENSIONS.name: '',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': str(content_length)
|
'Content-Length': str(content_length)
|
||||||
|
@ -267,7 +268,7 @@ class TestLogs(testing.TestBase):
|
||||||
'/log/single',
|
'/log/single',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_DIMENSIONS.name: '',
|
headers.X_DIMENSIONS.name: '',
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,19 @@ import random
|
||||||
import string
|
import string
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import falcon
|
||||||
from falcon import testing
|
from falcon import testing
|
||||||
import mock
|
import mock
|
||||||
import ujson as json
|
import ujson as json
|
||||||
|
|
||||||
from monasca_log_api.api import exceptions as log_api_exceptions
|
from monasca_log_api.api import exceptions as log_api_exceptions
|
||||||
from monasca_log_api.api import headers
|
from monasca_log_api.api import headers
|
||||||
from monasca_log_api.api import logs_api
|
|
||||||
from monasca_log_api.reference.v3 import logs
|
from monasca_log_api.reference.v3 import logs
|
||||||
from monasca_log_api.tests import base
|
from monasca_log_api.tests import base
|
||||||
|
|
||||||
ENDPOINT = '/logs'
|
ENDPOINT = '/logs'
|
||||||
TENANT_ID = 'bob'
|
TENANT_ID = 'bob'
|
||||||
|
ROLES = 'admin'
|
||||||
|
|
||||||
|
|
||||||
def _init_resource(test):
|
def _init_resource(test):
|
||||||
|
@ -101,7 +102,7 @@ class TestLogsMonitoring(testing.TestBase):
|
||||||
ENDPOINT,
|
ENDPOINT,
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_TENANT_ID.name: TENANT_ID,
|
headers.X_TENANT_ID.name: TENANT_ID,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': str(content_length)
|
'Content-Length': str(content_length)
|
||||||
|
@ -137,7 +138,7 @@ class TestLogsMonitoring(testing.TestBase):
|
||||||
ENDPOINT,
|
ENDPOINT,
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_TENANT_ID.name: TENANT_ID,
|
headers.X_TENANT_ID.name: TENANT_ID,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': str(content_length)
|
'Content-Length': str(content_length)
|
||||||
|
@ -181,7 +182,7 @@ class TestLogsMonitoring(testing.TestBase):
|
||||||
ENDPOINT,
|
ENDPOINT,
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: ROLES,
|
||||||
headers.X_TENANT_ID.name: TENANT_ID,
|
headers.X_TENANT_ID.name: TENANT_ID,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': str(content_length)
|
'Content-Length': str(content_length)
|
||||||
|
@ -204,3 +205,98 @@ class TestLogsMonitoring(testing.TestBase):
|
||||||
self.assertEqual(1, size_gauge.call_count)
|
self.assertEqual(1, size_gauge.call_count)
|
||||||
self.assertEqual(content_length,
|
self.assertEqual(content_length,
|
||||||
size_gauge.mock_calls[0][2]['value'])
|
size_gauge.mock_calls[0][2]['value'])
|
||||||
|
|
||||||
|
|
||||||
|
class TestLogs(testing.TestBase):
|
||||||
|
|
||||||
|
api_class = base.MockedAPI
|
||||||
|
|
||||||
|
def before(self):
|
||||||
|
self.conf = base.mock_config(self)
|
||||||
|
|
||||||
|
@mock.patch('monasca_log_api.reference.v3.common.bulk_processor.'
|
||||||
|
'BulkProcessor')
|
||||||
|
def test_should_pass_cross_tenant_id(self, bulk_processor):
|
||||||
|
logs_resource = _init_resource(self)
|
||||||
|
logs_resource._processor = bulk_processor
|
||||||
|
|
||||||
|
v3_body, v3_logs = _generate_v3_payload(1)
|
||||||
|
payload = json.dumps(v3_body)
|
||||||
|
content_length = len(payload)
|
||||||
|
self.simulate_request(
|
||||||
|
'/logs',
|
||||||
|
method='POST',
|
||||||
|
query_string='tenant_id=1',
|
||||||
|
headers={
|
||||||
|
headers.X_ROLES.name: ROLES,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': str(content_length)
|
||||||
|
},
|
||||||
|
body=payload
|
||||||
|
)
|
||||||
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
||||||
|
logs_resource._processor.send_message.assert_called_with(
|
||||||
|
logs=v3_logs,
|
||||||
|
global_dimensions=v3_body['dimensions'],
|
||||||
|
log_tenant_id='1')
|
||||||
|
|
||||||
|
@mock.patch('monasca_log_api.reference.v3.common.bulk_processor.'
|
||||||
|
'BulkProcessor')
|
||||||
|
def test_should_fail_not_delegate_ok_cross_tenant_id(self, _):
|
||||||
|
_init_resource(self)
|
||||||
|
self.simulate_request(
|
||||||
|
'/logs',
|
||||||
|
method='POST',
|
||||||
|
query_string='tenant_id=1',
|
||||||
|
headers={
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': '0'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(falcon.HTTP_403, self.srmock.status)
|
||||||
|
|
||||||
|
@mock.patch('monasca_log_api.reference.v3.common.bulk_processor.'
|
||||||
|
'BulkProcessor')
|
||||||
|
def test_should_pass_empty_cross_tenant_id_wrong_role(self,
|
||||||
|
bulk_processor):
|
||||||
|
logs_resource = _init_resource(self)
|
||||||
|
logs_resource._processor = bulk_processor
|
||||||
|
|
||||||
|
v3_body, _ = _generate_v3_payload(1)
|
||||||
|
payload = json.dumps(v3_body)
|
||||||
|
content_length = len(payload)
|
||||||
|
self.simulate_request(
|
||||||
|
'/logs',
|
||||||
|
method='POST',
|
||||||
|
headers={
|
||||||
|
headers.X_ROLES.name: 'some_role',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': str(content_length)
|
||||||
|
},
|
||||||
|
body=payload
|
||||||
|
)
|
||||||
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
||||||
|
self.assertEqual(1, bulk_processor.send_message.call_count)
|
||||||
|
|
||||||
|
@mock.patch('monasca_log_api.reference.v3.common.bulk_processor.'
|
||||||
|
'BulkProcessor')
|
||||||
|
def test_should_pass_empty_cross_tenant_id_ok_role(self,
|
||||||
|
bulk_processor):
|
||||||
|
logs_resource = _init_resource(self)
|
||||||
|
logs_resource._processor = bulk_processor
|
||||||
|
|
||||||
|
v3_body, _ = _generate_v3_payload(1)
|
||||||
|
payload = json.dumps(v3_body)
|
||||||
|
content_length = len(payload)
|
||||||
|
self.simulate_request(
|
||||||
|
'/logs',
|
||||||
|
method='POST',
|
||||||
|
headers={
|
||||||
|
headers.X_ROLES.name: ROLES,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': str(content_length)
|
||||||
|
},
|
||||||
|
body=payload
|
||||||
|
)
|
||||||
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
||||||
|
self.assertEqual(1, bulk_processor.send_message.call_count)
|
||||||
|
|
|
@ -22,31 +22,31 @@ import mock
|
||||||
from oslotest import base as os_test
|
from oslotest import base as os_test
|
||||||
|
|
||||||
from monasca_log_api.api import exceptions
|
from monasca_log_api.api import exceptions
|
||||||
from monasca_log_api.api import logs_api
|
|
||||||
from monasca_log_api.reference.common import validation
|
from monasca_log_api.reference.common import validation
|
||||||
from monasca_log_api.reference.v2.common import service as common_service
|
from monasca_log_api.reference.v2.common import service as common_service
|
||||||
from monasca_log_api.tests import base
|
from monasca_log_api.tests import base
|
||||||
|
|
||||||
|
|
||||||
class IsDelegate(os_test.BaseTestCase):
|
class IsDelegate(os_test.BaseTestCase):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(IsDelegate, self).__init__(*args, **kwargs)
|
||||||
|
self._conf = None
|
||||||
|
self._roles = ['admin']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(IsDelegate, self).setUp()
|
||||||
|
self._conf = base.mock_config(self)
|
||||||
|
|
||||||
def test_is_delegate_ok_role(self):
|
def test_is_delegate_ok_role(self):
|
||||||
roles = logs_api.MONITORING_DELEGATE_ROLE
|
self.assertTrue(validation.validate_is_delegate(self._roles))
|
||||||
self.assertTrue(validation.validate_is_delegate(roles))
|
|
||||||
|
|
||||||
def test_is_delegate_ok_role_in_roles(self):
|
def test_is_delegate_ok_role_in_roles(self):
|
||||||
roles = logs_api.MONITORING_DELEGATE_ROLE + ',a_role,b_role'
|
self._roles.extend(['a_role', 'b_role'])
|
||||||
self.assertTrue(validation.validate_is_delegate(roles))
|
self.assertTrue(validation.validate_is_delegate(self._roles))
|
||||||
|
|
||||||
def test_is_delegate_not_ok_role(self):
|
def test_is_delegate_not_ok_role(self):
|
||||||
roles = 'a_role,b_role'
|
roles = ['a_role', 'b_role']
|
||||||
self.assertFalse(validation.validate_is_delegate(roles))
|
|
||||||
|
|
||||||
def test_is_delegate_ok_role_as_list(self):
|
|
||||||
roles = {logs_api.MONITORING_DELEGATE_ROLE}
|
|
||||||
self.assertTrue(validation.validate_is_delegate(roles))
|
|
||||||
|
|
||||||
def test_is_delegate_not_ok_role_as_list(self):
|
|
||||||
roles = {'a_role', 'b_role'}
|
|
||||||
self.assertFalse(validation.validate_is_delegate(roles))
|
self.assertFalse(validation.validate_is_delegate(roles))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import mock
|
||||||
import ujson as json
|
import ujson as json
|
||||||
|
|
||||||
from monasca_log_api.api import headers
|
from monasca_log_api.api import headers
|
||||||
from monasca_log_api.api import logs_api
|
|
||||||
from monasca_log_api.reference.v2 import logs as v2_logs
|
from monasca_log_api.reference.v2 import logs as v2_logs
|
||||||
from monasca_log_api.reference.v3 import logs as v3_logs
|
from monasca_log_api.reference.v3 import logs as v3_logs
|
||||||
from monasca_log_api.tests import base
|
from monasca_log_api.tests import base
|
||||||
|
@ -48,6 +47,7 @@ class SameV2V3Output(testing.TestBase):
|
||||||
service = 'laas'
|
service = 'laas'
|
||||||
hostname = 'kornik'
|
hostname = 'kornik'
|
||||||
tenant_id = 'ironMan'
|
tenant_id = 'ironMan'
|
||||||
|
roles = 'admin'
|
||||||
|
|
||||||
v2_dimensions = 'hostname:%s,service:%s' % (hostname, service)
|
v2_dimensions = 'hostname:%s,service:%s' % (hostname, service)
|
||||||
v3_dimensions = {
|
v3_dimensions = {
|
||||||
|
@ -76,7 +76,7 @@ class SameV2V3Output(testing.TestBase):
|
||||||
'/v2.0',
|
'/v2.0',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: roles,
|
||||||
headers.X_DIMENSIONS.name: v2_dimensions,
|
headers.X_DIMENSIONS.name: v2_dimensions,
|
||||||
headers.X_APPLICATION_TYPE.name: component,
|
headers.X_APPLICATION_TYPE.name: component,
|
||||||
headers.X_TENANT_ID.name: tenant_id,
|
headers.X_TENANT_ID.name: tenant_id,
|
||||||
|
@ -90,7 +90,7 @@ class SameV2V3Output(testing.TestBase):
|
||||||
'/v3.0',
|
'/v3.0',
|
||||||
method='POST',
|
method='POST',
|
||||||
headers={
|
headers={
|
||||||
headers.X_ROLES.name: logs_api.MONITORING_DELEGATE_ROLE,
|
headers.X_ROLES.name: roles,
|
||||||
headers.X_TENANT_ID.name: tenant_id,
|
headers.X_TENANT_ID.name: tenant_id,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': '100'
|
'Content-Length': '100'
|
||||||
|
|
|
@ -33,7 +33,8 @@ class LogApiV2Client(rest_client.RestClient):
|
||||||
|
|
||||||
def send_single_log(self,
|
def send_single_log(self,
|
||||||
log,
|
log,
|
||||||
headers=None):
|
headers=None,
|
||||||
|
fields=None):
|
||||||
default_headers = {
|
default_headers = {
|
||||||
'X-Tenant-Id': 'b4265b0a48ae4fd3bdcee0ad8c2b6012',
|
'X-Tenant-Id': 'b4265b0a48ae4fd3bdcee0ad8c2b6012',
|
||||||
'X-Roles': 'admin',
|
'X-Roles': 'admin',
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from oslo_serialization import jsonutils as json
|
from oslo_serialization import jsonutils as json
|
||||||
|
from six.moves.urllib.parse import urlencode
|
||||||
from tempest.lib.common import rest_client
|
from tempest.lib.common import rest_client
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,15 +32,19 @@ class LogApiV3Client(rest_client.RestClient):
|
||||||
resp, response_body = self.send_request('GET', '/')
|
resp, response_body = self.send_request('GET', '/')
|
||||||
return resp, response_body
|
return resp, response_body
|
||||||
|
|
||||||
def send_single_log(self, log, headers=None):
|
def send_single_log(self, log, headers=None, fields=None):
|
||||||
default_headers = {
|
default_headers = {
|
||||||
'X-Tenant-Id': 'b4265b0a48ae4fd3bdcee0ad8c2b6012',
|
'X-Tenant-Id': 'b4265b0a48ae4fd3bdcee0ad8c2b6012',
|
||||||
'X-Roles': 'admin',
|
'X-Roles': 'admin',
|
||||||
}
|
}
|
||||||
default_headers.update(headers)
|
default_headers.update(headers)
|
||||||
msg = json.dumps(log)
|
msg = json.dumps(log)
|
||||||
|
uri = LogApiV3Client._uri
|
||||||
|
|
||||||
resp, body = self.post(LogApiV3Client._uri, msg, default_headers)
|
if fields:
|
||||||
|
uri += '?' + urlencode(fields)
|
||||||
|
|
||||||
|
resp, body = self.post(uri, msg, default_headers)
|
||||||
|
|
||||||
return resp, body
|
return resp, body
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ class BaseLogsTestCase(test.BaseTestCase):
|
||||||
cls.__name__,
|
cls.__name__,
|
||||||
identity_version=auth_version)
|
identity_version=auth_version)
|
||||||
credentials = cred_provider.get_creds_by_roles(
|
credentials = cred_provider.get_creds_by_roles(
|
||||||
['monasca-user']).credentials
|
['monasca-user', 'admin']).credentials
|
||||||
cls.os = clients.Manager(credentials=credentials)
|
cls.os = clients.Manager(credentials=credentials)
|
||||||
|
|
||||||
cls.logs_clients = cls.os.log_api_clients
|
cls.logs_clients = cls.os.log_api_clients
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
from tempest.lib.common.utils import test_utils
|
from tempest.lib.common.utils import test_utils
|
||||||
from tempest.lib import decorators
|
from tempest.lib import decorators
|
||||||
|
from testtools import matchers
|
||||||
|
|
||||||
from monasca_log_api_tempest.tests import base
|
from monasca_log_api_tempest.tests import base
|
||||||
|
|
||||||
|
@ -23,23 +24,28 @@ _RETRY_WAIT = 2
|
||||||
|
|
||||||
class TestSingleLog(base.BaseLogsTestCase):
|
class TestSingleLog(base.BaseLogsTestCase):
|
||||||
def _run_and_wait(self, key, data, version,
|
def _run_and_wait(self, key, data, version,
|
||||||
content_type='application/json', headers=None):
|
content_type='application/json',
|
||||||
|
headers=None, fields=None):
|
||||||
|
|
||||||
headers = base._get_headers(headers, content_type)
|
headers = base._get_headers(headers, content_type)
|
||||||
|
|
||||||
def wait():
|
def wait():
|
||||||
return self.logs_search_client.count_search_messages(key, headers) > 0
|
return self.logs_search_client.count_search_messages(key,
|
||||||
|
headers) > 0
|
||||||
|
|
||||||
self.assertEqual(0, self.logs_search_client.count_search_messages(key, headers),
|
self.assertEqual(0, self.logs_search_client.count_search_messages(key,
|
||||||
|
headers),
|
||||||
'Find log message in elasticsearch: {0}'.format(key))
|
'Find log message in elasticsearch: {0}'.format(key))
|
||||||
|
|
||||||
headers = base._get_headers(headers, content_type)
|
headers = base._get_headers(headers, content_type)
|
||||||
data = base._get_data(data, content_type, version=version)
|
data = base._get_data(data, content_type, version=version)
|
||||||
|
|
||||||
response, _ = self.logs_clients[version].send_single_log(data, headers)
|
client = self.logs_clients[version]
|
||||||
|
response, _ = client.send_single_log(data, headers, fields)
|
||||||
self.assertEqual(204, response.status)
|
self.assertEqual(204, response.status)
|
||||||
|
|
||||||
test_utils.call_until_true(wait, _RETRY_COUNT * _RETRY_WAIT, _RETRY_WAIT)
|
test_utils.call_until_true(wait, _RETRY_COUNT * _RETRY_WAIT,
|
||||||
|
_RETRY_WAIT)
|
||||||
response = self.logs_search_client.search_messages(key, headers)
|
response = self.logs_search_client.search_messages(key, headers)
|
||||||
self.assertEqual(1, len(response))
|
self.assertEqual(1, len(response))
|
||||||
|
|
||||||
|
@ -91,12 +97,23 @@ class TestSingleLog(base.BaseLogsTestCase):
|
||||||
def test_send_header_dimensions(self):
|
def test_send_header_dimensions(self):
|
||||||
sid, message = base.generate_unique_message()
|
sid, message = base.generate_unique_message()
|
||||||
headers = {'X-Dimensions':
|
headers = {'X-Dimensions':
|
||||||
'server:WebServer01,environment:production'}
|
'server:WebServer01,environment:production'}
|
||||||
response = self._run_and_wait(sid, message, headers=headers,
|
response = self._run_and_wait(sid, message, headers=headers,
|
||||||
version="v2")
|
version="v2")
|
||||||
self.assertEqual('production', response[0]['_source']['environment'])
|
self.assertEqual('production', response[0]['_source']['environment'])
|
||||||
self.assertEqual('WebServer01', response[0]['_source']['server'])
|
self.assertEqual('WebServer01', response[0]['_source']['server'])
|
||||||
|
|
||||||
|
@decorators.attr(type="gate")
|
||||||
|
def test_send_cross_tenant(self):
|
||||||
|
sid, message = base.generate_small_message()
|
||||||
|
headers = {'X-Roles': 'admin, monitoring-delegate'}
|
||||||
|
cross_tennant_id = '2106b2c8da0eecdb3df4ea84a0b5624b'
|
||||||
|
fields = {'tenant_id': cross_tennant_id}
|
||||||
|
response = self._run_and_wait(sid, message, version="v3",
|
||||||
|
headers=headers, fields=fields)
|
||||||
|
self.assertThat(response[0]['_source']['tenant'],
|
||||||
|
matchers.StartsWith(cross_tennant_id))
|
||||||
|
|
||||||
# TODO(trebski) following test not passing - failed to retrieve
|
# TODO(trebski) following test not passing - failed to retrieve
|
||||||
# big message from elasticsearch
|
# big message from elasticsearch
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue