mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-03 01:17:56 +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__)
|
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
|
# 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
|
# Note: As of version 4.0, all previous REMOVED values has been moved to migration script 0043
|
||||||
REMOVED_CONFIG_ELEMENTS = {
|
REMOVED_CONFIG_ELEMENTS = {
|
||||||
@ -72,7 +65,22 @@ REMOVED_CONFIG_ELEMENTS = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
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"
|
# Fields types, so inputs get more "beautiful"
|
||||||
class FieldType(enum.IntEnum):
|
class FieldType(enum.IntEnum):
|
||||||
UNKNOWN = -1
|
UNKNOWN = -1
|
||||||
@ -158,12 +166,12 @@ class Config:
|
|||||||
logger.debug(self)
|
logger.debug(self)
|
||||||
|
|
||||||
def get(self, force: bool = False) -> str:
|
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():
|
if not GlobalConfig.isInitialized():
|
||||||
logger.debug('Initializing configuration & updating db values')
|
logger.debug('Initializing configuration & updating db values')
|
||||||
GlobalConfig.initialize()
|
GlobalConfig.initialize()
|
||||||
else:
|
else:
|
||||||
_for_recovering_later.append(self)
|
Config._for_recovering_later.append(self)
|
||||||
return self._default
|
return self._default
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -194,7 +202,7 @@ class Config:
|
|||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
def set_params(self, params: typing.Any) -> None:
|
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:
|
def as_int(self, force: bool = False) -> int:
|
||||||
try:
|
try:
|
||||||
@ -233,14 +241,14 @@ class Config:
|
|||||||
return self._type
|
return self._type
|
||||||
|
|
||||||
def get_params(self) -> typing.Any:
|
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:
|
def get_help(self) -> str:
|
||||||
return gettext(self._help)
|
return gettext(self._help)
|
||||||
|
|
||||||
def set(self, value: typing.Union[str, bool, int]) -> None:
|
def set(self, value: typing.Union[str, bool, int]) -> None:
|
||||||
if GlobalConfig.isInitialized() is False or _is_migrating is True:
|
if GlobalConfig.isInitialized() is False or Config._is_migrating is True:
|
||||||
_for_saving_later.append((self, value))
|
Config._for_saving_later.append((self, value))
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
@ -311,9 +319,7 @@ class Config:
|
|||||||
def update(section: 'Config.SectionType', key: str, value: str, check_type: bool = False) -> bool:
|
def update(section: 'Config.SectionType', key: str, value: str, check_type: bool = False) -> bool:
|
||||||
# If cfg value does not exists, simply ignore request
|
# If cfg value does not exists, simply ignore request
|
||||||
try:
|
try:
|
||||||
cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[
|
cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[0]
|
||||||
0
|
|
||||||
]
|
|
||||||
if check_type and cfg.field_type in (
|
if check_type and cfg.field_type in (
|
||||||
Config.FieldType.READ,
|
Config.FieldType.READ,
|
||||||
Config.FieldType.HIDDEN,
|
Config.FieldType.HIDDEN,
|
||||||
@ -772,16 +778,14 @@ class GlobalConfig:
|
|||||||
help=_('Enable VNC menu for user services'),
|
help=_('Enable VNC menu for user services'),
|
||||||
)
|
)
|
||||||
|
|
||||||
_initDone = False
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def isInitialized() -> bool:
|
def isInitialized() -> bool:
|
||||||
return GlobalConfig._initDone
|
return Config._initialization_finished
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def initialize() -> None:
|
def initialize() -> None:
|
||||||
if GlobalConfig._initDone is False:
|
if Config._initialization_finished is False:
|
||||||
GlobalConfig._initDone = True
|
Config._initialization_finished = True
|
||||||
try:
|
try:
|
||||||
# Tries to initialize database data for global config so it is stored asap and get cached for use
|
# Tries to initialize database data for global config so it is stored asap and get cached for use
|
||||||
for v in GlobalConfig.__dict__.values():
|
for v in GlobalConfig.__dict__.values():
|
||||||
@ -789,16 +793,16 @@ class GlobalConfig:
|
|||||||
v.get()
|
v.get()
|
||||||
logger.debug('Initialized global config value %s=%s', v.key(), 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)
|
logger.debug('Get later: %s', c)
|
||||||
c.get()
|
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)
|
logger.debug('Saving delayed value: %s', c)
|
||||||
c.set(v)
|
c.set(v)
|
||||||
_for_saving_later[:] = [] # pyright: ignore[reportUnknownArgumentType]
|
Config._for_saving_later[:] = [] # pyright: ignore[reportUnknownArgumentType]
|
||||||
|
|
||||||
# Process some global config parameters
|
# Process some global config parameters
|
||||||
# GlobalConfig.UDS_THEME.setParams(['html5', 'semantic'])
|
# GlobalConfig.UDS_THEME.setParams(['html5', 'semantic'])
|
||||||
@ -810,14 +814,12 @@ class GlobalConfig:
|
|||||||
# Signals for avoid saving config values on migrations
|
# Signals for avoid saving config values on migrations
|
||||||
def _pre_migrate(sender: typing.Any, **kwargs: typing.Any) -> None:
|
def _pre_migrate(sender: typing.Any, **kwargs: typing.Any) -> None:
|
||||||
# logger.info('Migrating database, AVOID saving config values')
|
# logger.info('Migrating database, AVOID saving config values')
|
||||||
global _is_migrating
|
Config._is_migrating = True
|
||||||
_is_migrating = True
|
|
||||||
|
|
||||||
|
|
||||||
def _post_migrate(sender: typing.Any, **kwargs: typing.Any) -> None:
|
def _post_migrate(sender: typing.Any, **kwargs: typing.Any) -> None:
|
||||||
# logger.info('Migration DONE, ALLOWING saving config values')
|
# logger.info('Migration DONE, ALLOWING saving config values')
|
||||||
global _is_migrating
|
Config._is_migrating = False
|
||||||
_is_migrating = False
|
|
||||||
|
|
||||||
|
|
||||||
signals.pre_migrate.connect(_pre_migrate)
|
signals.pre_migrate.connect(_pre_migrate)
|
||||||
|
@ -50,16 +50,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class IPSingleMachineService(services.Service):
|
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
|
# Description of service
|
||||||
type_name = _('Static Single IP')
|
type_name = _('Static Single IP')
|
||||||
type_type = 'IPSingleMachineService'
|
type_type = 'IPSingleMachineService'
|
||||||
@ -75,6 +65,17 @@ class IPSingleMachineService(services.Service):
|
|||||||
|
|
||||||
services_type_provided = types.services.ServiceType.VDI
|
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]:
|
def get_host_mac(self) -> typing.Tuple[str, str]:
|
||||||
if ';' in self.host.as_str():
|
if ';' in self.host.as_str():
|
||||||
return typing.cast(tuple[str, str], tuple(self.host.as_str().split(';', 2)[:2]))
|
return typing.cast(tuple[str, str], tuple(self.host.as_str().split(';', 2)[:2]))
|
||||||
|
@ -34,11 +34,21 @@ import typing
|
|||||||
import datetime
|
import datetime
|
||||||
from unittest import mock
|
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 ...utils.test import UDSTransactionTestCase
|
||||||
|
|
||||||
from uds import models
|
|
||||||
from uds.migrations.fixers.providers_v4 import physical_machine_multiple
|
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
|
# Data from 3.6 version
|
||||||
|
|
||||||
PROVIDER_DATA: typing.Final[dict[str, typing.Any]] = {
|
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]]] = [
|
SERVICEPOOLS_DATA: typing.Final[list[dict[str, typing.Any]]] = [
|
||||||
{
|
{
|
||||||
'id': 100,
|
'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]]] = [
|
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',
|
'uuid': 'f1ac7d5-58c8-55c3-8bab-24ea1ed40be5',
|
||||||
'deployed_service_id': 100,
|
'deployed_service_id': 100,
|
||||||
'publication_id': None,
|
'publication_id': None,
|
||||||
'unique_id': 'dc.dkmon.local',
|
'unique_id': 'localhost',
|
||||||
'friendly_name': 'dc.dkmon.local',
|
'friendly_name': 'localhost',
|
||||||
'state': 'U',
|
'state': 'U',
|
||||||
'os_state': 'U',
|
'os_state': 'U',
|
||||||
'state_date': datetime.datetime(2024, 4, 25, 2, 51, 13),
|
'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',
|
'owner': 't-service-144',
|
||||||
'key': '848d16fb421048c690c9761c11dc1699',
|
'key': '848d16fb421048c690c9761c11dc1699',
|
||||||
'data': 'gASVQwAAAAAAAABdlCiMDTE3Mi4yNy4xLjI1fjCUjA0xNzIuMjcuMS4yNn4xlIwNMTcyLjI3LjEu\nMjd+MpSMC2xvY2FsaG9zdH4zlGUu\n',
|
'data': 'gASVVQAAAAAAAABdlCiMDTE3Mi4yNy4xLjI1fjCUjA0xNzIuMjcuMS4yNn4xlIwNMTcyLjI3LjEuMjd+MpSMHWxvY2FsaG9zdDswMToyMzo0NTo2Nzo4OTpBQn4zlGUu\n',
|
||||||
'attr1': '',
|
'attr1': '',
|
||||||
},
|
},
|
||||||
{'owner': 't-service-142', 'key': 'b6ac33477ae0a82fa2681c4d398d88d7', 'data': 'gARLAS4=\n', 'attr1': ''},
|
{'owner': 't-service-142', 'key': 'b6ac33477ae0a82fa2681c4d398d88d7', 'data': 'gARLAS4=\n', 'attr1': ''},
|
||||||
@ -251,7 +266,7 @@ class TestPhysicalMigration(UDSTransactionTestCase):
|
|||||||
"""
|
"""
|
||||||
# We have 2 services:
|
# We have 2 services:
|
||||||
# - Single IP
|
# - 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 service pool
|
||||||
# - One user service
|
# - One user service
|
||||||
# - Multiple IP
|
# - Multiple IP
|
||||||
@ -267,13 +282,55 @@ class TestPhysicalMigration(UDSTransactionTestCase):
|
|||||||
# - 172.27.1.25
|
# - 172.27.1.25
|
||||||
# - 172.27.1.26
|
# - 172.27.1.26
|
||||||
# - 172.27.1.27
|
# - 172.27.1.27
|
||||||
# - localhost
|
# - localhost;01:23:45:67:89:AB
|
||||||
# - One service pool
|
# - One service pool
|
||||||
# - Two user services
|
# - 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
|
# * Second one is 172.27.1.26
|
||||||
|
|
||||||
# First, proceed to migration of data
|
# First, proceed to migration of data
|
||||||
physical_machine_multiple.migrate(self.apps_mock(), None)
|
physical_machine_multiple.migrate(self.apps_mock(), None)
|
||||||
|
|
||||||
# Now check that data has been migrated correctly
|
# 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