diff --git a/devstack/files/monasca-api/api-config.yml b/devstack/files/monasca-api/api-config.yml index fc1526adf..6228525b3 100644 --- a/devstack/files/monasca-api/api-config.yml +++ b/devstack/files/monasca-api/api-config.yml @@ -42,7 +42,7 @@ kafka: mysql: driverClass: com.mysql.jdbc.Driver - url: "jdbc:mysql://%DATABASE_HOST%:%DATABASE_PORT%/mon?connectTimeout=5000&autoReconnect=true&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=UTC" + url: "jdbc:mysql://%DATABASE_HOST%:%DATABASE_PORT%/mon?connectTimeout=5000&autoReconnect=true&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=utf8" user: "%DATABASE_USER%" password: "%DATABASE_PASSWORD%" maxWaitForConnection: 1s diff --git a/etc/api-config.conf b/etc/api-config.conf index 42de7f722..e7737d08d 100755 --- a/etc/api-config.conf +++ b/etc/api-config.conf @@ -117,7 +117,7 @@ keyspace: monasca # Below is configuration for database. [database] -connection = "mysql+pymysql://monapi:password@192.168.10.4/mon" +connection = "mysql+pymysql://monapi:password@192.168.10.4/mon?charset=utf8mb4" # backend = sqlalchemy # host = 192.168.10.4 # username = monapi diff --git a/java/src/main/resources/api-config.yml b/java/src/main/resources/api-config.yml index e706c8f21..e9b0c4f71 100644 --- a/java/src/main/resources/api-config.yml +++ b/java/src/main/resources/api-config.yml @@ -27,7 +27,7 @@ kafka: mysql: driverClass: com.mysql.jdbc.Driver - url: jdbc:mysql://192.168.10.6:3306/mon?connectTimeout=5000&autoReconnect=true&useLegacyDatetimeCode=false + url: jdbc:mysql://192.168.10.6:3306/mon?connectTimeout=5000&autoReconnect=true&useLegacyDatetimeCode=false&characterEncoding=utf8 user: monapi password: password maxWaitForConnection: 1s diff --git a/monasca_api/tests/test_alarms.py b/monasca_api/tests/test_alarms.py index 5eb122507..f183f3b48 100644 --- a/monasca_api/tests/test_alarms.py +++ b/monasca_api/tests/test_alarms.py @@ -32,6 +32,7 @@ from monasca_api.v2.reference import alarms import oslo_config.fixture import oslotest.base as oslotest +import six CONF = oslo_config.cfg.CONF @@ -204,6 +205,7 @@ class TestAlarmsStateHistory(AlarmTestBase): class TestAlarmDefinition(AlarmTestBase): + def setUp(self): super(TestAlarmDefinition, self).setUp() @@ -691,3 +693,42 @@ class TestAlarmDefinition(AlarmTestBase): self.assertEqual(self.srmock.status, falcon.HTTP_200) self.assertThat(response, RESTResponseEquals(expected_data)) + + def test_get_alarm_definitions_with_multibyte_character(self): + def_name = 'alarm_definition' + if six.PY2: + def_name = def_name.decode('utf8') + + expected_data = { + u'alarm_actions': [], u'ok_actions': [], + u'description': None, u'match_by': [u'hostname'], + u'actions_enabled': True, u'undetermined_actions': [], + u'deterministic': False, + u'expression': u'max(test.metric{hostname=host}) gte 1', + u'id': u'00000001-0001-0001-0001-000000000001', + u'severity': u'LOW', u'name': def_name + } + + self.alarm_def_repo_mock.return_value.get_alarm_definition.return_value = { + 'alarm_actions': None, + 'ok_actions': None, + 'description': None, + 'match_by': u'hostname', + 'name': def_name, + 'actions_enabled': 1, + 'undetermined_actions': None, + 'expression': u'max(test.metric{hostname=host}) gte 1', + 'id': u'00000001-0001-0001-0001-000000000001', + 'severity': u'LOW' + } + + response = self.simulate_request( + '/v2.0/alarm-definitions/%s' % (expected_data[u'id']), + headers={ + 'X-Roles': 'admin', + 'X-Tenant-Id': TENANT_ID, + } + ) + + self.assertEqual(self.srmock.status, falcon.HTTP_200) + self.assertThat(response, RESTResponseEquals(expected_data)) diff --git a/monasca_api/v2/reference/alarm_definitions.py b/monasca_api/v2/reference/alarm_definitions.py index 770ed1a8f..f2db05e35 100644 --- a/monasca_api/v2/reference/alarm_definitions.py +++ b/monasca_api/v2/reference/alarm_definitions.py @@ -266,7 +266,7 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API, description = (alarm_definition_row['description'] if alarm_definition_row['description'] is not None else None) - expression = alarm_definition_row['expression'].decode('utf8') + expression = alarm_definition_row['expression'] is_deterministic = is_definition_deterministic(expression) result = { @@ -277,11 +277,11 @@ class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API, u'description': description, u'expression': expression, u'deterministic': is_deterministic, - u'id': alarm_definition_row['id'].decode('utf8'), + u'id': alarm_definition_row['id'], u'match_by': match_by, - u'name': alarm_definition_row['name'].decode('utf8'), - u'severity': alarm_definition_row['severity'].decode( - 'utf8').upper()} + u'name': alarm_definition_row['name'], + u'severity': alarm_definition_row['severity'].upper() + } return result @@ -629,7 +629,7 @@ def get_query_alarm_definition_expression(alarm_definition, def get_query_alarm_definition_description(alarm_definition, return_none=False): if 'description' in alarm_definition: - return alarm_definition['description'].decode('utf8') + return alarm_definition['description'] else: if return_none: return None diff --git a/monasca_tempest_tests/tests/api/test_alarm_definitions.py b/monasca_tempest_tests/tests/api/test_alarm_definitions.py index 7ff475ba3..84f7db988 100644 --- a/monasca_tempest_tests/tests/api/test_alarm_definitions.py +++ b/monasca_tempest_tests/tests/api/test_alarm_definitions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -28,15 +29,6 @@ NUM_ALARM_DEFINITIONS = 2 class TestAlarmDefinitions(base.BaseMonascaTest): - @classmethod - def resource_setup(cls): - super(TestAlarmDefinitions, cls).resource_setup() - - @classmethod - def resource_cleanup(cls): - super(TestAlarmDefinitions, cls).resource_cleanup() - - # Create @test.attr(type="gate") def test_create_alarm_definition(self): @@ -345,6 +337,30 @@ class TestAlarmDefinitions(base.BaseMonascaTest): links = response_body['links'] self._verify_list_alarm_definitions_links(links) + @test.attr(type="gate") + def test_list_alarm_definitions_with_multibyte_character(self): + name = data_utils.rand_name('alarm_definition').decode('utf8') + description = 'description'.decode('utf8') + + response_body_list = self._create_alarm_definitions( + name=name, + description=description, + number_of_definitions=1 + ) + alarm_definition = response_body_list[0] + + query_param = '?name=' + urlparse.quote(name.encode('utf8')) + resp, response_body = self.monasca_client.list_alarm_definitions( + query_param) + + self._verify_list_alarm_definitions_response_body(resp, response_body) + + # Test list alarm definition response body + elements = response_body['elements'] + self._verify_alarm_definitions_list(elements, [alarm_definition]) + links = response_body['links'] + self._verify_list_alarm_definitions_links(links) + @test.attr(type="gate") def test_list_alarm_definitions_with_name(self): name = data_utils.rand_name('alarm_definition') @@ -427,7 +443,7 @@ class TestAlarmDefinitions(base.BaseMonascaTest): name=name, description="description", expression=expression, - severity="low") + severity="LOW") resp, res_body_create_alarm_def = self.monasca_client.\ create_alarm_definitions(alarm_definition) self.assertEqual(201, resp.status) @@ -701,6 +717,28 @@ class TestAlarmDefinitions(base.BaseMonascaTest): links = response_body['links'] self._verify_list_alarm_definitions_links(links) + @test.attr(type="gate") + def test_get_alarm_definition_with_multibyte_character(self): + # Create an alarm definition + name = data_utils.rand_name('alarm_definition').decode('utf8') + description = 'description'.decode('utf8') + response_body_list = self._create_alarm_definitions( + name=name, + description=description, + number_of_definitions=1 + ) + alarm_definition = response_body_list[0] + + resp, response_body = self.monasca_client.get_alarm_definition( + alarm_definition['id']) + + self.assertEqual(200, resp.status) + self._verify_element_set(response_body) + self._verify_alarm_definitions_element(response_body, + alarm_definition) + links = response_body['links'] + self._verify_list_alarm_definitions_links(links) + # Update @test.attr(type="gate") @@ -951,17 +989,28 @@ class TestAlarmDefinitions(base.BaseMonascaTest): 'LOW', [], [], []) - def _create_alarm_definitions(self, expression, number_of_definitions): + def _create_alarm_definitions(self, number_of_definitions, **kwargs): self.rule = {'expression': 'mem_total_mb > 0'} + + expression = kwargs.get('expression', None) if expression is None: - expression = "max(cpu.system_perc) > 0" + expression = 'max(cpu.system_perc) > 0' + match_by = kwargs.get('match_by', ['device']) + response_body_list = [] for i in xrange(number_of_definitions): + + name = kwargs.get('name', + data_utils.rand_name('alarm_definition')) + desc = kwargs.get('description', + data_utils.rand_name('description')) + alarm_definition = helpers.create_alarm_definition( - name=data_utils.rand_name('alarm_definition'), - description=data_utils.rand_name('description'), + name=name, + description=desc, expression=expression, - match_by=['device']) + match_by=match_by + ) resp, response_body = self.monasca_client.create_alarm_definitions( alarm_definition) self.assertEqual(201, resp.status) diff --git a/monasca_tempest_tests/tests/api/test_metrics.py b/monasca_tempest_tests/tests/api/test_metrics.py index 41bee496b..5179ae808 100644 --- a/monasca_tempest_tests/tests/api/test_metrics.py +++ b/monasca_tempest_tests/tests/api/test_metrics.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -17,6 +18,7 @@ import time from six.moves import range as xrange +from six.moves import urllib_parse as urlparse from tempest.lib.common.utils import data_utils from tempest.lib import exceptions @@ -29,14 +31,6 @@ from monasca_tempest_tests.tests.api import helpers class TestMetrics(base.BaseMonascaTest): - @classmethod - def resource_setup(cls): - super(TestMetrics, cls).resource_setup() - - @classmethod - def resource_cleanup(cls): - super(TestMetrics, cls).resource_cleanup() - @test.attr(type='gate') def test_create_metric(self): name = data_utils.rand_name('name') @@ -79,6 +73,48 @@ class TestMetrics(base.BaseMonascaTest): "metrics = 0" self.fail(error_msg) + @test.attr(type='gate') + def test_create_metric_with_multibyte_character(self): + name = data_utils.rand_name('name').decode('utf8') + key = data_utils.rand_name('key').decode('utf8') + value = data_utils.rand_name('value').decode('utf8') + timestamp = int(round(time.time() * 1000)) + time_iso = helpers.timestamp_to_iso(timestamp) + end_timestamp = int(round((time.time() + 3600 * 24) * 1000)) + end_time_iso = helpers.timestamp_to_iso(end_timestamp) + value_meta_key = data_utils.rand_name('value_meta_key').decode('utf8') + value_meta_value = data_utils.rand_name('value_meta_value').decode('utf8') + metric = helpers.create_metric(name=name, + dimensions={key: value}, + timestamp=timestamp, + value=1.23, + value_meta={ + value_meta_key: value_meta_value + }) + resp, response_body = self.monasca_client.create_metrics(metric) + self.assertEqual(204, resp.status) + query_param = '?name=' + urlparse.quote(name.encode('utf8')) + \ + '&start_time=' + time_iso + '&end_time=' + end_time_iso + for i in xrange(constants.MAX_RETRIES): + resp, response_body = self.monasca_client.\ + list_measurements(query_param) + self.assertEqual(200, resp.status) + elements = response_body['elements'] + for element in elements: + if element['name'] == name: + self._verify_list_measurements_element(element, key, value) + measurement = element['measurements'][0] + self._verify_list_measurements_measurement( + measurement, metric, value_meta_key, value_meta_value) + return + time.sleep(constants.RETRY_WAIT_SECS) + if i == constants.MAX_RETRIES - 1: + error_msg = "Failed test_create_metric: " \ + "timeout on waiting for metrics: at least " \ + "one metric is needed. Current number of " \ + "metrics = 0" + self.fail(error_msg) + @test.attr(type='gate') def test_create_metrics(self): name = data_utils.rand_name('name') @@ -547,7 +583,10 @@ class TestMetrics(base.BaseMonascaTest): set(['timestamp', 'value', 'value_meta'])) self.assertTrue(str(element['id']) is not None) if test_key is not None and test_value is not None: - self.assertEqual(str(element['dimensions'][test_key]), test_value) + self.assertEqual( + element['dimensions'][test_key].encode('utf-8'), + test_value.encode('utf-8') + ) def _verify_list_measurements_measurement(self, measurement, test_metric, test_vm_key, @@ -570,7 +609,10 @@ class TestMetrics(base.BaseMonascaTest): self.fail(error_msg) self.assertEqual(measurement[1], test_metric['value']) if test_vm_key is not None and test_vm_value is not None: - self.assertEqual(str(measurement[2][test_vm_key]), test_vm_value) + self.assertEqual( + measurement[2][test_vm_key].encode('utf-8'), + test_vm_value.encode('utf-8') + ) def _verify_list_metrics_element(self, element, test_key=None, test_value=None, test_name=None):