def create_stack(self, template=None): if template is None: template = alarm_template temp = template_format.parse(template) template = parser.Template(temp) ctx = utils.dummy_context() ctx.tenant_id = 'test_tenant' stack = parser.Stack(ctx, utils.random_name(), template, disable_rollback=True) stack.store() self.m.StubOutWithMock(alarm.CeilometerAlarm, 'ceilometer') alarm.CeilometerAlarm.ceilometer().MultipleTimes().AndReturn(self.fa) al = copy.deepcopy(temp['Resources']['MEMAlarmHigh']['Properties']) al['description'] = mox.IgnoreArg() al['name'] = mox.IgnoreArg() al['alarm_actions'] = mox.IgnoreArg() al['insufficient_data_actions'] = None al['ok_actions'] = None al['repeat_actions'] = True al['enabled'] = True rule = dict(period=60, evaluation_periods=1, threshold=50) for field in ['period', 'evaluation_periods', 'threshold']: del al[field] for field in ['statistic', 'comparison_operator', 'meter_name']: rule[field] = al[field] del al[field] if 'query' in al and al['query']: query = al['query'] else: query = [] if 'query' in al: del al['query'] if 'matching_metadata' in al and al['matching_metadata']: for k, v in al['matching_metadata'].items(): key = 'metadata.metering.' + k query.append(dict(field=key, op='eq', value=six.text_type(v))) if 'matching_metadata' in al: del al['matching_metadata'] if query: rule['query'] = query al['threshold_rule'] = rule al['type'] = 'threshold' self.m.StubOutWithMock(self.fa.alarms, 'create') self.fa.alarms.create(**al).AndReturn(FakeCeilometerAlarm()) return stack
def create_test_stack(self): test_template = { 'HeatTemplateFormatVersion': '2012-12-12', 'Parameters': { 'Foo': { 'Type': 'String' }, 'Pass': { 'Type': 'String', 'NoEcho': True } }, 'Resources': { 'TestResource': { 'Type': 'GenericResource', 'Properties': { 'Foo': 'abc' } } }, 'Outputs': { 'food': { 'Value': { 'Fn::GetAtt': ['TestResource', 'foo'] } } } } template = parser.Template(test_template) self.ctx = utils.dummy_context() self.ctx.tenant_id = 'test_tenant' env = environment.Environment() env.load({u'parameters': {u'Foo': 'user_data', u'Pass': '******'}}) self.stack_name = utils.random_name() stack = parser.Stack(self.ctx, self.stack_name, template, env=env, disable_rollback=True) self.stack = stack stack.store() self.created_time = stack.created_time self.create_at = timeutils.isotime(self.created_time) stack.create() self.expected = {} for action in ('create', 'suspend', 'delete'): self.make_mocks(action)
def create_stack(self, cnxt, stack_name, template, params, files, args): """ The create_stack method creates a new stack using the template provided. Note that at this stage the template has already been fetched from the heat-api process if using a template-url. :param cnxt: RPC context. :param stack_name: Name of the stack you want to create. :param template: Template of stack you want to create. :param params: Stack Input Params :param files: Files referenced from the template :param args: Request parameters/args passed from API """ logger.info(_('template is %s') % template) def _stack_create(stack): # Create/Adopt a stack, and create the periodic task if successful if stack.adopt_stack_data: stack.adopt() else: stack.create() if (stack.action in (stack.CREATE, stack.ADOPT) and stack.status == stack.COMPLETE): # Schedule a periodic watcher task for this stack self.stack_watch.start_watch_task(stack.id, cnxt) else: logger.warning( _("Stack create failed, status %s") % stack.status) tmpl = parser.Template(template, files=files) self._validate_new_stack(cnxt, stack_name, tmpl) # Extract the common query parameters common_params = api.extract_args(args) env = environment.Environment(params) stack = parser.Stack(cnxt, stack_name, tmpl, env, **common_params) self._validate_deferred_auth_context(cnxt, stack) stack.validate() stack.store() self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id, _stack_create, stack) return dict(stack.identifier())
def update_stack(self, cnxt, stack_identity, template, params, files, args): """ The update_stack method updates an existing stack based on the provided template and parameters. Note that at this stage the template has already been fetched from the heat-api process if using a template-url. :param cnxt: RPC context. :param stack_identity: Name of the stack you want to create. :param template: Template of stack you want to create. :param params: Stack Input Params :param files: Files referenced from the template :param args: Request parameters/args passed from API """ # Get the database representation of the existing stack db_stack = self._get_stack(cnxt, stack_identity) LOG.info(_('Updating stack %s') % db_stack.name) current_stack = parser.Stack.load(cnxt, stack=db_stack) if current_stack.action == current_stack.SUSPEND: msg = _('Updating a stack when it is suspended') raise exception.NotSupported(feature=msg) # Now parse the template and any parameters for the updated # stack definition. tmpl = parser.Template(template, files=files) if len(tmpl[tmpl.RESOURCES]) > cfg.CONF.max_resources_per_stack: raise exception.RequestLimitExceeded( message=exception.StackResourceLimitExceeded.msg_fmt) stack_name = current_stack.name common_params = api.extract_args(args) common_params.setdefault(rpc_api.PARAM_TIMEOUT, current_stack.timeout_mins) env = environment.Environment(params) updated_stack = parser.Stack(cnxt, stack_name, tmpl, env, **common_params) updated_stack.parameters.set_stack_id(current_stack.identifier()) self._validate_deferred_auth_context(cnxt, updated_stack) updated_stack.validate() self.thread_group_mgr.start_with_lock(cnxt, current_stack, self.engine_id, current_stack.update, updated_stack) return dict(current_stack.identifier())
def _create_test_instance(self, return_server, name): stack_name = '%s_s' % name t = template_format.parse(wp_template) template = parser.Template(t) kwargs = { 'KeyName': 'test', 'InstanceType': 'm1.large', 'SubnetId': '4156c7a5-e8c4-4aff-a6e1-8f3c7bc83861' } stack = parser.Stack(utils.dummy_context(), stack_name, template, environment.Environment(kwargs), stack_id=uuidutils.generate_uuid()) t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2' instance = instances.Instance('%s_name' % name, t['Resources']['WebServer'], stack) self.m.StubOutWithMock(instance, 'nova') instance.nova().MultipleTimes().AndReturn(self.fc) self.m.StubOutWithMock(instance, 'neutron') instance.neutron().MultipleTimes().AndReturn(FakeNeutron()) instance.t = instance.stack.resolve_runtime_data(instance.t) # need to resolve the template functions server_userdata = nova_utils.build_userdata( instance, instance.t['Properties']['UserData']) instance.mime_string = server_userdata self.m.StubOutWithMock(self.fc.servers, 'create') self.fc.servers.create(image=1, flavor=3, key_name='test', name=utils.PhysName(stack_name, instance.name), security_groups=None, userdata=server_userdata, scheduler_hints=None, meta=None, nics=[{ 'port-id': '64d913c1-bcb1-42d2-8f0a-9593dbcaf251' }], availability_zone=None).AndReturn(return_server) self.m.ReplayAll() scheduler.TaskRunner(instance.create)() return instance
def test_validate_not_allowed_values_integer_str(self): t = template_format.parse(test_template_allowed_integers_str) template = parser.Template(t) # test with size parameter provided as string err = self.assertRaises(exception.StackValidationFailed, parser.Stack, self.ctx, 'test_stack', template, environment.Environment({'size': '3'})) self.assertIn('"3" is not an allowed value [1, 4, 8]', str(err)) # test with size parameter provided as number err = self.assertRaises(exception.StackValidationFailed, parser.Stack, self.ctx, 'test_stack', template, environment.Environment({'size': 3})) self.assertIn('"3" is not an allowed value [1, 4, 8]', str(err))
def setUpClass(cls): # Create a dummy stack in the DB as WatchRule instances # must be associated with a stack ctx = context.get_admin_context() ctx.username = '******' ctx.tenant_id = '123456' empty_tmpl = {"template": {}} tmpl = parser.Template(empty_tmpl) stack_name = 'dummystack' params = parser.Parameters(stack_name, tmpl, {'foo': 'bar'}) dummy_stack = parser.Stack(ctx, stack_name, tmpl, params) dummy_stack.state_set(dummy_stack.CREATE_COMPLETE, 'Testing') dummy_stack.store() cls.stack_id = dummy_stack.id
def create_stack(self, stack_name='test_stack', stub=True): temp = template_format.parse(test_template_signal) template = parser.Template(temp) ctx = context.get_admin_context() ctx.tenant_id = 'test_tenant' stack = parser.Stack(ctx, stack_name, template, disable_rollback=True) # Stub out the stack ID so we have a known value with utils.UUIDStub(self.stack_id): stack.store() if stub: self.m.StubOutWithMock(sr.SignalResponder, 'keystone') sr.SignalResponder.keystone().MultipleTimes().AndReturn(self.fc) return stack
def _setup_test_clouddbinstance(self, name, parsed_t): stack_name = '%s_stack' % name t = parsed_t template = parser.Template(t) stack = parser.Stack(utils.dummy_context(), stack_name, template, environment.Environment({'name': 'test'}), stack_id=str(uuid.uuid4())) instance = os_database.OSDBInstance('%s_name' % name, t['Resources']['MySqlCloudDB'], stack) instance.t = instance.stack.resolve_runtime_data(instance.t) return instance
def test_delete_rollback(self): self.stack = parser.Stack(self.ctx, 'delete_rollback_test', parser.Template({}), disable_rollback=False) stack_id = self.stack.store() db_s = db_api.stack_get(self.ctx, stack_id) self.assertNotEqual(db_s, None) self.stack.delete(action=self.stack.ROLLBACK) db_s = db_api.stack_get(self.ctx, stack_id) self.assertEqual(db_s, None) self.assertEqual(self.stack.state, self.stack.ROLLBACK_COMPLETE)
def test_unregistered_image(self): t = template_format.parse(test_template_image) template = parser.Template(t) stack = parser.Stack(self.ctx, 'test_stack', template, environment.Environment({'KeyName': 'test'})) self._mock_get_image_id_fail( 'image_name', exception.ImageNotFound(image_name='image_name')) self.m.ReplayAll() resource = stack['Instance'] self.assertRaises(exception.StackValidationFailed, resource.validate) self.m.VerifyAll()
def test_translate_outputs(self): """Test translation of outputs into internal engine format.""" hot_tpl = template_format.parse(''' heat_template_version: 2013-05-23 outputs: output1: description: output1 value: value1 ''') expected = {'output1': {'Description': 'output1', 'Value': 'value1'}} tmpl = parser.Template(hot_tpl) self.assertEqual(tmpl[hot.OUTPUTS], expected)
def parse_stack(self, t): ctx = context.RequestContext.from_dict({ 'tenant': 'test_tenant', 'username': '******', 'password': '******', 'auth_url': 'http://localhost:5000/v2.0'}) stack_name = 'test_stack' tmpl = parser.Template(t) params = parser.Parameters(stack_name, tmpl, {'external_network': 'abcd1234', 'internal_network': 'xyz1234', 'internal_subnet': '12.12.12.0'}) stack = parser.Stack(ctx, stack_name, tmpl, params) return stack
def test_unregistered_key(self): t = template_format.parse(test_unregistered_key) template = parser.Template(t) params = {'KeyName': 'not_registered'} stack = parser.Stack(self.ctx, 'test_stack', template, environment.Environment(params)) self.m.StubOutWithMock(instances.Instance, 'nova') instances.Instance.nova().AndReturn(self.fc) self.m.StubOutWithMock(clients.OpenStackClients, 'nova') clients.OpenStackClients.nova().AndReturn(self.fc) self.m.ReplayAll() resource = stack['Instance'] self.assertRaises(exception.StackValidationFailed, resource.validate)
def test_unregistered_image(self): t = template_format.parse(test_template_image) template = parser.Template(t) stack = parser.Stack(self.ctx, 'test_stack', template, environment.Environment({'KeyName': 'test'})) self.m.StubOutWithMock(clients.OpenStackClients, 'nova') clients.OpenStackClients.nova().AndReturn(self.fc) self.m.ReplayAll() resource = stack['Instance'] self.assertRaises(exception.StackValidationFailed, resource.validate) self.m.VerifyAll()
def test_get_file_missing_files(self): """Test get_file function with no matching key in files section.""" snippet = {'get_file': 'file:///tmp/foo.yaml'} tmpl = parser.Template(hot_tpl_empty, files={ 'file:///tmp/bar.yaml': 'bar contents' }) missingErr = self.assertRaises( ValueError, tmpl.resolve_get_file, snippet) self.assertEqual( ('No content found in the "files" section for ' 'get_file path: file:///tmp/foo.yaml'), str(missingErr))
def parse_stack(self, t): class DummyContext(): tenant = 'test_tenant' tenant_id = '1234abcd' username = '******' password = '******' auth_url = 'http://localhost:5000/v2.0' t['Parameters']['KeyName']['Value'] = 'test' stack = parser.Stack(DummyContext(), 'test_stack', parser.Template(t), stack_id=-1) return stack
def test_validate_schema_constraints_length_wrong_format(self): hot_tpl = template_format.parse(''' heat_template_version: 2013-05-23 parameters: param1: type: string constraints: - length: foo default: foo ''') error = self.assertRaises(constraints.InvalidSchemaError, parameters.Parameters, "stack_testit", parser.Template(hot_tpl)) self.assertEqual("Invalid length constraint, expected a mapping", str(error))
def _setup_test_cloud_dns_instance(self, name, parsed_t): stack_name = '%s_stack' % name t = parsed_t template = parser.Template(t) stack = parser.Stack(utils.dummy_context(), stack_name, template, environment.Environment({'name': 'test'}), stack_id=str(uuid.uuid4())) instance = cloud_dns.CloudDns( '%s_name' % name, t['Resources']['domain'], stack) return instance
def test_template_as_resource(self): """ Test that the resulting resource has the right prop and attrib schema. Note that this test requires the Wordpress_Single_Instance.yaml template in the templates directory since we want to test using a non-trivial template. """ test_templ_name = "WordPress_Single_Instance.yaml" path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates', test_templ_name) # check if its in the directory list vs. exists to work around # case-insensitive file systems self.assertIn(test_templ_name, os.listdir(os.path.dirname(path))) with open(path) as test_templ_file: test_templ = test_templ_file.read() self.assertTrue(test_templ, "Empty test template") self.m.StubOutWithMock(urlfetch, "get") urlfetch.get(test_templ_name, allowed_schemes=('http', 'https')).AndReturn(test_templ) parsed_test_templ = template_format.parse(test_templ) self.m.ReplayAll() json_snippet = { "Type": test_templ_name, "Properties": { "KeyName": "mykeyname", "DBName": "wordpress1", "DBUsername": "******", "DBPassword": "******", "DBRootPassword": "******", "LinuxDistribution": "U10" } } stack = parser.Stack(None, 'test_stack', parser.Template({}), stack_id=uuidutils.generate_uuid()) templ_resource = resource.Resource("test_templ_resource", json_snippet, stack) self.m.VerifyAll() self.assertIsInstance(templ_resource, template_resource.TemplateResource) for prop in parsed_test_templ.get("Parameters", {}): self.assertIn(prop, templ_resource.properties) for attrib in parsed_test_templ.get("Outputs", {}): self.assertIn(attrib, templ_resource.attributes) for k, v in json_snippet.get("Properties").items(): self.assertEqual(v, templ_resource.properties[k])
def test_hot_template_validate_param(self): len_desc = 'string length should be between 8 and 16' pattern_desc1 = 'Value must consist of characters only' pattern_desc2 = 'Value must start with a lowercase character' hot_tpl = template_format.parse(''' heat_template_version: 2013-05-23 parameters: db_name: description: The WordPress database name type: string default: wordpress constraints: - length: { min: 8, max: 16 } description: %s - allowed_pattern: "[a-zA-Z]+" description: %s - allowed_pattern: "[a-z]+[a-zA-Z]*" description: %s ''' % (len_desc, pattern_desc1, pattern_desc2)) tmpl = parser.Template(hot_tpl) def run_parameters(value): tmpl.parameters( identifier.HeatIdentifier('', "stack_testit", None), {'db_name': value}) return True value = 'wp' err = self.assertRaises(ValueError, run_parameters, value) self.assertIn(len_desc, str(err)) value = 'abcdefghijklmnopq' err = self.assertRaises(ValueError, run_parameters, value) self.assertIn(len_desc, str(err)) value = 'abcdefgh1' err = self.assertRaises(ValueError, run_parameters, value) self.assertIn(pattern_desc1, str(err)) value = 'Abcdefghi' err = self.assertRaises(ValueError, run_parameters, value) self.assertIn(pattern_desc2, str(err)) value = 'abcdefghi' self.assertTrue(run_parameters(value)) value = 'abcdefghI' self.assertTrue(run_parameters(value))
def _setup_test_group_autoscaling(self, intags=None, nova_tags=None): stack_name = 'tag_as_name' t = template_format.parse(autoscaling_template) template = parser.Template(t) stack = parser.Stack(utils.dummy_context(), stack_name, template, environment.Environment({'KeyName': 'test'}), stack_id=str(uuid.uuid4())) t['Resources']['WebServer']['Properties']['Tags'] += intags # create the launch configuration conf = stack['Config'] self.assertIsNone(conf.validate()) scheduler.TaskRunner(conf.create)() self.assertEqual((conf.CREATE, conf.COMPLETE), conf.state) group = stack['WebServer'] group_refid = utils.PhysName(stack.name, group.name) nova_tags['metering.groupname'] = group_refid nova_tags['AutoScalingGroupName'] = group_refid self.m.StubOutWithMock(group, '_cooldown_timestamp') group._cooldown_timestamp(mox.IgnoreArg()).AndReturn(None) self.m.StubOutWithMock(instances.Instance, 'nova') instances.Instance.nova().MultipleTimes().AndReturn(self.fc) self.m.StubOutWithMock(clients.OpenStackClients, 'nova') clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc) group.t = group.stack.resolve_runtime_data(group.t) # need to resolve the template functions self.m.StubOutWithMock(self.fc.servers, 'create') self.fc.servers.create(image=1, flavor=1, key_name='test', name=mox.IgnoreArg(), security_groups=None, userdata=mox.IgnoreArg(), scheduler_hints=None, meta=nova_tags, nics=None, availability_zone=None).AndReturn( self.fc.servers.list()[1]) return group
def setUpDatabase(self): if self.stack_id is not None: return # Create a dummy stack in the DB as WatchRule instances # must be associated with a stack ctx = utils.dummy_context() ctx.auth_token = 'abcd1234' empty_tmpl = {"template": {}} tmpl = parser.Template(empty_tmpl) stack_name = 'dummystack' dummy_stack = parser.Stack(ctx, stack_name, tmpl) dummy_stack.state_set(dummy_stack.CREATE, dummy_stack.COMPLETE, 'Testing') dummy_stack.store() self.stack_id = dummy_stack.id
def test_bad_find_in_map(self): tmpl = parser.Template(mapping_template) finds = ({ 'Fn::FindInMap': "String" }, { 'Fn::FindInMap': { "Dict": "String" } }, { 'Fn::FindInMap': ["ShortList", "foo"] }, { 'Fn::FindInMap': ["ReallyShortList"] }) for find in finds: self.assertRaises(KeyError, tmpl.resolve_find_in_map, find)
def test_unregistered_image(self): t = template_format.parse(test_template_image) template = parser.Template(t) stack = parser.Stack(self.ctx, 'test_stack', template, environment.Environment({'KeyName': 'test'})) self.m.StubOutWithMock(instances.Instance, 'nova') instances.Instance.nova().AndReturn(self.fc) instances.Instance.nova().AndReturn(self.fc) self.m.ReplayAll() resource = stack.resources['Instance'] self.assertRaises(exception.ImageNotFound, resource.validate) self.m.VerifyAll()
def parse_stack(self, t): ctx = context.RequestContext.from_dict({ 'tenant': 'test_tenant', 'username': '******', 'password': '******', 'auth_url': 'http://localhost:5000/v2.0' }) template = parser.Template(t) params = parser.Parameters('test_stack', template, {'KeyName': 'test'}) stack = parser.Stack(ctx, 'test_stack', template, params, stack_id=-1) return stack
def parse_stack(t, params={}, stack_name='test_stack', stack_id=None, timeout_mins=None): ctx = dummy_context() template = parser.Template(t) stack = parser.Stack(ctx, stack_name, template, environment.Environment(params), stack_id, timeout_mins=timeout_mins) stack.store() return stack
def test_invalid_security_groups_with_nics(self): t = template_format.parse(test_template_invalid_secgroups) template = parser.Template(t) stack = parser.Stack(self.ctx, 'test_stack', template, environment.Environment({'KeyName': 'test'})) self._mock_get_image_id_success('image_name', 'image_id') self.m.StubOutWithMock(clients.OpenStackClients, 'nova') clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc) self.m.ReplayAll() resource = stack['Instance'] self.assertRaises(exception.ResourcePropertyConflict, resource.validate) self.m.VerifyAll()
def test_invalid_security_group_ids_with_nics(self): t = template_format.parse(test_template_invalid_secgroupids) template = parser.Template(t) stack = parser.Stack(self.ctx, 'test_stack', template, environment.Environment({'KeyName': 'test'})) self._mock_get_image_id_success('image_name', 'image_id') self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') nova.NovaClientPlugin._create().AndReturn(self.fc) self.m.ReplayAll() resource = stack['Instance'] self.assertRaises(exception.ResourcePropertyConflict, resource.validate) self.m.VerifyAll()
def _setup_test_cloud_dns_instance(self, name, parsed_t): stack_name = '%s_stack' % name t = parsed_t template = parser.Template(t) stack = parser.Stack(None, stack_name, template, environment.Environment({'name': 'test'}), stack_id=uuidutils.generate_uuid()) instance = cloud_dns.CloudDns( '%s_name' % name, t['Resources']['domain'], stack) instance.t = instance.stack.resolve_runtime_data(instance.t) return instance