~canonical-ci-engineering/ubuntu-ci-services-itself/ansible

« back to all changes in this revision

Viewing changes to library/cloud/ec2_vol

  • Committer: Package Import Robot
  • Author(s): Michael Vogt, Harlan Lieberman-Berg, Michael Vogt
  • Date: 2013-11-01 09:40:59 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20131101094059-6w580ocxzqyqzuu3
Tags: 1.3.4+dfsg-1
[ Harlan Lieberman-Berg ]
* New upstream release (Closes: #717777).
  Fixes CVE-2013-2233 (Closes: #714822).
  Fixes CVE-2013-4259 (Closes: #721766).
* Drop fix-ansible-cfg patch.
* Change docsite generation to not expect docs as part of a wordpress install.
* Add trivial patch to fix lintian error with rpm-key script.
* Add patch header information to fix-html-makefile.

[ Michael Vogt ]
* add myself to uploader
* build/ship the module manpages for ansible in the ansible package

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
# This file is part of Ansible
 
3
#
 
4
# Ansible is free software: you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation, either version 3 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# Ansible is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
 
17
DOCUMENTATION = '''
 
18
---
 
19
module: ec2_vol
 
20
short_description: create and attach a volume, return volume id and device map
 
21
description:
 
22
    - creates an EBS volume and optionally attaches it to an instance.  If both an instance ID and a device name is given and the instance has a device at the device name, then no volume is created and no attachment is made.  This module has a dependency on python-boto.
 
23
version_added: "1.1"
 
24
options:
 
25
  aws_secret_key:
 
26
    description:
 
27
      - AWS secret key. If not set then the value of the AWS_SECRET_KEY environment variable is used. 
 
28
    required: false
 
29
    default: None
 
30
    aliases: ['ec2_secret_key', 'secret_key' ]
 
31
  aws_access_key:
 
32
    description:
 
33
      - AWS access key. If not set then the value of the AWS_ACCESS_KEY environment variable is used.
 
34
    required: false
 
35
    default: None
 
36
    aliases: ['ec2_access_key', 'access_key' ]
 
37
  ec2_url:
 
38
    description:
 
39
      - Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints).  Must be specified if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used
 
40
    required: false
 
41
    default: null
 
42
    aliases: []
 
43
  instance:
 
44
    description:
 
45
      - instance ID if you wish to attach the volume. 
 
46
    required: false
 
47
    default: null 
 
48
    aliases: []
 
49
  volume_size:
 
50
    description:
 
51
      - size of volume (in GB) to create.
 
52
    required: true
 
53
    default: null
 
54
    aliases: []
 
55
  iops:
 
56
    description:
 
57
      - the provisioned IOPs you want to associate with this volume (integer).
 
58
    required: false
 
59
    default: 100
 
60
    aliases: []
 
61
    version_added: "1.3"
 
62
  device_name:
 
63
    description:
 
64
      - device id to override device mapping. Assumes /dev/sdf for Linux/UNIX and /dev/xvdf for Windows.
 
65
    required: false
 
66
    default: null
 
67
    aliases: []
 
68
  region:
 
69
    description:
 
70
      - The AWS region to use. If not specified then the value of the EC2_REGION environment variable, if any, is used.
 
71
    required: false
 
72
    default: null
 
73
    aliases: ['aws_region', 'ec2_region']
 
74
  zone:
 
75
    description:
 
76
      - zone in which to create the volume, if unset uses the zone the instance is in (if set) 
 
77
    required: false
 
78
    default: null
 
79
    aliases: ['aws_zone', 'ec2_zone']
 
80
requirements: [ "boto" ]
 
81
author: Lester Wade
 
82
'''
 
83
 
 
84
EXAMPLES = '''
 
85
# Simple attachment action
 
86
- local_action: 
 
87
    module: ec2_vol 
 
88
    instance: XXXXXX 
 
89
    volume_size: 5 
 
90
    device_name: sdd
 
91
 
 
92
# Example using custom iops params   
 
93
- local_action: 
 
94
    module: ec2_vol 
 
95
    instance: XXXXXX 
 
96
    volume_size: 5 
 
97
    iops: 200
 
98
    device_name: sdd
 
99
 
 
100
# Playbook example combined with instance launch 
 
101
- local_action: 
 
102
    module: ec2 
 
103
    keypair: "{{ keypair }}"
 
104
    image: "{{ image }}"
 
105
    wait: yes 
 
106
    count: 3
 
107
    register: ec2
 
108
- local_action: 
 
109
    module: ec2_vol 
 
110
    instance: "{{ item.id }} " 
 
111
    volume_size: 5
 
112
    with_items: ec2.instances
 
113
    register: ec2_vol
 
114
'''
 
115
 
 
116
# Note: this module needs to be made idempotent. Possible solution is to use resource tags with the volumes.
 
117
# if state=present and it doesn't exist, create, tag and attach. 
 
118
# Check for state by looking for volume attachment with tag (and against block device mapping?).
 
119
# Would personally like to revisit this in May when Eucalyptus also has tagging support (3.3).
 
120
 
 
121
import sys
 
122
import time
 
123
 
 
124
try:
 
125
    import boto.ec2
 
126
except ImportError:
 
127
    print "failed=True msg='boto required for this module'"
 
128
    sys.exit(1)
 
129
 
 
130
AWS_REGIONS = ['ap-northeast-1',
 
131
               'ap-southeast-1',
 
132
               'ap-southeast-2',
 
133
               'eu-west-1',
 
134
               'sa-east-1',
 
135
               'us-east-1',
 
136
               'us-west-1',
 
137
               'us-west-2']
 
138
 
 
139
def main():
 
140
    module = AnsibleModule(
 
141
        argument_spec = dict(
 
142
            instance = dict(),
 
143
            volume_size = dict(required=True),
 
144
            iops = dict(),
 
145
            device_name = dict(),
 
146
            region = dict(aliases=['aws_region', 'ec2_region'], choices=AWS_REGIONS),
 
147
            zone = dict(aliases=['availability_zone', 'aws_zone', 'ec2_zone']),
 
148
            ec2_url = dict(),
 
149
            aws_secret_key = dict(aliases=['ec2_secret_key', 'secret_key'], no_log=True),
 
150
            aws_access_key = dict(aliases=['ec2_access_key', 'access_key']),
 
151
        )
 
152
    )
 
153
 
 
154
    instance = module.params.get('instance')
 
155
    volume_size = module.params.get('volume_size')
 
156
    iops = module.params.get('iops')
 
157
    device_name = module.params.get('device_name')
 
158
    region = module.params.get('region')
 
159
    zone = module.params.get('zone')
 
160
    ec2_url = module.params.get('ec2_url')
 
161
    aws_secret_key = module.params.get('aws_secret_key')
 
162
    aws_access_key = module.params.get('aws_access_key')
 
163
 
 
164
    # allow eucarc environment variables to be used if ansible vars aren't set
 
165
    if not ec2_url and 'EC2_URL' in os.environ:
 
166
        ec2_url = os.environ['EC2_URL']
 
167
 
 
168
    if not aws_secret_key:
 
169
        if  'AWS_SECRET_KEY' in os.environ:
 
170
            aws_secret_key = os.environ['AWS_SECRET_KEY']
 
171
        elif 'EC2_SECRET_KEY' in os.environ:
 
172
            aws_secret_key = os.environ['EC2_SECRET_KEY']
 
173
 
 
174
    if not aws_access_key:
 
175
        if 'AWS_ACCESS_KEY' in os.environ:
 
176
            aws_access_key = os.environ['AWS_ACCESS_KEY']
 
177
        elif 'EC2_ACCESS_KEY' in os.environ:
 
178
            aws_access_key = os.environ['EC2_ACCESS_KEY']
 
179
 
 
180
    if not region:
 
181
        if 'AWS_REGION' in os.environ:
 
182
            region = os.environ['AWS_REGION']
 
183
        elif 'EC2_REGION' in os.environ:
 
184
            region = os.environ['EC2_REGION']
 
185
    
 
186
    # If we have a region specified, connect to its endpoint.
 
187
    if region: 
 
188
        try:
 
189
            ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
 
190
        except boto.exception.NoAuthHandlerFound, e:
 
191
            module.fail_json(msg = str(e))
 
192
    # Otherwise, no region so we fallback to the old connection method
 
193
    elif ec2_url:
 
194
        try:
 
195
            ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key)
 
196
        except boto.exception.NoAuthHandlerFound, e:
 
197
            module.fail_json(msg = str(e))
 
198
    else:
 
199
        module.fail_json(msg="Either region or ec2_url must be specified")
 
200
 
 
201
    # Here we need to get the zone info for the instance. This covers situation where 
 
202
    # instance is specified but zone isn't.
 
203
    # Useful for playbooks chaining instance launch with volume create + attach and where the
 
204
    # zone doesn't matter to the user.
 
205
 
 
206
    if instance:
 
207
        reservation = ec2.get_all_instances(instance_ids=instance)
 
208
        inst = reservation[0].instances[0]
 
209
        zone = inst.placement
 
210
 
 
211
        # Check if there is a volume already mounted there.
 
212
        if device_name:
 
213
            if device_name in inst.block_device_mapping:
 
214
                module.exit_json(msg="Volume mapping for %s already exists on instance %s" % (device_name, instance),
 
215
 
 
216
                                 changed=False)
 
217
 
 
218
    # If custom iops is defined we use volume_type "io1" rather than the default of "standard"
 
219
 
 
220
    if iops:
 
221
        volume_type = 'io1'
 
222
    else:
 
223
        volume_type = 'standard'
 
224
 
 
225
    # If no instance supplied, try volume creation based on module parameters.
 
226
 
 
227
    try:
 
228
        volume = ec2.create_volume(volume_size, zone, None, volume_type, iops)
 
229
        while volume.status != 'available':
 
230
            time.sleep(3)
 
231
            volume.update()
 
232
    except boto.exception.BotoServerError, e:
 
233
        module.fail_json(msg = "%s: %s" % (e.error_code, e.error_message))
 
234
 
 
235
    # Attach the created volume.
 
236
 
 
237
    if device_name and instance:
 
238
        try:
 
239
            attach = volume.attach(inst.id, device_name)
 
240
            while volume.attachment_state() != 'attached':
 
241
                time.sleep(3)
 
242
                volume.update()
 
243
        except boto.exception.BotoServerError, e:
 
244
            module.fail_json(msg = "%s: %s" % (e.error_code, e.error_message))           
 
245
   
 
246
    # If device_name isn't set, make a choice based on best practices here:
 
247
    # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html
 
248
    
 
249
    # In future this needs to be more dynamic but combining block device mapping best practices
 
250
    # (bounds for devices, as above) with instance.block_device_mapping data would be tricky. For me ;)
 
251
 
 
252
    # Use password data attribute to tell whether the instance is Windows or Linux
 
253
 
 
254
    if device_name is None and instance:
 
255
        try:
 
256
            if not ec2.get_password_data(inst.id):
 
257
                device_name = '/dev/sdf'
 
258
                attach = volume.attach(inst.id, device_name)
 
259
                while volume.attachment_state() != 'attached':
 
260
                    time.sleep(3)
 
261
                    volume.update()
 
262
            else:
 
263
                device_name = '/dev/xvdf'
 
264
                attach = volume.attach(inst.id, device_name)
 
265
                while volume.attachment_state() != 'attached':
 
266
                    time.sleep(3)
 
267
                    volume.update()
 
268
        except boto.exception.BotoServerError, e:
 
269
            module.fail_json(msg = "%s: %s" % (e.error_code, e.error_message))
 
270
 
 
271
    print json.dumps({
 
272
        "volume_id": volume.id,
 
273
        "device": device_name
 
274
    })
 
275
    sys.exit(0)
 
276
 
 
277
# this is magic, see lib/ansible/module_common.py
 
278
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
 
279
 
 
280
main()