93
94
res.state_set('CREATE', 'IN_PROGRESS')
94
95
self.stack.add_resource(res)
95
96
loaded_res, stack = resource.Resource.load(self.stack.context,
97
self.assertEqual(loaded_res.id, res.id)
98
self.assertEqual(loaded_res.id, res.id)
99
self.assertEqual(self.stack.t, stack.t)
101
def test_resource_load_with_state_cleanup(self):
102
self.old_stack = parser.Stack(
103
utils.dummy_context(), 'test_old_stack',
105
'HeatTemplateFormatVersion': '2012-12-12',
107
'test_res': {'Type': 'ResourceWithPropsType',
108
'Properties': {'Foo': 'abc'}}}}))
109
self.old_stack.store()
110
self.new_stack = parser.Stack(utils.dummy_context(), 'test_new_stack',
111
template.Template(empty_template))
112
self.new_stack.store()
113
snippet = rsrc_defn.ResourceDefinition('aresource',
114
'GenericResourceType')
116
res = resource.Resource('aresource', snippet, self.old_stack)
117
res.current_template_id = self.old_stack.t.id
118
res.state_set('CREATE', 'IN_PROGRESS')
119
self.old_stack.add_resource(res)
120
loaded_res, stack = resource.Resource.load(self.old_stack.context,
122
self.assertEqual(loaded_res.id, res.id)
123
self.assertEqual(self.old_stack.t, stack.t)
124
self.assertNotEqual(self.new_stack.t, stack.t)
99
126
def test_resource_invalid_name(self):
100
127
snippet = rsrc_defn.ResourceDefinition('wrong/name',
977
1006
scheduler.TaskRunner(res.resume)()
978
1007
self.assertEqual((res.RESUME, res.COMPLETE), res.state)
980
def test_suspend_fail_inprogress(self):
981
tmpl = rsrc_defn.ResourceDefinition('test_resource',
982
'GenericResourceType',
984
res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
985
scheduler.TaskRunner(res.create)()
986
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
988
res.state_set(res.CREATE, res.IN_PROGRESS)
989
suspend = scheduler.TaskRunner(res.suspend)
990
self.assertRaises(exception.ResourceFailure, suspend)
992
res.state_set(res.UPDATE, res.IN_PROGRESS)
993
suspend = scheduler.TaskRunner(res.suspend)
994
self.assertRaises(exception.ResourceFailure, suspend)
996
res.state_set(res.DELETE, res.IN_PROGRESS)
997
suspend = scheduler.TaskRunner(res.suspend)
998
self.assertRaises(exception.ResourceFailure, suspend)
1000
def test_resume_fail_not_suspend_complete(self):
1001
tmpl = rsrc_defn.ResourceDefinition('test_resource',
1002
'GenericResourceType',
1004
res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
1005
scheduler.TaskRunner(res.create)()
1006
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
1008
non_suspended_states = [s for s in
1009
itertools.product(res.ACTIONS, res.STATUSES)
1010
if s != (res.SUSPEND, res.COMPLETE)]
1011
for state in non_suspended_states:
1009
def test_suspend_fail_invalid_states(self):
1010
tmpl = rsrc_defn.ResourceDefinition('test_resource',
1011
'GenericResourceType',
1013
res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
1014
scheduler.TaskRunner(res.create)()
1015
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
1017
invalid_actions = (a for a in res.ACTIONS if a != res.SUSPEND)
1018
invalid_status = (s for s in res.STATUSES if s != res.COMPLETE)
1019
invalid_states = [s for s in
1020
itertools.product(invalid_actions, invalid_status)]
1022
for state in invalid_states:
1023
res.state_set(*state)
1024
suspend = scheduler.TaskRunner(res.suspend)
1025
expected = 'State %s invalid for suspend' % six.text_type(state)
1026
exc = self.assertRaises(exception.ResourceFailure, suspend)
1027
self.assertIn(expected, six.text_type(exc))
1029
def test_resume_fail_invalid_states(self):
1030
tmpl = rsrc_defn.ResourceDefinition('test_resource',
1031
'GenericResourceType',
1033
res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
1034
scheduler.TaskRunner(res.create)()
1035
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
1037
invalid_states = [s for s in
1038
itertools.product(res.ACTIONS, res.STATUSES)
1039
if s not in ((res.SUSPEND, res.COMPLETE),
1040
(res.RESUME, res.FAILED),
1041
(res.RESUME, res.COMPLETE))]
1042
for state in invalid_states:
1012
1043
res.state_set(*state)
1013
1044
resume = scheduler.TaskRunner(res.resume)
1014
self.assertRaises(exception.ResourceFailure, resume)
1045
expected = 'State %s invalid for resume' % six.text_type(state)
1046
exc = self.assertRaises(exception.ResourceFailure, resume)
1047
self.assertIn(expected, six.text_type(exc))
1016
1049
def test_suspend_fail_exception(self):
1017
1050
tmpl = rsrc_defn.ResourceDefinition('test_resource',
1414
1447
res_obj = res_objs['test_res_enc']
1415
1448
self.assertEqual('string', res_obj.properties_data['prop1'])
1417
@mock.patch.object(resource.Resource, '_store_or_update')
1450
def _assert_resource_lock(self, res_id, engine_id, atomic_key):
1451
rs = resource_objects.Resource.get_obj(self.stack.context, res_id)
1452
self.assertEqual(engine_id, rs.engine_id)
1453
self.assertEqual(atomic_key, rs.atomic_key)
1418
1455
@mock.patch.object(resource.Resource, 'create')
1419
def test_create_convergence(self,
1421
mock_store_update_method):
1456
def test_create_convergence(self, mock_create):
1422
1457
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
1423
1458
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
1424
res.create_convergence('template_key', {(1, True): {},
1459
res.action = res.CREATE
1461
self._assert_resource_lock(res.id, None, None)
1462
res_data = {(1, True): {u'id': 1, u'name': 'A', 'attrs': {}},
1463
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
1464
res.create_convergence(self.stack.t.id, res_data, 'engine-007')
1427
1466
mock_create.assert_called_once_with()
1428
self.assertEqual('template_key', res.current_template_id)
1429
self.assertEqual([1], res.requires)
1430
self.assertTrue(mock_store_update_method.called)
1432
@mock.patch.object(resource.Resource, '_store_or_update')
1467
self.assertEqual(self.stack.t.id, res.current_template_id)
1468
self.assertItemsEqual([1, 3], res.requires)
1469
self._assert_resource_lock(res.id, None, 2)
1471
def test_create_convergence_sets_requires_for_failure(self):
1473
Ensure that requires are computed correctly even if resource
1476
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
1477
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
1479
dummy_ex = exception.ResourceNotAvailable(resource_name=res.name)
1480
res.create = mock.Mock(side_effect=dummy_ex)
1481
self._assert_resource_lock(res.id, None, None)
1482
res_data = {(1, True): {u'id': 5, u'name': 'A', 'attrs': {}},
1483
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
1484
self.assertRaises(exception.ResourceNotAvailable,
1485
res.create_convergence, self.stack.t.id, res_data,
1487
self.assertItemsEqual([5, 3], res.requires)
1488
self._assert_resource_lock(res.id, None, 2)
1490
@mock.patch.object(resource.Resource, 'adopt')
1491
def test_adopt_convergence(self, mock_adopt):
1492
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
1493
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
1494
res.action = res.ADOPT
1496
self.stack.adopt_stack_data = {'resources': {'test_res': {
1497
'resource_id': 'fluffy'}}}
1498
self._assert_resource_lock(res.id, None, None)
1499
res_data = {(1, True): {u'id': 5, u'name': 'A', 'attrs': {}},
1500
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
1501
res.create_convergence(self.stack.t.id, res_data, 'engine-007')
1503
mock_adopt.assert_called_once_with(
1504
resource_data={'resource_id': 'fluffy'})
1505
self.assertItemsEqual([5, 3], res.requires)
1506
self._assert_resource_lock(res.id, None, 2)
1433
1508
@mock.patch.object(resource.Resource, 'update')
1434
def test_update_convergence(self,
1436
mock_store_update_method
1438
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
1509
def test_update_convergence(self, mock_update):
1510
tmpl = rsrc_defn.ResourceDefinition('test_res',
1511
'ResourceWithPropsType')
1439
1512
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
1440
1513
res.requires = [2]
1441
res.update_convergence('template_key', {(1, True): {},
1444
mock_update.assert_called_once_with(res.t)
1445
self.assertEqual('template_key', res.current_template_id)
1446
self.assertEqual([1, 2], res.requires)
1447
self.assertTrue(mock_store_update_method.called)
1515
self._assert_resource_lock(res.id, None, None)
1517
new_temp = template.Template({
1518
'HeatTemplateFormatVersion': '2012-12-12',
1520
'test_res': {'Type': 'ResourceWithPropsType',
1521
'Properties': {'Foo': 'abc'}}
1525
res_data = {(1, True): {u'id': 4, u'name': 'A', 'attrs': {}},
1526
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
1527
res.update_convergence(new_temp.id, res_data, 'engine-007')
1529
mock_update.assert_called_once_with(
1530
new_temp.resource_definitions(self.stack)[res.name])
1531
self.assertEqual(new_temp.id, res.current_template_id)
1532
self.assertItemsEqual([3, 4], res.requires)
1533
self._assert_resource_lock(res.id, None, 2)
1449
1535
def test_update_in_progress_convergence(self):
1450
1536
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
1451
1537
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
1452
res.status = resource.Resource.IN_PROGRESS
1538
res.requires = [1, 2]
1540
rs = resource_objects.Resource.get_obj(self.stack.context, res.id)
1541
rs.update_and_save({'engine_id': 'not-this'})
1542
self._assert_resource_lock(res.id, 'not-this', None)
1544
res_data = {(1, True): {u'id': 4, u'name': 'A', 'attrs': {}},
1545
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
1453
1546
ex = self.assertRaises(resource.UpdateInProgress,
1454
1547
res.update_convergence,
1455
1548
'template_key',
1549
res_data, 'engine-007')
1457
1550
msg = ("The resource %s is already being updated." %
1459
1552
self.assertEqual(msg, six.text_type(ex))
1553
# ensure requirements are not updated for failed resource
1554
self.assertEqual([1, 2], res.requires)
1461
@mock.patch.object(resource.Resource, '_store_or_update')
1462
@mock.patch.object(resource.Resource, 'delete')
1463
def test_delete_convergence(self,
1465
mock_store_update_method):
1556
def test_delete_convergence(self):
1466
1557
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
1467
1558
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
1468
1559
res.requires = [1, 2]
1469
res.delete_convergence('template_key', {(1, True): {},
1472
mock_delete.assert_called_once_with()
1473
self.assertEqual('template_key', res.current_template_id)
1474
self.assertEqual([2], res.requires)
1475
self.assertTrue(mock_store_update_method.called)
1561
res.destroy = mock.Mock()
1562
self._assert_resource_lock(res.id, None, None)
1563
res.delete_convergence('engine-007')
1564
self.assertTrue(res.destroy.called)
1477
1566
def test_delete_in_progress_convergence(self):
1478
1567
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
1479
1568
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
1480
res.status = resource.Resource.IN_PROGRESS
1570
rs = resource_objects.Resource.get_obj(self.stack.context, res.id)
1571
rs.update_and_save({'engine_id': 'not-this'})
1572
self._assert_resource_lock(res.id, 'not-this', None)
1481
1573
ex = self.assertRaises(resource.UpdateInProgress,
1482
1574
res.delete_convergence,
1485
1576
msg = ("The resource %s is already being updated." %
1487
1578
self.assertEqual(msg, six.text_type(ex))
2240
2331
res.has_hook = mock.Mock(return_value=False)
2241
2332
self.assertRaises(exception.ResourceActionNotSupported,
2242
2333
res.signal, {'unset_hook': 'pre-create'})
2336
class ResourceAvailabilityTest(common.HeatTestCase):
2337
def _mock_client_plugin(self, service_types=[], is_available=True):
2338
mock_client_plugin = mock.Mock()
2339
mock_service_types = mock.PropertyMock(return_value=service_types)
2340
type(mock_client_plugin).service_types = mock_service_types
2341
mock_client_plugin.does_endpoint_exist = mock.Mock(
2342
return_value=is_available)
2343
return mock_service_types, mock_client_plugin
2345
def test_default_true_with_default_client_name_none(self):
2347
When default_client_name is None, resource is considered as available.
2349
with mock.patch(('heat.tests.generic_resource'
2350
'.ResourceWithDefaultClientName.default_client_name'),
2351
new_callable=mock.PropertyMock) as mock_client_name:
2352
mock_client_name.return_value = None
2353
self.assertTrue((generic_rsrc.ResourceWithDefaultClientName.
2354
is_service_available(context=mock.Mock())))
2356
@mock.patch.object(clients.OpenStackClients, 'client_plugin')
2357
def test_default_true_empty_service_types(
2359
mock_client_plugin_method):
2361
When service_types is empty list, resource is considered as available.
2364
mock_service_types, mock_client_plugin = self._mock_client_plugin()
2365
mock_client_plugin_method.return_value = mock_client_plugin
2368
generic_rsrc.ResourceWithDefaultClientName.is_service_available(
2369
context=mock.Mock()))
2370
mock_client_plugin_method.assert_called_once_with(
2371
generic_rsrc.ResourceWithDefaultClientName.default_client_name)
2372
mock_service_types.assert_called_once_with()
2374
@mock.patch.object(clients.OpenStackClients, 'client_plugin')
2375
def test_service_deployed(
2377
mock_client_plugin_method):
2379
When the service is deployed, resource is considered as available.
2382
mock_service_types, mock_client_plugin = self._mock_client_plugin(
2385
mock_client_plugin_method.return_value = mock_client_plugin
2388
generic_rsrc.ResourceWithDefaultClientName.is_service_available(
2389
context=mock.Mock()))
2390
mock_client_plugin_method.assert_called_once_with(
2391
generic_rsrc.ResourceWithDefaultClientName.default_client_name)
2392
mock_service_types.assert_called_once_with()
2393
mock_client_plugin.does_endpoint_exist.assert_called_once_with(
2394
service_type='test_type',
2395
service_name=(generic_rsrc.ResourceWithDefaultClientName
2396
.default_client_name)
2399
@mock.patch.object(clients.OpenStackClients, 'client_plugin')
2400
def test_service_not_deployed(
2402
mock_client_plugin_method):
2404
When the service is not deployed, resource is considered as
2408
mock_service_types, mock_client_plugin = self._mock_client_plugin(
2409
['test_type_un_deployed'],
2412
mock_client_plugin_method.return_value = mock_client_plugin
2415
generic_rsrc.ResourceWithDefaultClientName.is_service_available(
2416
context=mock.Mock()))
2417
mock_client_plugin_method.assert_called_once_with(
2418
generic_rsrc.ResourceWithDefaultClientName.default_client_name)
2419
mock_service_types.assert_called_once_with()
2420
mock_client_plugin.does_endpoint_exist.assert_called_once_with(
2421
service_type='test_type_un_deployed',
2422
service_name=(generic_rsrc.ResourceWithDefaultClientName
2423
.default_client_name)
2426
def test_service_not_deployed_throws_exception(self):
2428
When the service is not deployed, make sure resource is throwing
2429
ResourceTypeUnavailable exception.
2431
with mock.patch.object(
2432
generic_rsrc.ResourceWithDefaultClientName,
2433
'is_service_available') as mock_method:
2434
mock_method.return_value = False
2436
definition = rsrc_defn.ResourceDefinition(
2437
name='Test Resource',
2438
resource_type='UnavailableResourceType')
2440
mock_stack = mock.MagicMock()
2442
ex = self.assertRaises(
2443
exception.ResourceTypeUnavailable,
2444
generic_rsrc.ResourceWithDefaultClientName.__new__,
2445
cls=generic_rsrc.ResourceWithDefaultClientName,
2447
definition=definition,
2450
msg = ('Service sample does not have required endpoint in service'
2451
' catalog for the resource type UnavailableResourceType')
2452
self.assertEqual(msg,
2454
'invalid exception message')
2456
# Make sure is_service_available is called on the right class
2457
mock_method.assert_called_once_with(mock_stack.context)