Added metapools capacity of show grouped pools transports

This commit is contained in:
Adolfo Gómez García 2021-04-22 14:44:48 +02:00
parent ce73d4e29f
commit e9a719a2eb
11 changed files with 154 additions and 42 deletions

View File

@ -63,7 +63,7 @@ class MetaPools(ModelHandler):
}
save_fields = ['name', 'short_name', 'comments', 'tags',
'image_id', 'servicesPoolGroup_id', 'visible', 'policy', 'calendar_message']
'image_id', 'servicesPoolGroup_id', 'visible', 'policy', 'calendar_message', 'transport_grouping']
table_title = _('Meta Pools')
table_fields = [
@ -74,6 +74,7 @@ class MetaPools(ModelHandler):
{'user_services_in_preparation': {'title': _('In Preparation')}},
{'visible': {'title': _('Visible'), 'type': 'callback'}},
{'pool_group_name': {'title': _('Pool Group')}},
{'label': {'title': _('Label')}},
{'tags': {'title': _('tags'), 'visible': False}},
]
@ -113,6 +114,7 @@ class MetaPools(ModelHandler):
'fallbackAccess': item.fallbackAccess,
'permission': permissions.getEffectivePermission(self._user, item),
'calendar_message': item.calendar_message,
'transport_grouping': item.transport_grouping
}
return val
@ -135,7 +137,7 @@ class MetaPools(ModelHandler):
'tooltip': ugettext('Image assocciated with this service'),
'type': gui.InputField.IMAGECHOICE_TYPE,
'order': 120,
'tab': ugettext('Display'),
'tab': gui.DISPLAY_TAB,
}, {
'name': 'servicesPoolGroup_id',
'values': [gui.choiceImage(-1, _('Default'), DEFAULT_THUMB_BASE64)] + gui.sortedChoices([gui.choiceImage(v.uuid, v.name, v.thumb64) for v in ServicePoolGroup.objects.all()]),
@ -143,7 +145,7 @@ class MetaPools(ModelHandler):
'tooltip': ugettext('Pool group for this pool (for pool classify on display)'),
'type': gui.InputField.IMAGECHOICE_TYPE,
'order': 121,
'tab': ugettext('Display'),
'tab': gui.DISPLAY_TAB,
}, {
'name': 'visible',
'value': True,
@ -151,7 +153,7 @@ class MetaPools(ModelHandler):
'tooltip': ugettext('If active, metapool will be visible for users'),
'type': gui.InputField.CHECKBOX_TYPE,
'order': 123,
'tab': ugettext('Display'),
'tab': gui.DISPLAY_TAB,
}, {
'name': 'calendar_message',
'value': '',
@ -159,7 +161,15 @@ class MetaPools(ModelHandler):
'tooltip': ugettext('Custom message to be shown to users if access is limited by calendar rules.'),
'type': gui.InputField.TEXT_TYPE,
'order': 124,
'tab': ugettext('Display'),
'tab': gui.DISPLAY_TAB,
}, {
'name': 'transport_grouping',
'values': [gui.choiceItem(k, str(v)) for k, v in MetaPool.TRANSPORT_SELECT.items()],
'label': ugettext('Transport Selection'),
'tooltip': ugettext('Transport selection policy'),
'type': gui.InputField.CHOICE_TYPE,
'order': 125,
'tab': gui.DISPLAY_TAB
}]:
self.addField(localGUI, field)

View File

@ -36,6 +36,7 @@ import typing
from django.utils.translation import ugettext_lazy as _, ugettext
from uds.models import Transport, Network, ServicePool
from uds.core import transports
from uds.core.ui import gui
from uds.core.util import permissions
from uds.core.util import os_detector as OsDetector
@ -49,7 +50,7 @@ logger = logging.getLogger(__name__)
class Transports(ModelHandler):
model = Transport
save_fields = ['name', 'comments', 'tags', 'priority', 'nets_positive', 'allowed_oss']
save_fields = ['name', 'comments', 'tags', 'priority', 'nets_positive', 'allowed_oss', 'label']
table_title = _('Transports')
table_fields = [
@ -107,6 +108,16 @@ class Transports(ModelHandler):
'type': 'multichoice',
'order': 103
})
field = self.addField(field, {
'name': 'label',
'length': 32,
'value': '',
'label': ugettext('Label'),
'tooltip': ugettext('Metapool transport label (only used on metapool transports grouping)'),
'type': 'text',
'order': 201,
'tab': gui.ADVANCED_TAB
})
return field
@ -120,6 +131,7 @@ class Transports(ModelHandler):
'comments': item.comments,
'priority': item.priority,
'nets_positive': item.nets_positive,
'label': item.label,
'networks': [{'id': n.uuid} for n in item.networks.all()],
'allowed_oss': [{'id': x} for x in item.allowed_oss.split(',')] if item.allowed_oss != '' else [],
'pools': pools,

View File

@ -749,7 +749,7 @@ class UserServiceManager:
Get service info from user service
"""
if idService[0] == 'M': # Meta pool
return self.getMeta(user, srcIp, os, idService[1:])
return self.getMeta(user, srcIp, os, idService[1:], idTransport)
userService = self.locateUserService(user, idService, create=True)
@ -908,6 +908,7 @@ class UserServiceManager:
srcIp: str,
os: typing.MutableMapping,
idMetaPool: str,
idTransport: str,
clientHostName: typing.Optional[str] = None,
) -> typing.Tuple[
typing.Optional[str],
@ -953,7 +954,13 @@ class UserServiceManager:
) -> typing.Optional[typing.Tuple[ServicePool, Transport]]:
found = None
t: Transport
for t in pool.transports.all().order_by('priority'):
if idTransport == 'meta': # Autoselected:
q = pool.transports.all().order_by('priority')
elif idTransport[:6] == 'LABEL:':
q = pool.transports.filter(label=idTransport[6:])
else:
q = pool.transports.filter(uuid=idTransport)
for t in q:
typeTrans = t.getType()
if (
t.getType()

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2 on 2021-04-22 13:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uds', '0039_auto_20201111_1329'),
]
operations = [
migrations.AddField(
model_name='metapool',
name='transport_grouping',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='transport',
name='label',
field=models.CharField(db_index=True, default='', max_length=32),
),
]

View File

@ -68,12 +68,22 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
PRIORITY_POOL = 1
MOST_AVAILABLE_BY_NUMBER = 2
TYPES = {
TYPES: typing.Mapping[int, str] = {
ROUND_ROBIN_POOL: _('Evenly distributed'),
PRIORITY_POOL: _('Priority'),
MOST_AVAILABLE_BY_NUMBER: _('Greater % available'),
}
# Type of transport grouping
AUTO_TRANSPORT_SELECT = 0
COMMON_TRANSPORT_SELECT = 1
LABEL_TRANSPORT_SELECT = 2
TRANSPORT_SELECT: typing.Mapping[int, str] = {
AUTO_TRANSPORT_SELECT: _('Automatic selection'),
COMMON_TRANSPORT_SELECT: _('Use only common transports'),
LABEL_TRANSPORT_SELECT: _('Group Transports by label')
}
name = models.CharField(max_length=128, default='')
short_name = models.CharField(max_length=32, default='')
comments = models.CharField(max_length=256, default='')
@ -103,6 +113,8 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
# Pool selection policy
policy = models.SmallIntegerField(default=0)
# If use common transports instead of auto select one
transport_grouping = models.IntegerField(default=0)
# "fake" declarations for type checking
objects: 'models.BaseManager[MetaPool]'

View File

@ -62,6 +62,8 @@ class Transport(ManagedObjectModel, TaggingMixin):
nets_positive = models.BooleanField(default=False)
# We store allowed oss as a comma-separated list
allowed_oss = models.CharField(max_length=255, default='')
# Label, to group transports on meta pools
label = models.CharField(max_length=32, default='', db_index=True)
# "fake" declarations for type checking
objects: 'models.BaseManager[Transport]'

File diff suppressed because one or more lines are too long

View File

@ -93,6 +93,6 @@
</svg>
</div>
</uds-root>
<script src="/uds/res/admin/runtime.js?stamp=1619085422" defer></script><script src="/uds/res/admin/polyfills-es5.js?stamp=1619085422" nomodule defer></script><script src="/uds/res/admin/polyfills.js?stamp=1619085422" defer></script><script src="/uds/res/admin/main.js?stamp=1619085422" defer></script></body>
<script src="/uds/res/admin/runtime.js?stamp=1619092209" defer></script><script src="/uds/res/admin/polyfills-es5.js?stamp=1619092209" nomodule defer></script><script src="/uds/res/admin/polyfills.js?stamp=1619092209" defer></script><script src="/uds/res/admin/main.js?stamp=1619092209" defer></script></body>
</html>

View File

@ -101,12 +101,12 @@ urlpatterns = [
re_path(r'^uds/utility/download/(?P<idDownload>[a-zA-Z0-9-]*)$', uds.web.views.download, name='utility.downloader'),
# WEB API path (not REST api, frontend)
re_path(r'^uds/webapi/img/transport/(?P<idTrans>[a-zA-Z0-9-]+)$', uds.web.views.transportIcon, name='webapi.transportIcon'),
re_path(r'^uds/webapi/img/transport/(?P<idTrans>[a-zA-Z0-9:-]+)$', uds.web.views.transportIcon, name='webapi.transportIcon'),
re_path(r'^uds/webapi/img/gallery/(?P<idImage>[a-zA-Z0-9-]+)$', uds.web.views.image, name='webapi.galleryImage'),
re_path(r'^uds/webapi/action/(?P<idService>.+)/enable/(?P<idTransport>[a-zA-Z0-9-]+)$', uds.web.views.userServiceEnabler, name='webapi.enabler'),
re_path(r'^uds/webapi/action/(?P<idService>.+)/enable/(?P<idTransport>[a-zA-Z0-9:-]+)$', uds.web.views.userServiceEnabler, name='webapi.enabler'),
re_path(r'^uds/webapi/action/(?P<idService>.+)/(?P<actionString>[a-zA-Z0-9-]+)$', uds.web.views.action, name='webapi.action'),
re_path(r'^uds/webapi/action/(?P<idService>.+)/(?P<actionString>[a-zA-Z0-9:-]+)$', uds.web.views.action, name='webapi.action'),
# Services list, ...
path(r'uds/webapi/services', uds.web.views.modern.servicesData, name='webapi.services'),

View File

@ -101,6 +101,34 @@ def getServicesData(
logger.debug('Checking meta pools: %s', availMetaPools)
services = []
# Metapool helpers
def transportIterator(member) -> typing.Iterable[Transport]:
for t in member.pool.transports.all():
typeTrans = t.getType()
if (
typeTrans
and t.validForIp(request.ip)
and typeTrans.supportsOs(osName)
and t.validForOs(osName)
):
yield t
def buildMetaTransports(
transports: typing.Iterable[Transport],
isLabel: bool,
) -> typing.List[typing.Mapping[str, typing.Any]]:
idd = lambda i: i.uuid if not isLabel else 'LABEL:' + i.label
return [
{
'id': idd(i),
'name': i.name,
'link': html.udsAccessLink(request, 'M' + meta.uuid, idd(i)),
'priority': 0,
}
for i in transports
]
# Preload all assigned user services for this user
# Add meta pools data first
for meta in availMetaPools:
@ -108,35 +136,48 @@ def getServicesData(
metaTransports: typing.List[typing.Mapping[str, typing.Any]] = []
in_use = meta.number_in_use > 0 # type: ignore # anotated value
if True: # If meta.use_common_transports
inAll: typing.Optional[typing.Set[str]] = None
tmpSet: typing.Set[str]
if (
meta.transport_grouping == MetaPool.COMMON_TRANSPORT_SELECT
): # If meta.use_common_transports
# only keep transports that are in ALL members
inAll: typing.Optional[typing.Set[str]] = None
for member in meta.members.all():
tmpSet: typing.Set[str] = set()
for t in member.pool.transports.all():
if inAll is None: # if first...
typeTrans = t.getType()
if (
typeTrans
and t.validForIp(request.ip)
and typeTrans.supportsOs(osName)
and t.validForOs(osName)
):
tmpSet.add(t.uuid)
elif t.uuid in inAll:
for member in meta.members.all().order_by('priority'):
tmpSet = set()
# if first pool, get all its transports and check that are valid
for t in transportIterator(member):
if inAll is None:
tmpSet.add(t.uuid)
elif t.uuid in inAll: # For subsequent, reduce...
tmpSet.add(t.uuid)
inAll = tmpSet
# tmpSet has ALL common transports
metaTransports = [
{
'id': i.uuid,
'name': i.name,
'link': html.udsAccessLink(request, 'M' + meta.uuid, i.uuid),
'priority': 0,
}
for i in Transport.objects.filter(uuid__in=inAll or [])
]
metaTransports = buildMetaTransports(
Transport.objects.filter(uuid__in=inAll or []),
isLabel=False
)
elif meta.transport_grouping == MetaPool.LABEL_TRANSPORT_SELECT:
ltrans: typing.MutableMapping[str, Transport] = {}
for member in meta.members.all():
tmpSet = set()
# if first pool, get all its transports and check that are valid
for t in transportIterator(member):
if not t.label:
continue
if t.label not in ltrans:
ltrans[t.label] = t
if inAll is None:
tmpSet.add(t.label)
elif t.label in inAll: # For subsequent, reduce...
tmpSet.add(t.label)
inAll = tmpSet
# tmpSet has ALL common transports
metaTransports = buildMetaTransports(
(v for k, v in ltrans.items() if k in (inAll or set())),
isLabel=True
)
else:
for member in meta.members.all():
# if pool.isInMaintenance():
@ -187,7 +228,7 @@ def getServicesData(
'group': group,
'transports': metaTransports,
'imageId': meta.image and meta.image.uuid or 'x',
'show_transports': False,
'show_transports': len(metaTransports) > 1,
'allow_users_remove': False,
'allow_users_reset': False,
'maintenance': meta.isInMaintenance(),
@ -209,7 +250,7 @@ def getServicesData(
use_count = str(sPool.usage_count) # type: ignore # anotated value
left_count = str(sPool.max_srvs - sPool.usage_count) # type: ignore # anotated value
trans = []
trans: typing.List[typing.MutableMapping[str, typing.Any]] = []
for t in sorted(
sPool.transports.all(), key=lambda x: x.priority
): # In memory sort, allows reuse prefetched and not too big array

View File

@ -100,7 +100,12 @@ def transportOwnLink(
@cache_page(3600, key_prefix='img', cache='memory')
def transportIcon(request: 'ExtendedHttpRequest', idTrans: str) -> HttpResponse:
try:
transport: Transport = Transport.objects.get(uuid=processUuid(idTrans))
transport: Transport
if idTrans[:6] == 'LABEL:':
# Get First label
transport = Transport.objects.filter(label=idTrans[6:]).order_by('priority')[0]
else:
transport = Transport.objects.get(uuid=processUuid(idTrans))
return HttpResponse(transport.getInstance().icon(), content_type='image/png')
except Exception:
return HttpResponse(DEFAULT_IMAGE, content_type='image/png')