From e05eef829699e08fcbdb6f3368457841d058ed76 Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Sat, 30 Sep 2017 21:31:07 -0400 Subject: [PATCH 1/5] clarify help for what collect_by_instance and collect_without_dimension actually do --- src/diamond/handler/cloudwatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/diamond/handler/cloudwatch.py b/src/diamond/handler/cloudwatch.py index fc706886a..ce92cf6c3 100644 --- a/src/diamond/handler/cloudwatch.py +++ b/src/diamond/handler/cloudwatch.py @@ -135,8 +135,8 @@ def get_default_config_help(self): 'name': 'CloudWatch metric name', 'unit': 'CloudWatch metric unit', 'collector': 'Diamond collector name', - 'collect_by_instance': 'Collect metrics for instances separately', - 'collect_without_dimension': 'Collect metrics without dimension' + 'collect_by_instance': 'Send metric with InstanceId dimension', + 'collect_without_dimension': 'Send metric with no dimension' }) return config From 4ce10b2a8650d01b14d7dff36b9176a82cfab68b Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Sun, 1 Oct 2017 07:42:02 -0400 Subject: [PATCH 2/5] cloudwatch handler - add support for user-specified dimensions --- src/diamond/handler/cloudwatch.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/diamond/handler/cloudwatch.py b/src/diamond/handler/cloudwatch.py index ce92cf6c3..ecf7f3cd2 100644 --- a/src/diamond/handler/cloudwatch.py +++ b/src/diamond/handler/cloudwatch.py @@ -36,6 +36,18 @@ name = Avg05 namespace = MachineLoad unit = None + +[[[SnmpUptime]]] +collect_by_instance = False +collect_without_dimension = True +collector = snmpraw +metric = sysUpTimeInstance +name = Uptime +namespace = Diamond +unit = None +[[[[additional_dimensions]]]] +Hostname = foo + """ import sys @@ -87,7 +99,8 @@ def __init__(self, config=None): self.valid_config = ('region', 'collector', 'metric', 'namespace', 'name', 'unit', 'collect_by_instance', - 'collect_without_dimension') + 'collect_without_dimension', + 'additional_dimensions') self.rules = [] for key_name, section in self.config.items(): @@ -118,7 +131,8 @@ def get_default_rule_config(self): 'name': '', 'unit': 'None', 'collect_by_instance': True, - 'collect_without_dimension': False + 'collect_without_dimension': False, + 'additional_dimensions': {} }) return config @@ -136,7 +150,9 @@ def get_default_config_help(self): 'unit': 'CloudWatch metric unit', 'collector': 'Diamond collector name', 'collect_by_instance': 'Send metric with InstanceId dimension', - 'collect_without_dimension': 'Send metric with no dimension' + 'collect_without_dimension': 'Send metric with no dimension', + 'additional_dimensions': 'Name/Value additional dimensions to ' + 'send with metric' }) return config @@ -155,7 +171,8 @@ def get_default_config(self): 'name': 'Avg01', 'unit': 'None', 'collect_by_instance': True, - 'collect_without_dimension': False + 'collect_without_dimension': False, + 'additional_dimensions': {} }) return config @@ -230,6 +247,8 @@ def send_metrics_to_cloudwatch(self, rule, metric, dimensions): timestamp = datetime.datetime.utcfromtimestamp(metric.timestamp) + dimensions.update(rule['additional_dimensions']) + self.log.debug( "CloudWatch: Attempting to publish metric: %s to %s " "with value (%s) for dimensions %s @%s", From 5ac5c43c5ed4b57ad3cf844f8960272135cb5729 Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Sun, 1 Oct 2017 07:42:54 -0400 Subject: [PATCH 3/5] cloudwatch handler - set timeout on boto.utils.get_instance_metadata - otherwise, hangs forever if running outside of EC2 --- src/diamond/handler/cloudwatch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/diamond/handler/cloudwatch.py b/src/diamond/handler/cloudwatch.py index ecf7f3cd2..99bfa3a14 100644 --- a/src/diamond/handler/cloudwatch.py +++ b/src/diamond/handler/cloudwatch.py @@ -89,7 +89,9 @@ def __init__(self, config=None): # Initialize Options self.region = self.config['region'] - instance_metadata = boto.utils.get_instance_metadata() + instance_metadata = boto.utils.get_instance_metadata( + timeout=1, num_retries=5 + ) if 'instance-id' in instance_metadata: self.instance_id = instance_metadata['instance-id'] self.log.debug("Setting InstanceId: " + self.instance_id) From 19d136f0598a574db391037e73a95b92fce1992d Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Sun, 1 Oct 2017 08:36:46 -0400 Subject: [PATCH 4/5] cloudwatch - additional dimensions should be a separate metric send --- src/diamond/handler/cloudwatch.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/diamond/handler/cloudwatch.py b/src/diamond/handler/cloudwatch.py index 99bfa3a14..5e10f7387 100644 --- a/src/diamond/handler/cloudwatch.py +++ b/src/diamond/handler/cloudwatch.py @@ -45,7 +45,7 @@ name = Uptime namespace = Diamond unit = None -[[[[additional_dimensions]]]] +[[[[collect_with_dimensions]]]] Hostname = foo """ @@ -102,7 +102,7 @@ def __init__(self, config=None): self.valid_config = ('region', 'collector', 'metric', 'namespace', 'name', 'unit', 'collect_by_instance', 'collect_without_dimension', - 'additional_dimensions') + 'collect_with_dimensions') self.rules = [] for key_name, section in self.config.items(): @@ -134,7 +134,7 @@ def get_default_rule_config(self): 'unit': 'None', 'collect_by_instance': True, 'collect_without_dimension': False, - 'additional_dimensions': {} + 'collect_with_dimensions': {} }) return config @@ -153,8 +153,8 @@ def get_default_config_help(self): 'collector': 'Diamond collector name', 'collect_by_instance': 'Send metric with InstanceId dimension', 'collect_without_dimension': 'Send metric with no dimension', - 'additional_dimensions': 'Name/Value additional dimensions to ' - 'send with metric' + 'collect_with_dimensions': 'Name/Value additional dimensions to ' + 'send metric with' }) return config @@ -174,7 +174,7 @@ def get_default_config(self): 'unit': 'None', 'collect_by_instance': True, 'collect_without_dimension': False, - 'additional_dimensions': {} + 'collect_with_dimensions': {} }) return config @@ -242,6 +242,12 @@ def process(self, metric): metric, {}) + if len(rule['collect_with_dimensions']) > 0: + self.send_metrics_to_cloudwatch( + rule, + metric, + rule['collect_with_dimensions']) + def send_metrics_to_cloudwatch(self, rule, metric, dimensions): """ Send metrics to CloudWatch for the given dimensions @@ -249,8 +255,6 @@ def send_metrics_to_cloudwatch(self, rule, metric, dimensions): timestamp = datetime.datetime.utcfromtimestamp(metric.timestamp) - dimensions.update(rule['additional_dimensions']) - self.log.debug( "CloudWatch: Attempting to publish metric: %s to %s " "with value (%s) for dimensions %s @%s", From 4d994031dcfdac11dd1100d9e21b663ca8c0a0f2 Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Fri, 6 Oct 2017 19:06:12 -0400 Subject: [PATCH 5/5] issue #678 - convert cloudwatch handler to boto3 --- src/diamond/handler/cloudwatch.py | 54 +++++++++++++++++++------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/diamond/handler/cloudwatch.py b/src/diamond/handler/cloudwatch.py index 5e10f7387..0a0753668 100644 --- a/src/diamond/handler/cloudwatch.py +++ b/src/diamond/handler/cloudwatch.py @@ -6,7 +6,7 @@ #### Dependencies - * [boto](http://boto.readthedocs.org/en/latest/index.html) + * [boto3](http://boto3.readthedocs.org/en/latest/) #### Configuration @@ -57,11 +57,11 @@ from configobj import Section try: - import boto - import boto.ec2.cloudwatch - import boto.utils + import boto3 + from botocore.utils import InstanceMetadataFetcher except ImportError: - boto = None + boto3 = None + InstanceMetadataFetcher = None class cloudwatchHandler(Handler): @@ -78,7 +78,7 @@ def __init__(self, config=None): # Initialize Handler Handler.__init__(self, config) - if not boto: + if not boto3: self.log.error( "CloudWatch: Boto is not installed, please install boto.") return @@ -89,13 +89,15 @@ def __init__(self, config=None): # Initialize Options self.region = self.config['region'] - instance_metadata = boto.utils.get_instance_metadata( - timeout=1, num_retries=5 - ) - if 'instance-id' in instance_metadata: - self.instance_id = instance_metadata['instance-id'] + try: + self.instance_id = InstanceMetadataFetcher( + timeout=1, num_attempts=5 + )._get_request( + 'http://169.254.169.254/latest/meta-data/instance-id', + 1, num_attempts=5 + ).text.strip() self.log.debug("Setting InstanceId: " + self.instance_id) - else: + except Exception: self.instance_id = None self.log.error('CloudWatch: Failed to load instance metadata') @@ -188,12 +190,13 @@ def _bind(self): "CloudWatch: Attempting to connect to CloudWatch at Region: %s", self.region) try: - self.connection = boto.ec2.cloudwatch.connect_to_region( - self.region) + self.connection = boto3.client( + 'cloudwatch', region_name=self.region + ) self.log.debug( "CloudWatch: Succesfully Connected to CloudWatch at Region: %s", self.region) - except boto.exception.EC2ResponseError: + except boto3.exceptions.Boto3Error: self.log.error('CloudWatch: CloudWatch Exception Handler: ') def __del__(self): @@ -209,7 +212,7 @@ def process(self, metric): """ Process a metric and send it to CloudWatch """ - if not boto: + if not boto3: return collector = str(metric.getCollectorPath()) @@ -267,11 +270,20 @@ def send_metrics_to_cloudwatch(self, rule, metric, dimensions): try: self.connection.put_metric_data( - str(rule['namespace']), - str(rule['name']), - str(metric.value), - timestamp, str(rule['unit']), - dimensions) + Namespace=str(rule['namespace']), + MetricData=[ + { + 'MetricName': str(rule['name']), + 'Dimensions': [ + {'Name': x, 'Value': dimensions[x]} + for x in dimensions.keys() + ], + 'Timestamp': timestamp, + 'Value': metric.value, + 'Unit': str(rule['unit']) + } + ] + ) self.log.debug( "CloudWatch: Successfully published metric: %s to" " %s with value (%s) for dimensions %s",