From 0c734d3a3ee4e8b7e33f3a331949097bc40b7d8d Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Thu, 7 Nov 2013 21:40:29 -0500 Subject: [PATCH 1/3] New module: ec2_ubuntu_ami This module will retrieve the AMI of the most recent official Ubuntu EC2 image for a given Ubuntu release. --- cloud/ec2_ubuntu_ami | 177 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 cloud/ec2_ubuntu_ami diff --git a/cloud/ec2_ubuntu_ami b/cloud/ec2_ubuntu_ami new file mode 100644 index 00000000000..062971ab25f --- /dev/null +++ b/cloud/ec2_ubuntu_ami @@ -0,0 +1,177 @@ +#!/usr/bin/python +# +# (c) 2013, Nimbis Services +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +DOCUMENTATION = ''' +--- +module: ec2_ubuntu_ami +short_description: Retrieve AWS AMIs for official Ubuntu images +description: + - The Ubuntu project maintains a list of the latest version of Ubuntu images on EC2 accessible via http. + - This module retrieves the AMI for a given Ubuntu release by making an http query against the appropriate cloud-images.ubuntu.com url and parsing the output. + - For example: https://cloud-images.ubuntu.com/query/precise/server/released.current.txt has information about Ubuntu 12.04 (precise pangolin) release, server edition. + - Returns C(ami), C(aki), C(ari), C(serial), C(tag) + - If there is no AKI or ARI associated with an image, these will be C(null). + - Example output: C({"ami": "ami-69f5a900", "changed": false, "aki": "aki-88aa75e1", "tag": "release", "ari": null, "serial": "20131024"}) +options: + release: + description: short name of the release (e.g., C(precise)) + required: true + stream: + description: Type of release. + required: false + default: "server" + choices: ["server", "desktop"] + store: + description: Back-end store for instance + required: false + default: "ebs" + choices: ["ebs", "instance-store"] + arch: + description: CPU architecture + required: false + default: "amd64" + choices: ["i386", "amd64"] + region: + description: EC2 region + required: false + default: us-east-1 + choices: ["ap-northeast-1", "ap-southeast-1", "ap-southeast-2", + "eu-west-1", "sa-east-1", "us-east-1", "us-west-1", "us-west-2"] + virt: + description: virutalization type + required: false + default: paravirtual + choices: ["paravirtual", "hvm"] + +author: Lorin Hochstein +''' + +EXAMPLES = ''' +- name: Lauch an Ubuntu 12.04 (Precise Pangolin) EC2 instance + hosts: 127.0.0.1 + connection: local + tasks: + - name: Get the Ubuntu precise AMI + ec2_ubuntu_ami: release=precise region=us-west-1 store=instance-store + register: ubuntu_image + - name: Start the EC2 instance + ec2: image={{ ubuntu_image.ami }} instance_type=m1.small key_name=mykey +''' + +import csv +import json +import urllib2 +import urlparse + +AWS_REGIONS = ['ap-northeast-1', + 'ap-southeast-1', + 'ap-southeast-2', + 'eu-west-1', + 'sa-east-1', + 'us-east-1', + 'us-west-1', + 'us-west-2'] + + +def get_url(module, url): + """ Get url and return response """ + try: + r = urllib2.urlopen(url) + except (urllib2.HTTPError, urllib2.URLError), e: + code = getattr(e, 'code', -1) + module.fail_json(msg="Request failed: %s" % str(e), status_code=code) + return r + + +def get_ami(table, release, stream, store, + arch, region, virt): + """ Get the Ubuntu AMI that matches query given a table of AMIs + + table: an iterable that returns a row of + (release, stream, tag, serial, region, ami, aki, ari, virt) + release: ubuntu release name + stream: 'server' or 'desktop' + store: 'ebs' or 'instance-store' + arch: 'i386' or 'amd64' + region: EC2 region + virt: 'paravirtual' or 'hvm' + + Returns (ami, aki, ari, tag, serial)""" + expected = (release, stream, store, arch, region, virt) + + for row in table: + (actual_release, actual_stream, tag, serial, + actual_store, actual_arch, actual_region, ami, aki, ari, + actual_virt) = row + actual = (actual_release, actual_stream, actual_store, actual_arch, + actual_region, actual_virt) + if actual == expected: + # aki and ari are sometimes blank + if aki == '': + aki = None + if ari == '': + ari = None + return (ami, aki, ari, tag, serial) + + raise KeyError() + + +def get_ubuntu_url(release, stream): + url = "https://cloud-images.ubuntu.com/query/%s/%s/released.current.txt" + return url % (release, stream) + + +def main(): + arg_spec = dict( + release=dict(required=True), + stream=dict(required=False, default='server', + choices=['desktop', 'server']), + store=dict(required=False, default='ebs', + choices=['ebs', 'instance-store']), + arch=dict(required=False, default='amd64', + choices=['i386', 'amd64']), + region=dict(required=False, default='us-east-1', choices=AWS_REGIONS), + virt=dict(required=False, default='paravirtual', + choices=['paravirtual', 'hvm']) + ) + module = AnsibleModule(argument_spec=arg_spec) + release = module.params['release'] + stream = module.params['stream'] + store = module.params['store'] + arch = module.params['arch'] + region = module.params['region'] + virt = module.params['virt'] + + url = get_ubuntu_url(release, stream) + + req = get_url(module, url) + reader = csv.reader(req, delimiter='\t') + try: + ami, aki, ari, tag, serial = get_ami(reader, release, stream, store, + arch, region, virt) + module.exit_json(changed=False, ami=ami, aki=aki, ari=ari, tag=tag, + serial=serial) + except KeyError: + module.fail_json(msg="No matching AMI found") + + +# this is magic, see lib/ansible/module_common.py +#<> + +if __name__ == '__main__': + main() From 6f139b47bcb7c8a04ee8ff4f9a13f4283d6039f1 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Sat, 9 Nov 2013 14:54:56 -0500 Subject: [PATCH 2/3] Rename to ec2_ami_search --- cloud/{ec2_ubuntu_ami => ec2_ami_search} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cloud/{ec2_ubuntu_ami => ec2_ami_search} (100%) diff --git a/cloud/ec2_ubuntu_ami b/cloud/ec2_ami_search similarity index 100% rename from cloud/ec2_ubuntu_ami rename to cloud/ec2_ami_search From e1bbfa6210762c1819446027913ddfc13254e03d Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Sat, 9 Nov 2013 14:56:02 -0500 Subject: [PATCH 3/3] Genericize module to support multiple distros Make the module implementatino more generic to support distributions other than Ubuntu in the future. Adds distro as a new parameter. --- cloud/ec2_ami_search | 66 +++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/cloud/ec2_ami_search b/cloud/ec2_ami_search index 062971ab25f..0bb3fab0078 100644 --- a/cloud/ec2_ami_search +++ b/cloud/ec2_ami_search @@ -18,16 +18,18 @@ # along with Ansible. If not, see . DOCUMENTATION = ''' --- -module: ec2_ubuntu_ami -short_description: Retrieve AWS AMIs for official Ubuntu images +module: ec2_ami_search +short_description: Retrieve AWS AMI for a given operating system. description: - - The Ubuntu project maintains a list of the latest version of Ubuntu images on EC2 accessible via http. - - This module retrieves the AMI for a given Ubuntu release by making an http query against the appropriate cloud-images.ubuntu.com url and parsing the output. - - For example: https://cloud-images.ubuntu.com/query/precise/server/released.current.txt has information about Ubuntu 12.04 (precise pangolin) release, server edition. + - Look up the most recent AMI on AWS for a given operating system. - Returns C(ami), C(aki), C(ari), C(serial), C(tag) - If there is no AKI or ARI associated with an image, these will be C(null). - Example output: C({"ami": "ami-69f5a900", "changed": false, "aki": "aki-88aa75e1", "tag": "release", "ari": null, "serial": "20131024"}) options: + distro: + description: Linux distribution (e.g., C(ubuntu)) + required: true + choices: ["ubuntu"] release: description: short name of the release (e.g., C(precise)) required: true @@ -67,7 +69,7 @@ EXAMPLES = ''' connection: local tasks: - name: Get the Ubuntu precise AMI - ec2_ubuntu_ami: release=precise region=us-west-1 store=instance-store + ec2_ami_search: distro=ubuntu release=precise region=us-west-1 store=instance-store register: ubuntu_image - name: Start the EC2 instance ec2: image={{ ubuntu_image.ami }} instance_type=m1.small key_name=mykey @@ -78,6 +80,8 @@ import json import urllib2 import urlparse +SUPPORTED_DISTROS = ['ubuntu'] + AWS_REGIONS = ['ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2', @@ -98,9 +102,31 @@ def get_url(module, url): return r -def get_ami(table, release, stream, store, - arch, region, virt): - """ Get the Ubuntu AMI that matches query given a table of AMIs +def ubuntu(module): + """ Get the ami for ubuntu """ + + release = module.params['release'] + stream = module.params['stream'] + store = module.params['store'] + arch = module.params['arch'] + region = module.params['region'] + virt = module.params['virt'] + + url = get_ubuntu_url(release, stream) + + req = get_url(module, url) + reader = csv.reader(req, delimiter='\t') + try: + ami, aki, ari, tag, serial = lookup_ubuntu_ami(reader, release, stream, + store, arch, region, virt) + module.exit_json(changed=False, ami=ami, aki=aki, ari=ari, tag=tag, + serial=serial) + except KeyError: + module.fail_json(msg="No matching AMI found") + + +def lookup_ubuntu_ami(table, release, stream, store, arch, region, virt): + """ Look up the Ubuntu AMI that matches query given a table of AMIs table: an iterable that returns a row of (release, stream, tag, serial, region, ami, aki, ari, virt) @@ -138,6 +164,7 @@ def get_ubuntu_url(release, stream): def main(): arg_spec = dict( + distro=dict(required=True, choices=SUPPORTED_DISTROS), release=dict(required=True), stream=dict(required=False, default='server', choices=['desktop', 'server']), @@ -150,24 +177,13 @@ def main(): choices=['paravirtual', 'hvm']) ) module = AnsibleModule(argument_spec=arg_spec) - release = module.params['release'] - stream = module.params['stream'] - store = module.params['store'] - arch = module.params['arch'] - region = module.params['region'] - virt = module.params['virt'] + distro = module.params['distro'] - url = get_ubuntu_url(release, stream) + if distro == 'ubuntu': + ubuntu(module) + else: + module.fail_json(msg="Unsupported distro: %s" % distro) - req = get_url(module, url) - reader = csv.reader(req, delimiter='\t') - try: - ami, aki, ari, tag, serial = get_ami(reader, release, stream, store, - arch, region, virt) - module.exit_json(changed=False, ami=ami, aki=aki, ari=ari, tag=tag, - serial=serial) - except KeyError: - module.fail_json(msg="No matching AMI found") # this is magic, see lib/ansible/module_common.py