1
0
mirror of https://github.com/dkmstr/openuds.git synced 2024-12-22 13:34:04 +03:00

added remove duplicates to open stack

This commit is contained in:
Adolfo Gómez García 2024-07-18 16:15:11 +02:00
parent 43d0f3b4d4
commit 4fea6e6a3e
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
6 changed files with 79 additions and 37 deletions

View File

@ -120,6 +120,17 @@ class TestOpenStackClient(UDSTransactionTestCase):
msg='Timeout waiting for snapshot to be available',
)
def wait_for_server(
self,
server: openstack_types.ServerInfo,
power_state: openstack_types.PowerState = openstack_types.PowerState.RUNNING,
) -> None:
helpers.waiter(
lambda: self.oclient.get_server_info(server.id, force=True).power_state == power_state,
timeout=30,
msg='Timeout waiting for server to be running',
)
@contextlib.contextmanager
def create_test_volume(self) -> typing.Iterator[openstack_types.VolumeInfo]:
volume = self.oclient.t_create_volume(
@ -164,7 +175,11 @@ class TestOpenStackClient(UDSTransactionTestCase):
)
@contextlib.contextmanager
def create_test_server(self) -> typing.Iterator[openstack_types.ServerInfo]:
def create_test_server(
self,
) -> typing.Iterator[
tuple[openstack_types.ServerInfo, openstack_types.VolumeInfo, openstack_types.SnapshotInfo]
]:
with self.create_test_volume() as volume:
with self.create_test_snapshot(volume) as snapshot:
server = self.oclient.create_server_from_snapshot(
@ -177,14 +192,10 @@ class TestOpenStackClient(UDSTransactionTestCase):
)
try:
# Wait for server to be running
helpers.waiter(
lambda: self.oclient.get_server_info(server.id, force=True).power_state.is_running(),
timeout=30,
msg='Timeout waiting for server to be running',
)
self.wait_for_server(server)
# Reget server info to complete all data
server = self.oclient.get_server_info(server.id, force=True)
yield server
yield server, volume, snapshot
finally:
self.oclient.delete_server(server.id)
@ -199,8 +210,8 @@ class TestOpenStackClient(UDSTransactionTestCase):
self.assertIn(self._regionid, [r.id for r in regions])
def test_list_servers(self) -> None:
with self.create_test_server() as server1:
with self.create_test_server() as server2:
with self.create_test_server() as (server1, _, _):
with self.create_test_server() as (server2, _, _):
servers = self.oclient.list_servers(force=True)
self.assertGreaterEqual(len(servers), 2)
self.assertIn(
@ -262,7 +273,7 @@ class TestOpenStackClient(UDSTransactionTestCase):
self.assertIn(self._security_group_name, [sg.name for sg in security_groups])
def test_get_server_info(self) -> None:
with self.create_test_server() as server:
with self.create_test_server() as (server, _, _):
server_info = self.oclient.get_server_info(server.id)
self.assertEqual(server.id, server_info.id)
self.assertEqual(server.name, server_info.name)
@ -303,7 +314,7 @@ class TestOpenStackClient(UDSTransactionTestCase):
self.oclient.create_snapshot(volume_id='non-existing-volume', name='non-existing-snapshot')
def test_create_server_from_snapshot(self) -> None:
with self.create_test_server() as server:
with self.create_test_server() as (server, _, _):
self.assertIsNotNone(server.id)
# Trying to create a server from a non existing snapshot should raise an exceptions.NotFoundException
@ -330,23 +341,16 @@ class TestOpenStackClient(UDSTransactionTestCase):
self.oclient.delete_snapshot('non-existing-snapshot')
def test_operations_server(self) -> None:
with self.create_test_server() as server:
with self.create_test_server() as (server, _, _):
# Server is already running, first stop it
self.oclient.stop_server(server.id)
helpers.waiter(
lambda: self.oclient.get_server_info(server.id, force=True).power_state.is_stopped(),
timeout=30,
msg='Timeout waiting for server to be stopped',
)
self.wait_for_server(server, openstack_types.PowerState.SHUTDOWN)
self.oclient.start_server(server.id)
helpers.waiter(
lambda: self.oclient.get_server_info(server.id, force=True).power_state.is_running(),
timeout=30,
msg='Timeout waiting for server to be running',
)
self.wait_for_server(server)
self.oclient.reset_server(server.id)
# Here we need to wait for the server to be active again
helpers.waiter(
lambda: self.oclient.get_server_info(server.id, force=True).status.is_active(),
timeout=30,
@ -355,19 +359,11 @@ class TestOpenStackClient(UDSTransactionTestCase):
# Suspend
self.oclient.suspend_server(server.id)
helpers.waiter(
lambda: self.oclient.get_server_info(server.id, force=True).power_state.is_suspended(),
timeout=30,
msg='Timeout waiting for server to be suspended',
)
self.wait_for_server(server, openstack_types.PowerState.SUSPENDED)
# Resume
self.oclient.resume_server(server.id)
helpers.waiter(
lambda: self.oclient.get_server_info(server.id, force=True).power_state.is_running(),
timeout=30,
msg='Timeout waiting for server to be running',
)
self.wait_for_server(server)
# Reboot
self.oclient.reboot_server(server.id)
@ -399,6 +395,25 @@ class TestOpenStackClient(UDSTransactionTestCase):
def test_test_connection(self) -> None:
self.assertTrue(self.oclient.test_connection())
def test_is_available(self) -> None:
self.assertTrue(self.oclient.is_available())
self.assertTrue(self.oclient.is_available())
# Some useful tests
def test_duplicated_server_name(self) -> None:
with self.create_test_server() as (server, _volume, snapshot):
res = self.oclient.create_server_from_snapshot(
snapshot_id=snapshot.id,
name=server.name,
flavor_id=self._flavorid,
network_id=self._networkid,
security_groups_names=[],
availability_zone=self._availability_zone_id,
)
# Has been created, and no problem at all
self.assertIsNotNone(res)
# Now, delete it
# wait for server to be running
self.wait_for_server(res)
self.oclient.delete_server(res.id)

View File

@ -77,7 +77,6 @@ class DynamicService(services.Service, abc.ABC): # pylint: disable=too-many-pub
order=102,
tab=types.ui.Tab.ADVANCED,
)
maintain_on_error = fields.maintain_on_error_field(
order=103,
tab=types.ui.Tab.ADVANCED,
@ -110,7 +109,7 @@ class DynamicService(services.Service, abc.ABC): # pylint: disable=too-many-pub
"""
return name
# overridable
# overridable, but not needed if no remove_duplicates is used
def find_duplicates(self, name: str, mac: str) -> collections.abc.Iterable[str]:
"""
Checks if a machine with the same name or mac exists
@ -125,6 +124,8 @@ class DynamicService(services.Service, abc.ABC): # pylint: disable=too-many-pub
Note:
Maybe we can only check name or mac, or both, depending on the service
This method must be be provided if the field remove_duplicates is used
If not, will raise a NotImplementedError
"""
raise NotImplementedError(f'{self.__class__}: find_duplicates must be implemented if remove_duplicates is used!')

View File

@ -519,6 +519,7 @@ class DynamicUserService(services.UserService, autoserializable.AutoSerializable
def remove_duplicated_names(self) -> None:
name = self.get_vmname()
try:
retry = False
for vmid in self.service().perform_find_duplicates(name, self.get_unique_id()):
userservice = self.db_obj()
log.log(
@ -528,7 +529,12 @@ class DynamicUserService(services.UserService, autoserializable.AutoSerializable
types.log.LogSource.SERVICE,
)
self.service().delete(self, vmid)
retry = True
if retry:
# Retry again in a while if duplicated machines where found, until we remove all of them
# Note that this way, can request the deletion of the same machine multiple times
# but this is not a problem, as the service will simply ignore the request if the machine is not there
# when the deletion is requested
self.retry_later()
except Exception as e:
logger.warning('Locating duplicated machines: %s', e)

View File

@ -1,11 +1,14 @@
from uds.core import exceptions as core_exceptions
class Error(core_exceptions.UDSException):
"""
Base exception for this module
"""
pass
class RetryableError(Error):
"""
Exception that is raised when an error is detected that can be retried
@ -31,3 +34,12 @@ class NotFoundError(Error):
def __init__(self, message: str):
super().__init__(message)
class AlreadyExistsError(Error):
"""
Exception that is raised when an object already exists
"""
def __init__(self, message: str):
super().__init__(message)

View File

@ -198,7 +198,7 @@ def get_certificates_from_field(
# Timeout
def timeout_field(
default: int = 3,
default: int = 5,
order: int = 90,
tab: 'types.ui.Tab|str|None' = types.ui.Tab.ADVANCED,
old_field_name: typing.Optional[str] = None,

View File

@ -31,6 +31,7 @@
Author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import logging
import collections.abc
import typing
from django.utils.translation import gettext_noop as _
@ -178,6 +179,7 @@ class OpenStackLiveService(DynamicService):
maintain_on_error = DynamicService.maintain_on_error
try_soft_shutdown = DynamicService.try_soft_shutdown
remove_duplicates = DynamicService.remove_duplicates
prov_uuid = gui.HiddenField()
@ -233,6 +235,12 @@ class OpenStackLiveService(DynamicService):
def sanitized_name(self, name: str) -> str:
return self.provider().sanitized_name(name)
def find_duplicates(self, name: str, mac: str) -> collections.abc.Iterable[str]:
# Only looks for name duplicates, the mac is created by openstack, so it should be unique
for i in self.api.list_servers():
if i.name == name:
yield i.id
def get_ip(self, caller_instance: typing.Optional['DynamicUserService | DynamicPublication'], vmid: str) -> str:
return self.api.get_server_info(vmid).validated().addresses[0].ip