def _create_with_remote_credential(self, credential_secret_id=None, ca_cert=None, insecure=False): t = template_format.parse(parent_stack_template) properties = t['resources']['remote_stack']['properties'] if credential_secret_id: properties['context']['credential_secret_id'] = ( credential_secret_id) if ca_cert: properties['context']['ca_cert'] = (ca_cert) if insecure: properties['context']['insecure'] = insecure t = json.dumps(t) self.patchobject(policy.Enforcer, 'check_is_admin') rsrc = self.create_remote_stack(stack_template=t) env = environment.get_child_environment(rsrc.stack.env, {'name': 'foo'}) args = { 'stack_name': rsrc.physical_resource_name(), 'template': template_format.parse(remote_template), 'timeout_mins': 60, 'disable_rollback': True, 'parameters': { 'name': 'foo' }, 'files': self.files, 'environment': env.user_env_as_dict(), } self.heat.stacks.create.assert_called_with(**args) self.assertEqual(2, len(self.heat.stacks.get.call_args_list)) rsrc.validate() return rsrc
def _create_with_remote_credential(self, credential_secret_id=None, ca_cert=None, insecure=False): t = template_format.parse(parent_stack_template) properties = t['resources']['remote_stack']['properties'] if credential_secret_id: properties['context']['credential_secret_id'] = ( credential_secret_id) if ca_cert: properties['context']['ca_cert'] = ( ca_cert) if insecure: properties['context']['insecure'] = insecure t = json.dumps(t) self.patchobject(policy.Enforcer, 'check_is_admin') rsrc = self.create_remote_stack(stack_template=t) env = environment.get_child_environment(rsrc.stack.env, {'name': 'foo'}) args = { 'stack_name': rsrc.physical_resource_name(), 'template': template_format.parse(remote_template), 'timeout_mins': 60, 'disable_rollback': True, 'parameters': {'name': 'foo'}, 'files': self.files, 'environment': env.user_env_as_dict(), } self.heat.stacks.create.assert_called_with(**args) self.assertEqual(2, len(self.heat.stacks.get.call_args_list)) rsrc.validate() return rsrc
def _create_template(self, num_instances, num_replace=0, template_version=('HeatTemplateFormatVersion', '2012-12-12')): """Create a template to represent autoscaled instances. Also see heat.scaling.template.member_definitions. """ instance_definition = self._get_resource_definition() old_resources = grouputils.get_member_definitions(self, include_failed=True) definitions = list(template.member_definitions( old_resources, instance_definition, num_instances, num_replace, short_id.generate_id)) child_env = environment.get_child_environment( self.stack.env, self.child_params(), item_to_remove=self.resource_info) tmpl = template.make_template(definitions, version=template_version, child_env=child_env) # Subclasses use HOT templates att_func, res_func = 'get_attr', 'get_resource' if att_func not in tmpl.functions or res_func not in tmpl.functions: att_func, res_func = 'Fn::GetAtt', 'Ref' get_attr = functools.partial(tmpl.functions[att_func], None, att_func) get_res = functools.partial(tmpl.functions[res_func], None, res_func) for odefn in self._nested_output_defns([k for k, d in definitions], get_attr, get_res): tmpl.add_output(odefn) return tmpl
def _stack_kwargs(self, user_params, child_template, adopt_data=None): if user_params is None: user_params = self.child_params() if child_template is None: child_template = self.child_template() child_env = environment.get_child_environment( self.stack.env, user_params, child_resource_name=self.name, item_to_remove=self.resource_info) parsed_template = self._child_parsed_template(child_template, child_env) if adopt_data is None: template_id = parsed_template.store(self.context) return { 'template_id': template_id, 'template': None, 'params': None, 'files': None, } else: return { 'template': parsed_template.t, 'params': child_env.user_env_as_dict(), 'files': parsed_template.files, }
def test_drill_down_prefer_exact_match(self): env = { u'resource_registry': { u'resources': { u'*esource': { u'hooks': 'pre-create', }, u'res*': { u'hooks': 'pre-create', }, u'resource': { u'OS::Fruit': u'carrots.yaml', u'hooks': 'pre-update', }, u'resource*': { u'hooks': 'pre-create', }, u'*resource': { u'hooks': 'pre-create', }, u'*sour*': { u'hooks': 'pre-create', }, } } } penv = environment.Environment(env) cenv = environment.get_child_environment( penv, None, child_resource_name=u'resource') registry = cenv.user_env_as_dict()['resource_registry'] resources = registry['resources'] self.assertEqual(u'carrots.yaml', resources[u'OS::Fruit']) self.assertEqual('pre-update', resources[u'hooks'])
def test_drill_down_matching_wildcard(self): env = { u'resource_registry': { u'resources': { u'nested': { u'c': { u'OS::Fruit': u'carrots.yaml', u'hooks': 'pre-create', }, }, u'nest*': { u'nested_res': { u'hooks': 'pre-create', }, } } } } penv = environment.Environment(env) cenv = environment.get_child_environment(penv, None, child_resource_name=u'nested') registry = cenv.user_env_as_dict()['resource_registry'] resources = registry['resources'] self.assertIn('c', resources) self.assertIn('nested_res', resources) res = cenv.get_resource_info('OS::Fruit', resource_name='c') self.assertIsNotNone(res) self.assertEqual(u'carrots.yaml', res.value)
def test_drill_down_to_child_resource(self): env = { u'resource_registry': { u'OS::Food': u'fruity.yaml', u'resources': { u'a': { u'OS::Fruit': u'apples.yaml', u'hooks': 'pre-create', }, u'nested': { u'b': { u'OS::Fruit': u'carrots.yaml', }, u'nested_res': { u'hooks': 'pre-create', } } } } } penv = environment.Environment(env) cenv = environment.get_child_environment(penv, None, child_resource_name=u'nested') registry = cenv.user_env_as_dict()['resource_registry'] resources = registry['resources'] self.assertIn('nested_res', resources) self.assertIn('hooks', resources['nested_res']) self.assertIsNotNone( cenv.get_resource_info('OS::Food', resource_name='abc')) self.assertIsNone( cenv.get_resource_info('OS::Fruit', resource_name='a')) res = cenv.get_resource_info('OS::Fruit', resource_name='b') self.assertIsNotNone(res) self.assertEqual(u'carrots.yaml', res.value)
def _create_template(self, num_instances, num_replace=0, template_version=('HeatTemplateFormatVersion', '2012-12-12')): """Create a template to represent autoscaled instances. Also see heat.scaling.template.member_definitions. """ instance_definition = self._get_resource_definition() old_resources = grouputils.get_member_definitions(self, include_failed=True) definitions = list(template.member_definitions( old_resources, instance_definition, num_instances, num_replace, short_id.generate_id)) child_env = environment.get_child_environment( self.stack.env, self.child_params(), item_to_remove=self.resource_info) tmpl = template.make_template(definitions, version=template_version, child_env=child_env) # Subclasses use HOT templates att_func = 'get_attr' if att_func not in tmpl.functions: att_func = 'Fn::GetAtt' get_attr = functools.partial(tmpl.functions[att_func], None, att_func) for odefn in self._nested_output_defns([k for k, d in definitions], get_attr): tmpl.add_output(odefn) return tmpl
def test_registry_merge_favor_child(self): env1 = {u'resource_registry': {u'OS::Food': u'carrots.yaml'}} env2 = {u'resource_registry': {u'OS::Food': u'apples.yaml'}} penv = environment.Environment(env=env1) cenv = environment.get_child_environment(penv, env2) res = cenv.get_resource_info('OS::Food') self.assertEqual('apples.yaml', res.value)
def test_drill_down_to_child_resource(self): env = { u'resource_registry': { u'OS::Food': u'fruity.yaml', u'resources': { u'a': { u'OS::Fruit': u'apples.yaml', u'hooks': 'pre-create', }, u'nested': { u'b': { u'OS::Fruit': u'carrots.yaml', }, u'nested_res': { u'hooks': 'pre-create', } } } } } penv = environment.Environment(env) cenv = environment.get_child_environment( penv, None, child_resource_name=u'nested') registry = cenv.user_env_as_dict()['resource_registry'] resources = registry['resources'] self.assertIn('nested_res', resources) self.assertIn('hooks', resources['nested_res']) self.assertIsNotNone( cenv.get_resource_info('OS::Food', resource_name='abc')) self.assertIsNone( cenv.get_resource_info('OS::Fruit', resource_name='a')) res = cenv.get_resource_info('OS::Fruit', resource_name='b') self.assertIsNotNone(res) self.assertEqual(u'carrots.yaml', res.value)
def validate(self): super(RemoteStack, self).validate() try: self.heat() except Exception as ex: exc_info = dict(region=self._region_name, exc=six.text_type(ex)) msg = _('Cannot establish connection to Heat endpoint at region ' '"%(region)s" due to "%(exc)s"') % exc_info raise exception.StackValidationFailed(message=msg) try: params = self.properties[self.PARAMETERS] env = environment.get_child_environment(self.stack.env, params) tmpl = template_format.parse(self.properties[self.TEMPLATE]) args = { 'template': tmpl, 'files': self.stack.t.files, 'environment': env.user_env_as_dict(), } self.heat().stacks.validate(**args) except Exception as ex: exc_info = dict(region=self._region_name, exc=six.text_type(ex)) LOG.error(_LE('exception: %s'), type(ex)) msg = _('Failed validating stack template using Heat endpoint at ' 'region "%(region)s" due to "%(exc)s"') % exc_info raise exception.StackValidationFailed(message=msg)
def _create_template(self, num_instances, num_replace=0, template_version=('HeatTemplateFormatVersion', '2012-12-12')): """ Create a template to represent autoscaled instances. Also see heat.scaling.template.member_definitions. """ instance_definition = self._get_resource_definition() old_resources = self._get_instance_templates() definitions = template.member_definitions(old_resources, instance_definition, num_instances, num_replace, short_id.generate_id) child_env = environment.get_child_environment( self.stack.env, self.child_params(), item_to_remove=self.resource_info) return template.make_template(definitions, version=template_version, child_env=child_env)
def _parse_nested_stack(self, stack_name, child_template, child_params, timeout_mins=None, adopt_data=None): if timeout_mins is None: timeout_mins = self.stack.timeout_mins stack_user_project_id = self.stack.stack_user_project_id new_nested_depth = self._child_nested_depth() child_env = environment.get_child_environment( self.stack.env, child_params, child_resource_name=self.name, item_to_remove=self.resource_info) parsed_template = self._child_parsed_template(child_template, child_env) self._validate_nested_resources(parsed_template) # Note we disable rollback for nested stacks, since they # should be rolled back by the parent stack on failure nested = parser.Stack(self.context, stack_name, parsed_template, timeout_mins=timeout_mins, disable_rollback=True, parent_resource=self.name, owner_id=self.stack.id, user_creds_id=self.stack.user_creds_id, stack_user_project_id=stack_user_project_id, adopt_stack_data=adopt_data, nested_depth=new_nested_depth) nested.set_parent_stack(self.stack) return nested
def test_drill_down_matching_wildcard(self): env = { u'resource_registry': { u'resources': { u'nested': { u'c': { u'OS::Fruit': u'carrots.yaml', u'hooks': 'pre-create', }, }, u'nest*': { u'nested_res': { u'hooks': 'pre-create', }, } } } } penv = environment.Environment(env) cenv = environment.get_child_environment( penv, None, child_resource_name=u'nested') registry = cenv.user_env_as_dict()['resource_registry'] resources = registry['resources'] self.assertIn('c', resources) self.assertIn('nested_res', resources) res = cenv.get_resource_info('OS::Fruit', resource_name='c') self.assertIsNotNone(res) self.assertEqual(u'carrots.yaml', res.value)
def test_update(self): stacks = [get_stack(stack_status='UPDATE_IN_PROGRESS'), get_stack(stack_status='UPDATE_COMPLETE')] rsrc = self.create_remote_stack() props = copy.deepcopy(rsrc.parsed_template()['Properties']) props['parameters']['name'] = 'bar' update_snippet = rsrc_defn.ResourceDefinition(rsrc.name, rsrc.type(), props) self.heat.stacks.get = mock.MagicMock(side_effect=stacks) scheduler.TaskRunner(rsrc.update, update_snippet)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) self.assertEqual('bar', rsrc.properties.get('parameters')['name']) env = environment.get_child_environment(rsrc.stack.env, {'name': 'bar'}) fields = { 'stack_id': rsrc.resource_id, 'template': template_format.parse(remote_template), 'timeout_mins': 60, 'disable_rollback': True, 'parameters': {'name': 'bar'}, 'files': self.files, 'environment': env.user_env_as_dict(), } self.heat.stacks.update.assert_called_with(**fields) self.assertEqual(2, len(self.heat.stacks.get.call_args_list))
def test_item_to_remove_complex(self): env = { u'resource_registry': { u'OS::Food': u'fruity.yaml', u'resources': { u'abc': { u'OS::Food': u'nutty.yaml' } } } } penv = environment.Environment(env) # the victim we want is the most specific one. victim = penv.get_resource_info('OS::Food', resource_name='abc') self.assertEqual(['resources', 'abc', 'OS::Food'], victim.path) cenv = environment.get_child_environment(penv, None, item_to_remove=victim) res = cenv.get_resource_info('OS::Food', resource_name='abc') self.assertEqual(['OS::Food'], res.path) rr = cenv.user_env_as_dict()['resource_registry'] self.assertIn('OS::Food', rr) self.assertNotIn('OS::Food', rr['resources']['abc']) # make sure the parent env is uneffected innocent2 = penv.get_resource_info('OS::Food', resource_name='abc') self.assertEqual(['resources', 'abc', 'OS::Food'], innocent2.path)
def update_with_template(self, child_template, user_params=None, timeout_mins=None): """Update the nested stack with the new template.""" if self.id is None: self._store() nested_stack = self.nested() if nested_stack is None: # if the create failed for some reason and the nested # stack was not created, we need to create an empty stack # here so that the update will work. def _check_for_completion(creator_fn): while not self.check_create_complete(creator_fn): yield empty_temp = template_format.parse( "heat_template_version: '2013-05-23'") stack_creator = self.create_with_template(empty_temp, {}) checker = scheduler.TaskRunner(_check_for_completion, stack_creator) checker(timeout=self.stack.timeout_secs()) if stack_creator is not None: stack_creator.run_to_completion() nested_stack = self.nested() if timeout_mins is None: timeout_mins = self.stack.timeout_mins if user_params is None: user_params = self.child_params() child_env = environment.get_child_environment( self.stack.env, user_params, child_resource_name=self.name, item_to_remove=self.resource_info) parsed_template = self._child_parsed_template(child_template, child_env) cookie = { 'previous': { 'updated_at': nested_stack.updated_time, 'state': nested_stack.state } } args = {rpc_api.PARAM_TIMEOUT: timeout_mins} try: self.rpc_client().update_stack(self.context, nested_stack.identifier(), parsed_template.t, child_env.user_env_as_dict(), parsed_template.files, args) except Exception as ex: LOG.exception(_LE('update_stack')) self.raise_local_exception(ex) return cookie
def test_params_normal(self): new_params = {"parameters": {"foo": "bar", "tester": "Yes"}} penv = environment.Environment() expected = {"parameter_defaults": {}, "encrypted_param_names": [], "resource_registry": {"resources": {}}} expected.update(new_params) cenv = environment.get_child_environment(penv, new_params) self.assertEqual(expected, cenv.user_env_as_dict())
def test_update(self): stacks = [ get_stack(stack_status='UPDATE_IN_PROGRESS'), get_stack(stack_status='UPDATE_COMPLETE') ] rsrc = self.create_remote_stack() props = dict(rsrc.properties) props['parameters']['name'] = 'bar' update_snippet = rsrc_defn.ResourceDefinition(rsrc.name, rsrc.type(), props) self.heat.stacks.get = mock.MagicMock(side_effect=stacks) scheduler.TaskRunner(rsrc.update, update_snippet)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) self.assertEqual('bar', rsrc.properties.get('parameters')['name']) env = environment.get_child_environment(rsrc.stack.env, {'name': 'bar'}) fields = { 'stack_id': rsrc.resource_id, 'template': template_format.parse(remote_template), 'timeout_mins': 60, 'disable_rollback': True, 'parameters': { 'name': 'bar' }, 'files': self.files, 'environment': env.user_env_as_dict(), } self.heat.stacks.update.assert_called_with(**fields) self.assertEqual(2, len(self.heat.stacks.get.call_args_list))
def _parse_nested_stack(self, stack_name, child_template, child_params, timeout_mins=None, adopt_data=None): if timeout_mins is None: timeout_mins = self.stack.timeout_mins stack_user_project_id = self.stack.stack_user_project_id new_nested_depth = self._child_nested_depth() child_env = environment.get_child_environment( self.stack.env, child_params, child_resource_name=self.name, item_to_remove=self.resource_info) parsed_template = self._child_parsed_template(child_template, child_env) # Note we disable rollback for nested stacks, since they # should be rolled back by the parent stack on failure nested = parser.Stack(self.context, stack_name, parsed_template, timeout_mins=timeout_mins, disable_rollback=True, parent_resource=self.name, owner_id=self.stack.id, user_creds_id=self.stack.user_creds_id, stack_user_project_id=stack_user_project_id, adopt_stack_data=adopt_data, nested_depth=new_nested_depth) nested.set_parent_stack(self.stack) return nested
def test_registry_merge_favor_child(self): env1 = {u"resource_registry": {u"OS::Food": u"carrots.yaml"}} env2 = {u"resource_registry": {u"OS::Food": u"apples.yaml"}} penv = environment.Environment(env=env1) cenv = environment.get_child_environment(penv, env2) res = cenv.get_resource_info("OS::Food") self.assertEqual("apples.yaml", res.value)
def test_item_to_remove_none(self): env = {u"resource_registry": {u"OS::Food": u"fruity.yaml"}} penv = environment.Environment(env) victim = penv.get_resource_info("OS::Food", resource_name="abc") self.assertIsNotNone(victim) cenv = environment.get_child_environment(penv, None) res = cenv.get_resource_info("OS::Food", resource_name="abc") self.assertIsNotNone(res)
def test_item_to_remove_none(self): env = {u'resource_registry': {u'OS::Food': u'fruity.yaml'}} penv = environment.Environment(env) victim = penv.get_resource_info('OS::Food', resource_name='abc') self.assertIsNotNone(victim) cenv = environment.get_child_environment(penv, None) res = cenv.get_resource_info('OS::Food', resource_name='abc') self.assertIsNotNone(res)
def test_registry_merge_simple(self): env1 = {u'resource_registry': {u'OS::Food': u'fruity.yaml'}} env2 = {u'resource_registry': {u'OS::Fruit': u'apples.yaml'}} penv = environment.Environment(env=env1) cenv = environment.get_child_environment(penv, env2) rr = cenv.user_env_as_dict()['resource_registry'] self.assertIn('OS::Food', rr) self.assertIn('OS::Fruit', rr)
def test_params_normal(self): new_params = {'parameters': {'foo': 'bar', 'tester': 'Yes'}} penv = environment.Environment() expected = {'parameter_defaults': {}, 'resource_registry': {'resources': {}}} expected.update(new_params) cenv = environment.get_child_environment(penv, new_params) self.assertEqual(expected, cenv.user_env_as_dict())
def test_registry_merge_simple(self): env1 = {u"resource_registry": {u"OS::Food": u"fruity.yaml"}} env2 = {u"resource_registry": {u"OS::Fruit": u"apples.yaml"}} penv = environment.Environment(env=env1) cenv = environment.get_child_environment(penv, env2) rr = cenv.user_env_as_dict()["resource_registry"] self.assertIn("OS::Food", rr) self.assertIn("OS::Fruit", rr)
def test_params_flat(self): new_params = {'foo': 'bar', 'tester': 'Yes'} penv = environment.Environment() expected = {'parameters': new_params, 'encrypted_param_names': [], 'parameter_defaults': {}, 'resource_registry': {'resources': {}}} cenv = environment.get_child_environment(penv, new_params) self.assertEqual(expected, cenv.user_env_as_dict())
def test_params_normal(self): new_params = {'parameters': {'foo': 'bar', 'tester': 'Yes'}} penv = environment.Environment() expected = {'parameter_defaults': {}, 'encrypted_param_names': [], 'resource_registry': {'resources': {}}} expected.update(new_params) cenv = environment.get_child_environment(penv, new_params) self.assertEqual(expected, cenv.user_env_as_dict())
def update_with_template(self, child_template, user_params=None, timeout_mins=None): """Update the nested stack with the new template.""" if self.id is None: self._store() nested_stack = self.nested() if nested_stack is None: # if the create failed for some reason and the nested # stack was not created, we need to create an empty stack # here so that the update will work. def _check_for_completion(creator_fn): while not self.check_create_complete(creator_fn): yield empty_temp = template_format.parse( "heat_template_version: '2013-05-23'") stack_creator = self.create_with_template(empty_temp, {}) checker = scheduler.TaskRunner(_check_for_completion, stack_creator) checker(timeout=self.stack.timeout_secs()) if stack_creator is not None: stack_creator.run_to_completion() nested_stack = self.nested() if timeout_mins is None: timeout_mins = self.stack.timeout_mins if user_params is None: user_params = self.child_params() child_env = environment.get_child_environment( self.stack.env, user_params, child_resource_name=self.name, item_to_remove=self.resource_info) parsed_template = self._child_parsed_template(child_template, child_env) cookie = {'previous': { 'updated_at': nested_stack.updated_time, 'state': nested_stack.state}} args = {rpc_api.PARAM_TIMEOUT: timeout_mins} try: self.rpc_client().update_stack( self.context, nested_stack.identifier(), parsed_template.t, child_env.user_env_as_dict(), parsed_template.files, args) except Exception as ex: LOG.exception(_LE('update_stack')) self.raise_local_exception(ex) return cookie
def create_with_template(self, child_template, user_params=None, timeout_mins=None, adopt_data=None): """Create the nested stack with the given template.""" name = self.physical_resource_name() if timeout_mins is None: timeout_mins = self.stack.timeout_mins stack_user_project_id = self.stack.stack_user_project_id if user_params is None: user_params = self.child_params() child_env = environment.get_child_environment( self.stack.env, user_params, child_resource_name=self.name, item_to_remove=self.resource_info) new_nested_depth = self._child_nested_depth() parsed_template = self._child_parsed_template(child_template, child_env) adopt_data_str = None if adopt_data is not None: if 'environment' not in adopt_data: adopt_data['environment'] = child_env.user_env_as_dict() if 'template' not in adopt_data: if isinstance(child_template, template.Template): adopt_data['template'] = child_template.t else: adopt_data['template'] = child_template adopt_data_str = json.dumps(adopt_data) args = { rpc_api.PARAM_TIMEOUT: timeout_mins, rpc_api.PARAM_DISABLE_ROLLBACK: True, rpc_api.PARAM_ADOPT_STACK_DATA: adopt_data_str } try: result = self.rpc_client()._create_stack( self.context, name, parsed_template.t, child_env.user_env_as_dict(), parsed_template.files, args, owner_id=self.stack.id, user_creds_id=self.stack.user_creds_id, stack_user_project_id=stack_user_project_id, nested_depth=new_nested_depth, parent_resource_name=self.name) except Exception as ex: self.raise_local_exception(ex) self.resource_id_set(result['stack_id'])
def test_params_flat(self): new_params = {'foo': 'bar', 'tester': 'Yes'} penv = environment.Environment() expected = {'parameters': new_params, 'encrypted_param_names': [], 'parameter_defaults': {}, 'event_sinks': [], 'resource_registry': {'resources': {}}} cenv = environment.get_child_environment(penv, new_params) self.assertEqual(expected, cenv.env_as_dict())
def test_params_parent_overwritten(self): new_params = {'parameters': {'foo': 'bar', 'tester': 'Yes'}} parent_params = {'parameters': {'gone': 'hopefully'}} penv = environment.Environment(env=parent_params) expected = {'parameter_defaults': {}, 'encrypted_param_names': [], 'resource_registry': {'resources': {}}} expected.update(new_params) cenv = environment.get_child_environment(penv, new_params) self.assertEqual(expected, cenv.user_env_as_dict())
def test_params_parent_overwritten(self): new_params = {'parameters': {'foo': 'bar', 'tester': 'Yes'}} parent_params = {'parameters': {'gone': 'hopefully'}} penv = environment.Environment(env=parent_params) expected = {'parameter_defaults': {}, 'encrypted_param_names': [], 'event_sinks': [], 'resource_registry': {'resources': {}}} expected.update(new_params) cenv = environment.get_child_environment(penv, new_params) self.assertEqual(expected, cenv.env_as_dict())
def test_item_to_remove_simple(self): env = {u"resource_registry": {u"OS::Food": u"fruity.yaml"}} penv = environment.Environment(env) victim = penv.get_resource_info("OS::Food", resource_name="abc") self.assertIsNotNone(victim) cenv = environment.get_child_environment(penv, None, item_to_remove=victim) self.assertRaises(exception.EntityNotFound, cenv.get_resource_info, "OS::Food", resource_name="abc") self.assertNotIn("OS::Food", cenv.user_env_as_dict()["resource_registry"]) # make sure the parent env is unaffected innocent = penv.get_resource_info("OS::Food", resource_name="abc") self.assertIsNotNone(innocent)
def create_with_template(self, child_template, user_params=None, timeout_mins=None, adopt_data=None): """Create the nested stack with the given template.""" name = self.physical_resource_name() if timeout_mins is None: timeout_mins = self.stack.timeout_mins stack_user_project_id = self.stack.stack_user_project_id if user_params is None: user_params = self.child_params() child_env = environment.get_child_environment( self.stack.env, user_params, child_resource_name=self.name, item_to_remove=self.resource_info) new_nested_depth = self._child_nested_depth() parsed_template = self._child_parsed_template(child_template, child_env) adopt_data_str = None if adopt_data is not None: if 'environment' not in adopt_data: adopt_data['environment'] = child_env.user_env_as_dict() if 'template' not in adopt_data: if isinstance(child_template, template.Template): adopt_data['template'] = child_template.t else: adopt_data['template'] = child_template adopt_data_str = json.dumps(adopt_data) args = {rpc_api.PARAM_TIMEOUT: timeout_mins, rpc_api.PARAM_DISABLE_ROLLBACK: True, rpc_api.PARAM_ADOPT_STACK_DATA: adopt_data_str} try: result = self.rpc_client()._create_stack( self.context, name, parsed_template.t, child_env.user_env_as_dict(), parsed_template.files, args, owner_id=self.stack.id, user_creds_id=self.stack.user_creds_id, stack_user_project_id=stack_user_project_id, nested_depth=new_nested_depth, parent_resource_name=self.name) except Exception as ex: self.raise_local_exception(ex) self.resource_id_set(result['stack_id'])
def test_item_to_remove_simple(self): env = {u'resource_registry': {u'OS::Food': u'fruity.yaml'}} penv = environment.Environment(env) victim = penv.get_resource_info('OS::Food', resource_name='abc') self.assertIsNotNone(victim) cenv = environment.get_child_environment(penv, None, item_to_remove=victim) res = cenv.get_resource_info('OS::Food', resource_name='abc') self.assertIsNone(res) self.assertNotIn('OS::Food', cenv.user_env_as_dict()['resource_registry']) # make sure the parent env is uneffected innocent = penv.get_resource_info('OS::Food', resource_name='abc') self.assertIsNotNone(innocent)
def handle_create(self): params = self.properties[self.PARAMETERS] env = environment.get_child_environment(self.stack.env, params) tmpl = template_format.parse(self.properties[self.TEMPLATE]) args = { 'stack_name': self.physical_resource_name_or_FnGetRefId(), 'template': tmpl, 'timeout_mins': self.properties[self.TIMEOUT], 'disable_rollback': True, 'parameters': params, 'files': self.stack.t.files, 'environment': env.user_env_as_dict(), } remote_stack_id = self.heat().stacks.create(**args)['stack']['id'] self.resource_id_set(remote_stack_id)
def _parse_nested_stack(self, stack_name, child_template, child_params=None, timeout_mins=None, adopt_data=None): if self.stack.nested_depth >= cfg.CONF.max_nested_stack_depth: msg = _("Recursion depth exceeds %d." ) % cfg.CONF.max_nested_stack_depth raise exception.RequestLimitExceeded(message=msg) parsed_template = self._parse_child_template(child_template) self._validate_nested_resources(parsed_template) # Don't overwrite the attributes_schema for subclasses that # define their own attributes_schema. if not hasattr(type(self), 'attributes_schema'): self.attributes = None self._outputs_to_attribs(parsed_template) if timeout_mins is None: timeout_mins = self.stack.timeout_mins stack_user_project_id = self.stack.stack_user_project_id new_nested_depth = self.stack.nested_depth + 1 if child_params is None: child_params = self.child_params() child_env = environment.get_child_environment(self.stack.env, child_params) # Note we disable rollback for nested stacks, since they # should be rolled back by the parent stack on failure nested = parser.Stack(self.context, stack_name, parsed_template, env=child_env, timeout_mins=timeout_mins, disable_rollback=True, parent_resource=self, owner_id=self.stack.id, user_creds_id=self.stack.user_creds_id, stack_user_project_id=stack_user_project_id, adopt_stack_data=adopt_data, nested_depth=new_nested_depth) return nested
def test_item_to_remove_complex(self): env = { u"resource_registry": {u"OS::Food": u"fruity.yaml", u"resources": {u"abc": {u"OS::Food": u"nutty.yaml"}}} } penv = environment.Environment(env) # the victim we want is the most specific one. victim = penv.get_resource_info("OS::Food", resource_name="abc") self.assertEqual(["resources", "abc", "OS::Food"], victim.path) cenv = environment.get_child_environment(penv, None, item_to_remove=victim) res = cenv.get_resource_info("OS::Food", resource_name="abc") self.assertEqual(["OS::Food"], res.path) rr = cenv.user_env_as_dict()["resource_registry"] self.assertIn("OS::Food", rr) self.assertNotIn("OS::Food", rr["resources"]["abc"]) # make sure the parent env is uneffected innocent2 = penv.get_resource_info("OS::Food", resource_name="abc") self.assertEqual(["resources", "abc", "OS::Food"], innocent2.path)
def test_drill_down_matching_wildcard(self): env = { u"resource_registry": { u"resources": { u"nested": {u"c": {u"OS::Fruit": u"carrots.yaml", u"hooks": "pre-create"}}, u"nest*": {u"nested_res": {u"hooks": "pre-create"}}, } } } penv = environment.Environment(env) cenv = environment.get_child_environment(penv, None, child_resource_name=u"nested") registry = cenv.user_env_as_dict()["resource_registry"] resources = registry["resources"] self.assertIn("c", resources) self.assertIn("nested_res", resources) res = cenv.get_resource_info("OS::Fruit", resource_name="c") self.assertIsNotNone(res) self.assertEqual(u"carrots.yaml", res.value)
def _create_template( self, num_instances, num_replace=0, template_version=("HeatTemplateFormatVersion", "2012-12-12") ): """Create a template to represent autoscaled instances. Also see heat.scaling.template.member_definitions. """ instance_definition = self._get_resource_definition() old_resources = grouputils.get_member_definitions(self, include_failed=True) definitions = template.member_definitions( old_resources, instance_definition, num_instances, num_replace, short_id.generate_id ) child_env = environment.get_child_environment( self.stack.env, self.child_params(), item_to_remove=self.resource_info ) return template.make_template(definitions, version=template_version, child_env=child_env)
def test_item_to_remove_complex(self): env = {u'resource_registry': {u'OS::Food': u'fruity.yaml', u'resources': {u'abc': { u'OS::Food': u'nutty.yaml'}}}} penv = environment.Environment(env) # the victim we want is the most specific one. victim = penv.get_resource_info('OS::Food', resource_name='abc') self.assertEqual(['resources', 'abc', 'OS::Food'], victim.path) cenv = environment.get_child_environment(penv, None, item_to_remove=victim) res = cenv.get_resource_info('OS::Food', resource_name='abc') self.assertEqual(['OS::Food'], res.path) rr = cenv.user_env_as_dict()['resource_registry'] self.assertIn('OS::Food', rr) self.assertNotIn('OS::Food', rr['resources']['abc']) # make sure the parent env is uneffected innocent2 = penv.get_resource_info('OS::Food', resource_name='abc') self.assertEqual(['resources', 'abc', 'OS::Food'], innocent2.path)
def test_create(self): rsrc = self.create_remote_stack() self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) self.assertEqual('c8a19429-7fde-47ea-a42f-40045488226c', rsrc.resource_id) env = environment.get_child_environment(rsrc.stack.env, {'name': 'foo'}) args = { 'stack_name': rsrc.physical_resource_name(), 'template': template_format.parse(remote_template), 'timeout_mins': 60, 'disable_rollback': True, 'parameters': {'name': 'foo'}, 'files': self.files, 'environment': env.user_env_as_dict(), } self.heat.stacks.create.assert_called_with(**args) self.assertEqual(2, len(self.heat.stacks.get.call_args_list))
def _stack_kwargs(self, user_params, child_template): if user_params is None: user_params = self.child_params() if child_template is None: child_template = self.child_template() child_env = environment.get_child_environment( self.stack.env, user_params, child_resource_name=self.name, item_to_remove=self.resource_info) parsed_template = self._child_parsed_template(child_template, child_env) return { 'template': parsed_template.t, 'params': child_env.user_env_as_dict(), 'files': parsed_template.files }
def _create_template(self, num_instances, num_replace=0, template_version=('HeatTemplateFormatVersion', '2012-12-12')): """ Create a template to represent autoscaled instances. Also see heat.scaling.template.resource_templates. """ instance_definition = self._get_instance_definition() old_resources = self._get_instance_templates() definitions = template.resource_templates( old_resources, instance_definition, num_instances, num_replace) child_env = environment.get_child_environment( self.stack.env, self.child_params(), item_to_remove=self.resource_info) return template.make_template(definitions, version=template_version, child_env=child_env)
def _create_template(self, num_instances, num_replace=0, template_version=('HeatTemplateFormatVersion', '2012-12-12')): """Create a template to represent autoscaled instances. Also see heat.scaling.template.member_definitions. """ instance_definition = self._get_resource_definition() old_resources = grouputils.get_member_definitions(self, include_failed=True) # WRS: Detect a scale down. Issue a vote # If any vote is rejected, set new_resources to be same size as old existing = grouputils.get_members(self) if num_instances < len(existing): LOG.info("WRS downscale detected, vote initiated") for i in range(num_instances, len(existing)): if existing[i].wrs_vote() is False: LOG.info("WRS downscale blocked by vote") num_instances = len(existing) break definitions = list(template.member_definitions( old_resources, instance_definition, num_instances, num_replace, short_id.generate_id, delete_oldest=False)) child_env = environment.get_child_environment( self.stack.env, self.child_params(), item_to_remove=self.resource_info) tmpl = template.make_template(definitions, version=template_version, child_env=child_env) # Subclasses use HOT templates att_func = 'get_attr' if att_func not in tmpl.functions: att_func = 'Fn::GetAtt' get_attr = functools.partial(tmpl.functions[att_func], None, att_func) for odefn in self._nested_output_defns([k for k, d in definitions], get_attr): tmpl.add_output(odefn) return tmpl
def handle_update(self, json_snippet, tmpl_diff, prop_diff): if self.resource_id and prop_diff: snippet = json_snippet.get('Properties', {}) self.properties = properties.Properties(self.properties_schema, snippet, function.resolve, self.name) params = self.properties[self.PARAMETERS] env = environment.get_child_environment(self.stack.env, params) tmpl = template_format.parse(self.properties[self.TEMPLATE]) fields = { 'stack_id': self.resource_id, 'parameters': params, 'template': tmpl, 'timeout_mins': self.properties[self.TIMEOUT], 'disable_rollback': self.stack.disable_rollback, 'files': self.stack.t.files, 'environment': env.user_env_as_dict(), } self.heat().stacks.update(**fields)
def handle_update(self, json_snippet, tmpl_diff, prop_diff): # Always issue an update to the remote stack and let the individual # resources in it decide if they need updating. if self.resource_id: self.properties = json_snippet.properties(self.properties_schema, self.context) params = self.properties[self.PARAMETERS] env = environment.get_child_environment(self.stack.env, params) tmpl = template_format.parse(self.properties[self.TEMPLATE]) fields = { 'stack_id': self.resource_id, 'parameters': params, 'template': tmpl, 'timeout_mins': self.properties[self.TIMEOUT], 'disable_rollback': self.stack.disable_rollback, 'files': self.stack.t.files, 'environment': env.user_env_as_dict(), } self.heat().stacks.update(**fields)
def child_definition(self, child_template=None, user_params=None, nested_identifier=None): if user_params is None: user_params = self.child_params() if child_template is None: child_template = self.child_template() if nested_identifier is None: nested_identifier = self.nested_identifier() child_env = environment.get_child_environment( self.stack.env, user_params, child_resource_name=self.name, item_to_remove=self.resource_info) parsed_template = self._child_parsed_template(child_template, child_env) return stk_defn.StackDefinition(self.context, parsed_template, nested_identifier, None)
def validate(self): super(RemoteStack, self).validate() try: with TempCACertFile(self.cacert) as cacert_path: self.heat(cacert_path) except Exception as ex: if self._credential: location = "remote cloud" else: location = 'region "%s"' % self._region_name exc_info = dict(location=location, exc=six.text_type(ex)) msg = _('Cannot establish connection to Heat endpoint at ' '%(location)s due to "%(exc)s"') % exc_info raise exception.StackValidationFailed(message=msg) try: params = self.properties[self.PARAMETERS] env = environment.get_child_environment(self.stack.env, params) tmpl = template_format.parse(self.properties[self.TEMPLATE]) args = { 'template': tmpl, 'files': self.stack.t.files, 'environment': env.user_env_as_dict(), } with TempCACertFile(self.cacert) as cacert_path: self.heat(cacert_path).stacks.validate(**args) except Exception as ex: if self._credential: location = "remote cloud" else: location = 'region "%s"' % self._region_name exc_info = dict(location=location, exc=six.text_type(ex)) msg = _('Failed validating stack template using Heat endpoint at ' '%(location)s due to "%(exc)s"') % exc_info raise exception.StackValidationFailed(message=msg)