diff --git a/monasca_tempest_tests/clients/event_api.py b/monasca_tempest_tests/clients/event_api.py new file mode 100644 index 0000000..c151fc9 --- /dev/null +++ b/monasca_tempest_tests/clients/event_api.py @@ -0,0 +1,35 @@ +# Copyright 2019 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 elasticsearch_client +from monasca_tempest_tests.services import events_api_client + + +class Manager(clients.Manager): + def __init__(self, credentials=None): + super(Manager, self).__init__(credentials) + + self.events_api_client = events_api_client.EventApiClient( + self.auth_provider, + 'events', + None + ) + + self.events_search_client = elasticsearch_client.ElasticsearchClient( + self.auth_provider, + 'events-search', + None + ) diff --git a/monasca_tempest_tests/clients/log_api.py b/monasca_tempest_tests/clients/log_api.py index 7193e77..03dafb1 100644 --- a/monasca_tempest_tests/clients/log_api.py +++ b/monasca_tempest_tests/clients/log_api.py @@ -14,8 +14,8 @@ from tempest import clients +from monasca_tempest_tests.services import elasticsearch_client from monasca_tempest_tests.services import log_api_v3_client -from monasca_tempest_tests.services import log_search_client class Manager(clients.Manager): @@ -28,7 +28,7 @@ class Manager(clients.Manager): None ) - self.log_search_client = log_search_client.LogsSearchClient( + self.log_search_client = elasticsearch_client.ElasticsearchClient( self.auth_provider, 'logs-search', None diff --git a/monasca_tempest_tests/services/log_search_client.py b/monasca_tempest_tests/services/elasticsearch_client.py similarity index 59% rename from monasca_tempest_tests/services/log_search_client.py rename to monasca_tempest_tests/services/elasticsearch_client.py index 6a8d1c4..a126c81 100644 --- a/monasca_tempest_tests/services/log_search_client.py +++ b/monasca_tempest_tests/services/elasticsearch_client.py @@ -12,16 +12,19 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg from oslo_serialization import jsonutils as json from six import PY3 from tempest.lib.common import rest_client +CONF = cfg.CONF -class LogsSearchClient(rest_client.RestClient): + +class ElasticsearchClient(rest_client.RestClient): uri_prefix = "/elasticsearch" def __init__(self, auth_provider, service, region): - super(LogsSearchClient, self).__init__( + super(ElasticsearchClient, self).__init__( auth_provider, service, region, @@ -49,7 +52,7 @@ class LogsSearchClient(rest_client.RestClient): def count_search_messages(self, message, headers): return len(self.search_messages(message, headers)) - def search_messages(self, message, headers): + def search_messages(self, message, headers=None): uri = '_msearch' body = """ {"index" : "*", "search_type" : "dfs_query_then_fetch"} @@ -60,5 +63,30 @@ class LogsSearchClient(rest_client.RestClient): body = self.deserialize(body) return body['responses'][0].get('hits', {}).get('hits', []) + def search_event_by_event_type(self, event_type): + uri = '_msearch' + body = """ + {"index" : "*", "search_type" : "dfs_query_then_fetch"} + {"query" : {"match" : {"event_type":" """ + event_type + """ "}}} + """ + header = {'kbn-version': CONF.monitoring.kibana_version} + response, body = self.post(self._uri(uri), body, header) + self.expected_success(200, response.status) + body = self.deserialize(body) + return body['responses'][0].get('hits', {}).get('hits', []) + + def search_event(self, event): + uri = '_msearch' + header = {'kbn-version': CONF.monitoring.kibana_version} + event = json.dumps(event) + body = """ + {"index" : "*", "search_type" : "dfs_query_then_fetch"} + {"query" : {"match" : {"event":" """ + event + """ "}}} + """ + response, body = self.post(self._uri(uri), body, header) + 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) diff --git a/monasca_tempest_tests/services/events_api_client.py b/monasca_tempest_tests/services/events_api_client.py new file mode 100644 index 0000000..cd9ce2d --- /dev/null +++ b/monasca_tempest_tests/services/events_api_client.py @@ -0,0 +1,34 @@ +# Copyright 2019 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 json + +from tempest.lib.common import rest_client + + +class EventApiClient(rest_client.RestClient): + + _uri = '/v1.0/events' + + def __init__(self, auth_provider, service, region): + super(EventApiClient, self).__init__( + auth_provider, + service, + region + ) + + def send_events(self, events, headers=None): + msg = json.dumps(events) + resp, body = self.post(self._uri, body=msg, headers=headers) + return resp, body diff --git a/monasca_tempest_tests/tests/event_api/__init__.py b/monasca_tempest_tests/tests/event_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/monasca_tempest_tests/tests/event_api/base.py b/monasca_tempest_tests/tests/event_api/base.py new file mode 100644 index 0000000..6b2d640 --- /dev/null +++ b/monasca_tempest_tests/tests/event_api/base.py @@ -0,0 +1,94 @@ +# Copyright 2019 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 datetime import datetime +import json +from os import path +import pytz +import random +import string + +from oslo_config import cfg +from tempest.common import credentials_factory as cred_factory +from tempest import test + +from monasca_tempest_tests.clients import event_api as clients + +CONF = cfg.CONF + + +def get_simple_event(): + json_file_path = path.join(path.dirname(__file__), + 'req_simple_event.json') + with open(json_file_path, 'r') as f: + return json.loads(f.read()) + + +def create_header(header={}, content_type='application/json'): + header.update({'Content-Type': content_type, + 'kbn-version': CONF.monitoring.kibana_version}) + return header + + +def generate_simple_event_type_string(): + letters = string.ascii_lowercase + random_string = ''.join((random.choice(letters) for _ in range(15))) + return '{}.{}'.format(random_string[:7], random_string[8:]) + + +def generate_simple_event(event_type=None, timestamp=None, events=None, num_of_events=1): + if event_type is None: + event_type = generate_simple_event_type_string() + if events is None: + events = \ + [{ + 'dimensions': { + 'service': 'compute', + 'topic': 'notification.sample', + 'hostname': 'mars'}, + 'project_id': '6f70656e737461636b20342065766572', + 'event': { + 'event_type': event_type, + 'payload': { + 'nova_object.data': { + 'architecture': 'x86_64', + 'availability_zone': 'nova', + 'created_at': '2012-10-29T13:42:11Z'}}}}] \ + * num_of_events + if timestamp is None: + timestamp = datetime.now(tz=pytz.utc).strftime("%Y-%m-%dT%H:%M:%SZ%z") + return {'timestamp': timestamp, + 'events': events} + + +class BaseEventsTestCase(test.BaseTestCase): + """Base test case class for all Event API tests.""" + + @classmethod + def skip_checks(cls): + super(BaseEventsTestCase, cls).skip_checks() + + @classmethod + def resource_setup(cls): + super(BaseEventsTestCase, 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( + ['admin']).credentials + cls.os_primary = clients.Manager(credentials=credentials) + + cls.events_api_client = cls.os_primary.events_api_client + cls.events_search_client = cls.os_primary.events_search_client diff --git a/monasca_tempest_tests/tests/event_api/test_simple.py b/monasca_tempest_tests/tests/event_api/test_simple.py new file mode 100644 index 0000000..e6dc5d7 --- /dev/null +++ b/monasca_tempest_tests/tests/event_api/test_simple.py @@ -0,0 +1,76 @@ +# Copyright 2019 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 tempest.lib import exceptions + +from monasca_tempest_tests.tests.event_api import base + +RETRY_COUNT = 15 +RETRY_WAIT = 2 + + +class BaseEventsTestCase(base.BaseEventsTestCase): + + def check_if_events_are_present_in_db(self, event_type): + return len(self.events_search_client.search_event_by_event_type(event_type)) > 0 + + def send_and_retrieve_events(self, events=None, header=None, events_number=1): + if events is None: + event_type = base.generate_simple_event_type_string() + events = base.generate_simple_event(event_type, num_of_events=events_number) + if header is None: + header = base.create_header() + response, _ = self.events_api_client.send_events(events, header) + self.assertEqual(200, response.status) + test_utils.call_until_true(self.check_if_events_are_present_in_db, + RETRY_COUNT * RETRY_WAIT, + RETRY_WAIT, + event_type) + response = self.events_search_client.search_event_by_event_type(event_type) + + self.assertEqual(events_number, len(response)) + + @decorators.attr(type=['gate', 'smoke']) + def test_single_event(self): + self.send_and_retrieve_events() + + @decorators.attr(type='gate') + def test_multiple_events(self): + self.send_and_retrieve_events(events_number=5) + + @decorators.attr(type='gate') + def test_missing_body(self): + header = base.create_header() + try: + response, _ = self.events_api_client.send_events(None, header) + except exceptions.UnprocessableEntity as exc: + self.assertEqual(422, exc.resp.status) + + @decorators.attr(type='gate') + def test_empty_event(self): + header = base.create_header() + body = base.generate_simple_event(events=[]) + try: + response, _ = self.events_api_client.send_events(body, header) + except exceptions.UnprocessableEntity as exc: + self.assertEqual(422, exc.resp.status) + + def test_empty_content_type(self): + body = base.generate_simple_event() + try: + response, _ = self.events_api_client.send_events(body, {}) + except exceptions.BadRequest as exc: + self.assertEqual(400, exc.resp.status)