Add multibyte character support for alarm definition

It was failed that putting multibyte character into
alarm definition name and description by monascaclient.
There were some mistakes about treatment of utf8 encoding.
And mysql connection had no utf8 option, so mysql could not
handle multibyte character.

Change-Id: I8743f89fcc5d5efd4e50f440b76d78abc037e8e7
This commit is contained in:
Shinya Kawabata 2016-11-11 20:33:27 +09:00 committed by Tomasz Trębski
parent a89a4aae04
commit e8831f8229
7 changed files with 166 additions and 34 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = '_'
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))

View File

@ -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

View File

@ -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('_').decode('utf8')
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('_').decode('utf8')
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)

View File

@ -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('').decode('utf8')
key = data_utils.rand_name('').decode('utf8')
value = data_utils.rand_name('').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_').decode('utf8')
value_meta_value = data_utils.rand_name('value_meta_').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):