Skip to content

Commit

Permalink
Merge "Adding Jobs and Job Executions panels for Sahara"
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenkins authored and openstack-gerrit committed Jul 24, 2014
2 parents ec4cc7d + 0be5fb4 commit c11d044
Show file tree
Hide file tree
Showing 33 changed files with 2,142 additions and 1 deletion.
4 changes: 3 additions & 1 deletion openstack_dashboard/dashboards/project/dashboard.py
Expand Up @@ -66,7 +66,9 @@ class DataProcessingPanels(horizon.PanelGroup):
'data_processing.cluster_templates',
'data_processing.clusters',
'data_processing.data_sources',
'data_processing.job_binaries', )
'data_processing.job_binaries',
'data_processing.jobs',
'data_processing.job_executions', )


class Project(horizon.Dashboard):
Expand Down
Empty file.
@@ -0,0 +1,27 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from django.utils.translation import ugettext_lazy as _

import horizon

from openstack_dashboard.dashboards.project import dashboard


class JobExecutionsPanel(horizon.Panel):
name = _("Job Executions")
slug = 'data_processing.job_executions'
permissions = ('openstack.services.data_processing',)


dashboard.Project.register(JobExecutionsPanel)
@@ -0,0 +1,112 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

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 openstack_dashboard.api import sahara as saharaclient
from openstack_dashboard.dashboards.project.data_processing. \
jobs import tables as j_t

LOG = logging.getLogger(__name__)


class DeleteJobExecution(tables.BatchAction):
name = "delete"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Job execution")
data_type_plural = _("Job executions")
classes = ('btn-danger', 'btn-terminate')

def action(self, request, obj_id):
saharaclient.job_execution_delete(request, 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:project:data_processing.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:project:data_processing.jobs:choose-plugin"
classes = ('ajax-modal', 'btn-launch')


class UpdateRow(tables.Row):
ajax = True

def get_data(self, request, job_execution_id):
job_execution = saharaclient.job_execution_get(request,
job_execution_id)
return job_execution


class JobExecutionsTable(tables.DataTable):
class StatusColumn(tables.Column):
def get_data(self, datum):
return datum.info['status']

STATUS_CHOICES = (
("DONEWITHERROR", False),
("FAILED", False),
("KILLED", False),
("SUCCEEDED", True),
)

name = tables.Column("id",
verbose_name=_("ID"),
display_choices=(("id", "ID"), ("name", "Name")),
link=("horizon:project:data_processing.job_executions:details"))

status = StatusColumn("info",
status=True,
status_choices=STATUS_CHOICES,
verbose_name=_("Status"))

def get_object_display(self, datum):
return datum.id

class Meta:
name = "job_executions"
row_class = UpdateRow
status_columns = ["status"]
verbose_name = _("Job Executions")
table_actions = [DeleteJobExecution]
row_actions = [DeleteJobExecution,
ReLaunchJobExistingCluster,
ReLaunchJobNewCluster]
@@ -0,0 +1,73 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging

from django.utils.translation import ugettext_lazy as _

from horizon import tabs

from openstack_dashboard.api import sahara as saharaclient

LOG = logging.getLogger(__name__)


class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "job_execution_tab"
template_name = ("project/data_processing.job_executions/_details.html")

def get_context_data(self, request):
job_execution_id = self.tab_group.kwargs['job_execution_id']
job_execution = saharaclient.job_execution_get(request,
job_execution_id)
object_names = self.get_object_names(job_execution,
request)

return {"job_execution": job_execution,
"object_names": object_names}

def get_object_names(self, job_ex, request):
object_names = {}
obj_names_map = {'input_name': {'obj': 'data_source_get',
'obj_id': job_ex.input_id},
'output_name': {'obj': 'data_source_get',
'obj_id': job_ex.output_id},
'cluster_name': {'obj': 'cluster_get',
'obj_id': job_ex.cluster_id},
'job_name': {'obj': 'job_get',
'obj_id': job_ex.job_id}}
for item in obj_names_map:
object_names[item] = (
self.get_object_name(obj_names_map[item]['obj_id'],
obj_names_map[item]['obj'],
request))

return object_names

def get_object_name(self, obj_id, sahara_obj, request):
object_name = None
try:
s_func = getattr(saharaclient, sahara_obj)
obj = s_func(request, obj_id)
object_name = obj.name
except Exception as e:
LOG.warn("Unable to get name for %s with object_id %s (%s)" %
(sahara_obj, obj_id, str(e)))
return object_name


class JobExecutionDetailsTabs(tabs.TabGroup):
slug = "job_execution_details"
tabs = (GeneralTab,)
sticky = True
@@ -0,0 +1,39 @@
{% load i18n sizeformat %}
<h3>{% trans "Job Execution Overview" %}</h3>
<div class="status row-fluid detail">
<dl>
<dt>{% trans "Status" %}</dt>
<dd>{{ job_execution.info.status }}</dd>
<dt>{% trans "Id" %}</dt>
<dd>{{ job_execution.id }}</dd>
<dt>{% trans "Job Id" %}</dt>
<dd>{{ job_execution.job_id }} ({{ object_names.job_name }})</dd>
<dt>{% trans "Input Id" %}</dt>
<dd>{{ job_execution.input_id }} ({{ object_names.input_name }})</dd>
<dt>{% trans "Output Id" %}</dt>
<dd>{{ job_execution.output_id }} ({{ object_names.output_name }})</dd>
<dt>{% trans "Cluster Id" %}</dt>
<dd>{{ job_execution.cluster_id }} ({{ object_names.cluster_name }})</dd>
<dt>{% trans "Last Updated" %}</dt>
<dd>{{ job_execution.updated_at }}</dd>
<dt>{% trans "Return Code" %}</dt>
<dd>{{ job_execution.return_code }}</dd>
<dt>{% trans "Oozie Job Id" %}</dt>
<dd>{{ job_execution.oozie_job_id }}</dd>
<dt>{% trans "Created" %}</dt>
<dd>{{ job_execution.created_at }}</dd>
<dt>{% trans "Tenant Id" %}</dt>
<dd>{{ job_execution.tenant_id }}</dd>
<dt>{% trans "Job Configuration" %}</dt>
<dd>{% for group, vals in job_execution.job_configs.iteritems %}
<ul><li><span style="font-weight:bold">{{ group }}:</span>
{%if group == "args" %}
<ul>{% for val in vals %} <li>{{ val }}</li> {% endfor %}</ul>
{% else %}
<ul>{% for key, val in vals.iteritems %} <li>{{ key }} = {{ val }}</li> {% endfor %}</ul>
{% endif %}
</li></ul>
{% endfor %}
</dd>
</dl>
</div>
@@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Job Execution Details" %}{% endblock %}

{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Job Execution Details") %}
{% endblock page_header %}

{% block main %}
<div class="row-fluid">
<div class="span12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}
@@ -0,0 +1,66 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Data Processing" %}{% endblock %}

{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Job Executions") %}
{% endblock page_header %}

{% block main %}

<div class="job_executions">
{{ 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 %}
@@ -0,0 +1,46 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from django.core.urlresolvers import reverse
from django import http

from mox import IsA # noqa

from openstack_dashboard import api
from openstack_dashboard.test import helpers as test


INDEX_URL = reverse('horizon:project:data_processing.job_executions:index')
DETAILS_URL = reverse(
'horizon:project:data_processing.job_executions:details', args=['id'])


class DataProcessingJobExecutionTests(test.TestCase):
@test.create_stubs({api.sahara: ('job_execution_list',)})
def test_index(self):
api.sahara.job_execution_list(IsA(http.HttpRequest)) \
.AndReturn(self.job_executions.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res,
'project/data_processing.job_executions/job_executions.html')
self.assertContains(res, 'Executions')

@test.create_stubs({api.sahara: ('job_execution_get',)})
def test_details(self):
api.sahara.job_execution_get(IsA(http.HttpRequest), IsA(unicode)) \
.AndReturn(self.job_executions.list()[0])
self.mox.ReplayAll()
res = self.client.get(DETAILS_URL)
self.assertTemplateUsed(res,
'project/data_processing.job_executions/details.html')
self.assertContains(res, 'RUNNING')

0 comments on commit c11d044

Please sign in to comment.