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