Adding ability to relaunch jobs

Adding the ability to relaunch a job from a given job-execution (job configuration is preserved and editable).
There are options to either launch on an existing or a new cluster.

Implements: blueprint job-relaunch

Change-Id: Ibc93beb0ebd0cbb6d6afe0e30ee82c0a43b4f93f
This commit is contained in:
Chad Roberts 2013-12-16 10:08:02 -05:00
parent bbba154c49
commit 4f0a2df306
5 changed files with 163 additions and 7 deletions

View File

@ -15,11 +15,14 @@
import logging
from django.core import urlresolvers
from django.utils import http
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from savannadashboard.api.client import client as savannaclient
from savannadashboard.jobs import tables as j_t
LOG = logging.getLogger(__name__)
@ -37,6 +40,35 @@ class DeleteJobExecution(tables.BatchAction):
savanna.job_executions.delete(obj_id)
class ReLaunchJobExistingCluster(j_t.ChoosePlugin):
name = "relaunch-job-existing"
verbose_name = _("Relaunch On Existing Cluster")
action_present = _("Launch")
action_past = _("Launched")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
url = "horizon:savanna:jobs:launch-job"
classes = ('ajax-modal', 'btn-launch')
def get_link_url(self, datum):
base_url = urlresolvers.reverse(self.url)
params = http.urlencode({'job_id': datum.job_id,
'job_execution_id': datum.id})
return "?".join([base_url, params])
class ReLaunchJobNewCluster(ReLaunchJobExistingCluster):
name = "relaunch-job-new"
verbose_name = _("Relaunch On New Cluster")
action_present = _("Launch")
action_past = _("Launched")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
url = "horizon:savanna:jobs:choose-plugin"
classes = ('ajax-modal', 'btn-launch')
class UpdateRow(tables.Row):
ajax = True
@ -77,4 +109,6 @@ class JobExecutionsTable(tables.DataTable):
status_columns = ["status"]
verbose_name = _("Job Executions")
table_actions = [DeleteJobExecution]
row_actions = [DeleteJobExecution]
row_actions = [DeleteJobExecution,
ReLaunchJobExistingCluster,
ReLaunchJobNewCluster]

View File

@ -18,12 +18,18 @@ from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import url
import savannadashboard.job_executions.views as views
from savannadashboard.jobs import views as job_views
urlpatterns = patterns('',
url(r'^$', views.JobExecutionsView.as_view(),
name='index'),
url(r'^$', views.JobExecutionsView.as_view(),
name='job-executions'),
url(r'^launch-job$',
job_views.LaunchJobView.as_view()),
url(r'^launch-job-new-cluster$',
job_views.LaunchJobNewClusterView.as_view()),
url(r'^(?P<job_execution_id>[^/]+)$',
views.JobExecutionDetailsView.as_view(),
name='details'))

View File

@ -138,6 +138,17 @@ class JobConfigAction(workflows.Action):
def __init__(self, request, *args, **kwargs):
super(JobConfigAction, self).__init__(request, *args, **kwargs)
job_ex_id = request.REQUEST.get("job_execution_id")
if job_ex_id is not None:
client = savannaclient(request)
job_ex_id = request.REQUEST.get("job_execution_id")
job_configs = client.job_executions.get(job_ex_id).job_configs
self.fields['job_configs'].initial =\
json.dumps(job_configs['configs'])
self.fields['job_params'].initial =\
json.dumps(job_configs['params'])
self.fields['job_args'].initial =\
json.dumps(job_configs['args'])
def populate_property_name_choices(self, request, context):
client = savannaclient(request)
@ -238,9 +249,44 @@ class SelectHadoopPluginAction(t_flows.SelectPluginAction):
self.fields["job_id"] = forms.ChoiceField(
label=_("Plugin name"),
required=True,
initial=request.GET["job_id"],
initial=request.GET.get("job_id") or request.POST.get("job_id"),
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
self.fields["job_configs"] = forms.ChoiceField(
label=_("Job configs"),
required=True,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
self.fields["job_args"] = forms.ChoiceField(
label=_("Job args"),
required=True,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
self.fields["job_params"] = forms.ChoiceField(
label=_("Job params"),
required=True,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
job_ex_id = request.REQUEST.get("job_execution_id")
if job_ex_id is not None:
self.fields["job_execution_id"] = forms.ChoiceField(
label=_("Job Execution Id"),
required=True,
initial=request.REQUEST.get("job_execution_id"),
widget=forms.HiddenInput(
attrs={"class": "hidden_create_field"}))
client = savannaclient(request)
job_ex_id = request.REQUEST.get("job_execution_id")
job_configs = client.job_executions.get(job_ex_id).job_configs
self.fields["job_configs"].initial =\
json.dumps(job_configs["configs"])
self.fields["job_params"].initial =\
json.dumps(job_configs["params"])
self.fields["job_args"].initial =\
json.dumps(job_configs["args"])
class Meta:
name = _("Select plugin and hadoop version for cluster")
help_text_template = ("cluster_templates/_create_general_help.html")

View File

@ -12,4 +12,55 @@
{{ job_executions_table.render }}
</div>
<script type="text/javascript">
addHorizonLoadEvent(function () {
horizon.modals.addModalInitFunction(function (modal) {
if ($(modal).find(".nav-tabs").find("li").size() == 1) {
// hide tab bar for plugin/version modal wizard
$('div#modal_wrapper ul.nav-tabs').hide();
}
$(".hidden_nodegroups_field").val("");
$(".hidden_configure_field").val("");
lower_limit = 0;
$(".count-field").change();
if ($(modal).find(".hidden_create_field").length > 0) {
var form = $(".hidden_create_field").closest("form");
var successful = false;
form.submit(function (e) {
var newElement = $("<a id='hiddenbutton' class='btn btn-small ajax-modal' style='display:none'/>");
var oldHref = $("#job_executions__action_delete")[0].href;
var plugin = $("#id_plugin_name option:selected").val();
var version = $("#id_" + plugin + "_version option:selected").val();
var job_id = $("#id_job_id").val();
var job_execution_id = $("#id_job_execution_id").val();
form.find(".close").click();
newElement.attr("href", "launch-job-new-cluster?" +
"plugin_name=" + encodeURIComponent(plugin) +
"&hadoop_version=" + encodeURIComponent(version) +
"&job_id=" + encodeURIComponent(job_id) +
"&job_execution_id=" + encodeURIComponent(job_execution_id));
newElement.appendTo("#main_content");
$("#hiddenbutton").click();
return false;
});
$(".plugin_version_choice").closest(".control-group").hide();
}
//display version for selected plugin
$(document).on('change', '.plugin_name_choice', switch_versions);
function switch_versions() {
$(".plugin_version_choice").closest(".control-group").hide();
var plugin = $(this);
$("." + plugin.val() + "_version_choice").closest(".control-group").show();
}
$(".plugin_name_choice").change();
});
});
</script>
{% endblock %}

View File

@ -2,9 +2,11 @@
<tr id_attr="$id">
<div class="input control-group">
<td><input style="display: inline" field="key" list="properties" placeholder="Select property name"
onkeyup="trySetValue(this)" onchange="trySetValue(this)" onclick="trySetValue(this)"/></td>
onkeyup="trySetValue(this)" onchange="trySetValue(this)" onclick="trySetValue(this)"
value="$name"/></td>
<td><input style="display: inline; margin-left:10px; margin-right:10px" field="value" class="input-medium"
onkeyup="set_props()" onchange="set_props()" onclick="set_props()"/></td>
onkeyup="set_props()" onchange="set_props()" onclick="set_props()"
value="$value"/></td>
<td><input style="display: inline; margin-top:-10px" type="button" class="btn btn-danger"
onclick="delete_prop(this)" value="Remove"/></td>
</div>
@ -14,9 +16,11 @@
<tr id_attr="$id">
<div class="input control-group">
<td><input style="display: inline" field="key"
onkeyup="set_props()" onchange="set_props()" onclick="set_props()"/></td>
onkeyup="set_props()" onchange="set_props()" onclick="set_props()"
value="$name"/></td>
<td><input style="display: inline; margin-left:10px; margin-right:10px" field="value" class="input-medium"
onkeyup="set_props()" onchange="set_props()" onclick="set_props()"/></td>
onkeyup="set_props()" onchange="set_props()" onclick="set_props()"
value="$value"/></td>
<td><input style="display: inline; margin-top:-10px" type="button" class="btn btn-danger"
onclick="delete_prop(this)" value="Remove"/></td>
</div>
@ -57,14 +61,19 @@
});
}
function add_prop(target) {
function add_prop(target, name, value) {
var name = (typeof name === 'undefined') ? '' : name;
var value = (typeof value === 'undefined') ? '' : value;
$("#" + target + " table").show();
var id = get_next_id(target);
var tmpl_from = target == "params" ? "args" : target;
var template = $("#" + tmpl_from + "_template").text()
.replace(/\$id/g, id);
template = template.replace(/\$name/g, name);
template = template.replace(/\$value/g, value);
$("#" + target + " tbody").append(template);
set_props();
return id
}
function delete_prop(el) {
var tr = $(el).parents("tr")[0];
@ -97,6 +106,15 @@
return res;
}
function load_prior_configs() {
for(targ in targets) {
var configs = JSON.parse($("#id_job_" + targets[targ]).val());
for(conf in configs) {
add_prop(targets[targ], conf, configs[conf]);
}
}
}
properties = {};
$("label[for=id_property_name]").hide();
$("#id_property_name").hide().find("option")
@ -127,6 +145,7 @@
.replace(/\$target/g, target));
});
$("#job_cfg table").hide();
load_prior_configs();
}
});
</script>