Commit d895addc by Hannah Chen Committed by GitHub

Merge pull request #4044 from edx/hchen/minos-lifecycle

upgrade minos scripts to boto3
parents 1aa63d30 e7f51684
...@@ -29,71 +29,68 @@ group and state. ...@@ -29,71 +29,68 @@ group and state.
} }
""" """
import argparse import argparse
import boto import boto3
import boto.ec2.autoscale
import json import json
from collections import defaultdict from collections import defaultdict
from os import environ from os import environ
class LifecycleInventory(): class LifecycleInventory():
profile = None def __init__(self, region):
def __init__(self, profile):
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
self.profile = profile self.region = region
def get_e_d_from_tags(self, group): def get_e_d_from_tags(self, group):
environment = "default_environment" environment = "default_environment"
deployment = "default_deployment" deployment = "default_deployment"
for r in group.tags: for r in group['Tags']:
if r.key == "environment": if r['Key'] == "environment":
environment = r.value environment = r['Value']
elif r.key == "deployment": elif r['Key'] == "deployment":
deployment = r.value deployment = r['Value']
return environment,deployment return environment,deployment
def get_instance_dict(self): def get_instance_dict(self):
ec2 = boto.ec2.connect_to_region(region,profile_name=self.profile) ec2 = boto3.client('ec2', region_name=self.region)
reservations = ec2.get_all_instances() reservations = ec2.describe_instances()['Reservations']
dict = {} dict = {}
for instance in [i for r in reservations for i in r.instances]: for instance in [i for r in reservations for i in r['Instances']]:
dict[instance.id] = instance dict[instance['InstanceId']] = instance
return dict return dict
def run(self): def run(self):
asg = boto.ec2.autoscale.connect_to_region(region,profile_name=self.profile) asg = boto3.client('autoscaling', region_name=self.region)
groups = asg.get_all_groups()
groups = asg.describe_auto_scaling_groups()['AutoScalingGroups']
instances = self.get_instance_dict() instances = self.get_instance_dict()
inventory = defaultdict(list) inventory = defaultdict(list)
for group in groups: for group in groups:
for instance in group.instances: for instance in group['Instances']:
private_ip_address = instances[instance.instance_id].private_ip_address private_ip_address = instances[instance['InstanceId']]['PrivateIpAddress']
if private_ip_address: if private_ip_address:
environment,deployment = self.get_e_d_from_tags(group) environment,deployment = self.get_e_d_from_tags(group)
inventory[environment + "_" + deployment + "_" + instance.lifecycle_state.replace(":","_")].append(private_ip_address) inventory[environment + "_" + deployment + "_" + instance['LifecycleState'].replace(":","_")].append(private_ip_address)
inventory[group.name].append(private_ip_address) inventory[group['AutoScalingGroupName']].append(private_ip_address)
inventory[group.name + "_" + instance.lifecycle_state.replace(":","_")].append(private_ip_address) inventory[group['AutoScalingGroupName'] + "_" + instance['LifecycleState'].replace(":","_")].append(private_ip_address)
inventory[instance.lifecycle_state.replace(":","_")].append(private_ip_address) inventory[instance['LifecycleState'].replace(":","_")].append(private_ip_address)
print json.dumps(inventory, sort_keys=True, indent=2) print json.dumps(inventory, sort_keys=True, indent=2)
if __name__=="__main__": if __name__=="__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('-p', '--profile', help='The aws profile to use when connecting.') parser.add_argument('-r', '--region', help='The aws region to use when connecting.', default='us-east-1')
parser.add_argument('-l', '--list', help='Ansible passes this, we ignore it.', action='store_true', default=True) parser.add_argument('-l', '--list', help='Ansible passes this, we ignore it.', action='store_true', default=True)
args = parser.parse_args() args = parser.parse_args()
region = environ.get('AWS_REGION','us-east-1')
LifecycleInventory(args.profile).run() LifecycleInventory(args.region).run()
...@@ -17,12 +17,9 @@ It relies on some component applying the proper tags and performing pre-retireme ...@@ -17,12 +17,9 @@ It relies on some component applying the proper tags and performing pre-retireme
""" """
import argparse import argparse
import boto import boto3
import boto.ec2
import boto.sqs
import json import json
import subprocess import subprocess
from boto.sqs.message import RawMessage
import logging import logging
import os import os
from distutils import spawn from distutils import spawn
...@@ -35,40 +32,37 @@ class LifecycleHandler: ...@@ -35,40 +32,37 @@ class LifecycleHandler:
INSTANCE_TERMINATION = 'autoscaling:EC2_INSTANCE_TERMINATING' INSTANCE_TERMINATION = 'autoscaling:EC2_INSTANCE_TERMINATING'
TEST_NOTIFICATION = 'autoscaling:TEST_NOTIFICATION' TEST_NOTIFICATION = 'autoscaling:TEST_NOTIFICATION'
NUM_MESSAGES = 10 NUM_MESSAGES = 10
WAIT_TIME_SECONDS = 10 WAIT_TIME_SECONDS = 1
VISIBILITY_TIMEOUT = 10
def __init__(self, profile, queue, hook, dry_run, bin_directory=None): def __init__(self, region, queue, hook, dry_run, bin_directory=None):
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
self.queue = queue self.queue = queue
self.hook = hook self.hook = hook
self.profile = profile self.region = region
if bin_directory: if bin_directory:
os.environ["PATH"] = bin_directory + os.pathsep + os.environ["PATH"] os.environ["PATH"] = bin_directory + os.pathsep + os.environ["PATH"]
self.aws_bin = spawn.find_executable('aws') self.aws_bin = spawn.find_executable('aws')
self.python_bin = spawn.find_executable('python') self.python_bin = spawn.find_executable('python')
self.region = os.environ.get('AWS_REGION','us-east-1')
self.base_cli_command ="{python_bin} {aws_bin} ".format( self.base_cli_command ="{python_bin} {aws_bin} ".format(
python_bin=self.python_bin, python_bin=self.python_bin,
aws_bin=self.aws_bin) aws_bin=self.aws_bin)
if self.profile:
self.base_cli_command += "--profile {profile} ".format(profile=self.profile)
if self.region: if self.region:
self.base_cli_command += "--region {region} ".format(region=self.region) self.base_cli_command += "--region {region} ".format(region=self.region)
self.dry_run = dry_run self.dry_run = args.dry_run
self.ec2_con = boto.ec2.connect_to_region(self.region) self.ec2_con = boto3.client('ec2',region_name=self.region)
self.sqs_con = boto.sqs.connect_to_region(self.region) self.sqs_con = boto3.client('sqs',region_name=self.region)
def process_lifecycle_messages(self): def process_lifecycle_messages(self):
queue = self.sqs_con.get_queue(self.queue) queue_url = self.sqs_con.get_queue_url(QueueName=self.queue)['QueueUrl']
queue = boto3.resource('sqs', region_name=self.region).Queue(queue_url)
# Needed to get unencoded message for ease of processing for sqs_message in self.sqs_con.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=LifecycleHandler.NUM_MESSAGES, VisibilityTimeout=LifecycleHandler.VISIBILITY_TIMEOUT,
queue.set_message_class(RawMessage) WaitTimeSeconds=LifecycleHandler.WAIT_TIME_SECONDS).get('Messages', []):
body = json.loads(sqs_message['Body'])
for sqs_message in queue.get_messages(LifecycleHandler.NUM_MESSAGES,
wait_time_seconds=LifecycleHandler.WAIT_TIME_SECONDS):
body = json.loads(sqs_message.get_body_encoded())
as_message = json.loads(body['Message']) as_message = json.loads(body['Message'])
logging.info("Proccessing message {message}.".format(message=as_message)) logging.info("Proccessing message {message}.".format(message=as_message))
...@@ -113,7 +107,7 @@ class LifecycleHandler: ...@@ -113,7 +107,7 @@ class LifecycleHandler:
def delete_sqs_message(self, queue, sqs_message, as_message, dry_run): def delete_sqs_message(self, queue, sqs_message, as_message, dry_run):
if not dry_run: if not dry_run:
logging.info("Deleting message with body {message}".format(message=as_message)) logging.info("Deleting message with body {message}".format(message=as_message))
self.sqs_con.delete_message(queue, sqs_message) self.sqs_con.delete_message(QueueUrl=queue.url, ReceiptHandle=sqs_message['ReceiptHandle'])
else: else:
logging.info("Would have deleted message with body {message}".format(message=as_message)) logging.info("Would have deleted message with body {message}".format(message=as_message))
...@@ -154,10 +148,12 @@ class LifecycleHandler: ...@@ -154,10 +148,12 @@ class LifecycleHandler:
""" """
Simple boto call to get the instance based on the instance-id Simple boto call to get the instance based on the instance-id
""" """
instances = self.ec2_con.get_only_instances([instance_id]) reservations = self.ec2_con.describe_instances(InstanceIds=[instance_id]).get('Reservations', [])
instances = []
if len(reservations) == 1:
instances = reservations[0].get('Instances', [])
if len(instances) == 1: if len(instances) == 1:
return self.ec2_con.get_only_instances([instance_id])[0] return self.ec2_con.describe_instances(InstanceIds=[instance_id])['Reservations'][0]['Instances'][0]
else: else:
return None return None
...@@ -167,9 +163,13 @@ class LifecycleHandler: ...@@ -167,9 +163,13 @@ class LifecycleHandler:
with the value 'true' with the value 'true'
""" """
instance = self.get_ec2_instance_by_id(instance_id) instance = self.get_ec2_instance_by_id(instance_id)
tags_dict = {}
if instance: if instance:
if 'safe_to_retire' in instance.tags and instance.tags['safe_to_retire'].lower() == 'true': tags_dict = {}
for t in instance['Tags']:
tags_dict[t['Key']] = t['Value']
if 'safe_to_retire' in tags_dict and tags_dict['safe_to_retire'].lower() == 'true':
logging.info("Instance with id {id} is safe to retire.".format(id=instance_id)) logging.info("Instance with id {id} is safe to retire.".format(id=instance_id))
return True return True
else: else:
...@@ -184,9 +184,9 @@ class LifecycleHandler: ...@@ -184,9 +184,9 @@ class LifecycleHandler:
if __name__=="__main__": if __name__=="__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('-p', '--profile', parser.add_argument('-r', '--region',
help='The boto profile to use ' help='The aws region to use '
'per line.',default=None) 'per line.',default='us-east-1')
parser.add_argument('-b', '--bin-directory', required=False, default=None, parser.add_argument('-b', '--bin-directory', required=False, default=None,
help='The bin directory of the virtual env ' help='The bin directory of the virtual env '
'from which to run the AWS cli (optional)') 'from which to run the AWS cli (optional)')
...@@ -201,5 +201,5 @@ if __name__=="__main__": ...@@ -201,5 +201,5 @@ if __name__=="__main__":
parser.set_defaults(dry_run=False) parser.set_defaults(dry_run=False)
args = parser.parse_args() args = parser.parse_args()
lh = LifecycleHandler(args.profile, args.queue, args.hook, args.dry_run, args.bin_directory) lh = LifecycleHandler(args.region, args.queue, args.hook, args.dry_run, args.bin_directory)
lh.process_lifecycle_messages() lh.process_lifecycle_messages()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment