1#!/usr/bin/python
2
3# Copyright (c) 2018 Catalyst Cloud Ltd.
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7
8__metaclass__ = type
9
10ANSIBLE_METADATA = {'metadata_version': '1.1',
11                    'status': ['preview'],
12                    'supported_by': 'community'}
13
14DOCUMENTATION = '''
15---
16module: os_pool
17short_description: Add/Delete a pool in the load balancing service from OpenStack Cloud
18extends_documentation_fragment: openstack
19version_added: "2.7"
20author: "Lingxian Kong (@lingxiankong)"
21description:
22   - Add or Remove a pool from the OpenStack load-balancer service.
23options:
24   name:
25     description:
26        - Name that has to be given to the pool
27     required: true
28   state:
29     description:
30       - Should the resource be present or absent.
31     choices: [present, absent]
32     default: present
33   loadbalancer:
34     description:
35        - The name or id of the load balancer that this pool belongs to.
36          Either loadbalancer or listener must be specified for pool creation.
37   listener:
38     description:
39        - The name or id of the listener that this pool belongs to.
40          Either loadbalancer or listener must be specified for pool creation.
41   protocol:
42     description:
43        - The protocol for the pool.
44     choices: [HTTP, HTTPS, PROXY, TCP, UDP]
45     default: HTTP
46   lb_algorithm:
47     description:
48        - The load balancing algorithm for the pool.
49     choices: [LEAST_CONNECTIONS, ROUND_ROBIN, SOURCE_IP]
50     default: ROUND_ROBIN
51   wait:
52     description:
53        - If the module should wait for the pool to be ACTIVE.
54     type: bool
55     default: 'yes'
56   timeout:
57     description:
58        - The amount of time the module should wait for the pool to get
59          into ACTIVE state.
60     default: 180
61   availability_zone:
62     description:
63       - Ignored. Present for backwards compatibility
64requirements: ["openstacksdk"]
65'''
66
67RETURN = '''
68id:
69    description: The pool UUID.
70    returned: On success when I(state) is 'present'
71    type: str
72    sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
73listener:
74    description: Dictionary describing the pool.
75    returned: On success when I(state) is 'present'
76    type: complex
77    contains:
78        id:
79            description: Unique UUID.
80            type: str
81            sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
82        name:
83            description: Name given to the pool.
84            type: str
85            sample: "test"
86        description:
87            description: The pool description.
88            type: str
89            sample: "description"
90        loadbalancers:
91            description: A list of load balancer IDs.
92            type: list
93            sample: [{"id": "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"}]
94        listeners:
95            description: A list of listener IDs.
96            type: list
97            sample: [{"id": "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"}]
98        members:
99            description: A list of member IDs.
100            type: list
101            sample: [{"id": "b32eef7e-d2a6-4ea4-a301-60a873f89b3b"}]
102        loadbalancer_id:
103            description: The load balancer ID the pool belongs to. This field is set when the pool doesn't belong to any listener in the load balancer.
104            type: str
105            sample: "7c4be3f8-9c2f-11e8-83b3-44a8422643a4"
106        listener_id:
107            description: The listener ID the pool belongs to.
108            type: str
109            sample: "956aa716-9c2f-11e8-83b3-44a8422643a4"
110        provisioning_status:
111            description: The provisioning status of the pool.
112            type: str
113            sample: "ACTIVE"
114        operating_status:
115            description: The operating status of the pool.
116            type: str
117            sample: "ONLINE"
118        is_admin_state_up:
119            description: The administrative state of the pool.
120            type: bool
121            sample: true
122        protocol:
123            description: The protocol for the pool.
124            type: str
125            sample: "HTTP"
126        lb_algorithm:
127            description: The load balancing algorithm for the pool.
128            type: str
129            sample: "ROUND_ROBIN"
130'''
131
132EXAMPLES = '''
133# Create a pool, wait for the pool to be active.
134- os_pool:
135    cloud: mycloud
136    endpoint_type: admin
137    state: present
138    name: test-pool
139    loadbalancer: test-loadbalancer
140    protocol: HTTP
141    lb_algorithm: ROUND_ROBIN
142
143# Delete a pool
144- os_pool:
145    cloud: mycloud
146    endpoint_type: admin
147    state: absent
148    name: test-pool
149'''
150
151import time
152
153from ansible.module_utils.basic import AnsibleModule
154from ansible.module_utils.openstack import openstack_full_argument_spec, \
155    openstack_module_kwargs, openstack_cloud_from_module
156
157
158def _wait_for_pool_status(module, cloud, pool_id, status, failures,
159                          interval=5):
160    timeout = module.params['timeout']
161
162    total_sleep = 0
163    if failures is None:
164        failures = []
165
166    while total_sleep < timeout:
167        pool = cloud.load_balancer.get_pool(pool_id)
168        provisioning_status = pool.provisioning_status
169        if provisioning_status == status:
170            return pool
171        if provisioning_status in failures:
172            module.fail_json(
173                msg="pool %s transitioned to failure state %s" %
174                    (pool_id, provisioning_status)
175            )
176
177        time.sleep(interval)
178        total_sleep += interval
179
180    module.fail_json(
181        msg="timeout waiting for pool %s to transition to %s" %
182            (pool_id, status)
183    )
184
185
186def main():
187    argument_spec = openstack_full_argument_spec(
188        name=dict(required=True),
189        state=dict(default='present', choices=['absent', 'present']),
190        loadbalancer=dict(default=None),
191        listener=dict(default=None),
192        protocol=dict(default='HTTP',
193                      choices=['HTTP', 'HTTPS', 'TCP', 'UDP', 'PROXY']),
194        lb_algorithm=dict(
195            default='ROUND_ROBIN',
196            choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP']
197        )
198    )
199    module_kwargs = openstack_module_kwargs(
200        mutually_exclusive=[['loadbalancer', 'listener']]
201    )
202    module = AnsibleModule(argument_spec, **module_kwargs)
203    sdk, cloud = openstack_cloud_from_module(module)
204
205    loadbalancer = module.params['loadbalancer']
206    listener = module.params['listener']
207
208    try:
209        changed = False
210        pool = cloud.load_balancer.find_pool(name_or_id=module.params['name'])
211
212        if module.params['state'] == 'present':
213            if not pool:
214                loadbalancer_id = None
215                if not (loadbalancer or listener):
216                    module.fail_json(
217                        msg="either loadbalancer or listener must be provided"
218                    )
219
220                if loadbalancer:
221                    lb = cloud.load_balancer.find_load_balancer(loadbalancer)
222                    if not lb:
223                        module.fail_json(msg='load balancer %s is not '
224                                             'found' % loadbalancer)
225                    loadbalancer_id = lb.id
226
227                listener_id = None
228                if listener:
229                    listener_ret = cloud.load_balancer.find_listener(listener)
230                    if not listener_ret:
231                        module.fail_json(msg='listener %s is not found'
232                                             % listener)
233                    listener_id = listener_ret.id
234
235                pool = cloud.load_balancer.create_pool(
236                    name=module.params['name'],
237                    loadbalancer_id=loadbalancer_id,
238                    listener_id=listener_id,
239                    protocol=module.params['protocol'],
240                    lb_algorithm=module.params['lb_algorithm']
241                )
242                changed = True
243
244                if not module.params['wait']:
245                    module.exit_json(changed=changed,
246                                     pool=pool.to_dict(),
247                                     id=pool.id)
248
249            if module.params['wait']:
250                pool = _wait_for_pool_status(module, cloud, pool.id, "ACTIVE",
251                                             ["ERROR"])
252
253            module.exit_json(changed=changed, pool=pool.to_dict(),
254                             id=pool.id)
255
256        elif module.params['state'] == 'absent':
257            if pool:
258                cloud.load_balancer.delete_pool(pool)
259                changed = True
260
261            module.exit_json(changed=changed)
262    except sdk.exceptions.OpenStackCloudException as e:
263        module.fail_json(msg=str(e), extra_data=e.extra_data)
264
265
266if __name__ == "__main__":
267    main()
268