Ejemplo n.º 1
0
    def put_metric_data(self, req):
        """
        Implements PutMetricData API action
        """

        con = req.context
        parms = dict(req.params)
        namespace = api_utils.get_param_value(parms, 'Namespace')

        # Extract data from the request so we can pass it to the engine
        # We have to do this in two passes, because the AWS
        # query format nests the dimensions within the MetricData
        # query-parameter-list (see AWS PutMetricData docs)
        # extract_param_list gives a list-of-dict, which we then
        # need to process (each dict) for dimensions
        metric_data = api_utils.extract_param_list(parms, prefix='MetricData')
        if not len(metric_data):
            logger.error("Request does not contain required MetricData")
            return exception.HeatMissingParameterError("MetricData list")

        watch_name = None
        dimensions = []
        for p in metric_data:
            dimension = api_utils.extract_param_pairs(p,
                                                      prefix='Dimensions',
                                                      keyname='Name',
                                                      valuename='Value')
            if 'AlarmName' in dimension:
                watch_name = dimension['AlarmName']
            else:
                dimensions.append(dimension)

        # We expect an AlarmName dimension as currently the engine
        # implementation requires metric data to be associated
        # with an alarm.  When this is fixed, we can simply
        # parse the user-defined dimensions and add the list to
        # the metric data
        if not watch_name:
            logger.error("Request does not contain AlarmName dimension!")
            return exception.HeatMissingParameterError("AlarmName dimension")

        # Extract the required data from the metric_data
        # and format dict to pass to engine
        data = {
            'Namespace': namespace,
            api_utils.get_param_value(metric_data[0], 'MetricName'): {
                'Unit': api_utils.get_param_value(metric_data[0], 'Unit'),
                'Value': api_utils.get_param_value(metric_data[0], 'Value'),
                'Dimensions': dimensions
            }
        }

        try:
            self.engine_rpcapi.create_watch_data(con, watch_name, data)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        result = {'ResponseMetadata': None}
        return api_utils.format_response("PutMetricData", result)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
def get_param_value(params, key):
    """
    Helper function, looks up an expected parameter in a parsed
    params dict and returns the result.  If params does not contain
    the requested key we raise an exception of the appropriate type
    """
    try:
        return params[key]
    except KeyError:
        LOG.error(_("Request does not contain %s parameter!") % key)
        raise exception.HeatMissingParameterError(key)
Ejemplo n.º 4
0
    def put_metric_data(self, req):
        """
        Implements PutMetricData API action
        """
        self._enforce(req, 'PutMetricData')

        con = req.context
        parms = dict(req.params)
        namespace = api_utils.get_param_value(parms, 'Namespace')

        # Extract data from the request so we can pass it to the engine
        # We have to do this in two passes, because the AWS
        # query format nests the dimensions within the MetricData
        # query-parameter-list (see AWS PutMetricData docs)
        # extract_param_list gives a list-of-dict, which we then
        # need to process (each dict) for dimensions
        metric_data = api_utils.extract_param_list(parms, prefix='MetricData')
        if not len(metric_data):
            LOG.error(_LE("Request does not contain required MetricData"))
            return exception.HeatMissingParameterError("MetricData list")

        watch_name = None
        dimensions = []
        for p in metric_data:
            dimension = api_utils.extract_param_pairs(p,
                                                      prefix='Dimensions',
                                                      keyname='Name',
                                                      valuename='Value')
            if 'AlarmName' in dimension:
                watch_name = dimension['AlarmName']
            else:
                dimensions.append(dimension)

        # Extract the required data from the metric_data
        # and format dict to pass to engine
        data = {
            'Namespace': namespace,
            api_utils.get_param_value(metric_data[0], 'MetricName'): {
                'Unit': api_utils.get_param_value(metric_data[0], 'Unit'),
                'Value': api_utils.get_param_value(metric_data[0], 'Value'),
                'Dimensions': dimensions
            }
        }

        try:
            self.rpc_client.create_watch_data(con, watch_name, data)
        except messaging.RemoteError as ex:
            return exception.map_remote_error(ex)

        result = {'ResponseMetadata': None}
        return api_utils.format_response("PutMetricData", result)
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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)