diff --git a/common.py b/common.py index b2da391..4c87484 100644 --- a/common.py +++ b/common.py @@ -286,7 +286,9 @@ class CRM(dict): self['groups'][name] = specs def delete_resource(self, *resources): - """Deletes one or more objects/resources within Pacemaker. + """Specify objects/resources to be deleted from within Pacemaker. This + is not additive, the list of resources is set to exaclty what was + passed in. Parameters ---------- @@ -303,6 +305,26 @@ class CRM(dict): """ self['delete_resources'] = resources + def add_delete_resource(self, resource): + """Specify an object/resource to delete from within Pacemaker. It can + be called multiple times to add additional resources to the deletion + list. + + Parameters + ---------- + resources: str + the name or id of the specific resource to delete. + + Returns + ------- + None + + See Also + -------- + http://crmsh.github.io/man/#cmdhelp_configure_delete + """ + self['delete_resources'] = (*self['delete_resources'], resource) + def init_services(self, *resources): """Specifies that the service(s) is an init or upstart service. diff --git a/requires.py b/requires.py index 8921b8c..da5b740 100644 --- a/requires.py +++ b/requires.py @@ -114,6 +114,15 @@ class HAClusterRequires(RelationBase): self.bind_on(iface=iface, mcastport=mcastport) self.manage_resources(resources) + def delete_resource(self, resource_name): + resource_dict = self.get_local('resources') + if resource_dict: + resources = relations.hacluster.common.CRM(**resource_dict) + else: + resources = relations.hacluster.common.CRM() + resources.add_delete_resource(resource_name) + self.set_local(resources=resources) + def add_vip(self, name, vip, iface=None, netmask=None): """Add a VirtualIP object for each user specified vip to self.resources diff --git a/unit_tests/test_common.py b/unit_tests/test_common.py index 1d481c5..426ca8a 100644 --- a/unit_tests/test_common.py +++ b/unit_tests/test_common.py @@ -192,6 +192,27 @@ class TestHAClusterCommonCRM(unittest.TestCase): crm['delete_resources'], ('res_mysql_vip', 'grp_mysql')) + def test_add_delete_resource(self): + crm = common.CRM() + crm.add_delete_resource('res_mysql_vip') + self.assertEqual(crm['delete_resources'], ('res_mysql_vip',)) + + def test_add_delete_resource_multi(self): + crm = common.CRM() + crm.add_delete_resource('res_mysql_vip') + crm.add_delete_resource('grp_mysql') + self.assertEqual( + crm['delete_resources'], + ('res_mysql_vip', 'grp_mysql')) + + def test_add_delete_resource_mix(self): + crm = common.CRM() + crm.delete_resource('grp_mysql') + crm.add_delete_resource('res_mysql_vip') + self.assertEqual( + crm['delete_resources'], + ('grp_mysql', 'res_mysql_vip')) + def test_init_services(self): crm = common.CRM() crm.init_services('haproxy') diff --git a/unit_tests/test_requires.py b/unit_tests/test_requires.py index ff5a8b3..d2929af 100644 --- a/unit_tests/test_requires.py +++ b/unit_tests/test_requires.py @@ -11,6 +11,7 @@ # limitations under the License. +import copy import json import mock import unittest @@ -212,6 +213,48 @@ class TestHAClusterRequires(unittest.TestCase): self.bind_on.assert_called_once_with(iface='tr34', mcastport=111) self.manage_resources.assert_called_once_with('resources') + def test_delete_resource(self): + expected = { + 'resources': {}, + 'resource_params': {}, + 'delete_resources': ('doomed_res',), + 'groups': {}, + 'ms': {}, + 'orders': {}, + 'colocations': {}, + 'clones': {}, + 'locations': {}, + 'init_services': []} + self.patch_kr('get_local', None) + self.patch_kr('set_local') + self.cr.delete_resource('doomed_res') + self.set_local.assert_called_once_with(resources=expected) + + def test_delete_resource_multi(self): + base = { + 'resources': {}, + 'resource_params': {}, + 'groups': {}, + 'ms': {}, + 'orders': {}, + 'colocations': {}, + 'clones': {}, + 'locations': {}, + 'init_services': []} + res_with_res1 = copy.deepcopy(base) + res_with_res1_res2 = copy.deepcopy(base) + res_with_res1['delete_resources'] = ('doomed_res1',) + res_with_res1_res2['delete_resources'] = ('doomed_res1', 'doomed_res2') + get_local_results = [res_with_res1, base] + self.patch_kr('get_local') + self.get_local.side_effect = lambda x: get_local_results.pop() + self.patch_kr('set_local') + self.cr.delete_resource('doomed_res1') + self.cr.delete_resource('doomed_res2') + self.set_local.assert_has_calls([ + mock.call(resources=res_with_res1), + mock.call(resources=res_with_res1_res2)]) + def test_add_vip(self): expected = { 'resources': {