Preserve data type when parsing MySQL configs
The data type information was lost in conversions. IniCodec should deserialize Python objects (like other codecs). guestagent_utils.to_bytes should return byte values as ints. * The IniCodec is also used by Cassandra. Tested with both MySQL and Cassandra scenario tests. Change-Id: Ibb703b3db6814fc0c9ea4c6d96399f6c881cea03 Closes-Bug: 1599656
This commit is contained in:
parent
6e2922bc49
commit
df509b7252
@ -1,5 +1,5 @@
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 1124
|
||||
Content-Length: 1094
|
||||
Date: Mon, 18 Mar 2013 19:09:17 GMT
|
||||
|
||||
|
@ -2,42 +2,42 @@
|
||||
"instance": {
|
||||
"configuration": {
|
||||
"basedir": "/usr",
|
||||
"connect_timeout": "15",
|
||||
"connect_timeout": 15,
|
||||
"datadir": "/var/lib/mysql/data",
|
||||
"default_storage_engine": "innodb",
|
||||
"innodb_buffer_pool_size": "150M",
|
||||
"innodb_data_file_path": "ibdata1:10M:autoextend",
|
||||
"innodb_file_per_table": "1",
|
||||
"innodb_file_per_table": 1,
|
||||
"innodb_log_buffer_size": "25M",
|
||||
"innodb_log_file_size": "50M",
|
||||
"innodb_log_files_in_group": "2",
|
||||
"innodb_log_files_in_group": 2,
|
||||
"join_buffer_size": "1M",
|
||||
"key_buffer_size": "50M",
|
||||
"local-infile": "0",
|
||||
"local-infile": 0,
|
||||
"max_allowed_packet": "1024K",
|
||||
"max_connections": "100",
|
||||
"max_connections": 100,
|
||||
"max_heap_table_size": "16M",
|
||||
"max_user_connections": "100",
|
||||
"max_user_connections": 100,
|
||||
"myisam-recover-options": "BACKUP,FORCE",
|
||||
"open_files_limit": "512",
|
||||
"open_files_limit": 512,
|
||||
"pid_file": "/var/run/mysqld/mysqld.pid",
|
||||
"port": "3306",
|
||||
"port": 3306,
|
||||
"query_cache_limit": "1M",
|
||||
"query_cache_size": "8M",
|
||||
"query_cache_type": "1",
|
||||
"query_cache_type": 1,
|
||||
"read_buffer_size": "512K",
|
||||
"read_rnd_buffer_size": "512K",
|
||||
"server_id": "271898715",
|
||||
"skip-external-locking": "1",
|
||||
"server_id": 271898715,
|
||||
"skip-external-locking": 1,
|
||||
"sort_buffer_size": "1M",
|
||||
"table_definition_cache": "256",
|
||||
"table_open_cache": "256",
|
||||
"thread_cache_size": "4",
|
||||
"table_definition_cache": 256,
|
||||
"table_open_cache": 256,
|
||||
"thread_cache_size": 4,
|
||||
"thread_stack": "192K",
|
||||
"tmp_table_size": "16M",
|
||||
"tmpdir": "/var/tmp",
|
||||
"user": "mysql",
|
||||
"wait_timeout": "120",
|
||||
"wait_timeout": 120,
|
||||
"performance_schema": "ON"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
fixes:
|
||||
- Fix IniCodec to deserialize Python objects.
|
||||
This also brings it in line with other codecs.
|
||||
guestagent_utils.to_bytes return the byte values
|
||||
as ints.
|
||||
See bug 1599656
|
@ -179,8 +179,8 @@ class IniCodec(StreamCodec):
|
||||
...
|
||||
|
||||
The above file content would be represented as:
|
||||
{'section_1': {'key': 'value', 'key': 'value', ...},
|
||||
'section_2': {'key': 'value', 'key': 'value', ...}
|
||||
{'section_1': {'key': value, 'key': value, ...},
|
||||
'section_2': {'key': value, 'key': value, ...}
|
||||
...
|
||||
}
|
||||
"""
|
||||
@ -190,9 +190,8 @@ class IniCodec(StreamCodec):
|
||||
:param default_value: Default value for keys with no value.
|
||||
If set, all keys are written as 'key = value'.
|
||||
The key is written without trailing '=' if None.
|
||||
:type default_value: string
|
||||
:type default_value: object
|
||||
"""
|
||||
self._value_converter = StringConverter({default_value: None})
|
||||
self._default_value = default_value
|
||||
self._comment_markers = comment_markers
|
||||
|
||||
@ -207,7 +206,8 @@ class IniCodec(StreamCodec):
|
||||
parser = self._init_config_parser()
|
||||
parser.readfp(self._pre_parse(stream))
|
||||
|
||||
return {s: {k: self._value_converter.to_strings(v)
|
||||
return {s: {k:
|
||||
StringConverter({None: self._default_value}).to_objects(v)
|
||||
for k, v in parser.items(s, raw=True)}
|
||||
for s in parser.sections()}
|
||||
|
||||
@ -231,8 +231,11 @@ class IniCodec(StreamCodec):
|
||||
for section in sections:
|
||||
parser.add_section(section)
|
||||
for key, value in sections[section].items():
|
||||
str_val = StringConverter(
|
||||
{self._default_value: None}).to_strings(value)
|
||||
parser.set(section, key,
|
||||
self._value_converter.to_strings(value))
|
||||
str(str_val) if str_val is not None
|
||||
else str_val)
|
||||
|
||||
return parser
|
||||
|
||||
@ -383,6 +386,7 @@ class KeyValueCodec(PropertiesCodec):
|
||||
...
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, delimiter='=', comment_markers=('#'),
|
||||
unpack_singletons=True, string_mappings=None):
|
||||
super(KeyValueCodec, self).__init__(
|
||||
|
@ -117,6 +117,6 @@ def to_bytes(value):
|
||||
'G': 1024 ** 3,
|
||||
}[suffix]
|
||||
|
||||
return str(int(round(factor * float(value))))
|
||||
return int(round(factor * float(value)))
|
||||
|
||||
return value
|
||||
|
@ -24,6 +24,7 @@ from trove.tests.unittests import trove_testtools
|
||||
|
||||
|
||||
class TestConfigurationParser(trove_testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestConfigurationParser, self).setUp()
|
||||
|
||||
@ -42,11 +43,12 @@ class TestConfigurationParser(trove_testtools.TestCase):
|
||||
d_parsed = dict(parsed)
|
||||
self.assertIsNotNone(d_parsed)
|
||||
self.assertEqual("/var/run/mysqld/mysqld.pid", d_parsed["pid-file"])
|
||||
self.assertEqual('15', d_parsed["connect_timeout"])
|
||||
self.assertEqual(15, d_parsed["connect_timeout"])
|
||||
self.assertEqual('1', d_parsed["skip-external-locking"])
|
||||
|
||||
|
||||
class TestConfigurationController(trove_testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestConfigurationController, self).setUp()
|
||||
self.controller = ConfigurationsController()
|
||||
@ -179,6 +181,7 @@ class TestConfigurationController(trove_testtools.TestCase):
|
||||
|
||||
|
||||
class TestConfigurationsParameterController(trove_testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestConfigurationsParameterController, self).setUp()
|
||||
self.controller = service.ConfigurationsParameterController()
|
||||
|
@ -122,27 +122,27 @@ class TestConfigurationOverrideStrategy(trove_testtools.TestCase):
|
||||
'value': '1.4142'}},
|
||||
1,
|
||||
{'Section_1': {'name': 'sqrt(2)',
|
||||
'value': '1.4142'}}
|
||||
'value': 1.4142}}
|
||||
)
|
||||
|
||||
user_overrides_v2 = ('id2',
|
||||
{'Section_1': {'is_number': 'False'}},
|
||||
{'Section_1': {'is_number': False}},
|
||||
2,
|
||||
{'Section_1': {'is_number': 'False'}}
|
||||
{'Section_1': {'is_number': False}}
|
||||
)
|
||||
|
||||
system_overrides_v1 = ('id1',
|
||||
{'Section_1': {'name': 'e',
|
||||
'value': '2.7183'}},
|
||||
'value': 2.7183}},
|
||||
1,
|
||||
{'Section_1': {'name': 'e',
|
||||
'value': '2.7183'}}
|
||||
'value': 2.7183}}
|
||||
)
|
||||
|
||||
system_overrides_v2 = ('id2',
|
||||
{'Section_2': {'is_number': 'True'}},
|
||||
{'Section_2': {'is_number': True}},
|
||||
2,
|
||||
{'Section_2': {'is_number': 'True'}}
|
||||
{'Section_2': {'is_number': True}}
|
||||
)
|
||||
|
||||
self._test_import_override_strategy(
|
||||
@ -153,36 +153,36 @@ class TestConfigurationOverrideStrategy(trove_testtools.TestCase):
|
||||
# single file.
|
||||
user_overrides_v1 = ('id1',
|
||||
{'Section_1': {'name': 'sqrt(2)',
|
||||
'value': '1.4142'}},
|
||||
'value': 1.4142}},
|
||||
1,
|
||||
{'Section_1': {'name': 'sqrt(2)',
|
||||
'is_number': 'False',
|
||||
'value': '1.4142'}}
|
||||
'is_number': False,
|
||||
'value': 1.4142}}
|
||||
)
|
||||
|
||||
user_overrides_v2 = ('id1',
|
||||
{'Section_1': {'is_number': 'False'}},
|
||||
{'Section_1': {'is_number': False}},
|
||||
1,
|
||||
{'Section_1': {'name': 'sqrt(2)',
|
||||
'is_number': 'False',
|
||||
'value': '1.4142'}}
|
||||
'is_number': False,
|
||||
'value': 1.4142}}
|
||||
)
|
||||
|
||||
system_overrides_v1 = ('id1',
|
||||
{'Section_1': {'name': 'e',
|
||||
'value': '2.7183'}},
|
||||
'value': 2.7183}},
|
||||
1,
|
||||
{'Section_1': {'name': 'e',
|
||||
'value': '2.7183'},
|
||||
'Section_2': {'is_number': 'True'}}
|
||||
'value': 2.7183},
|
||||
'Section_2': {'is_number': True}}
|
||||
)
|
||||
|
||||
system_overrides_v2 = ('id1',
|
||||
{'Section_2': {'is_number': 'True'}},
|
||||
{'Section_2': {'is_number': True}},
|
||||
1,
|
||||
{'Section_1': {'name': 'e',
|
||||
'value': '2.7183'},
|
||||
'Section_2': {'is_number': 'True'}}
|
||||
'value': 2.7183},
|
||||
'Section_2': {'is_number': True}}
|
||||
)
|
||||
|
||||
self._test_import_override_strategy(
|
||||
@ -193,8 +193,8 @@ class TestConfigurationOverrideStrategy(trove_testtools.TestCase):
|
||||
def _test_import_override_strategy(
|
||||
self, system_overrides, user_overrides, test_multi_rev):
|
||||
base_config_contents = {'Section_1': {'name': 'pi',
|
||||
'is_number': 'True',
|
||||
'value': '3.1415'}
|
||||
'is_number': True,
|
||||
'value': 3.1415}
|
||||
}
|
||||
|
||||
codec = IniCodec()
|
||||
@ -361,21 +361,21 @@ class TestConfigurationOverrideStrategy(trove_testtools.TestCase):
|
||||
@patch.multiple(operating_system, chmod=Mock(), chown=Mock())
|
||||
def _assert_get_value(self, override_strategy):
|
||||
base_config_contents = {'Section_1': {'name': 'pi',
|
||||
'is_number': 'True',
|
||||
'value': '3.1415'}
|
||||
'is_number': True,
|
||||
'value': 3.1415}
|
||||
}
|
||||
|
||||
config_overrides_v1a = {'Section_1': {'name': 'sqrt(2)',
|
||||
'value': '1.4142'}
|
||||
'value': 1.4142}
|
||||
}
|
||||
|
||||
config_overrides_v2 = {'Section_1': {'name': 'e',
|
||||
'value': '2.7183'},
|
||||
'value': 2.7183},
|
||||
'Section_2': {'foo': 'bar'}
|
||||
}
|
||||
|
||||
config_overrides_v1b = {'Section_1': {'name': 'sqrt(4)',
|
||||
'value': '2.0'}
|
||||
'value': 2.0}
|
||||
}
|
||||
|
||||
codec = IniCodec()
|
||||
@ -397,22 +397,22 @@ class TestConfigurationOverrideStrategy(trove_testtools.TestCase):
|
||||
|
||||
# Test value before applying overrides.
|
||||
self.assertEqual('pi', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('3.1415', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(3.1415, manager.get_value('Section_1')['value'])
|
||||
|
||||
# Test value after applying overrides.
|
||||
manager.apply_user_override(config_overrides_v1a, change_id='id1')
|
||||
self.assertEqual('sqrt(2)', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('1.4142', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(1.4142, manager.get_value('Section_1')['value'])
|
||||
manager.apply_user_override(config_overrides_v2, change_id='id2')
|
||||
self.assertEqual('e', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('2.7183', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(2.7183, manager.get_value('Section_1')['value'])
|
||||
self.assertEqual('bar', manager.get_value('Section_2')['foo'])
|
||||
|
||||
# Editing change 'id1' become visible only after removing
|
||||
# change 'id2', which overrides 'id1'.
|
||||
manager.apply_user_override(config_overrides_v1b, change_id='id1')
|
||||
self.assertEqual('e', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('2.7183', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(2.7183, manager.get_value('Section_1')['value'])
|
||||
|
||||
# Test value after removing overrides.
|
||||
|
||||
@ -420,35 +420,35 @@ class TestConfigurationOverrideStrategy(trove_testtools.TestCase):
|
||||
# removing 'id2'.
|
||||
manager.remove_user_override(change_id='id2')
|
||||
self.assertEqual('sqrt(4)', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('2.0', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(2.0, manager.get_value('Section_1')['value'])
|
||||
|
||||
# Back to the base.
|
||||
manager.remove_user_override(change_id='id1')
|
||||
self.assertEqual('pi', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('3.1415', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(3.1415, manager.get_value('Section_1')['value'])
|
||||
self.assertIsNone(manager.get_value('Section_2'))
|
||||
|
||||
# Test system overrides.
|
||||
manager.apply_system_override(
|
||||
config_overrides_v1b, change_id='id1')
|
||||
self.assertEqual('sqrt(4)', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('2.0', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(2.0, manager.get_value('Section_1')['value'])
|
||||
|
||||
# The system values should take precedence over the user
|
||||
# override.
|
||||
manager.apply_user_override(
|
||||
config_overrides_v1a, change_id='id1')
|
||||
self.assertEqual('sqrt(4)', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('2.0', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(2.0, manager.get_value('Section_1')['value'])
|
||||
|
||||
# The user values should become visible only after removing the
|
||||
# system change.
|
||||
manager.remove_system_override(change_id='id1')
|
||||
self.assertEqual('sqrt(2)', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('1.4142', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(1.4142, manager.get_value('Section_1')['value'])
|
||||
|
||||
# Back to the base.
|
||||
manager.remove_user_override(change_id='id1')
|
||||
self.assertEqual('pi', manager.get_value('Section_1')['name'])
|
||||
self.assertEqual('3.1415', manager.get_value('Section_1')['value'])
|
||||
self.assertEqual(3.1415, manager.get_value('Section_1')['value'])
|
||||
self.assertIsNone(manager.get_value('Section_2'))
|
||||
|
@ -137,9 +137,9 @@ class TestGuestagentUtils(trove_testtools.TestCase):
|
||||
|
||||
def test_to_bytes(self):
|
||||
self.assertEqual('1024', guestagent_utils.to_bytes('1024'))
|
||||
self.assertEqual('1048576', guestagent_utils.to_bytes('1024K'))
|
||||
self.assertEqual('1073741824', guestagent_utils.to_bytes('1024M'))
|
||||
self.assertEqual('1099511627776', guestagent_utils.to_bytes('1024G'))
|
||||
self.assertEqual(1048576, guestagent_utils.to_bytes('1024K'))
|
||||
self.assertEqual(1073741824, guestagent_utils.to_bytes('1024M'))
|
||||
self.assertEqual(1099511627776, guestagent_utils.to_bytes('1024G'))
|
||||
self.assertEqual('1024T', guestagent_utils.to_bytes('1024T'))
|
||||
self.assertEqual(1024, guestagent_utils.to_bytes(1024))
|
||||
self.assertEqual('Hello!', guestagent_utils.to_bytes('Hello!'))
|
||||
|
@ -55,32 +55,23 @@ class TestOperatingSystem(trove_testtools.TestCase):
|
||||
|
||||
def test_ini_file_codec(self):
|
||||
data_no_none = {"Section1": {"s1k1": 's1v1',
|
||||
"s1k2": '3.1415926535'},
|
||||
"Section2": {"s2k1": '1',
|
||||
"s2k2": 'True'}}
|
||||
"s1k2": 3.1415926535},
|
||||
"Section2": {"s2k1": 1,
|
||||
"s2k2": True}}
|
||||
|
||||
self._test_file_codec(data_no_none, IniCodec())
|
||||
|
||||
data_with_none = {"Section1": {"s1k1": 's1v1',
|
||||
"s1k2": '3.1415926535'},
|
||||
"Section2": {"s2k1": '1',
|
||||
"s2k2": 'True',
|
||||
"s1k2": 3.1415926535},
|
||||
"Section2": {"s2k1": 1,
|
||||
"s2k2": True,
|
||||
"s2k3": None}}
|
||||
|
||||
# Keys with None values will be written without value.
|
||||
self._test_file_codec(data_with_none, IniCodec())
|
||||
|
||||
# Non-string values will be converted to strings.
|
||||
data_with_none_as_objects = {"Section1": {"s1k1": 's1v1',
|
||||
"s1k2": 3.1415926535},
|
||||
"Section2": {"s2k1": 1,
|
||||
"s2k2": True,
|
||||
"s2k3": None}}
|
||||
self._test_file_codec(data_with_none_as_objects, IniCodec(),
|
||||
expected_data=data_with_none)
|
||||
|
||||
# None will be replaced with 'default_value'.
|
||||
default_value = '1'
|
||||
default_value = 1
|
||||
expected_data = guestagent_utils.update_dict(
|
||||
{"Section2": {"s2k3": default_value}}, dict(data_with_none))
|
||||
self._test_file_codec(data_with_none,
|
||||
@ -92,7 +83,11 @@ class TestOperatingSystem(trove_testtools.TestCase):
|
||||
"Section2": {"s2k1": '1',
|
||||
"s2k2": 'True'},
|
||||
"Section3": {"Section4": {"s4k1": '3.1415926535',
|
||||
"s4k2": None}}
|
||||
"s4k2": None}},
|
||||
"Section5": {"s5k1": 1,
|
||||
"s5k2": True},
|
||||
"Section6": {"Section7": {"s7k1": 3.1415926535,
|
||||
"s7k2": None}}
|
||||
}
|
||||
|
||||
self._test_file_codec(data, YamlCodec())
|
||||
@ -128,7 +123,11 @@ class TestOperatingSystem(trove_testtools.TestCase):
|
||||
"Section2": {"s2k1": '1',
|
||||
"s2k2": 'True'},
|
||||
"Section3": {"Section4": {"s4k1": '3.1415926535',
|
||||
"s4k2": None}}
|
||||
"s4k2": None}},
|
||||
"Section5": {"s5k1": 1,
|
||||
"s5k2": True},
|
||||
"Section6": {"Section7": {"s7k1": 3.1415926535,
|
||||
"s7k2": None}}
|
||||
}
|
||||
|
||||
self._test_file_codec(data, JsonCodec())
|
||||
|
Loading…
Reference in New Issue
Block a user