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_member 17short_description: Add/Delete a member for a pool in load balancer from OpenStack Cloud 18extends_documentation_fragment: openstack 19version_added: "2.7" 20author: "Lingxian Kong (@lingxiankong)" 21description: 22 - Add or Remove a member for a pool from the OpenStack load-balancer service. 23options: 24 name: 25 description: 26 - Name that has to be given to the member 27 required: true 28 state: 29 description: 30 - Should the resource be present or absent. 31 choices: [present, absent] 32 default: present 33 pool: 34 description: 35 - The name or id of the pool that this member belongs to. 36 required: true 37 protocol_port: 38 description: 39 - The protocol port number for the member. 40 default: 80 41 address: 42 description: 43 - The IP address of the member. 44 subnet_id: 45 description: 46 - The subnet ID the member service is accessible from. 47 wait: 48 description: 49 - If the module should wait for the load balancer to be ACTIVE. 50 type: bool 51 default: 'yes' 52 timeout: 53 description: 54 - The amount of time the module should wait for the load balancer to get 55 into ACTIVE state. 56 default: 180 57 availability_zone: 58 description: 59 - Ignored. Present for backwards compatibility 60requirements: ["openstacksdk"] 61''' 62 63RETURN = ''' 64id: 65 description: The member UUID. 66 returned: On success when I(state) is 'present' 67 type: str 68 sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69" 69member: 70 description: Dictionary describing the member. 71 returned: On success when I(state) is 'present' 72 type: complex 73 contains: 74 id: 75 description: Unique UUID. 76 type: str 77 sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69" 78 name: 79 description: Name given to the member. 80 type: str 81 sample: "test" 82 description: 83 description: The member description. 84 type: str 85 sample: "description" 86 provisioning_status: 87 description: The provisioning status of the member. 88 type: str 89 sample: "ACTIVE" 90 operating_status: 91 description: The operating status of the member. 92 type: str 93 sample: "ONLINE" 94 is_admin_state_up: 95 description: The administrative state of the member. 96 type: bool 97 sample: true 98 protocol_port: 99 description: The protocol port number for the member. 100 type: int 101 sample: 80 102 subnet_id: 103 description: The subnet ID the member service is accessible from. 104 type: str 105 sample: "489247fa-9c25-11e8-9679-00224d6b7bc1" 106 address: 107 description: The IP address of the backend member server. 108 type: str 109 sample: "192.168.2.10" 110''' 111 112EXAMPLES = ''' 113# Create a member, wait for the member to be created. 114- os_member: 115 cloud: mycloud 116 endpoint_type: admin 117 state: present 118 name: test-member 119 pool: test-pool 120 address: 192.168.10.3 121 protocol_port: 8080 122 123# Delete a listener 124- os_member: 125 cloud: mycloud 126 endpoint_type: admin 127 state: absent 128 name: test-member 129 pool: test-pool 130''' 131 132import time 133 134from ansible.module_utils.basic import AnsibleModule 135from ansible.module_utils.openstack import openstack_full_argument_spec, \ 136 openstack_module_kwargs, openstack_cloud_from_module 137 138 139def _wait_for_member_status(module, cloud, pool_id, member_id, status, 140 failures, interval=5): 141 timeout = module.params['timeout'] 142 143 total_sleep = 0 144 if failures is None: 145 failures = [] 146 147 while total_sleep < timeout: 148 member = cloud.load_balancer.get_member(member_id, pool_id) 149 provisioning_status = member.provisioning_status 150 if provisioning_status == status: 151 return member 152 if provisioning_status in failures: 153 module.fail_json( 154 msg="Member %s transitioned to failure state %s" % 155 (member_id, provisioning_status) 156 ) 157 158 time.sleep(interval) 159 total_sleep += interval 160 161 module.fail_json( 162 msg="Timeout waiting for member %s to transition to %s" % 163 (member_id, status) 164 ) 165 166 167def main(): 168 argument_spec = openstack_full_argument_spec( 169 name=dict(required=True), 170 state=dict(default='present', choices=['absent', 'present']), 171 pool=dict(required=True), 172 address=dict(default=None), 173 protocol_port=dict(default=80, type='int'), 174 subnet_id=dict(default=None), 175 ) 176 module_kwargs = openstack_module_kwargs() 177 module = AnsibleModule(argument_spec, **module_kwargs) 178 sdk, cloud = openstack_cloud_from_module(module) 179 name = module.params['name'] 180 pool = module.params['pool'] 181 182 try: 183 changed = False 184 185 pool_ret = cloud.load_balancer.find_pool(name_or_id=pool) 186 if not pool_ret: 187 module.fail_json(msg='pool %s is not found' % pool) 188 189 pool_id = pool_ret.id 190 member = cloud.load_balancer.find_member(name, pool_id) 191 192 if module.params['state'] == 'present': 193 if not member: 194 member = cloud.load_balancer.create_member( 195 pool_ret, 196 address=module.params['address'], 197 name=name, 198 protocol_port=module.params['protocol_port'], 199 subnet_id=module.params['subnet_id'] 200 ) 201 changed = True 202 203 if not module.params['wait']: 204 module.exit_json(changed=changed, 205 member=member.to_dict(), 206 id=member.id) 207 208 if module.params['wait']: 209 member = _wait_for_member_status(module, cloud, pool_id, 210 member.id, "ACTIVE", 211 ["ERROR"]) 212 213 module.exit_json(changed=changed, member=member.to_dict(), 214 id=member.id) 215 216 elif module.params['state'] == 'absent': 217 if member: 218 cloud.load_balancer.delete_member(member, pool_ret) 219 changed = True 220 221 module.exit_json(changed=changed) 222 except sdk.exceptions.OpenStackCloudException as e: 223 module.fail_json(msg=str(e), extra_data=e.extra_data) 224 225 226if __name__ == "__main__": 227 main() 228