Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add options #5

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.pyc
dynamodb_create_cloudwatch_alarms.egg-info
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefix with /?

14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ If set as a cron job - updates existing alarms if
Read/Write Capacity Units DynamoDB table parameters changed.

Usage:
dynamodb_create_cloudwatch_alarms [options]
dynamodb_create_cloudwatch_alarms [-h | --help]
dynamodb-create-cloudwatch-alarms (-s <sns_topic_arn>) [-r <region>] [-p <prefix>] [--debug]
dynamodb-create-cloudwatch-alarms [-h | --help]

Options:
--debug Don't send data to AWS

Examples:
dynamodb_create_cloudwatch_alarms
dynamodb_create_cloudwatch_alarms --debug
-s <sns_topic_arn> For sending alarm ( require )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's required, don't make it an option, but make it an argument.
e.g. dynamodb-create-cloudwatch-alarms [options] <sns_topic_arn>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also give a usage example so people know what to put as an sns topic arn?

-r <region> AWS region
-p <prefix> DynamoDB name prefix
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what it's about for the user?

--debug Don't send data to AWS

```

# Install
Expand Down
110 changes: 91 additions & 19 deletions dynamodb_create_cloudwatch_alarms/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,75 @@


Usage:
dynamodb-create-cloudwatch-alarms [options]
dynamodb-create-cloudwatch-alarms (-s <sns_topic_arn>) [options]
dynamodb-create-cloudwatch-alarms [-h | --help]

Options:
--debug Don't send data to AWS
-s <sns_topic_arn> For sending alarm ( require )
-r <region> AWS region
-p <prefix> DynamoDB name prefix
--debug Don't send data to AWS

"""
import boto
import boto.ec2
import boto.dynamodb
import boto.dynamodb2
from docopt import docopt
from boto.ec2.cloudwatch import MetricAlarm

DEBUG = False
AWS_REGION = u'ap-northeast-1'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not put any default. Make it so it uses the default region configured in boto, and if use the region specified if one is specified.

AWS_SNS_ARN = u''
DYNAMO_PREF = u''

DDB_METRICS = frozenset([u'ConsumedReadCapacityUnits',
u'ConsumedWriteCapacityUnits'])
ALARM_PERIOD = 300
ALARM_EVALUATION_PERIOD = 12
ALARM_EVALUATION_PERIOD = 6
RATE = 0.8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment to explain what this RATE is?



def get_ddb_tables():
def _get_ddb_tables_list(ddb_connection):
"""
Retrieves all DynamoDB table names

Returns:
(set) Of valid DynamoDB table names
"""
ddb_connection = boto.connect_dynamodb()

ddb_tables_list_all = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's supposed to return a set, not point using a list here.


ddb_tables_list = ddb_connection.list_tables()
while u'LastEvaluatedTableName' in ddb_tables_list:
ddb_tables_list_all.extend(ddb_tables_list[u'TableNames'])
ddb_tables_list = ddb_connection.list_tables(
exclusive_start_table_name=ddb_tables_list
[u'LastEvaluatedTableName'])
else:
ddb_tables_list_all.extend(ddb_tables_list[u'TableNames'])

return ddb_tables_list_all


def get_ddb_tables():
"""
Retrieves all DynamoDB table describe

Returns:
(set) Of valid DynamoDB table describe list
"""

ddb_connection = boto.dynamodb2.connect_to_region(AWS_REGION)
ddb_tables_list = _get_ddb_tables_list(ddb_connection)

ddb_tables = set()
for ddb_table in ddb_tables_list:

if DYNAMO_PREF and not ddb_table.startswith(DYNAMO_PREF):
continue

ddb_table_attributes = ddb_connection.describe_table(ddb_table)

# creating a variable for each unit to satisfy flake8
ddb_tablename = ddb_table_attributes[u'Table'][u'TableName']
ddb_rcu = (ddb_table_attributes
Expand All @@ -50,6 +85,17 @@ def get_ddb_tables():
[u'Table'][u'ProvisionedThroughput'][u'WriteCapacityUnits'])
ddb_tables.add((ddb_tablename, ddb_rcu, ddb_wcu))

# check GlobalSecondaryIndexes
if u'GlobalSecondaryIndexes' not in ddb_table_attributes[u'Table']:
continue

gsi_list = ddb_table_attributes[u'Table'][u'GlobalSecondaryIndexes']
for gsi in gsi_list:
ddb_indexname = gsi[u'IndexName']
ddb_rcu = (gsi[u'ProvisionedThroughput'][u'ReadCapacityUnits'])
ddb_wcu = (gsi[u'ProvisionedThroughput'][u'WriteCapacityUnits'])
ddb_tables.add((ddb_tablename, ddb_rcu, ddb_wcu, ddb_indexname))

return ddb_tables


Expand Down Expand Up @@ -84,8 +130,9 @@ def get_existing_alarm_names(aws_cw_connect):

for existing_alarm in existing_alarms:
if existing_alarm.namespace == u'AWS/DynamoDB':
existing_alarm_names.update({existing_alarm.name:
existing_alarm.threshold})
existing_alarm_names.update({existing_alarm.name: {
u'threshold': existing_alarm.threshold,
u'alarm_actions': existing_alarm.alarm_actions}})

return existing_alarm_names

Expand Down Expand Up @@ -121,29 +168,43 @@ def get_ddb_alarms_to_create(ddb_tables, aws_cw_connect):
# initiate a MetricAlarm object for each DynamoDb table.
# for the threshold we calculate the 80 percent
# from the tables ProvisionedThroughput values
if len(table) > 3:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain in a comment why this > 3?

alarm_name = u'{0}-{1}-BasicAlarm'.format(
table[0] + u'-' + table[3],
metric.replace('Consumed', '') + 'Limit')
alarm_dimensions = {
u'TableName': table[0],
u'GlobalSecondaryIndexName': table[3]}
else:
alarm_name = u'{0}-{1}-BasicAlarm'.format(
table[0],
metric.replace('Consumed', '') + 'Limit')
alarm_dimensions = {u'TableName': table[0]}

ddb_table_alarm = MetricAlarm(
name=u'{}-{}-BasicAlarm'.format(table[0],
(metric.
replace('Consumed',
'') + 'Limit')),
name=alarm_name,
namespace=u'AWS/DynamoDB',
metric=u'{}'.format(metric), statistic='Sum',
metric=u'{0}'.format(metric), statistic='Sum',
comparison=u'>=',
threshold=0.8*threshold*ALARM_PERIOD,
threshold=RATE*threshold*ALARM_PERIOD,
period=ALARM_PERIOD,
evaluation_periods=ALARM_EVALUATION_PERIOD,
# Below insert the actions appropriate.
alarm_actions=[u'some_action'],
dimensions={u'TableName': table[0]})
alarm_actions=[AWS_SNS_ARN],
dimensions=alarm_dimensions)

# we create an Alarm metric for each new DDB table
if ddb_table_alarm.name not in existing_alarms.iterkeys():
alarms_to_create.add(ddb_table_alarm)
# checking the existing alarms thresholds
# update them if there are changes
for key, value in existing_alarms.iteritems():
if (key == ddb_table_alarm.name
and value != ddb_table_alarm.threshold):
if key != ddb_table_alarm.name:
continue

if str(value[u'threshold']) != str(ddb_table_alarm.threshold):
alarms_to_update.add(ddb_table_alarm)
elif value[u'alarm_actions'] != ddb_table_alarm.alarm_actions:
alarms_to_update.add(ddb_table_alarm)

return (alarms_to_create, alarms_to_update)
Expand All @@ -153,12 +214,23 @@ def main():
args = docopt(__doc__)

global DEBUG
global AWS_REGION
global AWS_SNS_ARN
global DYNAMO_PREF

AWS_SNS_ARN = args['-s']

if args['--debug']:
DEBUG = True

if args['-r']:
AWS_REGION = args['-r']

if args['-p']:
DYNAMO_PREF = args['-p']

ddb_tables = get_ddb_tables()
aws_cw_connect = boto.connect_cloudwatch()
aws_cw_connect = boto.ec2.cloudwatch.connect_to_region(AWS_REGION)

(alarms_to_create,
alarms_to_update) = get_ddb_alarms_to_create(ddb_tables,
Expand Down