Rev 5809 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#!/usr/bin/python## Auto-start and stop EC2 instances#import boto.ec2, datetime, sysfrom time import gmtime, strftime, strptime, sleep#Default start stop times (7am / 7pm AWST)DEFAULT_START_GMT = "23"DEFAULT_STOP_GMT = "11"#debug setting for dry-run#IS_DRY_RUN = TrueIS_DRY_RUN = False#tagging start and stop times is used to prevent starting / stopping instances#multiple times an hour, but it can be turned off to reduce number of tags used#by script if you don't think this is an issue (eg script only runs once an hour)#TAG_START_STOP_TIMES = FalseTAG_START_STOP_TIMES = True#################################################################################### Shoudn't need to modify anything below here####################################################################################State Code Constants#http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instance-status.htmlSTATE_PENDING = 0STATE_RUNNING = 16STATE_SHUTTING_DOWN = 32STATE_TERMINATED = 48STATE_STOPPING = 64STATE_STOPPED = 80# This is special flag to never kill this instance. Should really only be used# on your scripting server instance.TAG_DATE_TIME_FORMAT = "%Y-%m-%d"DEFAULT_NO_BOOT_TIME = "2000-01-01"DEFAULT_NO_KILL_TIME = "2000-01-01"TODAY_HOUR_DT_FORMAT = '%Y-%m-%d %H'DEFAULT_AUTO_TIME = '2000-01-01 00'TODAY_WEEKDAY_NAME = '%a'DEFAULT_WEEKDAY_ACTION = 'MTWHFSU'def main():gmtime_now = gmtime()#Test cases#Monday#gmtime_now = strptime("2014/11/24 10:00:00", "%Y/%m/%d %H:%M:%S")#Wednesday#gmtime_now = strptime("2014/11/26 10:00:00", "%Y/%m/%d %H:%M:%S")#Thursday#gmtime_now = strptime("2014/11/27 10:00:00", "%Y/%m/%d %H:%M:%S")#Friday#gmtime_now = strptime("2014/11/28 10:00:00", "%Y/%m/%d %H:%M:%S")#Saturday#gmtime_now = strptime("2014/11/29 10:00:00", "%Y/%m/%d %H:%M:%S")#Sunday#gmtime_now = strptime("2014/11/30 10:00:00", "%Y/%m/%d %H:%M:%S")startStopInstancesForTime(gmtime_now)def startStopInstancesForTime(gmtime_now):#Starts or stops instances based on the supplied GMT time.#@param gmtime_now - time struct of the current time in GMT#AWS credentialsregion = boto.config.get('sleep_tight', 'region', 'ap-southeast-2')aws_key = boto.config.get('sleep_tight', 'aws_access_key_id', '0')aws_secret = boto.config.get('sleep_tight', 'aws_secret_access_key', '0')# Connect to EC2conn = boto.ec2.connect_to_region(region, aws_access_key_id=aws_key, aws_secret_access_key=aws_secret)# Get current hourhh = strftime("%H", gmtime_now)# Get Todaythis_hour_str = strftime(TODAY_HOUR_DT_FORMAT, gmtime_now)this_hour_time = strptime(this_hour_str, TODAY_HOUR_DT_FORMAT)this_day_name = strftime(TODAY_WEEKDAY_NAME, gmtime_now)instances = conn.get_only_instances()for( instance ) in instances:instance_id = instance.idname = instance.tags.get("Name", instance_id)state_code = instance.state_codestart_hour_gmt = getSetTag(instance, 'START_HOUR_GMT', DEFAULT_START_GMT)stop_hour_gmt = getSetTag(instance, 'STOP_HOUR_GMT', DEFAULT_STOP_GMT)action_days = getSetTag(instance, 'ACTION_DAYS', DEFAULT_WEEKDAY_ACTION)no_boot_until_time = getTimeFromInstance(instance, 'NO_BOOT_UNTIL', DEFAULT_NO_BOOT_TIME, TAG_DATE_TIME_FORMAT)no_kill_until_time = getTimeFromInstance(instance, 'NO_KILL_UNTIL', DEFAULT_NO_KILL_TIME, TAG_DATE_TIME_FORMAT)last_auto_start_time = 99;last_auto_stop_time = 99;if ( TAG_START_STOP_TIMES ):last_auto_start_time = getTimeFromInstance(instance, 'LAST_AUTO_START', DEFAULT_AUTO_TIME, TODAY_HOUR_DT_FORMAT)last_auto_stop_time = getTimeFromInstance(instance, 'LAST_AUTO_STOP', DEFAULT_AUTO_TIME, TODAY_HOUR_DT_FORMAT)action_taken = "Do Nothing"try:if ( int(start_hour_gmt) == int(stop_hour_gmt) ):action_taken = "Not modifying - Start (" + start_hour_gmt + ") and stop (" \+ stop_hour_gmt + ") are same hour."elif ( isNoActionDay(action_days, this_day_name) ):action_taken = "Not modifying - Non actionable day (" + action_days + ") " + this_day_nameelse:total_msg = ""if ( this_hour_time <= no_boot_until_time ):total_msg += "No boot until " + strftime(TAG_DATE_TIME_FORMAT, no_boot_until_time)elif ( int(hh) == int(start_hour_gmt) and last_auto_start_time != this_hour_time ):total_msg += processStart( conn, instance, state_code, this_hour_str )else:total_msg += "No boot action"total_msg += " - "if ( this_hour_time <= no_kill_until_time ):total_msg += "No kill until " + strftime(TAG_DATE_TIME_FORMAT, no_kill_until_time)elif( int(hh) == int(stop_hour_gmt) and last_auto_stop_time != this_hour_time ):total_msg += processStop( conn, instance, state_code, this_hour_str )else:total_msg += "No kill action"if ( total_msg != "" ):action_taken = total_msgexcept:action_taken = "Unknown Exception"print this_hour_str, ':', instance_id, state_code, start_hour_gmt, stop_hour_gmt, " --- ", action_taken, "for:", namedef getSetTag(instance, tag, default_value):#Attempts to retrieve the the tag from the instance.#If the tag doesn't exist then it will create the tag on the instance#with the value set to the default.#@param instance - boto.ec2.instance.Instance#@param tag - string for the tag to lookup#@param default_value - default string if tag does not existtag_missing_default = "SOME_DEFAULT_NEVER_RTNED"the_tag = instance.tags.get(tag, tag_missing_default)if ( the_tag == tag_missing_default ):#the tag didnt exist copy callers default for return#create the tag with the callers defaultthe_tag = default_valuetry:instance.add_tag(tag, default_value)except:doNothing = True#else the_tag is set to the instances tag.return the_tagdef getTimeFromInstance(instance, tag, default_time_str, time_format):#Safely reads the tag from the instance and converts it to a time structure#in the case the tag is incorrectly formatted, or missing it will use the#default supplied as the output date#@param instance - boto.ec2.instance.Instance#@param tag - string for the tag to lookup#@param default_time_str - string of the date to use if the tag is missing# or incorrectly formed#@param time_format - string of the date format the tag and default_time_str# are expected to conform to.time_str = getSetTag(instance, tag, default_time_str)try:time_as_struct = strptime(time_str, time_format)except:time_as_struct = strptime(default_time_str, time_format)return time_as_structdef isNoActionDay( action_days, this_day_name ):#Determines if this day name is present in action days string#@param action_days - the days to return true for, is a string with the# first letter of the days permitted. H = Thursday, U = Sunday#@param this_day_name - the current day of the week as three letters strftime(%a, gmtime)noAction = Trueswitch_dict = {'Mon': 'M','Tue': 'T','Wed': 'W','Thu': 'H','Fri': 'F','Sat': 'S','Sun': 'U'}try:noAction = switch_dict[this_day_name] not in action_daysexcept:print 'Unknown day', this_day_namereturn noActiondef processStart( conn, instance, state_code, this_hour_str ):#Contains logic on whether to start instance based on current state.#Additional it will actually start the instance if determined to do so.#@param instance - oto.ec2.instance.Instance#@param state_code - instance's current state#@param this_hour_str - string indicating current GMT time in TODAY_HOUR_DT_FORMAT#@returns - string indicating the action taken.action_taken = "Unknown - start"if ( state_code == STATE_RUNNING ):action_taken = "Starting - no action already running"elif ( state_code == STATE_STOPPED ):action_taken = "Starting instance"try:conn.start_instances(instance_ids=instance.id, dry_run=IS_DRY_RUN)if ( TAG_START_STOP_TIMES ):#tag the instance so we don't potentially attempt to start it again this hourinstance.add_tag('LAST_AUTO_START', this_hour_str)except boto.exception.EC2ResponseError, e:print "Except while starting -", e#TODO start the instance and poss set elastip ipelse:action_taken = "Starting - ERROR state_code"return action_takendef processStop( conn, instance, state_code, this_hour_str ):#Contains logic on whether to stop instance based on current state.#Additional it will actually stop the instance if determined to do so.#@param instance - oto.ec2.instance.Instance#@param state_code - instance's current state#@param this_hour_str - string indicating current GMT time in TODAY_HOUR_DT_FORMAT#@returns - string indicating the action taken.action_taken = "Unknown - stop"if ( state_code in ( STATE_SHUTTING_DOWN, STATE_TERMINATED, STATE_STOPPING, STATE_STOPPED ) ):action_taken = "Stopping - no action already shutting/shutdown"elif ( state_code == STATE_RUNNING ):action_taken = "Stopping instance"try:conn.stop_instances(instance_ids=instance.id, dry_run=IS_DRY_RUN)if ( TAG_START_STOP_TIMES ):#tag the instance so we don't potentially attempt to stop it again this hourinstance.add_tag('LAST_AUTO_STOP', this_hour_str)except boto.exception.EC2ResponseError, e:print "Except while stopping -", eelse:action_taken = "Stopping - ERROR state_code"return action_taken#Jump to main.if __name__ == '__main__':main()