From 92de41d41098b9e73bd2b979f12aca17e2a2bb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Wed, 14 Mar 2018 08:03:23 +0100 Subject: [PATCH] * Added support for "reset" services that support it. Right now, we will test on enterprise this feature. --- server/src/uds/REST/methods/services.py | 2 ++ server/src/uds/REST/methods/services_pools.py | 14 ++++++++-- .../uds/core/managers/UserServiceManager.py | 16 ++++++++++- server/src/uds/core/services/BaseDeployed.py | 9 ++++++- server/src/uds/core/services/BaseService.py | 10 +++---- .../uds/core/services/BaseServiceProvider.py | 3 +-- .../0027_deployedservice_allow_users_reset.py | 20 ++++++++++++++ server/src/uds/models/ServicesPool.py | 6 ++++- .../static/adm/js/gui-d-servicespools.coffee | 8 ++++++ server/src/uds/templates/uds/html5/index.html | 27 +++++++++++++++++++ server/src/uds/urls.py | 3 ++- server/src/uds/web/views/__init__.py | 4 +-- server/src/uds/web/views/index.py | 4 ++- server/src/uds/web/views/service.py | 24 +++++++++++++++-- 14 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 server/src/uds/migrations/0027_deployedservice_allow_users_reset.py diff --git a/server/src/uds/REST/methods/services.py b/server/src/uds/REST/methods/services.py index 6f3d0ff0c..d6cc03cad 100644 --- a/server/src/uds/REST/methods/services.py +++ b/server/src/uds/REST/methods/services.py @@ -62,6 +62,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods @staticmethod def serviceInfo(item): info = item.getType() + return { 'icon': info.icon().replace('\n', ''), 'needs_publication': info.publicationType is not None, @@ -74,6 +75,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods 'allowedProtocols': info.allowedProtocols, 'servicesTypeProvided': info.servicesTypeProvided, 'must_assign_manually': info.mustAssignManually, + 'can_reset': info.canReset, } @staticmethod diff --git a/server/src/uds/REST/methods/services_pools.py b/server/src/uds/REST/methods/services_pools.py index 3502712a5..7b2a65904 100644 --- a/server/src/uds/REST/methods/services_pools.py +++ b/server/src/uds/REST/methods/services_pools.py @@ -72,7 +72,8 @@ class ServicesPools(ModelHandler): save_fields = ['name', 'short_name', 'comments', 'tags', 'service_id', 'osmanager_id', 'image_id', 'servicesPoolGroup_id', 'initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs', 'show_transports', - 'allow_users_remove', 'ignores_unused'] + 'allow_users_remove', 'allow_users_reset', 'ignores_unused'] + remove_fields = ['osmanager_id', 'service_id'] table_title = _('Service Pools') @@ -134,6 +135,7 @@ class ServicesPools(ModelHandler): 'restrained': item.isRestrained(), 'show_transports': item.show_transports, 'allow_users_remove': item.allow_users_remove, + 'allow_users_reset': item.allow_users_reset, 'ignores_unused': item.ignores_unused, 'fallbackAccess': item.fallbackAccess, 'permission': permissions.getEffectivePermission(self._user, item), @@ -186,13 +188,21 @@ class ServicesPools(ModelHandler): 'type': gui.InputField.CHECKBOX_TYPE, 'order': 111, 'tab': ugettext('Advanced'), + }, { + 'name': 'allow_users_reset', + 'value': False, + 'label': ugettext('Allow reset by users'), + 'tooltip': ugettext('If active, the user will be allowed to reset the service'), + 'type': gui.InputField.CHECKBOX_TYPE, + 'order': 112, + 'tab': ugettext('Advanced'), }, { 'name': 'ignores_unused', 'value': False, 'label': ugettext('Ignores unused'), 'tooltip': ugettext('If the option is enabled, UDS will not attempt to detect and remove the user services assigned but not in use.'), 'type': gui.InputField.CHECKBOX_TYPE, - 'order': 112, + 'order': 113, 'tab': ugettext('Advanced'), }, { 'name': 'image_id', diff --git a/server/src/uds/core/managers/UserServiceManager.py b/server/src/uds/core/managers/UserServiceManager.py index 654f6fdb5..b52280011 100644 --- a/server/src/uds/core/managers/UserServiceManager.py +++ b/server/src/uds/core/managers/UserServiceManager.py @@ -51,7 +51,7 @@ import requests import json import logging -__updated__ = '2018-03-02' +__updated__ = '2018-03-14' logger = logging.getLogger(__name__) traceLogger = logging.getLogger('traceLog') @@ -360,6 +360,20 @@ class UserServiceManager(object): UserServiceOpChecker.makeUnique(uService, ui, state) return False + def reset(self, uService): + UserService.objects.update() + uService = UserService.objects.get(id=uService.id) + if uService.deployed_service.service.getType().canReset is False: + return + + logger.debug('Reseting'.format(uService)) + + ui = uService.getInstance() + try: + ui.reset() + except Exception: + logger.exception('Reseting service') + def notifyPreconnect(self, uService, userName, protocol): url = uService.getCommsUrl() if url is None: diff --git a/server/src/uds/core/services/BaseDeployed.py b/server/src/uds/core/services/BaseDeployed.py index 8ebcea29a..18ab33d65 100644 --- a/server/src/uds/core/services/BaseDeployed.py +++ b/server/src/uds/core/services/BaseDeployed.py @@ -36,7 +36,7 @@ from uds.core import Environmentable from uds.core import Serializable from uds.core.util.State import State -__updated__ = '2017-09-29' +__updated__ = '2018-03-14' class UserDeployment(Environmentable, Serializable): @@ -574,6 +574,13 @@ class UserDeployment(Environmentable, Serializable): ''' raise Exception('cancel method for class {0} not provided!'.format(self.__class__.__name__)) + def reset(self): + ''' + This method is invoked for "reset" an user service + This method is not intended to be a task right now, it's more like the + ''' + raise Exception('reset method for class {0} not provided!'.format(self.__class__.__name__)) + def __str__(self): ''' Mainly used for debugging purposses diff --git a/server/src/uds/core/services/BaseService.py b/server/src/uds/core/services/BaseService.py index d7820f930..fe4debea6 100644 --- a/server/src/uds/core/services/BaseService.py +++ b/server/src/uds/core/services/BaseService.py @@ -37,7 +37,7 @@ from uds.core import Module from uds.core.transports import protocols from . import types -__updated__ = '2016-03-09' +__updated__ = '2018-03-14' class Service(Module): @@ -167,14 +167,14 @@ class Service(Module): # : Default behavior is False (and most common), but some services may need to respawn a new "copy" on every launch spawnsNew = False + # : If the service allows "reset", here we will announce it + # : Defaults to False + canReset = False + # : 'kind' of services that this service provides: # : For example, VDI, VAPP, ... servicesTypeProvided = types.ALL - # : If the service can provide any other option on release appart of "delete" & "keep assigned" - # : Defaults to None (no any other options are provided) - actionsOnRelease = None - def __init__(self, environment, parent, values=None): ''' Do not forget to invoke this in your derived class using "super(self.__class__, self).__init__(environment, parent, values)". diff --git a/server/src/uds/core/services/BaseServiceProvider.py b/server/src/uds/core/services/BaseServiceProvider.py index f1cc2459c..fc37b55d6 100644 --- a/server/src/uds/core/services/BaseServiceProvider.py +++ b/server/src/uds/core/services/BaseServiceProvider.py @@ -39,7 +39,7 @@ import logging logger = logging.getLogger(__name__) -__updated__ = '2016-04-25' +__updated__ = '2018-03-14' class ServiceProvider(Module): @@ -120,7 +120,6 @@ class ServiceProvider(Module): # : Note: this variable can be either a fixed value (integer, string) or a Gui text field (with a .value) ignoreLimits = None - @classmethod def getServicesTypes(cls): ''' diff --git a/server/src/uds/migrations/0027_deployedservice_allow_users_reset.py b/server/src/uds/migrations/0027_deployedservice_allow_users_reset.py new file mode 100644 index 000000000..4802412b5 --- /dev/null +++ b/server/src/uds/migrations/0027_deployedservice_allow_users_reset.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.9 on 2018-03-14 06:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('uds', '0026_auto_20180302_0525'), + ] + + operations = [ + migrations.AddField( + model_name='deployedservice', + name='allow_users_reset', + field=models.BooleanField(default=False), + ), + ] diff --git a/server/src/uds/models/ServicesPool.py b/server/src/uds/models/ServicesPool.py index 8e93a7aa4..8d2f7dc54 100644 --- a/server/src/uds/models/ServicesPool.py +++ b/server/src/uds/models/ServicesPool.py @@ -62,7 +62,7 @@ import logging import pickle import six -__updated__ = '2017-11-29' +__updated__ = '2018-03-14' logger = logging.getLogger(__name__) @@ -83,8 +83,12 @@ class DeployedService(UUIDModel, TaggingMixin): state = models.CharField(max_length=1, default=states.servicePool.ACTIVE, db_index=True) state_date = models.DateTimeField(default=NEVER) show_transports = models.BooleanField(default=True) + allow_users_remove = models.BooleanField(default=False) + allow_users_reset = models.BooleanField(default=False) + ignores_unused = models.BooleanField(default=False) + image = models.ForeignKey(Image, null=True, blank=True, related_name='deployedServices', on_delete=models.SET_NULL) servicesPoolGroup = models.ForeignKey(ServicesPoolGroup, null=True, blank=True, related_name='servicesPools', on_delete=models.SET_NULL) diff --git a/server/src/uds/static/adm/js/gui-d-servicespools.coffee b/server/src/uds/static/adm/js/gui-d-servicespools.coffee index c9888b569..588ee76eb 100644 --- a/server/src/uds/static/adm/js/gui-d-servicespools.coffee +++ b/server/src/uds/static/adm/js/gui-d-servicespools.coffee @@ -72,6 +72,7 @@ gui.servicesPools.link = (event) -> serviceChangedFnc = (formId) -> $fld = $(formId + " [name=\"service_id\"]") $osmFld = $(formId + " [name=\"osmanager_id\"]") + $canResetFld = $(formId + " [name=\"allow_users_reset\"]") selectors = [] $.each [ "initial_srvs" @@ -89,6 +90,13 @@ gui.servicesPools.link = (event) -> unless $fld.val() is -1 api.providers.service $fld.val(), (data) -> gui.doLog "Onchange", data + if $canResetFld.bootstrapSwitch("readonly") == data.info.can_reset + gui.doLog('reset doent not match field') + $canResetFld.bootstrapSwitch "toggleReadonly", true + if data.info.can_reset is false + gui.doLog($canResetFld.bootstrapSwitch("readonly"), data.info.can_reset) + + if data.info.needs_manager is false $osmFld.prop "disabled", "disabled" else diff --git a/server/src/uds/templates/uds/html5/index.html b/server/src/uds/templates/uds/html5/index.html index d64cb7e41..f634188e5 100644 --- a/server/src/uds/templates/uds/html5/index.html +++ b/server/src/uds/templates/uds/html5/index.html @@ -50,6 +50,9 @@ {% if ser.allow_users_remove %} {% endif %} + {% if ser.allow_users_reset %} + + {% endif %} {% endif %} @@ -284,6 +287,10 @@ span.gear > span.release { cursor: cell; } + + span.gear > span.reseter { + cursor: cell; + } {% endblock %} @@ -377,6 +384,26 @@ return false; }); + $('div.service:not(.maintenance, .notaccesible) > span.gear > span.release').on("click", function (event) { + event.stopPropagation(); + event.preventDefault(); + if ( confirm("{%trans 'Are you sure that you want to release this service. Its current content will be lost!' %}") ) { + window.location.href = $(this).attr('data-href'); + } + + return false; + }); + + $('div.service:not(.maintenance, .notaccesible) > span.gear > span.reseter').on("click", function (event) { + event.stopPropagation(); + event.preventDefault(); + if ( confirm("{%trans 'Are you sure that you want to reset this service. USE WITH CAUTION!' %}") ) { + window.location.href = $(this).attr('data-href'); + } + + return false; + }); + $(".maintenance").click( function(event) { $('#maintenance-dialog').modal({ keyboard: false diff --git a/server/src/uds/urls.py b/server/src/uds/urls.py index 149549729..9b3dcfda9 100644 --- a/server/src/uds/urls.py +++ b/server/src/uds/urls.py @@ -69,8 +69,9 @@ urlpatterns = patterns( # Client access enabler url(r'^enable/(?P.+)/(?P.+)$', 'web.views.clientEnabler', name='ClientAccessEnabler'), - # Releaser + # Releaser & reset url(r'^release/(?P.+)$', 'web.views.release', name='Releaser'), + url(r'^reset/(?P.+)$', 'web.views.reset', name='Reseter'), # Custom authentication callback (r'^auth/(?P.+)', 'web.views.authCallback'), diff --git a/server/src/uds/web/views/__init__.py b/server/src/uds/web/views/__init__.py index 60d35958c..90de93dbf 100644 --- a/server/src/uds/web/views/__init__.py +++ b/server/src/uds/web/views/__init__.py @@ -35,7 +35,7 @@ import logging from .login import login, logout, customAuth from .index import index, about from .prefs import prefs -from .service import transportOwnLink, transportIcon, clientEnabler, serviceImage, release +from .service import transportOwnLink, transportIcon, clientEnabler, serviceImage, release, reset from .auth import authCallback, authInfo, ticketAuth from .download import download from .client_download import client_downloads, plugin_detection @@ -44,6 +44,6 @@ from ..errors import error from .images import image from .file_storage import file_storage -__updated__ = '2017-10-26' +__updated__ = '2018-03-14' logger = logging.getLogger(__name__) diff --git a/server/src/uds/web/views/index.py b/server/src/uds/web/views/index.py index cf1eab7f8..bdba2f2da 100644 --- a/server/src/uds/web/views/index.py +++ b/server/src/uds/web/views/index.py @@ -52,7 +52,7 @@ import logging logger = logging.getLogger(__name__) -__updated__ = '2017-11-10' +__updated__ = '2018-03-14' def about(request): @@ -141,6 +141,7 @@ def index(request): 'imageId': imageId, 'show_transports': servicePool.show_transports, 'allow_users_remove': servicePool.allow_users_remove, + 'allow_users_reset': servicePool.allow_users_reset, 'maintenance': servicePool.isInMaintenance(), 'not_accesible': not servicePool.isAccessAllowed(), 'in_use': svr.in_use, @@ -200,6 +201,7 @@ def index(request): 'imageId': imageId, 'show_transports': svr.show_transports, 'allow_users_remove': svr.allow_users_remove, + 'allow_users_reset': svr.allow_users_reset, 'maintenance': svr.isInMaintenance(), 'not_accesible': not svr.isAccessAllowed(), 'in_use': in_use, diff --git a/server/src/uds/web/views/service.py b/server/src/uds/web/views/service.py index 9fb9a7884..fe032599b 100644 --- a/server/src/uds/web/views/service.py +++ b/server/src/uds/web/views/service.py @@ -56,7 +56,7 @@ import logging logger = logging.getLogger(__name__) -__updated__ = '2017-11-16' +__updated__ = '2018-03-14' @webLoginRequired(admin=False) @@ -159,7 +159,7 @@ def clientEnabler(request, idService, idTransport): def release(request, idService): logger.debug('ID Service: {}'.format(idService)) userService = userServiceManager().locateUserService(request.user, idService, create=False) - logger.debug('UserSrvice: >{}<'.format(userService)) + logger.debug('UserService: >{}<'.format(userService)) if userService is not None and userService.deployed_service.allow_users_remove: log.doLog( userService.deployed_service, @@ -172,3 +172,23 @@ def release(request, idService): return HttpResponseRedirect(reverse('Index')) + +@webLoginRequired(admin=False) +@never_cache +def reset(request, idService): + logger.debug('ID Service: {}'.format(idService)) + userService = userServiceManager().locateUserService(request.user, idService, create=False) + logger.debug('UserService: >{}<'.format(userService)) + if (userService is not None and userService.deployed_service.allow_users_reset + and userService.deployed_service.service.getType().canReset): + log.doLog( + userService.deployed_service, + log.INFO, + "Reseting User Service {} as requested by {} from {}".format(userService.friendly_name, request.user.pretty_name, request.ip), + log.WEB + ) + # userServiceManager().requestLogoff(userService) + userServiceManager().reset(userService) + + return HttpResponseRedirect(reverse('Index')) +