def test_index_detail(self, mock_enforce): self._mock_enforce_setup(mock_enforce, 'index', True) res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '1') res_identity = identifier.ResourceIdentifier(resource_name=res_name, **stack_identity) req = self._get(stack_identity._tenant_path() + '/resources', {'with_detail': 'true'}) resp_parameters = { "OS::project_id": "3ab5b02fa01f4f95afa1e254afc4a435", "network": "cf05086d-07c7-4ed6-95e5-e4af724677e6", "OS::stack_name": "s1", "admin_pass": "******", "key_name": "kk", "image": "fa5d387e-541f-4dfb-ae8a-83a614683f84", "db_port": "50000", "OS::stack_id": "723d7cee-46b3-4433-9c21-f3378eb0bfc4", "flavor": "1" }, engine_resp = [{ u'resource_identity': dict(res_identity), u'stack_name': stack_identity.stack_name, u'resource_name': res_name, u'resource_status_reason': None, u'updated_time': u'2012-07-23T13:06:00Z', u'stack_identity': stack_identity, u'resource_action': u'CREATE', u'resource_status': u'COMPLETE', u'physical_resource_id': u'a3455d8c-9f88-404d-a85b-5315293e67de', u'resource_type': u'AWS::EC2::Instance', u'parameters': resp_parameters, u'description': u'Hello description', u'stack_user_project_id': u'6f38bcfebbc4400b82d50c1a2ea3057d', }] mock_call = self.patchobject(rpc_client.EngineClient, 'call', return_value=engine_resp) result = self.controller.index(req, tenant_id=self.tenant, stack_name=stack_identity.stack_name, stack_id=stack_identity.stack_id) expected = { 'resources': [{ 'links': [{ 'href': self._url(res_identity), 'rel': 'self' }, { 'href': self._url(stack_identity), 'rel': 'stack' }], u'resource_name': res_name, u'logical_resource_id': res_name, u'resource_status_reason': None, u'updated_time': u'2012-07-23T13:06:00Z', u'resource_status': u'CREATE_COMPLETE', u'physical_resource_id': u'a3455d8c-9f88-404d-a85b-5315293e67de', u'resource_type': u'AWS::EC2::Instance', u'parameters': resp_parameters, u'description': u'Hello description', u'stack_user_project_id': u'6f38bcfebbc4400b82d50c1a2ea3057d' }] } self.assertEqual(expected, result) mock_call.assert_called_once_with(req.context, ('list_stack_resources', { 'stack_identity': stack_identity, 'nested_depth': 0, 'with_detail': True, 'filters': {} }), version='1.25')
def test_event_resource(self): si = identifier.HeatIdentifier('t', 's', 'i') pi = identifier.ResourceIdentifier(resource_name='r', **si) ei = identifier.EventIdentifier(event_id='e', **pi) self.assertEqual(pi, ei.resource())
def test_arn(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertEqual('arn:openstack:heat::t:stacks/s/i/p', hi.arn())
def test_resource_stack(self): si = identifier.HeatIdentifier('t', 's', 'i') ri = identifier.ResourceIdentifier(resource_name='r', **si) self.assertEqual(si, ri.stack())
def _test_event_init(self, event_id): si = identifier.HeatIdentifier('t', 's', 'i') pi = identifier.ResourceIdentifier(resource_name='p', **si) ei = identifier.EventIdentifier(event_id=event_id, **pi) self.assertEqual('/resources/p/events/{0}'.format(event_id), ei.path)
def test_not_equal(self): hi1 = identifier.HeatIdentifier('t', 's', 'i', 'p') hi2 = identifier.HeatIdentifier('t', 's', 'i', 'q') self.assertFalse(hi1 == hi2) self.assertFalse(hi2 == hi1)
def test_resource_init_path(self): si = identifier.HeatIdentifier('t', 's', 'i') pi = identifier.ResourceIdentifier(resource_name='p', **si) ri = identifier.ResourceIdentifier(resource_name='r', **pi) self.assertEqual('/resources/p/resources/r', ri.path)
def test_attrs(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertEqual('t', hi.tenant) self.assertEqual('s', hi.stack_name) self.assertEqual('i', hi.stack_id) self.assertEqual('/p', hi.path)
def test_url_path_default(self): hi = identifier.HeatIdentifier('t', 's', 'i') self.assertEqual('t/stacks/s/i', hi.url_path())
def create_or_update(self, req, action=None): """ Implements CreateStack and UpdateStack API actions. Create or update stack as defined in template file. """ def extract_args(params): """ Extract request parameters/arguments and reformat them to match the engine API. FIXME: we currently only support a subset of the AWS defined parameters (both here and in the engine) """ # TODO(shardy) : Capabilities, NotificationARNs keymap = { 'TimeoutInMinutes': engine_api.PARAM_TIMEOUT, 'DisableRollback': engine_api.PARAM_DISABLE_ROLLBACK } if 'DisableRollback' in params and 'OnFailure' in params: msg = _('DisableRollback and OnFailure ' 'may not be used together') raise exception.HeatInvalidParameterCombinationError( detail=msg) result = {} for k in keymap: if k in params: result[keymap[k]] = params[k] if 'OnFailure' in params: value = params['OnFailure'] if value == 'DO_NOTHING': result[engine_api.PARAM_DISABLE_ROLLBACK] = 'true' elif value in ('ROLLBACK', 'DELETE'): result[engine_api.PARAM_DISABLE_ROLLBACK] = 'false' return result if action not in self.CREATE_OR_UPDATE_ACTION: msg = _("Unexpected action %(action)s") % ({'action': action}) # This should not happen, so return HeatInternalFailureError return exception.HeatInternalFailureError(detail=msg) engine_action = { self.CREATE_STACK: self.rpc_client.create_stack, self.UPDATE_STACK: self.rpc_client.update_stack } con = req.context # Extract the stack input parameters stack_parms = self._extract_user_params(req.params) # Extract any additional arguments ("Request Parameters") create_args = extract_args(req.params) try: templ = self._get_template(req) except socket.gaierror: msg = _('Invalid Template URL') return exception.HeatInvalidParameterValueError(detail=msg) if templ is None: msg = _("TemplateBody or TemplateUrl were not given.") return exception.HeatMissingParameterError(detail=msg) try: stack = template_format.parse(templ) except ValueError: msg = _("The Template must be a JSON or YAML document.") return exception.HeatInvalidParameterValueError(detail=msg) args = { 'template': stack, 'params': stack_parms, 'files': {}, 'args': create_args } try: stack_name = req.params['StackName'] if action == self.CREATE_STACK: args['stack_name'] = stack_name else: args['stack_identity'] = self._get_identity(con, stack_name) result = engine_action[action](con, **args) except Exception as ex: return exception.map_remote_error(ex) try: identity = identifier.HeatIdentifier(**result) except (ValueError, TypeError): response = result else: response = {'StackId': identity.arn()} return api_utils.format_response(action, response)
def setUp(self): super(EngineRpcAPITestCase, self).setUp() self.stubs = stubout.StubOutForTesting() self.identity = dict( identifier.HeatIdentifier('engine_test_tenant', '6', 'wordpress')) self.rpcapi = rpc_client.EngineClient()
def identifier(self): ''' Return an identifier for this stack. ''' return identifier.HeatIdentifier(self.tenant_id, self.name, self.id)
def test_index(self, mock_enforce): self._mock_enforce_setup(mock_enforce, 'index', True) res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '1') res_identity = identifier.ResourceIdentifier(resource_name=res_name, **stack_identity) req = self._get(stack_identity._tenant_path() + '/resources') engine_resp = [{ u'resource_identity': dict(res_identity), u'stack_name': stack_identity.stack_name, u'resource_name': res_name, u'resource_status_reason': None, u'updated_time': u'2012-07-23T13:06:00Z', u'stack_identity': stack_identity, u'resource_action': u'CREATE', u'resource_status': u'COMPLETE', u'physical_resource_id': u'a3455d8c-9f88-404d-a85b-5315293e67de', u'resource_type': u'AWS::EC2::Instance', }] mock_call = self.patchobject(rpc_client.EngineClient, 'call', return_value=engine_resp) result = self.controller.index(req, tenant_id=self.tenant, stack_name=stack_identity.stack_name, stack_id=stack_identity.stack_id) expected = { 'resources': [{ 'links': [{ 'href': self._url(res_identity), 'rel': 'self' }, { 'href': self._url(stack_identity), 'rel': 'stack' }], u'resource_name': res_name, u'logical_resource_id': res_name, u'resource_status_reason': None, u'updated_time': u'2012-07-23T13:06:00Z', u'resource_status': u'CREATE_COMPLETE', u'physical_resource_id': u'a3455d8c-9f88-404d-a85b-5315293e67de', u'resource_type': u'AWS::EC2::Instance' }] } self.assertEqual(expected, result) mock_call.assert_called_once_with(req.context, ('list_stack_resources', { 'stack_identity': stack_identity, 'nested_depth': 0, 'with_detail': False, 'filters': {} }), version='1.25')
def test_show_with_nested_stack(self, mock_enforce): self._mock_enforce_setup(mock_enforce, 'show', True) res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') res_identity = identifier.ResourceIdentifier(resource_name=res_name, **stack_identity) nested_stack_identity = identifier.HeatIdentifier( self.tenant, 'nested', 'some_id') req = self._get(stack_identity._tenant_path()) engine_resp = { u'description': u'', u'resource_identity': dict(res_identity), u'stack_name': stack_identity.stack_name, u'resource_name': res_name, u'resource_status_reason': None, u'updated_time': u'2012-07-23T13:06:00Z', u'stack_identity': dict(stack_identity), u'resource_action': u'CREATE', u'resource_status': u'COMPLETE', u'physical_resource_id': u'a3455d8c-9f88-404d-a85b-5315293e67de', u'resource_type': u'AWS::EC2::Instance', u'attributes': { u'foo': 'bar' }, u'metadata': { u'ensureRunning': u'true' }, u'nested_stack_id': dict(nested_stack_identity) } mock_call = self.patchobject(rpc_client.EngineClient, 'call', return_value=engine_resp) result = self.controller.show(req, tenant_id=self.tenant, stack_name=stack_identity.stack_name, stack_id=stack_identity.stack_id, resource_name=res_name) expected = [{ 'href': self._url(res_identity), 'rel': 'self' }, { 'href': self._url(stack_identity), 'rel': 'stack' }, { 'href': self._url(nested_stack_identity), 'rel': 'nested' }] self.assertEqual(expected, result['resource']['links']) self.assertIsNone(result.get(rpc_api.RES_NESTED_STACK_ID)) mock_call.assert_called_once_with(req.context, ('describe_stack_resource', { 'stack_identity': stack_identity, 'resource_name': res_name, 'with_attr': None }), version='1.2')
def test_items(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertEqual('t', hi['tenant']) self.assertEqual('s', hi['stack_name']) self.assertEqual('i', hi['stack_id']) self.assertEqual('/p', hi['path'])
def test_url_path_with_unicode_path(self): hi = identifier.HeatIdentifier('t', 's', 'i', u'\u5de5') self.assertEqual('t/stacks/s/i/%E5%B7%A5', hi.url_path())
def test_equal_dict(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertTrue(hi == dict(hi)) self.assertTrue(dict(hi) == hi)
def test_tenant_escape(self): hi = identifier.HeatIdentifier(':/', 's', 'i') self.assertEqual(':/', hi.tenant) self.assertEqual('%3A%2F/stacks/s/i', hi.url_path()) self.assertEqual('arn:openstack:heat::%3A%2F:stacks/s/i', hi.arn())
def test_path_components(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p1/p2/p3') self.assertEqual(['p1', 'p2', 'p3'], hi._path_components())
def test_name_escape(self): hi = identifier.HeatIdentifier('t', ':%', 'i') self.assertEqual(':%', hi.stack_name) self.assertEqual('t/stacks/%3A%25/i', hi.url_path()) self.assertEqual('arn:openstack:heat::t:stacks/%3A%25/i', hi.arn())
def test_resource_init_from_dict(self): hi = identifier.HeatIdentifier('t', 's', 'i', '/resources/r') ri = identifier.ResourceIdentifier(**hi) self.assertEqual(hi, ri)
def test_id_escape(self): hi = identifier.HeatIdentifier('t', 's', ':/') self.assertEqual(':/', hi.stack_id) self.assertEqual('t/stacks/s/%3A%2F', hi.url_path()) self.assertEqual('arn:openstack:heat::t:stacks/s/%3A%2F', hi.arn())
def test_invalid_attr(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') hi.identity['foo'] = 'bar' self.assertRaises(AttributeError, getattr, hi, 'foo')
def test_path_escape(self): hi = identifier.HeatIdentifier('t', 's', 'i', ':/') self.assertEqual('/:/', hi.path) self.assertEqual('t/stacks/s/i/%3A/', hi.url_path()) self.assertEqual('arn:openstack:heat::t:stacks/s/i/%3A/', hi.arn())
def test_event_init_from_dict(self): hi = identifier.HeatIdentifier('t', 's', 'i', '/resources/p/events/42') ei = identifier.EventIdentifier(**hi) self.assertEqual(hi, ei)
def test_path_default(self): hi = identifier.HeatIdentifier('t', 's', 'i') self.assertEqual('', hi.path)
def test_invalid_item(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') hi.identity['foo'] = 'bar' self.assertRaises(KeyError, lambda o, k: o[k], hi, 'foo')
def test_equal(self): hi1 = identifier.HeatIdentifier('t', 's', 'i', 'p') hi2 = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertTrue(hi1 == hi2)
def test_arn_url(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertEqual('/arn%3Aopenstack%3Aheat%3A%3At%3Astacks%2Fs%2Fi%2Fp', hi.arn_url_path())
def _test_resource_index(self, event_id, mock_enforce): self._mock_enforce_setup(mock_enforce, 'index', True) res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') res_identity = identifier.ResourceIdentifier(resource_name=res_name, **stack_identity) ev_identity = identifier.EventIdentifier(event_id=event_id, **res_identity) req = self._get(stack_identity._tenant_path() + '/resources/' + res_name + '/events') kwargs = { 'stack_identity': stack_identity, 'limit': None, 'sort_keys': None, 'marker': None, 'sort_dir': None, 'filters': { 'resource_name': res_name } } engine_resp = [{ u'stack_name': u'wordpress', u'event_time': u'2012-07-23T13:05:39Z', u'stack_identity': dict(stack_identity), u'resource_name': res_name, u'resource_status_reason': u'state changed', u'event_identity': dict(ev_identity), u'resource_action': u'CREATE', u'resource_status': u'IN_PROGRESS', u'physical_resource_id': None, u'resource_properties': { u'UserData': u'blah' }, u'resource_type': u'AWS::EC2::Instance', }] self.m.StubOutWithMock(rpc_client.EngineClient, 'call') rpc_client.EngineClient.call( req.context, ('list_events', kwargs)).AndReturn(engine_resp) self.m.ReplayAll() result = self.controller.index(req, tenant_id=self.tenant, stack_name=stack_identity.stack_name, stack_id=stack_identity.stack_id, resource_name=res_name) expected = { 'events': [{ 'id': event_id, 'links': [ { 'href': self._url(ev_identity), 'rel': 'self' }, { 'href': self._url(res_identity), 'rel': 'resource' }, { 'href': self._url(stack_identity), 'rel': 'stack' }, ], u'resource_name': res_name, u'logical_resource_id': res_name, u'resource_status_reason': u'state changed', u'event_time': u'2012-07-23T13:05:39Z', u'resource_status': u'CREATE_IN_PROGRESS', u'physical_resource_id': None, }] } self.assertEqual(expected, result) self.m.VerifyAll()