def test_pre_op_candidates_provided(self): action = mock.Mock() action.context = self.context policy = dp.DeletionPolicy('test-policy', self.spec) # Both 'count' and 'candidates' are provided in deletion # field of action data action.data = { 'deletion': { 'count': 2, 'candidates': [ self.nodes_p1[0], self.nodes_p2[2], ], } } policy.pre_op(self.cluster['id'], action) pd = { 'status': 'OK', 'reason': 'Candidates generated', 'deletion': { 'count': 2, 'candidates': [self.nodes_p1[0], self.nodes_p2[2]], 'destroy_after_deletion': True, 'grace_period': 60, } } self.assertEqual(pd, action.data) action.store.assert_called_with(self.context)
def test_update_action_override(self): action = mock.Mock() action.data = { 'deletion': { 'count': 3, } } policy = dp.DeletionPolicy('test-policy', self.spec) policy._update_action(action, ['N1', 'N2']) pd = { 'status': 'OK', 'reason': 'Candidates generated', 'deletion': { 'count': 2, 'candidates': ['N1', 'N2'], 'destroy_after_deletion': True, 'grace_period': 60, 'reduce_desired_capacity': False, } } self.assertEqual(pd, action.data) action.store.assert_called_with(action.context)
def test_pre_op_with_zone_decisions(self, mock_load, mock_select, mock_update): action = mock.Mock() action.context = self.context action.inputs = {} action.data = { 'deletion': { 'count': 2, 'zones': { 'AZ1': 1, 'AZ2': 1 } } } cluster = mock.Mock(nodes=['a', 'b', 'c']) mock_load.return_value = cluster mock_select.return_value = ['NODE1', 'NODE2'] policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_update.assert_called_once_with(action, ['NODE1', 'NODE2']) mock_load.assert_called_once_with(action.context, cluster=None, cluster_id='FAKE_ID') mock_select.assert_called_once_with(cluster, {'AZ1': 1, 'AZ2': 1})
def test_pre_op_resize_not_deletion(self, mock_get, mock_parse, mock_update): def fake_parse(a, c): a.data = {} return 'OK', 'cool' action = mock.Mock() action.context = self.context action.action = consts.CLUSTER_RESIZE action.inputs = {} db_cluster = mock.Mock() mock_get.return_value = db_cluster mock_parse.side_effect = fake_parse policy = dp.DeletionPolicy('test-policy', self.spec) # a simulation of non-deletion RESZIE action.data = {} policy.pre_op('FAKE_ID', action) mock_get.assert_called_once_with(action.context, 'FAKE_ID', project_safe=True) mock_parse.assert_called_once_with(action, db_cluster) self.assertEqual(0, mock_update.call_count)
def test__victims_by_zones_profile_age(self, mock_select): cluster = mock.Mock() node1 = mock.Mock(id=1) node2 = mock.Mock(id=2) node3 = mock.Mock(id=3) cluster.nodes_by_zone.side_effect = [ [node1], [node2, node3] ] mock_select.side_effect = [['1'], ['2']] self.spec['properties']['criteria'] = 'OLDEST_PROFILE_FIRST' policy = dp.DeletionPolicy('test-policy', self.spec) res = policy._victims_by_zones(cluster, {'AZ1': 1, 'AZ2': 1}) self.assertEqual(['1', '2'], res) mock_select.assert_has_calls( [ mock.call([node1], 1), mock.call([node2, node3], 1) ], ) cluster.nodes_by_zone.assert_has_calls( [mock.call('AZ1'), mock.call('AZ2')], )
def test__victims_by_zones_age_youngest(self, mock_select): cluster = mock.Mock() node1 = mock.Mock(id=1) node2 = mock.Mock(id=3) node3 = mock.Mock(id=5) cluster.nodes_by_zone.side_effect = [ [node1], [node2, node3] ] mock_select.side_effect = [['1'], ['3', '5']] self.spec['properties']['criteria'] = 'YOUNGEST_FIRST' policy = dp.DeletionPolicy('test-policy', self.spec) res = policy._victims_by_zones(cluster, {'AZ5': 1, 'AZ6': 2}) self.assertEqual(['1', '3', '5'], res) mock_select.assert_has_calls( [ mock.call([node1], 1, False), mock.call([node2, node3], 2, False) ], ) cluster.nodes_by_zone.assert_has_calls( [mock.call('AZ5'), mock.call('AZ6')], )
def test_select_candidates_random(self): self.spec['properties']['criteria'] = dp.DeletionPolicy.RANDOM policy = dp.DeletionPolicy('test-policy', self.spec) nodes = policy._select_candidates(self.context, self.cluster['id'], 1) self.assertEqual(1, len(nodes)) nodes = policy._select_candidates(self.context, self.cluster['id'], 3) self.assertEqual(3, len(nodes)) nodes = policy._select_candidates(self.context, self.cluster['id'], 10) self.assertEqual(6, len(nodes))
def test_policy_init(self): policy = dp.DeletionPolicy('test-policy', self.spec) self.assertIsNone(policy.id) self.assertEqual('test-policy', policy.name) self.assertEqual('senlin.policy.deletion-1.0', policy.type) self.assertEqual('OLDEST_FIRST', policy.criteria) self.assertTrue(policy.destroy_after_deletion) self.assertEqual(60, policy.grace_period) self.assertFalse(policy.reduce_desired_capacity)
def test_pre_op_node_delete(self, mock_update): action = mock.Mock() action.action = consts.NODE_DELETE action.context = self.context action.node.id = 'NODE_ID' action.inputs = {} action.data = {} policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_update.assert_called_once_with(action, ['NODE_ID'])
def test_pre_op_node_delete(self, mock_update): action = mock.Mock(action=consts.NODE_DELETE, context=self.context, inputs={}, data={}, entity=mock.Mock(id='NODE_ID')) policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_update.assert_called_once_with(action, ['NODE_ID'])
def test_pre_op_del_nodes(self, mock_update): action = mock.Mock() action.context = self.context action.inputs = { 'count': 2, 'candidates': ['N1', 'N2'], } action.data = {} policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_update.assert_called_once_with(action, ['N1', 'N2'])
def test_select_candidates_youngest_first(self): self.spec['properties']['criteria'] = dp.DeletionPolicy.YOUNGEST_FIRST policy = dp.DeletionPolicy('test-policy', self.spec) nodes = policy._select_candidates(self.context, self.cluster['id'], 1) self.assertEqual(1, len(nodes)) self.assertEqual(self.nodes_p2[2], nodes[0]) nodes = policy._select_candidates(self.context, self.cluster['id'], 2) self.assertEqual(2, len(nodes)) self.assertEqual(self.nodes_p2[2], nodes[0]) self.assertEqual(self.nodes_p2[1], nodes[1])
def test_pre_op_with_region_decisions(self, mock_select, mock_update): action = mock.Mock(context=self.context, inputs={}) action.data = {'deletion': {'count': 2, 'regions': {'R1': 1, 'R2': 1}}} cluster = mock.Mock(nodes=['a', 'b', 'c']) action.entity = cluster mock_select.return_value = ['NODE1', 'NODE2'] policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_update.assert_called_once_with(action, ['NODE1', 'NODE2']) mock_select.assert_called_once_with(cluster, {'R1': 1, 'R2': 1})
def test_policy_init(self): policy = dp.DeletionPolicy('test-policy', self.spec) self.assertIsNone(policy.id) self.assertEqual('test-policy', policy.name) self.assertEqual('senlin.policy.deletion-1.0', policy.type) self.assertEqual(self.spec['properties']['criteria'], policy.criteria) self.assertEqual(self.spec['properties']['destroy_after_deletion'], policy.destroy_after_deletion) self.assertEqual(self.spec['properties']['grace_period'], policy.grace_period) self.assertEqual(self.spec['properties']['reduce_desired_capacity'], policy.reduce_desired_capacity)
def test_select_candidates_oldest_profile_first(self): criteria = dp.DeletionPolicy.OLDEST_PROFILE_FIRST self.spec['properties']['criteria'] = criteria policy = dp.DeletionPolicy('test-policy', self.spec) nodes = policy._select_candidates(self.context, self.cluster['id'], 1) self.assertEqual(1, len(nodes)) self.assertEqual(self.nodes_p1[0], nodes[0]) nodes = policy._select_candidates(self.context, self.cluster['id'], 2) self.assertEqual(2, len(nodes)) self.assertEqual(self.nodes_p1[0], nodes[0]) self.assertEqual(self.nodes_p1[1], nodes[1])
def test_pre_op_resize_failed_parse(self, mock_parse, mock_update): action = mock.Mock(context=self.context, inputs={}, data={}, action=consts.CLUSTER_RESIZE) cluster = mock.Mock(nodes=[mock.Mock(), mock.Mock()]) action.entity = cluster mock_parse.return_value = 'ERROR', 'Failed parsing.' policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) self.assertEqual('ERROR', action.data['status']) self.assertEqual('Failed parsing.', action.data['reason']) mock_parse.assert_called_once_with(action, cluster, 2) self.assertEqual(0, mock_update.call_count)
def test_pre_op_scale_in_without_count(self, mock_select, mock_update): action = mock.Mock(context=self.context, data={}, inputs={}, action=consts.CLUSTER_SCALE_IN) cluster = mock.Mock(nodes=[mock.Mock()]) action.entity = cluster mock_select.return_value = ['NODE_ID'] policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_update.assert_called_once_with(action, ['NODE_ID']) # the following was invoked with 1 because the input count is # not specified so 1 becomes the default mock_select.assert_called_once_with(cluster.nodes, 1, True)
def test_select_candidates_error_nodes_first(self): kwargs = { 'status': 'ERROR', 'status_reason': 'Unknown', } self.nodes_p3 = self._create_nodes(self.cluster['id'], self.profile3['id'], 1, **kwargs) self.spec['properties']['criteria'] = \ dp.DeletionPolicy.OLDEST_PROFILE_FIRST policy = dp.DeletionPolicy('test-policy', self.spec) nodes = policy._select_candidates(self.context, self.cluster['id'], 2) self.assertEqual(2, len(nodes)) self.assertEqual(self.nodes_p3[0], nodes[0]) self.assertEqual(self.nodes_p1[0], nodes[1]) self._delete_nodes(self.nodes_p3[0])
def test_pre_op_do_youngest_first(self, mock_select, mock_update): action = mock.Mock(context=self.context, inputs={}, data={'deletion': { 'count': 2 }}) cluster = mock.Mock(nodes=['a', 'b', 'c']) action.entity = cluster mock_select.return_value = ['NODE1', 'NODE2'] spec = copy.deepcopy(self.spec) spec['properties']['criteria'] = 'YOUNGEST_FIRST' policy = dp.DeletionPolicy('test-policy', spec) policy.pre_op('FAKE_ID', action) mock_select.assert_called_once_with(cluster.nodes, 2, False) mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
def test_pre_op_do_oldest_first(self, mock_load, mock_select, mock_update): action = mock.Mock() action.context = self.context action.inputs = {} action.data = {'deletion': {'count': 2}} cluster = mock.Mock(nodes=['a', 'b', 'c']) mock_select.return_value = ['NODE1', 'NODE2'] mock_load.return_value = cluster self.spec['properties']['criteria'] = 'OLDEST_FIRST' policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_load.assert_called_once_with(action.context, cluster=None, cluster_id='FAKE_ID') mock_select.assert_called_once_with(cluster.nodes, 2, True) mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
def test_victims_by_regions_age_oldest(self, mock_select): cluster = mock.Mock() node1 = mock.Mock(id=1) node2 = mock.Mock(id=2) node3 = mock.Mock(id=3) cluster.nodes_by_region.side_effect = [[node1], [node2, node3]] mock_select.side_effect = [['1'], ['2', '3']] self.spec['properties']['criteria'] = 'OLDEST_FIRST' policy = dp.DeletionPolicy('test-policy', self.spec) res = policy._victims_by_regions(cluster, {'R1': 1, 'R2': 2}) self.assertEqual(['1', '2', '3'], res) mock_select.assert_has_calls( [mock.call([node1], 1, True), mock.call([node2, node3], 2, True)]) cluster.nodes_by_region.assert_has_calls( [mock.call('R1'), mock.call('R2')])
def test_pre_op_resize_not_deletion(self, mock_parse, mock_update): def fake_parse(action, cluster, current): action.data = {} return 'OK', 'cool' action = mock.Mock(context=self.context, inputs={}, action=consts.CLUSTER_RESIZE) cluster = mock.Mock(nodes=[mock.Mock(), mock.Mock()]) action.entity = cluster mock_parse.side_effect = fake_parse policy = dp.DeletionPolicy('test-policy', self.spec) # a simulation of non-deletion RESZIE action.data = {} policy.pre_op('FAKE_ID', action) mock_parse.assert_called_once_with(action, cluster, 2) self.assertEqual(0, mock_update.call_count)
def test_pre_op_resize_with_count(self, mock_select, mock_update, mock_parse): def fake_parse(a, cluster, current): a.data = {'deletion': {'count': 2}} return 'OK', 'cool' action = mock.Mock(context=self.context, inputs={}, data={}, action=consts.CLUSTER_RESIZE) cluster = mock.Mock(nodes=[mock.Mock(), mock.Mock()]) action.entity = cluster mock_parse.side_effect = fake_parse mock_select.return_value = ['NID'] policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_parse.assert_called_once_with(action, cluster, 2) mock_update.assert_called_once_with(action, ['NID'])
def test_pre_op_scale_in_with_count(self, mock_load, mock_select, mock_update): action = mock.Mock() action.action = consts.CLUSTER_SCALE_IN action.context = self.context action.data = {} action.inputs = {'count': 2} cluster = mock.Mock(nodes=[mock.Mock()]) mock_load.return_value = cluster mock_select.return_value = ['NODE_ID'] policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) mock_load.assert_called_once_with(action.context, cluster=None, cluster_id='FAKE_ID') mock_update.assert_called_once_with(action, ['NODE_ID']) # the following was invoked with 1 because the input count is # greater than the cluster size mock_select.assert_called_once_with(cluster.nodes, 1, True)
def test_pre_op_resize_with_count(self, mock_get, mock_load, mock_select, mock_update, mock_parse): def fake_parse(a, obj): a.data = { 'deletion': { 'count': 2 } } return 'OK', 'cool' action = mock.Mock() action.context = self.context action.action = consts.CLUSTER_RESIZE action.inputs = {} action.data = {} db_cluster = mock.Mock() mock_get.return_value = db_cluster mock_parse.side_effect = fake_parse cluster = mock.Mock(nodes=[mock.Mock(), mock.Mock()]) mock_load.return_value = cluster mock_select.return_value = ['NID'] policy = dp.DeletionPolicy('test-policy', self.spec) # mock_parse = self.patchobject(su, 'parse_resize_params', # side_effect=fake_parse) policy.pre_op('FAKE_ID', action) mock_get.assert_called_once_with(action.context, 'FAKE_ID', project_safe=True) mock_parse.assert_called_once_with(action, db_cluster) mock_load.assert_called_once_with(action.context, cluster=db_cluster, cluster_id='FAKE_ID') mock_update.assert_called_once_with(action, ['NID'])
def test_pre_op_resize_failed_parse(self, mock_get, mock_parse, mock_update): action = mock.Mock() action.context = self.context action.action = consts.CLUSTER_RESIZE action.inputs = {} action.data = {} db_cluster = mock.Mock() mock_get.return_value = db_cluster mock_parse.return_value = 'ERROR', 'Failed parsing.' policy = dp.DeletionPolicy('test-policy', self.spec) policy.pre_op('FAKE_ID', action) self.assertEqual('ERROR', action.data['status']) self.assertEqual('Failed parsing.', action.data['reason']) mock_get.assert_called_once_with(action.context, 'FAKE_ID', project_safe=True) mock_parse.assert_called_once_with(action, db_cluster) self.assertEqual(0, mock_update.call_count)
def test_pre_op(self, mock_cluster, mock_parse): action = mock.Mock() action.context = self.context action.inputs = {} policy = dp.DeletionPolicy('test-policy', self.spec) mock_parse.return_value = 'OK', '' # CLUSTER_SCALE_IN or ClUSTER_DEL_NODES action # action data doesn't have 'deletion' field action.data = {} action.action = consts.CLUSTER_SCALE_IN policy.pre_op(self.cluster['id'], action) pd = { 'status': 'OK', 'reason': 'Candidates generated', 'deletion': { 'candidates': [self.nodes_p1[0]], 'destroy_after_deletion': True, 'grace_period': 60, } } self.assertEqual(pd, action.data) # action data has 'deletion' field, but 'count' is not provided action.data = {'abc': 123} action.action = consts.CLUSTER_DEL_NODES policy.pre_op(self.cluster['id'], action) pd = { 'status': 'OK', 'reason': 'Candidates generated', 'deletion': { 'candidates': [self.nodes_p1[0]], 'destroy_after_deletion': True, 'grace_period': 60, }, 'abc': 123 } self.assertEqual(pd, action.data) # 'count' is provided in inputs field of action action.inputs = {'count': 2} action.action = consts.CLUSTER_DEL_NODES action.data = {} policy.pre_op(self.cluster['id'], action) pd = { 'status': 'OK', 'reason': 'Candidates generated', 'deletion': { 'candidates': [self.nodes_p1[0], self.nodes_p1[1]], 'destroy_after_deletion': True, 'grace_period': 60, } } self.assertEqual(pd, action.data) action.store.assert_called_with(self.context) # CLUSTER_RESIZE action # delete nodes action.action = consts.CLUSTER_RESIZE action.data = {'deletion': {'count': 1}} policy.pre_op(self.cluster['id'], action) pd = { 'status': 'OK', 'reason': 'Candidates generated', 'deletion': { 'candidates': [self.nodes_p1[0]], 'destroy_after_deletion': True, 'grace_period': 60, 'count': 1, } } self.assertEqual(pd, action.data) action.store.assert_called_with(self.context)