Merge remote-tracking branch 'origin/v2.2'

This commit is contained in:
Adolfo Gómez García 2018-03-14 08:18:06 +01:00
commit b9cb82c054
14 changed files with 152 additions and 34 deletions

View File

@ -64,6 +64,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,
@ -76,6 +77,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

View File

@ -73,7 +73,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')
@ -139,6 +140,7 @@ class ServicesPools(ModelHandler):
'show_transports': item.show_transports,
'visible': item.visible,
'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),
@ -191,13 +193,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',

View File

@ -51,6 +51,8 @@ import requests
import json
import logging
__updated__ = '2018-03-14'
logger = logging.getLogger(__name__)
traceLogger = logging.getLogger('traceLog')
@ -363,10 +365,23 @@ 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):
proxy = uService.deployed_service.proxy
url = uService.getCommsUrl()
if url is None:
logger.debug('No notification is made because agent does not supports notifications')

View File

@ -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

View File

@ -27,9 +27,9 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
'''
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
'''
from __future__ import unicode_literals
from django.utils.translation import ugettext_noop as _
@ -37,11 +37,11 @@ from uds.core import Module
from uds.core.transports import protocols
from . import types
__updated__ = '2017-01-12'
__updated__ = '2018-03-14'
class Service(Module):
"""
'''
This class is in fact an interface, and represents a service, that is the
definition of an offering for consumers (users).
@ -76,7 +76,7 @@ class Service(Module):
only need data that is keeped at form fields, marshal and unmarshal and in fact
not needed.
"""
'''
# : Constant for indicating that max elements this service can deploy is unlimited.
UNLIMITED = -1
@ -170,79 +170,79 @@ 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)".
We want to use the env, parent methods outside class. If not called, you must implement your own methods
cache and storage are "convenient" methods to access _env.cache and _env.storage
"""
'''
super(Service, self).__init__(environment, values)
self._provider = parent
self.initialize(values)
def initialize(self, values):
"""
'''
This method will be invoked from __init__ constructor.
This is provided so you don't have to provide your own __init__ method,
and invoke base methods.
This will get invoked when all initialization stuff is done
Args:
values: If values is not none, this object is being initialized
Values: If values is not none, this object is being initialized
from administration interface, and not unmarshal will be done.
If it's None, this is initialized internally, and unmarshal will
be called after this.
Default implementation does nothing
"""
'''
pass
def parent(self):
"""
'''
Utility method to access parent provider for this service
Returns
Parent provider instance object (not database object)
"""
'''
return self._provider
def requestServicesForAssignation(self, **kwargs):
"""
'''
override this if mustAssignManualy is True
@params kwargs: Named arguments
@return an array with the services that we can assign (they must be of type deployedType)
We will access the returned array in "name" basis. This means that the service will be assigned by "name", so be care that every single service
returned are not repeated... :-)
"""
'''
raise Exception('The class {0} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'.format(self.__class__.__name__))
def macGenerator(self):
"""
'''
Utility method to access provided macs generator (inside environment)
Returns the environment unique mac addresses generator
"""
'''
return self.idGenerators('mac')
def nameGenerator(self):
"""
'''
Utility method to access provided names generator (inside environment)
Returns the environment unique name generator
"""
'''
return self.idGenerators('name')
def __str__(self):
"""
'''
String method, mainly used for debugging purposes
"""
'''
return "Base Service Provider"

View File

@ -39,7 +39,7 @@ import logging
logger = logging.getLogger(__name__)
__updated__ = '2016-04-25'
__updated__ = '2018-03-14'
class ServiceProvider(Module):

View File

@ -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),
),
]

View File

@ -63,7 +63,7 @@ import logging
import pickle
import six
__updated__ = '2018-02-14'
__updated__ = '2018-03-14'
logger = logging.getLogger(__name__)
@ -86,7 +86,10 @@ class DeployedService(UUIDModel, TaggingMixin):
show_transports = models.BooleanField(default=True)
visible = 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)

View File

@ -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

View File

@ -50,6 +50,9 @@
{% if ser.allow_users_remove %}
<span data-href="{% url 'Releaser' idService=ser.id %}" class="release fa fa-trash"> </span>
{% endif %}
{% if ser.allow_users_reset %}
<span data-href="{% url 'Reseter' idService=ser.id %}" class="reseter fa fa-refresh"> </span>
{% endif %}
</span>
{% endif %}
</div>
@ -284,6 +287,10 @@
span.gear > span.release {
cursor: cell;
}
span.gear > span.reseter {
cursor: cell;
}
</style>
{% 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

View File

@ -73,7 +73,7 @@ urlpatterns = [
# Releaser
re_path(r'^release/(?P<idService>.+)$', uds.web.views.release, name='Releaser'),
re_path(r'^reset/(?P<idService>.+)$', 'web.views.reset', name='Reseter'),
# Custom authentication callback
re_path(r'^auth/(?P<authName>.+)', uds.web.views.authCallback, name='uds.web.views.authCallback'),
re_path(r'^authinfo/(?P<authName>.+)', uds.web.views.authInfo, name='uds.web.views.authInfo'),

View File

@ -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
@ -43,4 +43,6 @@ from ..errors import error
from .images import image
from .file_storage import file_storage
__updated__ = '2018-03-14'
logger = logging.getLogger(__name__)

View File

@ -49,7 +49,7 @@ import logging
logger = logging.getLogger(__name__)
__updated__ = '2018-02-14'
__updated__ = '2018-03-14'
def about(request):
@ -137,6 +137,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,
@ -196,6 +197,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,

View File

@ -54,6 +54,8 @@ import logging
logger = logging.getLogger(__name__)
__updated__ = '2018-03-14'
@webLoginRequired(admin=False)
def transportOwnLink(request, idService, idTransport):
@ -154,7 +156,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,
@ -167,3 +169,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'))