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:
parent
e979c6e1e2
commit
6116db5147
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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={
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user