Skip to content

Commit 0f64562

Browse files
authored
Merge pull request #508 from RedHatQE/copilot/fix-507
2 parents 1b9e85a + 9e86cf9 commit 0f64562

2 files changed

Lines changed: 108 additions & 26 deletions

File tree

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ dependencies = [
6464
"python-heatclient",
6565
"python-ironicclient",
6666
"python-keystoneclient",
67-
"python-neutronclient==6.12.0",
68-
"python-novaclient==7.1.2",
67+
"python-neutronclient==10.0.0",
68+
"python-novaclient==18.10.0",
69+
"openstacksdk",
6970
"python-swiftclient",
7071
"pyvcloud==23.0.4",
7172
"pyvmomi>=6.5.0.2017.5.post1",

wrapanapi/systems/openstack.py

Lines changed: 105 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from functools import partial
1212
from re import search
1313

14+
import openstack
1415
import pytz
1516
from cinderclient import exceptions as cinder_exceptions
1617
from cinderclient.v3 import client as cinderclient
@@ -23,7 +24,6 @@
2324
from novaclient import client as osclient
2425
from novaclient import exceptions as os_exceptions
2526
from novaclient.client import SessionClient
26-
from novaclient.v2.floating_ips import FloatingIP
2727
from requests.exceptions import Timeout
2828
from swiftclient import client as swiftclient
2929
from swiftclient.exceptions import ClientException as SwiftException
@@ -229,11 +229,13 @@ def assign_floating_ip(self, floating_ip_pool, safety_timer=5):
229229
if len(free_ips) > 1:
230230
# There are 2 and more ips, so we will take the first one (eldest)
231231
ip = free_ips[0]
232-
self.logger.info("Reusing %s from pool %s", ip.ip, floating_ip_pool)
232+
self.logger.info(
233+
"Reusing %s from pool %s", ip.floating_ip_address, floating_ip_pool
234+
)
233235
else:
234236
# There is one or none, so create one.
235237
try:
236-
ip = self._api.floating_ips.create(floating_ip_pool)
238+
ip = self.system._create_floating_ip(floating_ip_pool)
237239
except allowed_exceptions as e:
238240
self.logger.error("Probably no more FIP slots available: %s", str(e))
239241
free_ips = self.system.free_fips(floating_ip_pool)
@@ -243,21 +245,24 @@ def assign_floating_ip(self, floating_ip_pool, safety_timer=5):
243245
ip = free_ips[0]
244246
self.logger.info(
245247
"Reused %s from pool %s because no more free spaces for new ips",
246-
ip.ip,
248+
ip.floating_ip_address,
247249
floating_ip_pool,
248250
)
249251
else:
250252
# Nothing can be done
251253
raise NoMoreFloatingIPs(f"Provider {self.system.auth_url} ran out of FIPs")
252-
self.logger.info("Created %s in pool %s", ip.ip, floating_ip_pool)
253-
instance.add_floating_ip(ip)
254+
self.logger.info("Created %s in pool %s", ip.floating_ip_address, floating_ip_pool)
255+
# Use openstacksdk to associate floating IP with server
256+
self.system.openstack_conn.compute.add_floating_ip_to_server(
257+
instance, ip.floating_ip_address
258+
)
254259

255260
# Now the grace period in which a FIP theft could happen
256261
time.sleep(safety_timer)
257262

258-
self.logger.info("Instance %s got a floating IP %s", self.name, ip.ip)
259-
assert self.ip == ip.ip, "Current IP does not match reserved floating IP!"
260-
return ip.ip
263+
self.logger.info("Instance %s got a floating IP %s", self.name, ip.floating_ip_address)
264+
assert self.ip == ip.floating_ip_address, "Current IP does not match reserved floating IP!"
265+
return ip.floating_ip_address
261266

262267
def unassign_floating_ip(self):
263268
"""Disassociates the floating IP (if present) from VM.
@@ -269,14 +274,20 @@ def unassign_floating_ip(self):
269274
ip_addr = self.ip
270275
if ip_addr is None:
271276
return None
272-
floating_ips = self._api.floating_ips.findall(ip=ip_addr)
277+
floating_ips = self.system._list_floating_ips(ip=ip_addr)
273278
if not floating_ips:
274279
return None
275280
floating_ip = floating_ips[0]
276281
self.logger.info(
277-
"Detaching floating IP %s/%s from %s", floating_ip.id, floating_ip.ip, instance.name
282+
"Detaching floating IP %s/%s from %s",
283+
floating_ip.id,
284+
floating_ip.floating_ip_address,
285+
instance.name,
286+
)
287+
# Use openstacksdk to disassociate floating IP from server
288+
self.system.openstack_conn.compute.remove_floating_ip_from_server(
289+
instance, floating_ip.floating_ip_address
278290
)
279-
instance.remove_floating_ip(floating_ip)
280291
wait_for(lambda: self.ip is None, delay=1, timeout="1m")
281292
return floating_ip
282293

@@ -629,6 +640,7 @@ def __init__(self, tenant, username, password, auth_url, **kwargs):
629640
self._sapi = None
630641
self._tenant_api = None
631642
self._stackapi = None
643+
self._openstack_conn = None
632644

633645
@property
634646
def _identifying_attrs(self):
@@ -700,6 +712,28 @@ def napi(self):
700712
self._napi = neutronclient.Client(session=self.session)
701713
return self._napi
702714

715+
@property
716+
def openstack_conn(self):
717+
"""OpenStack SDK connection for unified API access."""
718+
if not self._openstack_conn:
719+
auth_kwargs = dict(
720+
auth_url=self.auth_url,
721+
username=self.username,
722+
password=self.password,
723+
project_name=self.tenant,
724+
)
725+
if self.keystone_version == 3:
726+
auth_kwargs.update(
727+
dict(
728+
user_domain_id=self.domain_id,
729+
user_domain_name=self.domain_name,
730+
project_domain_id=self.domain_id,
731+
project_domain_name=self.domain_name,
732+
)
733+
)
734+
self._openstack_conn = openstack.connect(**auth_kwargs, verify=False)
735+
return self._openstack_conn
736+
703737
@property
704738
def tenant_api(self):
705739
if not self._tenant_api:
@@ -736,6 +770,48 @@ def stackapi(self):
736770
def info(self):
737771
return f"{self.api.client.service_type} {self.api.client.version}"
738772

773+
def _list_floating_ips(self, **filters):
774+
"""List floating IPs using openstacksdk with optional filters."""
775+
openstack_filters = {}
776+
777+
if "ip" in filters:
778+
openstack_filters["floating_ip_address"] = filters["ip"]
779+
if "fixed_ip" in filters:
780+
if filters["fixed_ip"] is not None:
781+
openstack_filters["fixed_ip_address"] = filters["fixed_ip"]
782+
if "pool" in filters and filters["pool"]:
783+
# Pool in nova corresponds to network in openstack sdk
784+
networks = list(
785+
self.openstack_conn.network.networks(name=filters["pool"], is_router_external=True)
786+
)
787+
if networks:
788+
openstack_filters["floating_network_id"] = networks[0].id
789+
790+
floating_ips = list(self.openstack_conn.network.floating_ips(**openstack_filters))
791+
792+
# Post-process for fixed_ip=None case (unassigned floating IPs)
793+
if "fixed_ip" in filters and filters["fixed_ip"] is None:
794+
floating_ips = [
795+
fip
796+
for fip in floating_ips
797+
if fip.fixed_ip_address is None or fip.fixed_ip_address == ""
798+
]
799+
800+
return floating_ips
801+
802+
def _create_floating_ip(self, pool_name):
803+
"""Create floating IP using openstacksdk."""
804+
# Find the external network by name
805+
networks = list(
806+
self.openstack_conn.network.networks(name=pool_name, is_router_external=True)
807+
)
808+
if not networks:
809+
raise Exception(f"External network '{pool_name}' not found")
810+
811+
network_id = networks[0].id
812+
floating_ip = self.openstack_conn.network.create_floating_ip(floating_network_id=network_id)
813+
return floating_ip
814+
739815
def _get_tenants(self):
740816
if self.keystone_version == 3:
741817
return self.tenant_api.list()
@@ -1067,13 +1143,15 @@ def volume_attachments(self, volume_id):
10671143

10681144
def free_fips(self, pool):
10691145
"""Returns list of free floating IPs sorted by ip address."""
1070-
return sorted(self.api.floating_ips.findall(fixed_ip=None, pool=pool), key=lambda ip: ip.ip)
1146+
return sorted(
1147+
self._list_floating_ips(fixed_ip=None, pool=pool), key=lambda ip: ip.floating_ip_address
1148+
)
10711149

10721150
def delete_floating_ip(self, floating_ip):
10731151
"""Deletes an existing FIP.
10741152
10751153
Args:
1076-
floating_ip: FloatingIP object or an IP address of the FIP.
1154+
floating_ip: OpenStack SDK FloatingIP object or an IP address of the FIP.
10771155
10781156
Returns:
10791157
True if it deleted a FIP, False if it did not delete it, most probably because it
@@ -1082,15 +1160,18 @@ def delete_floating_ip(self, floating_ip):
10821160
if floating_ip is None:
10831161
# To be able to chain with unassign_floating_ip, which can return None
10841162
return False
1085-
if not isinstance(floating_ip, FloatingIP):
1086-
floating_ip = self.api.floating_ips.findall(ip=floating_ip)
1087-
if not floating_ip:
1163+
if isinstance(floating_ip, str):
1164+
# If floating_ip is a string (IP address), find the floating IP object
1165+
floating_ip_list = self._list_floating_ips(ip=floating_ip)
1166+
if not floating_ip_list:
10881167
return False
1089-
floating_ip = floating_ip[0]
1090-
self.logger.info("Deleting floating IP %s/%s", floating_ip.id, floating_ip.ip)
1091-
floating_ip.delete()
1168+
floating_ip = floating_ip_list[0]
1169+
self.logger.info(
1170+
"Deleting floating IP %s/%s", floating_ip.id, floating_ip.floating_ip_address
1171+
)
1172+
self.openstack_conn.network.delete_floating_ip(floating_ip)
10921173
wait_for(
1093-
lambda: len(self.api.floating_ips.findall(ip=floating_ip.ip)) == 0,
1174+
lambda: len(self._list_floating_ips(ip=floating_ip.floating_ip_address)) == 0,
10941175
delay=1,
10951176
timeout="1m",
10961177
)
@@ -1108,7 +1189,7 @@ def get_first_floating_ip(self, pool=None):
11081189
pool_name = getattr(pool, "name", pool) # obj attr, or passed thing (string) otherwise
11091190
fip = None
11101191
try:
1111-
fip = self.api.floating_ips.create(pool_name)
1192+
fip = self._create_floating_ip(pool_name)
11121193
except os_exceptions.NotFound:
11131194
self.logger.exception("Exception while creating FIP for pool: %s", pool_name)
11141195
else:
@@ -1120,11 +1201,11 @@ def get_first_floating_ip(self, pool=None):
11201201
pool_name,
11211202
)
11221203
try:
1123-
fip = next(ip for ip in self.api.floating_ips.list() if ip.instance_id is None)
1204+
fip = next(ip for ip in self._list_floating_ips() if ip.fixed_ip_address is None)
11241205
except StopIteration:
11251206
self.logger.error("No more Floating IPs available")
11261207
return None
1127-
return fip.ip
1208+
return fip.floating_ip_address
11281209

11291210
def stack_exist(self, stack_name):
11301211
stack = self.stackapi.stacks.get(stack_name)

0 commit comments

Comments
 (0)