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:
parent
c39c8c9583
commit
30bbf1ef0f
@ -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)
|
||||
|
@ -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():
|
||||
|
@ -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}')
|
||||
|
Loading…
Reference in New Issue
Block a user