Skip to content

Instantly share code, notes, and snippets.

@igorlg
Created November 2, 2018 00:58
Show Gist options
  • Save igorlg/0d461a3fdc632676c1006160ab4e2d79 to your computer and use it in GitHub Desktop.
Save igorlg/0d461a3fdc632676c1006160ab4e2d79 to your computer and use it in GitHub Desktop.
CloudWatch Dasboard for EC2/EBS

Creates a basic CloudWatch Dashboard for an EC2 instance and it's EBS volumes.

usage: cw_dashboard_ec2.py [-h] [--dashboard_name DASHBOARD_NAME]
                           [--skip_devices [SKIP_DEVICES [SKIP_DEVICES ...]]]
                           [--volume_group VOLUME_GROUP]
                           [--aws_profile AWS_PROFILE] [--dump]
                           instance_id

Generate a CloudWatch Dashboard for an EC2 Instance

positional arguments:
  instance_id           ID of the Instance

optional arguments:
  -h, --help            show this help message and exit
  --dashboard_name DASHBOARD_NAME
                        Name of the Dashboard
  --skip_devices [SKIP_DEVICES [SKIP_DEVICES ...]]
                        Device Name to skip from the Dashboard
  --volume_group VOLUME_GROUP
                        Name to give to the group of volumes
  --aws_profile AWS_PROFILE
                        AWS Profile to use. Defaults to "oren-nonprod"
  --dump                Dump JSON content instead of creating Dashboard
import boto3, argparse, json
from pprint import pprint
def tag_name(resource):
try:
for t in resource.tags:
if t['Key'] == 'Name':
return t['Value']
return 'null'
except TypeError:
return 'null'
def default(name):
if name == 'dashboard_name':
from string import ascii_uppercase, digits
from random import choice as random_choice
return 'Dashboard_{}'.format(''.join(random_choice(ascii_uppercase + digits) for _ in range(5)))
elif name == 'volume_group':
return 'default'
elif name == 'aws_profile':
return 'default'
elif name == 'dump':
return False
class MyList(list):
def append(self,arg):
if isinstance(arg,list):
self.extend(arg)
else:
super(MyList, self).append(arg)
parser = argparse.ArgumentParser(description='Generate a CloudWatch Dashboard for an EC2 Instance')
parser.add_argument('instance_id', action='store', help='ID of the Instance')
parser.add_argument('--dashboard_name', action='store', help='Name of the Dashboard', default=default('dashboard_name'))
parser.add_argument('--skip_devices', action='append', help='Device Name to skip from the Dashboard', nargs='*', default=MyList([]))
parser.add_argument('--volume_group', action='store', help='Name to give to the group of volumes', default=default('volume_group'))
parser.add_argument('--aws_profile', action='store', help='AWS Profile to use. Defaults to "oren-nonprod"', default=default('aws_profile'))
parser.add_argument('--dump', action='store_true', help='Dump JSON content instead of creating Dashboard', default=default('dump'))
args = parser.parse_args()
sess = boto3.Session(profile_name=args.aws_profile)
ec2 = sess.resource('ec2')
region = sess.region_name
console_url = 'https://{}.console.aws.amazon.com/ec2/v2/home?region={}'.format(region, region)
instance = ec2.Instance(args.instance_id)
volumes = [ ec2.Volume(v['Ebs']['VolumeId']) for v in instance.block_device_mappings if not v['DeviceName'].replace('/dev/', '') in args.skip_devices ]
widgets = [ ]
# EC2 Widgets
instance_metrics = [
{ 'name': 'CPUUtilization', 'x': 0, 'y': 0, },
{ 'name': 'NetworkIn', 'x': 6, 'y': 0, },
{ 'name': 'NetworkOut', 'x': 12, 'y': 0, },
{ 'name': 'DiskReadBytes', 'x': 18, 'y': 0, },
]
for m in instance_metrics:
widgets.append(
{
'x': m['x'], 'y': m['y'],
'type': 'metric', 'width': 6, 'height': 6,
'properties': {
'metrics': [ [ 'AWS/EC2', m['name'], 'InstanceId', args.instance_id ] ],
'region': region,
'title': 'EC2 {}'.format(m['name']),
'view': 'timeSeries', 'stacked': False, 'period': 300,
}
}
)
# EBS Widgets
volume_metrics = [
{ 'name': 'VolumeReadOps', 'x': 0, 'y': 6, },
{ 'name': 'VolumeReadBytes', 'x': 6, 'y': 6, },
{ 'name': 'VolumeTotalReadTime', 'x': 12, 'y': 6, },
{ 'name': 'VolumeQueueLength', 'x': 18, 'y': 6, },
{ 'name': 'VolumeReadBytes', 'x': 12, 'y': 12, },
]
for m in volume_metrics:
metric = {
'x': m['x'], 'y': m['y'],
'type': 'metric', 'width': 6, 'height': 6,
'properties': {
'metrics': [ [ 'AWS/EBS', m['name'], 'VolumeId', volumes[0].id ], ],
'region': region, 'title': '{} {}'.format(args.volume_group, m['name']),
'view': 'timeSeries', 'stacked': False, 'period': 300,
}
}
for v in volumes[1:]:
metric['properties']['metrics'].append([ '...', v.id ])
widgets.append(metric)
# Markdown Widget
markdown = [ '# EC2 Instance', ''
'Instance name: {} ID: [{}]({}#Instances:search={})'.format(tag_name(instance), instance.id, console_url, instance.id),
'', '## Volumes', 'List of volumes detailed here and links to their Console pages below.', '',
'### {}'.format(args.volume_group), '',
'Device | Volume Name | Volume ID', '----|---------|---------',
]
for v in volumes:
markdown.append ( '{} | {} | [{}]({}#Volumes:search={};sort=size)'.format ( v.attachments[0]['Device'].replace('/dev/', ''),
tag_name(v), v.id, console_url, v.id
)
)
widgets.append({ 'type': 'text', 'x': 0, 'y': 12, 'width': 12, 'height': 9, 'properties': { 'markdown': "\n".join(markdown) } } )
dash_body = json.dumps({ 'widgets': widgets })
if args.dump:
print(dash_body)
else:
cw = sess.client('cloudwatch')
resp = cw.put_dashboard(DashboardName=args.dashboard_name, DashboardBody=dash_body)
print('boto3 Response:\n{}'.format(json.dumps(resp, indent=2)))
print('Link to the Dashboard:\nhttps://{}.console.aws.amazon.com/cloudwatch/home?region={}#dashboards:name={}'.format(region, region, args.dashboard_name))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment