1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# 4# Copyright (C) 2019 Huawei 5# GNU General Public License v3.0+ (see COPYING or 6# https://www.gnu.org/licenses/gpl-3.0.txt) 7 8from __future__ import absolute_import, division, print_function 9__metaclass__ = type 10 11############################################################################### 12# Documentation 13############################################################################### 14 15DOCUMENTATION = ''' 16--- 17module: hwc_vpc_private_ip 18description: 19 - vpc private ip management. 20short_description: Creates a resource of Vpc/PrivateIP in Huawei Cloud 21notes: 22 - If I(id) option is provided, it takes precedence over I(subnet_id), I(ip_address) for private ip selection. 23 - I(subnet_id), I(ip_address) are used for private ip selection. If more than one private ip with this options exists, execution is aborted. 24 - No parameter support updating. If one of option is changed, the module will create a new resource. 25version_added: '0.2.0' 26author: Huawei Inc. (@huaweicloud) 27requirements: 28 - keystoneauth1 >= 3.6.0 29options: 30 state: 31 description: 32 - Whether the given object should exist in Huawei Cloud. 33 type: str 34 choices: ['present', 'absent'] 35 default: 'present' 36 subnet_id: 37 description: 38 - Specifies the ID of the subnet from which IP addresses are 39 assigned. Cannot be changed after creating the private ip. 40 type: str 41 required: true 42 ip_address: 43 description: 44 - Specifies the target IP address. The value can be an available IP 45 address in the subnet. If it is not specified, the system 46 automatically assigns an IP address. Cannot be changed after 47 creating the private ip. 48 type: str 49 required: false 50extends_documentation_fragment: 51- community.general.hwc 52 53''' 54 55EXAMPLES = ''' 56# create a private ip 57- name: Create vpc 58 hwc_network_vpc: 59 cidr: "192.168.100.0/24" 60 name: "ansible_network_vpc_test" 61 register: vpc 62- name: Create subnet 63 hwc_vpc_subnet: 64 gateway_ip: "192.168.100.32" 65 name: "ansible_network_subnet_test" 66 dhcp_enable: True 67 vpc_id: "{{ vpc.id }}" 68 cidr: "192.168.100.0/26" 69 register: subnet 70- name: Create a private ip 71 community.general.hwc_vpc_private_ip: 72 subnet_id: "{{ subnet.id }}" 73 ip_address: "192.168.100.33" 74''' 75 76RETURN = ''' 77 subnet_id: 78 description: 79 - Specifies the ID of the subnet from which IP addresses are 80 assigned. 81 type: str 82 returned: success 83 ip_address: 84 description: 85 - Specifies the target IP address. The value can be an available IP 86 address in the subnet. If it is not specified, the system 87 automatically assigns an IP address. 88 type: str 89 returned: success 90''' 91 92from ansible_collections.community.general.plugins.module_utils.hwc_utils import ( 93 Config, HwcClientException, HwcModule, are_different_dicts, build_path, 94 get_region, is_empty_value, navigate_value) 95 96 97def build_module(): 98 return HwcModule( 99 argument_spec=dict( 100 state=dict(default='present', choices=['present', 'absent'], 101 type='str'), 102 subnet_id=dict(type='str', required=True), 103 ip_address=dict(type='str') 104 ), 105 supports_check_mode=True, 106 ) 107 108 109def main(): 110 """Main function""" 111 112 module = build_module() 113 config = Config(module, "vpc") 114 115 try: 116 resource = None 117 if module.params['id']: 118 resource = True 119 else: 120 v = search_resource(config) 121 if len(v) > 1: 122 raise Exception("Found more than one resource(%s)" % ", ".join([ 123 navigate_value(i, ["id"]) for i in v])) 124 125 if len(v) == 1: 126 resource = v[0] 127 module.params['id'] = navigate_value(resource, ["id"]) 128 129 result = {} 130 changed = False 131 if module.params['state'] == 'present': 132 if resource is None: 133 if not module.check_mode: 134 create(config) 135 changed = True 136 137 current = read_resource(config, exclude_output=True) 138 expect = user_input_parameters(module) 139 if are_different_dicts(expect, current): 140 raise Exception( 141 "Cannot change option from (%s) to (%s)of an" 142 " existing resource.(%s)" % (current, expect, module.params.get('id'))) 143 144 result = read_resource(config) 145 result['id'] = module.params.get('id') 146 else: 147 if resource: 148 if not module.check_mode: 149 delete(config) 150 changed = True 151 152 except Exception as ex: 153 module.fail_json(msg=str(ex)) 154 155 else: 156 result['changed'] = changed 157 module.exit_json(**result) 158 159 160def user_input_parameters(module): 161 return { 162 "ip_address": module.params.get("ip_address"), 163 "subnet_id": module.params.get("subnet_id"), 164 } 165 166 167def create(config): 168 module = config.module 169 client = config.client(get_region(module), "vpc", "project") 170 opts = user_input_parameters(module) 171 172 params = build_create_parameters(opts) 173 r = send_create_request(module, params, client) 174 module.params['id'] = navigate_value(r, ["privateips", "id"], 175 {"privateips": 0}) 176 177 178def delete(config): 179 module = config.module 180 client = config.client(get_region(module), "vpc", "project") 181 182 send_delete_request(module, None, client) 183 184 185def read_resource(config, exclude_output=False): 186 module = config.module 187 client = config.client(get_region(module), "vpc", "project") 188 189 res = {} 190 191 r = send_read_request(module, client) 192 res["read"] = fill_read_resp_body(r) 193 194 return update_properties(module, res, None, exclude_output) 195 196 197def _build_query_link(opts): 198 query_link = "?marker={marker}&limit=10" 199 200 return query_link 201 202 203def search_resource(config): 204 module = config.module 205 client = config.client(get_region(module), "vpc", "project") 206 opts = user_input_parameters(module) 207 identity_obj = _build_identity_object(opts) 208 query_link = _build_query_link(opts) 209 link = build_path(module, "subnets/{subnet_id}/privateips") + query_link 210 211 result = [] 212 p = {'marker': ''} 213 while True: 214 url = link.format(**p) 215 r = send_list_request(module, client, url) 216 if not r: 217 break 218 219 for item in r: 220 item = fill_list_resp_body(item) 221 if not are_different_dicts(identity_obj, item): 222 result.append(item) 223 224 if len(result) > 1: 225 break 226 227 p['marker'] = r[-1].get('id') 228 229 return result 230 231 232def build_create_parameters(opts): 233 params = dict() 234 235 v = navigate_value(opts, ["ip_address"], None) 236 if not is_empty_value(v): 237 params["ip_address"] = v 238 239 v = navigate_value(opts, ["subnet_id"], None) 240 if not is_empty_value(v): 241 params["subnet_id"] = v 242 243 if not params: 244 return params 245 246 params = {"privateips": [params]} 247 248 return params 249 250 251def send_create_request(module, params, client): 252 url = "privateips" 253 try: 254 r = client.post(url, params) 255 except HwcClientException as ex: 256 msg = ("module(hwc_vpc_private_ip): error running " 257 "api(create), error: %s" % str(ex)) 258 module.fail_json(msg=msg) 259 260 return r 261 262 263def send_delete_request(module, params, client): 264 url = build_path(module, "privateips/{id}") 265 266 try: 267 r = client.delete(url, params) 268 except HwcClientException as ex: 269 msg = ("module(hwc_vpc_private_ip): error running " 270 "api(delete), error: %s" % str(ex)) 271 module.fail_json(msg=msg) 272 273 return r 274 275 276def send_read_request(module, client): 277 url = build_path(module, "privateips/{id}") 278 279 r = None 280 try: 281 r = client.get(url) 282 except HwcClientException as ex: 283 msg = ("module(hwc_vpc_private_ip): error running " 284 "api(read), error: %s" % str(ex)) 285 module.fail_json(msg=msg) 286 287 return navigate_value(r, ["privateip"], None) 288 289 290def fill_read_resp_body(body): 291 result = dict() 292 293 result["id"] = body.get("id") 294 295 result["ip_address"] = body.get("ip_address") 296 297 result["subnet_id"] = body.get("subnet_id") 298 299 return result 300 301 302def update_properties(module, response, array_index, exclude_output=False): 303 r = user_input_parameters(module) 304 305 v = navigate_value(response, ["read", "ip_address"], array_index) 306 r["ip_address"] = v 307 308 v = navigate_value(response, ["read", "subnet_id"], array_index) 309 r["subnet_id"] = v 310 311 return r 312 313 314def send_list_request(module, client, url): 315 316 r = None 317 try: 318 r = client.get(url) 319 except HwcClientException as ex: 320 msg = ("module(hwc_vpc_private_ip): error running " 321 "api(list), error: %s" % str(ex)) 322 module.fail_json(msg=msg) 323 324 return navigate_value(r, ["privateips"], None) 325 326 327def _build_identity_object(all_opts): 328 result = dict() 329 330 result["id"] = None 331 332 v = navigate_value(all_opts, ["ip_address"], None) 333 result["ip_address"] = v 334 335 v = navigate_value(all_opts, ["subnet_id"], None) 336 result["subnet_id"] = v 337 338 return result 339 340 341def fill_list_resp_body(body): 342 result = dict() 343 344 result["id"] = body.get("id") 345 346 result["ip_address"] = body.get("ip_address") 347 348 result["subnet_id"] = body.get("subnet_id") 349 350 return result 351 352 353if __name__ == '__main__': 354 main() 355