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

Fixed config

Done migration tests for PhysicalMachines
This commit is contained in:
Adolfo Gómez García 2024-04-26 00:58:48 +02:00
parent c39c8c9583
commit 30bbf1ef0f
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
3 changed files with 108 additions and 48 deletions

View File

@ -45,13 +45,6 @@ from uds.models.config import Config as DBConfig
logger = logging.getLogger(__name__)
_for_saving_later: list[tuple['Config.Value', typing.Any]] = []
_for_recovering_later: list['Config.Value'] = []
_is_migrating: bool = False
# For custom params (for choices mainly)
_config_params: dict[str, typing.Any] = {}
# Pair of section/value removed from current UDS version
# Note: As of version 4.0, all previous REMOVED values has been moved to migration script 0043
REMOVED_CONFIG_ELEMENTS = {
@ -72,7 +65,22 @@ REMOVED_CONFIG_ELEMENTS = {
),
}
class Config:
# Global configuration values
_for_saving_later: typing.ClassVar[list[tuple['Config.Value', typing.Any]]] = []
_for_recovering_later: typing.ClassVar[list['Config.Value']] = []
# For custom params (for choices mainly)
_config_params: typing.ClassVar[dict[str, typing.Any]] = {}
# If we are migrating, we do not want to access database
_is_migrating: typing.ClassVar[bool] = False
# If initialization has been done
_initialization_finished: typing.ClassVar[bool] = False
# Fields types, so inputs get more "beautiful"
class FieldType(enum.IntEnum):
UNKNOWN = -1
@ -134,7 +142,7 @@ class Config:
return self._section_name
class Value:
_section: 'Config.Section'
_section: 'Config.Section'
_type: int
_key: str
_default: str
@ -158,12 +166,12 @@ class Config:
logger.debug(self)
def get(self, force: bool = False) -> str:
if apps.ready and _is_migrating is False:
if apps.ready and Config._is_migrating is False:
if not GlobalConfig.isInitialized():
logger.debug('Initializing configuration & updating db values')
GlobalConfig.initialize()
else:
_for_recovering_later.append(self)
Config._for_recovering_later.append(self)
return self._default
try:
@ -194,7 +202,7 @@ class Config:
return self._data
def set_params(self, params: typing.Any) -> None:
_config_params[self._section.name() + self._key] = params
Config._config_params[self._section.name() + self._key] = params
def as_int(self, force: bool = False) -> int:
try:
@ -219,7 +227,7 @@ class Config:
if self.get(force) == '0':
return False
return True
def as_str(self, force: bool = False) -> str:
return self.get(force)
@ -233,14 +241,14 @@ class Config:
return self._type
def get_params(self) -> typing.Any:
return _config_params.get(self._section.name() + self._key, None)
return Config._config_params.get(self._section.name() + self._key, None)
def get_help(self) -> str:
return gettext(self._help)
def set(self, value: typing.Union[str, bool, int]) -> None:
if GlobalConfig.isInitialized() is False or _is_migrating is True:
_for_saving_later.append((self, value))
if GlobalConfig.isInitialized() is False or Config._is_migrating is True:
Config._for_saving_later.append((self, value))
return
if isinstance(value, bool):
@ -311,9 +319,7 @@ class Config:
def update(section: 'Config.SectionType', key: str, value: str, check_type: bool = False) -> bool:
# If cfg value does not exists, simply ignore request
try:
cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[
0
]
cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[0]
if check_type and cfg.field_type in (
Config.FieldType.READ,
Config.FieldType.HIDDEN,
@ -772,16 +778,14 @@ class GlobalConfig:
help=_('Enable VNC menu for user services'),
)
_initDone = False
@staticmethod
def isInitialized() -> bool:
return GlobalConfig._initDone
return Config._initialization_finished
@staticmethod
def initialize() -> None:
if GlobalConfig._initDone is False:
GlobalConfig._initDone = True
if Config._initialization_finished is False:
Config._initialization_finished = True
try:
# Tries to initialize database data for global config so it is stored asap and get cached for use
for v in GlobalConfig.__dict__.values():
@ -789,16 +793,16 @@ class GlobalConfig:
v.get()
logger.debug('Initialized global config value %s=%s', v.key(), v.get())
for c in _for_recovering_later:
for c in Config._for_recovering_later:
logger.debug('Get later: %s', c)
c.get()
_for_recovering_later[:] = [] # pyright: ignore[reportUnknownArgumentType]
Config._for_recovering_later[:] = [] # pyright: ignore[reportUnknownArgumentType]
for c, v in _for_saving_later:
for c, v in Config._for_saving_later:
logger.debug('Saving delayed value: %s', c)
c.set(v)
_for_saving_later[:] = [] # pyright: ignore[reportUnknownArgumentType]
Config._for_saving_later[:] = [] # pyright: ignore[reportUnknownArgumentType]
# Process some global config parameters
# GlobalConfig.UDS_THEME.setParams(['html5', 'semantic'])
@ -810,14 +814,12 @@ class GlobalConfig:
# Signals for avoid saving config values on migrations
def _pre_migrate(sender: typing.Any, **kwargs: typing.Any) -> None:
# logger.info('Migrating database, AVOID saving config values')
global _is_migrating
_is_migrating = True
Config._is_migrating = True
def _post_migrate(sender: typing.Any, **kwargs: typing.Any) -> None:
# logger.info('Migration DONE, ALLOWING saving config values')
global _is_migrating
_is_migrating = False
Config._is_migrating = False
signals.pre_migrate.connect(_pre_migrate)

View File

@ -50,16 +50,6 @@ logger = logging.getLogger(__name__)
class IPSingleMachineService(services.Service):
# Gui
host = gui.TextField(
length=64,
label=_('Machine IP (and possibly MAC)'),
order=1,
tooltip=_('Machine IP'),
required=True,
old_field_name='ip',
)
# Description of service
type_name = _('Static Single IP')
type_type = 'IPSingleMachineService'
@ -74,6 +64,17 @@ class IPSingleMachineService(services.Service):
user_service_type = IPMachineUserService
services_type_provided = types.services.ServiceType.VDI
# Gui
host = gui.TextField(
length=64,
label=_('Host IP/FQDN'),
order=1,
tooltip=_('IP or FQDN of the server to connect to. Can include MAC address separated by ";" after the IP/Hostname'),
required=True,
old_field_name='ip',
)
def get_host_mac(self) -> typing.Tuple[str, str]:
if ';' in self.host.as_str():

View File

@ -34,11 +34,21 @@ import typing
import datetime
from unittest import mock
from uds import models
from uds.core import consts, types
from uds.core.util import fields
from ...utils.test import UDSTransactionTestCase
from uds import models
from uds.migrations.fixers.providers_v4 import physical_machine_multiple
from uds.services.PhysicalMachines import (
service_single,
service_multi,
deployment,
deployment_multi,
)
# Data from 3.6 version
PROVIDER_DATA: typing.Final[dict[str, typing.Any]] = {
@ -74,6 +84,9 @@ SERVICES_DATA: typing.Final[list[dict[str, typing.Any]]] = [
},
]
SINGLE_IP_SERVICE_IDX: typing.Final[int] = 1
MULTIPLE_IP_SERVICE_IDX: typing.Final[int] = 0
SERVICEPOOLS_DATA: typing.Final[list[dict[str, typing.Any]]] = [
{
'id': 100,
@ -129,6 +142,8 @@ SERVICEPOOLS_DATA: typing.Final[list[dict[str, typing.Any]]] = [
},
]
SINGLE_IP_SERVICEPOOL_IDX: typing.Final[int] = 1
MULTIPLE_IP_SERVICEPOOL_IDX: typing.Final[int] = 0
USERSERVICES_DATA: typing.Final[list[dict[str, typing.Any]]] = [
{
@ -136,8 +151,8 @@ USERSERVICES_DATA: typing.Final[list[dict[str, typing.Any]]] = [
'uuid': 'f1ac7d5-58c8-55c3-8bab-24ea1ed40be5',
'deployed_service_id': 100,
'publication_id': None,
'unique_id': 'dc.dkmon.local',
'friendly_name': 'dc.dkmon.local',
'unique_id': 'localhost',
'friendly_name': 'localhost',
'state': 'U',
'os_state': 'U',
'state_date': datetime.datetime(2024, 4, 25, 2, 51, 13),
@ -200,7 +215,7 @@ STORAGE_DATA: typing.Final[list[dict[str, typing.Any]]] = [
{
'owner': 't-service-144',
'key': '848d16fb421048c690c9761c11dc1699',
'data': 'gASVQwAAAAAAAABdlCiMDTE3Mi4yNy4xLjI1fjCUjA0xNzIuMjcuMS4yNn4xlIwNMTcyLjI3LjEu\nMjd+MpSMC2xvY2FsaG9zdH4zlGUu\n',
'data': 'gASVVQAAAAAAAABdlCiMDTE3Mi4yNy4xLjI1fjCUjA0xNzIuMjcuMS4yNn4xlIwNMTcyLjI3LjEuMjd+MpSMHWxvY2FsaG9zdDswMToyMzo0NTo2Nzo4OTpBQn4zlGUu\n',
'attr1': '',
},
{'owner': 't-service-142', 'key': 'b6ac33477ae0a82fa2681c4d398d88d7', 'data': 'gARLAS4=\n', 'attr1': ''},
@ -251,7 +266,7 @@ class TestPhysicalMigration(UDSTransactionTestCase):
"""
# We have 2 services:
# - Single IP
# - IP is localhost, should be migrated to (localhost, 127.0.0.1)
# - IP is localhost, should be migrated to localhost
# - One service pool
# - One user service
# - Multiple IP
@ -267,13 +282,55 @@ class TestPhysicalMigration(UDSTransactionTestCase):
# - 172.27.1.25
# - 172.27.1.26
# - 172.27.1.27
# - localhost
# - localhost;01:23:45:67:89:AB
# - One service pool
# - Two user services
# * First one, is from an already unexisting machine "172.27.1.15" (removed from the list BEFORE migration)
# * First one, is localhost
# * Second one is 172.27.1.26
# First, proceed to migration of data
physical_machine_multiple.migrate(self.apps_mock(), None)
# Now check that data has been migrated correctly
# Single ip
single_ip = typing.cast('service_single.IPSingleMachineService', models.Service.objects.get(uuid=SERVICES_DATA[SINGLE_IP_SERVICE_IDX]['uuid']).get_instance())
self.assertEqual(single_ip.host.value, 'localhost')
# Multiple ip
multi_ip = typing.cast('service_multi.IPMachinesService', models.Service.objects.get(uuid=SERVICES_DATA[MULTIPLE_IP_SERVICE_IDX]['uuid']).get_instance())
server_group = fields.get_server_group_from_field(multi_ip.server_group)
self.assertEqual(server_group.name, 'Physical Machines Server Group for Multiple IPS')
ips_to_check = {'172.27.1.25', '172.27.1.26', '172.27.1.27', '127.0.0.1'}
for server in server_group.servers.all():
self.assertEqual(server.server_type, types.servers.ServerType.UNMANAGED, f'Invalid server type for {server.ip}')
self.assertIn(server.ip, ips_to_check, f'Invalid server ip {server.ip}: {ips_to_check}')
ips_to_check.remove(server.ip)
# Ensure has a hostname, and MAC is empty
self.assertNotEqual(server.hostname, '')
# Localhost has a MAC, rest of servers have MAC_UNKNOWN (empty equivalent)
# Also, should have 127.0.0.1 as ip if localhost
if server.hostname == 'localhost':
self.assertEqual(server.ip, '127.0.0.1')
self.assertEqual(server.mac, '01:23:45:67:89:AB')
else:
self.assertEqual(server.mac, consts.MAC_UNKNOWN)
# If is 172.27.1.26 ensure is locked
if server.ip == '172.27.1.26' or server.hostname == 'localhost':
self.assertTrue(server.locked_until is not None and server.locked_until > datetime.datetime.now(), f'Server {server.ip} is not locked')
else:
self.assertIsNone(server.locked_until, f'Server {server.ip} is locked')
# Ensure all ips have been checked
self.assertEqual(len(ips_to_check), 0)
# Now, check UserServices
for userservice_data in USERSERVICES_DATA:
# Get the user service
if userservice_data['deployed_service_id'] == SERVICEPOOLS_DATA[SINGLE_IP_SERVICEPOOL_IDX]['id']:
userservice = typing.cast('deployment.IPMachineUserService', models.UserService.objects.get(uuid=userservice_data['uuid']).get_instance())
self.assertEqual(userservice._ip, 'dc.dkmon.local~1') # Same as original data
else:
userservice = typing.cast('deployment_multi.IPMachinesUserService', models.UserService.objects.get(uuid=userservice_data['uuid']).get_instance())
self.assertEqual(userservice._ip, userservice_data['unique_id'], f'Invalid IP for {userservice_data["unique_id"]}: {userservice._ip}')