1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-11-17 08:24:12 +03:00

22 Commits

Author SHA1 Message Date
Adolfo Gómez García
1780d85bc1 Handle potential None value for storage status in StorageInfo 2025-11-13 16:07:20 +01:00
Adolfo Gómez García
2c31b7da36 Fix assertion message in ServerManagerManagedServersTest to clarify counter logic 2025-11-12 03:39:58 +01:00
Adolfo Gómez García
bc950acf1d Remove redundant 'ha_restart_priority' entry from test client configuration 2025-11-12 03:37:10 +01:00
Adolfo Gómez García
d511451997 Refactor _check_machine_state method to improve parameter naming and simplify state checking logic 2025-11-12 03:05:23 +01:00
Adolfo Gómez García
1096edf79f Fix file unlinking logic by ensuring the temporary file is added for unlinking 2025-11-04 16:37:18 +01:00
Adolfo Gómez García
bd7ab4f22e Refactor task manager to ensure at least one scheduler and delayed task thread is started 2025-11-03 16:48:08 +01:00
Adolfo Gómez García
7df6babad0 Update log maintenance frequency to run hourly 2025-11-01 05:00:10 +01:00
Adolfo Gómez García
335c1a8c05 Refactor log maintenance logic to improve clarity and efficiency in log deletion process 2025-11-01 04:59:04 +01:00
Adolfo Gómez García
b07a062ad6 Refactor log maintenance to improve query readability and optimize old log retention logic 2025-11-01 03:53:39 +01:00
Adolfo Gómez García
a099e37c16 Fixed "eager" and incorrect log treating 2025-11-01 03:41:15 +01:00
Adolfo Gómez García
88b3dc8d0c Change post method return type from MutableMapping to dict for TunnelRegister class 2025-10-30 19:17:06 +01:00
Adolfo Gómez García
8fed907c73 Add support for methods requiring 'self' in autospec side effects 2025-10-30 17:27:15 +01:00
Adolfo Gómez García
bb2a097d86 Refactor autospec function to replace inner_data with test_data and add getter/setter methods for test data management 2025-10-30 17:21:11 +01:00
Adolfo Gómez García
3398762cb4 Refactor autospec function to improve readability and add inner_data parameter 2025-10-30 17:08:14 +01:00
Adolfo Gómez
b85beacc22 Merge pull request #142 from VirtualCable/dev/andres/v4.0-req
Dev/andres/v4.0 req
2025-10-30 16:47:39 +01:00
aschumann-virtualcable
2a3ed794c4 Merge branch 'dev/andres/v4.0' into dev/andres/v4.0-req 2025-10-30 16:42:00 +01:00
aschumann-virtualcable
5057131b47 Enhance global scope for compatibility with older UDS clients by ensuring necessary modules and functions are accessible 2025-10-30 16:28:06 +01:00
Adolfo Gómez García
48c3c07f0f Fix remote viewer path detection in SPICE scripts to match 'VirtViewer' directory naming 2025-10-28 17:51:43 +01:00
Adolfo Gómez García
49d9bbb6f7 Refactor remote viewer path detection in SPICE scripts to use 'bin' directory for executable search 2025-10-28 17:22:30 +01:00
Adolfo Gómez García
48cc171ba4 Initialize executable variable in SPICE scripts to enhance remote viewer path detection 2025-10-28 17:16:54 +01:00
Adolfo Gómez García
551740fccc Refactor remote viewer path detection in SPICE scripts to improve executable search logic 2025-10-28 16:51:04 +01:00
aschumann-virtualcable
398ec1aac0 Merge remote-tracking branch 'origin/v4.0' into dev/andres/v4.0 2025-09-25 12:13:11 +02:00
20 changed files with 151 additions and 68 deletions

View File

@@ -153,7 +153,7 @@ class TunnelRegister(ServerRegisterBase):
name = 'register'
# Just a compatibility method for old tunnel servers
def post(self) -> collections.abc.MutableMapping[str, typing.Any]:
def post(self) -> dict[str, typing.Any]:
self._params['type'] = types.servers.ServerType.TUNNEL
self._params['os'] = self._params.get(
'os', types.os.KnownOS.LINUX.os_name()

View File

@@ -46,7 +46,7 @@ logger = logging.getLogger(__name__)
class BaseThread(threading.Thread, abc.ABC):
@abc.abstractmethod
def request_stop(self) -> None:
raise NotImplementedError
@@ -108,7 +108,9 @@ class TaskManager(metaclass=singleton.Singleton):
def add_other_tasks(self) -> None:
logger.info("Registering other tasks")
from uds.core.messaging.processor import MessageProcessorThread # pylint: disable=import-outside-toplevel
from uds.core.messaging.processor import (
MessageProcessorThread,
) # pylint: disable=import-outside-toplevel
thread = MessageProcessorThread()
thread.start()
@@ -126,11 +128,14 @@ class TaskManager(metaclass=singleton.Singleton):
self.register_scheduled_tasks()
n_schedulers: int = GlobalConfig.SCHEDULER_THREADS.as_int()
n_delayed_tasks: int = GlobalConfig.DELAYED_TASKS_THREADS.as_int()
if n_schedulers < 1:
n_schedulers = 1 # At least one scheduler
logger.info(
'Starting %s schedulers and %s task executors', n_schedulers, n_delayed_tasks
)
n_delayed_tasks: int = GlobalConfig.DELAYED_TASKS_THREADS.as_int()
if n_delayed_tasks < 1:
n_delayed_tasks = 1 # At least one delayed task
logger.info('Starting %s schedulers and %s task executors', n_schedulers, n_delayed_tasks)
signal.signal(signal.SIGTERM, TaskManager.sig_term)
signal.signal(signal.SIGINT, TaskManager.sig_term)

View File

@@ -144,7 +144,7 @@ class StorageInfo:
type=StorageType.from_str(storage.type.value),
available=storage.available,
used=storage.used,
status=StorageStatus.from_str(storage.status.value),
status=StorageStatus.from_str(storage.status.value if storage.status else 'unknown'),
)

View File

@@ -201,12 +201,12 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
Operation.FINISH,
]
def _check_machine_state(self, state: on.types.VmState) -> types.states.TaskState:
def _check_machine_state(self, check_state: on.types.VmState) -> types.states.TaskState:
logger.debug(
'Checking that state of machine %s (%s) is %s',
self._vmid,
self._name,
state,
check_state,
)
state = self.service().get_machine_state(self._vmid)
@@ -219,12 +219,8 @@ class OpenNebulaLiveDeployment(services.UserService, autoserializable.AutoSerial
ret = types.states.TaskState.RUNNING
if isinstance(state, (list, tuple)):
if state in state:
ret = types.states.TaskState.FINISHED
else:
if state == state:
ret = types.states.TaskState.FINISHED
if state == check_state:
ret = types.states.TaskState.FINISHED
return ret

View File

@@ -5,17 +5,30 @@ import os.path
import shutil
import os
# Asegura que subprocess, shutil, os, os.path y typing estén en el scope global para clientes antiguos (3.6)
globals()['subprocess'] = subprocess
globals()['shutil'] = shutil
globals()['os'] = os
globals()['os.path'] = os.path
globals()['typing'] = typing
logger = logging.getLogger(__name__)
# On older client versions, need importing globally to allow inner functions to work
import subprocess # type: ignore
try:
from uds.log import logger # For UDS Clients 3.6
except ImportError:
logger = logging.getLogger(__name__) # For UDS Clients 4.0
# También asegura logger en globales
globals()['logger'] = logger
# Avoid type checking annoing errors
try:
from uds import tools # type: ignore
except ImportError:
tools: typing.Any = None
raise
# Asegura tools en globales
globals()['tools'] = tools
if 'sp' not in globals():
# Inject local passed sp into globals for inner functions if not already there
@@ -64,6 +77,13 @@ def exec_thincast(thincast: str) -> None:
params = [os.path.expandvars(i) for i in [thincast] + sp['as_new_xfreerdp_params'] + [f'/v:{sp["address"]}']] # type: ignore
_exec_client_with_params(thincast, params)
# Añade las funciones al scope global para clientes antiguos (3.6)
globals()['_prepare_rdp_file'] = _prepare_rdp_file
globals()['_exec_client_with_params'] = _exec_client_with_params
globals()['exec_udsrdp'] = exec_udsrdp
globals()['exec_new_xfreerdp'] = exec_new_xfreerdp
globals()['exec_thincast'] = exec_thincast
# Typical Thincast Routes on Linux
thincast_list = [
'/usr/bin/thincast-remote-desktop-client',
@@ -106,7 +126,7 @@ if not executable:
'''
)
else:
logging.debug(f'RDP client found: {executable} of kind {kind}')
logger.debug(f'RDP client found: {executable} of kind {kind}')
# Execute the client found
if kind == 'thincast':

View File

@@ -1 +1 @@
Y7IpJNU/ne7JUqmOqNPu5gPlCWRDjK6nYKK69K6yxjEu7PT6g1ZkGlrNS2QeQY9bvOuxTlWF8nF+inbQTUQOte6rIX8kSZUJFcOKbxxcWDJiJvPArE5iKCJPpPPHFFH97MVVDYuYT4C5XvR7bDQNVlmxB6oUGvO/71mC4D3gxau1FA24fISRio3aCUVWFjEBlt55PyVhg9qn4r+jBP4bwBVW1NmmjONsIrf6+pvI3gIspYIwzBg9Nflv67aKY7tLdylkcQ7lHx2pY20onaDuT+WGRlQdqc4K0kAzZyTO9FEe/SGe0Nsi0BPMBK1LjFtNJ0KQABLWoTZWSXgloY/2JQvoOC1Z57K4g9R8Nq2BDNpCWADz2nP713zt30dK4Emg6Uh3lkew4zF98atY9Mb5bdNmzQp3dCv9xGAGPm66eIqQUEKDt7zone6j6r81c8jPbl7m/9f/iul8d3a4bPEv5DMHTL23YdFSc6AHDp32ngaB4qLxgtDCvQhnkrayqiYHOlYnS1A/TBS7c2K/Od0nZvuxZKUuDiC7RlSxEAo/EdmWSbeA01cYeh7v0lkdkQaOMGI45DPjLaCwuhgb85cbHcFEDo1CpZSYhyhqApx15/0Jt/CIuFM3FG0cf4B6/6T7Ll7IqGCeTl6nuF2N3ux1ND7UAMBpqjWAlg9/Y7T+aq4=
QvtwKeb/U+eyrCDzuaYDwqzgP0zSbKOo49gNPDs4h9w12D6NJsn4F8fsyaUQ7H8PQaIID+lThQGPGTKKYN0wNrhFHYx+PchgeyWOJkfvjlPDxu4JKkwmdlkvHAkfHjW7rJbzh90UkhJmRev3fX7mMAqNBUSbXEcL0/mB31iELzhMYY+RHrMrMpcIO86R0XAxNcaEnJhSW4gQuXonhRf6vrRfWbexqce9+XtftqxbOINlSgRFaFWdZu0Mtdm2kQ2FWq9XGxebha2+5PC1PRBhoxCRip2fEcTn9PzEy8gOjhm9masK8UJ/isVcfiM/ISJPbyDz3jSPV9Z+xtYkcrVQKV+GwvPuj3GCdMInxzAkUEPPNVTnqupHKfXXjAhi+sYZHO+TC0F79FiydxZmxMrJjcZbt+e1rCSDSzKdXn/WTHAuwTtwUj8cbrkrEwHrZWtQzgSGHSK/PlunZD7ASYg6QP7Dq8HRaYT1RmRmjH0tVzs/7y/HYMwdg0MI17zJcEyG7Q67PPa/RcUeSZ4eOMetnTnwPv29u9KpuAZm2P8mn+y4dMaRTC2uAARIHg77khWbVElhoO8JpVb31yHeLENvBmRxZv3Gli404cmzrH66N38xWHadZXYP6fiErptLGPdsL0sGKZRHbAZUjqppx5qwHmHWaNuBY2XOfIrlFvOr5UQ=

View File

@@ -1,11 +1,21 @@
import typing
import shutil
import os
import logging
import subprocess
import os.path
logger = logging.getLogger(__name__)
# Asegura que subprocess y shutil estén en el scope global para clientes antiguos (3.6)
globals()['subprocess'] = subprocess
globals()['shutil'] = shutil
globals()['os'] = os
globals()['os.path'] = os.path
globals()['typing'] = typing
try:
from uds.log import logger # For UDS Clients 3.6
except ImportError:
import logger
logger = logger.getLogger(__name__) # For UDS Clients 4.0
# On older client versions, need importing globally to allow inner functions to work
import subprocess # type: ignore
@@ -55,7 +65,7 @@ def _exec_client_with_params(executable: str, params: typing.List[str], unlink_f
tools.addFileToUnlink(unlink_file)
def exec_udsrdp(udsrdp: str, port: int) -> None:
logging.debug('UDSRDP client will use command line parameters')
logger.debug('UDSRDP client will use command line parameters')
params: typing.List[str] = [os.path.expandvars(i) for i in [app] + sp['as_new_xfreerdp_params'] + [f'/v:127.0.0.1:{port}']] # type: ignore
_exec_client_with_params(udsrdp, params)
@@ -66,7 +76,7 @@ def exec_new_xfreerdp(xfreerdp: str, port: int) -> None:
params = [xfreerdp, dest_filename, f'/p:{sp.get("password", "")}'] # type: ignore
_exec_client_with_params(xfreerdp, params, unlink_file=dest_filename)
else:
logging.debug('XFREERDP client will use command line parameters')
logger.debug('XFREERDP client will use command line parameters (xfreerdp)')
params: typing.List[str] = [os.path.expandvars(i) for i in [app] + sp['as_new_xfreerdp_params'] + [f'/v:127.0.0.1:{port}']] # type: ignore
_exec_client_with_params(xfreerdp, params)
@@ -77,7 +87,7 @@ def exec_thincast(thincast: str, port: int) -> None:
params = [thincast, dest_filename, f'/p:{sp.get("password", "")}'] # type: ignore
_exec_client_with_params(thincast, params, unlink_file=dest_filename)
else:
logging.debug('Thincast client will use command line parameters')
logger.debug('Thincast client will use command line parameters (xfreerdp)')
params: typing.List[str] = [os.path.expandvars(i) for i in [app] + sp['as_new_xfreerdp_params'] + [f'/v:127.0.0.1:{port}']] # type: ignore
_exec_client_with_params(thincast, params)
@@ -108,8 +118,7 @@ if fs.check() is False:
# If thincast exists, use it. If not, continue with UDSRDP/XFREERDP as before
if thincast_executable:
logging.debug('Thincast client found, using it')
#logging.debug(f'RDP file params: {sp.get("as_file", "")}')
logger.debug('Thincast client found, using it')
fnc, app = exec_thincast, thincast_executable
else:
xfreerdp: typing.Optional[str] = tools.findApp('xfreerdp3') or tools.findApp('xfreerdp') or tools.findApp('xfreerdp2')
@@ -130,5 +139,16 @@ else:
'''
)
# Asegura que app y fnc sean globales para clientes antiguos (3.6)
globals()['app'] = app
globals()['fnc'] = fnc
# Añade las funciones al scope global para clientes antiguos (3.6)
globals()['_prepare_rdp_file'] = _prepare_rdp_file
globals()['_exec_client_with_params'] = _exec_client_with_params
globals()['exec_udsrdp'] = exec_udsrdp
globals()['exec_new_xfreerdp'] = exec_new_xfreerdp
globals()['exec_thincast'] = exec_thincast
if fnc is not None and app is not None:
fnc(app, fs.server_address[1])

View File

@@ -1 +1 @@
n3a+b8OD5i8IAbP/fK2c72A1UzRScr7kH2BeY4zSaVJoJf+Xn4+F6Osm0emtS+1muStwHvIE9Hqnxdg8lbYHIrjRAGvFazs5xcExIvYU1rVA/gmBjaViqUl6SQc+3zWa7xXwOVrwQdoXF9WGYOAH8DAPT/aiKgeaCoKlCFNyrxlMImxWPlWRvCKrZQlWxvB6rKFC0apO+xxi4FU3xXIomWBufnLuDppYL+xwVzMj4YNWec3rdV6RT8cpBQzfkNec7rgHBOB+Py2BnY54TtMqlECpXZlEYy4NVORI0rwZ+aK8dvpGvRS4nWQnq3edRkWTzDZAo+Kkp17/fmCTEGucQ/TC4Y2qBoB35kxQmv1aWhWWyJ/BdpHI6lNJR884lK/yup3I+vQbf7ZJzu6wT98YHk0x+rsPt97FPxJsVdsxWG4IcaVgheuIjlYPNShMDoy/slOyEz/k0j7jWAhRu0WWMHjI/ozighX+jHmNeIG9EPim0sKZ/04okSXxhwwC1UzwcevUdrI2eUyaUEbq9Ua3SMCuQNmDVKFlJJ6PUucbWvgKsdsSMbW1L4syA41yTPk9dcYHlu32k8oaZ1o3QIDw7Do48pygwnWSJM9XlynFcRcYmOikRIrINJlJmKH6oY+ez9nva7cAV6rFel+jk7bgSZUrKMfdsnsLh1PSabnLJ/s=
PwNGSn1G1RSI6m56VoamK3c3DASZhca6H9ejQ8gzX3Izny7c/JjKAd6vU9/BL7vyM9a/46UaOPDH+R9kD7j6rbtvwo/32ULDqGU9Dt+9taRyzediIdfwg/XkI5A2H7pxamXiFWAwLW24FFSBEa5/DBPSSjBwFaEeJbGm6IqSuDfJQdR4C/6uy0wbwakMUhMsY6ZcUYB7QOKa4sKF6jQorjZ+RUHkkPiEtTCZvjP56c743B0nZ5qqBlA6sOV4vUPDntG8RSwdo6ds73foz8+Q/13qlTCh5IA7E+S2HoxRuF3nPDUL6ML1wHfWLlUnAQjrmYtD59YDh1PCBVuqasHZ6Qe4ShXepSwIzJUp4bmd3gQh6U7FMfbv0T9e9gx7WYQ1LUNJtpLXM0c84cLGAO0a6sPBJZ6S6R5Pxc9OCRxwykkBIm9FEjE8+8bcDyRGiTRtw/t7HUHP4gUM06sp/wnx63COKGzk9GBb+vMcx9VHH5sAZKs3Ghs8DB/7z4C8iIsIqBxVesWYAJQlngIXwxTAw9llrFnSTDPEmB9e2HgsWejezE35s1bICF5r0L9LxxCcqna+YkGFrgAjo9to9BW3WjRMmcRqvEQmucwCEcqaVJXBw2ffvGZEozrJ6HSOCpKKd4Hz2SxWS8MaQQHYMlysPo4I6pyPXGPHT4SCGL+XoNg=

View File

@@ -51,4 +51,4 @@ if executable is None:
subprocess.Popen([executable, filename]) # nosec
# tools.addFileToUnlink(filename)
tools.addFileToUnlink(filename)

View File

@@ -1 +1 @@
R3sdB3d7hhz6VEoAGNzdX0vIKgss4Xe1aEU62cTQKIA7Inw2zHkaBK0wRVxZqMYH5Of/iiyMiG+NlKFAWutSrOOthoyVT83RNtDpR4F+vDTZMpW2qopiiK6XbxX5PuJq/E3UWNsnElZasAIpJk9rKqlNEJ+tkycWyDkIYIHxE6M2yrqH+dWx/V3AaDMURtnzuZStzpxPMuEzezNdCI9Kbs1fCwSz18SRSkAtNINiMJbgBV573wwKEUUgKLzRHn1ijRk65OHRVLFDlRgXz/ArN0/nkW/FkQ9pYJW5MosD4TJfptO8QZ104jYPM960maMxObLWa4LZyzEVNZ2uIE9xIWHh9SCZ3zv37DfSU6hM9n4gOKlIVGIO5hrctjZLzAwRcgsBFcXLXxtlrHQERjh7Zgr5mevjXz0yO+76pUbdgEWVwlT8G5SG4yq7083drwfNgMv5uht8GyRqes+FD952ECjv+hVeoLRItg+mxaUnILHHJOyw8A47Ex0PikHzpbT/SxzQA+CkxEMRPSD6GDPBYiKBAYm+b0Yazo5Yzo85S2GRubPaEVp6UQ6AVgWEtsfhx5yeU+tJx6AD1+MYEpB04Re6XjMeSV5zdDsHkS/HQ7kafbxS0RxU78e0LEltbW06zGNGvk/cZlIr1SE93Ztvyi1R4wZNdDRaM6vLKOD3RGo=
b0NrFPNRgkQmGycaL/gUhFKShW34N3Yto33JyDT9ructKOTEzT8qCEnvp5ypb0vwZQBhCfya0ExGDO77DdRPb2QAvtQylPxaX+D7FdLAKZO8WOw+clCJGJHFlpiAi7a1lY0ve6dfJotu47vNppmOy5RGO1Iz9FMQuOpq0xNXcrGz9I5zez47Se0FkhU1XlYgrrI8uexQqc8faz+nw6fJE4ADnfqo+b6mJmRIm7gbE9VyMZz6NR65zqtcyOWgmRrDOO3w6dirEYOIES2GFfZXOl4L+5bIDTVbtrYGoTtPIgmom8fjFfOP2qWAhjQ7jsjDqC0pPshOlNqB4FyORoAEzQ10yt53bPJHaOe/9uzW75THNGCj8AVntzbLGDghdJG49Yv9gAxJPFpdkGhtesy92Q0pryDjtTtLBtTyWvj9iCpUremYp71tROFHdEY40ypG7YDmDHNdkK6vz99MsFwHpcjs9XnHAJlaJHy96FdI6dHBC4ePlaJSVABOb9SS74WyYVB/VOF6bZ55mbvD7XpzzsG7fk/JV6If047tULGnCdWJCvOZ05rI0H1nUJAwgg42VmOKxNKJnBKdP0hVPuvRg2L2pNDioocXxnXvYfUWBr6bq/6Vkv/qrkkkWy+XMhTSGD9nwskhpFdOMNfjeelr50bSGcl2QGzEO2SnKzrfdTo=

View File

@@ -11,12 +11,14 @@ from uds import tools # type: ignore
# Lets find remote viewer
# There is a bug that when installed, the remote viewer (at least 64 bits version) does not store correctly its path, so lets find it "a las bravas"
extraPaths = ()
executable = None
for env in ('PROGRAMFILES', 'PROGRAMW6432'):
if env in os.environ:
extraPaths += tuple(p + '\\bin' for p in glob.glob(os.environ[env] + '\\VirtViewer*')) # type: ignore
executable = tools.findApp('remote-viewer.exe', extraPaths)
for base_folder in glob.glob(os.environ[env] + '\\VirtViewer*'):
executable = tools.findApp('remote-viewer.exe', os.path.join(base_folder, 'bin'))
if executable is not None:
break
if executable is None:
raise Exception(

View File

@@ -1 +1 @@
KztBdwmg7Pz6I0Qu0MIGN8M/TC5ggVSjeqpWNK5fmi63zIRv1J2Mpv/v+BbFms/T6veQqJDQ7IngTavo3GMcJZxbLyGyTu192Hzj29nmxTWMTyhuUWEP2ye+0ctVRqz3L6qiJuaQ0b6lQwWTdXq858XvtB04+BVaEgpWXkKHRwdl1P1ra++pEOm4bFBblArpQxLwBoOMyljNJRC9SP7/zvYnXc2cbi/VJXowy9Mk4jUckKyIyCurUblNU54CVgQE7YULx7YHy7LPTxZ8Ul9VujEJsMkGKKpanmZwM8LirJQTqFBLUxvmLWMcQXGoKHMIiX8aBWpUYD9MB+Xf1XmPOXaC12XVMKckCUtoSisp9C4/Hk34P5Z/uXA5QGvNbnUozjgJnYcJXBPE9xyVV91t02YefANBe2C20cxeJfMi8ya36grA6rr0XAz11pHarji9IfHxNx6CGtsGNuA6zQC3Sj0VBgwV5i0ZUBFnNU16masMHm2i9t4AZCos81HuD/K2qNawFDOVGNvSrFtDqO5XdKoQd79jwKc/9EH1d59tQk23QNEcZFN1k4izdQ1JiA0EJBS7MdQJfqJ1/JdxFY+HMPWCfw7CmD0E9kTCL+iY6+iqEP6Mxi66ZWxVuKkur9vB+yzkzpocq3f8t0Eq06Y6rQM5j/sH6WxA9nAnUA1oG3g=
cH1rujpb4TXrilcSsjLckNv8xDUILhuLl4Ree3LpGOYY9fmDnIYU3xX9TzJAGhIfBonUuwdG1/m/y89SgyfFihQp7YN2DIyIp0Rm9nzMo12u6efNPO4Ae8X9UweNedKie9E9giMEPTMRSglhkSyMbV9pRRh5gPu49+jNRRMbs7Yhsx9MVaJnfSXrCVMx13aZTr0tCEkvZqcex6DYd0TwVcWN5fZgB7haCUQ//FCFXIVxwj72EBtwcIqDeDi97JOLXmLQ3sQo6zNeB0KutQGagNhvYsfCdkUQZPJDK+UrvXBg1q7IjCFC5CuilFzxcDcetmzAWxG1kKotkFfOPhRYYyUvWdrRvVKbRFrfDeFhWOfWzqhQokvRBes52s4YoQygnAsnamkYr9r27SHDLKyz/6i2eI7LiTdCG3P/LaPZI9alxkgjTKfqXqyaEed+5tWNXrniffZH4liIK+YtGVI5DpdsXbxOiQF8TeJOTyG4kLPjstS1dvc7OeP59BwCohGBR1Ebcmj4cjh/H112zzfMs1C9UuN8Jg3E+BeSTGsqObMti51Z+/FL6UA4OYx0WitXigxBeRtYxC9PlmXXpt4V/Wv3j4/fnDK94q6VUtnMq/PL0kMt6XiIjE5vTjm1LhHNiC2xMABFSZ2tBE9up3uFFJ8z6mNTGlJEpfrUHSAGcFY=

View File

@@ -12,12 +12,13 @@ from uds.tunnel import forward # type: ignore
# Lets find remote viewer
# There is a bug that when installed, the remote viewer (at least 64 bits version) does not store correctly its path, so lets find it "a las bravas"
extraPaths = ()
executable = None
for env in ('PROGRAMFILES', 'PROGRAMW6432'):
if env in os.environ:
extraPaths += tuple(p + '\\bin' for p in glob.glob(os.environ[env] + '\\VirtViewer*')) # type: ignore
executable = tools.findApp('remote-viewer.exe', extraPaths)
for base_folder in glob.glob(os.environ[env] + '\\VirtViewer*'):
executable = tools.findApp('remote-viewer.exe', os.path.join(base_folder, 'bin'))
if executable is not None:
break
if executable is None:
raise Exception(
@@ -38,9 +39,7 @@ if sp['ticket']: # type: ignore
# Check that tunnel works..
if fs.check() is False:
raise Exception(
'<p>Could not connect to tunnel server.</p><p>Please, check your network settings.</p>'
)
raise Exception('<p>Could not connect to tunnel server.</p><p>Please, check your network settings.</p>')
fss = None
if sp['ticket_secure']: # type: ignore

View File

@@ -1 +1 @@
nhj3N8ZUuQwdkFb68fTd57q0xEOtZHYiaHgW0xBOE9JKI7AhjUDDSt2Zvvr1QzTX2HzNSLNiQZ0wAPhAd1ol46xAcIL9GVEmJSlSb6BXBfO21YEstVwxM3BbAZlAQOK7e9NtxzeM8O+GcgvxFrLkLxrgnKw1odH8RDQiS8B/Ed1BCoWAS+pNbbHOwMOoL7o8rIPwSho2FBP+ulRb9RiIVfZSyKttJ+ryC+QSCOIPSdW38uk2tKD0cKhDao/kpigwHOPRmWc2jHULXHgOdUzX759Brg7aMX+saFYbd7bq6YvDIJ5V7jCucqnOyMfuXuFuu7sgRvNJibqoNpYPS+WAYM11nv5GXBd4t9+miVKKsh/llhYvKhY0wvUG7REIUYdojXR1yy1JENo+BS7SvI8wQZCKx1//TVNxMTQ3zYu6XzK5HZD+lAUDnnTmgIxWTLjwSDG8nf95i+D0sofVYZCHPYlZh8bkbxNBmk0LHNfT6C3cyFZJSTrYMS1m2O3Siq0bNI9vvNWG0Vvr2dxINReWrI2mvTrVXfN9j6CFAZHI5QY+iypTAXMLp/exR1+iN0K8QXVdNC16xKTDQLAOukK1+IbCNK2T+GZQHtOlPi34mLa5DUOScmpjlcdt/4szRFjGaWdQclj2+gtfXcN4DiqbwhFGw739ayY/CCJB7KfPnak=
WfChDg7NFGG1NjcvcVNA3I0TAiDG6IeRojb51w4K4iMYSUm540o2jHW2FpYD+tmcMQCmf788kw+2R5GHXMA37gXo1qHB1QlkXM0aCTJLp6UyZ8i8CcLsyWHrkjp2NzI24XQLNSYHAJn2zTfhCMd5Zyqw3gZmMTQKmkX+f+t2Oi0FGHwVhCp6ZVCRKLWKEFcc6b/uYFJ6IP9WaSXLg3ftOLw/e+GDzdYsrf9m2ytCBlXtm9XrtyhEszpIs+bseIUTbhKw2FDr2UoORezQPFN8pLZE/WoQ+A87tgfRaf0foc/LOm/Yo5Xkh5p7Q33eYY6jsGWiFWgYugQ3PzLHrK8agpZTYmHJymVUHItvZxGR2w1npIxAyzv6FM7aLhOSi+0nXLotCY01nRzBxCs46kmSapjKi2XpaRVskiM1dPdYJ8TUCKb9c2Gb7MUxVrXnWOgDBZZx5yQJaEKz5JXYlHwHb74xDleqwl93Vbv4KdKvYd/sBwK0uqdpojCzIIvNuwfpBqXrFWORGUnkrnIK0cCw2tA+oqBjMyFlIS6NhLLYfdqvsVPMV8CP3hRlmIX42EVi/f7XLHZQuOJcEhZuLg8RqwrSPX7b4sC/KLjOmXdMsUulwOFyVk39x5Ze6mNjDqxO7RNA4hZ3+6TlZnCbo5keZIEretOk+JwkWCZEbLFz5+I=

View File

@@ -31,6 +31,7 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import datetime
import logging
import typing
from django.db.models import Count
@@ -41,7 +42,7 @@ from uds.core.util.model import sql_now
from uds.core.util import config
# from uds.core.util.config import GlobalConfig
MAX_BATCH_SIZE: typing.Final[int] = 32768
logger = logging.getLogger(__name__)
@@ -52,6 +53,7 @@ class LogMaintenance(Job):
friendly_name = 'Log maintenance'
def run(self) -> None:
logger.debug('Starting Log maintenance')
# Select all disctinct owner_id and owner_type and count of each
# For each one, check if it has more than max_elements, and if so, delete the oldest ones
for owner_id, owner_type, count in (
@@ -68,19 +70,30 @@ class LogMaintenance(Job):
continue
max_elements = owner_type.get_max_elements()
# Ensures that last hour logs are not deleted
removing_before = sql_now() - datetime.timedelta(seconds=3600)
if 0 < max_elements < count: # Negative max elements means "unlimited"
# We will delete the oldest ones
for record in models.Log.objects.filter(
owner_id=owner_id,
owner_type=owner_type,
created__lt=removing_before,
).order_by('created', 'id')[: count - max_elements + 1]:
record.delete()
logger.debug(
'Log maintenance: Owner %s of type %s has %d logs, max is %d, cleaning up',
owner_id,
owner_type.name,
count,
max_elements,
)
ids_to_delete = list(
models.Log.objects.filter(
owner_id=owner_id,
owner_type=owner_type,
)
.order_by('created', 'id')
.values_list('id', flat=True)[max_elements : max_elements + MAX_BATCH_SIZE]
)
if ids_to_delete:
models.Log.objects.filter(id__in=ids_to_delete).delete()
# Also, delete all logs older than config.GlobalConfig.STATS_DURATION.as_int()*2 days
# This is to ensure we do not have "orphan" logs too old
models.Log.objects.filter(
created__lt=sql_now() - datetime.timedelta(days=config.GlobalConfig.STATS_DURATION.as_int() * 2)
).delete()
logger.debug('Log maintenance done')

View File

@@ -288,3 +288,4 @@ class ServiceCacheUpdater(Job):
self.reduce_l2_cache(servicepool_stat)
elif servicepool_stat.is_l2_cache_growth_required(): # We need more L2 items
self.grow_l2_cache(servicepool_stat)
logger.debug('Cache checking done')

View File

@@ -42,7 +42,10 @@ class SystemInformation(Job):
friendly_name = 'System Information update'
def run(self) -> None:
logger.debug('System information started')
try:
cluster.store_cluster_info()
except Exception as e:
logger.error('Error storing cluster hostname: %s', e)
logger.debug('System information done')

View File

@@ -258,8 +258,8 @@ class ServerManagerManagedServersTest(UDSTestCase):
uuid, counter = assignation
# uuid shuld be one on registered servers
self.assertTrue(uuid in self.all_uuids)
# And only one assignment, so counter is 1
self.assertTrue(counter, 1)
# And only assignements so counter is assignations + 1
self.assertTrue(counter, assignations + 1)
# Server locked should be None
self.assertIsNotNone(models.Server.objects.get(uuid=uuid).locked_until)

View File

@@ -202,7 +202,6 @@ class TestXenClient(UDSTransactionTestCase):
'start_delay': 0,
'shutdown_delay': 0,
'order': 0,
'ha_restart_priority': '',
'ha_always_run': False,
'ha_restart_priority': '',
'recommendations': '',

View File

@@ -34,22 +34,29 @@ import functools
import dataclasses
from unittest import mock
@dataclasses.dataclass
class AutoSpecMethodInfo:
name: str|typing.Callable[..., typing.Any]
name: str | typing.Callable[..., typing.Any]
returns: typing.Any = None # Can be a callable or a value
partial_args: typing.Tuple[typing.Any, ...] = ()
partial_kwargs: dict[str, typing.Any] = dataclasses.field(default_factory=dict[str, typing.Any])
def autospec(cls: type, metods_info: collections.abc.Iterable[AutoSpecMethodInfo], **kwargs: typing.Any) -> mock.Mock:
needs_self: bool = False # If the method needs self as first argument (for side_effect callables)
def autospec(
cls: type,
metods_info: collections.abc.Iterable[AutoSpecMethodInfo],
test_data: dict[str, typing.Any]|None = None,
**kwargs: typing.Any
) -> mock.Mock:
"""
This is a helper function that will create a mock object with the same methods as the class passed as parameter.
This is useful for testing purposes, where you want to mock a class and still have the same methods available.
Take some care when using decorators and methods instead of string for its name. Ensure decorator do not hide the original method.
(using functools.wraps or similar will do the trick, but take care of it)
The returned value is in fact a mock object, but with the same methods as the class passed as parameter.
"""
obj = mock.create_autospec(cls, **kwargs)
@@ -58,9 +65,27 @@ def autospec(cls: type, metods_info: collections.abc.Iterable[AutoSpecMethodInfo
name = method_info.name if isinstance(method_info.name, str) else method_info.name.__name__
mck = getattr(obj, name)
if callable(method_info.returns):
mck.side_effect = functools.partial(method_info.returns, *method_info.partial_args, **method_info.partial_kwargs)
#mck.side_effect = method_info.returns
if method_info.needs_self:
def side_effect_with_self(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
return method_info.returns(obj, *args, **kwargs)
mck.side_effect = side_effect_with_self
else:
mck.side_effect = functools.partial(
method_info.returns, *method_info.partial_args, **method_info.partial_kwargs
)
# mck.side_effect = method_info.returns
else:
mck.return_value = method_info.returns
return obj
obj._test_data = dict() if test_data is None else test_data
def get_test_data(attr: str) -> typing.Any:
return obj._test_data.get(attr)
def set_test_data(attr: str, data: typing.Any) -> None:
obj._test_data[attr] = data
obj.get_test_data = get_test_data
obj.set_test_data = set_test_data
return obj