Source code for c7n.actions.metric

# Copyright 2017-2018 Capital One Services, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime
import jmespath

from .core import BaseAction
from c7n.manager import resources
from c7n import utils


[docs]def average(numbers): return float(sum(numbers)) / max(len(numbers), 1)
[docs]def distinct_count(values): return float(len(set(values)))
METRIC_OPS = { 'count': len, 'distinct_count': distinct_count, 'sum': sum, 'average': average, } METRIC_UNITS = [ # Time 'Seconds', 'Microseconds', 'Milliseconds', # Bytes and Bits 'Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes', 'Bits', 'Kilobits', 'Megabits', 'Gigabits', 'Terabits', # Rates 'Bytes/Second', 'Kilobytes/Second', 'Megabytes/Second', 'Gigabytes/Second', 'Terabytes/Second', 'Bits/Second', 'Kilobits/Second', 'Megabits/Second', 'Gigabits/Second', 'Terabits/Second', 'Count/Second', # Other Scalars 'Percent', 'Count', 'None' ]
[docs]class PutMetric(BaseAction): """Action to put metrics based on an expression into CloudWatch metrics :example: .. code-block:: yaml policies: - name: track-attached-ebs resource: ec2 comment: | Put the count of the number of EBS attached disks to an instance filters: - Name: tracked-ec2-instance actions: - type: put-metric key: Reservations[].Instances[].BlockDeviceMappings[].DeviceName namespace: Usage Metrics metric_name: Attached Disks op: count units: Count op and units are optional and will default to simple Counts. """ # permissions are typically lowercase servicename:TitleCaseActionName permissions = {'cloudwatch:PutMetricData', } schema_alias = True schema = { 'type': 'object', 'required': ['type', 'key', 'namespace', 'metric_name'], 'properties': { 'type': {'enum': ['put-metric', ]}, 'key': {'type': 'string'}, # jmes path 'namespace': {'type': 'string'}, 'metric_name': {'type': 'string'}, 'dimensions': { 'type': 'array', 'items': {'type': 'object'}, }, 'op': {'enum': list(METRIC_OPS.keys())}, 'units': {'enum': METRIC_UNITS} } }
[docs] def process(self, resources): ns = self.data['namespace'] metric_name = self.data['metric_name'] key_expression = self.data.get('key', 'Resources[]') operation = self.data.get('op', 'count') units = self.data.get('units', 'Count') # dimensions are passed as a list of dicts dimensions = self.data.get('dimensions', []) now = datetime.utcnow() # reduce the resources by the key expression, and apply the operation to derive the value values = [] self.log.debug("searching for %s in %s", key_expression, resources) try: values = jmespath.search("Resources[]." + key_expression, {'Resources': resources}) # I had to wrap resourses in a dict like this in order to not have jmespath expressions # start with [] in the yaml files. It fails to parse otherwise. except TypeError as oops: self.log.error(oops.message) value = 0 try: f = METRIC_OPS[operation] value = f(values) except KeyError: self.log.error("Bad op for put-metric action: %s", operation) # for demo purposes # from math import sin, pi # value = sin((now.minute * 6 * 4 * pi) / 180) * ((now.hour + 1) * 4.0) metrics_data = [ { 'MetricName': metric_name, 'Dimensions': [{'Name': i[0], 'Value': i[1]} for d in dimensions for i in d.items()], 'Timestamp': now, 'Value': value, # TODO: support an operation of 'stats' to include this # structure instead of a single Value # Value and StatisticValues are mutually exclusive. # 'StatisticValues': { # 'SampleCount': 1, # 'Sum': 123.0, # 'Minimum': 123.0, # 'Maximum': 123.0 # }, 'Unit': units, }, ] client = utils.local_session( self.manager.session_factory).client('cloudwatch') client.put_metric_data(Namespace=ns, MetricData=metrics_data) return resources
[docs]def register_action_put_metric(registry, _): # apply put metric to each resource for resource in registry.keys(): klass = registry.get(resource) klass.action_registry.register('put-metric', PutMetric)
resources.subscribe(resources.EVENT_FINAL, register_action_put_metric)