Skip to content

Commit 936e626

Browse files
authored
Merge pull request #10 from merizrizal/8-add-floating-ip-module
8 add floating ip module
2 parents dd5a455 + 6ea966e commit 936e626

File tree

5 files changed

+376
-78
lines changed

5 files changed

+376
-78
lines changed

merizrizal/idcloudhost/plugins/module_utils/base.py

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,24 +65,85 @@ def _get_existing_network(self, name) -> dict:
6565

6666
return dict()
6767

68-
def _get_public_ipv4(self, vm_uuid=None, private_ipv4=None) -> dict:
68+
def _get_public_ipv4(self, vm_uuid=None, private_ipv4=None, name=None) -> dict:
6969
url, url_headers = self._init_url('network/ip_addresses')
7070

7171
response = requests.request('GET', url, headers=url_headers, timeout=360)
7272
data = response.json()
7373

7474
if isinstance(data, list) and len(data) > 0:
7575
for value in data:
76-
is_found = value['assigned_to_private_ip'] == private_ipv4
77-
is_found = is_found or value['assigned_to'] == vm_uuid
76+
is_found = 'assigned_to_private_ip' in value and value['assigned_to_private_ip'] == private_ipv4
77+
is_found = is_found or ('assigned_to' in value and value['assigned_to'] == vm_uuid)
78+
is_found = is_found or ('name' in value and value['name'] == name)
7879

7980
if is_found:
8081
result = dict(
8182
uuid=value['uuid'],
83+
name='' if 'name' not in value else value['name'],
8284
public_ipv4=value['address'],
85+
assigned_to_vm_uuid='' if 'assigned_to' not in value else value['assigned_to'],
86+
private_ipv4_address='' if 'assigned_to_private_ip' not in value else value['assigned_to_private_ip'],
8387
enabled=value['enabled']
8488
)
8589

8690
return result
8791

88-
return dict()
92+
return dict()
93+
94+
def _delete_public_ipv4(self, public_ipv4):
95+
url, url_headers = self._init_url(f'network/ip_addresses/{public_ipv4}')
96+
97+
response = requests.request('DELETE', url, headers=url_headers, timeout=360)
98+
99+
if response.status_code != 200:
100+
result = dict(
101+
error='There was a problem with the request when deleting the public IPv4 address.'
102+
)
103+
104+
self._module.fail_json(msg='Failed to delete the VM.', **result)
105+
106+
def _get_vm(self, uuid=None, name=None, include_public_ipv4=True) -> dict:
107+
url, url_headers = self._init_url('user-resource/vm/list')
108+
109+
response = requests.request('GET', url, headers=url_headers, timeout=360)
110+
data = response.json()
111+
112+
if isinstance(data, list) and len(data) > 0:
113+
for value in data:
114+
if value['uuid'] == uuid or value['name'] == name:
115+
return self._construct_vm_data(value, include_public_ipv4)
116+
117+
return dict()
118+
119+
def _construct_vm_data(self, data, include_public_ipv4=True) -> dict:
120+
floating_ip = dict()
121+
if include_public_ipv4:
122+
floating_ip = self._get_public_ipv4(data['uuid'], data['private_ipv4'])
123+
124+
public_ipv4 = '' if 'public_ipv4' not in floating_ip else floating_ip['public_ipv4']
125+
126+
disks = 0
127+
disk_uuid = ''
128+
for storage in data['storage']:
129+
if storage['primary']:
130+
disks = storage['size']
131+
disk_uuid = storage['uuid']
132+
break
133+
134+
vm = dict(
135+
uuid=data['uuid'],
136+
name=data['name'],
137+
hostname=data['hostname'],
138+
disks=disks,
139+
disk_uuid=disk_uuid,
140+
vcpu=data['vcpu'],
141+
ram=data['memory'],
142+
private_ipv4=data['private_ipv4'],
143+
public_ipv4=public_ipv4,
144+
billing_account=data['billing_account'],
145+
status=data['status'],
146+
changed=False
147+
)
148+
149+
return vm
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright (c) 2025, Mei Rizal (@merizrizal) <meriz.rizal@gmail.com>
5+
6+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
7+
# SPDX-License-Identifier: GPL-3.0-or-later
8+
from __future__ import absolute_import, division, print_function
9+
10+
__metaclass__ = type
11+
12+
DOCUMENTATION = r'''
13+
---
14+
module: floating_ip
15+
short_description: Manage floating IPs
16+
version_added: "1.0.0"
17+
18+
description: Manage floating IPs from a selected location.
19+
20+
options:
21+
api_key:
22+
description: API of idcloudhost.com uses tokens to allow access to the API.
23+
required: true
24+
type: str
25+
location:
26+
description: The location name of the network to which this floating IP is assigned.
27+
required: true
28+
type: str
29+
choices: [ jkt01, jkt02, jkt03, sgp01 ]
30+
name:
31+
description: Name of the floating IP that will be managed.
32+
required: true
33+
type: str
34+
vm_name:
35+
description: The name of the VM to which this IP will be assigned.
36+
required: false
37+
default: null
38+
type: str
39+
state:
40+
description:
41+
- Indicates the desired floating IP state.
42+
- If present, it will be created.
43+
- If absent, it will be deleted.
44+
- If unassigned, it will be unassigned from specific VM.
45+
default: present
46+
type: str
47+
choices: [ present, absent, unassign ]
48+
49+
author:
50+
- Mei Rizal (@merizrizal) <meriz.rizal@gmail.com>
51+
'''
52+
53+
EXAMPLES = r'''
54+
- name: Create new floating IP only
55+
merizrizal.idcloudhost.floating_ip:
56+
api_key: "{{ your_api_key }}"
57+
location: jkt01
58+
name: my_floating_ip_addr01
59+
# Since the default value of state is set to present, we may exclude the state below
60+
state: present
61+
62+
- name: Create new floating IP and assign to specific VM
63+
merizrizal.idcloudhost.floating_ip:
64+
api_key: "{{ your_api_key }}"
65+
location: jkt01
66+
name: my_floating_ip_addr01
67+
vm_name: my_ubuntu_vm01
68+
# Since the default value of state is set to present, we may exclude the state below
69+
state: present
70+
71+
- name: Unassign floating IP
72+
merizrizal.idcloudhost.floating_ip:
73+
api_key: "{{ your_api_key }}"
74+
location: jkt01
75+
name: my_floating_ip_addr01
76+
state: unassign
77+
78+
- name: Delete floating IP
79+
merizrizal.idcloudhost.floating_ip:
80+
api_key: "{{ your_api_key }}"
81+
location: jkt01
82+
name: my_floating_ip_addr01
83+
state: absent
84+
'''
85+
86+
RETURN = r'''
87+
uuid:
88+
description: UUID of the managed floating IP.
89+
type: str
90+
returned: success
91+
name:
92+
description: Name of the managed floating IP.
93+
type: str
94+
returned: success
95+
public_ipv4:
96+
description: Public IPv4 address of the managed floating IP.
97+
type: str
98+
returned: success
99+
vm_name:
100+
description: The name of the VM to which this IP is assigned.
101+
type: str
102+
returned: success
103+
assigned_to_vm_uuid:
104+
description: The uuid of the VM to which this IP is assigned.
105+
type: str
106+
returned: success
107+
private_ipv4_address:
108+
description: The address of private IPv4 to which this IP is assigned.
109+
type: str
110+
returned: success
111+
enabled:
112+
description: The status of the floating IP.
113+
type: bool
114+
returned: success
115+
'''
116+
117+
from ansible.module_utils.basic import AnsibleModule
118+
from ansible_collections.merizrizal.idcloudhost.plugins.module_utils.base import \
119+
Base
120+
121+
requests = None
122+
123+
124+
class FloatingIp(Base):
125+
def __init__(self):
126+
super().__init__()
127+
self._endpoint_url = 'network/ip_addresses'
128+
129+
def main(self):
130+
global requests
131+
requests = self._ensure_requests()
132+
133+
argument_spec = dict(
134+
api_key=dict(type='str', required=True, no_log=True),
135+
location=dict(type='str', required=True, choices=['jkt01', 'jkt02', 'jkt03', 'sgp01']),
136+
name=dict(type='str', required=True),
137+
vm_name=dict(type='str', default=None),
138+
state=dict(type='str', default='present', choices=['absent', 'present', 'unassign'])
139+
)
140+
141+
self._module = AnsibleModule(
142+
argument_spec=argument_spec,
143+
supports_check_mode=True,
144+
)
145+
146+
self._name = self._module.params['name']
147+
self._api_key = self._module.params['api_key']
148+
self._location = self._module.params['location']
149+
self._state = self._module.params['state']
150+
vm_name = self._module.params['vm_name']
151+
152+
floating_ip = self._get_public_ipv4(name=self._name)
153+
154+
vm = dict()
155+
if vm_name is not None:
156+
vm = self._get_vm(name=vm_name)
157+
if 'uuid' not in vm:
158+
self._module.fail_json(msg='Failed to create the floating IP. The VM name is provided, but no VM was found.')
159+
160+
if self._state == 'present':
161+
if 'uuid' in floating_ip:
162+
floating_ip.update(
163+
vm_name='' if vm_name is None else vm_name,
164+
changed=False
165+
)
166+
167+
if 'uuid' in vm and floating_ip['assigned_to_vm_uuid'] == '':
168+
data_response = self._assign_to_vm(floating_ip['public_ipv4'], vm['uuid'])
169+
data_response = self._update_assigned_floating_ip(data_response, vm['name'])
170+
171+
floating_ip.update(
172+
**data_response,
173+
changed=True
174+
)
175+
else:
176+
self._create_floating_ip(vm)
177+
elif self._state == 'absent':
178+
if 'uuid' in floating_ip:
179+
self._delete_public_ipv4(floating_ip['public_ipv4'])
180+
floating_ip.update(changed=True)
181+
else:
182+
floating_ip.update(changed=False)
183+
elif self._state == 'unassign':
184+
if 'uuid' in floating_ip:
185+
data_response = self._unassign_from_vm(floating_ip['public_ipv4'])
186+
data_response = self._update_assigned_floating_ip(data_response, '')
187+
188+
floating_ip.update(
189+
**data_response,
190+
changed=floating_ip['assigned_to_vm_uuid'] != ''
191+
)
192+
else:
193+
floating_ip.update(changed=False)
194+
195+
self._module.exit_json(**floating_ip)
196+
197+
def _create_floating_ip(self, vm):
198+
url, url_headers = self._init_url(self._endpoint_url)
199+
url_headers.update({'Content-Type': 'application/json'})
200+
201+
form_data = dict(
202+
name=self._name
203+
)
204+
205+
response = requests.request('POST', url, headers=url_headers, json=form_data, timeout=360)
206+
data = response.json()
207+
208+
if 'uuid' not in data:
209+
result = dict(
210+
error=data
211+
)
212+
213+
self._module.fail_json(msg='Failed to create the floating IP.', **result)
214+
else:
215+
result = dict(
216+
uuid=data['uuid'],
217+
name=data['name'],
218+
public_ipv4=data['address'],
219+
vm_name='',
220+
assigned_to_vm_uuid='',
221+
private_ipv4_address='',
222+
enabled=data['enabled'],
223+
changed=True
224+
)
225+
226+
data_response = dict()
227+
if 'uuid' in vm:
228+
data_response = self._assign_to_vm(data['address'], vm['uuid'])
229+
230+
data_response = self._update_assigned_floating_ip(data_response, vm['name'])
231+
result.update(**data_response)
232+
233+
self._module.exit_json(**result)
234+
235+
def _assign_to_vm(self, ipv4_address, vm_uuid) -> dict:
236+
url, url_headers = self._init_url(f'{self._endpoint_url}/{ipv4_address}/assign')
237+
url_headers.update({'Content-Type': 'application/json'})
238+
239+
form_data = dict(
240+
vm_uuid=vm_uuid
241+
)
242+
243+
response = requests.request('POST', url, headers=url_headers, json=form_data, timeout=360)
244+
data = response.json()
245+
246+
if 'uuid' in data:
247+
return data
248+
249+
return dict(
250+
msg='Failed to assign the floating IP into the selected VM.',
251+
error=data
252+
)
253+
254+
def _unassign_from_vm(self, ipv4_address) -> dict:
255+
url, url_headers = self._init_url(f'{self._endpoint_url}/{ipv4_address}/unassign')
256+
257+
response = requests.request('POST', url, headers=url_headers, timeout=360)
258+
data = response.json()
259+
260+
if 'uuid' in data:
261+
result = dict(**data)
262+
result.update(
263+
assigned_to='',
264+
assigned_to_private_ip=''
265+
)
266+
267+
return result
268+
269+
return dict(
270+
msg='Failed to unassign the floating IP from the selected VM.',
271+
error=data
272+
)
273+
274+
def _update_assigned_floating_ip(self, floating_ip, vm_name) -> dict:
275+
result = dict()
276+
277+
if 'uuid' in floating_ip:
278+
result.update(
279+
vm_name=vm_name,
280+
assigned_to_vm_uuid=floating_ip['assigned_to'],
281+
private_ipv4_address=floating_ip['assigned_to_private_ip']
282+
)
283+
elif 'msg' in floating_ip:
284+
self._module.fail_json(**floating_ip)
285+
286+
return result
287+
288+
289+
if __name__ == '__main__':
290+
FloatingIp().main()

merizrizal/idcloudhost/plugins/modules/get_public_ip.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@
4343
EXAMPLES = r'''
4444
- name: Get public IPv4 address from selected private_ipv4
4545
merizrizal.idcloudhost.get_public_ip:
46-
api_key: 2bnQkD6yOb7OkSwVCBXJSg1AHpfd99oY
46+
api_key: "{{ your_api_key }}"
4747
location: jkt01
4848
private_ipv4: 10.51.111.211
4949
5050
- name: Get public IPv4 address from selected vm_uuid
5151
merizrizal.idcloudhost.get_public_ip:
52-
api_key: 2bnQkD6yOb7OkSwVCBXJSg1AHpfd99oY
52+
api_key: "{{ your_api_key }}"
5353
location: jkt01
5454
vm_uuid: 88e5a11b-9c89-4986-99c7-90d43499317c
5555
'''

0 commit comments

Comments
 (0)