1
0
mirror of https://github.com/dkmstr/openuds.git synced 2024-12-22 13:34:04 +03:00

basic scheduled actions working (needs backedn worker to be done)

This commit is contained in:
Adolfo Gómez García 2016-03-30 11:23:23 +02:00
parent e979c6e1e2
commit 6116db5147
7 changed files with 198 additions and 92 deletions

View File

@ -65,7 +65,7 @@ class AccessCalendars(DetailHandler):
return {
'id': item.uuid,
'calendarId': item.calendar.uuid,
'name': item.calendar.name,
'calendar': item.calendar.name,
'access': item.access,
'priority': item.priority,
}
@ -87,7 +87,7 @@ class AccessCalendars(DetailHandler):
def getFields(self, parent):
return [
{'priority': {'title': _('Priority'), 'type': 'numeric', 'width': '6em'}},
{'name': {'title': _('Name')}},
{'calendar': {'title': _('Calendar')}},
{'access': {'title': _('Access')}},
]
@ -120,16 +120,21 @@ class ActionsCalendars(DetailHandler):
'''
Processes the transports detail requests of a Service Pool
'''
custom_methods = ('execute')
@staticmethod
def as_dict(item):
return {
'id': item.uuid,
'calendarId': item.calendar.uuid,
'name': item.calendar.name,
'action': CALENDAR_ACTION_DICT[item.action]['description'],
'calendar': item.calendar.name,
'action': item.action,
'actionDescription': CALENDAR_ACTION_DICT[item.action]['description'],
'atStart': item.atStart,
'offset': item.eventsOffset,
'params': json.loads(item.params)
'eventsOffset': item.eventsOffset,
'params': json.loads(item.params),
'nextExecution': item.nextExecution,
'lastExecution': item.lastExecution
}
def getItems(self, parent, item):
@ -148,11 +153,13 @@ class ActionsCalendars(DetailHandler):
def getFields(self, parent):
return [
{'name': {'title': _('Name')}},
{'action': {'title': _('Action')}},
{'calendar': {'title': _('Calendar')}},
{'actionDescription': {'title': _('Action')}},
{'params': {'title': _('Parameters')}},
{'atStart': {'title': _('Relative to')}},
{'offset': {'title': _('Time offset')}},
{'eventsOffset': {'title': _('Time offset')}},
{'nextExecution': {'title': _('Next execution'), 'type': 'datetime'}},
{'lastExecution': {'title': _('Last execution'), 'type': 'datetime'}},
]
def saveItem(self, parent, item):
@ -183,3 +190,11 @@ class ActionsCalendars(DetailHandler):
def deleteItem(self, parent, item):
CalendarAction.objects.get(uuid=processUuid(self._args[0])).delete()
def execute(self, parent, item):
logger.debug('Launching action')
uuid = processUuid(item)
calAction = CalendarAction.objects.get(uuid=uuid)
calAction.execute()
return self.success()

View File

@ -41,11 +41,12 @@ from uds.models.Calendar import Calendar
from uds.core.util.Cache import Cache
import datetime
import time
import six
import bitarray
import logging
__updated__ = '2016-03-14'
__updated__ = '2016-03-30'
logger = logging.getLogger(__name__)
@ -113,6 +114,9 @@ class CalendarChecker(object):
next_event = None
for rule in self.calendar.rules.all():
if rule.start > checkFrom or (rule.end is not None and rule.end < checkFrom.date()):
continue
if startEvent:
event = rule.as_rrule().after(checkFrom) # At start
else:
@ -149,22 +153,28 @@ class CalendarChecker(object):
return data[dtime.hour * 60 + dtime.minute]
def nextEvent(self, checkFrom=None, startEvent=True):
def nextEvent(self, checkFrom=None, startEvent=True, offset=None):
'''
Returns next event for this interval
Returns a list of two elements. First is datetime of event begining, second is timedelta of duration
'''
logger.debug('Obtainint nextEvent')
if checkFrom is None:
checkFrom = getSqlDatetime()
cacheKey = six.text_type(self.calendar.modified.toordinal()) + self.calendar.uuid + six.text_type(checkFrom.toordinal()) + 'event' + ('x' if startEvent is True else '_')
print cacheKey
if offset is None:
offset = datetime.timedelta(minutes=0)
cacheKey = six.text_type(self.calendar.modified.toordinal()) + self.calendar.uuid + six.text_type(offset.seconds) + six.text_type(int(time.mktime(checkFrom.timetuple()))) + 'event' + ('x' if startEvent is True else '_')
next_event = CalendarChecker.cache.get(cacheKey, None)
print next_event
if next_event is None:
next_event = self._updateEvents(checkFrom, startEvent)
logger.debug('Regenerating cached nextEvent')
next_event = self._updateEvents(checkFrom - offset, startEvent) # We substract on checkin, so we can take into account for next execution the "offset" on start & end (just the inverse of current, so we substract it)
if next_event is not None:
next_event += offset
CalendarChecker.cache.put(cacheKey, next_event, 3600)
else:
logger.debug('nextEvent cache hit')
CalendarChecker.hits += 1
return next_event

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-03-16 08:06
# Generated by Django 1.9.2 on 2016-03-30 09:38
from __future__ import unicode_literals
from django.db import migrations, models
@ -36,6 +36,8 @@ class Migration(migrations.Migration):
('atStart', models.BooleanField(default=False)),
('eventsOffset', models.IntegerField(default=0)),
('params', models.CharField(default='', max_length=1024)),
('lastExecution', models.DateTimeField(blank=True, db_index=True, default=None, null=True)),
('nextExecution', models.DateTimeField(blank=True, db_index=True, default=None, null=True)),
('calendar', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uds.Calendar')),
],
options={

View File

@ -34,16 +34,20 @@
from __future__ import unicode_literals
__updated__ = '2016-03-29'
__updated__ = '2016-03-30'
from django.utils.translation import ugettext_lazy as _
from django.db import models
from uds.models.Calendar import Calendar
from uds.models.UUIDModel import UUIDModel
from uds.models.Util import NEVER, getSqlDatetime
from uds.core.util import calendar
from uds.models.ServicesPool import ServicePool
from django.utils.encoding import python_2_unicode_compatible
# from django.utils.translation import ugettext_lazy as _, ugettext
import datetime
import json
import logging
logger = logging.getLogger(__name__)
@ -52,10 +56,10 @@ logger = logging.getLogger(__name__)
# Each line describes:
#
CALENDAR_ACTION_PUBLISH = { 'id' : 'PUBLISH', 'description': _('Publish'), 'params': () }
CALENDAR_ACTION_CACHE_L1 = { 'id': 'CACHEL1', 'description': _('Sets cache size'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Cache size') },) }
CALENDAR_ACTION_CACHE_L2 = { 'id': 'CACHEL2', 'description': _('Sets L2 cache size'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Cache L2 size') },) }
CALENDAR_ACTION_INITIAL = { 'id': 'INITIAL', 'description': _('Set initial services'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Initial services') },) }
CALENDAR_ACTION_MAX = { 'id': 'MAX', 'description': _('Change maximum number of services'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Maximum services') },) }
CALENDAR_ACTION_CACHE_L1 = { 'id': 'CACHEL1', 'description': _('Sets cache size'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Cache size'), 'default': '1' },) }
CALENDAR_ACTION_CACHE_L2 = { 'id': 'CACHEL2', 'description': _('Sets L2 cache size'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Cache L2 size'), 'default': '1' },) }
CALENDAR_ACTION_INITIAL = { 'id': 'INITIAL', 'description': _('Set initial services'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Initial services'), 'default': '1' },) }
CALENDAR_ACTION_MAX = { 'id': 'MAX', 'description': _('Change maximum number of services'), 'params': ({'type': 'numeric', 'name': 'size', 'description': _('Maximum services'), 'default': '10' },) }
CALENDAR_ACTION_DICT = dict(list((c['id'], c) for c in (CALENDAR_ACTION_PUBLISH, CALENDAR_ACTION_CACHE_L1,
CALENDAR_ACTION_CACHE_L2, CALENDAR_ACTION_INITIAL, CALENDAR_ACTION_MAX)))
@ -66,6 +70,9 @@ class CalendarAction(UUIDModel):
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='')
# Not to be edited, just to be used as indicators for executions
lastExecution = models.DateTimeField(default=None, db_index=True, null=True, blank=True)
nextExecution = models.DateTimeField(default=None, db_index=True, null=True, blank=True)
class Meta:
'''
@ -74,3 +81,38 @@ class CalendarAction(UUIDModel):
db_table = 'uds_cal_action'
app_label = 'uds'
@property
def offset(self):
return datetime.timedelta(minutes=self.eventsOffset)
def execute(self, save=True):
logger.debug('Executing action')
self.lastExecution = getSqlDatetime()
params = json.loads(self.params)
saveServicePool = save
if CALENDAR_ACTION_CACHE_L1['id'] == self.action:
self.servicePool.cache_l1_srvs = int(params['size'])
elif CALENDAR_ACTION_CACHE_L2['id'] == self.action:
self.servicePool.cache_l1_srvs = int(params['size'])
elif CALENDAR_ACTION_INITIAL['id'] == self.action:
self.servicePool.initial_srvs = int(params['size'])
elif CALENDAR_ACTION_MAX['id'] == self.action:
self.servicePool.max_srvs = int(params['size'])
elif CALENDAR_ACTION_PUBLISH['id'] == self.action:
self.servicePool.publish(changeLog='Scheduled publication action')
saveServicePool = False
# On save, will regenerate nextExecution
if save:
self.save()
if saveServicePool:
self.servicePool.save()
def save(self, *args, **kwargs):
self.nextExecution = calendar.CalendarChecker(self.calendar).nextEvent(checkFrom=self.lastExecution, startEvent=self.atStart, offset=self.offset)
return UUIDModel.save(self, *args, **kwargs)

View File

@ -1,5 +1,32 @@
readParamsFromInputs = (modalId) ->
a = {}
a[$(v).attr('name')] = $(v).val() for v in $(modalId + ' .action_parameters')
return a
actionSelectChangeFnc = (modalId, actionsList) ->
gui.doLog "onChange"
action = $(modalId + " #id_action_select").val()
if action == '-1'
return
$(modalId + " #parameters").empty()
for i in actionsList
if i['id'] == action
if i['params'].length > 0
html = ''
for j in i['params']
html += '<div class="form-group"><label for="fld_' + j['name'] +
'" class="col-sm-3 control-label">' + j['description'] +
'</label><div class="col-sm-9"><input type="' + j['type'] +
'" class="action_parameters" name="' + j['name'] +
'" value="' + j['default'] + '"></div></div>'
$(modalId + " #parameters").html(html)
gui.tools.applyCustoms modalId
return
gui.servicesPools.actionsCalendars = (servPool, info) ->
actionsCalendars = new GuiElement(api.servicesPools.detail(servPool.id, "actions", { permission: servPool.permission }), "actions")
actionsApi = api.servicesPools.detail(servPool.id, "actions", { permission: servPool.permission })
actionsCalendars = new GuiElement(actionsApi, "actions")
actionsCalendarsTable = actionsCalendars.table(
doNotLoadData: true
icon: 'assigned'
@ -8,6 +35,34 @@ gui.servicesPools.actionsCalendars = (servPool, info) ->
buttons: [
"new"
"edit"
{
text: gettext("Launch Now")
css: "disabled"
disabled: true
click: (val, value, btn, tbl, refreshFnc) ->
if val.length != 1
return
gui.doLog val, val[0]
gui.forms.confirmModal gettext("Execute action"), gettext("Launch action execution right now?"),
onYes: ->
actionsApi.invoke val[0].id + "/execute", ->
refreshFnc()
return
return
select: (vals, self, btn, tbl, refreshFnc) ->
unless vals.length == 1
$(btn).addClass "disabled"
$(btn).prop('disabled', true)
return
val = vals[0]
$(btn).removeClass("disabled").prop('disabled', false)
# $(btn).addClass("disabled").prop('disabled', true)
return
}
"delete"
"xls"
]
@ -27,17 +82,12 @@ gui.servicesPools.actionsCalendars = (servPool, info) ->
value.atStart = if value.atStart then gettext('Beginning') else gettext('Ending')
onNew: (value, table, refreshFnc) ->
readParamsFromInputs = (modalId) ->
a = {}
a[$(v).attr('name')] = $(v).val() for v in $(modalId + ' .action_parameters')
return a
api.templates.get "pool_add_action", (tmpl) ->
api.calendars.overview (data) ->
api.servicesPools.actionsList servPool.id, (actionsList) ->
modalId = gui.launchModal(gettext("Add scheduled action"), api.templates.evaluate(tmpl,
calendars: data
priority: 1
calendarId: ''
actionsList: actionsList
action: ''
@ -64,27 +114,7 @@ gui.servicesPools.actionsCalendars = (servPool, info) ->
return
$(modalId + ' #id_action_select').on "change", (event) ->
action = $(modalId + " #id_action_select").val()
if action == '-1'
return
$(modalId + " #parameters").empty()
for i in actionsList
if i['id'] == action
if i['params'].length > 0
html = ''
for j in i['params']
if j['type'] == 'numeric'
defval = '1'
else
defval = ''
html += '<div class="form-group"><label for="fld_' + j['name'] +
'" class="col-sm-3 control-label">' + j['description'] +
'</label><div class="col-sm-9"><input type="' + j['type'] +
'" class="action_parameters" name="' + j['name'] +
'" value="' + defval + '"></div></div>'
$(modalId + " #parameters").html(html)
gui.tools.applyCustoms modalId
return
actionSelectChangeFnc(modalId, actionsList)
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
@ -93,49 +123,56 @@ gui.servicesPools.actionsCalendars = (servPool, info) ->
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) ->
actionsCalendars.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()
actionsCalendars.rest.save
id: item.id
calendarId: calendar
access: access
priority: priority
, (data) ->
$(modalId).modal "hide"
refreshFnc()
api.templates.get "pool_add_action", (tmpl) ->
api.servicesPools.actionsList servPool.id, (actionsList) ->
actionsCalendars.rest.item value.id, (item) ->
for i in actionsList
if i['id'] == item.action
gui.doLog "Found ", i
for j in Object.keys(item.params)
gui.doLog "Testing ", j
for k in i['params']
gui.doLog 'Checking ', k
if k['name'] == j
gui.doLog 'Setting value'
k['default'] = item.params[j]
api.calendars.overview (data) ->
gui.doLog "Item: ", item
modalId = gui.launchModal(gettext("Edit access calendar"), api.templates.evaluate(tmpl,
calendars: data
calendarId: item.calendarId
actionsList: actionsList
action: item.action
eventsOffset: item.eventsOffset
atStart: item.atStart
))
$(modalId + " .button-accept").on "click", (event) ->
offset = $(modalId + " #id_offset").val()
calendar = $(modalId + " #id_calendar_select").val()
action = $(modalId + " #id_action_select").val()
atStart = $(modalId + " #atStart_field").is(":checked")
actionsCalendars.rest.save
id: item.id
calendarId: calendar
action: action
eventsOffset: offset
atStart: atStart
action: action
params: readParamsFromInputs(modalId)
, (data) ->
$(modalId).modal "hide"
refreshFnc()
return
return
$(modalId + ' #id_action_select').on "change", (event) ->
actionSelectChangeFnc(modalId, actionsList)
# Triggers the event manually
actionSelectChangeFnc(modalId, actionsList)
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
# Makes form "beautyfull" :-)
gui.tools.applyCustoms modalId
return
return
return

View File

@ -25,7 +25,7 @@ gui.servicesPools.accessCalendars = (servPool, info) ->
onData: (data) ->
data.push
id: -1,
name: 'DEFAULT',
calendar: '-',
priority: '<span style="visibility: hidden;font-size: 0px;">10000000</span>FallBack',
access: servPool.fallbackAccess
gui.doLog data

View File

@ -1,6 +1,6 @@
gui.servicesPools.publications = (servPool, info) ->
pubApi = api.servicesPools.detail(servPool.id, "publications")
publications = new GuiElement(pubApi, "publications", { permission: servPool.permission })
pubApi = api.servicesPools.detail(servPool.id, "publications", { permission: servPool.permission })
publications = new GuiElement(pubApi, "publications")
# Publications table
publicationsTable = publications.table(