Add unit tests and fix for _parse bug exposed

* Add unit tests for common.py and requires.py.
* Update tox env for above.
* Update gitignore to ignore common kruft.
* CRM._parse did not add a space when constructing 'results' after
  each argument of 'data'. This caused each element to run into the
  last.

Change-Id: I2c35820149618aae02171c89b26bf29ee5e22344
This commit is contained in:
Liam Young 2018-12-17 15:52:18 +00:00
parent ab38cf8b07
commit ce2aa63653
8 changed files with 797 additions and 22 deletions

4
.gitignore vendored
View File

@ -1,2 +1,6 @@
.tox
.testrepository
.stestr
.unit-state.db
**/__pycache__/

8
.testr.conf Normal file
View File

@ -0,0 +1,8 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ ./unit_tests $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -139,8 +139,8 @@ class CRM(dict):
if first:
results = results + ' '
first = False
results = results + ('%s %s' % (prefix, d))
results = results + ('%s %s ' % (prefix, d))
results = results.rstrip()
return results
def clone(self, name, resource, description=None, **kwargs):
@ -275,7 +275,7 @@ class CRM(dict):
"""
specs = ' '.join(resources)
if 'description' in kwargs:
specs = specs + (' description="' % kwargs['description'])
specs = specs + (' description=%s"' % kwargs['description'])
for key in 'meta', 'params':
if key not in kwargs:

View File

@ -1,2 +1,7 @@
flake8>=2.2.4,<=2.4.1
# Lint and unit test requirements
flake8
os-testr>=0.4.1
charms.reactive
mock>=1.2
coverage>=3.6
netifaces

23
tox.ini
View File

@ -1,6 +1,7 @@
[tox]
envlist = pep8,py27
envlist = pep8,py35
skipsdist = True
skip_missing_interpreters = True
[testenv]
setenv = VIRTUAL_ENV={envdir}
@ -9,29 +10,15 @@ install_command =
pip install {opts} {packages}
commands = ostestr {posargs}
[testenv:py27]
basepython = python2.7
deps = -r{toxinidir}/test-requirements.txt
# TODO: Need to write unit tests then remove the following command.
commands = /bin/true
[testenv:py34]
basepython = python3.4
deps = -r{toxinidir}/test-requirements.txt
# TODO: Need to write unit tests then remove the following command.
commands = /bin/true
[testenv:py35]
basepython = python3.5
deps = -r{toxinidir}/test-requirements.txt
# TODO: Need to write unit tests then remove the following command.
commands = /bin/true
commands = ostestr {posargs}
[testenv:py36]
basepython = python3.6
deps = -r{toxinidir}/test-requirements.txt
# TODO: Need to write unit tests then remove the following command.
commands = /bin/true
commands = ostestr {posargs}
[testenv:pep8]
basepython = python3
@ -43,4 +30,4 @@ basepython = python3
commands = {posargs}
[flake8]
ignore = E402,E226
ignore = E402,E226,W504

0
unit_tests/__init__.py Normal file
View File

357
unit_tests/test_common.py Normal file
View File

@ -0,0 +1,357 @@
# 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 mock
import unittest
import common
class TestHAClusterCommonCRM(unittest.TestCase):
def test_init(self):
crm = common.CRM()
expect = {
'resources': {},
'delete_resources': [],
'resource_params': {},
'groups': {},
'ms': {},
'orders': {},
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
self.assertEqual(
crm,
expect)
expect['resources'] = {'res1': 'res1'}
self.assertEqual(
common.CRM(resources={'res1': 'res1'}),
expect)
self.assertEqual(
common.CRM({'resources': {'res1': 'res1'}}),
expect)
def test_primitive(self):
crm = common.CRM()
crm.primitive('www8', 'apache',
params='configfile=/etc/apache/www8.conf',
operations='$id-ref=apache_ops')
self.assertEqual(
crm['resources']['www8'],
'apache')
self.assertEqual(
crm['resource_params']['www8'],
(' params configfile=/etc/apache/www8.conf '
'operations $id-ref=apache_ops'))
def test_primitive_description(self):
crm = common.CRM()
crm.primitive('www8', 'apache',
description='super awesome',
params='configfile=/etc/apache/www8.conf',
operations='$id-ref=apache_ops')
self.assertEqual(
crm['resources']['www8'],
'apache')
self.assertEqual(
crm['resource_params']['www8'],
('description="super awesome"'
' params configfile=/etc/apache/www8.conf '
'operations $id-ref=apache_ops'))
def test_primitive_multiops(self):
crm = common.CRM()
ops = ['monitor role=Master interval=60s',
'monitor role=Slave interval=300s']
crm.primitive('r0', 'ocf:linbit:drbd',
params='drbd_resource=r0',
op=ops)
self.assertEqual(
crm['resources']['r0'],
'ocf:linbit:drbd')
self.assertEqual(
crm['resource_params']['r0'],
(' params drbd_resource=r0 op monitor role=Master '
'interval=60s op monitor role=Slave interval=300s'))
def test__parse(self):
crm = common.CRM()
self.assertEqual(
crm._parse('prefix', 'var1'),
' prefix var1')
self.assertEqual(
crm._parse('prefix', ['var1']),
' prefix var1')
self.assertEqual(
crm._parse('prefix', ['var1', 'var2']),
' prefix var1 prefix var2')
def test_clone(self):
crm = common.CRM()
crm.clone(
'cl_nova_haproxy',
'res_neutron_haproxy',
description='FE Haproxy')
self.assertEqual(
crm['clones']['cl_nova_haproxy'],
'res_neutron_haproxy description="FE Haproxy"')
def test_clone_meta(self):
crm = common.CRM()
crm.clone(
'cl_nova_haproxy',
'res_neutron_haproxy',
description='FE Haproxy',
meta='clone-node-max=1')
self.assertEqual(
crm['clones']['cl_nova_haproxy'],
('res_neutron_haproxy description="FE Haproxy" '
'meta clone-node-max=1'))
def test_colocation(self):
crm = common.CRM()
crm.colocation('console_with_vip', 'ALWAYS', 'nova-console', 'vip')
self.assertEqual(
crm['colocations']['console_with_vip'],
'ALWAYS: nova-console vip')
def test_colocation_node_attr(self):
crm = common.CRM()
crm.colocation(
'console_with_vip',
'ALWAYS',
'nova-console',
'vip',
node_attribute='attr1')
self.assertEqual(
crm['colocations']['console_with_vip'],
'ALWAYS: nova-console vip node-attribute=attr1')
def test_group(self):
crm = common.CRM()
crm.group('grp_mysql', 'res_mysql_rbd', 'res_mysql_fs',
'res_mysql_vip', 'res_mysqld')
self.assertEqual(
crm['groups']['grp_mysql'],
'res_mysql_rbd res_mysql_fs res_mysql_vip res_mysqld')
def test_group_meta(self):
crm = common.CRM()
crm.group('grp_mysql', 'res_mysql_rbd', 'res_mysql_fs',
'res_mysql_vip', 'res_mysqld', meta='container="vm"')
self.assertEqual(
crm['groups']['grp_mysql'],
('res_mysql_rbd res_mysql_fs res_mysql_vip res_mysqld '
'meta container="vm"'))
def test_group_meta_and_params(self):
crm = common.CRM()
crm.group('grp_mysql', 'res_mysql_rbd', 'res_mysql_fs',
'res_mysql_vip', 'res_mysqld', meta='container="vm"',
params='config=/etc/mysql/db0.conf')
self.assertEqual(
crm['groups']['grp_mysql'],
('res_mysql_rbd res_mysql_fs res_mysql_vip res_mysqld '
'meta container="vm" '
'params config=/etc/mysql/db0.conf'))
def test_group_desc(self):
crm = common.CRM()
crm.group('grp_mysql', 'res_mysql_rbd', 'res_mysql_fs',
'res_mysql_vip', 'res_mysqld', description='useful desc')
self.assertEqual(
crm['groups']['grp_mysql'],
('res_mysql_rbd res_mysql_fs res_mysql_vip res_mysqld '
'description=useful desc"'))
def test_delete_resource(self):
crm = common.CRM()
crm.delete_resource('res_mysql_vip')
self.assertEqual(
crm['delete_resources'],
('res_mysql_vip',))
def test_delete_resource_multi(self):
crm = common.CRM()
crm.delete_resource('res_mysql_vip', 'grp_mysql')
self.assertEqual(
crm['delete_resources'],
('res_mysql_vip', 'grp_mysql'))
def test_init_services(self):
crm = common.CRM()
crm.init_services('haproxy')
self.assertEqual(
crm['init_services'],
('haproxy',))
def test_init_services_multi(self):
crm = common.CRM()
crm.init_services('haproxy', 'apache2')
self.assertEqual(
crm['init_services'],
('haproxy', 'apache2'))
def test_ms_meta(self):
crm = common.CRM()
crm.ms('disk1', 'drbd1', meta='notify=true globally-unique=false')
self.assertEqual(
crm['ms']['disk1'],
'drbd1 meta notify=true globally-unique=false')
def test_ms_meta_and_params(self):
crm = common.CRM()
crm.ms('disk1', 'drbd1',
meta='notify=true globally-unique=false',
params='config=/etc/mysql/db0.conf')
self.assertEqual(
crm['ms']['disk1'],
'drbd1 meta notify=true globally-unique=false '
'params config=/etc/mysql/db0.conf')
def test_ms_desc(self):
crm = common.CRM()
crm.ms('disk1', 'drbd1', description='useful desc')
self.assertEqual(
crm['ms']['disk1'],
'drbd1 description="useful desc"')
# The method signature of 'order' seems broken. Leaving out unit tests for
# it as they would just confirm broken behaviour.
def test_add(self):
crm = common.CRM()
mock1 = mock.MagicMock()
mock2 = mock.MagicMock()
mock1.configure_resource = mock2
crm.add(mock1)
mock2.assert_called_once_with(crm)
class TestHAClusterCommonInitService(unittest.TestCase):
def test_init(self):
init_svc = common.InitService('apache', 'apache2')
self.assertEqual(
init_svc.service_name,
'apache')
self.assertEqual(
init_svc.init_service_name,
'apache2')
self.assertTrue(init_svc.clone)
def test_init_no_clone(self):
init_svc = common.InitService('apache', 'apache2', clone=False)
self.assertFalse(init_svc.clone)
def test_configure_resource(self):
crm = common.CRM()
init_svc = common.InitService('apache', 'apache2')
init_svc.configure_resource(crm)
self.assertEqual(
crm['resources']['res_apache_apache2'],
'lsb:apache2')
self.assertEqual(
crm['resource_params']['res_apache_apache2'],
(' op monitor interval="5s"'))
self.assertEqual(crm['init_services'], ('apache2',))
self.assertEqual(
crm['clones']['cl_res_apache_apache2'],
'res_apache_apache2')
def test_configure_resource_no_clone(self):
crm = common.CRM()
init_svc = common.InitService('apache', 'apache2', clone=False)
init_svc.configure_resource(crm)
self.assertEqual(
crm['resources']['res_apache_apache2'],
'lsb:apache2')
self.assertEqual(
crm['resource_params']['res_apache_apache2'],
(' op monitor interval="5s"'))
self.assertEqual(crm['init_services'], ('apache2',))
self.assertFalse(crm['clones'].get('cl_res_apache_apache2'))
class TestHAClusterCommonVirtualIP(unittest.TestCase):
def test_init(self):
vip_svc = common.VirtualIP('apache', '10.110.1.1')
self.assertEqual(vip_svc.service_name, 'apache')
self.assertEqual(vip_svc.vip, '10.110.1.1')
self.assertIsNone(vip_svc.nic)
self.assertIsNone(vip_svc.cidr)
def test_init_no_default(self):
vip_svc = common.VirtualIP('apache', '10.110.1.1', 'eth1', '24')
self.assertEqual(vip_svc.service_name, 'apache')
self.assertEqual(vip_svc.vip, '10.110.1.1')
self.assertEqual(vip_svc.nic, 'eth1')
self.assertEqual(vip_svc.cidr, '24')
def test_configure_resource(self):
crm = common.CRM()
vip_svc = common.VirtualIP('apache', '10.110.1.1', 'eth1', '24')
vip_svc.configure_resource(crm)
self.assertEqual(
crm['resources']['res_apache_eth1_vip'],
'ocf:heartbeat:IPaddr2')
self.assertEqual(
crm['resource_params']['res_apache_eth1_vip'],
(' params ip="10.110.1.1" nic="eth1" cidr_netmask="24" '
'op monitor depth="0" timeout="20s" interval="10s"'))
def test_configure_resource_no_nic(self):
crm = common.CRM()
vip_svc = common.VirtualIP('apache', '10.110.1.1')
vip_svc.configure_resource(crm)
self.assertEqual(
crm['resources']['res_apache_a7815c8_vip'],
'ocf:heartbeat:IPaddr2')
self.assertEqual(
crm['resource_params']['res_apache_a7815c8_vip'],
(' params ip="10.110.1.1" '
'op monitor depth="0" timeout="20s" interval="10s"'))
class TestHAClusterCommonDNSEntry(unittest.TestCase):
def test_init(self):
dns_svc = common.DNSEntry(
'keystone',
'10.110.1.1',
'keystone.admin',
'admin')
self.assertEqual(dns_svc.service_name, 'keystone')
self.assertEqual(dns_svc.ip, '10.110.1.1')
self.assertEqual(dns_svc.fqdn, 'keystone.admin')
self.assertEqual(dns_svc.endpoint_type, 'admin')
def test_configure_resource(self):
crm = common.CRM()
dns_svc = common.DNSEntry(
'keystone',
'10.110.1.1',
'keystone.admin',
'admin')
dns_svc.configure_resource(crm)
self.assertEqual(
crm['resources']['res_keystone_admin_hostname'],
'ocf:maas:dns')
self.assertEqual(
crm['resource_params']['res_keystone_admin_hostname'],
' params fqdn="keystone.admin" ip_address="10.110.1.1"')

414
unit_tests/test_requires.py Normal file
View File

@ -0,0 +1,414 @@
# 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
import mock
import unittest
import common
# Deal with the 'relations.hacluster.common' import in requires.py which
# is invalid in the unit tests as there is no 'relations'.
relations_mock = mock.MagicMock()
relations_mock.hacluster.common = common
modules = {
'relations': relations_mock,
'relations.hacluster': mock.MagicMock(),
'relations.hacluster.common': common,
}
module_patcher = mock.patch.dict('sys.modules', modules)
module_patcher.start()
with mock.patch('charmhelpers.core.hookenv.metadata') as _meta:
_meta.return_Value = 'ss'
import requires
_hook_args = {}
TO_PATCH = [
'data_changed',
]
def mock_hook(*args, **kwargs):
def inner(f):
# remember what we were passed. Note that we can't actually determine
# the class we're attached to, as the decorator only gets the function.
_hook_args[f.__name__] = dict(args=args, kwargs=kwargs)
return f
return inner
class TestHAClusterRequires(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._patched_hook = mock.patch('charms.reactive.when', mock_hook)
cls._patched_hook_started = cls._patched_hook.start()
# force requires to rerun the mock_hook decorator:
# try except is Python2/Python3 compatibility as Python3 has moved
# reload to importlib.
try:
reload(requires)
except NameError:
import importlib
importlib.reload(requires)
@classmethod
def tearDownClass(cls):
cls._patched_hook.stop()
cls._patched_hook_started = None
cls._patched_hook = None
# and fix any breakage we did to the module
try:
reload(requires)
except NameError:
import importlib
importlib.reload(requires)
def patch(self, method):
_m = mock.patch.object(self.obj, method)
_mock = _m.start()
self.addCleanup(_m.stop)
return _mock
def setUp(self):
self.cr = requires.HAClusterRequires('some-relation', [])
self._patches = {}
self._patches_start = {}
self.obj = requires
for method in TO_PATCH:
setattr(self, method, self.patch(method))
def tearDown(self):
self.cr = None
for k, v in self._patches.items():
v.stop()
setattr(self, k, None)
self._patches = None
self._patches_start = None
def patch_kr(self, attr, return_value=None):
mocked = mock.patch.object(self.cr, attr)
self._patches[attr] = mocked
started = mocked.start()
started.return_value = return_value
self._patches_start[attr] = started
setattr(self, attr, started)
def test_joined(self):
self.patch_kr('set_state')
self.cr.joined()
self.set_state.assert_called_once_with('{relation_name}.connected')
def test_changed(self):
self.patch_kr('is_clustered', True)
self.patch_kr('set_state')
self.cr.changed()
self.set_state.assert_called_once_with('{relation_name}.available')
def test_changed_not_clustered(self):
self.patch_kr('is_clustered', False)
self.patch_kr('remove_state')
self.cr.changed()
self.remove_state.assert_called_once_with('{relation_name}.available')
def test_departed(self):
self.patch_kr('remove_state')
self.cr.departed()
self.remove_state.assert_has_calls([
mock.call('{relation_name}.available'),
mock.call('{relation_name}.connected')])
def test_is_clustered(self):
self.patch_kr('get_remote_all')
self.get_remote_all.return_value = [True]
self.assertTrue(self.cr.is_clustered())
self.get_remote_all.return_value = ['true']
self.assertTrue(self.cr.is_clustered())
self.get_remote_all.return_value = ['yes']
self.assertTrue(self.cr.is_clustered())
self.get_remote_all.return_value = None
self.assertFalse(self.cr.is_clustered())
self.get_remote_all.return_value = [False]
self.assertFalse(self.cr.is_clustered())
self.get_remote_all.return_value = ['false']
self.assertFalse(self.cr.is_clustered())
self.get_remote_all.return_value = ['flump']
self.assertFalse(self.cr.is_clustered())
def jsonify(self, options):
json_encode_options = dict(
sort_keys=True,
)
for k, v in options.items():
if v:
options[k] = json.dumps(v, **json_encode_options)
def test_manage_resources(self):
res = common.CRM()
res.primitive('res_neutron_haproxy', 'lsb:haproxy',
op='monitor interval="5s"')
res.init_services('haproxy')
res.clone('cl_nova_haproxy', 'res_neutron_haproxy')
expected = {
'json_clones': {"cl_nova_haproxy": "res_neutron_haproxy"},
'json_init_services': ["haproxy"],
'json_resource_params': {
"res_neutron_haproxy": ' op monitor interval="5s"'},
'json_resources': {"res_neutron_haproxy": "lsb:haproxy"}}
self.jsonify(expected)
self.data_changed.return_value = True
self.patch_kr('set_local')
self.patch_kr('set_remote')
self.cr.manage_resources(res)
self.set_local.assert_called_once_with(**expected)
self.set_remote.assert_called_once_with(**expected)
def test_manage_resources_no_change(self):
res = common.CRM()
res.primitive('res_neutron_haproxy', 'lsb:haproxy',
op='monitor interval="5s"')
res.init_services('haproxy')
res.clone('cl_nova_haproxy', 'res_neutron_haproxy')
self.data_changed.return_value = False
self.patch_kr('set_local')
self.patch_kr('set_remote')
self.cr.manage_resources(res)
self.assertFalse(self.set_local.called)
self.assertFalse(self.set_remote.called)
def test_bind_resources(self):
self.patch_kr('get_local', 'resources')
self.patch_kr('bind_on')
self.patch_kr('manage_resources')
self.cr.bind_resources()
self.bind_on.assert_called_once_with(iface=None, mcastport=4440)
self.manage_resources.assert_called_once_with('resources')
def test_bind_resources_no_defaults(self):
self.patch_kr('get_local', 'resources')
self.patch_kr('bind_on')
self.patch_kr('manage_resources')
self.cr.bind_resources(iface='tr34', mcastport=111)
self.bind_on.assert_called_once_with(iface='tr34', mcastport=111)
self.manage_resources.assert_called_once_with('resources')
def test_add_vip(self):
expected = {
'resources': {
'res_mysql_4b8ce37_vip': 'ocf:heartbeat:IPaddr2'},
'delete_resources': [],
'resource_params': {
'res_mysql_4b8ce37_vip': (
' params ip="10.110.5.43" op monitor depth="0" '
'timeout="20s" interval="10s"')},
'groups': {},
'ms': {},
'orders': {},
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
self.patch_kr('get_local', None)
self.patch_kr('set_local')
self.cr.add_vip('mysql', '10.110.5.43')
self.set_local.assert_called_once_with(resources=expected)
def test_add_additional_vip(self):
existing_resource = {
'resources': {
'res_mysql_4b8ce37_vip': 'ocf:heartbeat:IPaddr2'},
'delete_resources': [],
'resource_params': {
'res_mysql_4b8ce37_vip': (
' params ip="10.110.5.43" op monitor depth="0" '
'timeout="20s" interval="10s"')},
'groups': {},
'ms': {},
'orders': {},
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
expected = {
'resources': {
'res_mysql_4b8ce37_vip': 'ocf:heartbeat:IPaddr2',
'res_mysql_1993276_vip': 'ocf:heartbeat:IPaddr2'},
'delete_resources': [],
'resource_params': {
'res_mysql_4b8ce37_vip': (
' params ip="10.110.5.43" op monitor depth="0" '
'timeout="20s" interval="10s"'),
'res_mysql_1993276_vip': (
' params ip="10.120.5.43" op monitor depth="0" '
'timeout="20s" interval="10s"')},
'groups': {
'grp_mysql_vips': ('res_mysql_1993276_vip '
'res_mysql_4b8ce37_vip')},
'ms': {},
'orders': {},
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
self.patch_kr('get_local', existing_resource)
self.patch_kr('set_local')
self.cr.add_vip('mysql', '10.120.5.43')
self.set_local.assert_called_once_with(resources=expected)
def test_add_init_service(self):
expected = {
'resources': {
'res_mysql_telnetd': 'lsb:telnetd'},
'delete_resources': [],
'resource_params': {
'res_mysql_telnetd': ' op monitor interval="5s"'},
'groups': {},
'ms': {},
'orders': {},
'colocations': {},
'clones': {'cl_res_mysql_telnetd': 'res_mysql_telnetd'},
'locations': {},
'init_services': ('telnetd',)}
self.patch_kr('get_local', None)
self.patch_kr('set_local')
self.cr.add_init_service('mysql', 'telnetd')
self.set_local.assert_called_once_with(resources=expected)
def test_add_dnsha(self):
expected = {
'resources': {
'res_keystone_public_hostname': 'ocf:maas:dns'},
'delete_resources': [],
'resource_params': {
'res_keystone_public_hostname': (
' params fqdn="keystone.public" '
'ip_address="10.110.5.43"')},
'groups': {},
'ms': {},
'orders': {},
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
self.patch_kr('get_local', None)
self.patch_kr('set_local')
self.cr.add_dnsha(
'keystone',
'10.110.5.43',
'keystone.public',
'public')
self.set_local.assert_called_once_with(resources=expected)
def test_add_additional_dnsha(self):
existing_resource = {
'resources': {
'res_keystone_public_hostname': 'ocf:maas:dns'},
'delete_resources': [],
'resource_params': {
'res_keystone_public_hostname': (
' params fqdn="keystone.public" '
'ip_address="10.110.5.43"')},
'groups': {},
'ms': {},
'orders': {},
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
expected = {
'resources': {
'res_keystone_public_hostname': 'ocf:maas:dns',
'res_keystone_admin_hostname': 'ocf:maas:dns'},
'delete_resources': [],
'resource_params': {
'res_keystone_public_hostname': (
' params fqdn="keystone.public" '
'ip_address="10.110.5.43"'),
'res_keystone_admin_hostname': (
' params fqdn="keystone.admin" '
'ip_address="10.120.5.43"')},
'groups': {
'grp_keystone_hostnames': ('res_keystone_admin_hostname '
'res_keystone_public_hostname')},
'ms': {},
'orders': {},
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
self.patch_kr('get_local', existing_resource)
self.patch_kr('set_local')
self.cr.add_dnsha(
'keystone',
'10.120.5.43',
'keystone.admin',
'admin')
self.set_local.assert_called_once_with(resources=expected)
@mock.patch.object(requires.hookenv, 'related_units')
@mock.patch.object(requires.hookenv, 'relation_get')
def test_get_remote_all(self, relation_get, related_units):
unit_data = {
'rid:1': {
'app1/0': {
'key1': 'value1',
'key2': 'value2'},
'app1/1': {
'key1': 'value1',
'key2': 'value3'}},
'rid:2': {
'app2/0': {
'key1': 'value1',
'key2': 'value3'}},
'rid:3': {}}
def get_unit_data(key, unit, relation_id):
return unit_data[relation_id].get(unit, {}).get(key, {})
conv1 = mock.MagicMock()
conv1.relation_ids = ['rid:1', 'rid:2']
conv2 = mock.MagicMock()
conv2.relation_ids = ['rid:3']
self.patch_kr('conversations', [conv1, conv2])
related_units.side_effect = lambda x: unit_data[x].keys()
relation_get.side_effect = get_unit_data
# Check de-duplication:
self.assertEqual(
self.cr.get_remote_all('key1'),
['value1'])
# Check multiple values:
self.assertEqual(
self.cr.get_remote_all('key2'),
['value2', 'value3'])
# Check missing key
self.assertEqual(
self.cr.get_remote_all('key100'),
[])
# Check missing key with default
self.assertEqual(
self.cr.get_remote_all('key100', default='defaultvalue'),
['defaultvalue'])