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

Remove deprecated google modules (#1370)

* Remove deprecated google modules.

* Remove ignore.txt entries.
This commit is contained in:
Felix Fontein 2020-11-24 06:34:48 +01:00 committed by GitHub
parent eab9a43d2e
commit c52839c601
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 28 additions and 4610 deletions

View file

@ -0,0 +1,10 @@
removed_features:
- The deprecated ``gcdns_record`` module has been removed. Use ``google.cloud.gcp_dns_resource_record_set`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcdns_zone`` module has been removed. Use ``google.cloud.gcp_dns_managed_zone`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gce`` module has been removed. Use ``google.cloud.gcp_compute_instance`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_backend_service`` module has been removed. Use ``google.cloud.gcp_compute_backend_service`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_forwarding_rule`` module has been removed. Use ``google.cloud.gcp_compute_forwarding_rule`` or ``google.cloud.gcp_compute_global_forwarding_rule`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_healthcheck`` module has been removed. Use ``google.cloud.gcp_compute_health_check``, ``google.cloud.gcp_compute_http_health_check`` or ``google.cloud.gcp_compute_https_health_check`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_target_proxy`` module has been removed. Use ``google.cloud.gcp_compute_target_http_proxy`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_url_map`` module has been removed. Use ``google.cloud.gcp_compute_url_map`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcspanner`` module has been removed. Use ``google.cloud.gcp_spanner_database`` and/or ``google.cloud.gcp_spanner_instance`` instead (https://github.com/ansible-collections/community.general/pull/1370).

View file

@ -101,45 +101,45 @@ plugin_routing:
removal_version: 2.0.0
warning_text: Use the modules from the theforeman.foreman collection instead.
gcdns_record:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_dns_resource_record_set instead.
gcdns_zone:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_dns_managed_zone instead.
gce:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_compute_instance instead.
gcp_backend_service:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_compute_backend_service instead.
gcp_forwarding_rule:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_compute_forwarding_rule or google.cloud.gcp_compute_global_forwarding_rule instead.
gcp_healthcheck:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_compute_health_check, google.cloud.gcp_compute_http_health_check or google.cloud.gcp_compute_https_health_check instead.
gcp_target_proxy:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_compute_target_http_proxy instead.
gcp_url_map:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_compute_url_map instead.
gcpubsub_facts:
deprecation:
removal_version: 3.0.0
warning_text: see plugin documentation for details
gcspanner:
deprecation:
tombstone:
removal_version: 2.0.0
warning_text: see plugin documentation for details
warning_text: Use google.cloud.gcp_spanner_database and/or google.cloud.gcp_spanner_instance instead.
github_hooks:
tombstone:
removal_version: 2.0.0

View file

@ -1,780 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 CallFire Inc.
# 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
################################################################################
DOCUMENTATION = '''
---
module: gcdns_record
short_description: Creates or removes resource records in Google Cloud DNS
description:
- Creates or removes resource records in Google Cloud DNS.
author: "William Albert (@walbert947)"
requirements:
- "python >= 2.6"
- "apache-libcloud >= 0.19.0"
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_dns_resource_record_set) instead.
options:
state:
type: str
description:
- Whether the given resource record should or should not be present.
choices: ["present", "absent"]
default: "present"
record:
type: str
description:
- The fully-qualified domain name of the resource record.
required: true
aliases: ['name']
zone:
type: str
description:
- The DNS domain name of the zone (e.g., example.com).
- One of either I(zone) or I(zone_id) must be specified as an
option, or the module will fail.
- If both I(zone) and I(zone_id) are specified, I(zone_id) will be
used.
zone_id:
type: str
description:
- The Google Cloud ID of the zone (e.g., example-com).
- One of either I(zone) or I(zone_id) must be specified as an
option, or the module will fail.
- These usually take the form of domain names with the dots replaced
with dashes. A zone ID will never have any dots in it.
- I(zone_id) can be faster than I(zone) in projects with a large
number of zones.
- If both I(zone) and I(zone_id) are specified, I(zone_id) will be
used.
type:
type: str
description:
- The type of resource record to add.
required: true
choices: [ 'A', 'AAAA', 'CNAME', 'SRV', 'TXT', 'SOA', 'NS', 'MX', 'SPF', 'PTR' ]
record_data:
type: list
description:
- The record_data to use for the resource record.
- I(record_data) must be specified if I(state) is C(present) or
I(overwrite) is C(True), or the module will fail.
- Valid record_data vary based on the record's I(type). In addition,
resource records that contain a DNS domain name in the value
field (e.g., CNAME, PTR, SRV, .etc) MUST include a trailing dot
in the value.
- Individual string record_data for TXT records must be enclosed in
double quotes.
- For resource records that have the same name but different
record_data (e.g., multiple A records), they must be defined as
multiple list entries in a single record.
required: false
aliases: ['value']
ttl:
type: int
description:
- The amount of time in seconds that a resource record will remain
cached by a caching resolver.
default: 300
overwrite:
description:
- Whether an attempt to overwrite an existing record should succeed
or fail. The behavior of this option depends on I(state).
- If I(state) is C(present) and I(overwrite) is C(True), this
module will replace an existing resource record of the same name
with the provided I(record_data). If I(state) is C(present) and
I(overwrite) is C(False), this module will fail if there is an
existing resource record with the same name and type, but
different resource data.
- If I(state) is C(absent) and I(overwrite) is C(True), this
module will remove the given resource record unconditionally.
If I(state) is C(absent) and I(overwrite) is C(False), this
module will fail if the provided record_data do not match exactly
with the existing resource record's record_data.
type: bool
default: 'no'
service_account_email:
type: str
description:
- The e-mail address for a service account with access to Google
Cloud DNS.
pem_file:
type: path
description:
- The path to the PEM file associated with the service account
email.
- This option is deprecated and may be removed in a future release.
Use I(credentials_file) instead.
credentials_file:
type: path
description:
- The path to the JSON file associated with the service account
email.
project_id:
type: str
description:
- The Google Cloud Platform project ID to use.
notes:
- See also M(community.general.gcdns_zone).
- This modules's underlying library does not support in-place updates for
DNS resource records. Instead, resource records are quickly deleted and
recreated.
- SOA records are technically supported, but their functionality is limited
to verifying that a zone's existing SOA record matches a pre-determined
value. The SOA record cannot be updated.
- Root NS records cannot be updated.
- NAPTR records are not supported.
'''
EXAMPLES = '''
- name: Create an A record
community.general.gcdns_record:
record: 'www1.example.com'
zone: 'example.com'
type: A
value: '1.2.3.4'
- name: Update an existing record
community.general.gcdns_record:
record: 'www1.example.com'
zone: 'example.com'
type: A
overwrite: true
value: '5.6.7.8'
- name: Remove an A record
community.general.gcdns_record:
record: 'www1.example.com'
zone_id: 'example-com'
state: absent
type: A
value: '5.6.7.8'
- name: Create a CNAME record. Note the trailing dot of value
community.general.gcdns_record:
record: 'www.example.com'
zone_id: 'example-com'
type: CNAME
value: 'www.example.com.'
- name: Create an MX record with a custom TTL. Note the trailing dot of value
community.general.gcdns_record:
record: 'example.com'
zone: 'example.com'
type: MX
ttl: 3600
value: '10 mail.example.com.'
- name: Create multiple A records with the same name
community.general.gcdns_record:
record: 'api.example.com'
zone_id: 'example-com'
type: A
record_data:
- '192.0.2.23'
- '10.4.5.6'
- '198.51.100.5'
- '203.0.113.10'
- name: Change the value of an existing record with multiple record_data
community.general.gcdns_record:
record: 'api.example.com'
zone: 'example.com'
type: A
overwrite: true
record_data: # WARNING: All values in a record will be replaced
- '192.0.2.23'
- '192.0.2.42' # The changed record
- '198.51.100.5'
- '203.0.113.10'
- name: Safely remove a multi-line record
community.general.gcdns_record:
record: 'api.example.com'
zone_id: 'example-com'
state: absent
type: A
record_data: # NOTE: All of the values must match exactly
- '192.0.2.23'
- '192.0.2.42'
- '198.51.100.5'
- '203.0.113.10'
- name: Unconditionally remove a record
community.general.gcdns_record:
record: 'api.example.com'
zone_id: 'example-com'
state: absent
overwrite: true # overwrite is true, so no values are needed
type: A
- name: Create an AAAA record
community.general.gcdns_record:
record: 'www1.example.com'
zone: 'example.com'
type: AAAA
value: 'fd00:db8::1'
- name: Create a PTR record
community.general.gcdns_record:
record: '10.5.168.192.in-addr.arpa'
zone: '5.168.192.in-addr.arpa'
type: PTR
value: 'api.example.com.' # Note the trailing dot.
- name: Create an NS record
community.general.gcdns_record:
record: 'subdomain.example.com'
zone: 'example.com'
type: NS
ttl: 21600
record_data:
- 'ns-cloud-d1.googledomains.com.' # Note the trailing dots on values
- 'ns-cloud-d2.googledomains.com.'
- 'ns-cloud-d3.googledomains.com.'
- 'ns-cloud-d4.googledomains.com.'
- name: Create a TXT record
community.general.gcdns_record:
record: 'example.com'
zone_id: 'example-com'
type: TXT
record_data:
- '"v=spf1 include:_spf.google.com -all"' # A single-string TXT value
- '"hello " "world"' # A multi-string TXT value
'''
RETURN = '''
overwrite:
description: Whether to the module was allowed to overwrite the record
returned: success
type: bool
sample: True
record:
description: Fully-qualified domain name of the resource record
returned: success
type: str
sample: mail.example.com.
state:
description: Whether the record is present or absent
returned: success
type: str
sample: present
ttl:
description: The time-to-live of the resource record
returned: success
type: int
sample: 300
type:
description: The type of the resource record
returned: success
type: str
sample: A
record_data:
description: The resource record values
returned: success
type: list
sample: ['5.6.7.8', '9.10.11.12']
zone:
description: The dns name of the zone
returned: success
type: str
sample: example.com.
zone_id:
description: The Google Cloud DNS ID of the zone
returned: success
type: str
sample: example-com
'''
################################################################################
# Imports
################################################################################
import socket
from distutils.version import LooseVersion
try:
from libcloud import __version__ as LIBCLOUD_VERSION
from libcloud.common.google import InvalidRequestError
from libcloud.common.types import LibcloudError
from libcloud.dns.types import Provider
from libcloud.dns.types import RecordDoesNotExistError
from libcloud.dns.types import ZoneDoesNotExistError
HAS_LIBCLOUD = True
# The libcloud Google Cloud DNS provider.
PROVIDER = Provider.GOOGLE
except ImportError:
HAS_LIBCLOUD = False
PROVIDER = None
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gcdns import gcdns_connect
################################################################################
# Constants
################################################################################
# Apache libcloud 0.19.0 was the first to contain the non-beta Google Cloud DNS
# v1 API. Earlier versions contained the beta v1 API, which has since been
# deprecated and decommissioned.
MINIMUM_LIBCLOUD_VERSION = '0.19.0'
# The records that libcloud's Google Cloud DNS provider supports.
#
# Libcloud has a RECORD_TYPE_MAP dictionary in the provider that also contains
# this information and is the authoritative source on which records are
# supported, but accessing the dictionary requires creating a Google Cloud DNS
# driver object, which is done in a helper module.
#
# I'm hard-coding the supported record types here, because they (hopefully!)
# shouldn't change much, and it allows me to use it as a "choices" parameter
# in an AnsibleModule argument_spec.
SUPPORTED_RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'SRV', 'TXT', 'SOA', 'NS', 'MX', 'SPF', 'PTR']
################################################################################
# Functions
################################################################################
def create_record(module, gcdns, zone, record):
"""Creates or overwrites a resource record."""
overwrite = module.boolean(module.params['overwrite'])
record_name = module.params['record']
record_type = module.params['type']
ttl = module.params['ttl']
record_data = module.params['record_data']
data = dict(ttl=ttl, rrdatas=record_data)
# Google Cloud DNS wants the trailing dot on all DNS names.
if record_name[-1] != '.':
record_name = record_name + '.'
# If we found a record, we need to check if the values match.
if record is not None:
# If the record matches, we obviously don't have to change anything.
if _records_match(record.data['ttl'], record.data['rrdatas'], ttl, record_data):
return False
# The record doesn't match, so we need to check if we can overwrite it.
if not overwrite:
module.fail_json(
msg='cannot overwrite existing record, overwrite protection enabled',
changed=False
)
# The record either doesn't exist, or it exists and we can overwrite it.
if record is None and not module.check_mode:
# There's no existing record, so we'll just create it.
try:
gcdns.create_record(record_name, zone, record_type, data)
except InvalidRequestError as error:
if error.code == 'invalid':
# The resource record name and type are valid by themselves, but
# not when combined (e.g., an 'A' record with "www.example.com"
# as its value).
module.fail_json(
msg='value is invalid for the given type: ' +
"%s, got value: %s" % (record_type, record_data),
changed=False
)
elif error.code == 'cnameResourceRecordSetConflict':
# We're attempting to create a CNAME resource record when we
# already have another type of resource record with the name
# domain name.
module.fail_json(
msg="non-CNAME resource record already exists: %s" % record_name,
changed=False
)
else:
# The error is something else that we don't know how to handle,
# so we'll just re-raise the exception.
raise
elif record is not None and not module.check_mode:
# The Google provider in libcloud doesn't support updating a record in
# place, so if the record already exists, we need to delete it and
# recreate it using the new information.
gcdns.delete_record(record)
try:
gcdns.create_record(record_name, zone, record_type, data)
except InvalidRequestError:
# Something blew up when creating the record. This will usually be a
# result of invalid value data in the new record. Unfortunately, we
# already changed the state of the record by deleting the old one,
# so we'll try to roll back before failing out.
try:
gcdns.create_record(record.name, record.zone, record.type, record.data)
module.fail_json(
msg='error updating record, the original record was restored',
changed=False
)
except LibcloudError:
# We deleted the old record, couldn't create the new record, and
# couldn't roll back. That really sucks. We'll dump the original
# record to the failure output so the user can restore it if
# necessary.
module.fail_json(
msg='error updating record, and could not restore original record, ' +
"original name: %s " % record.name +
"original zone: %s " % record.zone +
"original type: %s " % record.type +
"original data: %s" % record.data,
changed=True)
return True
def remove_record(module, gcdns, record):
"""Remove a resource record."""
overwrite = module.boolean(module.params['overwrite'])
ttl = module.params['ttl']
record_data = module.params['record_data']
# If there is no record, we're obviously done.
if record is None:
return False
# If there is an existing record, do our values match the values of the
# existing record?
if not overwrite:
if not _records_match(record.data['ttl'], record.data['rrdatas'], ttl, record_data):
module.fail_json(
msg='cannot delete due to non-matching ttl or record_data: ' +
"ttl: %d, record_data: %s " % (ttl, record_data) +
"original ttl: %d, original record_data: %s" % (record.data['ttl'], record.data['rrdatas']),
changed=False
)
# If we got to this point, we're okay to delete the record.
if not module.check_mode:
gcdns.delete_record(record)
return True
def _get_record(gcdns, zone, record_type, record_name):
"""Gets the record object for a given FQDN."""
# The record ID is a combination of its type and FQDN. For example, the
# ID of an A record for www.example.com would be 'A:www.example.com.'
record_id = "%s:%s" % (record_type, record_name)
try:
return gcdns.get_record(zone.id, record_id)
except RecordDoesNotExistError:
return None
def _get_zone(gcdns, zone_name, zone_id):
"""Gets the zone object for a given domain name."""
if zone_id is not None:
try:
return gcdns.get_zone(zone_id)
except ZoneDoesNotExistError:
return None
# To create a zone, we need to supply a domain name. However, to delete a
# zone, we need to supply a zone ID. Zone ID's are often based on domain
# names, but that's not guaranteed, so we'll iterate through the list of
# zones to see if we can find a matching domain name.
available_zones = gcdns.iterate_zones()
found_zone = None
for zone in available_zones:
if zone.domain == zone_name:
found_zone = zone
break
return found_zone
def _records_match(old_ttl, old_record_data, new_ttl, new_record_data):
"""Checks to see if original and new TTL and values match."""
matches = True
if old_ttl != new_ttl:
matches = False
if old_record_data != new_record_data:
matches = False
return matches
def _sanity_check(module):
"""Run sanity checks that don't depend on info from the zone/record."""
overwrite = module.params['overwrite']
record_name = module.params['record']
record_type = module.params['type']
state = module.params['state']
ttl = module.params['ttl']
record_data = module.params['record_data']
# Apache libcloud needs to be installed and at least the minimum version.
if not HAS_LIBCLOUD:
module.fail_json(
msg='This module requires Apache libcloud %s or greater' % MINIMUM_LIBCLOUD_VERSION,
changed=False
)
elif LooseVersion(LIBCLOUD_VERSION) < MINIMUM_LIBCLOUD_VERSION:
module.fail_json(
msg='This module requires Apache libcloud %s or greater' % MINIMUM_LIBCLOUD_VERSION,
changed=False
)
# A negative TTL is not permitted (how would they even work?!).
if ttl < 0:
module.fail_json(
msg='TTL cannot be less than zero, got: %d' % ttl,
changed=False
)
# Deleting SOA records is not permitted.
if record_type == 'SOA' and state == 'absent':
module.fail_json(msg='cannot delete SOA records', changed=False)
# Updating SOA records is not permitted.
if record_type == 'SOA' and state == 'present' and overwrite:
module.fail_json(msg='cannot update SOA records', changed=False)
# Some sanity checks depend on what value was supplied.
if record_data is not None and (state == 'present' or not overwrite):
# A records must contain valid IPv4 addresses.
if record_type == 'A':
for value in record_data:
try:
socket.inet_aton(value)
except socket.error:
module.fail_json(
msg='invalid A record value, got: %s' % value,
changed=False
)
# AAAA records must contain valid IPv6 addresses.
if record_type == 'AAAA':
for value in record_data:
try:
socket.inet_pton(socket.AF_INET6, value)
except socket.error:
module.fail_json(
msg='invalid AAAA record value, got: %s' % value,
changed=False
)
# CNAME and SOA records can't have multiple values.
if record_type in ['CNAME', 'SOA'] and len(record_data) > 1:
module.fail_json(
msg='CNAME or SOA records cannot have more than one value, ' +
"got: %s" % record_data,
changed=False
)
# Google Cloud DNS does not support wildcard NS records.
if record_type == 'NS' and record_name[0] == '*':
module.fail_json(
msg="wildcard NS records not allowed, got: %s" % record_name,
changed=False
)
# Values for txt records must begin and end with a double quote.
if record_type == 'TXT':
for value in record_data:
if value[0] != '"' and value[-1] != '"':
module.fail_json(
msg='TXT record_data must be enclosed in double quotes, ' +
'got: %s' % value,
changed=False
)
def _additional_sanity_checks(module, zone):
"""Run input sanity checks that depend on info from the zone/record."""
overwrite = module.params['overwrite']
record_name = module.params['record']
record_type = module.params['type']
state = module.params['state']
# CNAME records are not allowed to have the same name as the root domain.
if record_type == 'CNAME' and record_name == zone.domain:
module.fail_json(
msg='CNAME records cannot match the zone name',
changed=False
)
# The root domain must always have an NS record.
if record_type == 'NS' and record_name == zone.domain and state == 'absent':
module.fail_json(
msg='cannot delete root NS records',
changed=False
)
# Updating NS records with the name as the root domain is not allowed
# because libcloud does not support in-place updates and root domain NS
# records cannot be removed.
if record_type == 'NS' and record_name == zone.domain and overwrite:
module.fail_json(
msg='cannot update existing root NS records',
changed=False
)
# SOA records with names that don't match the root domain are not permitted
# (and wouldn't make sense anyway).
if record_type == 'SOA' and record_name != zone.domain:
module.fail_json(
msg='non-root SOA records are not permitted, got: %s' % record_name,
changed=False
)
################################################################################
# Main
################################################################################
def main():
"""Main function"""
module = AnsibleModule(
argument_spec=dict(
state=dict(default='present', choices=['present', 'absent'], type='str'),
record=dict(required=True, aliases=['name'], type='str'),
zone=dict(type='str'),
zone_id=dict(type='str'),
type=dict(required=True, choices=SUPPORTED_RECORD_TYPES, type='str'),
record_data=dict(aliases=['value'], type='list'),
ttl=dict(default=300, type='int'),
overwrite=dict(default=False, type='bool'),
service_account_email=dict(type='str'),
pem_file=dict(type='path'),
credentials_file=dict(type='path'),
project_id=dict(type='str')
),
required_if=[
('state', 'present', ['record_data']),
('overwrite', False, ['record_data'])
],
required_one_of=[['zone', 'zone_id']],
supports_check_mode=True
)
_sanity_check(module)
record_name = module.params['record']
record_type = module.params['type']
state = module.params['state']
ttl = module.params['ttl']
zone_name = module.params['zone']
zone_id = module.params['zone_id']
json_output = dict(
state=state,
record=record_name,
zone=zone_name,
zone_id=zone_id,
type=record_type,
record_data=module.params['record_data'],
ttl=ttl,
overwrite=module.boolean(module.params['overwrite'])
)
# Google Cloud DNS wants the trailing dot on all DNS names.
if zone_name is not None and zone_name[-1] != '.':
zone_name = zone_name + '.'
if record_name[-1] != '.':
record_name = record_name + '.'
# Build a connection object that we can use to connect with Google Cloud
# DNS.
gcdns = gcdns_connect(module, provider=PROVIDER)
# We need to check that the zone we're creating a record for actually
# exists.
zone = _get_zone(gcdns, zone_name, zone_id)
if zone is None and zone_name is not None:
module.fail_json(
msg='zone name was not found: %s' % zone_name,
changed=False
)
elif zone is None and zone_id is not None:
module.fail_json(
msg='zone id was not found: %s' % zone_id,
changed=False
)
# Populate the returns with the actual zone information.
json_output['zone'] = zone.domain
json_output['zone_id'] = zone.id
# We also need to check if the record we want to create or remove actually
# exists.
try:
record = _get_record(gcdns, zone, record_type, record_name)
except InvalidRequestError:
# We gave Google Cloud DNS an invalid DNS record name.
module.fail_json(
msg='record name is invalid: %s' % record_name,
changed=False
)
_additional_sanity_checks(module, zone)
diff = dict()
# Build the 'before' diff
if record is None:
diff['before'] = ''
diff['before_header'] = '<absent>'
else:
diff['before'] = dict(
record=record.data['name'],
type=record.data['type'],
record_data=record.data['rrdatas'],
ttl=record.data['ttl']
)
diff['before_header'] = "%s:%s" % (record_type, record_name)
# Create, remove, or modify the record.
if state == 'present':
diff['after'] = dict(
record=record_name,
type=record_type,
record_data=module.params['record_data'],
ttl=ttl
)
diff['after_header'] = "%s:%s" % (record_type, record_name)
changed = create_record(module, gcdns, zone, record)
elif state == 'absent':
diff['after'] = ''
diff['after_header'] = '<absent>'
changed = remove_record(module, gcdns, record)
module.exit_json(changed=changed, diff=diff, **json_output)
if __name__ == '__main__':
main()

View file

@ -1,372 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 CallFire Inc.
# 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
################################################################################
DOCUMENTATION = '''
---
module: gcdns_zone
short_description: Creates or removes zones in Google Cloud DNS
description:
- Creates or removes managed zones in Google Cloud DNS.
author: "William Albert (@walbert947)"
requirements:
- "apache-libcloud >= 0.19.0"
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_dns_managed_zone) instead.
options:
state:
type: str
description:
- Whether the given zone should or should not be present.
choices: ["present", "absent"]
default: "present"
zone:
type: str
description:
- The DNS domain name of the zone.
- This is NOT the Google Cloud DNS zone ID (e.g., example-com). If
you attempt to specify a zone ID, this module will attempt to
create a TLD and will fail.
required: true
aliases: ['name']
description:
type: str
description:
- An arbitrary text string to use for the zone description.
default: ""
service_account_email:
type: str
description:
- The e-mail address for a service account with access to Google
Cloud DNS.
pem_file:
type: path
description:
- The path to the PEM file associated with the service account
email.
- This option is deprecated and may be removed in a future release.
Use I(credentials_file) instead.
credentials_file:
type: path
description:
- The path to the JSON file associated with the service account
email.
project_id:
type: str
description:
- The Google Cloud Platform project ID to use.
notes:
- See also M(community.general.gcdns_record).
- Zones that are newly created must still be set up with a domain registrar
before they can be used.
'''
EXAMPLES = '''
# Basic zone creation example.
- name: Create a basic zone with the minimum number of parameters.
community.general.gcdns_zone: zone=example.com
# Zone removal example.
- name: Remove a zone.
community.general.gcdns_zone: zone=example.com state=absent
# Zone creation with description
- name: Creating a zone with a description
community.general.gcdns_zone: zone=example.com description="This is an awesome zone"
'''
RETURN = '''
description:
description: The zone's description
returned: success
type: str
sample: This is an awesome zone
state:
description: Whether the zone is present or absent
returned: success
type: str
sample: present
zone:
description: The zone's DNS name
returned: success
type: str
sample: example.com.
'''
################################################################################
# Imports
################################################################################
from distutils.version import LooseVersion
try:
from libcloud import __version__ as LIBCLOUD_VERSION
from libcloud.common.google import InvalidRequestError
from libcloud.common.google import ResourceExistsError
from libcloud.common.google import ResourceNotFoundError
from libcloud.dns.types import Provider
# The libcloud Google Cloud DNS provider.
PROVIDER = Provider.GOOGLE
HAS_LIBCLOUD = True
except ImportError:
HAS_LIBCLOUD = False
PROVIDER = None
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gcdns import gcdns_connect
################################################################################
# Constants
################################################################################
# Apache libcloud 0.19.0 was the first to contain the non-beta Google Cloud DNS
# v1 API. Earlier versions contained the beta v1 API, which has since been
# deprecated and decommissioned.
MINIMUM_LIBCLOUD_VERSION = '0.19.0'
# The URL used to verify ownership of a zone in Google Cloud DNS.
ZONE_VERIFICATION_URL = 'https://www.google.com/webmasters/verification/'
################################################################################
# Functions
################################################################################
def create_zone(module, gcdns, zone):
"""Creates a new Google Cloud DNS zone."""
description = module.params['description']
extra = dict(description=description)
zone_name = module.params['zone']
# Google Cloud DNS wants the trailing dot on the domain name.
if zone_name[-1] != '.':
zone_name = zone_name + '.'
# If we got a zone back, then the domain exists.
if zone is not None:
return False
# The zone doesn't exist yet.
try:
if not module.check_mode:
gcdns.create_zone(domain=zone_name, extra=extra)
return True
except ResourceExistsError:
# The zone already exists. We checked for this already, so either
# Google is lying, or someone was a ninja and created the zone
# within milliseconds of us checking for its existence. In any case,
# the zone has already been created, so we have nothing more to do.
return False
except InvalidRequestError as error:
if error.code == 'invalid':
# The zone name or a parameter might be completely invalid. This is
# typically caused by an illegal DNS name (e.g. foo..com).
module.fail_json(
msg="zone name is not a valid DNS name: %s" % zone_name,
changed=False
)
elif error.code == 'managedZoneDnsNameNotAvailable':
# Google Cloud DNS will refuse to create zones with certain domain
# names, such as TLDs, ccTLDs, or special domain names such as
# example.com.
module.fail_json(
msg="zone name is reserved or already in use: %s" % zone_name,
changed=False
)
elif error.code == 'verifyManagedZoneDnsNameOwnership':
# This domain name needs to be verified before Google will create
# it. This occurs when a user attempts to create a zone which shares
# a domain name with a zone hosted elsewhere in Google Cloud DNS.
module.fail_json(
msg="ownership of zone %s needs to be verified at %s" % (zone_name, ZONE_VERIFICATION_URL),
changed=False
)
else:
# The error is something else that we don't know how to handle,
# so we'll just re-raise the exception.
raise
def remove_zone(module, gcdns, zone):
"""Removes an existing Google Cloud DNS zone."""
# If there's no zone, then we're obviously done.
if zone is None:
return False
# An empty zone will have two resource records:
# 1. An NS record with a list of authoritative name servers
# 2. An SOA record
# If any additional resource records are present, Google Cloud DNS will
# refuse to remove the zone.
if len(zone.list_records()) > 2:
module.fail_json(
msg="zone is not empty and cannot be removed: %s" % zone.domain,
changed=False
)
try:
if not module.check_mode:
gcdns.delete_zone(zone)
return True
except ResourceNotFoundError:
# When we performed our check, the zone existed. It may have been
# deleted by something else. It's gone, so whatever.
return False
except InvalidRequestError as error:
if error.code == 'containerNotEmpty':
# When we performed our check, the zone existed and was empty. In
# the milliseconds between the check and the removal command,
# records were added to the zone.
module.fail_json(
msg="zone is not empty and cannot be removed: %s" % zone.domain,
changed=False
)
else:
# The error is something else that we don't know how to handle,
# so we'll just re-raise the exception.
raise
def _get_zone(gcdns, zone_name):
"""Gets the zone object for a given domain name."""
# To create a zone, we need to supply a zone name. However, to delete a
# zone, we need to supply a zone ID. Zone ID's are often based on zone
# names, but that's not guaranteed, so we'll iterate through the list of
# zones to see if we can find a matching name.
available_zones = gcdns.iterate_zones()
found_zone = None
for zone in available_zones:
if zone.domain == zone_name:
found_zone = zone
break
return found_zone
def _sanity_check(module):
"""Run module sanity checks."""
zone_name = module.params['zone']
# Apache libcloud needs to be installed and at least the minimum version.
if not HAS_LIBCLOUD:
module.fail_json(
msg='This module requires Apache libcloud %s or greater' % MINIMUM_LIBCLOUD_VERSION,
changed=False
)
elif LooseVersion(LIBCLOUD_VERSION) < MINIMUM_LIBCLOUD_VERSION:
module.fail_json(
msg='This module requires Apache libcloud %s or greater' % MINIMUM_LIBCLOUD_VERSION,
changed=False
)
# Google Cloud DNS does not support the creation of TLDs.
if '.' not in zone_name or len([label for label in zone_name.split('.') if label]) == 1:
module.fail_json(
msg='cannot create top-level domain: %s' % zone_name,
changed=False
)
################################################################################
# Main
################################################################################
def main():
"""Main function"""
module = AnsibleModule(
argument_spec=dict(
state=dict(default='present', choices=['present', 'absent'], type='str'),
zone=dict(required=True, aliases=['name'], type='str'),
description=dict(default='', type='str'),
service_account_email=dict(type='str'),
pem_file=dict(type='path'),
credentials_file=dict(type='path'),
project_id=dict(type='str')
),
supports_check_mode=True
)
_sanity_check(module)
zone_name = module.params['zone']
state = module.params['state']
# Google Cloud DNS wants the trailing dot on the domain name.
if zone_name[-1] != '.':
zone_name = zone_name + '.'
json_output = dict(
state=state,
zone=zone_name,
description=module.params['description']
)
# Build a connection object that was can use to connect with Google
# Cloud DNS.
gcdns = gcdns_connect(module, provider=PROVIDER)
# We need to check if the zone we're attempting to create already exists.
zone = _get_zone(gcdns, zone_name)
diff = dict()
# Build the 'before' diff
if zone is None:
diff['before'] = ''
diff['before_header'] = '<absent>'
else:
diff['before'] = dict(
zone=zone.domain,
description=zone.extra['description']
)
diff['before_header'] = zone_name
# Create or remove the zone.
if state == 'present':
diff['after'] = dict(
zone=zone_name,
description=module.params['description']
)
diff['after_header'] = zone_name
changed = create_zone(module, gcdns, zone)
elif state == 'absent':
diff['after'] = ''
diff['after_header'] = '<absent>'
changed = remove_zone(module, gcdns, zone)
module.exit_json(changed=changed, diff=diff, **json_output)
if __name__ == '__main__':
main()

View file

@ -1,753 +0,0 @@
#!/usr/bin/python
# Copyright 2013 Google Inc.
# 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
short_description: create or terminate GCE instances
description:
- Creates or terminates Google Compute Engine (GCE) instances. See
U(https://cloud.google.com/compute) for an overview.
Full install/configuration instructions for the gce* modules can
be found in the comments of ansible/test/gce_tests.py.
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_compute_instance) instead.
options:
image:
type: str
description:
- image string to use for the instance (default will follow latest
stable debian image)
default: "debian-8"
image_family:
type: str
description:
- image family from which to select the image. The most recent
non-deprecated image in the family will be used.
external_projects:
type: list
description:
- A list of other projects (accessible with the provisioning credentials)
to be searched for the image.
instance_names:
type: str
description:
- a comma-separated list of instance names to create or destroy
machine_type:
type: str
description:
- machine type to use for the instance, use 'n1-standard-1' by default
default: "n1-standard-1"
metadata:
type: str
description:
- a hash/dictionary of custom data for the instance;
'{"key":"value", ...}'
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).
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
project_id:
type: str
description:
- your GCE project ID
name:
type: str
description:
- either a name of a single instance or when used with 'num_instances',
the base name of a cluster of nodes
aliases: ['base_name']
num_instances:
type: int
description:
- can be used with 'name', specifies
the number of nodes to provision using 'name'
as a base name
network:
type: str
description:
- name of the network, 'default' will be used if not specified
default: "default"
subnetwork:
type: str
description:
- name of the subnetwork in which the instance should be created
persistent_boot_disk:
description:
- if set, create the instance with a persistent boot disk
type: bool
default: 'no'
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).
state:
type: str
description:
- desired state of the resource
default: "present"
choices: ["active", "present", "absent", "deleted", "started", "stopped", "terminated"]
tags:
type: list
description:
- a comma-separated list of tags to associate with the instance
zone:
type: str
description:
- the GCE zone to use. The list of available zones is at U(https://cloud.google.com/compute/docs/regions-zones/regions-zones#available).
default: "us-central1-a"
ip_forward:
description:
- set to C(yes) if the instance can forward ip packets (useful for
gateways)
type: bool
default: 'no'
external_ip:
type: str
description:
- type of external ip, ephemeral by default; alternatively, a fixed gce ip or ip name can be given. Specify 'none' if no external ip is desired.
default: "ephemeral"
disk_auto_delete:
description:
- if set boot disk will be removed after instance destruction
type: bool
default: 'yes'
preemptible:
description:
- if set to C(yes), instances will be preemptible and time-limited.
(requires libcloud >= 0.20.0)
type: bool
disk_size:
type: int
description:
- The size of the boot disk created for this instance (in GB)
default: 10
requirements:
- "python >= 2.6"
- "apache-libcloud >= 0.13.3, >= 0.17.0 if using JSON credentials,
>= 0.20.0 if using preemptible option"
notes:
- Either I(instance_names) or I(name) is required.
- JSON credentials strongly preferred.
author:
- Eric Johnson (@erjohnso) <erjohnso@google.com>
- Tom Melendez (@supertom) <supertom@google.com>
'''
EXAMPLES = '''
# Basic provisioning example. Create a single Debian 8 instance in the
# us-central1-a Zone of the n1-standard-1 machine type.
# Create multiple instances by specifying multiple names, separated by
# commas in the instance_names field
# (e.g. my-test-instance1,my-test-instance2)
- community.general.gce:
instance_names: my-test-instance1
zone: us-central1-a
machine_type: n1-standard-1
image: debian-8
state: present
service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com"
credentials_file: "/path/to/your-key.json"
project_id: "your-project-name"
disk_size: 32
# Create a single instance of an image from the "my-base-image" image family
# in the us-central1-a Zone of the n1-standard-1 machine type.
# This image family is in the "my-other-project" GCP project.
- community.general.gce:
instance_names: my-test-instance1
zone: us-central1-a
machine_type: n1-standard-1
image_family: my-base-image
external_projects:
- my-other-project
state: present
service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com"
credentials_file: "/path/to/your-key.json"
project_id: "your-project-name"
disk_size: 32
# Create a single Debian 8 instance in the us-central1-a Zone
# Use existing disks, custom network/subnetwork, set service account permissions
# add tags and metadata.
- community.general.gce:
instance_names: my-test-instance
zone: us-central1-a
machine_type: n1-standard-1
state: present
metadata: '{"db":"postgres", "group":"qa", "id":500}'
tags:
- http-server
- my-other-tag
disks:
- name: disk-2
mode: READ_WRITE
- name: disk-3
mode: READ_ONLY
disk_auto_delete: false
network: foobar-network
subnetwork: foobar-subnetwork-1
preemptible: true
ip_forward: true
service_account_permissions:
- storage-full
- taskqueue
- bigquery
- https://www.googleapis.com/auth/ndev.clouddns.readwrite
service_account_email: "your-sa@your-project-name.iam.gserviceaccount.com"
credentials_file: "/path/to/your-key.json"
project_id: "your-project-name"
---
# Example Playbook
- name: Compute Engine Instance 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 multiple instances
# Basic provisioning example. Create multiple Debian 8 instances in the
# us-central1-a Zone of n1-standard-1 machine type.
community.general.gce:
instance_names: test1,test2,test3
zone: us-central1-a
machine_type: n1-standard-1
image: debian-8
state: present
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
metadata : '{ "startup-script" : "apt-get update" }'
register: gce
- name: Save host data
ansible.builtin.add_host:
hostname: "{{ item.public_ip }}"
groupname: gce_instances_ips
with_items: "{{ gce.instance_data }}"
- name: Wait for SSH for instances
ansible.builtin.wait_for:
delay: 1
host: "{{ item.public_ip }}"
port: 22
state: started
timeout: 30
with_items: "{{ gce.instance_data }}"
- name: Configure Hosts
hosts: gce_instances_ips
become: yes
become_method: sudo
roles:
- my-role-one
- my-role-two
tags:
- config
- name: Delete test-instances
# Basic termination of instance.
community.general.gce:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
instance_names: "{{ gce.instance_names }}"
zone: us-central1-a
state: absent
tags:
- delete
'''
import socket
import logging
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, unexpected_error_msg
from ansible_collections.community.general.plugins.module_utils.gcp import get_valid_location
from ansible.module_utils.six.moves import reduce
def get_instance_info(inst):
"""Retrieves instance information from an instance object and returns it
as a dictionary.
"""
metadata = {}
if 'metadata' in inst.extra and 'items' in inst.extra['metadata']:
for md in inst.extra['metadata']['items']:
metadata[md['key']] = md['value']
try:
netname = inst.extra['networkInterfaces'][0]['network'].split('/')[-1]
except Exception:
netname = None
try:
subnetname = inst.extra['networkInterfaces'][0]['subnetwork'].split('/')[-1]
except Exception:
subnetname = 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 = []
if len(inst.public_ips) == 0:
public_ip = None
else:
public_ip = inst.public_ips[0]
return ({
'image': inst.image is not None and inst.image.split('/')[-1] or None,
'disks': disk_names,
'machine_type': inst.size,
'metadata': metadata,
'name': inst.name,
'network': netname,
'subnetwork': subnetname,
'private_ip': inst.private_ips[0],
'public_ip': public_ip,
'status': ('status' in inst.extra) and inst.extra['status'] or None,
'tags': ('tags' in inst.extra) and inst.extra['tags'] or [],
'zone': ('zone' in inst.extra) and inst.extra['zone'].name or None,
})
def create_instances(module, gce, instance_names, number, lc_zone):
"""Creates new instances. Attributes other than instance_names are picked
up from 'module'
module : AnsibleModule object
community.general.gce: authenticated GCE libcloud driver
instance_names: python list of instance names to create
number: number of instances to create
lc_zone: GCEZone object
Returns:
A list of dictionaries with instance information
about the instances that were launched.
"""
image = module.params.get('image')
image_family = module.params.get('image_family')
external_projects = module.params.get('external_projects')
machine_type = module.params.get('machine_type')
metadata = module.params.get('metadata')
network = module.params.get('network')
subnetwork = module.params.get('subnetwork')
persistent_boot_disk = module.params.get('persistent_boot_disk')
disks = module.params.get('disks')
tags = module.params.get('tags')
ip_forward = module.params.get('ip_forward')
external_ip = module.params.get('external_ip')
disk_auto_delete = module.params.get('disk_auto_delete')
preemptible = module.params.get('preemptible')
disk_size = module.params.get('disk_size')
service_account_permissions = module.params.get('service_account_permissions')
if external_ip == "none":
instance_external_ip = None
elif external_ip != "ephemeral":
instance_external_ip = external_ip
try:
# check if instance_external_ip is an ip or a name
try:
socket.inet_aton(instance_external_ip)
instance_external_ip = GCEAddress(id='unknown', name='unknown', address=instance_external_ip, region='unknown', driver=gce)
except socket.error:
instance_external_ip = gce.ex_get_address(instance_external_ip)
except GoogleBaseError as e:
module.fail_json(msg='Unexpected error attempting to get a static ip %s, error: %s' % (external_ip, e.value))
else:
instance_external_ip = external_ip
new_instances = []
changed = False
lc_disks = []
disk_modes = []
for i, disk in enumerate(disks or []):
if isinstance(disk, dict):
lc_disks.append(gce.ex_get_volume(disk['name'], lc_zone))
disk_modes.append(disk['mode'])
else:
lc_disks.append(gce.ex_get_volume(disk, lc_zone))
# 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)
# 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
lc_image = LazyDiskImage(module, gce, image, lc_disks, family=image_family, projects=external_projects)
ex_sa_perms = []
bad_perms = []
if service_account_permissions:
for perm in service_account_permissions:
if perm not in gce.SA_SCOPES_MAP and not perm.startswith('https://www.googleapis.com/auth'):
bad_perms.append(perm)
if len(bad_perms) > 0:
module.fail_json(msg='bad permissions: %s' % str(bad_perms))
ex_sa_perms.append({'email': "default"})
ex_sa_perms[0]['scopes'] = service_account_permissions
# These variables all have default values but check just in case
if not lc_network or not lc_machine_type or not lc_zone:
module.fail_json(msg='Missing required create instance variable',
changed=False)
gce_args = dict(
location=lc_zone,
ex_network=network, ex_tags=tags, ex_metadata=metadata,
ex_can_ip_forward=ip_forward,
external_ip=instance_external_ip, ex_disk_auto_delete=disk_auto_delete,
ex_service_accounts=ex_sa_perms
)
if preemptible is not None:
gce_args['ex_preemptible'] = preemptible
if subnetwork is not None:
gce_args['ex_subnetwork'] = subnetwork
if isinstance(instance_names, str) and not number:
instance_names = [instance_names]
if isinstance(instance_names, str) and number:
instance_responses = gce.ex_create_multiple_nodes(instance_names, lc_machine_type,
lc_image(), number, **gce_args)
for resp in instance_responses:
n = resp
if isinstance(resp, libcloud.compute.drivers.gce.GCEFailedNode):
try:
n = gce.ex_get_node(n.name, lc_zone)
except ResourceNotFoundError:
pass
else:
# Assure that at least one node has been created to set changed=True
changed = True
new_instances.append(n)
else:
for instance in instance_names:
pd = None
if lc_disks:
pd = lc_disks[0]
elif persistent_boot_disk:
try:
pd = gce.ex_get_volume("%s" % instance, lc_zone)
except ResourceNotFoundError:
pd = gce.create_volume(disk_size, "%s" % instance, image=lc_image())
gce_args['ex_boot_disk'] = pd
inst = None
try:
inst = gce.ex_get_node(instance, lc_zone)
except ResourceNotFoundError:
inst = gce.create_node(
instance, lc_machine_type, lc_image(), **gce_args
)
changed = True
except GoogleBaseError as e:
module.fail_json(msg='Unexpected error attempting to create ' +
'instance %s, error: %s' % (instance, e.value))
if inst:
new_instances.append(inst)
for inst in new_instances:
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})
instance_names = []
instance_json_data = []
for inst in new_instances:
d = get_instance_info(inst)
instance_names.append(d['name'])
instance_json_data.append(d)
return (changed, instance_json_data, instance_names)
def change_instance_state(module, gce, instance_names, number, zone, state):
"""Changes the state of a list of instances. For example,
change from started to stopped, or started to absent.
module: Ansible module object
community.general.gce: authenticated GCE connection object
instance_names: a list of instance names to terminate
zone: GCEZone object where the instances reside prior to termination
state: 'state' parameter passed into module as argument
Returns a dictionary of instance names that were changed.
"""
changed = False
nodes = []
state_instance_names = []
if isinstance(instance_names, str) and number:
node_names = ['%s-%03d' % (instance_names, i) for i in range(number)]
elif isinstance(instance_names, str) and not number:
node_names = [instance_names]
else:
node_names = instance_names
for name in node_names:
inst = None
try:
inst = gce.ex_get_node(name, zone)
except ResourceNotFoundError:
state_instance_names.append(name)
except Exception as e:
module.fail_json(msg=unexpected_error_msg(e), changed=False)
else:
nodes.append(inst)
state_instance_names.append(name)
if state in ['absent', 'deleted'] and number:
changed_nodes = gce.ex_destroy_multiple_nodes(nodes) or [False]
changed = reduce(lambda x, y: x or y, changed_nodes)
else:
for node in nodes:
if state in ['absent', 'deleted']:
gce.destroy_node(node)
changed = True
elif state == 'started' and node.state == libcloud.compute.types.NodeState.STOPPED:
gce.ex_start_node(node)
changed = True
elif state in ['stopped', 'terminated'] and node.state == libcloud.compute.types.NodeState.RUNNING:
gce.ex_stop_node(node)
changed = True
return (changed, state_instance_names)
def main():
module = AnsibleModule(
argument_spec=dict(
image=dict(default='debian-8'),
image_family=dict(),
external_projects=dict(type='list'),
instance_names=dict(),
machine_type=dict(default='n1-standard-1'),
metadata=dict(),
name=dict(aliases=['base_name']),
num_instances=dict(type='int'),
network=dict(default='default'),
subnetwork=dict(),
persistent_boot_disk=dict(type='bool', default=False),
disks=dict(type='list'),
state=dict(choices=['active', 'present', 'absent', 'deleted',
'started', 'stopped', 'terminated'],
default='present'),
tags=dict(type='list'),
zone=dict(default='us-central1-a'),
service_account_email=dict(),
service_account_permissions=dict(type='list'),
pem_file=dict(type='path'),
credentials_file=dict(type='path'),
project_id=dict(),
ip_forward=dict(type='bool', default=False),
external_ip=dict(default='ephemeral'),
disk_auto_delete=dict(type='bool', default=True),
disk_size=dict(type='int', default=10),
preemptible=dict(type='bool', default=None),
),
mutually_exclusive=[('instance_names', 'name')]
)
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')
gce = gce_connect(module)
image = module.params.get('image')
image_family = module.params.get('image_family')
external_projects = module.params.get('external_projects')
instance_names = module.params.get('instance_names')
name = module.params.get('name')
number = module.params.get('num_instances')
subnetwork = module.params.get('subnetwork')
state = module.params.get('state')
zone = module.params.get('zone')
preemptible = module.params.get('preemptible')
changed = False
inames = None
if isinstance(instance_names, list):
inames = instance_names
elif isinstance(instance_names, str):
inames = instance_names.split(',')
if name:
inames = name
if not inames:
module.fail_json(msg='Must specify a "name" or "instance_names"',
changed=False)
if not zone:
module.fail_json(msg='Must specify a "zone"', changed=False)
lc_zone = get_valid_location(module, gce, zone)
if preemptible is not None and hasattr(libcloud, '__version__') and libcloud.__version__ < '0.20':
module.fail_json(msg="Apache Libcloud 0.20.0+ is required to use 'preemptible' option",
changed=False)
if subnetwork is not None and not hasattr(gce, 'ex_get_subnetwork'):
module.fail_json(msg="Apache Libcloud 1.0.0+ is required to use 'subnetwork' option",
changed=False)
json_output = {'zone': zone}
if state in ['absent', 'deleted', 'started', 'stopped', 'terminated']:
json_output['state'] = state
(changed, state_instance_names) = change_instance_state(
module, gce, inames, number, lc_zone, state)
# based on what user specified, return the same variable, although
# value could be different if an instance could not be destroyed
if instance_names or name and number:
json_output['instance_names'] = state_instance_names
elif name:
json_output['name'] = name
elif state in ['active', 'present']:
json_output['state'] = 'present'
(changed, instance_data, instance_name_list) = create_instances(
module, gce, inames, number, lc_zone)
json_output['instance_data'] = instance_data
if instance_names:
json_output['instance_names'] = instance_name_list
elif name:
json_output['name'] = name
json_output['changed'] = changed
module.exit_json(**json_output)
class LazyDiskImage:
"""
Object for lazy instantiation of disk image
gce.ex_get_image is a very expensive call, so we want to avoid calling it as much as possible.
"""
def __init__(self, module, gce, name, has_pd, family=None, projects=None):
self.image = None
self.was_called = False
self.gce = gce
self.name = name
self.has_pd = has_pd
self.module = module
self.family = family
self.projects = projects
def __call__(self):
if not self.was_called:
self.was_called = True
if not self.has_pd:
if self.family:
self.image = self.gce.ex_get_image_from_family(self.family, ex_project_list=self.projects)
else:
self.image = self.gce.ex_get_image(self.name, ex_project_list=self.projects)
if not self.image:
self.module.fail_json(msg='image or disks missing for create instance', changed=False)
return self.image
if __name__ == '__main__':
main()

View file

@ -1,420 +0,0 @@
#!/usr/bin/python
# Copyright 2017 Google Inc.
# 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: gcp_backend_service
short_description: Create or Destroy a Backend Service.
description:
- Create or Destroy a Backend Service. See
U(https://cloud.google.com/compute/docs/load-balancing/http/backend-service) for an overview.
Full install/configuration instructions for the Google Cloud modules can
be found in the comments of ansible/test/gce_tests.py.
requirements:
- "python >= 2.6"
- "apache-libcloud >= 1.3.0"
notes:
- Update is not currently supported.
- Only global backend services are currently supported. Regional backends not currently supported.
- Internal load balancing not currently supported.
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_compute_backend_service) instead.
author:
- "Tom Melendez (@supertom) <tom@supertom.com>"
options:
backend_service_name:
type: str
description:
- Name of the Backend Service.
required: true
backends:
type: list
description:
- List of backends that make up the backend service. A backend is made up of
an instance group and optionally several other parameters. See
U(https://cloud.google.com/compute/docs/reference/latest/backendServices)
for details.
required: true
healthchecks:
type: list
description:
- List of healthchecks. Only one healthcheck is supported.
required: true
enable_cdn:
description:
- If true, enable Cloud CDN for this Backend Service.
type: bool
port_name:
type: str
description:
- Name of the port on the managed instance group (MIG) that backend
services can forward data to. Required for external load balancing.
protocol:
type: str
description:
- The protocol this Backend Service uses to communicate with backends.
Possible values are HTTP, HTTPS, TCP, and SSL. The default is TCP.
choices: [HTTP, HTTPS, TCP, SSL]
default: TCP
required: false
timeout:
type: int
description:
- How many seconds to wait for the backend before considering it a failed
request. Default is 30 seconds. Valid range is 1-86400.
required: false
service_account_email:
type: str
description:
- Service account email
service_account_permissions:
type: list
description:
- service account permissions
credentials_file:
type: str
description:
- Path to the JSON file associated with the service account email.
pem_file:
type: str
description:
- Path to the PEM file associated with the service account email.
project_id:
type: str
description:
- GCE project ID.
state:
type: str
description:
- Desired state of the resource
default: "present"
choices: ["absent", "present"]
'''
EXAMPLES = '''
- name: Create Minimum Backend Service
community.general.gcp_backend_service:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
backend_service_name: "{{ bes }}"
backends:
- instance_group: managed_instance_group_1
healthchecks:
- healthcheck_name_for_backend_service
port_name: myhttpport
state: present
- name: Create BES with extended backend parameters
community.general.gcp_backend_service:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
backend_service_name: "{{ bes }}"
backends:
- instance_group: managed_instance_group_1
max_utilization: 0.6
max_rate: 10
- instance_group: managed_instance_group_2
max_utilization: 0.5
max_rate: 4
healthchecks:
- healthcheck_name_for_backend_service
port_name: myhttpport
state: present
timeout: 60
'''
RETURN = '''
backend_service_created:
description: Indicator Backend Service was created.
returned: When a Backend Service is created.
type: bool
sample: "True"
backend_service_deleted:
description: Indicator Backend Service was deleted.
returned: When a Backend Service is deleted.
type: bool
sample: "True"
backend_service_name:
description: Name of the Backend Service.
returned: Always.
type: str
sample: "my-backend-service"
backends:
description: List of backends (comprised of instance_group) that
make up a Backend Service.
returned: When a Backend Service exists.
type: list
sample: "[ { 'instance_group': 'mig_one', 'zone': 'us-central1-b'} ]"
enable_cdn:
description: If Cloud CDN is enabled. null if not set.
returned: When a backend service exists.
type: bool
sample: "True"
healthchecks:
description: List of healthchecks applied to the Backend Service.
returned: When a Backend Service exists.
type: list
sample: "[ 'my-healthcheck' ]"
protocol:
description: Protocol used to communicate with the Backends.
returned: When a Backend Service exists.
type: str
sample: "HTTP"
port_name:
description: Name of Backend Port.
returned: When a Backend Service exists.
type: str
sample: "myhttpport"
timeout:
description: In seconds, how long before a request sent to a backend is
considered failed.
returned: If specified.
type: int
sample: "myhttpport"
'''
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_collections.community.general.plugins.module_utils.gcp import check_params
def _validate_params(params):
"""
Validate backend_service params.
This function calls _validate_backend_params to verify
the backend-specific parameters.
:param params: Ansible dictionary containing configuration.
:type params: ``dict``
:return: True or raises ValueError
:rtype: ``bool`` or `class:ValueError`
"""
fields = [
{'name': 'timeout', 'type': int, 'min': 1, 'max': 86400},
]
try:
check_params(params, fields)
_validate_backend_params(params['backends'])
except Exception:
raise
return (True, '')
def _validate_backend_params(backends):
"""
Validate configuration for backends.
:param backends: Ansible dictionary containing backends configuration (only).
:type backends: ``dict``
:return: True or raises ValueError
:rtype: ``bool`` or `class:ValueError`
"""
fields = [
{'name': 'balancing_mode', 'type': str, 'values': ['UTILIZATION', 'RATE', 'CONNECTION']},
{'name': 'max_utilization', 'type': float},
{'name': 'max_connections', 'type': int},
{'name': 'max_rate', 'type': int},
{'name': 'max_rate_per_instance', 'type': float},
]
if not backends:
raise ValueError('backends should be a list.')
for backend in backends:
try:
check_params(backend, fields)
except Exception:
raise
if 'max_rate' in backend and 'max_rate_per_instance' in backend:
raise ValueError('Both maxRate or maxRatePerInstance cannot be set.')
return (True, '')
def get_backend_service(gce, name):
"""
Get a Backend Service from GCE.
:param gce: An initialized GCE driver object.
:type gce: :class: `GCENodeDriver`
:param name: Name of the Backend Service.
:type name: ``str``
:return: A GCEBackendService object or None.
:rtype: :class: `GCEBackendService` or None
"""
try:
# Does the Backend Service already exist?
return gce.ex_get_backendservice(name=name)
except ResourceNotFoundError:
return None
def get_healthcheck(gce, name):
return gce.ex_get_healthcheck(name)
def get_instancegroup(gce, name, zone=None):
return gce.ex_get_instancegroup(name=name, zone=zone)
def create_backend_service(gce, params):
"""
Create a new Backend Service.
:param gce: An initialized GCE driver object.
:type gce: :class: `GCENodeDriver`
:param params: Dictionary of parameters needed by the module.
:type params: ``dict``
:return: Tuple with changed stats
:rtype: tuple in the format of (bool, bool)
"""
from copy import deepcopy
changed = False
return_data = False
# only one healthcheck is currently supported
hc_name = params['healthchecks'][0]
hc = get_healthcheck(gce, hc_name)
backends = []
for backend in params['backends']:
ig = get_instancegroup(gce, backend['instance_group'],
backend.get('zone', None))
kwargs = deepcopy(backend)
kwargs['instance_group'] = ig
backends.append(gce.ex_create_backend(
**kwargs))
bes = gce.ex_create_backendservice(
name=params['backend_service_name'], healthchecks=[hc], backends=backends,
enable_cdn=params['enable_cdn'], port_name=params['port_name'],
timeout_sec=params['timeout'], protocol=params['protocol'])
if bes:
changed = True
return_data = True
return (changed, return_data)
def delete_backend_service(bes):
"""
Delete a Backend Service. The Instance Groups are NOT destroyed.
"""
changed = False
return_data = False
if bes.destroy():
changed = True
return_data = True
return (changed, return_data)
def main():
module = AnsibleModule(argument_spec=dict(
backends=dict(type='list', required=True),
backend_service_name=dict(required=True),
healthchecks=dict(type='list', required=True),
service_account_email=dict(),
service_account_permissions=dict(type='list'),
enable_cdn=dict(type='bool'),
port_name=dict(type='str'),
protocol=dict(type='str', default='TCP',
choices=['HTTP', 'HTTPS', 'SSL', 'TCP']),
timeout=dict(type='int'),
state=dict(choices=['absent', 'present'], default='present'),
pem_file=dict(),
credentials_file=dict(),
project_id=dict(), ), )
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 Backend Service support (1.3+) required for this module.')
gce = gce_connect(module)
if not hasattr(gce, 'ex_create_instancegroupmanager'):
module.fail_json(
msg='libcloud with GCE Backend Service support (1.3+) required for this module.',
changed=False)
params = {}
params['state'] = module.params.get('state')
params['backend_service_name'] = module.params.get('backend_service_name')
params['backends'] = module.params.get('backends')
params['healthchecks'] = module.params.get('healthchecks')
params['enable_cdn'] = module.params.get('enable_cdn', None)
params['port_name'] = module.params.get('port_name', None)
params['protocol'] = module.params.get('protocol', None)
params['timeout'] = module.params.get('timeout', None)
try:
_validate_params(params)
except Exception as e:
module.fail_json(msg=e.message, changed=False)
changed = False
json_output = {'state': params['state']}
bes = get_backend_service(gce, params['backend_service_name'])
if not bes:
if params['state'] == 'absent':
# Doesn't exist and state==absent.
changed = False
module.fail_json(
msg="Cannot delete unknown backend service: %s" %
(params['backend_service_name']))
else:
# Create
(changed, json_output['backend_service_created']) = create_backend_service(gce,
params)
elif params['state'] == 'absent':
# Delete
(changed, json_output['backend_service_deleted']) = delete_backend_service(bes)
else:
# TODO(supertom): Add update support when it is available in libcloud.
changed = False
json_output['changed'] = changed
json_output.update(params)
module.exit_json(**json_output)
if __name__ == '__main__':
main()

View file

@ -1,385 +0,0 @@
#!/usr/bin/python
# Copyright 2017 Google Inc.
# 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: gcp_forwarding_rule
short_description: Create, Update or Destroy a Forwarding_Rule.
description:
- Create, Update or Destroy a Forwarding_Rule. See
U(https://cloud.google.com/compute/docs/load-balancing/http/target-proxies) for an overview.
More details on the Global Forwarding_Rule API can be found at
U(https://cloud.google.com/compute/docs/reference/latest/globalForwardingRules)
More details on the Forwarding Rules API can be found at
U(https://cloud.google.com/compute/docs/reference/latest/forwardingRules)
requirements:
- "python >= 2.6"
- "google-api-python-client >= 1.6.2"
- "google-auth >= 0.9.0"
- "google-auth-httplib2 >= 0.0.2"
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_compute_forwarding_rule) or M(google.cloud.gcp_compute_global_forwarding_rule) instead.
notes:
- Currently only supports global forwarding rules.
As such, Load Balancing Scheme is always EXTERNAL.
author:
- "Tom Melendez (@supertom) <tom@supertom.com>"
options:
address:
type: str
description:
- IPv4 or named IP address. Must be of the same scope (regional, global).
Reserved addresses can (and probably should) be used for global
forwarding rules. You may reserve IPs from the console or
via the gce_eip module.
required: false
forwarding_rule_name:
type: str
description:
- Name of the Forwarding_Rule.
required: true
port_range:
type: str
description:
- For global forwarding rules, must be set to 80 or 8080 for TargetHttpProxy, and
443 for TargetHttpsProxy or TargetSslProxy.
required: false
protocol:
type: str
description:
- For global forwarding rules, TCP, UDP, ESP, AH, SCTP or ICMP. Default is TCP.
required: false
choices: [TCP]
default: TCP
region:
type: str
description:
- The region for this forwarding rule. Currently, only 'global' is supported.
required: true
state:
type: str
description:
- The state of the Forwarding Rule. 'present' or 'absent'
required: true
choices: ["present", "absent"]
target:
type: str
description:
- Target resource for forwarding rule. For global proxy, this is a Global
TargetProxy resource. Required for external load balancing (including Global load balancing)
required: false
project_id:
type: str
description:
- The Google Cloud Platform project ID to use.
pem_file:
type: str
description:
- The path to the PEM file associated with the service account email.
- This option is deprecated and may be removed in a future release. Use I(credentials_file) instead.
credentials_file:
type: str
description:
- The path to the JSON file associated with the service account email.
service_account_email:
type: str
description:
- service account email
service_account_permissions:
type: list
description:
- service account permissions
load_balancing_scheme:
type: str
choices: [EXTERNAL]
default: EXTERNAL
description:
- Load balancing scheme. At the moment the only choice is EXTERNAL.
'''
EXAMPLES = '''
- name: Create Minimum GLOBAL Forwarding_Rule
community.general.gcp_forwarding_rule:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
forwarding_rule_name: my-forwarding_rule
protocol: TCP
port_range: 80
region: global
target: my-target-proxy
state: present
- name: Create Forwarding_Rule w/reserved static address
community.general.gcp_forwarding_rule:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
forwarding_rule_name: my-forwarding_rule
protocol: TCP
port_range: 80
address: my-reserved-static-address-name
region: global
target: my-target-proxy
state: present
'''
RETURN = '''
forwarding_rule_name:
description: Name of the Forwarding_Rule
returned: Always
type: str
sample: my-target-proxy
forwarding_rule:
description: GCP Forwarding_Rule dictionary
returned: Always. Refer to GCP documentation for detailed field descriptions.
type: dict
sample: { "name": "my-forwarding_rule", "target": "..." }
region:
description: Region for Forwarding Rule.
returned: Always
type: bool
sample: true
state:
description: state of the Forwarding_Rule
returned: Always.
type: str
sample: present
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gcp import get_google_api_client, GCPUtils
USER_AGENT_PRODUCT = 'ansible-forwarding_rule'
USER_AGENT_VERSION = '0.0.1'
def _build_global_forwarding_rule_dict(params, project_id=None):
"""
Reformat services in Ansible Params.
:param params: Params from AnsibleModule object
:type params: ``dict``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: dictionary suitable for submission to GCP API.
:rtype ``dict``
"""
url = ''
if project_id:
url = GCPUtils.build_googleapi_url(project_id)
gcp_dict = GCPUtils.params_to_gcp_dict(params, 'forwarding_rule_name')
if 'target' in gcp_dict:
gcp_dict['target'] = '%s/global/targetHttpProxies/%s' % (url,
gcp_dict['target'])
if 'address' in gcp_dict:
gcp_dict['IPAddress'] = '%s/global/addresses/%s' % (url,
gcp_dict['address'])
del gcp_dict['address']
if 'protocol' in gcp_dict:
gcp_dict['IPProtocol'] = gcp_dict['protocol']
del gcp_dict['protocol']
return gcp_dict
def get_global_forwarding_rule(client, name, project_id=None):
"""
Get a Global Forwarding Rule from GCP.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Global Forwarding Rule.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: A dict resp from the respective GCP 'get' request.
:rtype: ``dict``
"""
try:
req = client.globalForwardingRules().get(
project=project_id, forwardingRule=name)
return GCPUtils.execute_api_client_req(req, raise_404=False)
except Exception:
raise
def create_global_forwarding_rule(client, params, project_id):
"""
Create a new Global Forwarding Rule.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_global_forwarding_rule_dict(params, project_id)
try:
req = client.globalForwardingRules().insert(project=project_id, body=gcp_dict)
return_data = GCPUtils.execute_api_client_req(req, client, raw=False)
if not return_data:
return_data = get_global_forwarding_rule(client,
name=params['forwarding_rule_name'],
project_id=project_id)
return (True, return_data)
except Exception:
raise
def delete_global_forwarding_rule(client, name, project_id):
"""
Delete a Global Forwarding Rule.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Target Proxy.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
try:
req = client.globalForwardingRules().delete(
project=project_id, forwardingRule=name)
return_data = GCPUtils.execute_api_client_req(req, client)
return (True, return_data)
except Exception:
raise
def update_global_forwarding_rule(client, forwarding_rule, params, name, project_id):
"""
Update a Global Forwarding_Rule. Currently, only a target can be updated.
If the forwarding_rule has not changed, the update will not occur.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param forwarding_rule: Name of the Target Proxy.
:type forwarding_rule: ``dict``
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:param name: Name of the Global Forwarding Rule.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_global_forwarding_rule_dict(params, project_id)
GCPUtils.are_params_equal(forwarding_rule, gcp_dict)
if forwarding_rule['target'] == gcp_dict['target']:
return (False, 'no update necessary')
try:
req = client.globalForwardingRules().setTarget(project=project_id,
forwardingRule=name,
body={'target': gcp_dict['target']})
return_data = GCPUtils.execute_api_client_req(
req, client=client, raw=False)
return (True, return_data)
except Exception:
raise
def main():
module = AnsibleModule(argument_spec=dict(
forwarding_rule_name=dict(required=True),
region=dict(required=True),
target=dict(required=False),
address=dict(type='str', required=False),
protocol=dict(required=False, default='TCP', choices=['TCP']),
port_range=dict(required=False),
load_balancing_scheme=dict(
required=False, default='EXTERNAL', choices=['EXTERNAL']),
state=dict(required=True, choices=['absent', 'present']),
service_account_email=dict(),
service_account_permissions=dict(type='list'),
pem_file=dict(),
credentials_file=dict(),
project_id=dict(), ), )
client, conn_params = get_google_api_client(module, 'compute', user_agent_product=USER_AGENT_PRODUCT,
user_agent_version=USER_AGENT_VERSION)
params = {}
params['state'] = module.params.get('state')
params['forwarding_rule_name'] = module.params.get('forwarding_rule_name')
params['region'] = module.params.get('region')
params['target'] = module.params.get('target', None)
params['protocol'] = module.params.get('protocol', None)
params['port_range'] = module.params.get('port_range')
if module.params.get('address', None):
params['address'] = module.params.get('address', None)
if params['region'] != 'global':
# This module currently doesn't support regional rules.
module.fail_json(
msg=("%s - Only global forwarding rules currently supported. "
"Be sure to specify 'global' for the region option.") %
(params['forwarding_rule_name']))
changed = False
json_output = {'state': params['state']}
forwarding_rule = None
if params['region'] == 'global':
forwarding_rule = get_global_forwarding_rule(client,
name=params['forwarding_rule_name'],
project_id=conn_params['project_id'])
if not forwarding_rule:
if params['state'] == 'absent':
# Doesn't exist in GCE, and state==absent.
changed = False
module.fail_json(
msg="Cannot delete unknown forwarding_rule: %s" %
(params['forwarding_rule_name']))
else:
# Create
changed, json_output['forwarding_rule'] = create_global_forwarding_rule(client,
params=params,
project_id=conn_params['project_id'])
elif params['state'] == 'absent':
# Delete
changed, json_output['forwarding_rule'] = delete_global_forwarding_rule(client,
name=params['forwarding_rule_name'],
project_id=conn_params['project_id'])
else:
changed, json_output['forwarding_rule'] = update_global_forwarding_rule(client,
forwarding_rule=forwarding_rule,
params=params,
name=params['forwarding_rule_name'],
project_id=conn_params['project_id'])
json_output['changed'] = changed
json_output.update(params)
module.exit_json(**json_output)
if __name__ == '__main__':
main()

View file

@ -1,457 +0,0 @@
#!/usr/bin/python
# Copyright 2017 Google Inc.
# 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: gcp_healthcheck
short_description: Create, Update or Destroy a Healthcheck.
description:
- Create, Update or Destroy a Healthcheck. Currently only HTTP and
HTTPS Healthchecks are supported. Healthchecks are used to monitor
individual instances, managed instance groups and/or backend
services. Healtchecks are reusable.
- Visit
U(https://cloud.google.com/compute/docs/load-balancing/health-checks)
for an overview of Healthchecks on GCP.
- See
U(https://cloud.google.com/compute/docs/reference/latest/httpHealthChecks) for
API details on HTTP Healthchecks.
- See
U(https://cloud.google.com/compute/docs/reference/latest/httpsHealthChecks)
for more details on the HTTPS Healtcheck API.
requirements:
- "python >= 2.6"
- "google-api-python-client >= 1.6.2"
- "google-auth >= 0.9.0"
- "google-auth-httplib2 >= 0.0.2"
notes:
- Only supports HTTP and HTTPS Healthchecks currently.
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: >
Use M(google.cloud.gcp_compute_health_check), M(google.cloud.gcp_compute_http_health_check) or
M(google.cloud.gcp_compute_https_health_check) instead.
author:
- "Tom Melendez (@supertom) <tom@supertom.com>"
options:
check_interval:
type: int
description:
- How often (in seconds) to send a health check.
default: 5
healthcheck_name:
type: str
description:
- Name of the Healthcheck.
required: true
healthcheck_type:
type: str
description:
- Type of Healthcheck.
required: true
choices: ["HTTP", "HTTPS"]
host_header:
type: str
description:
- The value of the host header in the health check request. If left
empty, the public IP on behalf of which this health
check is performed will be used.
default: ""
port:
type: int
description:
- The TCP port number for the health check request. The default value is
443 for HTTPS and 80 for HTTP.
request_path:
type: str
description:
- The request path of the HTTPS health check request.
required: false
default: "/"
state:
type: str
description: State of the Healthcheck.
choices: ["present", "absent"]
default: present
timeout:
type: int
description:
- How long (in seconds) to wait for a response before claiming
failure. It is invalid for timeout
to have a greater value than check_interval.
default: 5
unhealthy_threshold:
type: int
description:
- A so-far healthy instance will be marked unhealthy after this
many consecutive failures.
default: 2
healthy_threshold:
type: int
description:
- A so-far unhealthy instance will be marked healthy after this
many consecutive successes.
default: 2
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).
credentials_file:
type: str
description:
- Path to the JSON file associated with the service account email
project_id:
type: str
description:
- Your GCP project ID
'''
EXAMPLES = '''
- name: Create Minimum HealthCheck
community.general.gcp_healthcheck:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
healthcheck_name: my-healthcheck
healthcheck_type: HTTP
state: present
- name: Create HTTP HealthCheck
community.general.gcp_healthcheck:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
healthcheck_name: my-healthcheck
healthcheck_type: HTTP
host: my-host
request_path: /hc
check_interval: 10
timeout: 30
unhealthy_threshhold: 2
healthy_threshhold: 1
state: present
- name: Create HTTPS HealthCheck
community.general.gcp_healthcheck:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
healthcheck_name: "{{ https_healthcheck }}"
healthcheck_type: HTTPS
host_header: my-host
request_path: /hc
check_interval: 5
timeout: 5
unhealthy_threshold: 2
healthy_threshold: 1
state: present
'''
RETURN = '''
state:
description: state of the Healthcheck
returned: Always.
type: str
sample: present
healthcheck_name:
description: Name of the Healthcheck
returned: Always
type: str
sample: my-url-map
healthcheck_type:
description: Type of the Healthcheck
returned: Always
type: str
sample: HTTP
healthcheck:
description: GCP Healthcheck dictionary
returned: Always. Refer to GCP documentation for detailed field descriptions.
type: dict
sample: { "name": "my-hc", "port": 443, "requestPath": "/foo" }
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gcp import get_google_api_client, GCPUtils
USER_AGENT_PRODUCT = 'ansible-healthcheck'
USER_AGENT_VERSION = '0.0.1'
def _validate_healthcheck_params(params):
"""
Validate healthcheck params.
Simple validation has already assumed by AnsibleModule.
:param params: Ansible dictionary containing configuration.
:type params: ``dict``
:return: True or raises ValueError
:rtype: ``bool`` or `class:ValueError`
"""
if params['timeout'] > params['check_interval']:
raise ValueError("timeout (%s) is greater than check_interval (%s)" % (
params['timeout'], params['check_interval']))
return (True, '')
def _build_healthcheck_dict(params):
"""
Reformat services in Ansible Params for GCP.
:param params: Params from AnsibleModule object
:type params: ``dict``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: dictionary suitable for submission to GCP
HealthCheck (HTTP/HTTPS) API.
:rtype ``dict``
"""
gcp_dict = GCPUtils.params_to_gcp_dict(params, 'healthcheck_name')
if 'timeout' in gcp_dict:
gcp_dict['timeoutSec'] = gcp_dict['timeout']
del gcp_dict['timeout']
if 'checkInterval' in gcp_dict:
gcp_dict['checkIntervalSec'] = gcp_dict['checkInterval']
del gcp_dict['checkInterval']
if 'hostHeader' in gcp_dict:
gcp_dict['host'] = gcp_dict['hostHeader']
del gcp_dict['hostHeader']
if 'healthcheckType' in gcp_dict:
del gcp_dict['healthcheckType']
return gcp_dict
def _get_req_resource(client, resource_type):
if resource_type == 'HTTPS':
return (client.httpsHealthChecks(), 'httpsHealthCheck')
else:
return (client.httpHealthChecks(), 'httpHealthCheck')
def get_healthcheck(client, name, project_id=None, resource_type='HTTP'):
"""
Get a Healthcheck from GCP.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Url Map.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: A dict resp from the respective GCP 'get' request.
:rtype: ``dict``
"""
try:
resource, entity_name = _get_req_resource(client, resource_type)
args = {'project': project_id, entity_name: name}
req = resource.get(**args)
return GCPUtils.execute_api_client_req(req, raise_404=False)
except Exception:
raise
def create_healthcheck(client, params, project_id, resource_type='HTTP'):
"""
Create a new Healthcheck.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_healthcheck_dict(params)
try:
resource, _ = _get_req_resource(client, resource_type)
args = {'project': project_id, 'body': gcp_dict}
req = resource.insert(**args)
return_data = GCPUtils.execute_api_client_req(req, client, raw=False)
if not return_data:
return_data = get_healthcheck(client,
name=params['healthcheck_name'],
project_id=project_id)
return (True, return_data)
except Exception:
raise
def delete_healthcheck(client, name, project_id, resource_type='HTTP'):
"""
Delete a Healthcheck.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Url Map.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
try:
resource, entity_name = _get_req_resource(client, resource_type)
args = {'project': project_id, entity_name: name}
req = resource.delete(**args)
return_data = GCPUtils.execute_api_client_req(req, client)
return (True, return_data)
except Exception:
raise
def update_healthcheck(client, healthcheck, params, name, project_id,
resource_type='HTTP'):
"""
Update a Healthcheck.
If the healthcheck has not changed, the update will not occur.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param healthcheck: Name of the Url Map.
:type healthcheck: ``dict``
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:param name: Name of the Url Map.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_healthcheck_dict(params)
ans = GCPUtils.are_params_equal(healthcheck, gcp_dict)
if ans:
return (False, 'no update necessary')
try:
resource, entity_name = _get_req_resource(client, resource_type)
args = {'project': project_id, entity_name: name, 'body': gcp_dict}
req = resource.update(**args)
return_data = GCPUtils.execute_api_client_req(
req, client=client, raw=False)
return (True, return_data)
except Exception:
raise
def main():
module = AnsibleModule(argument_spec=dict(
healthcheck_name=dict(required=True),
healthcheck_type=dict(required=True,
choices=['HTTP', 'HTTPS']),
request_path=dict(required=False, default='/'),
check_interval=dict(required=False, type='int', default=5),
healthy_threshold=dict(required=False, type='int', default=2),
unhealthy_threshold=dict(required=False, type='int', default=2),
host_header=dict(required=False, type='str', default=''),
timeout=dict(required=False, type='int', default=5),
port=dict(required=False, type='int'),
state=dict(choices=['absent', 'present'], default='present'),
service_account_email=dict(),
service_account_permissions=dict(type='list'),
credentials_file=dict(),
project_id=dict(), ), )
client, conn_params = get_google_api_client(module, 'compute', user_agent_product=USER_AGENT_PRODUCT,
user_agent_version=USER_AGENT_VERSION)
params = {}
params['healthcheck_name'] = module.params.get('healthcheck_name')
params['healthcheck_type'] = module.params.get('healthcheck_type')
params['request_path'] = module.params.get('request_path')
params['check_interval'] = module.params.get('check_interval')
params['healthy_threshold'] = module.params.get('healthy_threshold')
params['unhealthy_threshold'] = module.params.get('unhealthy_threshold')
params['host_header'] = module.params.get('host_header')
params['timeout'] = module.params.get('timeout')
params['port'] = module.params.get('port', None)
params['state'] = module.params.get('state')
if not params['port']:
params['port'] = 80
if params['healthcheck_type'] == 'HTTPS':
params['port'] = 443
try:
_validate_healthcheck_params(params)
except Exception as e:
module.fail_json(msg=e.message, changed=False)
changed = False
json_output = {'state': params['state']}
healthcheck = get_healthcheck(client,
name=params['healthcheck_name'],
project_id=conn_params['project_id'],
resource_type=params['healthcheck_type'])
if not healthcheck:
if params['state'] == 'absent':
# Doesn't exist in GCE, and state==absent.
changed = False
module.fail_json(
msg="Cannot delete unknown healthcheck: %s" %
(params['healthcheck_name']))
else:
# Create
changed, json_output['healthcheck'] = create_healthcheck(client,
params=params,
project_id=conn_params['project_id'],
resource_type=params['healthcheck_type'])
elif params['state'] == 'absent':
# Delete
changed, json_output['healthcheck'] = delete_healthcheck(client,
name=params['healthcheck_name'],
project_id=conn_params['project_id'],
resource_type=params['healthcheck_type'])
else:
changed, json_output['healthcheck'] = update_healthcheck(client,
healthcheck=healthcheck,
params=params,
name=params['healthcheck_name'],
project_id=conn_params['project_id'],
resource_type=params['healthcheck_type'])
json_output['changed'] = changed
json_output.update(params)
module.exit_json(**json_output)
if __name__ == '__main__':
main()

View file

@ -1,320 +0,0 @@
#!/usr/bin/python
# Copyright 2017 Google Inc.
# 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: gcp_target_proxy
short_description: Create, Update or Destroy a Target_Proxy.
description:
- Create, Update or Destroy a Target_Proxy. See
U(https://cloud.google.com/compute/docs/load-balancing/http/target-proxies) for an overview.
More details on the Target_Proxy API can be found at
U(https://cloud.google.com/compute/docs/reference/latest/targetHttpProxies#resource-representations).
requirements:
- "python >= 2.6"
- "google-api-python-client >= 1.6.2"
- "google-auth >= 0.9.0"
- "google-auth-httplib2 >= 0.0.2"
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_compute_target_http_proxy) instead.
notes:
- Currently only supports global HTTP proxy.
author:
- "Tom Melendez (@supertom) <tom@supertom.com>"
options:
target_proxy_name:
type: str
description:
- Name of the Target_Proxy.
required: true
target_proxy_type:
type: str
description:
- Type of Target_Proxy. HTTP, HTTPS or SSL. Only HTTP is currently supported.
required: true
choices: [HTTP]
url_map_name:
type: str
description:
- Name of the Url Map. Required if type is HTTP or HTTPS proxy.
required: false
project_id:
type: str
description:
- your GCE project ID
pem_file:
type: str
description:
- path to the pem file associated with the service account email
This option is deprecated. Use 'credentials_file'.
credentials_file:
type: str
description:
- path to the JSON file associated with the service account email
service_account_email:
type: str
description:
- service account email
service_account_permissions:
type: list
description:
- service account permissions
state:
type: str
description: The state the target proxy should be in. C(present) or C(absent) are the only valid options.
required: true
choices: [present, absent]
'''
EXAMPLES = '''
- name: Create Minimum HTTP Target_Proxy
community.general.gcp_target_proxy:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
target_proxy_name: my-target_proxy
target_proxy_type: HTTP
url_map_name: my-url-map
state: present
'''
RETURN = '''
state:
description: state of the Target_Proxy
returned: Always.
type: str
sample: present
updated_target_proxy:
description: True if the target_proxy has been updated. Will not appear on
initial target_proxy creation.
returned: if the target_proxy has been updated.
type: bool
sample: true
target_proxy_name:
description: Name of the Target_Proxy
returned: Always
type: str
sample: my-target-proxy
target_proxy_type:
description: Type of Target_Proxy. One of HTTP, HTTPS or SSL.
returned: Always
type: str
sample: HTTP
target_proxy:
description: GCP Target_Proxy dictionary
returned: Always. Refer to GCP documentation for detailed field descriptions.
type: dict
sample: { "name": "my-target-proxy", "urlMap": "..." }
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gcp import get_google_api_client, GCPUtils
USER_AGENT_PRODUCT = 'ansible-target_proxy'
USER_AGENT_VERSION = '0.0.1'
def _build_target_proxy_dict(params, project_id=None):
"""
Reformat services in Ansible Params.
:param params: Params from AnsibleModule object
:type params: ``dict``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: dictionary suitable for submission to GCP UrlMap API.
:rtype ``dict``
"""
url = ''
if project_id:
url = GCPUtils.build_googleapi_url(project_id)
gcp_dict = GCPUtils.params_to_gcp_dict(params, 'target_proxy_name')
if 'urlMap' in gcp_dict:
gcp_dict['urlMap'] = '%s/global/urlMaps/%s' % (url,
gcp_dict['urlMap'])
return gcp_dict
def get_target_http_proxy(client, name, project_id=None):
"""
Get a Target HTTP Proxy from GCP.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Target Proxy.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: A dict resp from the respective GCP 'get' request.
:rtype: ``dict``
"""
req = client.targetHttpProxies().get(project=project_id,
targetHttpProxy=name)
return GCPUtils.execute_api_client_req(req, raise_404=False)
def create_target_http_proxy(client, params, project_id):
"""
Create a new Target_Proxy.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_target_proxy_dict(params, project_id)
try:
req = client.targetHttpProxies().insert(project=project_id,
body=gcp_dict)
return_data = GCPUtils.execute_api_client_req(req, client, raw=False)
if not return_data:
return_data = get_target_http_proxy(client,
name=params['target_proxy_name'],
project_id=project_id)
return (True, return_data)
except Exception:
raise
def delete_target_http_proxy(client, name, project_id):
"""
Delete a Target_Proxy.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Target Proxy.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
try:
req = client.targetHttpProxies().delete(
project=project_id, targetHttpProxy=name)
return_data = GCPUtils.execute_api_client_req(req, client)
return (True, return_data)
except Exception:
raise
def update_target_http_proxy(client, target_proxy, params, name, project_id):
"""
Update a HTTP Target_Proxy. Currently only the Url Map can be updated.
If the target_proxy has not changed, the update will not occur.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param target_proxy: Name of the Target Proxy.
:type target_proxy: ``dict``
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:param name: Name of the Target Proxy.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_target_proxy_dict(params, project_id)
GCPUtils.are_params_equal(target_proxy, gcp_dict)
if target_proxy['urlMap'] == gcp_dict['urlMap']:
return (False, 'no update necessary')
try:
req = client.targetHttpProxies().setUrlMap(project=project_id,
targetHttpProxy=name,
body={"urlMap": gcp_dict['urlMap']})
return_data = GCPUtils.execute_api_client_req(
req, client=client, raw=False)
return (True, return_data)
except Exception:
raise
def main():
module = AnsibleModule(argument_spec=dict(
target_proxy_name=dict(required=True),
target_proxy_type=dict(required=True, choices=['HTTP']),
url_map_name=dict(required=False),
state=dict(required=True, choices=['absent', 'present']),
service_account_email=dict(),
service_account_permissions=dict(type='list'),
pem_file=dict(),
credentials_file=dict(),
project_id=dict(), ), )
client, conn_params = get_google_api_client(module, 'compute', user_agent_product=USER_AGENT_PRODUCT,
user_agent_version=USER_AGENT_VERSION)
params = {}
params['state'] = module.params.get('state')
params['target_proxy_name'] = module.params.get('target_proxy_name')
params['target_proxy_type'] = module.params.get('target_proxy_type')
params['url_map'] = module.params.get('url_map_name', None)
changed = False
json_output = {'state': params['state']}
target_proxy = get_target_http_proxy(client,
name=params['target_proxy_name'],
project_id=conn_params['project_id'])
if not target_proxy:
if params['state'] == 'absent':
# Doesn't exist in GCE, and state==absent.
changed = False
module.fail_json(
msg="Cannot delete unknown target_proxy: %s" %
(params['target_proxy_name']))
else:
# Create
changed, json_output['target_proxy'] = create_target_http_proxy(client,
params=params,
project_id=conn_params['project_id'])
elif params['state'] == 'absent':
# Delete
changed, json_output['target_proxy'] = delete_target_http_proxy(client,
name=params['target_proxy_name'],
project_id=conn_params['project_id'])
else:
changed, json_output['target_proxy'] = update_target_http_proxy(client,
target_proxy=target_proxy,
params=params,
name=params['target_proxy_name'],
project_id=conn_params['project_id'])
json_output['updated_target_proxy'] = changed
json_output['changed'] = changed
json_output.update(params)
module.exit_json(**json_output)
if __name__ == '__main__':
main()

View file

@ -1,535 +0,0 @@
#!/usr/bin/python
# Copyright 2017 Google Inc.
# 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: gcp_url_map
short_description: Create, Update or Destroy a Url_Map.
description:
- Create, Update or Destroy a Url_Map. See
U(https://cloud.google.com/compute/docs/load-balancing/http/url-map) for an overview.
More details on the Url_Map API can be found at
U(https://cloud.google.com/compute/docs/reference/latest/urlMaps#resource).
requirements:
- "python >= 2.6"
- "google-api-python-client >= 1.6.2"
- "google-auth >= 0.9.0"
- "google-auth-httplib2 >= 0.0.2"
notes:
- Only supports global Backend Services.
- Url_Map tests are not currently supported.
author:
- "Tom Melendez (@supertom) <tom@supertom.com>"
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_compute_url_map) instead.
options:
url_map_name:
type: str
description:
- Name of the Url_Map.
required: true
default_service:
type: str
description:
- Default Backend Service if no host rules match.
required: true
host_rules:
type: list
description:
- The list of HostRules to use against the URL. Contains
a list of hosts and an associated path_matcher.
- The 'hosts' parameter is a list of host patterns to match. They
must be valid hostnames, except * will match any string of
([a-z0-9-.]*). In that case, * must be the first character
and must be followed in the pattern by either - or ..
- The 'path_matcher' parameter is name of the PathMatcher to use
to match the path portion of the URL if the hostRule matches the URL's
host portion.
required: false
path_matchers:
type: list
description:
- The list of named PathMatchers to use against the URL. Contains
path_rules, which is a list of paths and an associated service. A
default_service can also be specified for each path_matcher.
- The 'name' parameter to which this path_matcher is referred by the
host_rule.
- The 'default_service' parameter is the name of the
BackendService resource. This will be used if none of the path_rules
defined by this path_matcher is matched by the URL's path portion.
- The 'path_rules' parameter is a list of dictionaries containing a
list of paths and a service to direct traffic to. Each path item must
start with / and the only place a * is allowed is at the end following
a /. The string fed to the path matcher does not include any text after
the first ? or #, and those chars are not allowed here.
required: false
project_id:
type: str
description:
- The Google Cloud Platform project ID to use.
pem_file:
type: str
description:
- The path to the PEM file associated with the service account email.
- This option is deprecated and may be removed in a future release. Use I(credentials_file) instead.
credentials_file:
type: str
description:
- The path to the JSON file associated with the service account email.
service_account_email:
type: str
description:
- service account email
service_account_permissions:
type: list
description:
- service account permissions
state:
type: str
description: The state the URL map should be in. C(present) or C(absent) are the only valid options.
default: present
required: false
choices: [present, absent]
'''
EXAMPLES = '''
- name: Create Minimal Url_Map
community.general.gcp_url_map:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
url_map_name: my-url_map
default_service: my-backend-service
state: present
- name: Create UrlMap with pathmatcher
community.general.gcp_url_map:
service_account_email: "{{ service_account_email }}"
credentials_file: "{{ credentials_file }}"
project_id: "{{ project_id }}"
url_map_name: my-url-map-pm
default_service: default-backend-service
path_matchers:
- name: 'path-matcher-one'
description: 'path matcher one'
default_service: 'bes-pathmatcher-one-default'
path_rules:
- service: 'my-one-bes'
paths:
- '/data'
- '/aboutus'
host_rules:
- hosts:
- '*.'
path_matcher: 'path-matcher-one'
state: "present"
'''
RETURN = '''
host_rules:
description: List of HostRules.
returned: If specified.
type: dict
sample: [ { hosts: ["*."], "path_matcher": "my-pm" } ]
path_matchers:
description: The list of named PathMatchers to use against the URL.
returned: If specified.
type: dict
sample: [ { "name": "my-pm", "path_rules": [ { "paths": [ "/data" ] } ], "service": "my-service" } ]
state:
description: state of the Url_Map
returned: Always.
type: str
sample: present
updated_url_map:
description: True if the url_map has been updated. Will not appear on
initial url_map creation.
returned: if the url_map has been updated.
type: bool
sample: true
url_map_name:
description: Name of the Url_Map
returned: Always
type: str
sample: my-url-map
url_map:
description: GCP Url_Map dictionary
returned: Always. Refer to GCP documentation for detailed field descriptions.
type: dict
sample: { "name": "my-url-map", "hostRules": [...], "pathMatchers": [...] }
'''
try:
from ast import literal_eval
HAS_PYTHON26 = True
except ImportError:
HAS_PYTHON26 = False
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gcp import check_params, get_google_api_client, GCPUtils
from ansible.module_utils.six import string_types
USER_AGENT_PRODUCT = 'ansible-url_map'
USER_AGENT_VERSION = '0.0.1'
def _validate_params(params):
"""
Validate url_map params.
This function calls _validate_host_rules_params to verify
the host_rules-specific parameters.
This function calls _validate_path_matchers_params to verify
the path_matchers-specific parameters.
:param params: Ansible dictionary containing configuration.
:type params: ``dict``
:return: True or raises ValueError
:rtype: ``bool`` or `class:ValueError`
"""
fields = [
{'name': 'default_service', 'type': str, 'required': True},
{'name': 'host_rules', 'type': list},
{'name': 'path_matchers', 'type': list},
]
try:
check_params(params, fields)
if 'path_matchers' in params and params['path_matchers'] is not None:
_validate_path_matcher_params(params['path_matchers'])
if 'host_rules' in params and params['host_rules'] is not None:
_validate_host_rules_params(params['host_rules'])
except Exception:
raise
return (True, '')
def _validate_path_matcher_params(path_matchers):
"""
Validate configuration for path_matchers.
:param path_matchers: Ansible dictionary containing path_matchers
configuration (only).
:type path_matchers: ``dict``
:return: True or raises ValueError
:rtype: ``bool`` or `class:ValueError`
"""
fields = [
{'name': 'name', 'type': str, 'required': True},
{'name': 'default_service', 'type': str, 'required': True},
{'name': 'path_rules', 'type': list, 'required': True},
{'name': 'max_rate', 'type': int},
{'name': 'max_rate_per_instance', 'type': float},
]
pr_fields = [
{'name': 'service', 'type': str, 'required': True},
{'name': 'paths', 'type': list, 'required': True},
]
if not path_matchers:
raise ValueError(('path_matchers should be a list. %s (%s) provided'
% (path_matchers, type(path_matchers))))
for pm in path_matchers:
try:
check_params(pm, fields)
for pr in pm['path_rules']:
check_params(pr, pr_fields)
for path in pr['paths']:
if not path.startswith('/'):
raise ValueError("path for %s must start with /" % (
pm['name']))
except Exception:
raise
return (True, '')
def _validate_host_rules_params(host_rules):
"""
Validate configuration for host_rules.
:param host_rules: Ansible dictionary containing host_rules
configuration (only).
:type host_rules ``dict``
:return: True or raises ValueError
:rtype: ``bool`` or `class:ValueError`
"""
fields = [
{'name': 'path_matcher', 'type': str, 'required': True},
]
if not host_rules:
raise ValueError('host_rules should be a list.')
for hr in host_rules:
try:
check_params(hr, fields)
for host in hr['hosts']:
if not isinstance(host, string_types):
raise ValueError("host in hostrules must be a string")
elif '*' in host:
if host.index('*') != 0:
raise ValueError("wildcard must be first char in host, %s" % (
host))
else:
if host[1] not in ['.', '-', ]:
raise ValueError("wildcard be followed by a '.' or '-', %s" % (
host))
except Exception:
raise
return (True, '')
def _build_path_matchers(path_matcher_list, project_id):
"""
Reformat services in path matchers list.
Specifically, builds out URLs.
:param path_matcher_list: The GCP project ID.
:type path_matcher_list: ``list`` of ``dict``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: list suitable for submission to GCP
UrlMap API Path Matchers list.
:rtype ``list`` of ``dict``
"""
url = ''
if project_id:
url = GCPUtils.build_googleapi_url(project_id)
for pm in path_matcher_list:
if 'defaultService' in pm:
pm['defaultService'] = '%s/global/backendServices/%s' % (url,
pm['defaultService'])
if 'pathRules' in pm:
for rule in pm['pathRules']:
if 'service' in rule:
rule['service'] = '%s/global/backendServices/%s' % (url,
rule['service'])
return path_matcher_list
def _build_url_map_dict(params, project_id=None):
"""
Reformat services in Ansible Params.
:param params: Params from AnsibleModule object
:type params: ``dict``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: dictionary suitable for submission to GCP UrlMap API.
:rtype ``dict``
"""
url = ''
if project_id:
url = GCPUtils.build_googleapi_url(project_id)
gcp_dict = GCPUtils.params_to_gcp_dict(params, 'url_map_name')
if 'defaultService' in gcp_dict:
gcp_dict['defaultService'] = '%s/global/backendServices/%s' % (url,
gcp_dict['defaultService'])
if 'pathMatchers' in gcp_dict:
gcp_dict['pathMatchers'] = _build_path_matchers(gcp_dict['pathMatchers'], project_id)
return gcp_dict
def get_url_map(client, name, project_id=None):
"""
Get a Url_Map from GCP.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Url Map.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: A dict resp from the respective GCP 'get' request.
:rtype: ``dict``
"""
try:
req = client.urlMaps().get(project=project_id, urlMap=name)
return GCPUtils.execute_api_client_req(req, raise_404=False)
except Exception:
raise
def create_url_map(client, params, project_id):
"""
Create a new Url_Map.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_url_map_dict(params, project_id)
try:
req = client.urlMaps().insert(project=project_id, body=gcp_dict)
return_data = GCPUtils.execute_api_client_req(req, client, raw=False)
if not return_data:
return_data = get_url_map(client,
name=params['url_map_name'],
project_id=project_id)
return (True, return_data)
except Exception:
raise
def delete_url_map(client, name, project_id):
"""
Delete a Url_Map.
:param client: An initialized GCE Compute Discover resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param name: Name of the Url Map.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
try:
req = client.urlMaps().delete(project=project_id, urlMap=name)
return_data = GCPUtils.execute_api_client_req(req, client)
return (True, return_data)
except Exception:
raise
def update_url_map(client, url_map, params, name, project_id):
"""
Update a Url_Map.
If the url_map has not changed, the update will not occur.
:param client: An initialized GCE Compute Discovery resource.
:type client: :class: `googleapiclient.discovery.Resource`
:param url_map: Name of the Url Map.
:type url_map: ``dict``
:param params: Dictionary of arguments from AnsibleModule.
:type params: ``dict``
:param name: Name of the Url Map.
:type name: ``str``
:param project_id: The GCP project ID.
:type project_id: ``str``
:return: Tuple with changed status and response dict
:rtype: ``tuple`` in the format of (bool, dict)
"""
gcp_dict = _build_url_map_dict(params, project_id)
ans = GCPUtils.are_params_equal(url_map, gcp_dict)
if ans:
return (False, 'no update necessary')
gcp_dict['fingerprint'] = url_map['fingerprint']
try:
req = client.urlMaps().update(project=project_id,
urlMap=name, body=gcp_dict)
return_data = GCPUtils.execute_api_client_req(req, client=client, raw=False)
return (True, return_data)
except Exception:
raise
def main():
module = AnsibleModule(argument_spec=dict(
url_map_name=dict(required=True),
state=dict(choices=['absent', 'present'], default='present'),
default_service=dict(required=True),
path_matchers=dict(type='list', required=False),
host_rules=dict(type='list', required=False),
service_account_email=dict(),
service_account_permissions=dict(type='list'),
pem_file=dict(),
credentials_file=dict(),
project_id=dict(), ), required_together=[
['path_matchers', 'host_rules'], ])
client, conn_params = get_google_api_client(module, 'compute', user_agent_product=USER_AGENT_PRODUCT,
user_agent_version=USER_AGENT_VERSION)
params = {}
params['state'] = module.params.get('state')
params['url_map_name'] = module.params.get('url_map_name')
params['default_service'] = module.params.get('default_service')
if module.params.get('path_matchers'):
params['path_matchers'] = module.params.get('path_matchers')
if module.params.get('host_rules'):
params['host_rules'] = module.params.get('host_rules')
try:
_validate_params(params)
except Exception as e:
module.fail_json(msg=e.message, changed=False)
changed = False
json_output = {'state': params['state']}
url_map = get_url_map(client,
name=params['url_map_name'],
project_id=conn_params['project_id'])
if not url_map:
if params['state'] == 'absent':
# Doesn't exist in GCE, and state==absent.
changed = False
module.fail_json(
msg="Cannot delete unknown url_map: %s" %
(params['url_map_name']))
else:
# Create
changed, json_output['url_map'] = create_url_map(client,
params=params,
project_id=conn_params['project_id'])
elif params['state'] == 'absent':
# Delete
changed, json_output['url_map'] = delete_url_map(client,
name=params['url_map_name'],
project_id=conn_params['project_id'])
else:
changed, json_output['url_map'] = update_url_map(client,
url_map=url_map,
params=params,
name=params['url_map_name'],
project_id=conn_params['project_id'])
json_output['updated_url_map'] = changed
json_output['changed'] = changed
json_output.update(params)
module.exit_json(**json_output)
if __name__ == '__main__':
main()

View file

@ -1,304 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Google Inc.
# 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: gcspanner
short_description: Create and Delete Instances/Databases on Spanner
description:
- Create and Delete Instances/Databases on Spanner.
See U(https://cloud.google.com/spanner/docs) for an overview.
requirements:
- python >= 2.6
- google-auth >= 0.5.0
- google-cloud-spanner >= 0.23.0
notes:
- Changing the configuration on an existing instance is not supported.
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated modules released with increased functionality
alternative: Use M(google.cloud.gcp_spanner_database) and/or M(google.cloud.gcp_spanner_instance) instead.
author:
- Tom Melendez (@supertom) <tom@supertom.com>
options:
configuration:
type: str
description:
- Configuration the instance should use.
- Examples are us-central1, asia-east1 and europe-west1.
required: yes
instance_id:
type: str
description:
- GCP spanner instance name.
required: yes
database_name:
type: str
description:
- Name of database contained on the instance.
force_instance_delete:
description:
- To delete an instance, this argument must exist and be true (along with state being equal to absent).
type: bool
default: 'no'
instance_display_name:
type: str
description:
- Name of Instance to display.
- If not specified, instance_id will be used instead.
node_count:
type: int
description:
- Number of nodes in the instance.
default: 1
state:
type: str
description:
- State of the instance or database. Applies to the most granular resource.
- If a C(database_name) is specified we remove it.
- If only C(instance_id) is specified, that is what is removed.
choices: [ absent, present ]
default: present
project_id:
type: str
description:
- your GCE project ID
credentials_file:
type: str
description:
- path to the JSON file associated with the service account email
service_account_email:
type: str
description:
- service account email
'''
EXAMPLES = '''
- name: Create instance
community.general.gcspanner:
instance_id: '{{ instance_id }}'
configuration: '{{ configuration }}'
state: present
node_count: 1
- name: Create database
community.general.gcspanner:
instance_id: '{{ instance_id }}'
configuration: '{{ configuration }}'
database_name: '{{ database_name }}'
state: present
- name: Delete instance (and all databases)
- community.general.gcspanner:
instance_id: '{{ instance_id }}'
configuration: '{{ configuration }}'
state: absent
force_instance_delete: yes
'''
RETURN = '''
state:
description: The state of the instance or database. Value will be either 'absent' or 'present'.
returned: Always
type: str
sample: "present"
database_name:
description: Name of database.
returned: When database name is specified
type: str
sample: "mydatabase"
instance_id:
description: Name of instance.
returned: Always
type: str
sample: "myinstance"
previous_values:
description: List of dictionaries containing previous values prior to update.
returned: When an instance update has occurred and a field has been modified.
type: dict
sample: "'previous_values': { 'instance': { 'instance_display_name': 'my-instance', 'node_count': 1 } }"
updated:
description: Boolean field to denote an update has occurred.
returned: When an update has occurred.
type: bool
sample: True
'''
try:
from ast import literal_eval
HAS_PYTHON26 = True
except ImportError:
HAS_PYTHON26 = False
try:
from google.cloud import spanner
from google.gax.errors import GaxError
HAS_GOOGLE_CLOUD_SPANNER = True
except ImportError as e:
HAS_GOOGLE_CLOUD_SPANNER = False
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.gcp import check_min_pkg_version, get_google_cloud_credentials
from ansible.module_utils.six import string_types
CLOUD_CLIENT = 'google-cloud-spanner'
CLOUD_CLIENT_MINIMUM_VERSION = '0.23.0'
CLOUD_CLIENT_USER_AGENT = 'ansible-spanner-0.1'
def get_spanner_configuration_name(config_name, project_name):
config_name = 'projects/%s/instanceConfigs/regional-%s' % (project_name,
config_name)
return config_name
def instance_update(instance):
"""
Call update method on spanner client.
Note: A ValueError exception is thrown despite the client succeeding.
So, we validate the node_count and instance_display_name parameters and then
ignore the ValueError exception.
:param instance: a Spanner instance object
:type instance: class `google.cloud.spanner.Instance`
:returns True on success, raises ValueError on type error.
:rtype ``bool``
"""
errmsg = ''
if not isinstance(instance.node_count, int):
errmsg = 'node_count must be an integer %s (%s)' % (
instance.node_count, type(instance.node_count))
if instance.display_name and not isinstance(instance.display_name,
string_types):
errmsg = 'instance_display_name must be an string %s (%s)' % (
instance.display_name, type(instance.display_name))
if errmsg:
raise ValueError(errmsg)
try:
instance.update()
except ValueError:
# The ValueError here is the one we 'expect'.
pass
return True
def main():
module = AnsibleModule(
argument_spec=dict(
instance_id=dict(type='str', required=True),
state=dict(type='str', default='present', choices=['absent', 'present']),
database_name=dict(type='str'),
configuration=dict(type='str', required=True),
node_count=dict(type='int', default=1),
instance_display_name=dict(type='str'),
force_instance_delete=dict(type='bool', default=False),
service_account_email=dict(type='str'),
credentials_file=dict(type='str'),
project_id=dict(type='str'),
),
)
if not HAS_PYTHON26:
module.fail_json(
msg="GCE module requires python's 'ast' module, python v2.6+")
if not HAS_GOOGLE_CLOUD_SPANNER:
module.fail_json(msg="Please install google-cloud-spanner.")
if not check_min_pkg_version(CLOUD_CLIENT, CLOUD_CLIENT_MINIMUM_VERSION):
module.fail_json(msg="Please install %s client version %s" %
(CLOUD_CLIENT, CLOUD_CLIENT_MINIMUM_VERSION))
mod_params = {}
mod_params['state'] = module.params.get('state')
mod_params['instance_id'] = module.params.get('instance_id')
mod_params['database_name'] = module.params.get('database_name')
mod_params['configuration'] = module.params.get('configuration')
mod_params['node_count'] = module.params.get('node_count', None)
mod_params['instance_display_name'] = module.params.get('instance_display_name')
mod_params['force_instance_delete'] = module.params.get('force_instance_delete')
creds, params = get_google_cloud_credentials(module)
spanner_client = spanner.Client(project=params['project_id'],
credentials=creds,
user_agent=CLOUD_CLIENT_USER_AGENT)
changed = False
json_output = {}
i = None
if mod_params['instance_id']:
config_name = get_spanner_configuration_name(
mod_params['configuration'], params['project_id'])
i = spanner_client.instance(mod_params['instance_id'],
configuration_name=config_name)
d = None
if mod_params['database_name']:
# TODO(supertom): support DDL
ddl_statements = ''
d = i.database(mod_params['database_name'], ddl_statements)
if mod_params['state'] == 'absent':
# Remove the most granular resource. If database is specified
# we remove it. If only instance is specified, that is what is removed.
if d is not None and d.exists():
d.drop()
changed = True
else:
if i.exists():
if mod_params['force_instance_delete']:
i.delete()
else:
module.fail_json(
msg=(("Cannot delete Spanner instance: "
"'force_instance_delete' argument not specified")))
changed = True
elif mod_params['state'] == 'present':
if not i.exists():
i = spanner_client.instance(mod_params['instance_id'],
configuration_name=config_name,
display_name=mod_params['instance_display_name'],
node_count=mod_params['node_count'] or 1)
i.create()
changed = True
else:
# update instance
i.reload()
inst_prev_vals = {}
if i.display_name != mod_params['instance_display_name']:
inst_prev_vals['instance_display_name'] = i.display_name
i.display_name = mod_params['instance_display_name']
if mod_params['node_count']:
if i.node_count != mod_params['node_count']:
inst_prev_vals['node_count'] = i.node_count
i.node_count = mod_params['node_count']
if inst_prev_vals:
changed = instance_update(i)
json_output['updated'] = changed
json_output['previous_values'] = {'instance': inst_prev_vals}
if d:
if not d.exists():
d.create()
d.reload()
changed = True
json_output['changed'] = changed
json_output.update(mod_params)
module.exit_json(**json_output)
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
./cloud/google/gcdns_record.py

View file

@ -1 +0,0 @@
./cloud/google/gcdns_zone.py

View file

@ -1 +0,0 @@
./cloud/google/gce.py

View file

@ -1 +0,0 @@
./cloud/google/gcp_backend_service.py

View file

@ -1 +0,0 @@
./cloud/google/gcp_forwarding_rule.py

View file

@ -1 +0,0 @@
./cloud/google/gcp_healthcheck.py

View file

@ -1 +0,0 @@
./cloud/google/gcp_target_proxy.py

View file

@ -1 +0,0 @@
./cloud/google/gcp_url_map.py

View file

@ -1 +0,0 @@
./cloud/google/gcspanner.py

View file

@ -14,10 +14,6 @@ plugins/modules/cloud/centurylink/clc_modify_server.py validate-modules:paramete
plugins/modules/cloud/centurylink/clc_publicip.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/centurylink/clc_server.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcdns_record.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce.py pylint:blacklisted-name
plugins/modules/cloud/google/gce.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce.py yamllint:unparsable-with-libyaml
plugins/modules/cloud/google/gce_eip.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_eip.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce_img.py pylint:blacklisted-name
@ -37,13 +33,6 @@ plugins/modules/cloud/google/gce_snapshot.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_snapshot.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce_tag.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_tag.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_backend_service.py pylint:blacklisted-name
plugins/modules/cloud/google/gcp_backend_service.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_forwarding_rule.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_healthcheck.py pylint:blacklisted-name
plugins/modules/cloud/google/gcp_healthcheck.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_target_proxy.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_url_map.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcpubsub.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcpubsub_info.py validate-modules:parameter-state-invalid-choice
plugins/modules/cloud/heroku/heroku_collaborator.py validate-modules:parameter-list-no-elements
@ -682,7 +671,5 @@ plugins/modules/web_infrastructure/rundeck_acl_policy.py pylint:blacklisted-name
plugins/modules/web_infrastructure/sophos_utm/utm_network_interface_address.py validate-modules:parameter-type-not-in-doc
plugins/modules/web_infrastructure/sophos_utm/utm_proxy_exception.py validate-modules:doc-elements-mismatch
scripts/inventory/gce.py pylint:blacklisted-name
tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py future-import-boilerplate
tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py metaclass-boilerplate
tests/utils/shippable/check_matrix.py replace-urlopen
tests/utils/shippable/timing.py shebang

View file

@ -14,10 +14,6 @@ plugins/modules/cloud/centurylink/clc_modify_server.py validate-modules:paramete
plugins/modules/cloud/centurylink/clc_publicip.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/centurylink/clc_server.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcdns_record.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce.py pylint:blacklisted-name
plugins/modules/cloud/google/gce.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce.py yamllint:unparsable-with-libyaml
plugins/modules/cloud/google/gce_eip.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_eip.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce_img.py pylint:blacklisted-name
@ -37,13 +33,6 @@ plugins/modules/cloud/google/gce_snapshot.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_snapshot.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gce_tag.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_tag.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_backend_service.py pylint:blacklisted-name
plugins/modules/cloud/google/gcp_backend_service.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_forwarding_rule.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_healthcheck.py pylint:blacklisted-name
plugins/modules/cloud/google/gcp_healthcheck.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_target_proxy.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcp_url_map.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcpubsub.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/google/gcpubsub_info.py validate-modules:parameter-state-invalid-choice
plugins/modules/cloud/heroku/heroku_collaborator.py validate-modules:parameter-list-no-elements
@ -682,7 +671,5 @@ plugins/modules/web_infrastructure/rundeck_acl_policy.py pylint:blacklisted-name
plugins/modules/web_infrastructure/sophos_utm/utm_network_interface_address.py validate-modules:parameter-type-not-in-doc
plugins/modules/web_infrastructure/sophos_utm/utm_proxy_exception.py validate-modules:doc-elements-mismatch
scripts/inventory/gce.py pylint:blacklisted-name
tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py future-import-boilerplate
tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py metaclass-boilerplate
tests/utils/shippable/check_matrix.py replace-urlopen
tests/utils/shippable/timing.py shebang

View file

@ -6,16 +6,6 @@ plugins/module_utils/_mount.py future-import-boilerplate
plugins/module_utils/_mount.py metaclass-boilerplate
plugins/modules/cloud/centurylink/clc_alert_policy.py validate-modules:no-default-for-required-parameter
plugins/modules/cloud/centurylink/clc_firewall_policy.py validate-modules:no-default-for-required-parameter
plugins/modules/cloud/google/gcdns_record.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcdns_record.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcdns_record.py validate-modules:missing-main-call
plugins/modules/cloud/google/gcdns_zone.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcdns_zone.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcdns_zone.py validate-modules:missing-main-call
plugins/modules/cloud/google/gce.py pylint:blacklisted-name
plugins/modules/cloud/google/gce.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gce.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gce.py validate-modules:missing-main-call
plugins/modules/cloud/google/gce_eip.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_img.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_instance_template.py pylint:blacklisted-name
@ -26,26 +16,6 @@ plugins/modules/cloud/google/gce_net.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_pd.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_snapshot.py pylint:blacklisted-name
plugins/modules/cloud/google/gce_tag.py pylint:blacklisted-name
plugins/modules/cloud/google/gcp_backend_service.py pylint:blacklisted-name
plugins/modules/cloud/google/gcp_backend_service.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcp_backend_service.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcp_backend_service.py validate-modules:missing-main-call
plugins/modules/cloud/google/gcp_forwarding_rule.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcp_forwarding_rule.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcp_forwarding_rule.py validate-modules:missing-main-call
plugins/modules/cloud/google/gcp_healthcheck.py pylint:blacklisted-name
plugins/modules/cloud/google/gcp_healthcheck.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcp_healthcheck.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcp_healthcheck.py validate-modules:missing-main-call
plugins/modules/cloud/google/gcp_target_proxy.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcp_target_proxy.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcp_target_proxy.py validate-modules:missing-main-call
plugins/modules/cloud/google/gcp_url_map.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcp_url_map.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcp_url_map.py validate-modules:missing-main-call
plugins/modules/cloud/google/gcspanner.py validate-modules:deprecation-mismatch
plugins/modules/cloud/google/gcspanner.py validate-modules:invalid-documentation
plugins/modules/cloud/google/gcspanner.py validate-modules:missing-main-call
plugins/modules/cloud/kubevirt/kubevirt_cdi_upload.py validate-modules:doc-missing-type
plugins/modules/cloud/kubevirt/kubevirt_preset.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/kubevirt/kubevirt_pvc.py validate-modules:parameter-type-not-in-doc
@ -530,7 +500,5 @@ plugins/modules/web_infrastructure/nginx_status_facts.py validate-modules:invali
plugins/modules/web_infrastructure/rundeck_acl_policy.py pylint:blacklisted-name
plugins/modules/web_infrastructure/sophos_utm/utm_network_interface_address.py validate-modules:parameter-type-not-in-doc
scripts/inventory/gce.py pylint:blacklisted-name
tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py future-import-boilerplate
tests/unit/plugins/modules/cloud/google/test_gcp_forwarding_rule.py metaclass-boilerplate
tests/utils/shippable/check_matrix.py replace-urlopen
tests/utils/shippable/timing.py shebang

View file

@ -1,30 +0,0 @@
import unittest
from ansible_collections.community.general.plugins.modules.cloud.google.gcp_forwarding_rule import _build_global_forwarding_rule_dict
class TestGCPFowardingRule(unittest.TestCase):
"""Unit tests for gcp_fowarding_rule module."""
params_dict = {
'forwarding_rule_name': 'foo_fowarding_rule_name',
'address': 'foo_external_address',
'target': 'foo_targetproxy',
'region': 'global',
'port_range': 80,
'protocol': 'TCP',
'state': 'present',
}
def test__build_global_forwarding_rule_dict(self):
expected = {
'name': 'foo_fowarding_rule_name',
'IPAddress': 'https://www.googleapis.com/compute/v1/projects/my-project/global/addresses/foo_external_address',
'target': 'https://www.googleapis.com/compute/v1/projects/my-project/global/targetHttpProxies/foo_targetproxy',
'region': 'global',
'portRange': 80,
'IPProtocol': 'TCP',
}
actual = _build_global_forwarding_rule_dict(
self.params_dict, 'my-project')
self.assertEqual(expected, actual)

View file

@ -1,169 +0,0 @@
# 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
import unittest
from ansible_collections.community.general.plugins.modules.cloud.google.gcp_url_map import _build_path_matchers, _build_url_map_dict
class TestGCPUrlMap(unittest.TestCase):
"""Unit tests for gcp_url_map module."""
params_dict = {
'url_map_name': 'foo_url_map_name',
'description': 'foo_url_map description',
'host_rules': [
{
'description': 'host rules description',
'hosts': [
'www.example.com',
'www2.example.com'
],
'path_matcher': 'host_rules_path_matcher'
}
],
'path_matchers': [
{
'name': 'path_matcher_one',
'description': 'path matcher one',
'defaultService': 'bes-pathmatcher-one-default',
'pathRules': [
{
'service': 'my-one-bes',
'paths': [
'/',
'/aboutus'
]
}
]
},
{
'name': 'path_matcher_two',
'description': 'path matcher two',
'defaultService': 'bes-pathmatcher-two-default',
'pathRules': [
{
'service': 'my-two-bes',
'paths': [
'/webapp',
'/graphs'
]
}
]
}
]
}
def test__build_path_matchers(self):
input_list = [
{
'defaultService': 'bes-pathmatcher-one-default',
'description': 'path matcher one',
'name': 'path_matcher_one',
'pathRules': [
{
'paths': [
'/',
'/aboutus'
],
'service': 'my-one-bes'
}
]
},
{
'defaultService': 'bes-pathmatcher-two-default',
'description': 'path matcher two',
'name': 'path_matcher_two',
'pathRules': [
{
'paths': [
'/webapp',
'/graphs'
],
'service': 'my-two-bes'
}
]
}
]
expected = [
{
'defaultService': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/bes-pathmatcher-one-default',
'description': 'path matcher one',
'name': 'path_matcher_one',
'pathRules': [
{
'paths': [
'/',
'/aboutus'
],
'service': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/my-one-bes'
}
]
},
{
'defaultService': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/bes-pathmatcher-two-default',
'description': 'path matcher two',
'name': 'path_matcher_two',
'pathRules': [
{
'paths': [
'/webapp',
'/graphs'
],
'service': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/my-two-bes'
}
]
}
]
actual = _build_path_matchers(input_list, 'my-project')
self.assertEqual(expected, actual)
def test__build_url_map_dict(self):
expected = {
'description': 'foo_url_map description',
'hostRules': [
{
'description': 'host rules description',
'hosts': [
'www.example.com',
'www2.example.com'
],
'pathMatcher': 'host_rules_path_matcher'
}
],
'name': 'foo_url_map_name',
'pathMatchers': [
{
'defaultService': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/bes-pathmatcher-one-default',
'description': 'path matcher one',
'name': 'path_matcher_one',
'pathRules': [
{
'paths': [
'/',
'/aboutus'
],
'service': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/my-one-bes'
}
]
},
{
'defaultService': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/bes-pathmatcher-two-default',
'description': 'path matcher two',
'name': 'path_matcher_two',
'pathRules': [
{
'paths': [
'/webapp',
'/graphs'
],
'service': 'https://www.googleapis.com/compute/v1/projects/my-project/global/backendServices/my-two-bes'
}
]
}
]
}
actual = _build_url_map_dict(self.params_dict, 'my-project')
self.assertEqual(expected, actual)