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:
parent
418e1f7c0b
commit
a318e8ca2b
@ -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]:
|
||||
|
@ -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:
|
||||
|
@ -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}')
|
||||
|
@ -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
|
||||
"""
|
||||
|
@ -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
|
||||
"""
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user