lib/ansible/modules/cloud/alicloud/ali_slb_server.py (220 lines of code) (raw):

#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com.com> # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # # 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 http://www.gnu.org/licenses/. from __future__ import (absolute_import, division, print_function) __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = """ --- module: ali_slb_server short_description: Add or remove a list of backend servers to/from a specified SLB description: - Returns information about the backend servers. Will be marked changed when called only if state is changed. options: state: description: - Add or remove backend server to/from a specified slb default: 'present' choices: ['present', 'absent'] type: str load_balancer_id: description: - The unique ID of a Server Load Balancer instance required: true aliases: ['lb_id'] type: str backend_servers: description: - List of hash/dictionary of backend servers to add or set in when C(state=present) - List IDs of backend servers which in the load balancer when C(state=absent) required: true aliases: ['servers'] type: list elements: dict suboptions: server_id: description: - The ID of ecs instance which is added into load balancer. - One of I(server_id) and I(server_ids) required. server_ids: description: - If you have multiple servers to add and they have the same weight, type, you can use the server_ids parameter, which is a list of ids. - One of I(server_id) and I(server_ids) required. weight: description: - The weight of backend server in the load balancer. choices: [0~100] default: 100 type: description: - The type of backend server in the load balancer. choices: ['ecs', 'eni'] default: 'ecs' requirements: - "python >= 3.6" - "footmark >= 1.19.0" extends_documentation_fragment: - alicloud author: - "He Guimin (@xiaozhu36)" """ EXAMPLES = ''' # Provisioning new add or remove Backend Server from SLB # Basic example to add backend server to load balancer instance - name: add backend server hosts: localhost connection: local vars: load_balancer_id: lb-abcd1234 tasks: - name: add backend server ali_slb_server: load_balancer_id: '{{ load_balancer_id }}' backend_servers: - server_id: i-abcd1234 weight: 70 - server_id: i-abce1245 #Basic example to set backend server of load balancer instance - name: set backend server hosts: localhost connection: local vars: alicloud_access_key: <your-alicloud-access-key-id> alicloud_secret_key: <your-alicloud-access-secret-key> tasks: - name: set backend server ali_slb_server: alicloud_access_key: '{{ alicloud_access_key }}' alicloud_secret_key: '{{ alicloud_secret_key }}' load_balancer_id: lb-abcd1234 backend_servers: - server_id: i-abcd1234 weight: 50 - server_id: i-abcd1234 weight: 80 #Basic example to remove backend servers from load balancer instance - name: remove backend servers hosts: localhost connection: local tasks: - name: remove backend servers ali_slb_server: load_balancer_id: lb-abcd1234 state: absent backend_servers: - i-abcd1234 - i-abcd1234 ''' RETURN = ''' load_balancer_id: description: ID of the load balancer. returned: when success type: str sample: "lb-2zeyfm5a14c9ffxvxmvco" "backend_servers": description: Details about the backened-servers that were added. returned: when success type: list sample: [ { "health_status": "abnormal", "id": "i-2zeau2evvbnwufq0fa7q" }, { "health_status": "abnormal", "id": "i-2zehasnejqr6g6agys5a" } ] ''' from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.alicloud_ecs import ecs_argument_spec, slb_connect try: from footmark.exception import SLBResponseError HAS_FOOTMARK = True except ImportError: HAS_FOOTMARK = False def parse_server_ids(servers): parse_server = [] if servers: for s in servers: if "server_ids" in s: ids = s.pop("server_ids") for id in ids: server = {"server_id": id} server.update(s) parse_server.append(server) else: parse_server.append(s) return parse_server def get_backen_server_weight(server): """ Retrieves instance information from an instance ID and returns it as a dictionary """ return {'id': server.id, 'weight': server.weight} def get_backen_server_status(server): """ Retrieves instance information from an instance ID and returns it as a dictionary """ return {'id': server.id, 'health_status': server.status} def add_set_backend_servers(module, slb, load_balancer_id, backend_servers): """ Add and/or Set backend servers to an slb :param module: Ansible module object :param slb: authenticated slb connection object :param load_balancer_id: load balancer id to add/set backend servers to :param backend_servers: backend severs information to add/set :return: returns changed state, current added backend servers and custom message. """ changed = False server_id_param = 'server_id' backend_servers_to_set = [] backend_servers_to_add = [] result = [] current_backend_servers = [] changed_after_add = False changed_after_set = False try: load_balancer_info = slb.describe_load_balancer_attribute(load_balancer_id=load_balancer_id) # Verifying if server load balancer Object is present if load_balancer_info: existing_backend_servers = str(load_balancer_info.backend_servers['backend_server']) # Segregating existing backend servers from new backend servers from the provided backend servers for backend_server in backend_servers: backend_server_string = backend_server[server_id_param] + '\'' if backend_server_string in existing_backend_servers: backend_servers_to_set.append(backend_server) else: backend_servers_to_add.append(backend_server) # Adding new backend servers if provided if len(backend_servers_to_add) > 0: current_backend_servers.extend(slb.add_backend_servers(load_balancer_id=load_balancer_id, backend_servers=backend_servers_to_add)) changed = True # Setting exisiting backend servers if provided if len(backend_servers_to_set) > 0: backen_servers = slb.set_backend_servers(load_balancer_id=load_balancer_id, backend_servers=backend_servers_to_set) changed = True # If backend server result after set action is available then clearing actual list # and adding new result to it. if len(backen_servers) > 0: # Below operation clears list using slice operation current_backend_servers[:] = [] current_backend_servers.extend(backen_servers) if changed_after_add or changed_after_set: changed = True else: module.fail_json(msg="Could not find provided load balancer instance") except SLBResponseError as ex: module.fail_json(msg='Unable to add backend servers, error: {0}'.format(ex)) return changed, current_backend_servers def remove_backend_servers(module, slb, load_balancer_id, backend_servers): """ Remove backend servers from an slb :param module: Ansible module object :param slb: authenticated slb connection object :param load_balancer_id: load balancer id to remove backend servers from :param backend_servers: list of backend server ids to remove from slb :return: returns changed state, current added backend servers and custom message. """ changed = False try: backend_servers = slb.remove_backend_servers(load_balancer_id=load_balancer_id, backend_server_ids=backend_servers) changed = True except SLBResponseError as ex: module.fail_json(msg='Unable to remove backend servers, error: {0}'.format(ex)) return changed, backend_servers def describe_backend_servers_health_status(module, slb, load_balancer_id=None, listener_ports=None): """ Describe health status of added backend servers of an slb :param module: Ansible module object :param slb: authenticated slb connection object :param load_balancer_id: load balancer id to remove backend servers from :param listener_ports: list of ports to for which backend server health status is required :return: returns backend servers health status and custom message """ backend_servers_health_status = [] try: if listener_ports: for port in listener_ports: backend_server = slb.describe_backend_servers_health_status(load_balancer_id=load_balancer_id, port=port) backend_servers_health_status.extend(backend_server) else: backend_servers_health_status = slb.describe_backend_servers_health_status(load_balancer_id=load_balancer_id) except SLBResponseError as ex: module.fail_json(msg='Unable to describe backend servers health status, error: {0}'.format(ex)) return backend_servers_health_status def validate_backend_server_info(module, backend_servers, default_weight): """ Validate backend server information provided by user for add, set and remove action :param module: Ansible module object :param backend_servers: backend severs information to validate (list of dictionaries or string) :param default_weight: assigns default weight, if provided, for a backend server to set/add """ VALID_PARAMS = ['server_id', 'server_ids', 'weight', 'type'] for backend_server in backend_servers: if not isinstance(backend_server, dict): module.fail_json(msg='Invalid backend_servers parameter type [%s] for state=present.' % type(backend_server)) for k in backend_server: if k not in VALID_PARAMS: module.fail_json(msg='Invalid backend_server parameter {}'.format(k)) if "server_id" not in backend_server and "server_ids" not in backend_server: module.fail_json(msg="'server_id' or 'server_ids': required field is set") # verifying weight parameter for non numeral string and limit validation if "weight" in backend_server: try: w = int(backend_server['weight']) if w < 0 or w > 100: module.fail_json(msg="'weight': field value is invalid. Expect to [0-100].") except Exception as e: module.fail_json(msg="'weight': field value is invalid. Expect to positive integer [0-100].") def get_verify_listener_ports(module, listener_ports=None): """ Validate and get listener ports :param module: Ansible module object :param listener_ports: list of ports to for which backend server health status is required :return: formatted listener ports """ if listener_ports: if len(listener_ports) > 0: for port in listener_ports: try: port = int(port) except Exception as ex: module.fail_json(msg='Invalid port value') else: listener_ports = None return listener_ports def main(): argument_spec = ecs_argument_spec() argument_spec.update(dict( state=dict(choices=['present', 'absent'], default='present', type='str'), backend_servers=dict(required=True, type='list', elements='dict', aliases=['servers']), load_balancer_id=dict(required=True, aliases=['lb_id'], type='str'), )) # handling region parameter which is not required by this module del argument_spec['alicloud_region'] module = AnsibleModule(argument_spec=argument_spec) if HAS_FOOTMARK is False: module.fail_json(msg="'footmark' is required for the module ali_slb_server. " "Please install 'footmark' by using 'sudo pip install footmark'.") # handling region parameter which is required by common utils file to login but not required by this module module.params['alicloud_region'] = 'cn-hangzhou' slb = slb_connect(module) state = module.params['state'] backend_servers = parse_server_ids(module.params['backend_servers']) load_balancer_id = module.params['load_balancer_id'] if state == 'present': if len(backend_servers) > 0: validate_backend_server_info(module, backend_servers, 100) changed, current_backend_servers = add_set_backend_servers(module, slb, load_balancer_id, backend_servers) result_servers = [] for server in current_backend_servers: result_servers.append(get_backen_server_weight(server)) module.exit_json(changed=changed, backend_servers=result_servers, load_balancer_id=load_balancer_id) else: module.fail_json(msg='backend servers information is mandatory to state=present') if len(backend_servers) > 0: if not isinstance(backend_servers, list): module.fail_json(msg='Invalid backend_server parameter type [%s] for state=absent.' % type(backend_servers)) changed, backend_servers = remove_backend_servers(module, slb, load_balancer_id, backend_servers) result_servers = [] for server in backend_servers: result_servers.append(get_backen_server_weight(server)) module.exit_json(changed=changed, backend_servers=result_servers, load_balancer_id=load_balancer_id) else: module.fail_json(msg='backend server ID(s) information is mandatory to state=absent') # elif state == 'list': # # if load_balancer_id: # # listener_ports = get_verify_listener_ports(module, listener_ports) # # backend_servers = describe_backend_servers_health_status(module, slb, load_balancer_id=load_balancer_id, # listener_ports=listener_ports) # # result_servers = [] # for server in backend_servers: # result_servers.append(get_backen_server_status(server)) # module.exit_json(changed=changed, backend_servers=result_servers, load_balancer_id=load_balancer_id) # else: # module.fail_json(msg='load balancer id is mandatory to perform action') if __name__ == '__main__': main()