Merge "Remove bundled intree tempest plugin"
This commit is contained in:
commit
f15240f028
|
@ -1,211 +0,0 @@
|
|||
# Introduction
|
||||
|
||||
**Monasca-Log-Api** requires following components set up in the environment:
|
||||
|
||||
* monasca-log-transformer - component receiving data from monasca-log-api, [config](../devstack/files/transformer.conf)
|
||||
* monasca-log-persister - component saves data to ElasticSearch, [config](../devstack/files/persister.conf)
|
||||
* ElasticSearch - database that stores logs, [config](../devstack/files/elasticsearch.yml)
|
||||
|
||||
Those three components are all part of [devstack](https://github.com/openstack/monasca-api/tree/master/devstack).
|
||||
|
||||
* Logstash - it is a prerequisite for monasca-log-transformer and monasca-log-persister components
|
||||
|
||||
**Monasca-Log-Api** can be installed using following [Github repo](https://github.com/openstack/monasca-log-api/).
|
||||
In order to setup schema (kafka topics) please see [this](https://github.com/openstack/monasca-log-api/blob/master/devstack/plugin.sh#L198).
|
||||
|
||||
## Installation next to monasca-api
|
||||
|
||||
**Monasca-Api** and **Monasca-Log-Api** can be installed next to each other.
|
||||
Each one has been designed to work with different aspects of monitoring.
|
||||
Therefore it is possible to proceed with installation as described
|
||||
[here](https://github.com/openstack/monasca-log-api/blob/master/devstack/).
|
||||
|
||||
# Configuration
|
||||
1. Clone the OpenStack Tempest repo, and cd to it.
|
||||
|
||||
```bash
|
||||
git clone https://git.openstack.org/openstack/tempest.git
|
||||
cd tempest
|
||||
```
|
||||
|
||||
2. Create a virtualenv for running the Tempest tests and activate it.
|
||||
For example in the Tempest root dir
|
||||
|
||||
```bash
|
||||
virtualenv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
3. Install the Tempest requirements in the virtualenv.
|
||||
|
||||
```bash
|
||||
pip install \
|
||||
-c https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt \
|
||||
-r requirements.txt \
|
||||
-r test-requirements.txt
|
||||
```
|
||||
|
||||
4. Create ```etc/tempest.conf``` in the Tempest root dir by
|
||||
running the following command:
|
||||
|
||||
```bash
|
||||
oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf --output-file etc/tempest.conf
|
||||
```
|
||||
|
||||
Add the following sections to ```tempest.conf``` for testing
|
||||
using the monasca-vagrant environment.
|
||||
|
||||
```ini
|
||||
[identity]
|
||||
region = RegionOne
|
||||
auth_version = v3
|
||||
uri = http://10.36.99.238/identity_admin/v2.0
|
||||
uri_v3 = http://10.36.99.238/identity_admin/v3
|
||||
user_lockout_failure_attempts = 2
|
||||
user_locakout_duration = 5
|
||||
user_unique_last_password_count = 2
|
||||
admin_domain_scope = True
|
||||
|
||||
[auth]
|
||||
tempest_roles = monasca-user
|
||||
admin_project_name = admin
|
||||
admin_domain_name = default
|
||||
admin_password = secretadmin
|
||||
admin_username = admin
|
||||
use_dynamic_credentials = True
|
||||
|
||||
[monitoring]
|
||||
kibana_version = 4.6.3
|
||||
api_version = v2.0 # or v3.0
|
||||
```
|
||||
|
||||
Edit the variable values in the identity section to match your particular
|
||||
monasca-vagrant environment. Best way to do this might be
|
||||
|
||||
```bash
|
||||
source devstack/openrc {username} {password}
|
||||
```
|
||||
|
||||
and collect all relevant values using
|
||||
|
||||
```bash
|
||||
env | grep OS_
|
||||
```
|
||||
|
||||
An output will be similar to this one
|
||||
|
||||
```bash
|
||||
OS_PROJECT_DOMAIN_ID=default
|
||||
OS_REGION_NAME=RegionOne
|
||||
OS_USER_DOMAIN_ID=default
|
||||
OS_PROJECT_NAME=admin
|
||||
OS_IDENTITY_API_VERSION=3
|
||||
OS_PASSWORD=secretadmin
|
||||
OS_AUTH_TYPE=password
|
||||
OS_AUTH_URL=http://10.36.99.238/identity_admin
|
||||
OS_USERNAME=admin
|
||||
OS_TENANT_NAME=admin
|
||||
OS_VOLUME_API_VERSION=2
|
||||
OS_NO_CACHE=True
|
||||
```
|
||||
|
||||
5. Create ```etc/logging.conf``` in the Tempest root dir by making a copying
|
||||
```logging.conf.sample```.
|
||||
|
||||
6. Clone the monasca-log-api repo in a directory somewhere outside of the
|
||||
Tempest root dir.
|
||||
|
||||
7. Install the monasca-log-api in your venv, which will also register
|
||||
the Monasca Log Api Tempest Plugin as, monasca_log_api_tempest.
|
||||
|
||||
cd into the monasca-log-api root directory. Making sure that the tempest
|
||||
virtual env is still active, run the following command.
|
||||
|
||||
```
|
||||
pip install \
|
||||
-c https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt \
|
||||
-r requirements.txt \
|
||||
-r test-requirements.txt
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
See the [OpenStack Tempest Plugin
|
||||
Interface](https://docs.openstack.org/tempest/latest/plugin.html), for more
|
||||
details on Tempest Plugins and the plugin registration process.
|
||||
|
||||
# Running the Monasca Log Api Tempest
|
||||
The Monasca Tempest Tests can be run using a variety of methods including:
|
||||
1. [Testr](https://wiki.openstack.org/wiki/Testr)
|
||||
2. [Os-testr](https://docs.openstack.org/os-testr/latest/)
|
||||
3. [PyCharm](https://www.jetbrains.com/pycharm/)
|
||||
4. Tempest Scripts in Devstack
|
||||
|
||||
## Run the tests from the CLI using testr
|
||||
|
||||
[Testr](https://wiki.openstack.org/wiki/Testr) is a test runner that can be used to run the Tempest tests.
|
||||
|
||||
1. In the Tempest root dir, create a list of the Monasca Tempest Tests in a file.
|
||||
|
||||
```sh
|
||||
testr list-tests monasca_log_api_tempest > monasca_log_api_tempest
|
||||
```
|
||||
|
||||
2. Run the tests using testr
|
||||
|
||||
```sh
|
||||
testr run --load-list=monasca_log_api_tempest
|
||||
```
|
||||
|
||||
You can also use testr to create a list of specific tests for your needs.
|
||||
|
||||
## Run the tests from the CLI using os-testr (no file necessary)
|
||||
[Os-testr](https://docs.openstack.org/os-testr/latest/) is a test wrapper
|
||||
that can be used to run the Monasca Tempest tests.
|
||||
|
||||
1. In the Tempest root dir:
|
||||
|
||||
```
|
||||
ostestr --serial --regex monasca_log_api_tempest
|
||||
```
|
||||
|
||||
```--serial``` option is necessary here. Monasca Log Api tempest tests can't
|
||||
be run in parallel (default option in ostestr) because some tests depend on the
|
||||
same data and will randomly fail.
|
||||
|
||||
## Running/Debugging the Monasca Tempest Tests in PyCharm
|
||||
|
||||
Assuming that you have already created a PyCharm project for the
|
||||
```monasca-log-api``` do the following:
|
||||
|
||||
1. In PyCharm, Edit Configurations and add a new Python tests configuration by selecting Python tests->Nosetests.
|
||||
2. Name the test. For example TestSingleLog.
|
||||
3. Set the path to the script with the tests to run. For example, ~/repos/monasca-log-api/monasca_log_api_tempest/tests/test_single.py
|
||||
4. Set the name of the Class to test. For example TestVersions.
|
||||
5. Set the working directory to your local root Tempest repo. For example, ~/repos/tempest.
|
||||
6. Select the Python interpreter for your project to be the same as the one virtualenv created above. For example, ~/repos/tempest/.venv
|
||||
7. Run the tests. You should also be able to debug them.
|
||||
8. Step and repeat for other tests.
|
||||
|
||||
## Run the tests from the CLI using tempest scripts in devstack
|
||||
|
||||
1. In /opt/stack/tempest, run ```./run_tempest.sh monasca_log_api_tempest```
|
||||
2. If asked to create a new virtual environment, select yes
|
||||
3. Activate the virtual environment ```source .venv/bin/activate```
|
||||
4. In your monasca-log-api directory, run ```python setup.py install```
|
||||
5. In /opt/stack/tempest, run ```./run_tempest.sh monasca_log_api_tempest```
|
||||
|
||||
# References
|
||||
This section provides a few additional references that might be useful:
|
||||
* [Tempest - The OpenStack Integration Test Suite](https://docs.openstack.org/tempest/latest/overview.html#quickstart)
|
||||
* [Tempest Configuration Guide](https://github.com/openstack/tempest/blob/master/doc/source/configuration.rst#id1)
|
||||
* [OpenStack Tempest Plugin Interface](https://docs.openstack.org/tempest/latest/plugin.html)
|
||||
|
||||
In addition to the above references, another source of information is the following OpenStack projects:
|
||||
* [Manila Tempest Tests](https://github.com/openstack/manila/tree/master/manila_tempest_tests)
|
||||
* [Congress Tempest Tests](https://github.com/openstack/congress/tree/master/congress_tempest_tests).
|
||||
In particular, the Manila Tempest Tests were used as a reference implementation to develop the Monasca Tempest Tests.
|
||||
There is also a wiki [HOWTO use tempest with manila](https://wiki.openstack.org/wiki/Manila/docs/HOWTO_use_tempest_with_manila) that might be useful for Monasca too.
|
||||
|
||||
# Issues
|
||||
* Update documentation for testing using Devstack when available.
|
||||
* Consider changing from monasca_tempest_tests to monasca_api_tempest_tests.
|
|
@ -1,42 +0,0 @@
|
|||
# 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_log_api_tempest.services import log_api_v2_client
|
||||
from monasca_log_api_tempest.services import log_api_v3_client
|
||||
from monasca_log_api_tempest.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
|
||||
)
|
|
@ -1,39 +0,0 @@
|
|||
# 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_config import cfg
|
||||
|
||||
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'))
|
||||
]
|
||||
|
||||
monitoring_group = cfg.OptGroup(name='monitoring',
|
||||
title='Monitoring Service Options')
|
||||
MonitoringGroup = [
|
||||
cfg.StrOpt('api_version',
|
||||
default='v2.0',
|
||||
help='monasca-log-api API version'),
|
||||
cfg.StrOpt('kibana_version',
|
||||
default='4.6.3',
|
||||
help='Kibana version')
|
||||
]
|
|
@ -1,45 +0,0 @@
|
|||
# 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 os
|
||||
|
||||
from tempest import config
|
||||
from tempest.test_discover import plugins
|
||||
|
||||
from monasca_log_api_tempest import config as config_log_api
|
||||
|
||||
_ROOT_PCKG_NAME = "monasca_log_api_tempest"
|
||||
|
||||
|
||||
class MonascaLogApiTempestPlugin(plugins.TempestPlugin):
|
||||
def load_tests(self):
|
||||
base_path = os.path.split(os.path.dirname(
|
||||
os.path.abspath(__file__)))[0]
|
||||
test_dir = "%s/tests" % _ROOT_PCKG_NAME
|
||||
full_test_dir = os.path.join(base_path, test_dir)
|
||||
return full_test_dir, base_path
|
||||
|
||||
def register_opts(self, conf):
|
||||
config.register_opt_group(
|
||||
conf,
|
||||
config_log_api.service_available_group,
|
||||
config_log_api.ServiceAvailableGroup
|
||||
)
|
||||
config.register_opt_group(conf,
|
||||
config_log_api.monitoring_group,
|
||||
config_log_api.MonitoringGroup)
|
||||
|
||||
def get_opt_lists(self):
|
||||
return [(config_log_api.monitoring_group.name,
|
||||
config_log_api.MonitoringGroup)]
|
|
@ -1,51 +0,0 @@
|
|||
# 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)
|
|
@ -1,52 +0,0 @@
|
|||
# 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)
|
|
@ -1,62 +0,0 @@
|
|||
# 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)
|
|
@ -1,125 +0,0 @@
|
|||
# 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 random
|
||||
import string
|
||||
|
||||
from oslo_config import cfg
|
||||
from tempest.common import credentials_factory as cred_factory
|
||||
from tempest import test
|
||||
from tempest import exceptions
|
||||
|
||||
from monasca_log_api_tempest import 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
|
|
@ -1,107 +0,0 @@
|
|||
# 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_log_api_tempest.tests 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
|
||||
except exceptions.UnexpectedContentType as uct:
|
||||
self.assertEqual(503, uct.resp.status)
|
||||
return
|
||||
|
||||
self.assertTrue(False, 'API should respond with 413 or 503')
|
||||
|
||||
@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
|
||||
except exceptions.UnexpectedContentType as uct:
|
||||
self.assertEqual(503, uct.resp.status)
|
||||
return
|
||||
|
||||
self.assertTrue(False, 'API should respond with 413 or 503')
|
|
@ -1,128 +0,0 @@
|
|||
# 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_log_api_tempest.tests 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'}
|
||||
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')
|
|
@ -1,63 +0,0 @@
|
|||
# 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_log_api.tests import base as api_base
|
||||
from monasca_log_api_tempest.tests import base
|
||||
|
||||
_API_VERSION = 'v3'
|
||||
_RETRY_COUNT = 15
|
||||
_RETRY_WAIT = 2
|
||||
_UNICODE_CASES = api_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
|
||||
})
|
|
@ -25,7 +25,6 @@ setup-hooks =
|
|||
[files]
|
||||
packages =
|
||||
monasca_log_api
|
||||
monasca_log_api_tempest
|
||||
|
||||
data_files =
|
||||
etc/monasca =
|
||||
|
@ -39,9 +38,6 @@ console_scripts =
|
|||
wsgi_scripts =
|
||||
monasca-log-api-wsgi = monasca_log_api.app.wsgi:main
|
||||
|
||||
tempest.test_plugins =
|
||||
monasca_log_api_tests = monasca_log_api_tempest.plugin:MonascaLogApiTempestPlugin
|
||||
|
||||
oslo.config.opts =
|
||||
monasca_log_api = monasca_log_api.conf:list_opts
|
||||
|
||||
|
|
Loading…
Reference in New Issue