Adds support for attaching persistent disks to GCE instances

pull/6976/head
Chris Conway 10 years ago
parent d227330a55
commit 2257a69723

@ -89,6 +89,12 @@ options:
required: false
default: "false"
aliases: []
disks:
description:
- a list of persistent disks to attach to the instance; a string value gives the name of the disk; alternatively, a dictionary value can define 'name' and 'mode' ('READ_ONLY' or 'READ_WRITE'). The first entry will be the boot disk (which must be READ_WRITE).
required: false
default: null
aliases: []
state:
description:
- desired state of the resource
@ -210,8 +216,16 @@ def get_instance_info(inst):
netname = inst.extra['networkInterfaces'][0]['network'].split('/')[-1]
except:
netname = None
if 'disks' in inst.extra:
disk_names = [disk_info['source'].split('/')[-1]
for disk_info
in sorted(inst.extra['disks'],
key=lambda disk_info: disk_info['index'])]
else:
disk_names = []
return({
'image': not inst.image is None and inst.image.split('/')[-1] or None,
'disks': disk_names,
'machine_type': inst.size,
'metadata': metadata,
'name': inst.name,
@ -241,6 +255,7 @@ def create_instances(module, gce, instance_names):
metadata = module.params.get('metadata')
network = module.params.get('network')
persistent_boot_disk = module.params.get('persistent_boot_disk')
disks = module.params.get('disks')
state = module.params.get('state')
tags = module.params.get('tags')
zone = module.params.get('zone')
@ -249,6 +264,16 @@ def create_instances(module, gce, instance_names):
changed = False
lc_image = gce.ex_get_image(image)
lc_disks = []
disk_modes = []
for i, disk in enumerate(disks or []):
if isinstance(disk, dict):
lc_disks.append(gce.ex_get_volume(disk['name']))
disk_modes.append(disk['mode'])
else:
lc_disks.append(gce.ex_get_volume(disk))
# boot disk is implicitly READ_WRITE
disk_modes.append('READ_ONLY' if i > 0 else 'READ_WRITE')
lc_network = gce.ex_get_network(network)
lc_machine_type = gce.ex_get_size(machine_type)
lc_zone = gce.ex_get_zone(zone)
@ -283,7 +308,9 @@ def create_instances(module, gce, instance_names):
for name in instance_names:
pd = None
if persistent_boot_disk:
if lc_disks:
pd = lc_disks[0]
elif persistent_boot_disk:
try:
pd = gce.create_volume(None, "%s" % name, image=lc_image)
except ResourceExistsError:
@ -300,6 +327,28 @@ def create_instances(module, gce, instance_names):
module.fail_json(msg='Unexpected error attempting to create ' + \
'instance %s, error: %s' % (name, e.value))
for i, lc_disk in enumerate(lc_disks):
# Check whether the disk is already attached
if (len(inst.extra['disks']) > i):
attached_disk = inst.extra['disks'][i]
if attached_disk['source'] != lc_disk.extra['selfLink']:
module.fail_json(
msg=("Disk at index %d does not match: requested=%s found=%s" % (
i, lc_disk.extra['selfLink'], attached_disk['source'])))
elif attached_disk['mode'] != disk_modes[i]:
module.fail_json(
msg=("Disk at index %d is in the wrong mode: requested=%s found=%s" % (
i, disk_modes[i], attached_disk['mode'])))
else:
continue
gce.attach_volume(inst, lc_disk, ex_mode=disk_modes[i])
# Work around libcloud bug: attached volumes don't get added
# to the instance metadata. get_instance_info() only cares about
# source and index.
if len(inst.extra['disks']) != i+1:
inst.extra['disks'].append(
{'source': lc_disk.extra['selfLink'], 'index': i})
if inst:
new_instances.append(inst)
@ -352,6 +401,7 @@ def main():
name = dict(),
network = dict(default='default'),
persistent_boot_disk = dict(type='bool', default=False),
disks = dict(type='list'),
state = dict(choices=['active', 'present', 'absent', 'deleted'],
default='present'),
tags = dict(type='list'),

@ -95,3 +95,117 @@
- 'result.name == "{{ instance_name }}"'
- 'result.state == "absent"'
# ============================================================
- name: test disks given (expected changed=true)
gce:
name: "{{ instance_name }}"
disks:
- "{{ instance_name }}-base"
- "{{ instance_name }}-extra"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
state: present
register: result
- name: assert disks given
assert:
that:
- 'result.changed'
- 'result.instance_data[0].disks == ["{{ instance_name }}-base", "{{ instance_name }}-extra"]'
- 'result.state == "present"'
# ============================================================
- name: test disks given (expected changed=false)
gce:
name: "{{ instance_name }}"
disks:
- "{{ instance_name }}-base"
- "{{ instance_name }}-extra"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
state: present
register: result
- name: assert disks given
assert:
that:
- 'not result.changed'
- 'result.instance_data[0].disks == ["{{ instance_name }}-base", "{{ instance_name }}-extra"]'
- 'result.state == "present"'
# ============================================================
- name: test disks in the wrong order
gce:
name: "{{ instance_name }}"
disks:
- "{{ instance_name }}-extra"
- "{{ instance_name }}-base"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
register: result
ignore_errors: true
- name: assert disks in the wrong order
assert:
that:
- 'result.failed'
- '{{ result.msg | match("Disk at index 0 does not match:.*") }}'
# ============================================================
- name: test disks given with name and mode
gce:
name: "{{ instance_name }}"
disks:
- { name: "{{ instance_name }}-base", mode: "READ_WRITE" }
- { name: "{{ instance_name }}-extra", mode: "READ_ONLY" }
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
register: result
- name: assert disks given
assert:
that:
- 'not result.changed'
- 'result.state == "present"'
# ============================================================
- name: test disks given with name and wrong mode
gce:
name: "{{ instance_name }}"
disks:
- { name: "{{ instance_name }}-base", mode: "READ_ONLY" }
- "{{ instance_name }}-extra"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
register: result
ignore_errors: true
- name: assert disks given
assert:
that:
- 'result.failed'
- '{{ result.msg | match("Disk at index 0 is in the wrong mode:.*") }}'
# ============================================================
- name: test disks given, state absent (expected changed=true)
gce:
name: "{{ instance_name }}"
disks:
- "{{ instance_name }}-base"
- "{{ instance_name }}-extra"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
state: absent
register: result
- name: assert disks given, state absent (expected changed=true)
assert:
that:
- 'result.changed'
- 'result.state == "absent"'

@ -1,10 +1,10 @@
'''
Create GCE resources for use in integration tests.
Takes a prefix as a command-line argument and creates a persistent disk
named ${prefix}-base and a snapshot of it named ${prefix}-snapshot.
prefix will be forced to lowercase, to ensure the names are legal GCE
resource names.
Takes a prefix as a command-line argument and creates two persistent disks named
${prefix}-base and ${prefix}-extra and a snapshot of the base disk named
${prefix}-snapshot. prefix will be forced to lowercase, to ensure the names are
legal GCE resource names.
'''
import sys
@ -36,5 +36,7 @@ if __name__ == '__main__':
base_volume = gce.create_volume(
size=10, name=prefix+'-base', location='us-central1-a')
gce.create_volume_snapshot(base_volume, name=prefix+'-snapshot')
gce.create_volume(
size=10, name=prefix+'-extra', location='us-central1-a')
except KeyboardInterrupt, e:
print "\nExiting on user command."

Loading…
Cancel
Save