1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Adds support for creating GCE persistent disks from snapshots

This commit is contained in:
Chris Conway 2014-04-11 15:45:56 -07:00
parent 0b45b1256d
commit d227330a55
6 changed files with 183 additions and 7 deletions

View file

@ -24,9 +24,7 @@ short_description: utilize GCE persistent disk resources
description: description:
- This module can create and destroy unformatted GCE persistent disks - This module can create and destroy unformatted GCE persistent disks
U(https://developers.google.com/compute/docs/disks#persistentdisks). U(https://developers.google.com/compute/docs/disks#persistentdisks).
It also supports attaching and detaching disks from running instances It also supports attaching and detaching disks from running instances.
but does not support creating boot disks from images or snapshots. The
'gce' module supports creating instances with boot disks.
Full install/configuration instructions for the gce* modules can Full install/configuration instructions for the gce* modules can
be found in the comments of ansible/test/gce_tests.py. be found in the comments of ansible/test/gce_tests.py.
options: options:
@ -68,6 +66,12 @@ options:
required: false required: false
default: null default: null
aliases: [] aliases: []
snapshot:
description:
- the source snapshot to use for the disk
required: false
default: null
aliases: []
state: state:
description: description:
- desired state of the persistent disk - desired state of the persistent disk
@ -139,6 +143,7 @@ def main():
name = dict(required=True), name = dict(required=True),
size_gb = dict(default=10), size_gb = dict(default=10),
image = dict(), image = dict(),
snapshot = dict(),
state = dict(default='present'), state = dict(default='present'),
zone = dict(default='us-central1-b'), zone = dict(default='us-central1-b'),
service_account_email = dict(), service_account_email = dict(),
@ -155,6 +160,7 @@ def main():
name = module.params.get('name') name = module.params.get('name')
size_gb = module.params.get('size_gb') size_gb = module.params.get('size_gb')
image = module.params.get('image') image = module.params.get('image')
snapshot = module.params.get('snapshot')
state = module.params.get('state') state = module.params.get('state')
zone = module.params.get('zone') zone = module.params.get('zone')
@ -212,11 +218,20 @@ def main():
instance_name, zone), changed=False) instance_name, zone), changed=False)
if not disk: if not disk:
if image is not None and snapshot is not None:
module.fail_json(
msg='Cannot give both image (%s) and snapshot (%s)' % (
image, snapshot), changed=False)
lc_image = None lc_image = None
lc_snapshot = None
if image is not None: if image is not None:
lc_image = gce.ex_get_image(image) lc_image = gce.ex_get_image(image)
elif snapshot is not None:
lc_snapshot = gce.ex_get_snapshot(snapshot)
try: try:
disk = gce.create_volume(size_gb, name, location=zone, image=lc_image) disk = gce.create_volume(
size_gb, name, location=zone, image=lc_image,
snapshot=lc_snapshot)
except ResourceExistsError: except ResourceExistsError:
pass pass
except QuotaExceededError: except QuotaExceededError:
@ -225,7 +240,10 @@ def main():
except Exception, e: except Exception, e:
module.fail_json(msg=unexpected_error_msg(e), changed=False) module.fail_json(msg=unexpected_error_msg(e), changed=False)
json_output['size_gb'] = size_gb json_output['size_gb'] = size_gb
if image is not None:
json_output['image'] = image json_output['image'] = image
if snapshot is not None:
json_output['snapshot'] = snapshot
changed = True changed = True
if inst and not is_attached: if inst and not is_attached:
try: try:

View file

@ -39,6 +39,9 @@ cloud_cleanup: amazon_cleanup rackspace_cleanup
amazon_cleanup: amazon_cleanup:
python cleanup_ec2.py -y --match="^$(CLOUD_RESOURCE_PREFIX)" python cleanup_ec2.py -y --match="^$(CLOUD_RESOURCE_PREFIX)"
gce_setup:
python setup_gce.py "$(CLOUD_RESOURCE_PREFIX)"
gce_cleanup: gce_cleanup:
python cleanup_gce.py -y --match="^$(CLOUD_RESOURCE_PREFIX)" python cleanup_gce.py -y --match="^$(CLOUD_RESOURCE_PREFIX)"
@ -57,6 +60,7 @@ amazon: $(CREDENTIALS_FILE)
exit $$RC; exit $$RC;
gce: $(CREDENTIALS_FILE) gce: $(CREDENTIALS_FILE)
CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make gce_setup ; \
ansible-playbook gce.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -e "resource_prefix=$(CLOUD_RESOURCE_PREFIX)" -v $(TEST_FLAGS) ; \ ansible-playbook gce.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -e "resource_prefix=$(CLOUD_RESOURCE_PREFIX)" -v $(TEST_FLAGS) ; \
RC=$$? ; \ RC=$$? ; \
CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make gce_cleanup ; \ CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make gce_cleanup ; \

View file

@ -98,6 +98,12 @@ if __name__ == '__main__':
try: try:
# Delete matching instances # Delete matching instances
delete_gce_resources(gce.list_nodes, 'name', opts) delete_gce_resources(gce.list_nodes, 'name', opts)
# Delete matching snapshots
def get_snapshots():
for volume in gce.list_volumes():
for snapshot in gce.list_volume_snapshots(volume):
yield snapshot
delete_gce_resources(get_snapshots, 'name', opts)
# Delete matching disks # Delete matching disks
delete_gce_resources(gce.list_volumes, 'name', opts) delete_gce_resources(gce.list_volumes, 'name', opts)
except KeyboardInterrupt, e: except KeyboardInterrupt, e:

View file

@ -0,0 +1,51 @@
import collections
import os
import yaml
try:
from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver
_ = Provider.GCE
except ImportError:
print("failed=True " + \
"msg='libcloud with GCE support (0.13.3+) required for this module'")
sys.exit(1)
def add_credentials_options(parser):
default_service_account_email=None
default_pem_file=None
default_project_id=None
# Load details from credentials.yml
if os.path.isfile('credentials.yml'):
credentials = yaml.load(open('credentials.yml', 'r'))
default_service_account_email = credentials['gce_service_account_email']
default_pem_file = credentials['gce_pem_file']
default_project_id = credentials['gce_project_id']
parser.add_option("--service_account_email",
action="store", dest="service_account_email",
default=default_service_account_email,
help="GCE service account email. Default is loaded from credentials.yml.")
parser.add_option("--pem_file",
action="store", dest="pem_file",
default=default_pem_file,
help="GCE client key. Default is loaded from credentials.yml.")
parser.add_option("--project_id",
action="store", dest="project_id",
default=default_project_id,
help="Google Cloud project ID. Default is loaded from credentials.yml.")
def check_required(opts, parser):
for required in ['service_account_email', 'pem_file', 'project_id']:
if getattr(opts, required) is None:
parser.error("Missing required parameter: --%s" % required)
def get_gce_driver(opts):
# Connect to GCE
gce_cls = get_driver(Provider.GCE)
return gce_cls(
opts.service_account_email, opts.pem_file, project=opts.project_id)

View file

@ -161,3 +161,60 @@
- 'result.changed' - 'result.changed'
- 'result.name == "{{ instance_name }}"' - 'result.name == "{{ instance_name }}"'
- 'result.state == "absent"' - 'result.state == "absent"'
# ============================================================
- name: test snapshot given (state=present)
gce_pd:
name: "{{ instance_name }}"
snapshot: "{{ instance_name }}-snapshot"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
state: present
register: result
- name: assert image given (state=present)
assert:
that:
- 'result.changed'
- 'result.name == "{{ instance_name }}"'
- 'result.snapshot == "{{ instance_name }}-snapshot"'
- 'result.state == "present"'
# ============================================================
- name: test snapshot given (state=absent)
gce_pd:
name: "{{ instance_name }}"
snapshot: "{{ instance_name }}-snapshot"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
state: absent
register: result
- name: assert image given (state=absent)
assert:
that:
- 'result.changed'
- 'result.name == "{{ instance_name }}"'
- 'result.state == "absent"'
# ============================================================
- name: test both image and snapshot given
gce_pd:
name: "{{ instance_name }}"
image: "debian-7"
snapshot: "{{ instance_name }}-snapshot"
service_account_email: "{{ service_account_email }}"
pem_file: "{{ pem_file }}"
project_id: "{{ project_id }}"
state: present
register: result
ignore_errors: true
- name: assert image given (state=present)
assert:
that:
- 'result.failed'
- 'result.msg == "Cannot give both image (debian-7) and snapshot ({{ instance_name }}-snapshot)"'

View file

@ -0,0 +1,40 @@
'''
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.
'''
import sys
import optparse
import gce_credentials
def parse_args():
parser = optparse.OptionParser(
usage="%s [options] <prefix>" % (sys.argv[0],), description=__doc__)
gce_credentials.add_credentials_options(parser)
parser.add_option("--prefix",
action="store", dest="prefix",
help="String used to prefix GCE resource names (default: %default)")
(opts, args) = parser.parse_args()
gce_credentials.check_required(opts, parser)
if not args:
parser.error("Missing required argument: name prefix")
return (opts, args)
if __name__ == '__main__':
(opts, args) = parse_args()
gce = gce_credentials.get_gce_driver(opts)
prefix = args[0].lower()
try:
base_volume = gce.create_volume(
size=10, name=prefix+'-base', location='us-central1-a')
gce.create_volume_snapshot(base_volume, name=prefix+'-snapshot')
except KeyboardInterrupt, e:
print "\nExiting on user command."