diff -Nru heat-6.1.0/AUTHORS heat-6.1.1/AUTHORS --- heat-6.1.0/AUTHORS 2016-09-06 19:23:38.000000000 +0000 +++ heat-6.1.1/AUTHORS 2017-01-31 20:57:05.000000000 +0000 @@ -30,6 +30,7 @@ Arata Notsu Atsushi SAKAI Attila Fazekas +AvnishPal BK Box Bartosz Górski Ben Nemec diff -Nru heat-6.1.0/ChangeLog heat-6.1.1/ChangeLog --- heat-6.1.0/ChangeLog 2016-09-06 19:23:38.000000000 +0000 +++ heat-6.1.1/ChangeLog 2017-01-31 20:57:05.000000000 +0000 @@ -1,6 +1,19 @@ CHANGES ======= +6.1.1 +----- + +* To reset metadata for resources when mark unhealthy +* Use correct charset when create a text/* type MIME +* Use metadata and personality with server rebuild +* Updated from global requirements +* Use upper constraints for all jobs in tox.ini +* Prevent template validate from scanning ports +* Delete RoleAssignment if user/group does not exist +* Increase the timeout for the stop_stack message +* Don't use cast() to do StackResource delete + 6.1.0 ----- @@ -262,7 +275,7 @@ * Refactor property translation rule(2/2) * Refactor property translation rule(1/2) * Restrict update/replace of resource -* Fix a grammatical mistake of the example online +* Fix a grammatical mistake of the example online * Replace ex.message with exception_to_unicode(ex) * Add OS::Barbican::*Container * Fix zaqar queue creation workflow @@ -702,7 +715,7 @@ * Add Bay to Magnum resources * Updated from global requirements * Unit test for checking that migrations don't cause downtime -* Make neutron resources available based on service extensions +* Make neutron resources available based on service extensions * Allow get_attr: [res_name] format to get all attribute * Replaced mox with mock in test_metadata_refresh * Updated from global requirements @@ -1021,7 +1034,7 @@ * Add Keystone User/Group RoleAssignment resources * Removes unused posix-ipc requirement * Engine method validate_template fails for unavailable resource type -* Handle NotFound error for all _resolve_attributes +* Handle NotFound error for all _resolve_attributes * Make RoleAssigment class to RoleAssigmentMixin class * Add subnet property to Nova::Server nic * Use _get_network_id in other methods of mixin class @@ -1188,7 +1201,7 @@ * Updated from global requirements * Fix handle_delete for zaqar queue * Refactor format_resource_attributes -* Use default client name in nova - afterfixes +* Use default client name in nova - afterfixes * Revert "Temporarily skip failing scenario tests" * Add functional test for resource exposure * Add show_resource function to Magnum resources (8) @@ -1426,7 +1439,7 @@ * Convergence: Fix for finding best existing res * Use public discover_extensions from novaclient * Prepare SignalResponder class for more signal types -* Fix validation error for parameter group +* Fix validation error for parameter group * Convergence: Correct the class name EngineListener * Remove default of port_security_enabled * Update logging according oslo.i18n recommendations @@ -4843,7 +4856,7 @@ * Fix BaseException.message DeprecationWarning * Fix one of the last stack.state's -> status * Revert "check content type in JSONRequestDeserializer" -* Fix and enable H303 and F403 No wildcard (*) import +* Fix and enable H303 and F403 No wildcard (*) import * Implement attribute schema for resources * Introduce a schema for attributes * Restore babel setup config options @@ -5414,6 +5427,10 @@ * Add auth middleware for custom cloud backend * Add configurable cloud backend * Trivial commit to make daily rpm builds work again + +grizzly-2 +--------- + * heat engine : DBInstance don't pass credentials to cfn-init * heat engine : LoadBalancer resource delete nested stack * heat engine : DBInstance fix so nested stack is deleted @@ -5676,6 +5693,10 @@ * heat engine : kill running greenthreads on stack_delete * heat engine : Store all resource states to DB * Bump version to v8 + +v7-branch-eol +------------- + * heat engine : Make Resource::swift handle auth_token * Cleanup runcmd to exit 0 and not use 'type' * ReST API: Add a keystone endpoint @@ -5759,6 +5780,10 @@ * Unit test coverage for user resources, plus some user fixes * Change file to executable to get rid of rpmlint warning * Version 6 about to be announced for release, time for v7 + +v6.release +---------- + * heat : HA functional test missing import * heat tests : convert HA test to exec_sudo_command * heat tests : Fixup IHA functional test @@ -5950,6 +5975,10 @@ * heat engine : raise appropriate error for incorrect stack_name * heat API : Return correct AWS error response for invalid parameter * Bump to v6 + +v5.release +---------- + * heat engine : Avoid writing to class-scope parameters schema * Update getting started with credential crypto * Add tools directory to python package @@ -6068,6 +6097,10 @@ * Tidy up create timeout code * Get rid of _wrap_db_error() * Bump to v5 + +v4.release +---------- + * Store the user parameters in the stack table * heat GettingStarted guide, add oz install instructions * Add nested templates that implement a simple proxy/loadbalancer @@ -6235,6 +6268,10 @@ * Updated the version to 4 * Make 'heat help' show a list of commands * Allow the Getting Started script to run non-interactively + +v3.release +---------- + * Fix pep8 errors * Set cfn-hup to send events to the metadata server * Slim down the getting_started template @@ -6344,7 +6381,7 @@ * removing import of common config because it needs credentials * Adding the nova.exeption import to fix the tree * Add file support to cfn-init -* Adding instance delete test +* Adding instance delete test * Log when cfn-hup couldn't restart a service * putting python-novaclient back in pip requires * Removing nova from pip requires @@ -6375,6 +6412,10 @@ * Move common cfn code into cfn_helper.py * Fix more pep8 errors * Update version to v3 + +v2-M1.release +------------- + * Wordpress 2 instance with EBS and EIP * heat cli : Add bash_completion.d entry for heat * heat cli: Use python logging module @@ -6385,7 +6426,7 @@ * Use v1 / v2 / v3 for version numbers to match tags * Set proper author and mailing list in setup.py * Remove BUILDING.rst since there is no building taking place inside project -* Remove rpm spec file and Makefile. Will track packaging in a different repo +* Remove rpm spec file and Makefile. Will track packaging in a different repo * Remove tito - likely track this in a different repo * Fix the EIP cleanup * Add documentation for the per command CLI help options @@ -6528,11 +6569,11 @@ * Fix describe and delete * Pass the parameters to the parser * Remove some more unused files -* Don't need these files anymore. As per the layout in nova compute the manager is handling all the engine api calls +* Don't need these files anymore. As per the layout in nova compute the manager is handling all the engine api calls * Fix --template-file * Use some evil to get the jeos tdl path * Fix stack_delete() -* stack_db attributes seem broken. Comment these out for now and the API is generally working +* stack_db attributes seem broken. Comment these out for now and the API is generally working * Fix create a bit more * Hook up RPC methods * Pre-bake cftools into an image @@ -6564,8 +6605,8 @@ * Show how to create a network after using the openstack tools script * Remove errant debugging print from run-parser.py * Update to setup directions -* Stack list now communicating end to end. Time to hook some stuff up -* Change default exchange to heat-engine from nova. Hook up 'list' though it doesn't work yet +* Stack list now communicating end to end. Time to hook some stuff up +* Change default exchange to heat-engine from nova. Hook up 'list' though it doesn't work yet * Make create Working with recent glance mysql changes in F16/F17 RPMs * Add mysql db creation for glance * Fix run_parser.py to work with the db updates @@ -6600,7 +6641,7 @@ * Fix order of parmeters issue * Fix heat jeos_create F16 x86_64 litters files * Remove unneeded deps -* Fix up some imports so they work. I think this is right now. :) +* Fix up some imports so they work. I think this is right now. :) * Add README to tools directory to help point out what files do * Initial work on migrating heat-engine to rpc * Add error checking and help to tool @@ -6616,7 +6657,7 @@ * install/erase working better * Add a openstack helper install script * Add elastic ip association to parser -* A few cleanups and some comments. Nothing major +* A few cleanups and some comments. Nothing major * Use keystone auth environment variables * Improved "test" parser for python orchestration * Record events and retrieve them via "heat events_list " diff -Nru heat-6.1.0/debian/changelog heat-6.1.1/debian/changelog --- heat-6.1.0/debian/changelog 2016-09-14 11:53:36.000000000 +0000 +++ heat-6.1.1/debian/changelog 2017-02-27 16:14:03.000000000 +0000 @@ -1,3 +1,9 @@ +heat (1:6.1.1-0ubuntu1) xenial; urgency=medium + + * New upstream stable point release for OpenStack Mitaka (LP: #1668313) + + -- Chuck Short Mon, 27 Feb 2017 11:14:03 -0500 + heat (1:6.1.0-0ubuntu1) xenial; urgency=medium [ James Page ] diff -Nru heat-6.1.0/heat/common/urlfetch.py heat-6.1.1/heat/common/urlfetch.py --- heat-6.1.0/heat/common/urlfetch.py 2016-09-06 19:20:05.000000000 +0000 +++ heat-6.1.1/heat/common/urlfetch.py 2017-01-31 20:55:14.000000000 +0000 @@ -75,4 +75,5 @@ return result except exceptions.RequestException as ex: - raise URLFetchError(_('Failed to retrieve template: %s') % ex) + LOG.info(_LI('Failed to retrieve template: %s') % ex) + raise URLFetchError(_('Failed to retrieve template from %s') % url) diff -Nru heat-6.1.0/heat/engine/clients/os/keystone.py heat-6.1.1/heat/engine/clients/os/keystone.py --- heat-6.1.0/heat/engine/clients/os/keystone.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/clients/os/keystone.py 2017-01-31 20:55:15.000000000 +0000 @@ -11,7 +11,7 @@ # License for the specific language governing permissions and limitations # under the License. -from keystoneclient import exceptions +from keystoneauth1 import exceptions as ks_exceptions from heat.common import exception from heat.common import heat_keystoneclient as hkc @@ -23,7 +23,7 @@ class KeystoneClientPlugin(client_plugin.ClientPlugin): - exceptions_module = exceptions + exceptions_module = [ks_exceptions, exception] service_types = [IDENTITY] = ['identity'] @@ -31,19 +31,20 @@ return hkc.KeystoneClient(self.context) def is_not_found(self, ex): - return isinstance(ex, exceptions.NotFound) + return isinstance(ex, (ks_exceptions.NotFound, + exception.EntityNotFound)) def is_over_limit(self, ex): - return isinstance(ex, exceptions.RequestEntityTooLarge) + return isinstance(ex, ks_exceptions.RequestEntityTooLarge) def is_conflict(self, ex): - return isinstance(ex, exceptions.Conflict) + return isinstance(ex, ks_exceptions.Conflict) def get_role_id(self, role): try: role_obj = self.client().client.roles.get(role) return role_obj.id - except exceptions.NotFound: + except ks_exceptions.NotFound: role_list = self.client().client.roles.list(name=role) for role_obj in role_list: if role_obj.name == role: @@ -57,7 +58,7 @@ try: project_obj = self.client().client.projects.get(project) return project_obj.id - except exceptions.NotFound: + except ks_exceptions.NotFound: project_list = self.client().client.projects.list(name=project) for project_obj in project_list: if project_obj.name == project: @@ -72,7 +73,7 @@ try: domain_obj = self.client().client.domains.get(domain) return domain_obj.id - except exceptions.NotFound: + except ks_exceptions.NotFound: domain_list = self.client().client.domains.list(name=domain) for domain_obj in domain_list: if domain_obj.name == domain: @@ -86,7 +87,7 @@ try: group_obj = self.client().client.groups.get(group) return group_obj.id - except exceptions.NotFound: + except ks_exceptions.NotFound: group_list = self.client().client.groups.list(name=group) for group_obj in group_list: if group_obj.name == group: @@ -100,7 +101,7 @@ try: service_obj = self.client().client.services.get(service) return service_obj.id - except exceptions.NotFound: + except ks_exceptions.NotFound: service_list = self.client().client.services.list(name=service) if len(service_list) == 1: @@ -117,7 +118,7 @@ try: user_obj = self.client().client.users.get(user) return user_obj.id - except exceptions.NotFound: + except ks_exceptions.NotFound: user_list = self.client().client.users.list(name=user) for user_obj in user_list: if user_obj.name == user: @@ -129,7 +130,7 @@ try: region_obj = self.client().client.regions.get(region) return region_obj.id - except exceptions.NotFound: + except ks_exceptions.NotFound: raise exception.EntityNotFound(entity='KeystoneRegion', name=region) diff -Nru heat-6.1.0/heat/engine/clients/os/nova.py heat-6.1.1/heat/engine/clients/os/nova.py --- heat-6.1.0/heat/engine/clients/os/nova.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/clients/os/nova.py 2017-01-31 20:55:15.000000000 +0000 @@ -311,7 +311,15 @@ subtype = os.path.splitext(filename)[0] if content is None: content = '' - msg = text.MIMEText(content, _subtype=subtype) + + try: + content.encode('us-ascii') + charset = 'us-ascii' + except UnicodeEncodeError: + charset = 'utf-8' + msg = (text.MIMEText(content, _subtype=subtype, _charset=charset) + if subtype else text.MIMEText(content, _charset=charset)) + msg.add_header('Content-Disposition', 'attachment', filename=filename) return msg @@ -496,12 +504,13 @@ result=msg, resource_status=status) def rebuild(self, server_id, image_id, password=None, - preserve_ephemeral=False): + preserve_ephemeral=False, meta=None, files=None): """Rebuild the server and call check_rebuild to verify.""" server = self.fetch_server(server_id) if server: server.rebuild(image_id, password=password, - preserve_ephemeral=preserve_ephemeral) + preserve_ephemeral=preserve_ephemeral, + meta=meta, files=files) return True else: return False diff -Nru heat-6.1.0/heat/engine/resource.py heat-6.1.1/heat/engine/resource.py --- heat-6.1.0/heat/engine/resource.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/resource.py 2017-01-31 20:55:15.000000000 +0000 @@ -402,6 +402,16 @@ db_res.update_metadata(metadata) self._rsrc_metadata = metadata + def handle_metadata_reset(self): + """Default implementation; should be overridden by resources. + + Now we override this method to reset the metadata for scale-policy + and scale-group resources, because their metadata might hang in a + wrong state ('scaling_in_progress' is always True) if engine restarts + while scaling. + """ + pass + @classmethod def set_needed_by(cls, db_rsrc, needed_by, expected_engine_id=None): if db_rsrc: diff -Nru heat-6.1.0/heat/engine/resources/openstack/heat/multi_part.py heat-6.1.1/heat/engine/resources/openstack/heat/multi_part.py --- heat-6.1.0/heat/engine/resources/openstack/heat/multi_part.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/resources/openstack/heat/multi_part.py 2017-01-31 20:55:15.000000000 +0000 @@ -154,8 +154,15 @@ @staticmethod def _create_message(part, subtype, filename): - msg = (text.MIMEText(part, _subtype=subtype) - if subtype else text.MIMEText(part)) + charset = 'us-ascii' + try: + part.encode(charset) + except UnicodeEncodeError: + charset = 'utf-8' + msg = (text.MIMEText(part, _subtype=subtype, + _charset=charset) + if subtype else text.MIMEText(part, _charset=charset)) + if filename: msg.add_header('Content-Disposition', 'attachment', filename=filename) diff -Nru heat-6.1.0/heat/engine/resources/openstack/keystone/role_assignments.py heat-6.1.1/heat/engine/resources/openstack/keystone/role_assignments.py --- heat-6.1.0/heat/engine/resources/openstack/keystone/role_assignments.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/resources/openstack/keystone/role_assignments.py 2017-01-31 20:55:15.000000000 +0000 @@ -350,8 +350,12 @@ @property def user_id(self): - return (self.client_plugin().get_user_id( - self.properties.get(self.USER))) + try: + return self.client_plugin().get_user_id( + self.properties.get(self.USER)) + except Exception as ex: + self.client_plugin().ignore_not_found(ex) + return None def handle_create(self): self.create_assignment(user_id=self.user_id) @@ -407,8 +411,12 @@ @property def group_id(self): - return (self.client_plugin().get_group_id( - self.properties.get(self.GROUP))) + try: + return self.client_plugin().get_group_id( + self.properties.get(self.GROUP)) + except Exception as ex: + self.client_plugin().ignore_not_found(ex) + return None def handle_create(self): self.create_assignment(group_id=self.group_id) diff -Nru heat-6.1.0/heat/engine/resources/openstack/nova/server.py heat-6.1.1/heat/engine/resources/openstack/nova/server.py --- heat-6.1.0/heat/engine/resources/openstack/nova/server.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/resources/openstack/nova/server.py 2017-01-31 20:55:15.000000000 +0000 @@ -1081,13 +1081,22 @@ image_update_policy = ( prop_diff.get(self.IMAGE_UPDATE_POLICY) or self.properties[self.IMAGE_UPDATE_POLICY]) + instance_meta = prop_diff.get(self.METADATA, + self.properties[self.METADATA]) + + if instance_meta is not None: + instance_meta = self.client_plugin().meta_serialize(instance_meta) + personality_files = self.properties[self.PERSONALITY] + image = prop_diff[self.IMAGE] preserve_ephemeral = ( image_update_policy == 'REBUILD_PRESERVE_EPHEMERAL') password = (prop_diff.get(self.ADMIN_PASS) or self.properties[self.ADMIN_PASS]) kwargs = {'password': password, - 'preserve_ephemeral': preserve_ephemeral} + 'preserve_ephemeral': preserve_ephemeral, + 'meta': instance_meta, + 'files': personality_files} prg = progress.ServerUpdateProgress(self.resource_id, 'rebuild', handler_extra={'args': (image,), diff -Nru heat-6.1.0/heat/engine/resources/stack_resource.py heat-6.1.1/heat/engine/resources/stack_resource.py --- heat-6.1.0/heat/engine/resources/stack_resource.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/resources/stack_resource.py 2017-01-31 20:55:15.000000000 +0000 @@ -454,9 +454,10 @@ if self.abandon_in_progress: self.rpc_client().abandon_stack(self.context, stack_identity) else: - self.rpc_client().delete_stack(self.context, stack_identity) + self.rpc_client().delete_stack(self.context, stack_identity, + cast=False) except Exception as ex: - self.rpc_client().ignore_error_named(ex, 'NotFound') + self.rpc_client().ignore_error_named(ex, 'EntityNotFound') def handle_delete(self): return self.delete_nested() diff -Nru heat-6.1.0/heat/engine/service.py heat-6.1.1/heat/engine/service.py --- heat-6.1.0/heat/engine/service.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/engine/service.py 2017-01-31 20:55:15.000000000 +0000 @@ -79,6 +79,10 @@ cfg.CONF.import_opt('enable_stack_adopt', 'heat.common.config') cfg.CONF.import_opt('convergence_engine', 'heat.common.config') +# Time to wait for a stack to stop when cancelling running threads, before +# giving up on being able to start a delete. +STOP_STACK_TIMEOUT = 30 + LOG = logging.getLogger(__name__) @@ -1121,7 +1125,8 @@ # Another active engine has the lock elif stack_lock.StackLock.engine_alive(cnxt, engine_id): cancel_result = self._remote_call( - cnxt, engine_id, self.listener.SEND, + cnxt, engine_id, cfg.CONF.engine_life_check_timeout, + self.listener.SEND, stack_identity=stack_identity, message=cancel_message) if cancel_result is None: LOG.debug("Successfully sent %(msg)s message " @@ -1286,8 +1291,7 @@ return api.format_stack_output(stack, {output_key: output}, output_key) - def _remote_call(self, cnxt, lock_engine_id, call, **kwargs): - timeout = cfg.CONF.engine_life_check_timeout + def _remote_call(self, cnxt, lock_engine_id, timeout, call, **kwargs): self.cctxt = self._client.prepare( version='1.0', timeout=timeout, @@ -1337,7 +1341,8 @@ # Another active engine has the lock elif stack_lock.StackLock.engine_alive(cnxt, acquire_result): stop_result = self._remote_call( - cnxt, acquire_result, self.listener.STOP_STACK, + cnxt, acquire_result, STOP_STACK_TIMEOUT, + self.listener.STOP_STACK, stack_identity=stack_identity) if stop_result is None: LOG.debug("Successfully stopped remote task on engine %s" @@ -1704,6 +1709,7 @@ if mark_unhealthy: rsrc.state_set(rsrc.CHECK, rsrc.FAILED, reason=reason) elif rsrc.state == (rsrc.CHECK, rsrc.FAILED): + rsrc.handle_metadata_reset() rsrc.state_set(rsrc.CHECK, rsrc.COMPLETE, reason=reason) except exception.UpdateInProgress: diff -Nru heat-6.1.0/heat/scaling/cooldown.py heat-6.1.1/heat/scaling/cooldown.py --- heat-6.1.0/heat/scaling/cooldown.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/scaling/cooldown.py 2017-01-31 20:55:15.000000000 +0000 @@ -66,3 +66,9 @@ metadata['cooldown'] = {now: cooldown_reason} metadata['scaling_in_progress'] = False self.metadata_set(metadata) + + def handle_metadata_reset(self): + metadata = self.metadata_get() + if 'scaling_in_progress' in metadata: + metadata['scaling_in_progress'] = False + self.metadata_set(metadata) diff -Nru heat-6.1.0/heat/tests/clients/test_clients.py heat-6.1.1/heat/tests/clients/test_clients.py --- heat-6.1.0/heat/tests/clients/test_clients.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/tests/clients/test_clients.py 2017-01-31 20:55:15.000000000 +0000 @@ -594,6 +594,14 @@ plugin='keystone', exception=lambda: keystone_exc.NotFound(details='gone'), )), + ('keystone_entity_not_found', dict( + is_not_found=True, + is_over_limit=False, + is_client_exception=True, + is_conflict=False, + plugin='keystone', + exception=lambda: exception.EntityNotFound(), + )), ('keystone_exception', dict( is_not_found=False, is_over_limit=False, diff -Nru heat-6.1.0/heat/tests/engine/service/test_stack_delete.py heat-6.1.1/heat/tests/engine/service/test_stack_delete.py --- heat-6.1.0/heat/tests/engine/service/test_stack_delete.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/tests/engine/service/test_stack_delete.py 2017-01-31 20:55:15.000000000 +0000 @@ -152,7 +152,8 @@ mock_load.assert_called_once_with(self.ctx, stack=st) mock_try.assert_called_once_with() mock_alive.assert_called_once_with(self.ctx, OTHER_ENGINE) - mock_call.assert_called_once_with(self.ctx, OTHER_ENGINE, "stop_stack", + mock_call.assert_called_once_with(self.ctx, OTHER_ENGINE, mock.ANY, + "stop_stack", stack_identity=mock.ANY) @mock.patch.object(parser.Stack, 'load') @@ -185,7 +186,8 @@ mock_load.assert_called_with(self.ctx, stack=st) mock_try.assert_called_once_with() mock_alive.assert_called_once_with(self.ctx, OTHER_ENGINE) - mock_call.assert_called_once_with(self.ctx, OTHER_ENGINE, "stop_stack", + mock_call.assert_called_once_with(self.ctx, OTHER_ENGINE, mock.ANY, + "stop_stack", stack_identity=mock.ANY) mock_acquire.assert_called_once_with(True) diff -Nru heat-6.1.0/heat/tests/engine/service/test_stack_resources.py heat-6.1.1/heat/tests/engine/service/test_stack_resources.py --- heat-6.1.0/heat/tests/engine/service/test_stack_resources.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/tests/engine/service/test_stack_resources.py 2017-01-31 20:55:15.000000000 +0000 @@ -20,6 +20,7 @@ from heat.engine.clients.os import keystone from heat.engine import dependencies from heat.engine import resource as res +from heat.engine.resources.aws.ec2 import instance as ins from heat.engine import service from heat.engine import stack from heat.engine import stack_lock @@ -541,82 +542,77 @@ self.assertIsInstance(stack_dependencies, dependencies.Dependencies) self.assertEqual(2, len(stack_dependencies.graph())) + def _test_mark_healthy_asserts(self, action='CHECK', status='FAILED', + reason='state changed', meta=None): + rs = self.eng.describe_stack_resource( + self.ctx, self.stack.identifier(), + 'WebServer', with_attr=None) + self.assertIn('resource_action', rs) + self.assertIn('resource_status', rs) + self.assertIn('resource_status_reason', rs) + + self.assertEqual(action, rs['resource_action']) + self.assertEqual(status, rs['resource_status']) + self.assertEqual(reason, rs['resource_status_reason']) + if meta is not None: + self.assertIn('metadata', rs) + self.assertEqual(meta, rs['metadata']) + @tools.stack_context('service_mark_healthy_create_complete_test_stk') def test_mark_healthy_in_create_complete(self): self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', False, resource_status_reason='noop') - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) - self.assertIn('resource_action', r) - self.assertIn('resource_status', r) - self.assertIn('resource_status_reason', r) - - self.assertEqual(r['resource_action'], 'CREATE') - self.assertEqual(r['resource_status'], 'COMPLETE') - self.assertEqual(r['resource_status_reason'], 'state changed') + self._test_mark_healthy_asserts(action='CREATE', + status='COMPLETE') @tools.stack_context('service_mark_unhealthy_create_complete_test_stk') def test_mark_unhealthy_in_create_complete(self): + + reason = 'Some Reason' self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', True, - resource_status_reason='Some Reason') + resource_status_reason=reason) - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) - - self.assertEqual(r['resource_action'], 'CHECK') - self.assertEqual(r['resource_status'], 'FAILED') - self.assertEqual(r['resource_status_reason'], 'Some Reason') + self._test_mark_healthy_asserts(reason=reason) @tools.stack_context('service_mark_healthy_check_failed_test_stk') def test_mark_healthy_check_failed(self): + reason = 'Some Reason' self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', True, - resource_status_reason='Some Reason') + resource_status_reason=reason) + self._test_mark_healthy_asserts(reason=reason) - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) + meta = {'for_test': True} + + def override_metadata_reset(rsrc): + rsrc.metadata_set(meta) - self.assertEqual(r['resource_action'], 'CHECK') - self.assertEqual(r['resource_status'], 'FAILED') - self.assertEqual(r['resource_status_reason'], 'Some Reason') + ins.Instance.handle_metadata_reset = override_metadata_reset + reason = 'Good Reason' self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', False, - resource_status_reason='Good Reason') - - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) - - self.assertEqual(r['resource_action'], 'CHECK') - self.assertEqual(r['resource_status'], 'COMPLETE') - self.assertEqual(r['resource_status_reason'], 'Good Reason') + resource_status_reason=reason) + self._test_mark_healthy_asserts(status='COMPLETE', + reason=reason, + meta=meta) @tools.stack_context('service_mark_unhealthy_check_failed_test_stack') def test_mark_unhealthy_check_failed(self): + reason = 'Some Reason' self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', True, - resource_status_reason='Some Reason') - - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) - - self.assertEqual(r['resource_action'], 'CHECK') - self.assertEqual(r['resource_status'], 'FAILED') - self.assertEqual(r['resource_status_reason'], 'Some Reason') + resource_status_reason=reason) + self._test_mark_healthy_asserts(reason=reason) + new_reason = 'New Reason' self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', True, - resource_status_reason='New Reason') - - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) - - self.assertEqual(r['resource_action'], 'CHECK') - self.assertEqual(r['resource_status'], 'FAILED') - self.assertEqual(r['resource_status_reason'], 'New Reason') + resource_status_reason=new_reason) + self._test_mark_healthy_asserts(reason=new_reason) @tools.stack_context('service_mark_unhealthy_invalid_value_test_stk') def test_mark_unhealthy_invalid_value(self): @@ -632,28 +628,16 @@ def test_mark_unhealthy_none_reason(self): self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', True) - - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) - - self.assertEqual(r['resource_action'], 'CHECK') - self.assertEqual(r['resource_status'], 'FAILED') - self.assertEqual(r['resource_status_reason'], - 'state changed by resource_mark_unhealthy api') + default_reason = 'state changed by resource_mark_unhealthy api' + self._test_mark_healthy_asserts(reason=default_reason) @tools.stack_context('service_mark_unhealthy_empty_reason_test_stk') def test_mark_unhealthy_empty_reason(self): self.eng.resource_mark_unhealthy(self.ctx, self.stack.identifier(), 'WebServer', True, resource_status_reason="") - - r = self.eng.describe_stack_resource(self.ctx, self.stack.identifier(), - 'WebServer', with_attr=None) - - self.assertEqual(r['resource_action'], 'CHECK') - self.assertEqual(r['resource_status'], 'FAILED') - self.assertEqual(r['resource_status_reason'], - 'state changed by resource_mark_unhealthy api') + default_reason = 'state changed by resource_mark_unhealthy api' + self._test_mark_healthy_asserts(reason=default_reason) @tools.stack_context('service_mark_unhealthy_lock_no_converge_test_stk') def test_mark_unhealthy_lock_no_convergence(self): diff -Nru heat-6.1.0/heat/tests/openstack/keystone/test_role_assignments.py heat-6.1.1/heat/tests/openstack/keystone/test_role_assignments.py --- heat-6.1.0/heat/tests/openstack/keystone/test_role_assignments.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/tests/openstack/keystone/test_role_assignments.py 2017-01-31 20:55:15.000000000 +0000 @@ -535,6 +535,13 @@ user='user_1', project='project_1') + def test_user_role_assignment_delete_user_not_found(self): + self.test_role_assignment._stored_properties_data = {} + self.keystone_client_plugin.get_user_id.side_effect = [ + exception.EntityNotFound] + self.assertIsNone(self.test_role_assignment.handle_delete()) + self.roles.revoke.assert_not_called() + class KeystoneGroupRoleAssignmentTest(common.HeatTestCase): @@ -673,3 +680,10 @@ role='role_1', group='group_1', project='project_1') + + def test_group_role_assignment_delete_group_not_found(self): + self.test_role_assignment._stored_properties_data = {} + self.keystone_client_plugin.get_group_id.side_effect = [ + exception.EntityNotFound] + self.assertIsNone(self.test_role_assignment.handle_delete()) + self.roles.revoke.assert_not_called() diff -Nru heat-6.1.0/heat/tests/openstack/nova/test_server.py heat-6.1.1/heat/tests/openstack/nova/test_server.py --- heat-6.1.0/heat/tests/openstack/nova/test_server.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/tests/openstack/nova/test_server.py 2017-01-31 20:55:15.000000000 +0000 @@ -1854,11 +1854,13 @@ if 'REBUILD' == policy: mock_rebuild.assert_called_once_with( return_server, '2', password=password, - preserve_ephemeral=False) + preserve_ephemeral=False, + meta=None, files={}) else: mock_rebuild.assert_called_once_with( return_server, '2', password=password, - preserve_ephemeral=True) + preserve_ephemeral=True, + meta=None, files={}) def test_server_update_image_rebuild_status_rebuild(self): # Normally we will see 'REBUILD' first and then 'ACTIVE". @@ -1915,7 +1917,8 @@ six.text_type(error)) self.assertEqual((server.UPDATE, server.FAILED), server.state) mock_rebuild.assert_called_once_with( - return_server, '2', password=None, preserve_ephemeral=False) + return_server, '2', password=None, preserve_ephemeral=False, + meta=None, files={}) def test_server_update_properties(self): return_server = self.fc.servers.list()[1] diff -Nru heat-6.1.0/heat/tests/test_nested_stack.py heat-6.1.1/heat/tests/test_nested_stack.py --- heat-6.1.0/heat/tests/test_nested_stack.py 2016-09-06 19:20:05.000000000 +0000 +++ heat-6.1.1/heat/tests/test_nested_stack.py 2017-01-31 20:55:15.000000000 +0000 @@ -413,4 +413,4 @@ self.res.nested().identifier.return_value = stack_identity self.res.handle_delete() self.res.rpc_client.return_value.delete_stack.assert_called_once_with( - self.ctx, self.res.nested().identifier()) + self.ctx, self.res.nested().identifier(), cast=False) diff -Nru heat-6.1.0/heat/tests/test_provider_template.py heat-6.1.1/heat/tests/test_provider_template.py --- heat-6.1.0/heat/tests/test_provider_template.py 2016-09-06 19:20:05.000000000 +0000 +++ heat-6.1.1/heat/tests/test_provider_template.py 2017-01-31 20:55:15.000000000 +0000 @@ -1021,4 +1021,5 @@ rpcc = self.res.rpc_client.return_value rpcc.delete_stack.assert_called_once_with( self.ctx, - self.res.nested().identifier()) + self.res.nested().identifier(), + cast=False) diff -Nru heat-6.1.0/heat/tests/test_stack_resource.py heat-6.1.1/heat/tests/test_stack_resource.py --- heat-6.1.0/heat/tests/test_stack_resource.py 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat/tests/test_stack_resource.py 2017-01-31 20:55:15.000000000 +0000 @@ -516,7 +516,7 @@ side_effect=exception.NotFound()) self.assertIsNone(self.parent_resource.delete_nested()) rpcc.return_value.delete_stack.assert_called_once_with( - self.parent_resource.context, mock.ANY) + self.parent_resource.context, mock.ANY, cast=False) def test_need_update_for_nested_resource(self): """Test the resource with nested stack should need update. diff -Nru heat-6.1.0/heat.egg-info/pbr.json heat-6.1.1/heat.egg-info/pbr.json --- heat-6.1.0/heat.egg-info/pbr.json 2016-09-06 19:23:38.000000000 +0000 +++ heat-6.1.1/heat.egg-info/pbr.json 2017-01-31 20:57:05.000000000 +0000 @@ -1 +1 @@ -{"git_version": "ed46562", "is_release": true} \ No newline at end of file +{"is_release": true, "git_version": "32fefb7"} \ No newline at end of file diff -Nru heat-6.1.0/heat.egg-info/PKG-INFO heat-6.1.1/heat.egg-info/PKG-INFO --- heat-6.1.0/heat.egg-info/PKG-INFO 2016-09-06 19:23:38.000000000 +0000 +++ heat-6.1.1/heat.egg-info/PKG-INFO 2017-01-31 20:57:05.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: heat -Version: 6.1.0 +Version: 6.1.1 Summary: OpenStack Orchestration Home-page: http://www.openstack.org/ Author: OpenStack diff -Nru heat-6.1.0/heat.egg-info/requires.txt heat-6.1.1/heat.egg-info/requires.txt --- heat-6.1.0/heat.egg-info/requires.txt 2016-09-06 19:23:38.000000000 +0000 +++ heat-6.1.1/heat.egg-info/requires.txt 2017-01-31 20:57:05.000000000 +0000 @@ -48,7 +48,6 @@ PyYAML>=3.1.0 requests!=2.9.0,>=2.8.1 retrying!=1.3.0,>=1.2.3 -Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3 six>=1.9.0 SQLAlchemy<1.1.0,>=1.0.10 sqlalchemy-migrate>=0.9.6 @@ -57,3 +56,6 @@ [:(python_version!='2.7')] Routes!=2.0,!=2.3.0,>=1.12.3 + +[:(python_version=='2.7')] +Routes!=2.0,!=2.1,!=2.3.0,>=1.12.3 diff -Nru heat-6.1.0/heat.egg-info/SOURCES.txt heat-6.1.1/heat.egg-info/SOURCES.txt --- heat-6.1.0/heat.egg-info/SOURCES.txt 2016-09-06 19:23:40.000000000 +0000 +++ heat-6.1.1/heat.egg-info/SOURCES.txt 2017-01-31 20:57:06.000000000 +0000 @@ -903,6 +903,7 @@ heat_integrationtests/functional/test_create_update_neutron_port.py heat_integrationtests/functional/test_create_update_neutron_subnet.py heat_integrationtests/functional/test_default_parameters.py +heat_integrationtests/functional/test_delete.py heat_integrationtests/functional/test_encrypted_parameter.py heat_integrationtests/functional/test_encryption_vol_type.py heat_integrationtests/functional/test_env_merge.py diff -Nru heat-6.1.0/heat_integrationtests/functional/test_delete.py heat-6.1.1/heat_integrationtests/functional/test_delete.py --- heat-6.1.0/heat_integrationtests/functional/test_delete.py 1970-01-01 00:00:00.000000000 +0000 +++ heat-6.1.1/heat_integrationtests/functional/test_delete.py 2017-01-31 20:55:14.000000000 +0000 @@ -0,0 +1,42 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time + +from heat_integrationtests.functional import functional_base + + +class DeleteInProgressTest(functional_base.FunctionalTestsBase): + + root_template = ''' +heat_template_version: 2013-05-23 +resources: + rg: + type: OS::Heat::ResourceGroup + properties: + count: 125 + resource_def: + type: empty.yaml +''' + + empty_template = ''' +heat_template_version: 2013-05-23 +resources: +''' + + def test_delete_nested_stacks_create_in_progress(self): + files = {'empty.yaml': self.empty_template} + identifier = self.stack_create(template=self.root_template, + files=files, + expected_status='CREATE_IN_PROGRESS') + time.sleep(20) + self._stack_delete(identifier) diff -Nru heat-6.1.0/heat_integrationtests/prepare_test_env.sh heat-6.1.1/heat_integrationtests/prepare_test_env.sh --- heat-6.1.0/heat_integrationtests/prepare_test_env.sh 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/heat_integrationtests/prepare_test_env.sh 2017-01-31 20:55:15.000000000 +0000 @@ -52,7 +52,7 @@ # Skip some tests for convergence until it is fixed if [ "$ENABLE_CONVERGENCE" == "true" ] ; then - iniset heat_integrationtests.conf DEFAULT skip_functional_test_list 'StackValidationTest' + iniset heat_integrationtests.conf DEFAULT skip_functional_test_list 'StackValidationTest, DeleteInProgressTest' fi cat heat_integrationtests.conf diff -Nru heat-6.1.0/PKG-INFO heat-6.1.1/PKG-INFO --- heat-6.1.0/PKG-INFO 2016-09-06 19:23:40.000000000 +0000 +++ heat-6.1.1/PKG-INFO 2017-01-31 20:57:06.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: heat -Version: 6.1.0 +Version: 6.1.1 Summary: OpenStack Orchestration Home-page: http://www.openstack.org/ Author: OpenStack diff -Nru heat-6.1.0/setup.cfg heat-6.1.1/setup.cfg --- heat-6.1.0/setup.cfg 2016-09-06 19:23:40.000000000 +0000 +++ heat-6.1.1/setup.cfg 2017-01-31 20:57:06.000000000 +0000 @@ -184,5 +184,4 @@ [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff -Nru heat-6.1.0/test-requirements.txt heat-6.1.1/test-requirements.txt --- heat-6.1.0/test-requirements.txt 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/test-requirements.txt 2017-01-31 20:55:15.000000000 +0000 @@ -7,10 +7,10 @@ bandit>=0.17.3 # Apache-2.0 coverage>=3.6 # Apache-2.0 fixtures<2.0,>=1.3.1 # Apache-2.0/BSD -kombu>=3.0.25 # BSD +kombu!=4.0.0,!=4.0.1,>=3.0.25 # BSD mock>=1.2 # BSD mox3>=0.7.0 # Apache-2.0 -PyMySQL>=0.6.2 # MIT License +PyMySQL!=0.7.7,>=0.6.2 # MIT License oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 paramiko>=1.16.0 # LGPL diff -Nru heat-6.1.0/tox.ini heat-6.1.1/tox.ini --- heat-6.1.0/tox.ini 2016-09-06 19:20:07.000000000 +0000 +++ heat-6.1.1/tox.ini 2017-01-31 20:55:15.000000000 +0000 @@ -40,13 +40,9 @@ python tools/custom_guidelines.py --exclude heat/engine/resources/aws [testenv:venv] -# Note: infra doesn't support to use of constraints for this target. -install_command = pip install -U --force-reinstall {opts} {packages} commands = {posargs} [testenv:cover] -# Note: infra doesn't support to use of constraints for this target. -install_command = pip install -U --force-reinstall {opts} {packages} commands = python setup.py testr --coverage --testr-args='^(?!heat_integrationtests){posargs}' @@ -77,6 +73,4 @@ commands = oslo_debug_helper {posargs} [testenv:releasenotes] -# Note: infra doesn't support to use of constraints for this target. -install_command = pip install -U --force-reinstall {opts} {packages} commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html