From 018a8eb8ee69b161c95f010ba496477f905c885e Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Thu, 9 Aug 2018 03:21:44 +1200 Subject: [PATCH] Add os_loadbalancer module for OpenStack Octavia service (#42552) --- .../cloud/openstack/os_loadbalancer.py | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 lib/ansible/modules/cloud/openstack/os_loadbalancer.py diff --git a/lib/ansible/modules/cloud/openstack/os_loadbalancer.py b/lib/ansible/modules/cloud/openstack/os_loadbalancer.py new file mode 100644 index 0000000000..4db1acda0b --- /dev/null +++ b/lib/ansible/modules/cloud/openstack/os_loadbalancer.py @@ -0,0 +1,258 @@ +#!/usr/bin/python + +# Copyright (c) 2018 Catalyst Cloud Ltd. +# 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 + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: os_loadbalancer +short_description: Add/Delete load balancer from OpenStack Cloud +extends_documentation_fragment: openstack +version_added: "2.7" +author: "Lingxian Kong (@lingxiankong)" +description: + - Add or Remove load balancer from the OpenStack load-balancer service. +options: + name: + description: + - Name that has to be given to the load balancer + required: true + state: + description: + - Should the resource be present or absent. + choices: [present, absent] + default: present + vip_network: + description: + - The name or id of the network for the virtual IP of the load balancer. + One of vip_network, vip_subnet, or vip_port must be specified. + vip_subnet: + description: + - The name or id of the subnet for the virtual IP of the load balancer. + One of vip_network, vip_subnet, or vip_port must be specified. + vip_port: + description: + - The name or id of the load balancer virtual IP port. One of + vip_network, vip_subnet, or vip_port must be specified. + vip_address: + description: + - IP address of the load balancer virtual IP. + wait: + description: + - If the module should wait for the load balancer to be created. + type: bool + default: 'yes' + timeout: + description: + - The amount of time the module should wait for the load balancer to get + into ACTIVE state. + default: 180 + availability_zone: + description: + - Ignored. Present for backwards compatibility +requirements: ["openstacksdk"] +''' + +RETURN = ''' +id: + description: Unique UUID. + returned: success + type: string +name: + description: Name given to the load balancer. + returned: success + type: string +vip_network_id: + description: Network ID the load balancer virutal IP port belongs in. + returned: success + type: string +vip_subnet_id: + description: Subnet ID the load balancer virutal IP port belongs in. + returned: success + type: string +vip_port_id: + description: The load balancer virutal IP port ID. + returned: success + type: string +vip_address: + description: The load balancer virutal IP address. + returned: success + type: string +provisioning_status: + description: The provisioning status of the load balancer. + returned: success + type: string +operating_status: + description: The operating status of the load balancer. + returned: success + type: string +is_admin_state_up: + description: The administrative state of the load balancer. + returned: success + type: bool +listeners: + description: The associated listener IDs, if any. + returned: success + type: list +pools: + description: The associated pool IDs, if any. + returned: success + type: list +''' + +EXAMPLES = ''' +# Create a load balancer by specifying the VIP subnet. +- os_loadbalancer: + auth: + auth_url: https://identity.example.com + username: admin + password: passme + project_name: admin + state: present + name: my_lb + vip_subnet: my_subnet + timeout: 150 + +# Create a load balancer by specifying the VIP network and the IP address. +- os_loadbalancer: + auth: + auth_url: https://identity.example.com + username: admin + password: passme + project_name: admin + state: present + name: my_lb + vip_network: my_network + vip_address: 192.168.0.11 +''' + +import time + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.openstack import openstack_full_argument_spec, \ + openstack_module_kwargs, openstack_cloud_from_module + + +def _lb_wait_for_status(module, cloud, lb, status, failures, interval=5): + """Wait for load balancer to be in a particular provisioning status.""" + timeout = module.params['timeout'] + + total_sleep = 0 + if failures is None: + failures = [] + + while total_sleep < timeout: + lb = cloud.load_balancer.get_load_balancer(lb.id) + if lb.provisioning_status == status: + return None + if lb.provisioning_status in failures: + module.fail_json( + msg="Load Balancer %s transitioned to failure state %s" % + (lb.id, lb.provisioning_status) + ) + + time.sleep(interval) + total_sleep += interval + + module.fail_json( + msg="Timeout waiting for Load Balancer %s to transition to %s" % + (lb.id, status) + ) + + +def main(): + argument_spec = openstack_full_argument_spec( + name=dict(required=True), + state=dict(default='present', choices=['absent', 'present']), + vip_network=dict(required=False), + vip_subnet=dict(required=False), + vip_port=dict(required=False), + vip_address=dict(required=False), + ) + module_kwargs = openstack_module_kwargs( + required_one_of=[ + ['vip_network', 'vip_subnet', 'vip_port'], + ] + ) + module = AnsibleModule(argument_spec, **module_kwargs) + + sdk, cloud = openstack_cloud_from_module(module) + vip_network_id = None + vip_subnet_id = None + vip_port_id = None + + try: + changed = False + lb = cloud.load_balancer.find_load_balancer( + name_or_id=module.params['name']) + + if module.params['state'] == 'present': + if not lb: + if module.params['vip_network']: + network = cloud.get_network(module.params['vip_network']) + if not network: + module.fail_json( + msg='network %s is not found' % + module.params['vip_network'] + ) + vip_network_id = network.id + if module.params['vip_subnet']: + subnet = cloud.get_subnet(module.params['vip_subnet']) + if not subnet: + module.fail_json( + msg='subnet %s is not found' % + module.params['vip_subnet'] + ) + vip_subnet_id = subnet.id + if module.params['vip_port']: + port = cloud.get_port(module.params['vip_port']) + if not port: + module.fail_json( + msg='port %s is not found' % + module.params['vip_port'] + ) + vip_port_id = port.id + + lb = cloud.load_balancer.create_load_balancer( + name=module.params['name'], + vip_network_id=vip_network_id, + vip_subnet_id=vip_subnet_id, + vip_port_id=vip_port_id, + vip_address=module.params['vip_address'], + ) + changed = True + + if not module.params['wait']: + module.exit_json(changed=changed, + loadbalancer=lb.to_dict(), id=lb.id) + + if module.params['wait']: + _lb_wait_for_status(module, cloud, lb, "ACTIVE", ["ERROR"]) + + module.exit_json(changed=changed, loadbalancer=lb.to_dict(), + id=lb.id) + elif module.params['state'] == 'absent': + if not lb: + changed = False + else: + # Deleting load balancer with `cascade=False` does not make + # sense because the deletion will always fail if there are + # sub-resources. + cloud.load_balancer.delete_load_balancer(lb, cascade=True) + changed = True + + module.exit_json(changed=changed) + except sdk.exceptions.OpenStackCloudException as e: + module.fail_json(msg=str(e), extra_data=e.extra_data) + + +if __name__ == "__main__": + main()