Adds support for creating GCE persistent disks from snapshots
This commit is contained in:
parent
0b45b1256d
commit
d227330a55
6 changed files with 183 additions and 7 deletions
|
@ -24,9 +24,7 @@ short_description: utilize GCE persistent disk resources
|
|||
description:
|
||||
- This module can create and destroy unformatted GCE persistent disks
|
||||
U(https://developers.google.com/compute/docs/disks#persistentdisks).
|
||||
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.
|
||||
It also supports attaching and detaching disks from running instances.
|
||||
Full install/configuration instructions for the gce* modules can
|
||||
be found in the comments of ansible/test/gce_tests.py.
|
||||
options:
|
||||
|
@ -68,6 +66,12 @@ options:
|
|||
required: false
|
||||
default: null
|
||||
aliases: []
|
||||
snapshot:
|
||||
description:
|
||||
- the source snapshot to use for the disk
|
||||
required: false
|
||||
default: null
|
||||
aliases: []
|
||||
state:
|
||||
description:
|
||||
- desired state of the persistent disk
|
||||
|
@ -139,6 +143,7 @@ def main():
|
|||
name = dict(required=True),
|
||||
size_gb = dict(default=10),
|
||||
image = dict(),
|
||||
snapshot = dict(),
|
||||
state = dict(default='present'),
|
||||
zone = dict(default='us-central1-b'),
|
||||
service_account_email = dict(),
|
||||
|
@ -155,6 +160,7 @@ def main():
|
|||
name = module.params.get('name')
|
||||
size_gb = module.params.get('size_gb')
|
||||
image = module.params.get('image')
|
||||
snapshot = module.params.get('snapshot')
|
||||
state = module.params.get('state')
|
||||
zone = module.params.get('zone')
|
||||
|
||||
|
@ -212,11 +218,20 @@ def main():
|
|||
instance_name, zone), changed=False)
|
||||
|
||||
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_snapshot = 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:
|
||||
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:
|
||||
pass
|
||||
except QuotaExceededError:
|
||||
|
@ -225,7 +240,10 @@ def main():
|
|||
except Exception, e:
|
||||
module.fail_json(msg=unexpected_error_msg(e), changed=False)
|
||||
json_output['size_gb'] = size_gb
|
||||
json_output['image'] = image
|
||||
if image is not None:
|
||||
json_output['image'] = image
|
||||
if snapshot is not None:
|
||||
json_output['snapshot'] = snapshot
|
||||
changed = True
|
||||
if inst and not is_attached:
|
||||
try:
|
||||
|
|
|
@ -39,6 +39,9 @@ cloud_cleanup: amazon_cleanup rackspace_cleanup
|
|||
amazon_cleanup:
|
||||
python cleanup_ec2.py -y --match="^$(CLOUD_RESOURCE_PREFIX)"
|
||||
|
||||
gce_setup:
|
||||
python setup_gce.py "$(CLOUD_RESOURCE_PREFIX)"
|
||||
|
||||
gce_cleanup:
|
||||
python cleanup_gce.py -y --match="^$(CLOUD_RESOURCE_PREFIX)"
|
||||
|
||||
|
@ -57,7 +60,8 @@ amazon: $(CREDENTIALS_FILE)
|
|||
exit $$RC;
|
||||
|
||||
gce: $(CREDENTIALS_FILE)
|
||||
ansible-playbook gce.yml -i $(INVENTORY) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -e "resource_prefix=$(CLOUD_RESOURCE_PREFIX)" -v $(TEST_FLAGS) ; \
|
||||
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) ; \
|
||||
RC=$$? ; \
|
||||
CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make gce_cleanup ; \
|
||||
exit $$RC;
|
||||
|
|
|
@ -98,6 +98,12 @@ if __name__ == '__main__':
|
|||
try:
|
||||
# Delete matching instances
|
||||
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_gce_resources(gce.list_volumes, 'name', opts)
|
||||
except KeyboardInterrupt, e:
|
||||
|
|
51
test/integration/gce_credentials.py
Normal file
51
test/integration/gce_credentials.py
Normal 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)
|
|
@ -161,3 +161,60 @@
|
|||
- 'result.changed'
|
||||
- 'result.name == "{{ instance_name }}"'
|
||||
- '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)"'
|
||||
|
||||
|
|
40
test/integration/setup_gce.py
Normal file
40
test/integration/setup_gce.py
Normal 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."
|
Loading…
Reference in a new issue