diff --git a/lib/ansible/modules/net_tools/infinity/__init__.py b/lib/ansible/modules/net_tools/infinity/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/ansible/modules/net_tools/infinity/infinity.py b/lib/ansible/modules/net_tools/infinity/infinity.py new file mode 100644 index 0000000000..78bb02fbc5 --- /dev/null +++ b/lib/ansible/modules/net_tools/infinity/infinity.py @@ -0,0 +1,572 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (c), meiliu@fusionlayer.com, 2017 +# +# 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. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = """ +module: infinity +short_description: "manage Infinity IPAM using Rest API" +description: + - "Manage Infinity IPAM using REST API" +version_added: "2.4" +author: + - "Meirong Liu" +options: + server_ip: + description: + - Infinity server_ip with IP address + required: true + username: + description: + - Username to access Infinity + - The user must have Rest API privileges + required: true + password: + description: + - Infinity password + required: true + action: + description: + - Action to perform + required: true + choices: + - reserve_next_available_ip + - release_ip + - delete_network + - add_network + - reserve_network + - release_network + - get_network_id + + network_id: + description: + - Network ID + default: '' + ip_address: + description: + - IP Address for a reservation or a release + default: '' + network_address: + description: + - Network address with CIDR format (e.g., 192.168.310.0) + required: false + default: "" + network_size: + description: + - Network bitmask (e.g. 255.255.255.220) or CIDR format (e.g., /26) + default: '' + network_name: + description: + - The name of a network + default: '' + network_location: + description: + - the parent network id for a given network + default: -1 + network_type: + description: + - Network type defined by Infinity + choices: + - lan + - shared_lan + - supernet + default: "lan" + network_family: + description: + - Network family defined by Infinity, e.g. IPv4, IPv6 and Dual stack + choices: + - 4 + - 6 + - dual + default: "4" + +""" + + +EXAMPLES = """ +--- +- hosts: localhost + connection: local + strategy: debug + tasks: + - name: Reserve network into Infinity IPAM + infinity: + server_ip: "80.75.107.12" + username: "username" + password: "password" + action: "reserve_network" + network_name: "reserve_new_ansible_network" + network_family: "4" + network_type: 'lan' + network_id: "1201" + network_size: "/28" + register: infinity + +""" + +RETURN = """ +network_id: + description: id for a given network + returned: success + type: string + sample: '1501' +ip_info: + description: when reserve next available ip address from a network, the ip address info ) is returned. + returned: success + type: string + sample: '{"address": "192.168.10.3", "hostname": "", "FQDN": "", "domainname": "", "id": 3229}' +network_info: + description: when reserving a LAN network from a Infinity supernet by providing network_size, the information about the reserved network is returned. + returned: success + type: string + sample: {"network_address": "192.168.10.32/28","network_family": "4", "network_id": 3102, + "network_size": null,"description": null,"network_location": "3085", + "ranges": { "id": 0, "name": null,"first_ip": null,"type": null,"last_ip": null}, + "network_type": "lan","network_name": "'reserve_new_ansible_network'"} +""" + + +from ansible.module_utils.basic import json +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url + + +class Infinity(object): + """ + Class for manage REST API calls with the Infinity. + """ + + def __init__(self, module, server_ip, username, password): + self.module = module + self.auth_user = username + self.auth_pass = password + self.base_url = "https://%s/rest/v1/" % (str(server_ip)) + + def _get_api_call_ansible_handler( + self, + method='get', + resource_url='', + stat_codes=[200], + params=None, + payload_data=None): + """ + Perform the HTTPS request by using anible get/delete method + """ + request_url = str(self.base_url) + str(resource_url) + response = None + headers = {'Content-Type': 'application/json'} + if not request_url: + self.module.exit_json( + msg="When sending Rest api call , the resource URL is empty, please check.") + if payload_data and not isinstance(payload_data, str): + payload_data = json.dumps(payload_data) + response_raw = open_url( + str(request_url), + method=method, + timeout=20, + headers=headers, + url_username=self.auth_user, + url_password=self.auth_pass, + validate_certs=False, + force_basic_auth=True, + data=payload_data) + + response = response_raw.read() + payload = '' + if response_raw.code not in stat_codes: + self.module.exit_json( + changed=False, + meta=" openurl response_raw.code show error and error code is %r" % + (response_raw.code)) + else: + if isinstance(response, str) and len(response) > 0: + payload = response + elif method.lower() == 'delete' and response_raw.code == 204: + payload = 'Delete is done.' + if isinstance(payload, dict) and "text" in payload: + self.module.exit_json( + changed=False, + meta="when calling rest api, returned data is not json ") + raise Exception(payload["text"]) + return payload + + # --------------------------------------------------------------------------- + # get_network() + # --------------------------------------------------------------------------- + def get_network(self, network_id, network_name, limit=-1): + """ + Search network_name inside Infinity by using rest api + Network id or network_name needs to be provided + return the details of a given with given network_id or name + """ + if network_name is None and network_id is None: + self.module.exit_json( + msg="You must specify one of the options 'network_name' or 'network_id'.") + method = "get" + resource_url = '' + params = {} + response = None + if network_id: + resource_url = "networks/" + str(network_id) + response = self._get_api_call_ansible_handler(method, resource_url) + if network_id is None and network_name: + method = "get" + resource_url = "search" + params = {"query": json.dumps( + {"name": network_name, "type": "network"})} + response = self._get_api_call_ansible_handler( + method, resource_url, payload_data=json.dumps(params)) + if response and isinstance(response, str): + response = json.loads(response) + if response and isinstance(response, list) and len( + response) > 1 and limit == 1: + response = response[0] + response = json.dumps(response) + return response + + # --------------------------------------------------------------------------- + # get_network_id() + # --------------------------------------------------------------------------- + def get_network_id(self, network_name="", network_type='lan'): + """ + query network_id from Infinity via rest api based on given network_name + """ + method = 'get' + resource_url = 'search' + response = None + if network_name is None: + self.module.exit_json( + msg="You must specify the option 'network_name'") + params = {"query": json.dumps( + {"name": network_name, "type": "network"})} + response = self._get_api_call_ansible_handler( + method, resource_url, payload_data=json.dumps(params)) + network_id = "" + if response and isinstance(response, str): + response = json.loads(response) + if response and isinstance(response, list): + response = response[0] + network_id = response['id'] + return network_id + + # --------------------------------------------------------------------------- + # reserve_next_available_ip() + # --------------------------------------------------------------------------- + def reserve_next_available_ip(self, network_id=""): + """ + Reserve ip address via Infinity by using rest api + network_id: the id of the network that users would like to reserve network from + return the next available ip address from that given network + """ + method = "post" + resource_url = '' + response = None + ip_info = '' + if not network_id: + self.module.exit_json( + msg="You must specify the option 'network_id'.") + if network_id: + resource_url = "networks/" + str(network_id) + "/reserve_ip" + response = self._get_api_call_ansible_handler(method, resource_url) + if response and response.find( + "[") >= 0 and response.find("]") >= 0: + start_pos = response.find("{") + end_pos = response.find("}") + ip_info = response[start_pos: (end_pos + 1)] + return ip_info + + # ------------------------- + # release_ip() + # ------------------------- + def release_ip(self, network_id="", ip_address=""): + """ + Reserve ip address via Infinity by using rest api + """ + method = "get" + resource_url = '' + response = None + if ip_address is None or network_id is None: + self.module.exit_json( + msg="You must specify those two options: 'network_id' and 'ip_address'.") + + resource_url = "networks/" + str(network_id) + "/children" + response = self._get_api_call_ansible_handler(method, resource_url) + if not response: + self.module.exit_json( + msg="There is an error in release ip %s from network %s." % + (ip_address, network_id)) + + ip_list = json.loads(response) + ip_idlist = [] + for ip_item in ip_list: + ip_id = ip_item['id'] + ip_idlist.append(ip_id) + deleted_ip_id = '' + for ip_id in ip_idlist: + ip_response = '' + resource_url = "ip_addresses/" + str(ip_id) + ip_response = self._get_api_call_ansible_handler( + method, + resource_url, + stat_codes=[200]) + if ip_response and json.loads( + ip_response)['address'] == str(ip_address): + deleted_ip_id = ip_id + break + if deleted_ip_id: + method = 'delete' + resource_url = "ip_addresses/" + str(deleted_ip_id) + response = self._get_api_call_ansible_handler( + method, resource_url, stat_codes=[204]) + else: + self.module.exit_json( + msg=" When release ip, could not find the ip address %r from the given network %r' ." % + (ip_address, network_id)) + + return response + + # ------------------- + # delete_network() + # ------------------- + def delete_network(self, network_id="", network_name=""): + """ + delete network from Infinity by using rest api + """ + method = 'delete' + resource_url = '' + response = None + if network_id is None and network_name is None: + self.module.exit_json( + msg="You must specify one of those options: 'network_id','network_name' .") + if network_id is None and network_name: + network_id = self.get_network_id(network_name=network_name) + if network_id: + resource_url = "networks/" + str(network_id) + response = self._get_api_call_ansible_handler( + method, resource_url, stat_codes=[204]) + return response + + # reserve_network() + # --------------------------------------------------------------------------- + def reserve_network(self, network_id="", + reserved_network_name="", reserved_network_description="", + reserved_network_size="", reserved_network_family='4', + reserved_network_type='lan', reserved_network_address="",): + """ + Reserves the first available network of specified size from a given supernet +