* Fixed Actions Calendars migrations

* Started support for actions calendars edition on dashboard
This commit is contained in:
Adolfo Gómez García 2016-03-16 11:30:56 +01:00
parent b0a6807ea4
commit b5387d4922
15 changed files with 245 additions and 31 deletions

View File

@ -34,14 +34,14 @@ from __future__ import unicode_literals
# On centos, old six release does not includes byte2int, nor six.PY2
import six
VERSION = '1.9.0'
VERSION = '2.0.0'
__title__ = 'udsactor'
__version__ = VERSION
__build__ = 0x010750
__author__ = 'Adolfo Gómez'
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>'
__license__ = "BSD 3-clause"
__copyright__ = "Copyright 2014-2015 VirtualCable S.L.U."
__copyright__ = "Copyright 2014-2016 VirtualCable S.L.U."
if not hasattr(six, 'byte2int'):

View File

@ -42,7 +42,7 @@ from uds.models import User
from uds.web import errors
from uds.core.managers import cryptoManager, userServiceManager
from uds.core.util.Config import GlobalConfig
from uds.core.services.Exceptions import ServiceNotReadyError
from uds.core.services.Exceptions import ServiceNotReadyError, ServiceAccessDeniedByCalendar, ServiceInMaintenanceMode
from uds.core import VERSION as UDS_VERSION
import six
@ -147,6 +147,10 @@ class Client(Handler):
# Refresh ticket and make this retrayable
TicketStore.revalidate(ticket, 20) # Retry will be in at most 5 seconds
return Client.result(error=errors.SERVICE_IN_PREPARATION, errorCode=e.code, retryable=True)
except ServiceAccessDeniedByCalendar:
return Client.result(error=errors.SERVICE_CALENDAR_DENIED)
except ServiceInMaintenanceMode:
return Client.result(error=errors.SERVICE_IN_MAINTENANCE)
except Exception as e:
logger.exception("Exception")
return Client.result(error=six.text_type(e))

View File

@ -38,7 +38,7 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext as _
from uds.models import CalendarAccess, Calendar
from uds.models import CalendarAccess, CalendarAction, Calendar
from uds.core.util.State import State
from uds.core.util.model import processUuid
from uds.core.util import log
@ -113,3 +113,68 @@ class AccessCalendars(DetailHandler):
def deleteItem(self, parent, item):
CalendarAccess.objects.get(uuid=processUuid(self._args[0])).delete()
class ActionsCalendars(DetailHandler):
'''
Processes the transports detail requests of a Service Pool
'''
@staticmethod
def as_dict(item):
return {
'id': item.uuid,
'calendarId': item.calendar.uuid,
'name': item.calendar.name,
'action': item.action,
'atStart': item.atStart,
'offset': item.eventOffset,
'params': item.params
}
def getItems(self, parent, item):
try:
if item is None:
return [ActionsCalendars.as_dict(i) for i in parent.calendaraction_set.all()]
else:
i = CalendarAction.objects.get(uuid=processUuid(item))
return ActionsCalendars.as_dict(i)
except Exception:
self.invalidItemException()
def getTitle(self, parent):
return _('Scheduled actions')
def getFields(self, parent):
return [
{'name': {'title': _('Name')}},
{'action': {'title': _('Action')}},
{'atStart': {'title': _('Referer')}},
{'offset': {'title': _('Time offset')}},
]
def saveItem(self, parent, item):
# If already exists
uuid = processUuid(self._params['id']) if 'id' in self._params else None
calendar = Calendar.objects.get(uuid=processUuid(self._params['calendarId']))
action = self._params['action'].upper()
eventOffset = int(self._params['eventOffset'])
atStart = (self._params['atStart'] == 'true')
if uuid is not None:
calAction = CalendarAction.objects.get(uuid=uuid)
calAction.calendar = calendar
calAction.servicePool = parent
calAction.action = action
calAction.atStart = atStart
calAction.eventOffset = eventOffset
calAction.save()
else:
CalendarAction.objects.create(calendar=calendar, servicePool=parent, action=action, atStart=atStart, eventOffset=eventOffset)
return self.success()
def deleteItem(self, parent, item):
CalendarAction.objects.get(uuid=processUuid(self._args[0])).delete()

View File

@ -43,7 +43,7 @@ from uds.REST.model import ModelHandler
from uds.REST import RequestError, ResponseError
from uds.core.ui.UserInterface import gui
from .user_services import AssignedService, CachedService, Groups, Transports, Publications, Changelog
from .services_pool_calendars import AccessCalendars
from .services_pool_calendars import AccessCalendars, ActionsCalendars
from .services import Services
import logging
@ -64,6 +64,7 @@ class ServicesPools(ModelHandler):
'publications': Publications,
'changelog': Changelog,
'access': AccessCalendars,
'actions': ActionsCalendars
}
save_fields = ['name', 'comments', 'tags', 'service_id', 'osmanager_id', 'image_id', 'servicesPoolGroup_id', 'initial_srvs', 'cache_l1_srvs', 'cache_l2_srvs', 'max_srvs', 'show_transports']

View File

@ -39,7 +39,7 @@ from uds.core.services.Exceptions import OperationException
from uds.core.util.State import State
from uds.core.util import log
from uds.core.util.Config import GlobalConfig
from uds.core.services.Exceptions import MaxServicesReachedError, ServiceInMaintenanceMode, InvalidServiceException, ServiceNotReadyError
from uds.core.services.Exceptions import MaxServicesReachedError, ServiceInMaintenanceMode, InvalidServiceException, ServiceNotReadyError, ServiceAccessDeniedByCalendar
from uds.models import ServicePool, UserService, getSqlDatetime, Transport
from uds.core import services
from uds.core.services import Service
@ -51,7 +51,7 @@ import requests
import json
import logging
__updated__ = '2016-03-14'
__updated__ = '2016-03-16'
logger = logging.getLogger(__name__)
@ -470,10 +470,13 @@ class UserServiceManager(object):
# Now we have to locate an instance of the service, so we can assign it to user.
userService = self.getAssignationForUser(ds, user)
logger.debug('Found service: {0}'.format(userService))
if userService.isInMaintenance() is True:
raise ServiceInMaintenanceMode()
logger.debug('Found service: {0}'.format(userService))
if userService.deployed_service.isAccessAllowed() is False:
raise ServiceAccessDeniedByCalendar()
if idTransport is None or idTransport == '': # Find a suitable transport
for v in userService.deployed_service.transports.order_by('priority'):

View File

@ -32,7 +32,7 @@
'''
from __future__ import unicode_literals
__updated__ = '2015-11-06'
__updated__ = '2016-03-16'
class ServiceException(Exception):
@ -95,6 +95,12 @@ class ServiceInMaintenanceMode(ServiceException):
'''
pass
class ServiceAccessDeniedByCalendar(ServiceException):
'''
This service can't be accessed right now, probably due to date-time restrictions
'''
pass
class ServiceNotReadyError(ServiceException):
'''

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-02-19 07:26
# Generated by Django 1.9.2 on 2016-03-16 08:06
from __future__ import unicode_literals
from django.db import migrations, models
@ -32,8 +32,10 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.CharField(default=None, max_length=50, null=True, unique=True)),
('action', models.CharField(max_length=64)),
('params', models.CharField(max_length=1024)),
('action', models.CharField(default='', max_length=64)),
('atStart', models.BooleanField(default=False)),
('eventsOffset', models.IntegerField(default=0)),
('params', models.CharField(default='', max_length=1024)),
('calendar', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uds.Calendar')),
],
options={

View File

@ -34,7 +34,7 @@
from __future__ import unicode_literals
__updated__ = '2016-02-18'
__updated__ = '2016-03-16'
from django.db import models
from uds.models.Calendar import Calendar
@ -47,12 +47,20 @@ import logging
logger = logging.getLogger(__name__)
# Current posible actions
ACTION_PUBLISH = 'PUBLISH'
ACTION_CACHE_L1 = 'CACHEL1'
ACTION_CACHE_L2 = 'CACHEL2'
ACTION_INITIAL = 'INITIAL'
ACTION_MAX = 'MAX'
class CalendarAction(UUIDModel):
calendar = models.ForeignKey(Calendar, on_delete=models.CASCADE)
servicePool = models.ForeignKey(ServicePool, on_delete=models.CASCADE)
action = models.CharField(max_length=64)
params = models.CharField(max_length=1024)
action = models.CharField(max_length=64, default='')
atStart = models.BooleanField(default=False) # If false, action is done at end of event
eventsOffset = models.IntegerField(default=0) # In minutes
params = models.CharField(max_length=1024, default='')
class Meta:
'''

View File

@ -60,7 +60,7 @@ from uds.core.util.calendar import CalendarChecker
from datetime import timedelta
import logging
__updated__ = '2016-02-26'
__updated__ = '2016-03-16'
logger = logging.getLogger(__name__)
@ -203,6 +203,7 @@ class DeployedService(UUIDModel, TaggingMixin):
for ac in self.calendaraccess_set.all():
if CalendarChecker(ac.calendar).check(chkDateTime) is True:
access = ac.access
break # Stops on first rule match found
return access == states.action.ALLOW

View File

@ -0,0 +1,109 @@
gui.servicesPools.actionsCalendars = (servPool, info) ->
accessCalendars = new GuiElement(api.servicesPools.detail(servPool.id, "actions", { permission: servPool.permission }), "actions")
accessCalendarsTable = accessCalendars.table(
doNotLoadData: true
icon: 'assigned'
container: "actions-placeholder"
rowSelect: "multi"
buttons: [
"new"
"edit"
"delete"
"xls"
]
onCheck: (action, selected) ->
if action == 'edit'
return true
for v in selected
if v.id == -1
return false # No action allowed on DEFAULT
return true
onNew: (value, table, refreshFnc) ->
api.templates.get "pool_add_access", (tmpl) ->
api.calendars.overview (data) ->
modalId = gui.launchModal(gettext("Add access calendar"), api.templates.evaluate(tmpl,
calendars: data
priority: 1
calendarId: ''
accessList: accessList
access: 'ALLOW'
))
$(modalId + " .button-accept").on "click", (event) ->
priority = $(modalId + " #id_priority").val()
calendar = $(modalId + " #id_calendar_select").val()
access = $(modalId + " #id_access_select").val()
accessCalendars.rest.create
calendarId: calendar
access: access
priority: priority
, (data) ->
$(modalId).modal "hide"
refreshFnc()
return
return
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
return
return
onEdit: (value, event, table, refreshFnc) ->
if value.id == -1
api.templates.get "pool_access_default", (tmpl) ->
modalId = gui.launchModal(gettext("Default fallback access"), api.templates.evaluate(tmpl,
accessList: accessList
access: servPool.fallbackAccess
))
$(modalId + " .button-accept").on "click", (event) ->
access = $(modalId + " #id_access_select").val()
servPool.fallbackAccess = access
gui.servicesPools.rest.setFallbackAccess servPool.id, access, (data) ->
$(modalId).modal "hide"
refreshFnc()
return
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
api.templates.get "pool_add_access", (tmpl) ->
accessCalendars.rest.item value.id, (item) ->
api.calendars.overview (data) ->
gui.doLog "Item: ", item
modalId = gui.launchModal(gettext("Edit access calendar"), api.templates.evaluate(tmpl,
calendars: data
priority: item.priority
calendarId: item.calendarId
accessList: accessList
access: item.access
))
$(modalId + " .button-accept").on "click", (event) ->
priority = $(modalId + " #id_priority").val()
calendar = $(modalId + " #id_calendar_select").val()
access = $(modalId + " #id_access_select").val()
accessCalendars.rest.save
id: item.id
calendarId: calendar
access: access
priority: priority
, (data) ->
$(modalId).modal "hide"
refreshFnc()
return
return
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
return
return
onDelete: gui.methods.del(accessCalendars, gettext("Remove access calendar"), gettext("Access calendar removal error"))
)
return [accessCalendarsTable]

View File

@ -126,7 +126,7 @@ gui.servicesPools.link = (event) ->
transports: "transports-placeholder"
publications: "publications-placeholder"
changelog: "changelog-placeholder"
scheduling: "scheduling-placeholder"
actions: "actions-placeholder"
access: "access-placeholder"
logs: "logs-placeholder"
)
@ -381,6 +381,10 @@ gui.servicesPools.link = (event) ->
else
$("#publications-placeholder_tab").addClass "hidden"
# Actions calendars
for v in gui.servicesPools.actionsCalendars(servPool, info)
prevTables.push v
#
# Access calendars
#

View File

@ -121,6 +121,7 @@
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-connectivity.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools-calendars.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools-actions.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools-publications.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-servicespools-transports.coffee"></script>
<script type="text/coffeescript" charset="utf-8" src="{% get_static_prefix %}adm/js/gui-d-config.coffee"></script>

View File

@ -14,9 +14,9 @@
{% for message in messages %}
<div class="alert alert-danger alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{{ message }}
{{ message }}
</div>
{% endfor %}
{% endfor %}
</div>
</div>
{% endif %}
@ -33,10 +33,10 @@
<li><a href="#{{ groups }}" data-toggle="tab">{% endverbatim %}{% trans 'Groups' %}{% verbatim %}</a></li>
<li><a href="#{{ transports }}" data-toggle="tab">{% endverbatim %}{% trans 'Transports' %}{% verbatim %}</a></li>
<li><a id="{{ publications }}_tab" href="#{{ publications }}" data-toggle="tab">{% endverbatim %}{% trans 'Publications' %}{% verbatim %}</a></li>
<li><a id="{{ scheduling }}_tab" href="#{{ scheduling }}" data-toggle="tab">{% endverbatim %}{% trans 'Scheduled actions' %}{% verbatim %}</a></li>
<li><a id="{{ actions }}_tab" href="#{{ actions }}" data-toggle="tab">{% endverbatim %}{% trans 'Scheduled actions' %}{% verbatim %}</a></li>
<li><a id="{{ access }}_tab" href="#{{ access }}" data-toggle="tab">{% endverbatim %}{% trans 'Access Calendars' %}{% verbatim %}</a></li>
<li><a href="#{{ logs }}" data-toggle="tab">{% endverbatim %}{% trans 'Logs' %}{% verbatim %}</a></li>
</ul>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="{{ pool_info }}">
<div class="row">
@ -44,7 +44,7 @@
<div class="well">
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="{{ assigned_services }}">
<div class="row">
@ -72,12 +72,12 @@
<div id="{{ publications }}">
</div>
<div id="{{ changelog }}">
</div>
</div>
</div>
<div class="tab-pane fade" id="{{ scheduling }}">...</div>
<div class="tab-pane fade" id="{{ actions }}">...</div>
<div class="tab-pane fade" id="{{ access }}">...</div>
<div class="tab-pane fade" id="{{ logs }}">...</div>
</div>
</div>
</div>
</div>
{% endverbatim %}
{% endverbatim %}

View File

@ -65,6 +65,7 @@ BROWSER_NOT_SUPPORTED = 11,
SERVICE_IN_MAINTENANCE = 12
SERVICE_NOT_READY = 13
SERVICE_IN_PREPARATION = 14
SERVICE_CALENDAR_DENIED = 15
strings = [
@ -82,7 +83,8 @@ strings = [
_('Your browser is not supported. Please, upgrade it to a modern HTML5 browser like Firefox or Chrome'),
_('The requested service is in maintenance mode'),
_('The service is not ready.\nPlease, try again in a few moments.'),
_('Preparing service')
_('Preparing service'),
_('Service access denied by calendars')
]

View File

@ -44,16 +44,17 @@ from uds.core.ui import theme
from uds.core.util.model import processUuid
from uds.models import Transport, Image
from uds.core.util import html
from uds.core.services.Exceptions import ServiceNotReadyError, MaxServicesReachedError
from uds.core.services.Exceptions import ServiceNotReadyError, MaxServicesReachedError, ServiceAccessDeniedByCalendar
import uds.web.errors as errors
import six
import json
import logging
logger = logging.getLogger(__name__)
__updated__ = '2015-10-09'
__updated__ = '2016-03-16'
@webLoginRequired(admin=False)
@ -136,11 +137,18 @@ def clientEnabler(request, idService, idTransport):
except MaxServicesReachedError:
logger.info('Number of service reached MAX for service pool "{}"'.format(idService))
error = errors.errorString(errors.MAX_SERVICES_REACHED)
except ServiceAccessDeniedByCalendar:
logger.info('Access tried to a calendar limited access pool "{}"'.format(idService))
error = errors.errorString(errors.SERVICE_CALENDAR_DENIED)
except Exception as e:
logger.exception('Error')
error = six.text_type(e)
return HttpResponse(
'{{ "url": "{}", "error": "{}" }}'.format(url, error),
json.dumps({
'url': six.text_type(url),
'error': six.text_type(error)
}),
content_type='application/json'
)