1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00
community.general/plugins/modules/cloud/google/gce_instance_template.py
Alexei Znamensky 4b26990d8b
Tidy up validate-modules ignores for cloud/google modules (#1265)
* fixed validation-modules for plugins/modules/cloud/google/gcdns_record.py

* fixed validation-modules for plugins/modules/cloud/google/gcdns_zone.py

* fixed validation-modules for plugins/modules/cloud/google/gce_eip.py

* fixed validation-modules for plugins/modules/cloud/google/gce_img.py

* fixed validation-modules for plugins/modules/cloud/google/gce_instance_template.py

* fixed validation-modules for plugins/modules/cloud/google/gce_labels.py

* fixed validation-modules for plugins/modules/cloud/google/gce_lb.py

* fixed validation-modules for plugins/modules/cloud/google/gce_mig.py

* fixed validation-modules for plugins/modules/cloud/google/gce_net.py

* fixed validation-modules for plugins/modules/cloud/google/gce_pd.py

* fixed validation-modules for plugins/modules/cloud/google/gce_snapshot.py

* fixed validation-modules for plugins/modules/cloud/google/gce_tag.py

* fixed validation-modules for plugins/modules/cloud/google/gcp_backend_service.py

* fixed validation-modules for plugins/modules/cloud/google/gcp_forwarding_rule.py

* fixed validation-modules for plugins/modules/cloud/google/gcp_healthcheck.py

* fixed validation-modules for plugins/modules/cloud/google/gcp_target_proxy.py

* fixed validation-modules for plugins/modules/cloud/google/gcpubsub_info.py

* fixed validation-modules for plugins/modules/cloud/google/gcpubsub.py

* fixed validation-modules for plugins/modules/cloud/google/gcp_url_map.py

* fixed validation-modules for plugins/modules/cloud/google/gcspanner.py

* fixed validation-modules for plugins/modules/cloud/google/gc_storage.py

* adjust parameter description in gce_eip.py

* fixed validation-modules for plugins/modules/cloud/google/gce.py

* removed extra type definition

* reformatted long lines

* Tidy up validate-modules ignores for cloud/google modules

* gc_storage.py: fixed parameter to be overwrite and alias force, instead of the other way around

* rolled back a number of ignore lines that ansible 2.9 believes to defy sanity

* gce_instance_template.py: the metadata parameter brings no definition whatsoever in argument_spec, causing a number of problems. Rolling back for now.

* Fixes on docs from the PR
2020-11-12 08:24:30 +01:00

605 lines
19 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: gce_instance_template
short_description: create or destroy instance templates of Compute Engine of GCP.
description:
- Creates or destroy Google instance templates
of Compute Engine of Google Cloud Platform.
options:
state:
type: str
description:
- The desired state for the instance template.
default: "present"
choices: ["present", "absent"]
name:
type: str
description:
- The name of the GCE instance template.
required: True
aliases: [base_name]
size:
type: str
description:
- The desired machine type for the instance template.
default: "f1-micro"
source:
type: str
description:
- A source disk to attach to the instance.
Cannot specify both I(image) and I(source).
image:
type: str
description:
- The image to use to create the instance.
Cannot specify both both I(image) and I(source).
image_family:
type: str
description:
- The image family to use to create the instance.
If I(image) has been used I(image_family) is ignored.
Cannot specify both I(image) and I(source).
default: debian-8
disk_type:
type: str
description:
- Specify a C(pd-standard) disk or C(pd-ssd) for an SSD disk.
choices:
- pd-standard
- pd-ssd
default: pd-standard
disk_auto_delete:
description:
- Indicate that the boot disk should be
deleted when the Node is deleted.
default: true
type: bool
network:
type: str
description:
- The network to associate with the instance.
default: "default"
subnetwork:
type: str
description:
- The Subnetwork resource name for this instance.
can_ip_forward:
description:
- Set to C(yes) to allow instance to
send/receive non-matching src/dst packets.
type: bool
default: 'no'
external_ip:
type: str
description:
- The external IP address to use.
If C(ephemeral), a new non-static address will be
used. If C(None), then no external address will
be used. To use an existing static IP address
specify address name.
default: "ephemeral"
service_account_email:
type: str
description:
- service account email
service_account_permissions:
type: list
description:
- service account permissions (see
U(https://cloud.google.com/sdk/gcloud/reference/compute/instances/create),
--scopes section for detailed information)
- >
Available choices are:
C(bigquery), C(cloud-platform), C(compute-ro), C(compute-rw),
C(useraccounts-ro), C(useraccounts-rw), C(datastore), C(logging-write),
C(monitoring), C(sql-admin), C(storage-full), C(storage-ro),
C(storage-rw), C(taskqueue), C(userinfo-email).
automatic_restart:
description:
- Defines whether the instance should be
automatically restarted when it is
terminated by Compute Engine.
type: bool
preemptible:
description:
- Defines whether the instance is preemptible.
type: bool
tags:
type: list
description:
- a comma-separated list of tags to associate with the instance
metadata:
description:
- a hash/dictionary of custom data for the instance;
'{"key":"value", ...}'
description:
type: str
description:
- description of instance template
disks:
type: list
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).
nic_gce_struct:
type: list
description:
- Support passing in the GCE-specific
formatted networkInterfaces[] structure.
disks_gce_struct:
type: list
description:
- Support passing in the GCE-specific
formatted formatted disks[] structure. Case sensitive.
see U(https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource) for detailed information
project_id:
type: str
description:
- your GCE project ID
pem_file:
type: path
description:
- path to the pem file associated with the service account email
This option is deprecated. Use 'credentials_file'.
credentials_file:
type: path
description:
- path to the JSON file associated with the service account email
subnetwork_region:
type: str
description:
- Region that subnetwork resides in. (Required for subnetwork to successfully complete)
requirements:
- "python >= 2.6"
- "apache-libcloud >= 0.13.3, >= 0.17.0 if using JSON credentials,
>= 0.20.0 if using preemptible option"
notes:
- JSON credentials strongly preferred.
author: "Gwenael Pellen (@GwenaelPellenArkeup) <gwenael.pellen@arkeup.com>"
'''
EXAMPLES = '''
# Usage
- name: Create instance template named foo
community.general.gce_instance_template:
name: foo
size: n1-standard-1
image_family: ubuntu-1604-lts
state: present
project_id: "your-project-name"
credentials_file: "/path/to/your-key.json"
service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com"
# Example Playbook
- name: Compute Engine Instance Template Examples
hosts: localhost
vars:
service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com"
credentials_file: "/path/to/your-key.json"
project_id: "your-project-name"
tasks:
- name: Create instance template
community.general.gce_instance_template:
name: my-test-instance-template
size: n1-standard-1
image_family: ubuntu-1604-lts
state: present
project_id: "{{ project_id }}"
credentials_file: "{{ credentials_file }}"
service_account_email: "{{ service_account_email }}"
- name: Delete instance template
community.general.gce_instance_template:
name: my-test-instance-template
size: n1-standard-1
image_family: ubuntu-1604-lts
state: absent
project_id: "{{ project_id }}"
credentials_file: "{{ credentials_file }}"
service_account_email: "{{ service_account_email }}"
# Example playbook using disks_gce_struct
- name: Compute Engine Instance Template Examples
hosts: localhost
vars:
service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com"
credentials_file: "/path/to/your-key.json"
project_id: "your-project-name"
tasks:
- name: Create instance template
community.general.gce_instance_template:
name: foo
size: n1-standard-1
state: present
project_id: "{{ project_id }}"
credentials_file: "{{ credentials_file }}"
service_account_email: "{{ service_account_email }}"
disks_gce_struct:
- device_name: /dev/sda
boot: true
autoDelete: true
initializeParams:
diskSizeGb: 30
diskType: pd-ssd
sourceImage: projects/debian-cloud/global/images/family/debian-8
'''
RETURN = '''
'''
import traceback
try:
from ast import literal_eval
HAS_PYTHON26 = True
except ImportError:
HAS_PYTHON26 = False
try:
import libcloud
from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver
from libcloud.common.google import GoogleBaseError, QuotaExceededError, \
ResourceExistsError, ResourceInUseError, ResourceNotFoundError
from libcloud.compute.drivers.gce import GCEAddress
_ = Provider.GCE
HAS_LIBCLOUD = True
except ImportError:
HAS_LIBCLOUD = False
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gce import gce_connect
from ansible.module_utils._text import to_native
def get_info(inst):
"""Retrieves instance template information
"""
return({
'name': inst.name,
'extra': inst.extra,
})
def create_instance_template(module, gce):
"""Create an instance template
module : AnsibleModule object
gce: authenticated GCE libcloud driver
Returns:
instance template information
"""
# get info from module
name = module.params.get('name')
size = module.params.get('size')
source = module.params.get('source')
image = module.params.get('image')
image_family = module.params.get('image_family')
disk_type = module.params.get('disk_type')
disk_auto_delete = module.params.get('disk_auto_delete')
network = module.params.get('network')
subnetwork = module.params.get('subnetwork')
subnetwork_region = module.params.get('subnetwork_region')
can_ip_forward = module.params.get('can_ip_forward')
external_ip = module.params.get('external_ip')
service_account_permissions = module.params.get(
'service_account_permissions')
service_account_email = module.params.get('service_account_email')
on_host_maintenance = module.params.get('on_host_maintenance')
automatic_restart = module.params.get('automatic_restart')
preemptible = module.params.get('preemptible')
tags = module.params.get('tags')
metadata = module.params.get('metadata')
description = module.params.get('description')
disks_gce_struct = module.params.get('disks_gce_struct')
changed = False
# args of ex_create_instancetemplate
gce_args = dict(
name="instance",
size="f1-micro",
source=None,
image=None,
disk_type='pd-standard',
disk_auto_delete=True,
network='default',
subnetwork=None,
can_ip_forward=None,
external_ip='ephemeral',
service_accounts=None,
on_host_maintenance=None,
automatic_restart=None,
preemptible=None,
tags=None,
metadata=None,
description=None,
disks_gce_struct=None,
nic_gce_struct=None
)
gce_args['name'] = name
gce_args['size'] = size
if source is not None:
gce_args['source'] = source
if image:
gce_args['image'] = image
else:
if image_family:
image = gce.ex_get_image_from_family(image_family)
gce_args['image'] = image
else:
gce_args['image'] = "debian-8"
gce_args['disk_type'] = disk_type
gce_args['disk_auto_delete'] = disk_auto_delete
gce_network = gce.ex_get_network(network)
gce_args['network'] = gce_network
if subnetwork is not None:
gce_args['subnetwork'] = gce.ex_get_subnetwork(subnetwork, region=subnetwork_region)
if can_ip_forward is not None:
gce_args['can_ip_forward'] = can_ip_forward
if external_ip == "ephemeral":
instance_external_ip = external_ip
elif external_ip == "none":
instance_external_ip = None
else:
try:
instance_external_ip = gce.ex_get_address(external_ip)
except GoogleBaseError as err:
# external_ip is name ?
instance_external_ip = external_ip
gce_args['external_ip'] = instance_external_ip
ex_sa_perms = []
bad_perms = []
if service_account_permissions:
for perm in service_account_permissions:
if perm not in gce.SA_SCOPES_MAP:
bad_perms.append(perm)
if len(bad_perms) > 0:
module.fail_json(msg='bad permissions: %s' % str(bad_perms))
if service_account_email is not None:
ex_sa_perms.append({'email': str(service_account_email)})
else:
ex_sa_perms.append({'email': "default"})
ex_sa_perms[0]['scopes'] = service_account_permissions
gce_args['service_accounts'] = ex_sa_perms
if on_host_maintenance is not None:
gce_args['on_host_maintenance'] = on_host_maintenance
if automatic_restart is not None:
gce_args['automatic_restart'] = automatic_restart
if preemptible is not None:
gce_args['preemptible'] = preemptible
if tags is not None:
gce_args['tags'] = tags
if disks_gce_struct is not None:
gce_args['disks_gce_struct'] = disks_gce_struct
# Try to convert the user's metadata value into the format expected
# by GCE. First try to ensure user has proper quoting of a
# dictionary-like syntax using 'literal_eval', then convert the python
# dict into a python list of 'key' / 'value' dicts. Should end up
# with:
# [ {'key': key1, 'value': value1}, {'key': key2, 'value': value2}, ...]
if metadata:
if isinstance(metadata, dict):
md = metadata
else:
try:
md = literal_eval(str(metadata))
if not isinstance(md, dict):
raise ValueError('metadata must be a dict')
except ValueError as e:
module.fail_json(msg='bad metadata: %s' % str(e))
except SyntaxError as e:
module.fail_json(msg='bad metadata syntax')
if hasattr(libcloud, '__version__') and libcloud.__version__ < '0.15':
items = []
for k, v in md.items():
items.append({"key": k, "value": v})
metadata = {'items': items}
else:
metadata = md
gce_args['metadata'] = metadata
if description is not None:
gce_args['description'] = description
instance = None
try:
instance = gce.ex_get_instancetemplate(name)
except ResourceNotFoundError:
try:
instance = gce.ex_create_instancetemplate(**gce_args)
changed = True
except GoogleBaseError as err:
module.fail_json(
msg='Unexpected error attempting to create instance {0}, error: {1}'
.format(
instance,
err.value
)
)
if instance:
json_data = get_info(instance)
else:
module.fail_json(msg="no instance template!")
return (changed, json_data, name)
def delete_instance_template(module, gce):
""" Delete instance template.
module : AnsibleModule object
gce: authenticated GCE libcloud driver
Returns:
instance template information
"""
name = module.params.get('name')
current_state = "absent"
changed = False
# get instance template
instance = None
try:
instance = gce.ex_get_instancetemplate(name)
current_state = "present"
except GoogleBaseError as e:
json_data = dict(msg='instance template not exists: %s' % to_native(e),
exception=traceback.format_exc())
if current_state == "present":
rc = instance.destroy()
if rc:
changed = True
else:
module.fail_json(
msg='instance template destroy failed'
)
json_data = {}
return (changed, json_data, name)
def module_controller(module, gce):
''' Control module state parameter.
module : AnsibleModule object
gce: authenticated GCE libcloud driver
Returns:
nothing
Exit:
AnsibleModule object exit with json data.
'''
json_output = dict()
state = module.params.get("state")
if state == "present":
(changed, output, name) = create_instance_template(module, gce)
json_output['changed'] = changed
json_output['msg'] = output
elif state == "absent":
(changed, output, name) = delete_instance_template(module, gce)
json_output['changed'] = changed
json_output['msg'] = output
module.exit_json(**json_output)
def check_if_system_state_would_be_changed(module, gce):
''' check_if_system_state_would_be_changed !
module : AnsibleModule object
gce: authenticated GCE libcloud driver
Returns:
system_state changed
'''
changed = False
current_state = "absent"
state = module.params.get("state")
name = module.params.get("name")
try:
gce.ex_get_instancetemplate(name)
current_state = "present"
except GoogleBaseError as e:
module.fail_json(msg='GCE get instancetemplate problem: %s' % to_native(e),
exception=traceback.format_exc())
if current_state != state:
changed = True
if current_state == "absent":
if changed:
output = 'instance template {0} will be created'.format(name)
else:
output = 'nothing to do for instance template {0} '.format(name)
if current_state == "present":
if changed:
output = 'instance template {0} will be destroyed'.format(name)
else:
output = 'nothing to do for instance template {0} '.format(name)
return (changed, output)
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(choices=['present', 'absent'], default='present'),
name=dict(required=True, aliases=['base_name']),
size=dict(default='f1-micro'),
source=dict(),
image=dict(),
image_family=dict(default='debian-8'),
disk_type=dict(choices=['pd-standard', 'pd-ssd'], default='pd-standard', type='str'),
disk_auto_delete=dict(type='bool', default=True),
network=dict(default='default'),
subnetwork=dict(),
can_ip_forward=dict(type='bool', default=False),
external_ip=dict(default='ephemeral'),
service_account_email=dict(),
service_account_permissions=dict(type='list'),
automatic_restart=dict(type='bool', default=None),
preemptible=dict(type='bool', default=None),
tags=dict(type='list'),
metadata=dict(),
description=dict(),
disks=dict(type='list'),
nic_gce_struct=dict(type='list'),
project_id=dict(),
pem_file=dict(type='path'),
credentials_file=dict(type='path'),
subnetwork_region=dict(),
disks_gce_struct=dict(type='list')
),
mutually_exclusive=[['source', 'image']],
required_one_of=[['image', 'image_family']],
supports_check_mode=True
)
if not HAS_PYTHON26:
module.fail_json(
msg="GCE module requires python's 'ast' module, python v2.6+")
if not HAS_LIBCLOUD:
module.fail_json(
msg='libcloud with GCE support (0.17.0+) required for this module')
try:
gce = gce_connect(module)
except GoogleBaseError as e:
module.fail_json(msg='GCE Connection failed %s' % to_native(e), exception=traceback.format_exc())
if module.check_mode:
(changed, output) = check_if_system_state_would_be_changed(module, gce)
module.exit_json(
changed=changed,
msg=output
)
else:
module_controller(module, gce)
if __name__ == '__main__':
main()