"Fossies" - the Fresh Open Source Software Archive 
Member "senlin-8.0.0/senlin/tests/unit/policies/test_deletion_policy.py" (16 Oct 2019, 20645 Bytes) of package /linux/misc/openstack/senlin-8.0.0.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the last
Fossies "Diffs" side-by-side code changes report for "test_deletion_policy.py":
6.0.0_vs_7.0.0.
1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
11 # under the License.
12
13 import copy
14 import mock
15
16 from senlin.common import consts
17 from senlin.common import scaleutils as su
18 from senlin.policies import deletion_policy as dp
19 from senlin.tests.unit.common import base
20 from senlin.tests.unit.common import utils
21
22
23 class TestDeletionPolicy(base.SenlinTestCase):
24
25 def setUp(self):
26 super(TestDeletionPolicy, self).setUp()
27 self.context = utils.dummy_context()
28 self.spec = {
29 'type': 'senlin.policy.deletion',
30 'version': '1.0',
31 'properties': {
32 'criteria': 'OLDEST_FIRST',
33 'destroy_after_deletion': True,
34 'grace_period': 60,
35 'reduce_desired_capacity': False
36 }
37 }
38
39 def test_policy_init(self):
40 policy = dp.DeletionPolicy('test-policy', self.spec)
41
42 self.assertIsNone(policy.id)
43 self.assertEqual('test-policy', policy.name)
44 self.assertEqual('senlin.policy.deletion-1.0', policy.type)
45 self.assertEqual('OLDEST_FIRST', policy.criteria)
46 self.assertTrue(policy.destroy_after_deletion)
47 self.assertEqual(60, policy.grace_period)
48 self.assertFalse(policy.reduce_desired_capacity)
49
50 @mock.patch.object(su, 'nodes_by_random')
51 def test_victims_by_regions_random(self, mock_select):
52 cluster = mock.Mock()
53 node1 = mock.Mock(id=1)
54 node2 = mock.Mock(id=2)
55 node3 = mock.Mock(id=3)
56 cluster.nodes_by_region.side_effect = [
57 [node1], [node2, node3]
58 ]
59
60 mock_select.side_effect = [['1'], ['2', '3']]
61
62 self.spec['properties']['criteria'] = 'RANDOM'
63 policy = dp.DeletionPolicy('test-policy', self.spec)
64
65 res = policy._victims_by_regions(cluster, {'R1': 1, 'R2': 2})
66 self.assertEqual(['1', '2', '3'], res)
67 mock_select.assert_has_calls([
68 mock.call([node1], 1),
69 mock.call([node2, node3], 2)
70 ])
71 cluster.nodes_by_region.assert_has_calls([
72 mock.call('R1'), mock.call('R2')])
73
74 @mock.patch.object(su, 'nodes_by_profile_age')
75 def test_victims_by_regions_profile_age(self, mock_select):
76 cluster = mock.Mock()
77 node1 = mock.Mock(id=1)
78 node2 = mock.Mock(id=2)
79 node3 = mock.Mock(id=3)
80 cluster.nodes_by_region.side_effect = [
81 [node1], [node2, node3]
82 ]
83
84 mock_select.side_effect = [['1'], ['2', '3']]
85
86 self.spec['properties']['criteria'] = 'OLDEST_PROFILE_FIRST'
87 policy = dp.DeletionPolicy('test-policy', self.spec)
88
89 res = policy._victims_by_regions(cluster, {'R1': 1, 'R2': 2})
90 self.assertEqual(['1', '2', '3'], res)
91 mock_select.assert_has_calls([
92 mock.call([node1], 1),
93 mock.call([node2, node3], 2)
94 ])
95 cluster.nodes_by_region.assert_has_calls([
96 mock.call('R1'), mock.call('R2')])
97
98 @mock.patch.object(su, 'nodes_by_age')
99 def test_victims_by_regions_age_oldest(self, mock_select):
100 cluster = mock.Mock()
101 node1 = mock.Mock(id=1)
102 node2 = mock.Mock(id=2)
103 node3 = mock.Mock(id=3)
104 cluster.nodes_by_region.side_effect = [
105 [node1], [node2, node3]
106 ]
107
108 mock_select.side_effect = [['1'], ['2', '3']]
109
110 self.spec['properties']['criteria'] = 'OLDEST_FIRST'
111 policy = dp.DeletionPolicy('test-policy', self.spec)
112
113 res = policy._victims_by_regions(cluster, {'R1': 1, 'R2': 2})
114 self.assertEqual(['1', '2', '3'], res)
115 mock_select.assert_has_calls([
116 mock.call([node1], 1, True),
117 mock.call([node2, node3], 2, True)
118 ])
119 cluster.nodes_by_region.assert_has_calls([
120 mock.call('R1'), mock.call('R2')])
121
122 @mock.patch.object(su, 'nodes_by_age')
123 def test_victims_by_regions_age_youngest(self, mock_select):
124 cluster = mock.Mock()
125 node1 = mock.Mock(id=1)
126 node2 = mock.Mock(id=2)
127 node3 = mock.Mock(id=3)
128 cluster.nodes_by_region.side_effect = [
129 [node1], [node2, node3]
130 ]
131
132 mock_select.side_effect = [['1'], ['2', '3']]
133
134 self.spec['properties']['criteria'] = 'YOUNGEST_FIRST'
135 policy = dp.DeletionPolicy('test-policy', self.spec)
136
137 res = policy._victims_by_regions(cluster, {'R1': 1, 'R2': 2})
138 self.assertEqual(['1', '2', '3'], res)
139 mock_select.assert_has_calls([
140 mock.call([node1], 1, False),
141 mock.call([node2, node3], 2, False)
142 ])
143 cluster.nodes_by_region.assert_has_calls([
144 mock.call('R1'), mock.call('R2')])
145
146 @mock.patch.object(su, 'nodes_by_random')
147 def test_victims_by_zones_random(self, mock_select):
148 cluster = mock.Mock()
149 node1 = mock.Mock(id=1)
150 node2 = mock.Mock(id=2)
151 node3 = mock.Mock(id=3)
152 cluster.nodes_by_zone.side_effect = [
153 [node1], [node2, node3]
154 ]
155
156 mock_select.side_effect = [['1'], ['3']]
157
158 self.spec['properties']['criteria'] = 'RANDOM'
159 policy = dp.DeletionPolicy('test-policy', self.spec)
160
161 res = policy._victims_by_zones(cluster, {'AZ1': 1, 'AZ2': 1})
162 self.assertEqual(['1', '3'], res)
163 mock_select.assert_has_calls([
164 mock.call([node1], 1),
165 mock.call([node2, node3], 1)
166 ])
167 cluster.nodes_by_zone.assert_has_calls(
168 [mock.call('AZ1'), mock.call('AZ2')],
169 )
170
171 @mock.patch.object(su, 'nodes_by_profile_age')
172 def test_victims_by_zones_profile_age(self, mock_select):
173 cluster = mock.Mock()
174 node1 = mock.Mock(id=1)
175 node2 = mock.Mock(id=2)
176 node3 = mock.Mock(id=3)
177 cluster.nodes_by_zone.side_effect = [
178 [node1], [node2, node3]
179 ]
180
181 mock_select.side_effect = [['1'], ['2']]
182
183 self.spec['properties']['criteria'] = 'OLDEST_PROFILE_FIRST'
184 policy = dp.DeletionPolicy('test-policy', self.spec)
185
186 res = policy._victims_by_zones(cluster, {'AZ1': 1, 'AZ2': 1})
187 self.assertEqual(['1', '2'], res)
188 mock_select.assert_has_calls(
189 [
190 mock.call([node1], 1),
191 mock.call([node2, node3], 1)
192 ],
193 )
194 cluster.nodes_by_zone.assert_has_calls(
195 [mock.call('AZ1'), mock.call('AZ2')],
196 )
197
198 @mock.patch.object(su, 'nodes_by_age')
199 def test_victims_by_zones_age_oldest(self, mock_select):
200 cluster = mock.Mock()
201 node1 = mock.Mock(id=1)
202 node2 = mock.Mock(id=2)
203 node3 = mock.Mock(id=3)
204 cluster.nodes_by_zone.side_effect = [
205 [node1], [node2, node3]
206 ]
207
208 mock_select.side_effect = [['1'], ['3']]
209
210 self.spec['properties']['criteria'] = 'OLDEST_FIRST'
211 policy = dp.DeletionPolicy('test-policy', self.spec)
212
213 res = policy._victims_by_zones(cluster, {'AZ1': 1, 'AZ8': 1})
214 self.assertEqual(['1', '3'], res)
215 mock_select.assert_has_calls([
216 mock.call([node1], 1, True),
217 mock.call([node2, node3], 1, True)
218 ])
219 cluster.nodes_by_zone.assert_has_calls(
220 [mock.call('AZ1'), mock.call('AZ8')],
221 )
222
223 @mock.patch.object(su, 'nodes_by_age')
224 def test_victims_by_zones_age_youngest(self, mock_select):
225 cluster = mock.Mock()
226 node1 = mock.Mock(id=1)
227 node2 = mock.Mock(id=3)
228 node3 = mock.Mock(id=5)
229 cluster.nodes_by_zone.side_effect = [
230 [node1], [node2, node3]
231 ]
232
233 mock_select.side_effect = [['1'], ['3', '5']]
234
235 self.spec['properties']['criteria'] = 'YOUNGEST_FIRST'
236 policy = dp.DeletionPolicy('test-policy', self.spec)
237
238 res = policy._victims_by_zones(cluster, {'AZ5': 1, 'AZ6': 2})
239 self.assertEqual(['1', '3', '5'], res)
240 mock_select.assert_has_calls(
241 [
242 mock.call([node1], 1, False),
243 mock.call([node2, node3], 2, False)
244 ],
245 )
246 cluster.nodes_by_zone.assert_has_calls(
247 [mock.call('AZ5'), mock.call('AZ6')],
248 )
249
250 def test_update_action_clean(self):
251 action = mock.Mock()
252 action.data = {}
253
254 policy = dp.DeletionPolicy('test-policy', self.spec)
255
256 policy._update_action(action, ['N1', 'N2'])
257
258 pd = {
259 'status': 'OK',
260 'reason': 'Candidates generated',
261 'deletion': {
262 'count': 2,
263 'candidates': ['N1', 'N2'],
264 'destroy_after_deletion': True,
265 'grace_period': 60,
266 'reduce_desired_capacity': False,
267 }
268 }
269 self.assertEqual(pd, action.data)
270 action.store.assert_called_with(action.context)
271
272 def test_update_action_override(self):
273 action = mock.Mock()
274 action.data = {
275 'deletion': {
276 'count': 3,
277 }
278 }
279
280 policy = dp.DeletionPolicy('test-policy', self.spec)
281
282 policy._update_action(action, ['N1', 'N2'])
283
284 pd = {
285 'status': 'OK',
286 'reason': 'Candidates generated',
287 'deletion': {
288 'count': 2,
289 'candidates': ['N1', 'N2'],
290 'destroy_after_deletion': True,
291 'grace_period': 60,
292 'reduce_desired_capacity': False,
293 }
294 }
295 self.assertEqual(pd, action.data)
296 action.store.assert_called_with(action.context)
297
298 @mock.patch.object(dp.DeletionPolicy, '_update_action')
299 def test_pre_op_del_nodes(self, mock_update):
300 action = mock.Mock()
301 action.context = self.context
302 action.inputs = {
303 'count': 2,
304 'candidates': ['N1', 'N2'],
305 }
306 action.data = {}
307 policy = dp.DeletionPolicy('test-policy', self.spec)
308
309 policy.pre_op('FAKE_ID', action)
310
311 mock_update.assert_called_once_with(action, ['N1', 'N2'])
312
313 @mock.patch.object(dp.DeletionPolicy, '_update_action')
314 def test_pre_op_node_delete(self, mock_update):
315 action = mock.Mock(action=consts.NODE_DELETE, context=self.context,
316 inputs={}, data={}, entity=mock.Mock(id='NODE_ID'))
317 policy = dp.DeletionPolicy('test-policy', self.spec)
318
319 policy.pre_op('FAKE_ID', action)
320
321 mock_update.assert_called_once_with(action, ['NODE_ID'])
322
323 @mock.patch.object(dp.DeletionPolicy, '_update_action')
324 @mock.patch.object(su, 'nodes_by_age')
325 def test_pre_op_with_count_decisions(self, mock_select, mock_update):
326 action = mock.Mock(context=self.context, inputs={},
327 data={'deletion': {'count': 2}})
328 cluster = mock.Mock(nodes=['a', 'b', 'c'])
329 action.entity = cluster
330 mock_select.return_value = ['NODE1', 'NODE2']
331 policy = dp.DeletionPolicy('test-policy', self.spec)
332
333 policy.pre_op('FAKE_ID', action)
334
335 mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
336 mock_select.assert_called_once_with(cluster.nodes, 2, True)
337
338 @mock.patch.object(dp.DeletionPolicy, '_update_action')
339 @mock.patch.object(dp.DeletionPolicy, '_victims_by_regions')
340 def test_pre_op_with_region_decisions(self, mock_select, mock_update):
341 action = mock.Mock(context=self.context, inputs={})
342 action.data = {
343 'deletion': {
344 'count': 2,
345 'regions': {
346 'R1': 1,
347 'R2': 1
348 }
349 }
350 }
351 cluster = mock.Mock(nodes=['a', 'b', 'c'])
352 action.entity = cluster
353 mock_select.return_value = ['NODE1', 'NODE2']
354 policy = dp.DeletionPolicy('test-policy', self.spec)
355
356 policy.pre_op('FAKE_ID', action)
357
358 mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
359 mock_select.assert_called_once_with(cluster, {'R1': 1, 'R2': 1})
360
361 @mock.patch.object(dp.DeletionPolicy, '_update_action')
362 @mock.patch.object(dp.DeletionPolicy, '_victims_by_zones')
363 def test_pre_op_with_zone_decisions(self, mock_select, mock_update):
364 action = mock.Mock(context=self.context, inputs={})
365 action.data = {
366 'deletion': {
367 'count': 2,
368 'zones': {
369 'AZ1': 1,
370 'AZ2': 1
371 }
372 }
373 }
374 cluster = mock.Mock(nodes=['a', 'b', 'c'])
375 action.entity = cluster
376 mock_select.return_value = ['NODE1', 'NODE2']
377 policy = dp.DeletionPolicy('test-policy', self.spec)
378
379 policy.pre_op('FAKE_ID', action)
380
381 mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
382 mock_select.assert_called_once_with(cluster, {'AZ1': 1, 'AZ2': 1})
383
384 @mock.patch.object(dp.DeletionPolicy, '_update_action')
385 @mock.patch.object(su, 'nodes_by_age')
386 def test_pre_op_scale_in_with_count(self, mock_select, mock_update):
387 action = mock.Mock(context=self.context, data={}, inputs={'count': 2},
388 action=consts.CLUSTER_SCALE_IN)
389 cluster = mock.Mock(nodes=[mock.Mock()])
390 action.entity = cluster
391 mock_select.return_value = ['NODE_ID']
392 policy = dp.DeletionPolicy('test-policy', self.spec)
393
394 policy.pre_op('FAKE_ID', action)
395
396 mock_update.assert_called_once_with(action, ['NODE_ID'])
397 # the following was invoked with 1 because the input count is
398 # greater than the cluster size
399 mock_select.assert_called_once_with(cluster.nodes, 1, True)
400
401 @mock.patch.object(dp.DeletionPolicy, '_update_action')
402 @mock.patch.object(su, 'nodes_by_age')
403 def test_pre_op_scale_in_without_count(self, mock_select, mock_update):
404 action = mock.Mock(context=self.context, data={}, inputs={},
405 action=consts.CLUSTER_SCALE_IN)
406 cluster = mock.Mock(nodes=[mock.Mock()])
407 action.entity = cluster
408 mock_select.return_value = ['NODE_ID']
409 policy = dp.DeletionPolicy('test-policy', self.spec)
410
411 policy.pre_op('FAKE_ID', action)
412
413 mock_update.assert_called_once_with(action, ['NODE_ID'])
414 # the following was invoked with 1 because the input count is
415 # not specified so 1 becomes the default
416 mock_select.assert_called_once_with(cluster.nodes, 1, True)
417
418 @mock.patch.object(dp.DeletionPolicy, '_update_action')
419 @mock.patch.object(su, 'parse_resize_params')
420 def test_pre_op_resize_failed_parse(self, mock_parse, mock_update):
421 action = mock.Mock(context=self.context, inputs={}, data={},
422 action=consts.CLUSTER_RESIZE)
423 cluster = mock.Mock(nodes=[mock.Mock(), mock.Mock()])
424 action.entity = cluster
425 mock_parse.return_value = 'ERROR', 'Failed parsing.'
426 policy = dp.DeletionPolicy('test-policy', self.spec)
427
428 policy.pre_op('FAKE_ID', action)
429
430 self.assertEqual('ERROR', action.data['status'])
431 self.assertEqual('Failed parsing.', action.data['reason'])
432 mock_parse.assert_called_once_with(action, cluster, 2)
433 self.assertEqual(0, mock_update.call_count)
434
435 @mock.patch.object(dp.DeletionPolicy, '_update_action')
436 @mock.patch.object(su, 'parse_resize_params')
437 def test_pre_op_resize_not_deletion(self, mock_parse, mock_update):
438 def fake_parse(action, cluster, current):
439 action.data = {}
440 return 'OK', 'cool'
441
442 action = mock.Mock(context=self.context, inputs={},
443 action=consts.CLUSTER_RESIZE)
444 cluster = mock.Mock(nodes=[mock.Mock(), mock.Mock()])
445 action.entity = cluster
446 mock_parse.side_effect = fake_parse
447 policy = dp.DeletionPolicy('test-policy', self.spec)
448 # a simulation of non-deletion RESZIE
449 action.data = {}
450
451 policy.pre_op('FAKE_ID', action)
452
453 mock_parse.assert_called_once_with(action, cluster, 2)
454 self.assertEqual(0, mock_update.call_count)
455
456 @mock.patch.object(su, 'parse_resize_params')
457 @mock.patch.object(dp.DeletionPolicy, '_update_action')
458 @mock.patch.object(su, 'nodes_by_age')
459 def test_pre_op_resize_with_count(self, mock_select, mock_update,
460 mock_parse):
461 def fake_parse(a, cluster, current):
462 a.data = {
463 'deletion': {
464 'count': 2
465 }
466 }
467 return 'OK', 'cool'
468
469 action = mock.Mock(context=self.context, inputs={}, data={},
470 action=consts.CLUSTER_RESIZE)
471 cluster = mock.Mock(nodes=[mock.Mock(), mock.Mock()])
472 action.entity = cluster
473 mock_parse.side_effect = fake_parse
474 mock_select.return_value = ['NID']
475 policy = dp.DeletionPolicy('test-policy', self.spec)
476
477 policy.pre_op('FAKE_ID', action)
478
479 mock_parse.assert_called_once_with(action, cluster, 2)
480 mock_update.assert_called_once_with(action, ['NID'])
481
482 @mock.patch.object(dp.DeletionPolicy, '_update_action')
483 @mock.patch.object(su, 'nodes_by_random')
484 def test_pre_op_do_random(self, mock_select, mock_update):
485 action = mock.Mock(context=self.context, inputs={},
486 data={'deletion': {'count': 2}})
487 cluster = mock.Mock(nodes=['a', 'b', 'c'])
488 action.entity = cluster
489 mock_select.return_value = ['NODE1', 'NODE2']
490 spec = copy.deepcopy(self.spec)
491 spec['properties']['criteria'] = 'RANDOM'
492 policy = dp.DeletionPolicy('test-policy', spec)
493
494 policy.pre_op('FAKE_ID', action)
495
496 mock_select.assert_called_once_with(cluster.nodes, 2)
497 mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
498
499 @mock.patch.object(dp.DeletionPolicy, '_update_action')
500 @mock.patch.object(su, 'nodes_by_profile_age')
501 def test_pre_op_do_oldest_profile(self, mock_select, mock_update):
502 action = mock.Mock(context=self.context, inputs={},
503 data={'deletion': {'count': 2}})
504 mock_select.return_value = ['NODE1', 'NODE2']
505 cluster = mock.Mock(nodes=['a', 'b', 'c'])
506 action.entity = cluster
507 spec = copy.deepcopy(self.spec)
508 spec['properties']['criteria'] = 'OLDEST_PROFILE_FIRST'
509 policy = dp.DeletionPolicy('test-policy', spec)
510
511 policy.pre_op('FAKE_ID', action)
512
513 mock_select.assert_called_once_with(cluster.nodes, 2)
514 mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
515
516 @mock.patch.object(dp.DeletionPolicy, '_update_action')
517 @mock.patch.object(su, 'nodes_by_age')
518 def test_pre_op_do_oldest_first(self, mock_select, mock_update):
519 action = mock.Mock(context=self.context, inputs={},
520 data={'deletion': {'count': 2}})
521 cluster = mock.Mock(nodes=['a', 'b', 'c'])
522 action.entity = cluster
523 mock_select.return_value = ['NODE1', 'NODE2']
524 spec = copy.deepcopy(self.spec)
525 spec['properties']['criteria'] = 'OLDEST_FIRST'
526 policy = dp.DeletionPolicy('test-policy', spec)
527
528 policy.pre_op('FAKE_ID', action)
529
530 mock_select.assert_called_once_with(cluster.nodes, 2, True)
531 mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])
532
533 @mock.patch.object(dp.DeletionPolicy, '_update_action')
534 @mock.patch.object(su, 'nodes_by_age')
535 def test_pre_op_do_youngest_first(self, mock_select, mock_update):
536 action = mock.Mock(context=self.context, inputs={},
537 data={'deletion': {'count': 2}})
538 cluster = mock.Mock(nodes=['a', 'b', 'c'])
539 action.entity = cluster
540 mock_select.return_value = ['NODE1', 'NODE2']
541 spec = copy.deepcopy(self.spec)
542 spec['properties']['criteria'] = 'YOUNGEST_FIRST'
543 policy = dp.DeletionPolicy('test-policy', spec)
544
545 policy.pre_op('FAKE_ID', action)
546
547 mock_select.assert_called_once_with(cluster.nodes, 2, False)
548 mock_update.assert_called_once_with(action, ['NODE1', 'NODE2'])