1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-03-11 00:58:39 +03:00

Added functionalty so Fixed Services (at least those derived from Generic one), overrides the max_countent.... because must be always CONSERVATIVE in order to work correctly

This commit is contained in:
Adolfo Gómez García 2024-10-16 18:04:06 +02:00
parent 418e1f7c0b
commit a318e8ca2b
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
7 changed files with 89 additions and 46 deletions

View File

@ -68,12 +68,13 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
@staticmethod
def service_info(item: models.Service) -> dict[str, typing.Any]:
info = item.get_type()
overrided_fields = info.overrided_pools_fields or {}
return {
'icon': info.icon64().replace('\n', ''),
'needs_publication': info.publication_type is not None,
'max_deployed': info.userservices_limit,
'uses_cache': info.uses_cache and info.overrided_fields is None,
'uses_cache': info.uses_cache and overrided_fields.get('uses_cache', True),
'uses_cache_l2': info.uses_cache_l2,
'cache_tooltip': _(info.cache_tooltip),
'cache_tooltip_l2': _(info.cache_tooltip_l2),
@ -144,7 +145,16 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
# Extract item db fields
# We need this fields for all
logger.debug('Saving service for %s / %s', parent, item)
fields = self.fields_from_params(['name', 'comments', 'data_type', 'tags', 'max_services_count_type'])
# Get the sevice type as first step, to obtain "overrided_fields" and other info
service_type = parent.get_instance().get_service_by_type(self._params['data_type'])
if not service_type:
raise exceptions.rest.RequestError('Service type not found')
fields = self.fields_from_params(
['name', 'comments', 'data_type', 'tags', 'max_services_count_type'],
defaults=service_type.overrided_fields,
)
# Fix max_services_count_type to ServicesCountingType enum or ServicesCountingType.STANDARD if not found
try:
fields['max_services_count_type'] = types.services.ServicesCountingType.from_int(
@ -173,7 +183,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
# Store token if this service provides one
service.token = service_instance.get_token() or None # If '', use "None" to
# This may launch an validation exception (the get_instance(...) part)
# This may launch an validation exception (the get_instance(...) part)
service.data = service_instance.serialize()
service.save()
@ -304,10 +314,14 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
},
)
# Remove all overrided fields from editables
overrided_fields = service.overrided_fields or {}
local_gui = [field_gui for field_gui in local_gui if field_gui['name'] not in overrided_fields]
return local_gui
except Exception as e:
logger.exception('getGui')
logger.exception('get_gui')
raise exceptions.rest.ResponseError(str(e)) from e
def get_logs(self, parent: 'Model', item: str) -> list[typing.Any]:

View File

@ -511,8 +511,8 @@ class ServicesPools(ModelHandler):
del fields['osmanager_id']
# If service has "overrided fields", overwrite received ones now
if service_type.overrided_fields:
for k, v in service_type.overrided_fields.items():
if service_type.overrided_pools_fields:
for k, v in service_type.overrided_pools_fields.items():
fields[k] = v
if service_type.uses_cache_l2 is False:

View File

@ -294,7 +294,9 @@ class BaseModelHandler(Handler):
'subtitle': subtitle or '',
}
def fields_from_params(self, fields_list: list[str]) -> dict[str, typing.Any]:
def fields_from_params(
self, fields_list: list[str], *, defaults: 'dict[str, typing.Any]|None' = None
) -> dict[str, typing.Any]:
"""
Reads the indicated fields from the parameters received, and if
:param fields_list: List of required fields
@ -313,7 +315,14 @@ class BaseModelHandler(Handler):
continue
args[k] = self._params.get(k, default)
else:
args[key] = self._params[key]
try:
args[key] = self._params[key]
except KeyError:
if defaults is not None and key in defaults:
args[key] = defaults[key]
else:
raise
# del self._params[key]
except KeyError as e:
raise exceptions.rest.RequestError(f'needed parameter not found in data {e}')

View File

@ -146,33 +146,29 @@ class DetailHandler(BaseModelHandler):
return r
if num_args == 1:
if self._args[0] == consts.rest.OVERVIEW:
return self.get_items(parent, None)
# if self._args[0] == GUI:
# gui = self.get_gui(parent, None)
# return sorted(gui, key=lambda f: f['gui']['order'])
if self._args[0] == consts.rest.TYPES:
types_ = self.get_types(parent, None)
logger.debug('Types: %s', types_)
return types_
if self._args[0] == consts.rest.GUI:
# Gui without type, valid
gui = self.get_gui(parent, '') # No type
return sorted(gui, key=lambda f: f['gui']['order'])
if self._args[0] == consts.rest.TABLEINFO:
return self.process_table_fields(
self.get_title(parent),
self.get_fields(parent),
self.get_row_style(parent),
)
# try to get id
return self.get_items(parent, process_uuid(self._args[0]))
match self._args[0]:
case consts.rest.OVERVIEW:
return self.get_items(parent, None)
case consts.rest.TYPES:
types_ = self.get_types(parent, None)
logger.debug('Types: %s', types_)
return types_
case consts.rest.TABLEINFO:
return self.process_table_fields(
self.get_title(parent),
self.get_fields(parent),
self.get_row_style(parent),
)
case consts.rest.GUI: # Used on some cases to get the gui for a detail with no subtypes
gui = self.get_processed_gui(parent, '')
return sorted(gui, key=lambda f: f['gui']['order'])
case _:
# try to get id
return self.get_items(parent, process_uuid(self._args[0]))
if num_args == 2:
if self._args[0] == consts.rest.GUI:
gui = self.get_gui(parent, self._args[1])
return sorted(gui, key=lambda f: f['gui']['order'])
return self.get_processed_gui(parent, self._args[1])
if self._args[0] == consts.rest.TYPES:
types_ = self.get_types(parent, self._args[1])
logger.debug('Types: %s', types_)
@ -180,10 +176,12 @@ class DetailHandler(BaseModelHandler):
if self._args[1] == consts.rest.LOG:
return self.get_logs(parent, self._args[0])
# Maybe a custom method?
r = self._check_is_custom_method(self._args[1], parent, self._args[0])
if r is not None:
return r
# Not understood, fallback, maybe the derived class can understand it
return self.fallback_get()
def put(self) -> typing.Any:
@ -319,28 +317,32 @@ class DetailHandler(BaseModelHandler):
"""
Gets the gui that is needed in order to "edit/add" new items on this detail
If not overriden, means that the detail has no edit/new Gui
Args:
parent (models.Model): Parent object
for_type (str): Type of object needing gui
Return:
collections.abc.Iterable[typing.Any]: A list of gui fields
"""
# raise RequestError('Gui not provided for this type of object')
return []
def get_processed_gui(self, parent: models.Model, for_type: str) -> collections.abc.Iterable[typing.Any]:
gui = self.get_gui(parent, for_type)
return sorted(gui, key=lambda f: f['gui']['order'])
def get_types(
self, parent: models.Model, for_type: typing.Optional[str]
) -> collections.abc.Iterable[types.rest.TypeInfoDict]:
"""
The default is that detail element will not have any types (they are "homogeneous")
but we provided this method, that can be overridden, in case one detail needs it
Args:
parent (models.Model): Parent object
for_type (typing.Optional[str]): Request argument in fact
Return:
collections.abc.Iterable[types.rest.TypeInfoDict]: A list of dictionaries describing type/types
"""

View File

@ -72,9 +72,6 @@ class UserServiceManager(metaclass=singleton.Singleton):
def manager() -> 'UserServiceManager':
return UserServiceManager() # Singleton pattern will return always the same instance
def get_cache_state_filter(self, service_pool: ServicePool, level: types.services.CacheLevel) -> Q:
return Q(cache_level=level) & self.get_state_filter(service_pool.service)
@staticmethod
def get_state_filter(service: 'models.Service') -> Q:
"""
@ -97,6 +94,9 @@ class UserServiceManager(metaclass=singleton.Singleton):
_('Maximum number of user services reached for this {}').format(service_pool)
)
def get_cache_state_filter(self, servicepool: ServicePool, level: types.services.CacheLevel) -> Q:
return Q(cache_level=level) & self.get_state_filter(servicepool.service)
def get_existing_user_services(self, service: 'models.Service') -> int:
"""
Returns the number of running user services for this service
@ -865,7 +865,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
) -> typing.Optional[UserService]:
"""
Locates a user service from a user and a service id
Args:
user: User owner of the service
id_service: Service id (A<uuid> for assigned, M<uuid> for meta, ?<uuid> for service pool)
@ -919,8 +919,8 @@ class UserServiceManager(metaclass=singleton.Singleton):
client_hostname: typing.Optional[str] = None,
) -> types.services.UserServiceInfo:
"""
Get service info from user service
Get service info from user service
Args:
user: User owner of the service
os: Detected OS (as provided by request)
@ -929,7 +929,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
transport_id: Transport id (optional). If not provided, will try to find a suitable one
validate_with_test: If True, will check if the service is ready
client_hostname: Client hostname (optional). If not provided, will use src_ip
Returns:
UserServiceInfo: User service info
"""

View File

@ -60,6 +60,10 @@ class FixedService(services.Service, abc.ABC): # pylint: disable=too-many-publi
needs_osmanager = False # If the service needs a s.o. manager (managers are related to agents provided by services, i.e. virtual machines with agent)
# can_reset = True
# Override the counting type to conservative on Fixed Services by default, that
# is the desired behaviour for fixed services
overrided_fields = {'max_services_count_type': types.services.ServicesCountingType.CONSERVATIVE}
# If machines has an alternate field with it, it will be used instead of "machines" field
alternate_machines_field: typing.Optional[str] = None

View File

@ -127,6 +127,17 @@ class Service(Module):
# : - If userservices_limit_field is None, userservices_limit will be set to consts.UNLIMITED (as default)
userservices_limit: int = consts.UNLIMITED
# : Overrided fields for service edition
# : This is a dictionary that will be used to override fields on service on its edition
# : This is useful for example to set the max_services_count_type for a service
# : or whatever field is needed to be overrided on service edition
# : Example:
# : overrided_fields = {
# : 'max_services_count_type': ServicesCountingType.STANDARD,
# : Note that overrided_fields will made the field not to be shown on service edition, so it will be "fixed" to the value provided
overrided_fields: typing.Optional[dict[str, typing.Any]] = None
# : If this item "has overrided fields", on deployed service edition, defined keys will overwrite defined ones
# : That is, this Dicionary will OVERWRITE fields ON ServicePool (normally cache related ones) dictionary from a REST api save invocation!!
# : Example:
@ -136,7 +147,8 @@ class Service(Module):
# : }
# : This means that service pool will have cache_l2_srvs = 10 and cache_l1_srvs = 20, no matter what the user has provided
# : on a save invocation to REST api for ServicePool
overrided_fields: typing.Optional[dict[str, typing.Any]] = None
# : Note that max_services_count_type is one of the variables of ServicesCountingType in this example
overrided_pools_fields: typing.Optional[dict[str, typing.Any]] = None
# : If this class uses cache or not. If uses cache is true, means that the
# : service can "prepare" some user deployments to allow quicker user access
@ -268,7 +280,7 @@ class Service(Module):
from the stuck cleaner job, for example. By default, this method returns True.
"""
return True
def allow_putting_back_to_cache(self) -> bool:
"""
Returns if this service can be put back to cache. This is used to check if a service can be put back to cache
@ -294,7 +306,9 @@ class Service(Module):
if self.userservices_limit == 0:
self.userservices_limit = consts.UNLIMITED
elif callable(userservices_limit_field):
self.userservices_limit = typing.cast(collections.abc.Callable[..., int], userservices_limit_field)()
self.userservices_limit = typing.cast(
collections.abc.Callable[..., int], userservices_limit_field
)()
else:
self.userservices_limit = consts.UNLIMITED
except Exception: