Flexible way to create, update, delete actions in a job

Job workflow now has a sortable list to select actions in order

Change-Id: I67c624d3a45109e4328259fca7b42cb4fb2077f7
This commit is contained in:
Memo Garcia 2015-07-09 17:15:20 +01:00
parent 6ba4d85a3e
commit a9e52f2ffe
7 changed files with 249 additions and 10 deletions

View File

@ -164,11 +164,21 @@ def job_create(request, context):
job = create_dict_action(**context)
client_id = job.pop('client_id', None)
job['description'] = job.pop('description', None)
actions = job.pop('job_actions', None)
job.pop('clients', None)
schedule = {}
if context['schedule_end_date']:
schedule['schedule_end_date'] = context.pop('schedule_end_date')
if context['schedule_interval']:
schedule['schedule_interval'] = context.pop('schedule_interval')
if context['schedule_start_date']:
schedule['schedule_start_date'] = context.pop('schedule_start_date')
job.pop('clients', None)
job['job_schedule'] = schedule
job['job_actions'] = []
job['job_actions'] = actions
job['client_id'] = client_id
return _freezerclient(request).jobs.create(job)
@ -187,8 +197,11 @@ def job_edit(request, context):
job = create_dict_action(**context)
job['description'] = job.pop('description', None)
# job['client_id'] = job.pop('client_id', None)
actions = job.pop('job_actions', None)
job.pop('clients', None)
job['job_schedule'] = schedule
job['job_actions'] = actions
job_id = job.pop('original_name', None)
return _freezerclient(request).jobs.update(job_id, job)
@ -256,6 +269,22 @@ def action_list(request):
return actions
def action_list_json(request):
return _freezerclient(request).actions.list()
def actions_in_job_json(request, job_id):
job = _freezerclient(request).jobs.get(job_id)
action_list = []
for action in job['job_actions']:
a = {
"action_id": action['action_id'],
"freezer_action": action['freezer_action']
}
action_list.append(a)
return action_list
def actions_in_job(request, job_id):
job = _freezerclient(request).jobs.get(job_id)
actions = []

View File

@ -1,7 +1,8 @@
import functools
from django.http import HttpResponse
from django.views import generic
import json
from openstack_dashboard.api.rest import utils as rest_utils
from openstack_dashboard.api.rest.utils import JSONResponse
@ -31,7 +32,34 @@ class Clients(generic.View):
# we don't have a "get all clients" api (probably for good reason) so
# we need to resort to getting a very high number.
clients = freezer_api.client_list(request, limit=9999)
clients = freezer_api.client_list(request)
clients = [c.get_dict() for c in clients]
return clients
class Actions(generic.View):
"""API for clients"""
@prevent_json_hijacking
@rest_utils.ajax()
def get(self, request):
"""Get all registered freezer actions"""
actions = freezer_api.action_list_json(request)
actions = json.dumps(actions)
return HttpResponse(actions,
content_type="application/json")
class ActionsInJob(generic.View):
"""API for actions in a job"""
@prevent_json_hijacking
@rest_utils.ajax()
def get(self, request, job_id=None):
"""Get all registered freezer actions"""
actions = freezer_api.actions_in_job_json(request, job_id)
actions = json.dumps(actions)
return HttpResponse(actions,
content_type="application/json")

View File

@ -28,4 +28,7 @@ import rest_api
urlpatterns = patterns(
'',
url(r'^api/clients$', rest_api.Clients.as_view(), name="api_clients"),
url(r'^api/actions/$', rest_api.Actions.as_view(), name="api_actions"),
url(r'^api/actions/job/(?P<job_id>[^/]+)?$',
rest_api.ActionsInJob.as_view(), name="api_actions_in_job"),
)

View File

@ -3,5 +3,43 @@
{% block help_message %}
{% endblock %}
<!-- Jquery code to hide freezer inputs -->
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.jobs.actions.action.js'></script>
<style>
#sortable1, #sortable2 {
position: relative;
min-height: 100px;
}
</style>
<div class="sortable_lists row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">Available Actions</div>
<div class="panel-body">
<ul id="sortable1" class="connectedSortable list-group dark_stripe">
</ul>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">Selected Actions In Order</div>
<div class="panel-body">
<ul id="sortable2" class="connectedSortable list-group dark_stripe">
</ul>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
{% include "horizon/common/_form_fields.html" %}
{{ table.render }}
</div>
<div class="col-sm-12">
{{ step.get_help_text }}
</div>
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.jobs.sortable.js'></script>

View File

@ -10,13 +10,30 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import datetime
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import workflows
import horizon_web_ui.freezer_ui.api.api as freezer_api
from horizon_web_ui.freezer_ui.utils import actions_in_job
class ActionsConfigurationAction(workflows.Action):
pass
class Meta(object):
name = _("Actions")
slug = "actions"
help_text_template = "freezer_ui/jobs" \
"/_actions.html"
class ActionsConfiguration(workflows.Step):
action_class = ActionsConfigurationAction
contributes = ('actions',)
class ClientsConfigurationAction(workflows.MembershipAction):
@ -133,6 +150,10 @@ class SchedulingConfiguration(workflows.Step):
class InfoConfigurationAction(workflows.Action):
actions = forms.CharField(
widget=forms.HiddenInput(),
required=False)
description = forms.CharField(
label=_("Job Name"),
help_text=_("Set a short description for this job"),
@ -150,7 +171,8 @@ class InfoConfigurationAction(workflows.Action):
class InfoConfiguration(workflows.Step):
action_class = InfoConfigurationAction
contributes = ('description',
'original_name',)
'original_name',
'actions')
class ConfigureJob(workflows.Workflow):
@ -162,15 +184,49 @@ class ConfigureJob(workflows.Workflow):
success_url = "horizon:freezer_ui:jobs:index"
default_steps = (InfoConfiguration,
ClientsConfiguration,
SchedulingConfiguration)
SchedulingConfiguration,
ActionsConfiguration)
def handle(self, request, context):
try:
if context['original_name'] == '':
# for each action_id get the action object and append it
# to context['job_actions']
actions = actions_in_job(context.pop('actions', []))
actions_for_job = []
for action in actions:
a = freezer_api.action_get(request, action)
a = {
'action_id': a['action_id'],
'freezer_action': a['freezer_action']
}
actions_for_job.append(a)
context['job_actions'] = actions_for_job
for client in context['clients']:
context['client_id'] = client
freezer_api.job_create(request, context)
else:
actions = actions_in_job(context.pop('actions', []))
actions_for_job = []
job_id = context['original_name']
job = freezer_api.job_get(request, job_id)
del job[0].data_dict['job_actions']
for action in actions:
a = freezer_api.action_get(request, action)
a = {
'action_id': a['action_id'],
'freezer_action': a['freezer_action']
}
actions_for_job.append(a)
context['job_actions'] = actions_for_job
return freezer_api.job_edit(request, context)
return True
except Exception:

View File

@ -0,0 +1,79 @@
$(function() {
$( "#sortable1, #sortable2" ).sortable({
connectWith: ".connectedSortable"
}).disableSelection();
});
var parent = $(".sortable_lists").parent();
parent.removeClass("col-sm-6");
parent.addClass("col-sm-12");
var siblings = parent.siblings();
siblings.remove();
$("form").submit(function(event){
var ids = "";
$("#sortable2 li").each(function(index) {
ids += ($(this).attr('id'));
ids += "==="
});
console.log(ids);
$('#id_actions').val(ids);
});
function get_actions_url() {
var url = $(location).attr("origin");
url += '/freezer_ui/api/actions';
return url
}
var job_id = $('#id_original_name').val();
if (job_id != "") {
var url_available = get_actions_url();
$.ajax({
url: url_available,
type: "GET",
cache: false,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function (data) {
$.each(data, function (index, item) {
$("#sortable1").append(
"<li class='list-group-item' id=" + item['action_id'] + ">" +
item['freezer_action']['backup_name'] +
"</li>"
);
});
},
error: function (request, error) {
console.error(error);
}
});
}
else {
var url = get_actions_url();
$.ajax({
url: url,
type: "GET",
cache: false,
dataType: 'json',
contentType: 'application/json; charset=utf-8' ,
success: function(data) {
$.each(data, function(index, item) {
$("#sortable1").append(
"<li class='list-group-item' id=" + item['action_id'] + ">" +
item['freezer_action']['backup_name'] +
"</li>"
);
});
},
error: function(request, error) {
console.error(error);
}
});
}

View File

@ -35,5 +35,11 @@ def create_dummy_id():
This is needed when the scheduler creates jobs with actions attached
directly, those actions are not registered in the db.
"""
return uuid.uuid4().hex
def actions_in_job(ids):
"""Return an ordered list of actions for a new job
"""
ids = ids.split('===')
return [i for i in ids if i]