384 lines
17 KiB
Python
384 lines
17 KiB
Python
import collections
|
|
import copy
|
|
import json
|
|
import mock
|
|
import six
|
|
import unittest
|
|
|
|
from charmhelpers.contrib.openstack import utils
|
|
|
|
if not six.PY3:
|
|
builtin_open = '__builtin__.open'
|
|
else:
|
|
builtin_open = 'builtins.open'
|
|
|
|
|
|
class UtilsTests(unittest.TestCase):
|
|
def setUp(self):
|
|
super(UtilsTests, self).setUp()
|
|
|
|
def test_compare_openstack_comparator(self):
|
|
self.assertTrue(utils.CompareOpenStackReleases('mitaka') < 'newton')
|
|
self.assertTrue(utils.CompareOpenStackReleases('pike') > 'essex')
|
|
|
|
@mock.patch.object(utils, 'config')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_set')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_ids')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.get_ipv6_addr')
|
|
def test_sync_db_with_multi_ipv6_addresses(self, mock_get_ipv6_addr,
|
|
mock_relation_ids,
|
|
mock_relation_set,
|
|
mock_config):
|
|
mock_config.return_value = None
|
|
addr1 = '2001:db8:1:0:f816:3eff:fe45:7c/64'
|
|
addr2 = '2001:db8:1:0:d0cf:528c:23eb:5000/64'
|
|
mock_get_ipv6_addr.return_value = [addr1, addr2]
|
|
mock_relation_ids.return_value = ['shared-db']
|
|
|
|
utils.sync_db_with_multi_ipv6_addresses('testdb', 'testdbuser')
|
|
hosts = json.dumps([addr1, addr2])
|
|
mock_relation_set.assert_called_with(relation_id='shared-db',
|
|
database='testdb',
|
|
username='testdbuser',
|
|
hostname=hosts)
|
|
|
|
@mock.patch.object(utils, 'config')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_set')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_ids')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.get_ipv6_addr')
|
|
def test_sync_db_with_multi_ipv6_addresses_single(self, mock_get_ipv6_addr,
|
|
mock_relation_ids,
|
|
mock_relation_set,
|
|
mock_config):
|
|
mock_config.return_value = None
|
|
addr1 = '2001:db8:1:0:f816:3eff:fe45:7c/64'
|
|
mock_get_ipv6_addr.return_value = [addr1]
|
|
mock_relation_ids.return_value = ['shared-db']
|
|
|
|
utils.sync_db_with_multi_ipv6_addresses('testdb', 'testdbuser')
|
|
hosts = json.dumps([addr1])
|
|
mock_relation_set.assert_called_with(relation_id='shared-db',
|
|
database='testdb',
|
|
username='testdbuser',
|
|
hostname=hosts)
|
|
|
|
@mock.patch.object(utils, 'config')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_set')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_ids')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.get_ipv6_addr')
|
|
def test_sync_db_with_multi_ipv6_addresses_w_prefix(self,
|
|
mock_get_ipv6_addr,
|
|
mock_relation_ids,
|
|
mock_relation_set,
|
|
mock_config):
|
|
mock_config.return_value = None
|
|
addr1 = '2001:db8:1:0:f816:3eff:fe45:7c/64'
|
|
mock_get_ipv6_addr.return_value = [addr1]
|
|
mock_relation_ids.return_value = ['shared-db']
|
|
|
|
utils.sync_db_with_multi_ipv6_addresses('testdb', 'testdbuser',
|
|
relation_prefix='bungabunga')
|
|
hosts = json.dumps([addr1])
|
|
mock_relation_set.assert_called_with(relation_id='shared-db',
|
|
bungabunga_database='testdb',
|
|
bungabunga_username='testdbuser',
|
|
bungabunga_hostname=hosts)
|
|
|
|
@mock.patch.object(utils, 'config')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_set')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_ids')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.get_ipv6_addr')
|
|
def test_sync_db_with_multi_ipv6_addresses_vips(self, mock_get_ipv6_addr,
|
|
mock_relation_ids,
|
|
mock_relation_set,
|
|
mock_config):
|
|
addr1 = '2001:db8:1:0:f816:3eff:fe45:7c/64'
|
|
addr2 = '2001:db8:1:0:d0cf:528c:23eb:5000/64'
|
|
vip1 = '2001:db8:1:0:f816:3eff:32b3:7c'
|
|
vip2 = '2001:db8:1:0:f816:3eff:32b3:7d'
|
|
mock_config.return_value = '%s 10.0.0.1 %s' % (vip1, vip2)
|
|
|
|
mock_get_ipv6_addr.return_value = [addr1, addr2]
|
|
mock_relation_ids.return_value = ['shared-db']
|
|
|
|
utils.sync_db_with_multi_ipv6_addresses('testdb', 'testdbuser')
|
|
hosts = json.dumps([addr1, addr2, vip1, vip2])
|
|
mock_relation_set.assert_called_with(relation_id='shared-db',
|
|
database='testdb',
|
|
username='testdbuser',
|
|
hostname=hosts)
|
|
|
|
@mock.patch('uuid.uuid4')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.related_units')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_set')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.relation_ids')
|
|
def test_remote_restart(self, mock_relation_ids, mock_relation_set,
|
|
mock_related_units, mock_uuid4):
|
|
mock_relation_ids.return_value = ['neutron-plugin-api-subordinate:8']
|
|
mock_related_units.return_value = ['neutron-api/0']
|
|
mock_uuid4.return_value = 'uuid4'
|
|
utils.remote_restart('neutron-plugin-api-subordinate')
|
|
mock_relation_set.assert_called_with(
|
|
relation_id='neutron-plugin-api-subordinate:8',
|
|
relation_settings={'restart-trigger': 'uuid4'}
|
|
)
|
|
|
|
@mock.patch.object(utils, 'lsb_release')
|
|
@mock.patch.object(utils, 'config')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.get_os_codename_package')
|
|
@mock.patch('charmhelpers.contrib.openstack.utils.'
|
|
'get_os_codename_install_source')
|
|
def test_os_release(self, mock_get_os_codename_install_source,
|
|
mock_get_os_codename_package,
|
|
mock_config, mock_lsb_release):
|
|
# Wipe the modules cached os_rel
|
|
utils._os_rel = None
|
|
mock_lsb_release.return_value = {"DISTRIB_CODENAME": "trusty"}
|
|
mock_get_os_codename_install_source.return_value = None
|
|
mock_get_os_codename_package.return_value = None
|
|
mock_config.return_value = 'cloud-pocket'
|
|
self.assertEqual(utils.os_release('my-pkg'), 'icehouse')
|
|
mock_get_os_codename_install_source.assert_called_once_with(
|
|
'cloud-pocket')
|
|
mock_get_os_codename_package.assert_called_once_with(
|
|
'my-pkg', fatal=False)
|
|
mock_config.assert_called_once_with('openstack-origin')
|
|
# Next call to os_release should pickup cached version
|
|
mock_get_os_codename_install_source.reset_mock()
|
|
mock_get_os_codename_package.reset_mock()
|
|
self.assertEqual(utils.os_release('my-pkg'), 'icehouse')
|
|
self.assertFalse(mock_get_os_codename_install_source.called)
|
|
self.assertFalse(mock_get_os_codename_package.called)
|
|
# Call os_release and bypass cache
|
|
mock_lsb_release.return_value = {"DISTRIB_CODENAME": "xenial"}
|
|
mock_get_os_codename_install_source.reset_mock()
|
|
mock_get_os_codename_package.reset_mock()
|
|
self.assertEqual(utils.os_release('my-pkg', reset_cache=True),
|
|
'mitaka')
|
|
mock_get_os_codename_install_source.assert_called_once_with(
|
|
'cloud-pocket')
|
|
mock_get_os_codename_package.assert_called_once_with(
|
|
'my-pkg', fatal=False)
|
|
# Override base
|
|
mock_lsb_release.return_value = {"DISTRIB_CODENAME": "xenial"}
|
|
mock_get_os_codename_install_source.reset_mock()
|
|
mock_get_os_codename_package.reset_mock()
|
|
self.assertEqual(utils.os_release('my-pkg', reset_cache=True, base="ocata"),
|
|
'ocata')
|
|
mock_get_os_codename_install_source.assert_called_once_with(
|
|
'cloud-pocket')
|
|
mock_get_os_codename_package.assert_called_once_with(
|
|
'my-pkg', fatal=False)
|
|
# Override source key
|
|
mock_config.reset_mock()
|
|
mock_get_os_codename_install_source.reset_mock()
|
|
mock_get_os_codename_package.reset_mock()
|
|
mock_get_os_codename_package.return_value = None
|
|
utils.os_release('my-pkg', reset_cache=True, source_key='source')
|
|
mock_config.assert_called_once_with('source')
|
|
mock_get_os_codename_install_source.assert_called_once_with(
|
|
'cloud-pocket')
|
|
|
|
@mock.patch.object(utils, 'os_release')
|
|
@mock.patch.object(utils, 'get_os_codename_install_source')
|
|
def test_enable_memcache(self, _get_os_codename_install_source,
|
|
_os_release):
|
|
# Check call with 'release'
|
|
self.assertFalse(utils.enable_memcache(release='icehouse'))
|
|
self.assertTrue(utils.enable_memcache(release='ocata'))
|
|
# Check call with 'source'
|
|
_os_release.return_value = None
|
|
_get_os_codename_install_source.return_value = 'icehouse'
|
|
self.assertFalse(utils.enable_memcache(source='distro'))
|
|
_os_release.return_value = None
|
|
_get_os_codename_install_source.return_value = 'ocata'
|
|
self.assertTrue(utils.enable_memcache(source='distro'))
|
|
# Check call with 'package'
|
|
_os_release.return_value = 'icehouse'
|
|
_get_os_codename_install_source.return_value = None
|
|
self.assertFalse(utils.enable_memcache(package='pkg1'))
|
|
_os_release.return_value = 'ocata'
|
|
_get_os_codename_install_source.return_value = None
|
|
self.assertTrue(utils.enable_memcache(package='pkg1'))
|
|
|
|
@mock.patch.object(utils, 'enable_memcache')
|
|
def test_enable_token_cache_pkgs(self, _enable_memcache):
|
|
_enable_memcache.return_value = False
|
|
self.assertEqual(utils.token_cache_pkgs(source='distro'), [])
|
|
_enable_memcache.return_value = True
|
|
self.assertEqual(utils.token_cache_pkgs(source='distro'),
|
|
['memcached', 'python-memcache'])
|
|
|
|
def test_update_json_file(self):
|
|
TEST_POLICY = """{
|
|
"delete_image_location": "",
|
|
"get_image_location": "",
|
|
"set_image_location": "",
|
|
"extra_property": "False"
|
|
}"""
|
|
|
|
TEST_POLICY_FILE = "/etc/glance/policy.json"
|
|
|
|
items_to_update = {
|
|
"get_image_location": "role:admin",
|
|
"extra_policy": "extra",
|
|
}
|
|
|
|
mock_open = mock.mock_open(read_data=TEST_POLICY)
|
|
with mock.patch(builtin_open, mock_open) as mock_file:
|
|
utils.update_json_file(TEST_POLICY_FILE, {})
|
|
self.assertFalse(mock_file.called)
|
|
|
|
utils.update_json_file(TEST_POLICY_FILE, items_to_update)
|
|
mock_file.assert_has_calls([
|
|
mock.call(TEST_POLICY_FILE),
|
|
mock.call(TEST_POLICY_FILE, 'w'),
|
|
], any_order=True)
|
|
|
|
modified_policy = json.loads(TEST_POLICY)
|
|
modified_policy.update(items_to_update)
|
|
mock_open().write.assert_called_with(
|
|
json.dumps(modified_policy, indent=4, sort_keys=True))
|
|
|
|
tmp = json.loads(TEST_POLICY)
|
|
tmp.update(items_to_update)
|
|
TEST_POLICY = json.dumps(tmp)
|
|
mock_open = mock.mock_open(read_data=TEST_POLICY)
|
|
with mock.patch(builtin_open, mock_open) as mock_file:
|
|
utils.update_json_file(TEST_POLICY_FILE, items_to_update)
|
|
mock_file.assert_has_calls([
|
|
mock.call(TEST_POLICY_FILE),
|
|
], any_order=True)
|
|
|
|
def test_ordered(self):
|
|
data = {'one': 1, 'two': 2, 'three': 3}
|
|
expected = [('one', 1), ('three', 3), ('two', 2)]
|
|
self.assertSequenceEqual(expected,
|
|
[x for x in utils.ordered(data).items()])
|
|
|
|
data = {
|
|
'one': 1,
|
|
'two': 2,
|
|
'three': {
|
|
'uno': 1,
|
|
'dos': 2,
|
|
'tres': 3
|
|
}
|
|
}
|
|
expected = collections.OrderedDict()
|
|
expected['one'] = 1
|
|
nested = collections.OrderedDict()
|
|
nested['dos'] = 2
|
|
nested['tres'] = 3
|
|
nested['uno'] = 1
|
|
expected['three'] = nested
|
|
expected['two'] = 2
|
|
self.assertEqual(expected, utils.ordered(data))
|
|
|
|
self.assertRaises(ValueError, utils.ordered, "foo")
|
|
|
|
def test_sequence_status_check_functions(self):
|
|
# all messages are reported and the highest priority status "wins"
|
|
f1 = mock.Mock(return_value=('blocked', 'status 1'))
|
|
f2 = mock.Mock(return_value=('', 'status 2'))
|
|
f3 = mock.Mock(return_value=('maintenance', 'status 3'))
|
|
f = utils.sequence_status_check_functions(f1, f2, f3)
|
|
expected = ('blocked', 'status 1, status 2, status 3')
|
|
result = f(mock.Mock())
|
|
self.assertEquals(result, expected)
|
|
# empty status must be replaced by "unknown"
|
|
f4 = mock.Mock(return_value=('', 'status 4'))
|
|
f5 = mock.Mock(return_value=('', 'status 5'))
|
|
f = utils.sequence_status_check_functions(f4, f5)
|
|
expected = ('unknown', 'status 4, status 5')
|
|
result = f(mock.Mock())
|
|
self.assertEquals(result, expected)
|
|
# sequencing 0 status checks must return state 'unknown', ''
|
|
f = utils.sequence_status_check_functions()
|
|
expected = ('unknown', '')
|
|
result = f(mock.Mock())
|
|
self.assertEquals(result, expected)
|
|
|
|
@mock.patch.object(utils, 'relation_get')
|
|
@mock.patch.object(utils, 'related_units')
|
|
@mock.patch.object(utils, 'relation_ids')
|
|
@mock.patch.object(utils, 'container_scoped_relations')
|
|
def test_container_scoped_relation_get(
|
|
self,
|
|
mock_container_scoped_relations,
|
|
mock_relation_ids,
|
|
mock_related_units,
|
|
mock_relation_get):
|
|
mock_container_scoped_relations.return_value = [
|
|
'relation1', 'relation2']
|
|
mock_relation_ids.return_value = ['rid']
|
|
mock_related_units.return_value = ['unit']
|
|
|
|
for rdata in utils.container_scoped_relation_get():
|
|
pass
|
|
mock_relation_ids.assert_has_calls([
|
|
mock.call('relation1'),
|
|
mock.call('relation2')])
|
|
mock_relation_get.assert_has_calls([
|
|
mock.call(attribute=None, unit='unit', rid='rid'),
|
|
mock.call(attribute=None, unit='unit', rid='rid')])
|
|
|
|
mock_relation_get.reset_mock()
|
|
for rdata in utils.container_scoped_relation_get(attribute='attr'):
|
|
pass
|
|
mock_relation_get.assert_has_calls([
|
|
mock.call(attribute='attr', unit='unit', rid='rid'),
|
|
mock.call(attribute='attr', unit='unit', rid='rid')])
|
|
|
|
@mock.patch.object(utils, 'container_scoped_relation_get')
|
|
def test_get_subordinate_release_packages(
|
|
self,
|
|
mock_container_scoped_relation_get):
|
|
rdata = {
|
|
'queens': {'snap': {'install': ['q_inst'], 'purge': ['q_purg']}},
|
|
'stein': {'deb': {'install': ['s_inst'], 'purge': ['s_purg']}}}
|
|
mock_container_scoped_relation_get.return_value = [
|
|
json.dumps(rdata),
|
|
json.dumps(rdata),
|
|
]
|
|
# None of the subordinate relations have information about rocky or
|
|
# earlier for deb installations
|
|
self.assertEquals(
|
|
utils.get_subordinate_release_packages('rocky'),
|
|
utils.SubordinatePackages(set(), set()))
|
|
# Information on most recent earlier release with matching package
|
|
# type will be provided when requesting a release not specifically
|
|
# provided by subordinates
|
|
self.assertEquals(
|
|
utils.get_subordinate_release_packages(
|
|
'rocky', package_type='snap'),
|
|
utils.SubordinatePackages(
|
|
{'q_inst'}, {'q_purg'}))
|
|
self.assertEquals(
|
|
utils.get_subordinate_release_packages('train'),
|
|
utils.SubordinatePackages(
|
|
{'s_inst'}, {'s_purg'}))
|
|
# Confirm operation when each subordinate has different release package
|
|
# information
|
|
rdata2 = copy.deepcopy(rdata)
|
|
rdata2.update({
|
|
'train': {'deb': {'install': ['t_inst'], 'purge': ['t_purg']}}})
|
|
mock_container_scoped_relation_get.return_value = [
|
|
json.dumps(rdata),
|
|
json.dumps(rdata2),
|
|
]
|
|
self.assertEquals(
|
|
utils.get_subordinate_release_packages('train'),
|
|
utils.SubordinatePackages(
|
|
{'s_inst', 't_inst'}, {'s_purg', 't_purg'}))
|
|
# Confirm operation when one of the subordinate relations does not
|
|
# implement sharing the package information
|
|
mock_container_scoped_relation_get.return_value = [
|
|
json.dumps(rdata),
|
|
None,
|
|
]
|
|
self.assertEquals(
|
|
utils.get_subordinate_release_packages('train'),
|
|
utils.SubordinatePackages(
|
|
{'s_inst'}, {'s_purg'}))
|