Skip to content

Commit 4474481

Browse files
committed
manage flexible_ip and failover_ip
1 parent b84221e commit 4474481

1 file changed

Lines changed: 75 additions & 30 deletions

File tree

plugins/inventory/scaleway.py

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
__metaclass__ = type
1010

11+
import ipaddress
12+
1113
DOCUMENTATION = r"""
1214
name: scaleway
1315
author:
@@ -110,11 +112,12 @@
110112
try:
111113
from scaleway_core.bridge import Zone
112114
from scaleway import Client, ScalewayException
115+
from scaleway.flexibleip.v1alpha1 import FlexibleipV1Alpha1API
113116
from scaleway.applesilicon.v1alpha1 import (
114117
ApplesiliconV1Alpha1API,
115118
ApplesiliconV1Alpha1PrivateNetworkAPI,
116119
ServerPrivateNetworkStatus,
117-
Server as ApplesiliconServer, Server,
120+
Server as ApplesiliconServer,
118121
)
119122
from scaleway.baremetal.v1 import (
120123
BaremetalV1API,
@@ -139,7 +142,7 @@
139142
HAS_SCALEWAY_SDK = False
140143

141144
@dataclass
142-
class _Host(ABC):
145+
class Host(ABC):
143146
"""Abstract base host object with common fields and network handling."""
144147

145148
id: str
@@ -169,6 +172,20 @@ def _populate_private_network(self, client: "Client", private_networks_id: list[
169172
self.private_ipv4.extend(ip.address.split("/")[0] for ip in ips if not ip.is_ipv6)
170173
self.private_ipv6.extend(ip.address.split("/")[0] for ip in ips if ip.is_ipv6)
171174

175+
def _populate_flexible_ip(self, client: "Client", server: Any ) -> None:
176+
supported_types = ("InstanceServer", "BaremetalServer")
177+
if server.__class__.__name__ not in supported_types:
178+
return
179+
flexible_ip_api = FlexibleipV1Alpha1API(client=client)
180+
fips = flexible_ip_api.list_flexible_i_ps_all(zone=server.zone, server_ids=[server.id])
181+
for fip in fips:
182+
if self.get_ip_version(fip.ip_address) == "IPv6":
183+
self.flexible_ipv6.extend(fip.ip_address.split("/")[0])
184+
else:
185+
self.flexible_ipv4.extend(fip.ip_address.split("/")[0])
186+
self.public_flexible_dns.extend(fip.reverse)
187+
188+
172189
def normalized_state(self) -> str:
173190
"""Normalize server state into standard values."""
174191
mapping = {
@@ -180,14 +197,24 @@ def normalized_state(self) -> str:
180197
state_str = str(self.state).lower()
181198
return mapping.get(state_str, state_str)
182199

200+
def get_ip_version(self, ip: str) -> str:
201+
"""
202+
https://docs.python.org/3/library/ipaddress.html
203+
"""
204+
try:
205+
parsed_ip = ipaddress.ip_network(ip)
206+
return "IPv4" if isinstance(parsed_ip, ipaddress.IPv4Network) else "IPv6"
207+
except ValueError:
208+
return "Invalid"
209+
183210

184211

185212
# ---------------------------------------------------------------------------
186213
# Product-specific subclasses
187214
# ---------------------------------------------------------------------------
188215

189216
@dataclass
190-
class _ApplesiliconHost(_Host):
217+
class ApplesiliconHost(Host):
191218
def populate_network(self, server: "ApplesiliconServer", client: "Client") -> None:
192219
if server.ip:
193220
self.public_ipv4.append(server.ip)
@@ -198,13 +225,17 @@ def populate_network(self, server: "ApplesiliconServer", client: "Client") -> No
198225
applesilicon_api = ApplesiliconV1Alpha1PrivateNetworkAPI(client=client)
199226

200227
private_networks = applesilicon_api.list_server_private_networks_all(server_id=server.id)
201-
self._populate_private_network(client, private_networks)
228+
self._populate_private_network(client, [pn.id for pn in private_networks])
202229

203230

204231
@dataclass
205-
class _InstanceServerHost(_Host):
232+
class InstanceServerHost(Host):
206233
public_dns: Optional[str] = None
207234
private_dns: Optional[str] = None
235+
flexible_ipv4: list[str] = field(default_factory=list)
236+
flexible_ipv6: list[str] = field(default_factory=list)
237+
public_flexible_dns: list[str] = field(default_factory=list)
238+
208239

209240
def populate_network(self, server: "InstanceServer", client: "Client") -> None:
210241
self.public_dns = f"{server.id}.pub.instances.scw.cloud"
@@ -217,12 +248,18 @@ def populate_network(self, server: "InstanceServer", client: "Client") -> None:
217248
self.public_ipv6.append(ip.address)
218249

219250
self._populate_private_network(client, [pn.id for pn in server.private_nics])
220-
251+
self._populate_flexible_ip(client, server)
221252

222253
@dataclass
223-
class _ElasticMetalHost(_Host):
254+
class ElasticMetalHost(Host):
255+
flexible_ipv4: list[str] = field(default_factory=list)
256+
flexible_ipv6: list[str] = field(default_factory=list)
257+
public_flexible_dns: list[str] = field(default_factory=list)
258+
224259
def populate_network(self, server: "BaremetalServer", client: "Client") -> None:
225-
for ip in server.ips or []:
260+
if server.ips is None:
261+
return
262+
for ip in server.ips:
226263
target_list = self.public_ipv4 if ip.version.lower() == "ipv4" else self.public_ipv6
227264
target_list.append(ip.address)
228265

@@ -233,12 +270,15 @@ def populate_network(self, server: "BaremetalServer", client: "Client") -> None:
233270
baremetal_pn_api = BaremetalV1PrivateNetworkAPI(client=client)
234271

235272
private_networks = baremetal_pn_api.list_server_private_networks_all(server_id=server.id)
236-
self._populate_private_network(client, private_networks)
273+
self._populate_private_network(client, [pn.id for pn in private_networks])
274+
self._populate_flexible_ip(client, server)
237275

238276

239277
@dataclass
240-
class _DediboxHost(_Host):
278+
class DediboxHost(Host):
241279
public_dns: list[str] = field(default_factory=list)
280+
failover_ipv4: list[str] = field(default_factory=list)
281+
failover_ipv6: list[str] = field(default_factory=list)
242282

243283
def populate_network(self, server: "DediboxServer", client: "Client") -> None:
244284
for interface in server.interfaces or []:
@@ -321,7 +361,7 @@ def get_cached_result(self, path: str, cache: bool) -> tuple[bool, Optional[Any]
321361
return True, cached_result
322362

323363
@staticmethod
324-
def _host_to_dict(host: _Host) -> dict[str, Any]:
364+
def _host_to_dict(host: Host) -> dict[str, Any]:
325365
return asdict(host)
326366

327367
def _list_servers_safe(self, api, zone):
@@ -335,16 +375,16 @@ def _list_servers_safe(self, api, zone):
335375
return []
336376
raise e
337377

338-
def _get_instances(self, client: "Client") -> list[_InstanceServerHost]:
378+
def _get_instances(self, client: "Client") -> list[InstanceServerHost]:
339379
instance_api = InstanceV1API(client)
340380
servers: list[InstanceServer] = []
341381
zones = self.get_option("zones")
342382
for zone in zones:
343383
servers.extend(instance_api.list_servers_all(zone=zone))
344-
results: list[_InstanceServerHost] = []
384+
results: list[InstanceServerHost] = []
345385

346386
for server in servers:
347-
host = _InstanceServerHost(
387+
host = InstanceServerHost(
348388
id=server.id,
349389
tags= ["instance"] + server.tags,
350390
zone=server.zone,
@@ -355,17 +395,17 @@ def _get_instances(self, client: "Client") -> list[_InstanceServerHost]:
355395
results.append(host)
356396
return results
357397

358-
def _get_elastic_metal(self, client: "Client") -> list[_ElasticMetalHost]:
398+
def _get_elastic_metal(self, client: "Client") -> list[ElasticMetalHost]:
359399
baremetal_api = BaremetalV1API(client)
360400
servers: list[BaremetalServer] = []
361401
zones = self.get_option("zones")
362402
for zone in zones:
363403
servers.extend(self._list_servers_safe(baremetal_api, zone))
364404

365-
results: list[_ElasticMetalHost] = []
405+
results: list[ElasticMetalHost] = []
366406

367407
for server in servers:
368-
host = _ElasticMetalHost(
408+
host = ElasticMetalHost(
369409
id=server.id,
370410
tags=["elasticmetal"] + server.tags,
371411
zone=server.zone,
@@ -376,15 +416,15 @@ def _get_elastic_metal(self, client: "Client") -> list[_ElasticMetalHost]:
376416
results.append(host)
377417
return results
378418

379-
def _get_apple_silicon(self, client: "Client") -> list[_ApplesiliconHost]:
419+
def _get_apple_silicon(self, client: "Client") -> list[ApplesiliconHost]:
380420
api = ApplesiliconV1Alpha1API(client)
381421
servers = []
382422
for zone in self.get_option("zones"):
383423
servers.extend(self._list_servers_safe(api, zone))
384424

385-
results: list[_ApplesiliconHost] = []
425+
results: list[ApplesiliconHost] = []
386426
for server in servers:
387-
host = _ApplesiliconHost(
427+
host = ApplesiliconHost(
388428
id=server.id,
389429
tags=["applesilicon"] + server.tags,
390430
zone=server.zone,
@@ -395,17 +435,17 @@ def _get_apple_silicon(self, client: "Client") -> list[_ApplesiliconHost]:
395435
results.append(host)
396436
return results
397437

398-
def _get_dedibox(self, client: "Client") -> list[_DediboxHost]:
438+
def _get_dedibox(self, client: "Client") -> list[DediboxHost]:
399439
dedibox_api = DediboxV1API(client)
400440
servers: list[DediboxServer] = []
401441
zones = self.get_option("zones")
402442
for zone in zones:
403443
servers.extend(self._list_servers_safe(dedibox_api, zone))
404444

405-
results: list[_DediboxHost] = []
445+
results: list[DediboxHost] = []
406446

407447
for server in servers:
408-
host = _DediboxHost(
448+
host = DediboxHost(
409449
id=str(server.id),
410450
tags=["dedibox"],
411451
zone=server.zone,
@@ -418,7 +458,7 @@ def _get_dedibox(self, client: "Client") -> list[_DediboxHost]:
418458
return results
419459

420460
@staticmethod
421-
def _get_host_attribute(host: _Host, host_attributes: list[str]):
461+
def _get_host_attribute(host: Host, host_attributes: list[str]):
422462
host_as_dict = host.__dict__
423463

424464
for host_attribute in host_attributes:
@@ -431,7 +471,7 @@ def _get_host_attribute(host: _Host, host_attributes: list[str]):
431471
raise AnsibleError(f"{host.id} has no attribute {host_attributes}")
432472

433473

434-
def get_host_groups(self, host: _Host):
474+
def get_host_groups(self, host: Host):
435475
return set(self.sanitize_tag(tag) for tag in host.tags).union(
436476
{self.sanitize_tag(host.zone)}
437477
)
@@ -447,23 +487,28 @@ def sanitize_tag(self, tag: str):
447487

448488
return tag
449489

450-
def populate(self, all_hosts: list[_Host]):
490+
def populate(self, all_hosts: list[Host]):
451491
host_attributes = self.get_option("hostnames")
492+
print("value of get_options(hostname): ", host_attributes)
452493
variables = self.get_option("variables") or {}
494+
print("value of get_options(variable): ", variables)
453495

454496
for host in all_hosts:
455497
groups = self.get_host_groups(host)
456498
try:
457499
hostname_value = self._get_host_attribute(host, host_attributes)
500+
print("value of _get_host_attribute(host, host_attributes): ", hostname_value)
458501
except AnsibleError as e:
459502
self.display.warning(f"Skipping host {host.id}: {e}")
460503
continue
461504

462505
# If the hostname attribute is a list, create a host for each element
463506
hostnames = hostname_value if isinstance(hostname_value, list) else [hostname_value]
507+
print("value of hostnames: ", hostnames)
464508

465509
for idx, hostname in enumerate(hostnames):
466510
# Ensure hostname is a string and unique if multiple
511+
print("vale of idx and hostnames: ", idx, hostname)
467512
if isinstance(hostname, list):
468513
self.display.warning(f"Skipping host {host.id}: nested lists are not supported.")
469514
continue
@@ -503,10 +548,10 @@ def populate(self, all_hosts: list[_Host]):
503548
def get_inventory(self):
504549
client = self._get_client()
505550

506-
instances: list[_InstanceServerHost] = self._get_instances(client)
507-
elastic_metals: list[_ElasticMetalHost] = self._get_elastic_metal(client)
508-
apple_silicon: list[_ApplesiliconHost] = self._get_apple_silicon(client)
509-
dedibox: list[_DediboxHost] = self._get_dedibox(client)
551+
instances: list[InstanceServerHost] = self._get_instances(client)
552+
elastic_metals: list[ElasticMetalHost] = self._get_elastic_metal(client)
553+
apple_silicon: list[ApplesiliconHost] = self._get_apple_silicon(client)
554+
dedibox: list[DediboxHost] = self._get_dedibox(client)
510555

511556
return instances + elastic_metals + apple_silicon + dedibox
512557

0 commit comments

Comments
 (0)