~ubuntu-branches/ubuntu/wily/heat/wily

« back to all changes in this revision

Viewing changes to heat/tests/test_resource.py

  • Committer: Package Import Robot
  • Author(s): Corey Bryant
  • Date: 2015-08-19 08:11:50 UTC
  • mfrom: (1.1.27)
  • Revision ID: package-import@ubuntu.com-20150819081150-m969fd35xn8bdmfu
Tags: 1:5.0.0~b2-0ubuntu1
* New upstream milestone for OpenStack Liberty.
* d/control: Align (build-)depends with upstream.
* d/p/fix-requirements.patch: Dropped. No longer needed.
* d/p/fixup-assert-regex.patch: Rebased.
* d/rules: Remove .eggs directory in override_dh_auto_clean.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
from heat.db import api as db_api
29
29
from heat.engine import attributes
30
30
from heat.engine.cfn import functions as cfn_funcs
 
31
from heat.engine import clients
31
32
from heat.engine import constraints
32
33
from heat.engine import dependencies
33
34
from heat.engine import environment
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,
96
 
                                                   res.id, {})
97
 
        self.assertEqual(loaded_res.id, res.id)
 
97
                                                   res.id, True, {})
 
98
        self.assertEqual(loaded_res.id, res.id)
 
99
        self.assertEqual(self.stack.t, stack.t)
 
100
 
 
101
    def test_resource_load_with_state_cleanup(self):
 
102
        self.old_stack = parser.Stack(
 
103
            utils.dummy_context(), 'test_old_stack',
 
104
            template.Template({
 
105
                'HeatTemplateFormatVersion': '2012-12-12',
 
106
                'Resources': {
 
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')
 
115
        # Store Resource
 
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,
 
121
                                                   res.id, False, {})
 
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)
98
125
 
99
126
    def test_resource_invalid_name(self):
100
127
        snippet = rsrc_defn.ResourceDefinition('wrong/name',
382
409
        tmpl = rsrc_defn.ResourceDefinition('test_resource', 'Foo')
383
410
        res = generic_rsrc.GenericResource('test_res_upd', tmpl, self.stack)
384
411
        res._store()
 
412
        new_tmpl_id = 2
385
413
        self.assertIsNotNone(res.id)
386
 
        new_id = res.make_replacement()
 
414
        new_id = res.make_replacement(new_tmpl_id)
387
415
        new_res = resource_objects.Resource.get_obj(res.context, new_id)
388
416
 
389
417
        self.assertEqual(new_id, res.replaced_by)
390
418
        self.assertEqual(res.id, new_res.replaces)
391
419
        self.assertIsNone(new_res.nova_instance)
 
420
        self.assertEqual(new_tmpl_id, new_res.current_template_id)
392
421
 
393
422
    def test_parsed_template(self):
394
423
        join_func = cfn_funcs.Join(None,
977
1006
        scheduler.TaskRunner(res.resume)()
978
1007
        self.assertEqual((res.RESUME, res.COMPLETE), res.state)
979
1008
 
980
 
    def test_suspend_fail_inprogress(self):
981
 
        tmpl = rsrc_defn.ResourceDefinition('test_resource',
982
 
                                            'GenericResourceType',
983
 
                                            {'Foo': 'abc'})
984
 
        res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
985
 
        scheduler.TaskRunner(res.create)()
986
 
        self.assertEqual((res.CREATE, res.COMPLETE), res.state)
987
 
 
988
 
        res.state_set(res.CREATE, res.IN_PROGRESS)
989
 
        suspend = scheduler.TaskRunner(res.suspend)
990
 
        self.assertRaises(exception.ResourceFailure, suspend)
991
 
 
992
 
        res.state_set(res.UPDATE, res.IN_PROGRESS)
993
 
        suspend = scheduler.TaskRunner(res.suspend)
994
 
        self.assertRaises(exception.ResourceFailure, suspend)
995
 
 
996
 
        res.state_set(res.DELETE, res.IN_PROGRESS)
997
 
        suspend = scheduler.TaskRunner(res.suspend)
998
 
        self.assertRaises(exception.ResourceFailure, suspend)
999
 
 
1000
 
    def test_resume_fail_not_suspend_complete(self):
1001
 
        tmpl = rsrc_defn.ResourceDefinition('test_resource',
1002
 
                                            'GenericResourceType',
1003
 
                                            {'Foo': 'abc'})
1004
 
        res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
1005
 
        scheduler.TaskRunner(res.create)()
1006
 
        self.assertEqual((res.CREATE, res.COMPLETE), res.state)
1007
 
 
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',
 
1012
                                            {'Foo': 'abc'})
 
1013
        res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
 
1014
        scheduler.TaskRunner(res.create)()
 
1015
        self.assertEqual((res.CREATE, res.COMPLETE), res.state)
 
1016
 
 
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)]
 
1021
 
 
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))
 
1028
 
 
1029
    def test_resume_fail_invalid_states(self):
 
1030
        tmpl = rsrc_defn.ResourceDefinition('test_resource',
 
1031
                                            'GenericResourceType',
 
1032
                                            {'Foo': 'abc'})
 
1033
        res = generic_rsrc.ResourceWithProps('test_resource', tmpl, self.stack)
 
1034
        scheduler.TaskRunner(res.create)()
 
1035
        self.assertEqual((res.CREATE, res.COMPLETE), res.state)
 
1036
 
 
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))
1015
1048
 
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'])
1416
1449
 
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)
 
1454
 
1418
1455
    @mock.patch.object(resource.Resource, 'create')
1419
 
    def test_create_convergence(self,
1420
 
                                mock_create,
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): {},
1425
 
                                                (1, True): {}})
 
1459
        res.action = res.CREATE
 
1460
        res._store()
 
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')
1426
1465
 
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)
1431
 
 
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)
 
1470
 
 
1471
    def test_create_convergence_sets_requires_for_failure(self):
 
1472
        '''
 
1473
        Ensure that requires are computed correctly even if resource
 
1474
        create fails,
 
1475
        '''
 
1476
        tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
 
1477
        res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
 
1478
        res._store()
 
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,
 
1486
                          'engine-007')
 
1487
        self.assertItemsEqual([5, 3], res.requires)
 
1488
        self._assert_resource_lock(res.id, None, 2)
 
1489
 
 
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
 
1495
        res._store()
 
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')
 
1502
 
 
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)
 
1507
 
1433
1508
    @mock.patch.object(resource.Resource, 'update')
1434
 
    def test_update_convergence(self,
1435
 
                                mock_update,
1436
 
                                mock_store_update_method
1437
 
                                ):
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): {},
1442
 
                                                (1, True): {}})
1443
 
 
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)
 
1514
        res._store()
 
1515
        self._assert_resource_lock(res.id, None, None)
 
1516
 
 
1517
        new_temp = template.Template({
 
1518
            'HeatTemplateFormatVersion': '2012-12-12',
 
1519
            'Resources': {
 
1520
                'test_res': {'Type': 'ResourceWithPropsType',
 
1521
                             'Properties': {'Foo': 'abc'}}
 
1522
            }}, env=self.env)
 
1523
        new_temp.store()
 
1524
 
 
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')
 
1528
 
 
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)
1448
1534
 
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]
 
1539
        res._store()
 
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)
 
1543
 
 
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',
1456
 
                               {})
 
1549
                               res_data, 'engine-007')
1457
1550
        msg = ("The resource %s is already being updated." %
1458
1551
               res.name)
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)
1460
1555
 
1461
 
    @mock.patch.object(resource.Resource, '_store_or_update')
1462
 
    @mock.patch.object(resource.Resource, 'delete')
1463
 
    def test_delete_convergence(self,
1464
 
                                mock_delete,
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): {},
1470
 
                                                (1, True): {}})
1471
 
 
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)
 
1560
        res._store()
 
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)
1476
1565
 
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
 
1569
        res._store()
 
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,
1483
 
                               'template_key',
1484
 
                               {})
 
1575
                               'engine-007')
1485
1576
        msg = ("The resource %s is already being updated." %
1486
1577
               res.name)
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'})
 
2334
 
 
2335
 
 
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
 
2344
 
 
2345
    def test_default_true_with_default_client_name_none(self):
 
2346
        '''
 
2347
        When default_client_name is None, resource is considered as available.
 
2348
        '''
 
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())))
 
2355
 
 
2356
    @mock.patch.object(clients.OpenStackClients, 'client_plugin')
 
2357
    def test_default_true_empty_service_types(
 
2358
            self,
 
2359
            mock_client_plugin_method):
 
2360
        '''
 
2361
        When service_types is empty list, resource is considered as available.
 
2362
        '''
 
2363
 
 
2364
        mock_service_types, mock_client_plugin = self._mock_client_plugin()
 
2365
        mock_client_plugin_method.return_value = mock_client_plugin
 
2366
 
 
2367
        self.assertTrue(
 
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()
 
2373
 
 
2374
    @mock.patch.object(clients.OpenStackClients, 'client_plugin')
 
2375
    def test_service_deployed(
 
2376
            self,
 
2377
            mock_client_plugin_method):
 
2378
        '''
 
2379
        When the service is deployed, resource is considered as available.
 
2380
        '''
 
2381
 
 
2382
        mock_service_types, mock_client_plugin = self._mock_client_plugin(
 
2383
            ['test_type']
 
2384
        )
 
2385
        mock_client_plugin_method.return_value = mock_client_plugin
 
2386
 
 
2387
        self.assertTrue(
 
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)
 
2397
        )
 
2398
 
 
2399
    @mock.patch.object(clients.OpenStackClients, 'client_plugin')
 
2400
    def test_service_not_deployed(
 
2401
            self,
 
2402
            mock_client_plugin_method):
 
2403
        '''
 
2404
        When the service is not deployed, resource is considered as
 
2405
        unavailable.
 
2406
        '''
 
2407
 
 
2408
        mock_service_types, mock_client_plugin = self._mock_client_plugin(
 
2409
            ['test_type_un_deployed'],
 
2410
            False
 
2411
        )
 
2412
        mock_client_plugin_method.return_value = mock_client_plugin
 
2413
 
 
2414
        self.assertFalse(
 
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)
 
2424
        )
 
2425
 
 
2426
    def test_service_not_deployed_throws_exception(self):
 
2427
        '''
 
2428
        When the service is not deployed, make sure resource is throwing
 
2429
        ResourceTypeUnavailable exception.
 
2430
        '''
 
2431
        with mock.patch.object(
 
2432
                generic_rsrc.ResourceWithDefaultClientName,
 
2433
                'is_service_available') as mock_method:
 
2434
            mock_method.return_value = False
 
2435
 
 
2436
            definition = rsrc_defn.ResourceDefinition(
 
2437
                name='Test Resource',
 
2438
                resource_type='UnavailableResourceType')
 
2439
 
 
2440
            mock_stack = mock.MagicMock()
 
2441
 
 
2442
            ex = self.assertRaises(
 
2443
                exception.ResourceTypeUnavailable,
 
2444
                generic_rsrc.ResourceWithDefaultClientName.__new__,
 
2445
                cls=generic_rsrc.ResourceWithDefaultClientName,
 
2446
                name='test_stack',
 
2447
                definition=definition,
 
2448
                stack=mock_stack)
 
2449
 
 
2450
            msg = ('Service sample does not have required endpoint in service'
 
2451
                   ' catalog for the resource type UnavailableResourceType')
 
2452
            self.assertEqual(msg,
 
2453
                             six.text_type(ex),
 
2454
                             'invalid exception message')
 
2455
 
 
2456
            # Make sure is_service_available is called on the right class
 
2457
            mock_method.assert_called_once_with(mock_stack.context)