def test_dict_round_trip(self): hii = identifier.HeatIdentifier('t', 's', 'i', 'p') hio = identifier.HeatIdentifier(**dict(hii)) self.assertEqual(hio.tenant, hii.tenant) self.assertEqual(hio.stack_name, hii.stack_name) self.assertEqual(hio.stack_id, hii.stack_id) self.assertEqual(hio.path, hii.path)
def test_not_equal_dict(self): hi1 = identifier.HeatIdentifier('t', 's', 'i', 'p') hi2 = identifier.HeatIdentifier('t', 's', 'i', 'q') self.assertFalse(hi1 == dict(hi2)) self.assertFalse(dict(hi1) == hi2) self.assertFalse(hi1 == { 'tenant': 't', 'stack_name': 's', 'stack_id': 'i' }) self.assertFalse({ 'tenant': 't', 'stack_name': 's', 'stack_id': 'i' } == hi1)
def test_arn_escape_decode_round_trip(self): hii = identifier.HeatIdentifier(':/', ':/', ':/', ':/') hio = identifier.HeatIdentifier.from_arn(hii.arn()) self.assertEqual(hio.tenant, hii.tenant) self.assertEqual(hio.stack_name, hii.stack_name) self.assertEqual(hio.stack_id, hii.stack_id) self.assertEqual(hio.path, hii.path)
def test_lookup(self): identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '1') req = self._get('/stacks/%(stack_name)s' % identity) self.m.StubOutWithMock(rpc, 'call') rpc.call( req.context, self.topic, { 'method': 'identify_stack', 'args': { 'stack_name': identity.stack_name }, 'version': self.api_version }, None).AndReturn(identity) self.m.ReplayAll() try: result = self.controller.lookup(req, tenant_id=identity.tenant, stack_name=identity.stack_name) except webob.exc.HTTPFound as found: self.assertEqual(found.location, self._url(identity)) else: self.fail('No redirect generated') self.m.VerifyAll()
def test_delete_bad_name(self): identity = identifier.HeatIdentifier(self.tenant, 'wibble', '6') template = {u'Foo': u'bar'} json_template = json.dumps(template) parameters = {u'InstanceType': u'm1.xlarge'} body = { 'template': template, 'parameters': parameters, 'timeout_mins': 30 } req = self._delete('/stacks/%(stack_name)s/%(stack_id)s' % identity) self.m.StubOutWithMock(rpc, 'call') # Engine returns None when delete successful rpc.call( req.context, self.topic, { 'method': 'delete_stack', 'args': { 'stack_identity': dict(identity) }, 'version': self.api_version }, None).AndRaise(rpc_common.RemoteError("AttributeError")) self.m.ReplayAll() self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, req, tenant_id=identity.tenant, stack_name=identity.stack_name, stack_id=identity.stack_id) self.m.VerifyAll()
def stack_url(req, identity): try: stack_identity = identifier.HeatIdentifier(**identity) except ValueError: err_reason = _("Invalid Stack address") raise exc.HTTPInternalServerError(explanation=err_reason) return req.relative_url(stack_identity.url_path(), True)
def make_url(req, identity): '''Return the URL for the supplied identity dictionary.''' try: stack_identity = identifier.HeatIdentifier(**identity) except ValueError: err_reason = _('Invalid Stack address') raise exc.HTTPInternalServerError(explanation=err_reason) return req.relative_url(stack_identity.url_path(), True)
def _stackid_format(self, resp): """ Add a host:port:stack prefix, this formats the StackId in the response more like the AWS spec """ if 'StackId' in resp: identity = identifier.HeatIdentifier(**resp['StackId']) resp['StackId'] = identity.arn() return resp
def test_index(self): req = self._get('/stacks') identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '1') engine_resp = { u'stacks': [{ u'stack_identity': dict(identity), u'updated_time': u'2012-07-09T09:13:11Z', u'template_description': u'blah', u'description': u'blah', u'stack_status_reason': u'Stack successfully created', u'creation_time': u'2012-07-09T09:12:45Z', u'stack_name': identity.stack_name, u'stack_status': u'CREATE_COMPLETE', u'parameters': {}, u'outputs': [], u'notification_topics': [], u'capabilities': [], u'disable_rollback': True, u'timeout_mins': 60, }] } self.m.StubOutWithMock(rpc, 'call') rpc.call( req.context, self.topic, { 'method': 'show_stack', 'args': { 'stack_identity': None }, 'version': self.api_version }, None).AndReturn(engine_resp) self.m.ReplayAll() result = self.controller.index(req, tenant_id=identity.tenant) expected = { 'stacks': [{ 'links': [{ "href": self._url(identity), "rel": "self" }], 'id': '1', u'updated_time': u'2012-07-09T09:13:11Z', u'description': u'blah', u'stack_status_reason': u'Stack successfully created', u'creation_time': u'2012-07-09T09:12:45Z', u'stack_name': u'wordpress', u'stack_status': u'CREATE_COMPLETE' }] } self.assertEqual(result, expected) self.m.VerifyAll()
def _id_format(resp): """ Format the StackId field in the response as an ARN, and process other IDs into the correct format. """ if 'StackId' in resp: identity = identifier.HeatIdentifier(**resp['StackId']) resp['StackId'] = identity.arn() if 'EventId' in resp: identity = identifier.EventIdentifier(**resp['EventId']) resp['EventId'] = identity.event_id return resp
def _get_stack(self, context, stack_identity): identity = identifier.HeatIdentifier(**stack_identity) if identity.tenant != context.tenant_id: raise AttributeError('Invalid tenant') s = db_api.stack_get(context, identity.stack_id) if s is None: raise AttributeError('Stack not found') if identity.path or s.name != identity.stack_name: raise AttributeError('Invalid stack ID') return s
def test_update(self): identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') stack_name = u'wordpress' stack_id = u'6' template = {u'Foo': u'bar'} json_template = json.dumps(template) parameters = {u'InstanceType': u'm1.xlarge'} body = { 'template': template, 'parameters': parameters, 'timeout_mins': 30 } req = self._put('/stacks/%(stack_name)s/%(stack_id)s' % identity, json.dumps(body)) self.m.StubOutWithMock(rpc, 'call') rpc.call( req.context, self.topic, { 'method': 'update_stack', 'args': { 'stack_identity': dict(identity), 'template': template, 'params': parameters, 'args': { 'timeout_mins': 30 } }, 'version': self.api_version }, None).AndReturn(dict(identity)) self.m.ReplayAll() self.assertRaises(webob.exc.HTTPAccepted, self.controller.update, req, tenant_id=identity.tenant, stack_name=identity.stack_name, stack_id=identity.stack_id, body=body) self.m.VerifyAll()
def test_create(self): identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '1') template = {u'Foo': u'bar'} json_template = json.dumps(template) parameters = {u'InstanceType': u'm1.xlarge'} body = { 'template': template, 'stack_name': identity.stack_name, 'parameters': parameters, 'timeout_mins': 30 } req = self._post('/stacks', json.dumps(body)) self.m.StubOutWithMock(rpc, 'call') rpc.call( req.context, self.topic, { 'method': 'create_stack', 'args': { 'stack_name': identity.stack_name, 'template': template, 'params': parameters, 'args': { 'timeout_mins': 30 } }, 'version': self.api_version }, None).AndReturn(dict(identity)) self.m.ReplayAll() try: response = self.controller.create(req, tenant_id=identity.tenant, body=body) except webob.exc.HTTPCreated as created: self.assertEqual(created.location, self._url(identity)) else: self.fail('HTTPCreated not raised') self.m.VerifyAll()
def test_get_template(self): identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity) template = {u'Foo': u'bar'} self.m.StubOutWithMock(rpc, 'call') rpc.call( req.context, self.topic, { 'method': 'get_template', 'args': { 'stack_identity': dict(identity) }, 'version': self.api_version }, None).AndReturn(template) self.m.ReplayAll() response = self.controller.template(req, tenant_id=identity.tenant, stack_name=identity.stack_name, stack_id=identity.stack_id) self.assertEqual(response, template) self.m.VerifyAll()
def test_show_aterr(self): identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') req = self._get('/stacks/%(stack_name)s/%(stack_id)s' % identity) self.m.StubOutWithMock(rpc, 'call') rpc.call( req.context, self.topic, { 'method': 'show_stack', 'args': { 'stack_identity': dict(identity) }, 'version': self.api_version }, None).AndRaise(rpc_common.RemoteError("AttributeError")) self.m.ReplayAll() self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, req, tenant_id=identity.tenant, stack_name=identity.stack_name, stack_id=identity.stack_id) self.m.VerifyAll()
def test_invalid_attr(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') hi.identity['foo'] = 'bar' self.assertRaises(AttributeError, getattr, hi, 'foo')
def test_items(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertEqual(hi['tenant'], 't') self.assertEqual(hi['stack_name'], 's') self.assertEqual(hi['stack_id'], 'i') self.assertEqual(hi['path'], '/p')
def test_path_default(self): hi = identifier.HeatIdentifier('t', 's', 'i') self.assertEqual(hi.path, '')
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(ei.resource(), pi)
def test_event_init_from_dict(self): hi = identifier.HeatIdentifier('t', 's', 'i', '/resources/p/events/42') ei = identifier.EventIdentifier(**hi) self.assertEqual(ei, hi)
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 : Capabilities, DisableRollback, NotificationARNs keymap = {'TimeoutInMinutes': engine_api.PARAM_TIMEOUT, } result = {} for k in keymap: if k in req.params: result[keymap[k]] = params[k] return result if action not in self.CREATE_OR_UPDATE_ACTION: msg = _("Unexpected action %s" % action) # This should not happen, so return HeatInternalFailureError return exception.HeatInternalFailureError(detail=msg) engine_action = {self.CREATE_STACK: self.engine_rpcapi.create_stack, self.UPDATE_STACK: self.engine_rpcapi.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 = json.loads(templ) except ValueError: msg = _("The Template must be a JSON document.") return exception.HeatInvalidParameterValueError(detail=msg) args = {'template': stack, 'params': stack_parms, '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 rpc_common.RemoteError 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 test_resource_stack(self): si = identifier.HeatIdentifier('t', 's', 'i') ri = identifier.ResourceIdentifier(resource_name='r', **si) self.assertEqual(ri.stack(), si)
def test_resource_init_from_dict(self): hi = identifier.HeatIdentifier('t', 's', 'i', '/resources/r') ri = identifier.ResourceIdentifier(**hi) self.assertEqual(ri, hi)
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(ri.path, '/resources/p/resources/r')
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_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_arn_id_int(self): hi = identifier.HeatIdentifier('t', 's', 42, 'p') self.assertEqual(hi.arn(), 'arn:openstack:heat::t:stacks/s/42/p')
def test_attrs(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p') self.assertEqual(hi.tenant, 't') self.assertEqual(hi.stack_name, 's') self.assertEqual(hi.stack_id, 'i') self.assertEqual(hi.path, '/p')
def test_path_components(self): hi = identifier.HeatIdentifier('t', 's', 'i', 'p1/p2/p3') self.assertEqual(hi._path_components(), ['p1', 'p2', 'p3'])
def test_event_init(self): si = identifier.HeatIdentifier('t', 's', 'i') pi = identifier.ResourceIdentifier(resource_name='p', **si) ei = identifier.EventIdentifier(event_id='e', **pi) self.assertEqual(ei.path, '/resources/p/events/e')