#!/usr/bin/python # Copyright 2013 Google Inc. # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . DOCUMENTATION = ''' --- module: gce_lb version_added: "1.5" short_description: create/destroy GCE load-balancer resources description: - This module can create and destroy Google Compute Engine C(loadbalancer) and C(httphealthcheck) resources. The primary LB resource is the C(load_balancer) resource and the health check parameters are all prefixed with I(httphealthcheck). The full documentation for Google Compute Engine load balancing is at U(https://developers.google.com/compute/docs/load-balancing/). However, the ansible module simplifies the configuration by following the libcloud model. Full install/configuration instructions for the gce* modules can be found in the comments of ansible/test/gce_tests.py. options: httphealthcheck_name: description: - the name identifier for the HTTP health check required: false default: null httphealthcheck_port: description: - the TCP port to use for HTTP health checking required: false default: 80 httphealthcheck_path: description: - the url path to use for HTTP health checking required: false default: "/" httphealthcheck_interval: description: - the duration in seconds between each health check request required: false default: 5 httphealthcheck_timeout: description: - the timeout in seconds before a request is considered a failed check required: false default: 5 httphealthcheck_unhealthy_count: description: - number of consecutive failed checks before marking a node unhealthy required: false default: 2 httphealthcheck_healthy_count: description: - number of consecutive successful checks before marking a node healthy required: false default: 2 httphealthcheck_host: description: - host header to pass through on HTTP check requests required: false default: null name: description: - name of the load-balancer resource required: false default: null protocol: description: - the protocol used for the load-balancer packet forwarding, tcp or udp required: false default: "tcp" choices: ['tcp', 'udp'] region: description: - the GCE region where the load-balancer is defined required: false choices: ["us-central1", "us-central2", "europe-west1"] external_ip: description: - the external static IPv4 (or auto-assigned) address for the LB required: false default: null port_range: description: - the port (range) to forward, e.g. 80 or 8000-8888 defaults to all ports required: false default: null members: description: - a list of zone/nodename pairs, e.g ['us-central1-a/www-a', ...] required: false aliases: ['nodes'] state: description: - desired state of the LB default: "present" choices: ["active", "present", "absent", "deleted"] aliases: [] requirements: [ "libcloud" ] author: Eric Johnson ''' EXAMPLES = ''' # Simple example of creating a new LB, adding members, and a health check - local_action: module: gce_lb name: testlb region: us-central1 members: ["us-central1-a/www-a", "us-central1-b/www-b"] httphealthcheck_name: hc httphealthcheck_port: 80 httphealthcheck_path: "/up" ''' import sys USER_AGENT_PRODUCT="Ansible-gce_lb" USER_AGENT_VERSION="v1beta15" try: from libcloud.compute.types import Provider from libcloud.compute.providers import get_driver from libcloud.loadbalancer.types import Provider as Provider_lb from libcloud.loadbalancer.providers import get_driver as get_driver_lb from libcloud.common.google import GoogleBaseError, QuotaExceededError, \ ResourceExistsError, ResourceNotFoundError _ = Provider.GCE except ImportError: print("failed=True " + \ "msg='libcloud with GCE support required for this module.'") sys.exit(1) # Load in the libcloud secrets file try: import secrets except ImportError: secrets = None ARGS = getattr(secrets, 'GCE_PARAMS', ()) KWARGS = getattr(secrets, 'GCE_KEYWORD_PARAMS', {}) if not ARGS or not 'project' in KWARGS: print("failed=True msg='Missing GCE connection " + \ "parameters in libcloud secrets file.'") sys.exit(1) def unexpected_error_msg(error): """Format error string based on passed in error.""" msg='Unexpected response: HTTP return_code[' msg+='%s], API error code[%s] and message: %s' % ( error.http_code, error.code, str(error.value)) return msg def main(): module = AnsibleModule( argument_spec = dict( httphealthcheck_name = dict(), httphealthcheck_port = dict(default=80), httphealthcheck_path = dict(default='/'), httphealthcheck_interval = dict(default=5), httphealthcheck_timeout = dict(default=5), httphealthcheck_unhealthy_count = dict(default=2), httphealthcheck_healthy_count = dict(default=2), httphealthcheck_host = dict(), name = dict(), protocol = dict(default='tcp'), region = dict(), external_ip = dict(), port_range = dict(), members = dict(type='list'), state = dict(default='present'), ) ) httphealthcheck_name = module.params.get('httphealthcheck_name') httphealthcheck_port = module.params.get('httphealthcheck_port') httphealthcheck_path = module.params.get('httphealthcheck_path') httphealthcheck_interval = module.params.get('httphealthcheck_interval') httphealthcheck_timeout = module.params.get('httphealthcheck_timeout') httphealthcheck_unhealthy_count = \ module.params.get('httphealthcheck_unhealthy_count') httphealthcheck_healthy_count = \ module.params.get('httphealthcheck_healthy_count') httphealthcheck_host = module.params.get('httphealthcheck_host') name = module.params.get('name') protocol = module.params.get('protocol') region = module.params.get('region') external_ip = module.params.get('external_ip') port_range = module.params.get('port_range') members = module.params.get('members') state = module.params.get('state') try: gce = get_driver(Provider.GCE)(*ARGS, **KWARGS) gce.connection.user_agent_append("%s/%s" % ( USER_AGENT_PRODUCT, USER_AGENT_VERSION)) gcelb = get_driver_lb(Provider_lb.GCE)(gce_driver=gce) gcelb.connection.user_agent_append("%s/%s" % ( USER_AGENT_PRODUCT, USER_AGENT_VERSION)) except Exception as e: module.fail_json(msg=unexpected_error_msg(e), changed=False) changed = False json_output = {'name': name, 'state': state} if not name and not httphealthcheck_name: module.fail_json(msg='Nothing to do, please specify a "name" ' + \ 'or "httphealthcheck_name" parameter', changed=False) if state in ['active', 'present']: # first, create the httphealthcheck if requested hc = None if httphealthcheck_name: json_output['httphealthcheck_name'] = httphealthcheck_name try: hc = gcelb.ex_create_healthcheck(httphealthcheck_name, host=httphealthcheck_host, path=httphealthcheck_path, port=httphealthcheck_port, interval=httphealthcheck_interval, timeout=httphealthcheck_timeout, unhealthy_threshold=httphealthcheck_unhealthy_count, healthy_threshold=httphealthcheck_healthy_count) changed = True except ResourceExistsError: hc = gce.ex_get_healthcheck(httphealthcheck_name) except Exception as e: module.fail_json(msg=unexpected_error_msg(e), changed=False) if hc is not None: json_output['httphealthcheck_host'] = hc.extra['host'] json_output['httphealthcheck_path'] = hc.path json_output['httphealthcheck_port'] = hc.port json_output['httphealthcheck_interval'] = hc.interval json_output['httphealthcheck_timeout'] = hc.timeout json_output['httphealthcheck_unhealthy_count'] = \ hc.unhealthy_threshold json_output['httphealthcheck_healthy_count'] = \ hc.healthy_threshold # create the forwarding rule (and target pool under the hood) lb = None if name: if not region: module.fail_json(msg='Missing required region name', changed=False) nodes = [] output_nodes = [] json_output['name'] = name # members is a python list of 'zone/inst' strings if members: for node in members: try: zone, node_name = node.split('/') nodes.append(gce.ex_get_node(node_name, zone)) output_nodes.append(node) except: # skip nodes that are badly formatted or don't exist pass try: if hc is not None: lb = gcelb.create_balancer(name, port_range, protocol, None, nodes, ex_region=region, ex_healthchecks=[hc], ex_address=external_ip) else: lb = gcelb.create_balancer(name, port_range, protocol, None, nodes, ex_region=region, ex_address=external_ip) changed = True except ResourceExistsError: lb = gcelb.get_balancer(name) except Exception as e: module.fail_json(msg=unexpected_error_msg(e), changed=False) if lb is not None: json_output['members'] = output_nodes json_output['protocol'] = protocol json_output['region'] = region json_output['external_ip'] = lb.ip json_output['port_range'] = lb.port hc_names = [] if 'healthchecks' in lb.extra: for hc in lb.extra['healthchecks']: hc_names.append(hc.name) json_output['httphealthchecks'] = hc_names if state in ['absent', 'deleted']: # first, delete the load balancer (forwarding rule and target pool) # if specified. if name: json_output['name'] = name try: lb = gcelb.get_balancer(name) gcelb.destroy_balancer(lb) changed = True except ResourceNotFoundError: pass except Exception as e: module.fail_json(msg=unexpected_error_msg(e), changed=False) # destroy the health check if specified if httphealthcheck_name: json_output['httphealthcheck_name'] = httphealthcheck_name try: hc = gce.ex_get_healthcheck(httphealthcheck_name) gce.ex_destroy_healthcheck(hc) changed = True except ResourceNotFoundError: pass except Exception as e: module.fail_json(msg=unexpected_error_msg(e), changed=False) json_output['changed'] = changed print json.dumps(json_output) sys.exit(0) # this is magic, see lib/ansible/module_common.py from ansible.module_utils.basic import * main()