def validate_template(self, req): """ Implements the ValidateTemplate API action Validates the specified template """ con = req.context parms = dict(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: template = json.loads(templ) except ValueError: msg = _("The Template must be a JSON document.") return exception.HeatInvalidParameterValueError(detail=msg) logger.info('validate_template') try: return self.engine_rpcapi.validate_template(con, template) except rpc_common.RemoteError as ex: return exception.map_remote_error(ex)
def validate_template(self, req): """ Implements the ValidateTemplate API action Validates the specified template """ self._enforce(req, 'ValidateTemplate') con = req.context 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: template = template_format.parse(templ) except ValueError: msg = _("The Template must be a JSON or YAML document.") return exception.HeatInvalidParameterValueError(detail=msg) logger.info('validate_template') def format_validate_parameter(key, value): """ Reformat engine output into the AWS "ValidateTemplate" format """ return { 'ParameterKey': key, 'DefaultValue': value.get(engine_api.PARAM_DEFAULT, ''), 'Description': value.get(engine_api.PARAM_DESCRIPTION, ''), 'NoEcho': value.get(engine_api.PARAM_NO_ECHO, 'false') } try: res = self.engine_rpcapi.validate_template(con, template) if 'Error' in res: return api_utils.format_response('ValidateTemplate', res['Error']) res['Parameters'] = [ format_validate_parameter(k, v) for k, v in res['Parameters'].items() ] return api_utils.format_response('ValidateTemplate', res) except Exception as ex: return exception.map_remote_error(ex)
def set_alarm_state(self, req): """ Implements SetAlarmState API action """ self._enforce(req, 'SetAlarmState') # Map from AWS state names to those used in the engine state_map = {'OK': engine_api.WATCH_STATE_OK, 'ALARM': engine_api.WATCH_STATE_ALARM, 'INSUFFICIENT_DATA': engine_api.WATCH_STATE_NODATA} con = req.context parms = dict(req.params) # Get mandatory parameters name = api_utils.get_param_value(parms, 'AlarmName') state = api_utils.get_param_value(parms, 'StateValue') if state not in state_map: msg = _('Invalid state %(state)s, ' 'expecting one of %(expect)s') % { 'state': state, 'expect': state_map.keys()} logger.error(msg) return exception.HeatInvalidParameterValueError(msg) logger.debug("setting %(name)s to %(state)s" % { 'name': name, 'state': state_map[state]}) try: self.rpc_client.set_watch_state(con, watch_name=name, state=state_map[state]) except rpc_common.RemoteError as ex: return exception.map_remote_error(ex) return api_utils.format_response("SetAlarmState", "")
def set_alarm_state(self, req): """ Implements SetAlarmState API action """ self._enforce(req, 'SetAlarmState') # Map from AWS state names to those used in the engine state_map = { 'OK': engine_api.WATCH_STATE_OK, 'ALARM': engine_api.WATCH_STATE_ALARM, 'INSUFFICIENT_DATA': engine_api.WATCH_STATE_NODATA } con = req.context parms = dict(req.params) # Get mandatory parameters name = api_utils.get_param_value(parms, 'AlarmName') state = api_utils.get_param_value(parms, 'StateValue') if state not in state_map: msg = _('Invalid state %(state)s, ' 'expecting one of %(expect)s') % { 'state': state, 'expect': state_map.keys() } logger.error(msg) return exception.HeatInvalidParameterValueError(msg) # Check for optional parameters # FIXME : We don't actually do anything with these in the engine yet.. state_reason = None state_reason_data = None if 'StateReason' in parms: state_reason = parms['StateReason'] if 'StateReasonData' in parms: state_reason_data = parms['StateReasonData'] logger.debug( _("setting %(name)s to %(state)s") % { 'name': name, 'state': state_map[state] }) try: self.engine_rpcapi.set_watch_state(con, watch_name=name, state=state_map[state]) except rpc_common.RemoteError as ex: return exception.map_remote_error(ex) return api_utils.format_response("SetAlarmState", "")
def _get_template(self, req): """ Get template file contents, either from local file or URL """ if 'TemplateBody' in req.params: logger.info('TemplateBody ...') return req.params['TemplateBody'] elif 'TemplateUrl' in req.params: logger.info('TemplateUrl %s' % req.params['TemplateUrl']) url = urlparse.urlparse(req.params['TemplateUrl']) if url.scheme == 'https': conn = httplib.HTTPSConnection(url.netloc) else: conn = httplib.HTTPConnection(url.netloc) try: conn.request("GET", url.path) except Exception as e: if e.errno == errno.ECONNREFUSED: msg = _('Connection refused to %s' % url.netloc) raise exception.HeatInvalidParameterValueError(detail=msg) raise r1 = conn.getresponse() logger.info('status %d' % r1.status) if r1.status == 200: # OK data = r1.read() conn.close() elif r1.status == 404: # NOT_FOUND msg = _('No match found for %s' % req.params['TemplateUrl']) raise exception.HeatInvalidParameterValueError(detail=msg) else: msg = _('Unexpected error, request returned %d' % r1.status) raise exception.HeatInvalidParameterValueError(detail=msg) return data return None
def _get_template(self, req): """Get template file contents, either from local file or URL.""" if 'TemplateBody' in req.params: LOG.debug('TemplateBody ...') return req.params['TemplateBody'] elif 'TemplateUrl' in req.params: url = req.params['TemplateUrl'] LOG.debug('TemplateUrl %s' % url) try: return urlfetch.get(url) except IOError as exc: msg = _('Failed to fetch template: %s') % exc raise exception.HeatInvalidParameterValueError(detail=msg) return None
def get_template(self, req): """ Implements the GetTemplate API action Get the template body for an existing stack """ con = req.context try: identity = self._get_identity(con, req.params['StackName']) templ = self.engine_rpcapi.get_template(con, identity) except rpc_common.RemoteError as ex: return exception.map_remote_error(ex) if templ is None: msg = _('stack not not found') return exception.HeatInvalidParameterValueError(detail=msg) return api_utils.format_response('GetTemplate', {'TemplateBody': templ})
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.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 = 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 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)