Example #1
0
    def validate_template(self, req):
        """
        Implements the ValidateTemplate API action
        Validates the specified template
        """

        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 = json.loads(templ)
        except ValueError:
            msg = _("The Template must be a JSON document.")
            return exception.HeatInvalidParameterValueError(detail=msg)

        logger.info('validate_template')
        try:
            res = self.engine_rpcapi.validate_template(con, template)
            return api_utils.format_response('ValidateTemplate', res)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)
Example #2
0
    def describe_alarms(self, req):
        """Implements DescribeAlarms API action."""
        self._enforce(req, 'DescribeAlarms')

        def format_metric_alarm(a):
            """Reformat engine output into the AWS "MetricAlarm" format."""
            keymap = {
                rpc_api.WATCH_ACTIONS_ENABLED: 'ActionsEnabled',
                rpc_api.WATCH_ALARM_ACTIONS: 'AlarmActions',
                rpc_api.WATCH_TOPIC: 'AlarmArn',
                rpc_api.WATCH_UPDATED_TIME:
                'AlarmConfigurationUpdatedTimestamp',
                rpc_api.WATCH_DESCRIPTION: 'AlarmDescription',
                rpc_api.WATCH_NAME: 'AlarmName',
                rpc_api.WATCH_COMPARISON: 'ComparisonOperator',
                rpc_api.WATCH_DIMENSIONS: 'Dimensions',
                rpc_api.WATCH_PERIODS: 'EvaluationPeriods',
                rpc_api.WATCH_INSUFFICIENT_ACTIONS:
                'InsufficientDataActions',
                rpc_api.WATCH_METRIC_NAME: 'MetricName',
                rpc_api.WATCH_NAMESPACE: 'Namespace',
                rpc_api.WATCH_OK_ACTIONS: 'OKActions',
                rpc_api.WATCH_PERIOD: 'Period',
                rpc_api.WATCH_STATE_REASON: 'StateReason',
                rpc_api.WATCH_STATE_REASON_DATA: 'StateReasonData',
                rpc_api.WATCH_STATE_UPDATED_TIME: 'StateUpdatedTimestamp',
                rpc_api.WATCH_STATE_VALUE: 'StateValue',
                rpc_api.WATCH_STATISTIC: 'Statistic',
                rpc_api.WATCH_THRESHOLD: 'Threshold',
                rpc_api.WATCH_UNIT: 'Unit',
            }

            # AWS doesn't return StackId in the main MetricAlarm
            # structure, so we add StackId as a dimension to all responses
            a[rpc_api.WATCH_DIMENSIONS].append({'StackId':
                                                a[rpc_api.WATCH_STACK_ID]})

            # Reformat dimensions list into AWS API format
            a[rpc_api.WATCH_DIMENSIONS] = self._reformat_dimensions(
                a[rpc_api.WATCH_DIMENSIONS])

            return api_utils.reformat_dict_keys(keymap, a)

        con = req.context
        parms = dict(req.params)
        try:
            name = parms['AlarmName']
        except KeyError:
            name = None

        try:
            watch_list = self.rpc_client.show_watch(con, watch_name=name)
        except messaging.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {'MetricAlarms': [format_metric_alarm(a)
                                for a in watch_list]}

        result = api_utils.format_response("DescribeAlarms", res)
        return result
Example #3
0
    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", "")
Example #4
0
    def describe_stack_resources(self, req):
        """Implements the DescribeStackResources API action.

        Return details of resources specified by the parameters.

        `StackName`: returns all resources belonging to the stack.

        `PhysicalResourceId`: returns all resources belonging to the stack this
        resource is associated with.

        Only one of the parameters may be specified.

        Optional parameter:

        `LogicalResourceId`: filter the resources list by the logical resource
        id.
        """
        self._enforce(req, "DescribeStackResources")

        def format_stack_resource(r):
            """Reformat engine output into AWS "StackResource" format."""
            keymap = {
                rpc_api.RES_DESCRIPTION: "Description",
                rpc_api.RES_NAME: "LogicalResourceId",
                rpc_api.RES_PHYSICAL_ID: "PhysicalResourceId",
                rpc_api.RES_STATUS_DATA: "ResourceStatusReason",
                rpc_api.RES_TYPE: "ResourceType",
                rpc_api.RES_STACK_ID: "StackId",
                rpc_api.RES_STACK_NAME: "StackName",
                rpc_api.RES_UPDATED_TIME: "Timestamp",
            }

            result = api_utils.reformat_dict_keys(keymap, r)

            result["ResourceStatus"] = self._resource_status(r)

            return self._id_format(result)

        con = req.context
        stack_name = req.params.get("StackName")
        physical_resource_id = req.params.get("PhysicalResourceId")
        if stack_name and physical_resource_id:
            msg = "Use `StackName` or `PhysicalResourceId` but not both"
            return exception.HeatInvalidParameterCombinationError(detail=msg)

        try:
            if stack_name is not None:
                identity = self._get_identity(con, stack_name)
            else:
                identity = self.rpc_client.find_physical_resource(con, physical_resource_id=physical_resource_id)
            resources = self.rpc_client.describe_stack_resources(
                con, stack_identity=identity, resource_name=req.params.get("LogicalResourceId")
            )

        except Exception as ex:
            return exception.map_remote_error(ex)

        result = [format_stack_resource(r) for r in resources]

        return api_utils.format_response("DescribeStackResources", {"StackResources": result})
Example #5
0
    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", "")
Example #6
0
    def list_stack_resources(self, req):
        """
        Implements the ListStackResources API action
        Return summary of the resources belonging to the specified stack.
        """
        def format_resource_summary(r):
            """
            Reformat engine output into the AWS "StackResourceSummary" format
            """
            keymap = {
                engine_api.RES_UPDATED_TIME: 'LastUpdatedTimestamp',
                engine_api.RES_NAME: 'LogicalResourceId',
                engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.RES_STATUS: 'ResourceStatus',
                engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.RES_TYPE: 'ResourceType',
            }

            return api_utils.reformat_dict_keys(keymap, r)

        con = req.context

        try:
            identity = self._get_identity(con, req.params['StackName'])
            resources = self.engine_rpcapi.list_stack_resources(con,
                    stack_identity=identity)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        summaries = [format_resource_summary(r) for r in resources]

        return api_utils.format_response('ListStackResources',
            {'StackResourceSummaries': summaries})
Example #7
0
    def list_stack_resources(self, req):
        """Implements the ListStackResources API action.

        Return summary of the resources belonging to the specified stack.
        """
        self._enforce(req, "ListStackResources")

        def format_resource_summary(r):
            """Reformat engine output to AWS "StackResourceSummary" format."""
            keymap = {
                rpc_api.RES_UPDATED_TIME: "LastUpdatedTimestamp",
                rpc_api.RES_NAME: "LogicalResourceId",
                rpc_api.RES_PHYSICAL_ID: "PhysicalResourceId",
                rpc_api.RES_STATUS_DATA: "ResourceStatusReason",
                rpc_api.RES_TYPE: "ResourceType",
            }

            result = api_utils.reformat_dict_keys(keymap, r)

            result["ResourceStatus"] = self._resource_status(r)

            return result

        con = req.context

        try:
            identity = self._get_identity(con, req.params["StackName"])
            resources = self.rpc_client.list_stack_resources(con, stack_identity=identity)
        except Exception as ex:
            return exception.map_remote_error(ex)

        summaries = [format_resource_summary(r) for r in resources]

        return api_utils.format_response("ListStackResources", {"StackResourceSummaries": summaries})
Example #8
0
File: watch.py Project: COSHPC/heat
    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()}
            LOG.error(msg)
            return exception.HeatInvalidParameterValueError(msg)

        LOG.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 messaging.RemoteError as ex:
            return exception.map_remote_error(ex)

        return api_utils.format_response("SetAlarmState", "")
Example #9
0
    def list_metrics(self, req):
        """
        Implements ListMetrics API action
        Lists metric datapoints associated with a particular alarm,
        or all alarms if none specified
        """
        self._enforce(req, "ListMetrics")

        def format_metric_data(d, fil={}):
            """
            Reformat engine output into the AWS "Metric" format
            Takes an optional filter dict, which is traversed
            so a metric dict is only returned if all keys match
            the filter dict
            """
            dimensions = [{"AlarmName": d[engine_api.WATCH_DATA_ALARM]}, {"Timestamp": d[engine_api.WATCH_DATA_TIME]}]
            for key in d[engine_api.WATCH_DATA]:
                dimensions.append({key: d[engine_api.WATCH_DATA][key]})

            newdims = self._reformat_dimensions(dimensions)

            result = {
                "MetricName": d[engine_api.WATCH_DATA_METRIC],
                "Dimensions": newdims,
                "Namespace": d[engine_api.WATCH_DATA_NAMESPACE],
            }

            for f in fil:
                try:
                    value = result[f]
                    if value != fil[f]:
                        # Filter criteria not met, return None
                        return
                except KeyError:
                    logger.warning(_("Invalid filter key %s, ignoring") % f)

            return result

        con = req.context
        parms = dict(req.params)
        # FIXME : Don't yet handle filtering by Dimensions
        filter_result = dict((k, v) for (k, v) in parms.iteritems() if k in ("MetricName", "Namespace"))
        logger.debug(_("filter parameters : %s") % filter_result)

        try:
            # Engine does not currently support query by namespace/metric
            # so we pass None/None and do any filtering locally
            null_kwargs = {"metric_namespace": None, "metric_name": None}
            watch_data = self.rpc_client.show_watch_metric(con, **null_kwargs)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {"Metrics": []}
        for d in watch_data:
            metric = format_metric_data(d, filter_result)
            if metric:
                res["Metrics"].append(metric)

        result = api_utils.format_response("ListMetrics", res)
        return result
Example #10
0
    def list_stack_resources(self, req):
        """
        Implements the ListStackResources API action
        Return summary of the resources belonging to the specified stack.
        """
        def format_resource_summary(r):
            """
            Reformat engine output into the AWS "StackResourceSummary" format
            """
            keymap = {
                engine_api.RES_UPDATED_TIME: 'LastUpdatedTimestamp',
                engine_api.RES_NAME: 'LogicalResourceId',
                engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.RES_STATUS: 'ResourceStatus',
                engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.RES_TYPE: 'ResourceType',
            }

            return api_utils.reformat_dict_keys(keymap, r)

        con = req.context

        try:
            identity = self._get_identity(con, req.params['StackName'])
            resources = self.engine_rpcapi.list_stack_resources(
                con,
                stack_identity=identity)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        summaries = [format_resource_summary(r) for r in resources]

        return api_utils.format_response('ListStackResources',
                                         {'StackResourceSummaries': summaries})
Example #11
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)
Example #12
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)
Example #13
0
    def describe_alarms(self, req):
        """
        Implements DescribeAlarms API action
        """
        self._enforce(req, "DescribeAlarms")

        def format_metric_alarm(a):
            """
            Reformat engine output into the AWS "MetricAlarm" format
            """
            keymap = {
                engine_api.WATCH_ACTIONS_ENABLED: "ActionsEnabled",
                engine_api.WATCH_ALARM_ACTIONS: "AlarmActions",
                engine_api.WATCH_TOPIC: "AlarmArn",
                engine_api.WATCH_UPDATED_TIME: "AlarmConfigurationUpdatedTimestamp",
                engine_api.WATCH_DESCRIPTION: "AlarmDescription",
                engine_api.WATCH_NAME: "AlarmName",
                engine_api.WATCH_COMPARISON: "ComparisonOperator",
                engine_api.WATCH_DIMENSIONS: "Dimensions",
                engine_api.WATCH_PERIODS: "EvaluationPeriods",
                engine_api.WATCH_INSUFFICIENT_ACTIONS: "InsufficientDataActions",
                engine_api.WATCH_METRIC_NAME: "MetricName",
                engine_api.WATCH_NAMESPACE: "Namespace",
                engine_api.WATCH_OK_ACTIONS: "OKActions",
                engine_api.WATCH_PERIOD: "Period",
                engine_api.WATCH_STATE_REASON: "StateReason",
                engine_api.WATCH_STATE_REASON_DATA: "StateReasonData",
                engine_api.WATCH_STATE_UPDATED_TIME: "StateUpdatedTimestamp",
                engine_api.WATCH_STATE_VALUE: "StateValue",
                engine_api.WATCH_STATISTIC: "Statistic",
                engine_api.WATCH_THRESHOLD: "Threshold",
                engine_api.WATCH_UNIT: "Unit",
            }

            # AWS doesn't return StackId in the main MetricAlarm
            # structure, so we add StackId as a dimension to all responses
            a[engine_api.WATCH_DIMENSIONS].append({"StackId": a[engine_api.WATCH_STACK_ID]})

            # Reformat dimensions list into AWS API format
            a[engine_api.WATCH_DIMENSIONS] = self._reformat_dimensions(a[engine_api.WATCH_DIMENSIONS])

            return api_utils.reformat_dict_keys(keymap, a)

        con = req.context
        parms = dict(req.params)
        try:
            name = parms["AlarmName"]
        except KeyError:
            name = None

        try:
            watch_list = self.rpc_client.show_watch(con, watch_name=name)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {"MetricAlarms": [format_metric_alarm(a) for a in watch_list]}

        result = api_utils.format_response("DescribeAlarms", res)
        return result
Example #14
0
    def describe_stack_resources(self, req):
        """
        Implements the DescribeStackResources API action
        Return details of resources specified by the parameters.

        `StackName`: returns all resources belonging to the stack
        `PhysicalResourceId`: returns all resources belonging to the stack this
                              resource is associated with.

        Only one of the parameters may be specified.

        Optional parameter:

        `LogicalResourceId`: filter the resources list by the logical resource
        id.
        """

        def format_stack_resource(r):
            """
            Reformat engine output into the AWS "StackResource" format
            """
            keymap = {
                engine_api.RES_DESCRIPTION: 'Description',
                engine_api.RES_NAME: 'LogicalResourceId',
                engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.RES_STATUS: 'ResourceStatus',
                engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.RES_TYPE: 'ResourceType',
                engine_api.RES_STACK_ID: 'StackId',
                engine_api.RES_STACK_NAME: 'StackName',
                engine_api.RES_UPDATED_TIME: 'Timestamp',
            }

            result = api_utils.reformat_dict_keys(keymap, r)

            return self._stackid_format(result)

        con = req.context
        stack_name = req.params.get('StackName')
        physical_resource_id = req.params.get('PhysicalResourceId')
        if stack_name and physical_resource_id:
            msg = 'Use `StackName` or `PhysicalResourceId` but not both'
            return exception.HeatInvalidParameterCombinationError(detail=msg)

        try:
            identity = self._get_identity(con, stack_name)
            resources = self.engine_rpcapi.describe_stack_resources(con,
                stack_identity=identity,
                physical_resource_id=physical_resource_id,
                logical_resource_id=req.params.get('LogicalResourceId'))

        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        result = [format_stack_resource(r) for r in resources]

        return api_utils.format_response('DescribeStackResources',
            {'StackResources': result})
Example #15
0
    def describe_stack_resources(self, req):
        """
        Implements the DescribeStackResources API action
        Return details of resources specified by the parameters.

        `StackName`: returns all resources belonging to the stack
        `PhysicalResourceId`: returns all resources belonging to the stack this
                              resource is associated with.

        Only one of the parameters may be specified.

        Optional parameter:

        `LogicalResourceId`: filter the resources list by the logical resource
        id.
        """

        def format_stack_resource(r):
            """
            Reformat engine output into the AWS "StackResource" format
            """
            keymap = {
                engine_api.RES_DESCRIPTION: 'Description',
                engine_api.RES_NAME: 'LogicalResourceId',
                engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.RES_STATUS: 'ResourceStatus',
                engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.RES_TYPE: 'ResourceType',
                engine_api.RES_STACK_ID: 'StackId',
                engine_api.RES_STACK_NAME: 'StackName',
                engine_api.RES_UPDATED_TIME: 'Timestamp',
            }

            result = api_utils.reformat_dict_keys(keymap, r)

            return self._stackid_format(result)

        con = req.context
        stack_name = req.params.get('StackName')
        physical_resource_id = req.params.get('PhysicalResourceId')
        if stack_name and physical_resource_id:
            msg = 'Use `StackName` or `PhysicalResourceId` but not both'
            return exception.HeatInvalidParameterCombinationError(detail=msg)

        try:
            identity = self._get_identity(con, stack_name)
            resources = self.engine_rpcapi.describe_stack_resources(con,
                stack_identity=identity,
                physical_resource_id=physical_resource_id,
                logical_resource_id=req.params.get('LogicalResourceId'))

        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        result = [format_stack_resource(r) for r in resources]

        return api_utils.format_response('DescribeStackResources',
            {'StackResources': result})
Example #16
0
    def describe_alarms(self, req):
        """Implements DescribeAlarms API action."""
        self._enforce(req, 'DescribeAlarms')

        def format_metric_alarm(a):
            """Reformat engine output into the AWS "MetricAlarm" format."""
            keymap = {
                rpc_api.WATCH_ACTIONS_ENABLED: 'ActionsEnabled',
                rpc_api.WATCH_ALARM_ACTIONS: 'AlarmActions',
                rpc_api.WATCH_TOPIC: 'AlarmArn',
                rpc_api.WATCH_UPDATED_TIME:
                'AlarmConfigurationUpdatedTimestamp',
                rpc_api.WATCH_DESCRIPTION: 'AlarmDescription',
                rpc_api.WATCH_NAME: 'AlarmName',
                rpc_api.WATCH_COMPARISON: 'ComparisonOperator',
                rpc_api.WATCH_DIMENSIONS: 'Dimensions',
                rpc_api.WATCH_PERIODS: 'EvaluationPeriods',
                rpc_api.WATCH_INSUFFICIENT_ACTIONS: 'InsufficientDataActions',
                rpc_api.WATCH_METRIC_NAME: 'MetricName',
                rpc_api.WATCH_NAMESPACE: 'Namespace',
                rpc_api.WATCH_OK_ACTIONS: 'OKActions',
                rpc_api.WATCH_PERIOD: 'Period',
                rpc_api.WATCH_STATE_REASON: 'StateReason',
                rpc_api.WATCH_STATE_REASON_DATA: 'StateReasonData',
                rpc_api.WATCH_STATE_UPDATED_TIME: 'StateUpdatedTimestamp',
                rpc_api.WATCH_STATE_VALUE: 'StateValue',
                rpc_api.WATCH_STATISTIC: 'Statistic',
                rpc_api.WATCH_THRESHOLD: 'Threshold',
                rpc_api.WATCH_UNIT: 'Unit',
            }

            # AWS doesn't return StackId in the main MetricAlarm
            # structure, so we add StackId as a dimension to all responses
            a[rpc_api.WATCH_DIMENSIONS].append(
                {'StackId': a[rpc_api.WATCH_STACK_ID]})

            # Reformat dimensions list into AWS API format
            a[rpc_api.WATCH_DIMENSIONS] = self._reformat_dimensions(
                a[rpc_api.WATCH_DIMENSIONS])

            return api_utils.reformat_dict_keys(keymap, a)

        con = req.context
        parms = dict(req.params)
        try:
            name = parms['AlarmName']
        except KeyError:
            name = None

        try:
            watch_list = self.rpc_client.show_watch(con, watch_name=name)
        except messaging.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {'MetricAlarms': [format_metric_alarm(a) for a in watch_list]}

        result = api_utils.format_response("DescribeAlarms", res)
        return result
Example #17
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):
            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)
Example #18
0
 def signal(self, req, arn, body=None):
     con = req.context
     identity = identifier.ResourceIdentifier.from_arn(arn)
     try:
         self.rpc_client.resource_signal(
             con, stack_identity=dict(identity.stack()), resource_name=identity.resource_name, details=body
         )
     except Exception as ex:
         return exception.map_remote_error(ex)
Example #19
0
 def signal(self, req, arn, body=None):
     con = req.context
     identity = identifier.ResourceIdentifier.from_arn(arn)
     try:
         self.engine.resource_signal(con,
                                     stack_identity=dict(identity.stack()),
                                     resource_name=identity.resource_name,
                                     details=body)
     except Exception as ex:
         return exception.map_remote_error(ex)
Example #20
0
    def update_waitcondition(self, req, body, arn):
        con = req.context
        identity = identifier.ResourceIdentifier.from_arn(arn)
        try:
            md = self.engine.metadata_update(
                con, stack_identity=dict(identity.stack()), resource_name=identity.resource_name, metadata=body
            )
        except Exception as ex:
            return exception.map_remote_error(ex)

        return {"resource": identity.resource_name, "metadata": md}
Example #21
0
 def signal(self, req, body, arn):
     con = req.context
     identity = identifier.ResourceIdentifier.from_arn(arn)
     try:
         md = self.engine.resource_signal(
             con,
             stack_identity=dict(identity.stack()),
             resource_name=identity.resource_name,
             details=body)
     except rpc_common.RemoteError as ex:
         return exception.map_remote_error(ex)
Example #22
0
    def cancel_update(self, req):
        action = "CancelUpdateStack"
        self._enforce(req, action)
        con = req.context
        stack_name = req.params["StackName"]
        stack_identity = self._get_identity(con, stack_name)
        try:
            self.rpc_client.stack_cancel_update(con, stack_identity=stack_identity, cancel_with_rollback=True)
        except Exception as ex:
            return exception.map_remote_error(ex)

        return api_utils.format_response(action, {})
Example #23
0
    def cancel_update(self, req):
        action = 'CancelUpdateStack'
        self._enforce(req, action)
        con = req.context
        stack_name = req.params['StackName']
        stack_identity = self._get_identity(con, stack_name)
        try:
            self.rpc_client.stack_cancel_update(
                con, stack_identity=stack_identity)
        except Exception as ex:
            return exception.map_remote_error(ex)

        return api_utils.format_response(action, {})
Example #24
0
    def update_waitcondition(self, req, body, arn):
        con = req.context
        identity = identifier.ResourceIdentifier.from_arn(arn)
        try:
            md = self.rpc_client.metadata_update(
                con,
                stack_identity=dict(identity.stack()),
                resource_name=identity.resource_name,
                metadata=body)
        except Exception as ex:
            return exception.map_remote_error(ex)

        return {'resource': identity.resource_name, 'metadata': md}
Example #25
0
    def update_waitcondition(self, req, body, arn):
        con = req.context
        identity = identifier.ResourceIdentifier.from_arn(arn)
        try:
            md = self.rpc_client.resource_signal(
                con,
                stack_identity=dict(identity.stack()),
                resource_name=identity.resource_name,
                details=body)
        except Exception as ex:
            return exception.map_remote_error(ex)

        return {'resource': identity.resource_name, 'metadata': md}
Example #26
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)
Example #27
0
    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", "")
Example #28
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)
Example #29
0
    def get_template(self, req):
        """Implements the GetTemplate API action.

        Get the template body for an existing stack.
        """
        self._enforce(req, "GetTemplate")

        con = req.context
        try:
            identity = self._get_identity(con, req.params["StackName"])
            templ = self.rpc_client.get_template(con, identity)
        except Exception as ex:
            return exception.map_remote_error(ex)

        return api_utils.format_response("GetTemplate", {"TemplateBody": templ})
Example #30
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 rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)
Example #31
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):
            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)

        # 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 rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        result = {"ResponseMetadata": None}
        return api_utils.format_response("PutMetricData", result)
Example #32
0
    def events_list(self, req):
        """
        Implements the DescribeStackEvents API action
        Returns events related to a specified stack (or all stacks)
        """
        self._enforce(req, 'DescribeStackEvents')

        def format_stack_event(e):
            """
            Reformat engine output into the AWS "StackEvent" format
            """
            keymap = {
                engine_api.EVENT_ID: 'EventId',
                engine_api.EVENT_RES_NAME: 'LogicalResourceId',
                engine_api.EVENT_RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.EVENT_RES_PROPERTIES: 'ResourceProperties',
                engine_api.EVENT_RES_STATUS: 'ResourceStatus',
                engine_api.EVENT_RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.EVENT_RES_TYPE: 'ResourceType',
                engine_api.EVENT_STACK_ID: 'StackId',
                engine_api.EVENT_STACK_NAME: 'StackName',
                engine_api.EVENT_TIMESTAMP: 'Timestamp',
            }

            result = api_utils.reformat_dict_keys(keymap, e)
            action = e[engine_api.EVENT_RES_ACTION]
            status = e[engine_api.EVENT_RES_STATUS]
            if action and status:
                result['ResourceStatus'] = '_'.join((action, status))
            else:
                result['ResourceStatus'] = status
            result['ResourceProperties'] = json.dumps(result[
                                                      'ResourceProperties'])

            return self._id_format(result)

        con = req.context
        stack_name = req.params.get('StackName', None)
        try:
            identity = stack_name and self._get_identity(con, stack_name)
            events = self.engine_rpcapi.list_events(con, identity)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        result = [format_stack_event(e) for e in events]

        return api_utils.format_response('DescribeStackEvents',
                                         {'StackEvents': result})
Example #33
0
    def get_template(self, req):
        """Implements the GetTemplate API action.

        Get the template body for an existing stack.
        """
        self._enforce(req, 'GetTemplate')

        con = req.context
        try:
            identity = self._get_identity(con, req.params['StackName'])
            templ = self.rpc_client.get_template(con, identity)
        except Exception as ex:
            return exception.map_remote_error(ex)

        return api_utils.format_response('GetTemplate',
                                         {'TemplateBody': templ})
Example #34
0
    def list(self, req):
        """
        Implements ListStacks API action
        Lists summary information for all stacks
        """

        def format_stack_summary(s):
            """
            Reformat engine output into the AWS "StackSummary" format
            """
            # Map the engine-api format to the AWS StackSummary datatype
            keymap = {
                engine_api.STACK_CREATION_TIME: 'CreationTime',
                engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
                engine_api.STACK_ID: 'StackId',
                engine_api.STACK_NAME: 'StackName',
                engine_api.STACK_STATUS: 'StackStatus',
                engine_api.STACK_STATUS_DATA: 'StackStatusReason',
                engine_api.STACK_TMPL_DESCRIPTION: 'TemplateDescription',
            }

            result = api_utils.reformat_dict_keys(keymap, s)

            # AWS docs indicate DeletionTime is ommitted for current stacks
            # This is still TODO in the engine, we don't keep data for
            # stacks after they are deleted
            if engine_api.STACK_DELETION_TIME in s:
                result['DeletionTime'] = s[engine_api.STACK_DELETION_TIME]

            return self._stackid_addprefix(result)

        con = req.context
        parms = dict(req.params)

        try:
            # Note show_stack returns details for all stacks when called with
            # no stack_name, we only use a subset of the result here though
            stack_list = self.engine_rpcapi.show_stack(con,
                                                       stack_name=None,
                                                       params=parms)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {'StackSummaries': [format_stack_summary(s)
                                   for s in stack_list['stacks']]}

        return api_utils.format_response('ListStacks', res)
Example #35
0
    def list(self, req):
        """
        Implements ListStacks API action
        Lists summary information for all stacks
        """
        def format_stack_summary(s):
            """
            Reformat engine output into the AWS "StackSummary" format
            """
            # Map the engine-api format to the AWS StackSummary datatype
            keymap = {
                engine_api.STACK_CREATION_TIME: 'CreationTime',
                engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
                engine_api.STACK_ID: 'StackId',
                engine_api.STACK_NAME: 'StackName',
                engine_api.STACK_STATUS: 'StackStatus',
                engine_api.STACK_STATUS_DATA: 'StackStatusReason',
                engine_api.STACK_TMPL_DESCRIPTION: 'TemplateDescription',
            }

            result = api_utils.reformat_dict_keys(keymap, s)

            # AWS docs indicate DeletionTime is ommitted for current stacks
            # This is still TODO in the engine, we don't keep data for
            # stacks after they are deleted
            if engine_api.STACK_DELETION_TIME in s:
                result['DeletionTime'] = s[engine_api.STACK_DELETION_TIME]

            return self._stackid_format(result)

        con = req.context
        parms = dict(req.params)

        try:
            # Note show_stack returns details for all stacks when called with
            # no stack_name, we only use a subset of the result here though
            stack_list = self.engine_rpcapi.show_stack(con, None)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {
            'StackSummaries':
            [format_stack_summary(s) for s in stack_list['stacks']]
        }

        return api_utils.format_response('ListStacks', res)
Example #36
0
    def delete(self, req):
        """
        Implements the DeleteStack API action
        Deletes the specified stack
        """
        con = req.context
        try:
            identity = self._get_identity(con, req.params['StackName'])
            res = self.engine_rpcapi.delete_stack(con, identity, cast=False)

        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        if res is None:
            return api_utils.format_response('DeleteStack', '')
        else:
            return api_utils.format_response('DeleteStack', res['Error'])
Example #37
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)
Example #38
0
    def delete(self, req):
        """
        Implements the DeleteStack API action
        Deletes the specified stack
        """
        con = req.context
        try:
            identity = self._get_identity(con, req.params['StackName'])
            res = self.engine_rpcapi.delete_stack(con, identity, cast=False)

        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        if res is None:
            return api_utils.format_response('DeleteStack', '')
        else:
            return api_utils.format_response('DeleteStack', res['Error'])
Example #39
0
    def describe_stack_resource(self, req):
        """
        Implements the DescribeStackResource API action
        Return the details of the given resource belonging to the given stack.
        """
        self._enforce(req, 'DescribeStackResource')

        def format_resource_detail(r):
            """
            Reformat engine output into the AWS "StackResourceDetail" format
            """
            keymap = {
                engine_api.RES_DESCRIPTION: 'Description',
                engine_api.RES_UPDATED_TIME: 'LastUpdatedTimestamp',
                engine_api.RES_NAME: 'LogicalResourceId',
                engine_api.RES_METADATA: 'Metadata',
                engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.RES_TYPE: 'ResourceType',
                engine_api.RES_STACK_ID: 'StackId',
                engine_api.RES_STACK_NAME: 'StackName',
            }

            result = api_utils.reformat_dict_keys(keymap, r)

            result['ResourceStatus'] = self._resource_status(r)

            return self._id_format(result)

        con = req.context

        try:
            identity = self._get_identity(con, req.params['StackName'])
            resource_details = self.engine_rpcapi.describe_stack_resource(
                con,
                stack_identity=identity,
                resource_name=req.params.get('LogicalResourceId'))

        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        result = format_resource_detail(resource_details)

        return api_utils.format_response('DescribeStackResource',
                                         {'StackResourceDetail': result})
Example #40
0
    def describe_stack_resource(self, req):
        """
        Implements the DescribeStackResource API action
        Return the details of the given resource belonging to the given stack.
        """
        self._enforce(req, 'DescribeStackResource')

        def format_resource_detail(r):
            """
            Reformat engine output into the AWS "StackResourceDetail" format
            """
            keymap = {
                engine_api.RES_DESCRIPTION: 'Description',
                engine_api.RES_UPDATED_TIME: 'LastUpdatedTimestamp',
                engine_api.RES_NAME: 'LogicalResourceId',
                engine_api.RES_METADATA: 'Metadata',
                engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.RES_TYPE: 'ResourceType',
                engine_api.RES_STACK_ID: 'StackId',
                engine_api.RES_STACK_NAME: 'StackName',
            }

            result = api_utils.reformat_dict_keys(keymap, r)

            result['ResourceStatus'] = self._resource_status(r)

            return self._id_format(result)

        con = req.context

        try:
            identity = self._get_identity(con, req.params['StackName'])
            resource_details = self.engine_rpcapi.describe_stack_resource(
                con,
                stack_identity=identity,
                resource_name=req.params.get('LogicalResourceId'))

        except Exception as ex:
            return exception.map_remote_error(ex)

        result = format_resource_detail(resource_details)

        return api_utils.format_response('DescribeStackResource',
                                         {'StackResourceDetail': result})
Example #41
0
    def list(self, req):
        """
        Implements ListStacks API action
        Lists summary information for all stacks
        """
        self._enforce(req, 'ListStacks')

        def format_stack_summary(s):
            """
            Reformat engine output into the AWS "StackSummary" format
            """
            # Map the engine-api format to the AWS StackSummary datatype
            keymap = {
                engine_api.STACK_CREATION_TIME: 'CreationTime',
                engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
                engine_api.STACK_ID: 'StackId',
                engine_api.STACK_NAME: 'StackName',
                engine_api.STACK_STATUS_DATA: 'StackStatusReason',
                engine_api.STACK_TMPL_DESCRIPTION: 'TemplateDescription',
            }

            result = api_utils.reformat_dict_keys(keymap, s)

            action = s[engine_api.STACK_ACTION]
            status = s[engine_api.STACK_STATUS]
            result['StackStatus'] = '_'.join((action, status))

            # AWS docs indicate DeletionTime is ommitted for current stacks
            # This is still TODO(unknown) in the engine, we don't keep data for
            # stacks after they are deleted
            if engine_api.STACK_DELETION_TIME in s:
                result['DeletionTime'] = s[engine_api.STACK_DELETION_TIME]

            return self._id_format(result)

        con = req.context
        try:
            stack_list = self.engine_rpcapi.list_stacks(con)
        except Exception as ex:
            return exception.map_remote_error(ex)

        res = {'StackSummaries': [format_stack_summary(s) for s in stack_list]}

        return api_utils.format_response('ListStacks', res)
Example #42
0
    def list(self, req):
        """
        Implements ListStacks API action
        Lists summary information for all stacks
        """
        self._enforce(req, 'ListStacks')

        def format_stack_summary(s):
            """
            Reformat engine output into the AWS "StackSummary" format
            """
            # Map the engine-api format to the AWS StackSummary datatype
            keymap = {
                engine_api.STACK_CREATION_TIME: 'CreationTime',
                engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
                engine_api.STACK_ID: 'StackId',
                engine_api.STACK_NAME: 'StackName',
                engine_api.STACK_STATUS_DATA: 'StackStatusReason',
                engine_api.STACK_TMPL_DESCRIPTION: 'TemplateDescription',
            }

            result = api_utils.reformat_dict_keys(keymap, s)

            action = s[engine_api.STACK_ACTION]
            status = s[engine_api.STACK_STATUS]
            result['StackStatus'] = '_'.join((action, status))

            # AWS docs indicate DeletionTime is ommitted for current stacks
            # This is still TODO(unknown) in the engine, we don't keep data for
            # stacks after they are deleted
            if engine_api.STACK_DELETION_TIME in s:
                result['DeletionTime'] = s[engine_api.STACK_DELETION_TIME]

            return self._id_format(result)

        con = req.context
        try:
            stack_list = self.engine_rpcapi.list_stacks(con)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {'StackSummaries': [format_stack_summary(s) for s in stack_list]}

        return api_utils.format_response('ListStacks', res)
Example #43
0
File: watch.py Project: cmukai/heat
    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", "")
Example #44
0
    def events_list(self, req):
        """
        Implements the DescribeStackEvents API action.
        Returns events related to a specified stack (or all stacks).
        """
        self._enforce(req, 'DescribeStackEvents')

        def format_stack_event(e):
            """
            Reformat engine output into the AWS "StackEvent" format
            """
            keymap = {
                engine_api.EVENT_ID: 'EventId',
                engine_api.EVENT_RES_NAME: 'LogicalResourceId',
                engine_api.EVENT_RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.EVENT_RES_PROPERTIES: 'ResourceProperties',
                engine_api.EVENT_RES_STATUS_DATA: 'ResourceStatusReason',
                engine_api.EVENT_RES_TYPE: 'ResourceType',
                engine_api.EVENT_STACK_ID: 'StackId',
                engine_api.EVENT_STACK_NAME: 'StackName',
                engine_api.EVENT_TIMESTAMP: 'Timestamp',
            }

            result = api_utils.reformat_dict_keys(keymap, e)
            action = e[engine_api.EVENT_RES_ACTION]
            status = e[engine_api.EVENT_RES_STATUS]
            result['ResourceStatus'] = '_'.join((action, status))
            result['ResourceProperties'] = json.dumps(result[
                                                      'ResourceProperties'])

            return self._id_format(result)

        con = req.context
        stack_name = req.params.get('StackName', None)
        try:
            identity = stack_name and self._get_identity(con, stack_name)
            events = self.engine_rpcapi.list_events(con, identity)
        except Exception as ex:
            return exception.map_remote_error(ex)

        result = [format_stack_event(e) for e in events]

        return api_utils.format_response('DescribeStackEvents',
                                         {'StackEvents': result})
Example #45
0
    def get_template(self, req):
        """
        Implements the GetTemplate API action.
        Get the template body for an existing stack.
        """
        self._enforce(req, "GetTemplate")

        con = req.context
        try:
            identity = self._get_identity(con, req.params["StackName"])
            templ = self.engine_rpcapi.get_template(con, identity)
        except Exception 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})
Example #46
0
    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})
Example #47
0
    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})
Example #48
0
File: stacks.py Project: srz01/heat
    def delete(self, req):
        """
        Implements the DeleteStack API action.
        Deletes the specified stack.
        """
        self._enforce(req, 'DeleteStack')

        con = req.context
        try:
            identity = self._get_identity(con, req.params['StackName'])
            res = self.rpc_client.delete_stack(con, identity, cast=False)

        except Exception as ex:
            return exception.map_remote_error(ex)

        if res is None:
            return api_utils.format_response('DeleteStack', '')
        else:
            return api_utils.format_response('DeleteStack', res['Error'])
Example #49
0
    def delete(self, req):
        """Implements the DeleteStack API action.

        Deletes the specified stack.
        """
        self._enforce(req, "DeleteStack")

        con = req.context
        try:
            identity = self._get_identity(con, req.params["StackName"])
            res = self.rpc_client.delete_stack(con, identity, cast=False)

        except Exception as ex:
            return exception.map_remote_error(ex)

        if res is None:
            return api_utils.format_response("DeleteStack", "")
        else:
            return api_utils.format_response("DeleteStack", res["Error"])
Example #50
0
    def events_list(self, req):
        """
        Implements the DescribeStackEvents API action
        Returns events related to a specified stack (or all stacks)
        """
        def format_stack_event(e):
            """
            Reformat engine output into the AWS "StackEvent" format
            """
            keymap = {
                engine_api.EVENT_ID: 'EventId',
                engine_api.EVENT_RES_NAME: 'LogicalResourceId',
                engine_api.EVENT_RES_PHYSICAL_ID: 'PhysicalResourceId',
                engine_api.EVENT_RES_PROPERTIES: 'ResourceProperties',
                engine_api.EVENT_RES_STATUS: 'ResourceStatus',
                engine_api.EVENT_RES_STATUS_DATA: 'ResourceStatusData',
                engine_api.EVENT_RES_TYPE: 'ResourceType',
                engine_api.EVENT_STACK_ID: 'StackId',
                engine_api.EVENT_STACK_NAME: 'StackName',
                engine_api.EVENT_TIMESTAMP: 'Timestamp',
            }

            result = api_utils.reformat_dict_keys(keymap, e)

            return self._stackid_addprefix(result)

        con = req.context
        parms = dict(req.params)

        stack_name = req.params.get('StackName', None)
        try:
            event_res = self.engine_rpcapi.list_events(con,
                                                       stack_name=stack_name,
                                                       params=parms)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        events = 'Error' not in event_res and event_res['events'] or []

        result = [format_stack_event(e) for e in events]

        return api_utils.format_response('DescribeStackEvents',
            {'StackEvents': result})
Example #51
0
 def test_map_remote_error_request_limit_exceeded(self):
     ex = common_exception.RequestLimitExceeded(message="testing")
     expected = aws_exception.HeatRequestLimitExceeded
     self.assertIsInstance(aws_exception.map_remote_error(ex), expected)
Example #52
0
 def test_map_remote_error_inval_param_error(self):
     ex = AttributeError()
     expected = aws_exception.HeatInvalidParameterValueError
     self.assertIsInstance(aws_exception.map_remote_error(ex), expected)
Example #53
0
 def test_map_remote_error_denied_error(self):
     ex = common_exception.Forbidden()
     expected = aws_exception.HeatAccessDeniedError
     self.assertIsInstance(aws_exception.map_remote_error(ex), expected)
Example #54
0
 def test_map_remote_error_already_exists_error(self):
     ex = common_exception.StackExists(stack_name="teststack")
     expected = aws_exception.AlreadyExistsError
     self.assertIsInstance(aws_exception.map_remote_error(ex), expected)
Example #55
0
 def test_map_remote_error_invalid_action_error(self):
     ex = common_exception.ActionInProgress(stack_name="teststack",
                                            action="testing")
     expected = aws_exception.HeatActionInProgressError
     self.assertIsInstance(aws_exception.map_remote_error(ex), expected)
Example #56
0
    def describe(self, req):
        """
        Implements DescribeStacks API action
        Gets detailed information for a stack (or all stacks)
        """
        def format_stack_outputs(o):
            keymap = {
                engine_api.OUTPUT_DESCRIPTION: 'Description',
                engine_api.OUTPUT_KEY: 'OutputKey',
                engine_api.OUTPUT_VALUE: 'OutputValue',
            }

            return api_utils.reformat_dict_keys(keymap, o)

        def format_stack(s):
            """
            Reformat engine output into the AWS "StackSummary" format
            """
            keymap = {
                engine_api.STACK_CAPABILITIES: 'Capabilities',
                engine_api.STACK_CREATION_TIME: 'CreationTime',
                engine_api.STACK_DESCRIPTION: 'Description',
                engine_api.STACK_DISABLE_ROLLBACK: 'DisableRollback',
                engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
                engine_api.STACK_NOTIFICATION_TOPICS: 'NotificationARNs',
                engine_api.STACK_PARAMETERS: 'Parameters',
                engine_api.STACK_ID: 'StackId',
                engine_api.STACK_NAME: 'StackName',
                engine_api.STACK_STATUS: 'StackStatus',
                engine_api.STACK_STATUS_DATA: 'StackStatusReason',
                engine_api.STACK_TIMEOUT: 'TimeoutInMinutes',
            }

            result = api_utils.reformat_dict_keys(keymap, s)

            # Reformat outputs, these are handled separately as they are
            # only present in the engine output for a completely created
            # stack
            result['Outputs'] = []
            if engine_api.STACK_OUTPUTS in s:
                for o in s[engine_api.STACK_OUTPUTS]:
                    result['Outputs'].append(format_stack_outputs(o))

            # Reformat Parameters dict-of-dict into AWS API format
            # This is a list-of-dict with nasty "ParameterKey" : key
            # "ParameterValue" : value format.
            result['Parameters'] = [{'ParameterKey':k,
                'ParameterValue':v}
                for (k, v) in result['Parameters'].items()]

            return self._stackid_format(result)

        con = req.context
        parms = dict(req.params)

        # If no StackName parameter is passed, we pass None into the engine
        # this returns results for all stacks (visible to this user), which
        # is the behavior described in the AWS DescribeStacks API docs
        try:
            if 'StackName' in req.params:
                identity = self._get_identity(con, req.params['StackName'])
            else:
                identity = None

            stack_list = self.engine_rpcapi.show_stack(con, identity)

        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {'Stacks': [format_stack(s) for s in stack_list['stacks']]}

        return api_utils.format_response('DescribeStacks', res)
Example #57
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)
Example #58
0
    def describe(self, req):
        """
        Implements DescribeStacks API action
        Gets detailed information for a stack (or all stacks)
        """
        self._enforce(req, 'DescribeStacks')

        def format_stack_outputs(o):
            keymap = {
                engine_api.OUTPUT_DESCRIPTION: 'Description',
                engine_api.OUTPUT_KEY: 'OutputKey',
                engine_api.OUTPUT_VALUE: 'OutputValue',
            }

            def replacecolon(d):
                return dict(map(lambda (k, v): (k.replace(':', '.'), v),
                                d.items()))

            def transform(attrs):
                """
                Recursively replace all : with . in dict keys
                so that they are not interpreted as xml namespaces.
                """
                new = replacecolon(attrs)
                for key, value in new.items():
                    if isinstance(value, dict):
                        new[key] = transform(value)
                return new

            return api_utils.reformat_dict_keys(keymap, transform(o))

        def format_stack(s):
            """
            Reformat engine output into the AWS "StackSummary" format
            """
            keymap = {
                engine_api.STACK_CAPABILITIES: 'Capabilities',
                engine_api.STACK_CREATION_TIME: 'CreationTime',
                engine_api.STACK_DESCRIPTION: 'Description',
                engine_api.STACK_DISABLE_ROLLBACK: 'DisableRollback',
                engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
                engine_api.STACK_NOTIFICATION_TOPICS: 'NotificationARNs',
                engine_api.STACK_PARAMETERS: 'Parameters',
                engine_api.STACK_ID: 'StackId',
                engine_api.STACK_NAME: 'StackName',
                engine_api.STACK_STATUS_DATA: 'StackStatusReason',
                engine_api.STACK_TIMEOUT: 'TimeoutInMinutes',
            }

            result = api_utils.reformat_dict_keys(keymap, s)

            action = s[engine_api.STACK_ACTION]
            status = s[engine_api.STACK_STATUS]
            result['StackStatus'] = '_'.join((action, status))

            # Reformat outputs, these are handled separately as they are
            # only present in the engine output for a completely created
            # stack
            result['Outputs'] = []
            if engine_api.STACK_OUTPUTS in s:
                for o in s[engine_api.STACK_OUTPUTS]:
                    result['Outputs'].append(format_stack_outputs(o))

            # Reformat Parameters dict-of-dict into AWS API format
            # This is a list-of-dict with nasty "ParameterKey" : key
            # "ParameterValue" : value format.
            result['Parameters'] = [{'ParameterKey': k,
                                    'ParameterValue': v}
                                    for (k, v) in result['Parameters'].items()]

            return self._id_format(result)

        con = req.context
        # If no StackName parameter is passed, we pass None into the engine
        # this returns results for all stacks (visible to this user), which
        # is the behavior described in the AWS DescribeStacks API docs
        try:
            if 'StackName' in req.params:
                identity = self._get_identity(con, req.params['StackName'])
            else:
                identity = None

            stack_list = self.engine_rpcapi.show_stack(con, identity)

        except Exception as ex:
            return exception.map_remote_error(ex)

        res = {'Stacks': [format_stack(s) for s in stack_list]}

        return api_utils.format_response('DescribeStacks', res)
Example #59
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)
Example #60
0
    def list_metrics(self, req):
        """
        Implements ListMetrics API action
        Lists metric datapoints associated with a particular alarm,
        or all alarms if none specified
        """
        self._enforce(req, 'ListMetrics')

        def format_metric_data(d, fil={}):
            """
            Reformat engine output into the AWS "Metric" format
            Takes an optional filter dict, which is traversed
            so a metric dict is only returned if all keys match
            the filter dict
            """
            dimensions = [{
                'AlarmName': d[engine_api.WATCH_DATA_ALARM]
            }, {
                'Timestamp': d[engine_api.WATCH_DATA_TIME]
            }]
            for key in d[engine_api.WATCH_DATA]:
                dimensions.append({key: d[engine_api.WATCH_DATA][key]})

            newdims = self._reformat_dimensions(dimensions)

            result = {
                'MetricName': d[engine_api.WATCH_DATA_METRIC],
                'Dimensions': newdims,
                'Namespace': d[engine_api.WATCH_DATA_NAMESPACE],
            }

            for f in fil:
                try:
                    value = result[f]
                    if value != fil[f]:
                        # Filter criteria not met, return None
                        return
                except KeyError:
                    logger.warning(_("Invalid filter key %s, ignoring") % f)

            return result

        con = req.context
        parms = dict(req.params)
        # FIXME : Don't yet handle filtering by Dimensions
        filter_result = dict((k, v) for (k, v) in parms.iteritems()
                             if k in ("MetricName", "Namespace"))
        logger.debug(_("filter parameters : %s") % filter_result)

        try:
            # Engine does not currently support query by namespace/metric
            # so we pass None/None and do any filtering locally
            null_kwargs = {'metric_namespace': None, 'metric_name': None}
            watch_data = self.rpc_client.show_watch_metric(con, **null_kwargs)
        except rpc_common.RemoteError as ex:
            return exception.map_remote_error(ex)

        res = {'Metrics': []}
        for d in watch_data:
            metric = format_metric_data(d, filter_result)
            if metric:
                res['Metrics'].append(metric)

        result = api_utils.format_response("ListMetrics", res)
        return result