Merge "Merged monasca log api tempest plugin into monasca tempest plugin"

This commit is contained in:
Zuul 2018-01-25 07:52:23 +00:00 committed by Gerrit Code Review
commit 7acfc005d6
21 changed files with 771 additions and 16 deletions

View File

@ -2,7 +2,7 @@
Monasca Tempest Plugin
======================
Tempest plugin for Monasca Project
Tempest plugin for Monasca API and Log API Project
It contains the tempest plugin for the functional testing of Monasca Project.

33
devstack/README.rst Normal file
View File

@ -0,0 +1,33 @@
====================
Enabling in Devstack
====================
**WARNING**: the stack.sh script must be run in a disposable VM that is not
being created automatically, see the README.md file in the "devstack"
repository.
1. Download DevStack::
git clone https://git.openstack.org/openstack-dev/devstack.git
cd devstack
2. Add this repo as an external repository::
> cat local.conf
[[local|localrc]]
enable_plugin monasca-tempest-plugin https://git.openstack.org/openstack/monasca-tempest-plugin
3. run ``stack.sh``
Running Monasca tempest tests
=============================
1. Listing Monasca tempest tests::
tempest list-plugins
2. Running monasca tempest tests::
cd /opt/stack/tempest
tempest run -r monasca_tempest_plugin.tests.api
tempest run -r monasca_tempest_plugin.tests.log_api

13
devstack/plugin.sh Normal file
View File

@ -0,0 +1,13 @@
# install_monasca_tempest_plugin
function install_monasca_tempest_plugin {
setup_dev_lib "monasca-tempest-plugin"
}
if [[ "$1" == "stack" ]]; then
case "$2" in
install)
echo_summary "Installing monasca-tempest-plugin"
install_monasca_tempest_plugin
;;
esac
fi

3
devstack/settings Normal file
View File

@ -0,0 +1,3 @@
GITREPO["monasca-tempest-plugin"]=${MONASCA_TEMPEST_REPO:-${GIT_BASE}/openstack/monasca-tempest-plugin.git}
GITDIR["monasca-tempest-plugin"]=$DEST/monasca-tempest-plugin
GITBRANCH["monasca-tempest-plugin"]=master

View File

@ -0,0 +1,42 @@
# Copyright 2015-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 tempest import clients
from monasca_tempest_tests.services import log_api_v2_client
from monasca_tempest_tests.services import log_api_v3_client
from monasca_tempest_tests.services import log_search_client
class Manager(clients.Manager):
def __init__(self, credentials=None):
super(Manager, self).__init__(credentials)
self.log_api_clients = {
"v2": log_api_v2_client.LogApiV2Client(
self.auth_provider,
'logs_v2',
None
),
"v3": log_api_v3_client.LogApiV3Client(
self.auth_provider,
'logs',
None
)
}
self.log_search_client = log_search_client.LogsSearchClient(
self.auth_provider,
'logs-search',
None
)

View File

@ -15,10 +15,21 @@
from oslo_config import cfg
service_option = cfg.BoolOpt("monasca",
default=True,
help="Whether or not Monasca is expected to be "
"available")
service_available_group = cfg.OptGroup(name='service_available',
title='Available OpenStack Services')
ServiceAvailableGroup = [
cfg.BoolOpt('logs',
default=True,
help=('Whether or not Monasca-Log-Api '
'is expected to be available')),
cfg.BoolOpt('logs-search',
default=True,
help=('Whether or not Monasca-Log-Api search engine '
'(ElasticSearch) is expected to be available')),
cfg.BoolOpt("monasca",
default=True,
help="Whether or not Monasca is expected to be "
"available")]
monitoring_group = cfg.OptGroup(name="monitoring",
title="Monitoring Service Options")
@ -37,5 +48,11 @@ MonitoringGroup = [
default='publicURL',
choices=['public', 'admin', 'internal',
'publicURL', 'adminURL', 'internalURL'],
help="The endpoint type to use for the monitoring service.")
help="The endpoint type to use for the monitoring service."),
cfg.StrOpt('api_version',
default='v2.0',
help='monasca-log-api API version'),
cfg.StrOpt('kibana_version',
default='4.6.3',
help='Kibana version')
]

View File

@ -14,6 +14,7 @@
import os
from tempest import config
from tempest.test_discover import plugins
from monasca_tempest_tests import config as config_monitoring
@ -28,13 +29,15 @@ class MonascaTempestPlugin(plugins.TempestPlugin):
return full_test_dir, base_path
def register_opts(self, conf):
conf.register_opt(config_monitoring.service_option,
group='service_available')
conf.register_group(config_monitoring.monitoring_group)
conf.register_opts(config_monitoring.MonitoringGroup,
group='monitoring')
config.register_opt_group(
conf,
config_monitoring.service_available_group,
config_monitoring.ServiceAvailableGroup
)
config.register_opt_group(conf,
config_monitoring.monitoring_group,
config_monitoring.MonitoringGroup)
def get_opt_lists(self):
return [(config_monitoring.monitoring_group.name,
config_monitoring.MonitoringGroup),
('service_available', [config_monitoring.service_option])]
config_monitoring.MonitoringGroup)]

View File

@ -0,0 +1,51 @@
# 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.
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
class LogApiV2Client(rest_client.RestClient):
_uri = "/log/single"
def __init__(self, auth_provider, service, region):
super(LogApiV2Client, self).__init__(
auth_provider,
service,
region
)
def get_version(self):
resp, response_body = self.send_request('GET', '/')
return resp, response_body
def send_single_log(self,
log,
headers=None,
fields=None):
default_headers = {
'X-Tenant-Id': 'b4265b0a48ae4fd3bdcee0ad8c2b6012',
'X-Roles': 'admin',
'X-Dimensions': 'dev:tempest'
}
default_headers.update(headers)
msg = json.dumps(log)
resp, body = self.post(LogApiV2Client._uri, msg, default_headers)
return resp, body
def custom_request(self, method, headers=None, body=None):
self.request(method=method, url=LogApiV2Client._uri, headers=headers, body=body)

View File

@ -0,0 +1,52 @@
# 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.
from oslo_serialization import jsonutils as json
from six.moves.urllib.parse import urlencode
from tempest.lib.common import rest_client
class LogApiV3Client(rest_client.RestClient):
_uri = "/logs"
def __init__(self, auth_provider, service, region):
super(LogApiV3Client, self).__init__(
auth_provider,
service,
region
)
def get_version(self):
resp, response_body = self.send_request('GET', '/')
return resp, response_body
def send_single_log(self, log, headers=None, fields=None):
default_headers = {
'X-Tenant-Id': 'b4265b0a48ae4fd3bdcee0ad8c2b6012',
'X-Roles': 'admin',
}
default_headers.update(headers)
msg = json.dumps(log)
uri = LogApiV3Client._uri
if fields:
uri += '?' + urlencode(fields)
resp, body = self.post(uri, msg, default_headers)
return resp, body
def custom_request(self, method, headers=None, body=None):
self.request(method=method, url=LogApiV3Client._uri, headers=headers, body=body)

View File

@ -0,0 +1,62 @@
# 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.
from oslo_serialization import jsonutils as json
from tempest.lib.common import rest_client
class LogsSearchClient(rest_client.RestClient):
uri_prefix = "/elasticsearch"
def __init__(self, auth_provider, service, region):
super(LogsSearchClient, self).__init__(
auth_provider,
service,
region,
)
@staticmethod
def deserialize(body):
return json.loads(body.replace("\n", ""))
@staticmethod
def serialize(body):
return json.dumps(body)
def get_metadata(self):
uri = "/"
response, body = self.get(self._uri(uri))
self.expected_success(200, response.status)
if body:
body = self.deserialize(body)
return response, body
def count_search_messages(self, message, headers):
return len(self.search_messages(message, headers))
def search_messages(self, message, headers):
uri = '_msearch'
body = """
{"index" : "*", "search_type" : "dfs_query_then_fetch"}
{"query" : {"match" : {"message":" """+message+""" "}}}
"""
response, body = self.post(self._uri(uri), body, headers)
self.expected_success(200, response.status)
body = self.deserialize(body)
return body['responses'][0].get('hits', {}).get('hits', [])
def _uri(self, url):
return '{}/{}'.format(self.uri_prefix, url)

View File

@ -18,7 +18,7 @@ from tempest import config
from tempest.lib import exceptions
import tempest.test
from monasca_tempest_tests import clients
from monasca_tempest_tests.clients import api as clients
CONF = config.CONF

View File

@ -16,7 +16,7 @@ import time
from tempest.lib import decorators
from tempest.lib import exceptions
from monasca_tempest_tests import clients
from monasca_tempest_tests.clients import api as clients
from monasca_tempest_tests.tests.api import base
from monasca_tempest_tests.tests.api import helpers

View File

@ -0,0 +1,187 @@
# coding=utf-8
#
# 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 codecs
import random
import string
from oslo_config import cfg
import six
from tempest.common import credentials_factory as cred_factory
from tempest import exceptions
from tempest import test
from monasca_tempest_tests.clients import log_api as clients
CONF = cfg.CONF
_ONE_MB = 1024 * 1024 # MB
def _get_message_size(size_base):
"""Returns message size in number of characters.
Method relies on UTF-8 where 1 character = 1 byte.
"""
return int(round(size_base * _ONE_MB, 1))
_SMALL_MESSAGE_SIZE = _get_message_size(0.001)
_MEDIUM_MESSAGE_SIZE = _get_message_size(0.01)
_LARGE_MESSAGE_SIZE = _get_message_size(0.1)
_REJECTABLE_MESSAGE_SIZE = _get_message_size(1.1)
def generate_unique_message(message=None, size=50):
letters = string.ascii_lowercase
def rand(amount, space=True):
space = ' ' if space else ''
return ''.join((random.choice(letters + space) for _ in range(amount)))
sid = rand(10, space=False)
if not message:
message = rand(size)
return sid, sid + ' ' + message
def generate_small_message(message=None):
return generate_unique_message(message, _SMALL_MESSAGE_SIZE)
def generate_medium_message(message=None):
return generate_unique_message(message, _MEDIUM_MESSAGE_SIZE)
def generate_large_message(message=None):
return generate_unique_message(message, _LARGE_MESSAGE_SIZE)
def generate_rejectable_message(message=None):
return generate_unique_message(message, _REJECTABLE_MESSAGE_SIZE)
def _get_headers(headers=None, content_type="application/json"):
if not headers:
headers = {}
headers.update({
'Content-Type': content_type,
'kbn-version': CONF.monitoring.kibana_version
})
return headers
def _get_data(message, content_type="application/json", version="v3"):
if version == "v3":
data = {
'logs': [{
'message': message
}]
}
elif 'application/json' == content_type:
data = {
'message': message
}
return data
class BaseLogsTestCase(test.BaseTestCase):
"""Base test case class for all Monitoring API tests."""
@classmethod
def skip_checks(cls):
super(BaseLogsTestCase, cls).skip_checks()
@classmethod
def resource_setup(cls):
super(BaseLogsTestCase, cls).resource_setup()
auth_version = CONF.identity.auth_version
cred_provider = cred_factory.get_credentials_provider(
cls.__name__,
identity_version=auth_version)
credentials = cred_provider.get_creds_by_roles(
['monasca-user', 'admin']).credentials
cls.os_primary = clients.Manager(credentials=credentials)
cls.logs_clients = cls.os_primary.log_api_clients
cls.logs_search_client = cls.os_primary.log_search_client
@staticmethod
def cleanup_resources(method, list_of_ids):
for resource_id in list_of_ids:
try:
method(resource_id)
except exceptions.EndpointNotFound:
pass
def _hex_to_unicode(hex_raw):
hex_raw = six.b(hex_raw.replace(' ', ''))
hex_str_raw = codecs.getdecoder('hex')(hex_raw)[0]
hex_str = hex_str_raw.decode('utf-8', 'replace')
return hex_str
# NOTE(trebskit) => http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
UNICODE_MESSAGES = [
# Unicode is evil...
{'case': 'arabic', 'input': 'يونيكود هو الشر'},
{'case': 'polish', 'input': 'Unicode to zło'},
{'case': 'greek', 'input': 'Unicode είναι κακό'},
{'case': 'portuguese', 'input': 'Unicode é malvado'},
{'case': 'lao', 'input': 'unicode ເປັນຄວາມຊົ່ວຮ້າຍ'},
{'case': 'german', 'input': 'Unicode ist böse'},
{'case': 'japanese', 'input': 'ユニコードは悪です'},
{'case': 'russian', 'input': 'Unicode - зло'},
{'case': 'urdu', 'input': 'یونیسیڈ برائی ہے'},
{'case': 'weird', 'input': '🆄🅽🅸🅲🅾🅳🅴 🅸🆂 🅴🆅🅸🅻...'}, # funky, huh ?
# conditions from link above
# 2.3 Other boundary conditions
{'case': 'stress_2_3_1', 'input': _hex_to_unicode('ed 9f bf')},
{'case': 'stress_2_3_2', 'input': _hex_to_unicode('ee 80 80')},
{'case': 'stress_2_3_3', 'input': _hex_to_unicode('ef bf bd')},
{'case': 'stress_2_3_4', 'input': _hex_to_unicode('f4 8f bf bf')},
{'case': 'stress_2_3_5', 'input': _hex_to_unicode('f4 90 80 80')},
# 3.5 Impossible byes
{'case': 'stress_3_5_1', 'input': _hex_to_unicode('fe')},
{'case': 'stress_3_5_2', 'input': _hex_to_unicode('ff')},
{'case': 'stress_3_5_3', 'input': _hex_to_unicode('fe fe ff ff')},
# 4.1 Examples of an overlong ASCII character
{'case': 'stress_4_1_1', 'input': _hex_to_unicode('c0 af')},
{'case': 'stress_4_1_2', 'input': _hex_to_unicode('e0 80 af')},
{'case': 'stress_4_1_3', 'input': _hex_to_unicode('f0 80 80 af')},
{'case': 'stress_4_1_4', 'input': _hex_to_unicode('f8 80 80 80 af')},
{'case': 'stress_4_1_5', 'input': _hex_to_unicode('fc 80 80 80 80 af')},
# 4.2 Maximum overlong sequences
{'case': 'stress_4_2_1', 'input': _hex_to_unicode('c1 bf')},
{'case': 'stress_4_2_2', 'input': _hex_to_unicode('e0 9f bf')},
{'case': 'stress_4_2_3', 'input': _hex_to_unicode('f0 8f bf bf')},
{'case': 'stress_4_2_4', 'input': _hex_to_unicode('f8 87 bf bf bf')},
{'case': 'stress_4_2_5', 'input': _hex_to_unicode('fc 83 bf bf bf bf')},
# 4.3 Overlong representation of the NUL character
{'case': 'stress_4_3_1', 'input': _hex_to_unicode('c0 80')},
{'case': 'stress_4_3_2', 'input': _hex_to_unicode('e0 80 80')},
{'case': 'stress_4_3_3', 'input': _hex_to_unicode('f0 80 80 80')},
{'case': 'stress_4_3_4', 'input': _hex_to_unicode('f8 80 80 80 80')},
{'case': 'stress_4_3_5', 'input': _hex_to_unicode('fc 80 80 80 80 80')},
# and some cheesy example from polish novel 'Pan Tadeusz'
{'case': 'mr_t', 'input': 'Hajże na Soplicę!'},
# it won't be complete without that one
{'case': 'mr_b', 'input': 'Grzegorz Brzęczyszczykiewicz, '
'Chrząszczyżewoszyce, powiat Łękołody'},
# great success, christmas time
{'case': 'olaf', 'input': ''}
]

View File

@ -0,0 +1,101 @@
# Copyright 2015-2017 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 tempest.lib import decorators
from tempest.lib import exceptions
from monasca_tempest_tests.tests.log_api import base
class TestLogApiConstraints(base.BaseLogsTestCase):
@decorators.attr(type='gate')
def test_should_reject_if_body_is_empty(self):
headers = base._get_headers()
for cli in self.logs_clients.itervalues():
try:
cli.custom_request('POST', headers, None)
except exceptions.UnprocessableEntity as urc:
# depending on the actual server (for example gunicorn vs mod_wsgi)
# monasca-log-api may return a different error code
self.assertTrue(urc.resp.status in [411, 422])
return
self.assertTrue(False, 'API should respond with an error')
@decorators.attr(type='gate')
def test_should_reject_if_content_type_missing(self):
headers = base._get_headers(content_type='')
for cli in self.logs_clients.itervalues():
try:
cli.custom_request('POST', headers, '{}')
except exceptions.BadRequest as urc:
self.assertEqual(400, urc.resp.status)
return
self.assertTrue(False, 'API should respond with 400')
@decorators.attr(type='gate')
def test_should_reject_if_wrong_content_type(self):
headers = base._get_headers(content_type='video/3gpp')
for cli in self.logs_clients.itervalues():
try:
cli.custom_request('POST', headers, '{}')
except exceptions.InvalidContentType as urc:
self.assertEqual(415, urc.resp.status)
return
self.assertTrue(False, 'API should respond with 400')
@decorators.attr(type='gate')
def test_should_reject_too_big_message(self):
_, message = base.generate_rejectable_message()
headers = base._get_headers(self.logs_clients["v3"].get_headers())
# Add 'Connection: Keep-Alive' to send large message before
# connection is closed by client. In class ClosingHttp is added
# header 'connection:close' (which will cause closing socket before sending whole message).
# Data are send in small TCP packages.
# Without this header set to Keep-Alive Tempest lib will try to retry connection and finally
# raise ProtocolError.
headers.update({'Connection': 'Keep-Alive'})
for ver, cli in self.logs_clients.items():
data = base._get_data(message, version=ver)
try:
cli.send_single_log(data, headers)
except exceptions.OverLimit as urc:
self.assertEqual(413, urc.resp.status)
return
self.assertTrue(False, 'API should respond with 413')
@decorators.attr(type='gate')
def test_should_reject_too_big_message_multiline(self):
_, message = base.generate_rejectable_message()
message = message.replace(' ', '\n')
headers = base._get_headers(self.logs_clients["v3"].get_headers())
# Add Connection: Keep-Alive to send large message before
# connection is closed by cli. In class ClosingHttp is added
# header connection:close (which will cause closing socket before sending whole message).
# Data are send in small TCP packages.
# Without this header set to Keep-Alive Tempest lib will try to retry connection and finally
# raise ProtocolError.
headers.update({'Connection': 'Keep-Alive'})
for ver, cli in self.logs_clients.items():
data = base._get_data(message, version=ver)
try:
cli.send_single_log(data, headers)
except exceptions.OverLimit as urc:
self.assertEqual(413, urc.resp.status)
return
self.assertTrue(False, 'API should respond with 413')

View File

@ -0,0 +1,127 @@
# Copyright 2015-2017 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 tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from testtools import matchers
from monasca_tempest_tests.tests.log_api import base
_RETRY_COUNT = 15
_RETRY_WAIT = 2
class TestSingleLog(base.BaseLogsTestCase):
def _run_and_wait(self, key, data, version,
content_type='application/json',
headers=None, fields=None):
headers = base._get_headers(headers, content_type)
def wait():
return self.logs_search_client.count_search_messages(key,
headers) > 0
self.assertEqual(0, self.logs_search_client.count_search_messages(key,
headers),
'Find log message in elasticsearch: {0}'.format(key))
headers = base._get_headers(headers, content_type)
data = base._get_data(data, content_type, version=version)
client = self.logs_clients[version]
response, _ = client.send_single_log(data, headers, fields)
self.assertEqual(204, response.status)
test_utils.call_until_true(wait, _RETRY_COUNT * _RETRY_WAIT,
_RETRY_WAIT)
response = self.logs_search_client.search_messages(key, headers)
self.assertEqual(1, len(response))
return response
@decorators.attr(type="gate")
def test_small_message(self):
for ver in self.logs_clients:
self._run_and_wait(*base.generate_small_message(), version=ver)
@decorators.attr(type="gate")
def test_medium_message(self):
for ver in self.logs_clients:
self._run_and_wait(*base.generate_medium_message(), version=ver)
@decorators.attr(type="gate")
def test_big_message(self):
for ver in self.logs_clients:
self._run_and_wait(*base.generate_large_message(), version=ver)
@decorators.attr(type="gate")
def test_small_message_multiline(self):
for ver in self.logs_clients:
sid, message = base.generate_small_message()
self._run_and_wait(sid, message.replace(' ', '\n'), version=ver)
@decorators.attr(type="gate")
def test_medium_message_multiline(self):
for ver in self.logs_clients:
sid, message = base.generate_medium_message()
self._run_and_wait(sid, message.replace(' ', '\n'), version=ver)
@decorators.attr(type="gate")
def test_big_message_multiline(self):
for ver in self.logs_clients:
sid, message = base.generate_large_message()
self._run_and_wait(sid, message.replace(' ', '\n'), version=ver)
@decorators.attr(type="gate")
def test_send_header_application_type(self):
sid, message = base.generate_unique_message()
headers = {'X-Application-Type': 'application-type-test'}
response = self._run_and_wait(sid, message, headers=headers,
version="v2")
self.assertEqual('application-type-test',
response[0]['_source']['component'])
@decorators.attr(type="gate")
def test_send_header_dimensions(self):
sid, message = base.generate_unique_message()
headers = {'X-Dimensions': 'server:WebServer01,environment:production'} # noqa
response = self._run_and_wait(sid, message, headers=headers,
version="v2")
self.assertEqual('production', response[0]['_source']['environment'])
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
# big message from elasticsearch
# @decorators.attr(type='gate')
# def test_should_truncate_big_message(self):
# message_size = base._get_message_size(0.9999)
# sid, message = base.generate_unique_message(size=message_size)
#
# headers = base._get_headers(self.logs_clients.get_headers())
# response = self._run_and_wait(sid, message, headers=headers)
#
# self.assertTrue(False, 'API should respond with 500')

View File

@ -0,0 +1,62 @@
# Copyright 2017 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 tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from monasca_tempest_tests.tests.log_api import base
_API_VERSION = 'v3'
_RETRY_COUNT = 15
_RETRY_WAIT = 2
_UNICODE_CASES = base.UNICODE_MESSAGES
class TestUnicodeV3(base.BaseLogsTestCase):
def _run_and_wait(self, key, data,
content_type='application/json',
headers=None, fields=None):
headers = base._get_headers(headers, content_type)
def wait():
return self.logs_search_client.count_search_messages(key,
headers) > 0
self.assertEqual(0, self.logs_search_client.count_search_messages(key,
headers),
'Find log message in elasticsearch: {0}'.format(key))
headers = base._get_headers(headers, content_type)
data = base._get_data(data, content_type, version=_API_VERSION)
client = self.logs_clients[_API_VERSION]
response, _ = client.send_single_log(data, headers, fields)
self.assertEqual(204, response.status)
test_utils.call_until_true(wait, _RETRY_COUNT * _RETRY_WAIT,
_RETRY_WAIT)
response = self.logs_search_client.search_messages(key, headers)
self.assertEqual(1, len(response))
return response
@decorators.attr(type="gate")
def test_unicode_message(self):
for m in _UNICODE_CASES:
case, msg = m.values()
self._run_and_wait(*base.generate_small_message(msg), headers={
'LA-Unicode-Case': case
})

View File

@ -8,3 +8,5 @@ oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
six>=1.10.0 # MIT
oslo.utils>=3.33.0 # Apache-2.0
tempest>=17.1.0 # Apache-2.0
oslotest>=3.2.0 # Apache-2.0
mock>=2.0.0 # BSD

View File

@ -46,7 +46,7 @@ commands = oslo_debug_helper {posargs}
# H201 no 'except:' at least use 'except Exception:'
# H302 import only modules
# H405 multi line docstring summary not separated with an empty line
ignore = F821,H201,H302,H405
ignore = F821,H201,H302,H405,E901,E226
# H106: Dont put vim configuration in source files
# H203: Use assertIs(Not)None to check for None
enable-extensions=H106,H203