Adding Job Binaries panel for Sahara

The Job Binaries panel allows for creating/uploading
files that will serve as executables or libraries for
Elastic Data Processing jobs.
This code was originally from:
https://github.com/openstack/sahara-dashboard

Change-Id: I4e7b265a7d3292d7cec395bf2ff11fd546f06360
Partial-Implements: blueprint merge-sahara-dashboard
Co-Authored-By: Nikita Konovalov <nkonovalov@mirantis.com>
Co-Authored-By: Dmitry Mescheryakov <dmescheryakov@mirantis.com>
This commit is contained in:
Chad Roberts 2014-04-29 12:15:58 -04:00
parent 991882ca4a
commit f25628d91c
17 changed files with 778 additions and 1 deletions

View File

@ -65,7 +65,8 @@ class DataProcessingPanels(horizon.PanelGroup):
'data_processing.nodegroup_templates', 'data_processing.nodegroup_templates',
'data_processing.cluster_templates', 'data_processing.cluster_templates',
'data_processing.clusters', 'data_processing.clusters',
'data_processing.data_sources', ) 'data_processing.data_sources',
'data_processing.job_binaries', )
class Project(horizon.Dashboard): class Project(horizon.Dashboard):

View File

@ -0,0 +1,197 @@
# 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
import uuid
from django.forms import util
from django.forms import widgets
from django import template
from django.template import defaultfilters
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from openstack_dashboard.api import sahara as saharaclient
LOG = logging.getLogger(__name__)
class LabeledInput(widgets.Input):
def render(self, name, values, attrs=None):
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
output = "<span id='%s'>%s</span>%s" %\
("id_%s_label" % name,
"internal-db://",
('<input%s />' % util.flatatt(final_attrs)))
return mark_safe(output)
class JobBinaryCreateForm(forms.SelfHandlingForm):
NEW_SCRIPT = "%%%NEWSCRIPT%%%"
UPLOAD_BIN = "%%%UPLOADFILE%%%"
job_binary_name = forms.CharField(label=_("Name"))
job_binary_type = forms.ChoiceField(label=_("Storage type"))
job_binary_url = forms.CharField(label=_("URL"),
required=False,
widget=LabeledInput())
job_binary_internal = forms.ChoiceField(label=_("Internal binary"),
required=False)
job_binary_file = forms.FileField(label=_("Upload File"),
required=False)
job_binary_script_name = forms.CharField(label=_("Script name"),
required=False)
job_binary_script = forms.CharField(label=_("Script text"),
required=False,
widget=forms.Textarea())
job_binary_username = forms.CharField(label=_("Username"),
required=False)
job_binary_password = forms.CharField(label=_("Password"),
required=False,
widget=forms.PasswordInput(
attrs={'autocomplete': 'off'}))
job_binary_description = forms.CharField(label=_("Description"),
required=False,
widget=forms.Textarea())
def __init__(self, request, *args, **kwargs):
super(JobBinaryCreateForm, self).__init__(request, *args, **kwargs)
self.help_text_template = ("project/data_processing.job_binaries/"
"_create_job_binary_help.html")
self.fields["job_binary_type"].choices =\
[("internal-db", "Internal database"),
("swift", "Swift")]
self.fields["job_binary_internal"].choices =\
self.populate_job_binary_internal_choices(request)
def populate_job_binary_internal_choices(self, request):
try:
job_binaries = saharaclient.job_binary_internal_list(request)
except Exception:
exceptions.handle(request,
_("Failed to get list of internal binaries."))
job_binaries = []
choices = [(job_binary.id, job_binary.name)
for job_binary in job_binaries]
choices.insert(0, (self.NEW_SCRIPT, '*Create a script'))
choices.insert(0, (self.UPLOAD_BIN, '*Upload a new file'))
return choices
def handle(self, request, context):
try:
extra = {}
bin_url = "%s://%s" % (context["job_binary_type"],
context["job_binary_url"])
if(context["job_binary_type"] == "internal-db"):
bin_url = self.handle_internal(request, context)
elif(context["job_binary_type"] == "swift"):
extra = self.handle_swift(request, context)
saharaclient.job_binary_create(
request,
context["job_binary_name"],
bin_url,
context["job_binary_description"],
extra)
messages.success(request, "Successfully created job binary")
return True
except Exception:
exceptions.handle(request,
_("Unable to create job binary"))
return False
def get_help_text(self, extra_context=None):
text = ""
extra_context = extra_context or {}
if self.help_text_template:
tmpl = template.loader.get_template(self.help_text_template)
context = template.RequestContext(self.request, extra_context)
text += tmpl.render(context)
else:
text += defaultfilters.linebreaks(force_unicode(self.help_text))
return defaultfilters.safe(text)
class Meta:
name = _("Create Job Binary")
help_text_template = ("project/data_processing.job_binaries/"
"_create_job_binary_help.html")
def handle_internal(self, request, context):
result = ""
bin_id = context["job_binary_internal"]
if(bin_id == self.UPLOAD_BIN):
try:
result = saharaclient.job_binary_internal_create(
request,
self.get_unique_binary_name(
request, request.FILES["job_binary_file"].name),
request.FILES["job_binary_file"].read())
except Exception:
exceptions.handle(request,
_("Unable to upload job binary"))
return None
elif(bin_id == self.NEW_SCRIPT):
try:
result = saharaclient.job_binary_internal_create(
request,
self.get_unique_binary_name(
request, context["job_binary_script_name"]),
context["job_binary_script"])
except Exception:
exceptions.handle(request,
_("Unable to create job binary"))
return None
bin_id = result.id
return "internal-db://%s" % bin_id
def handle_swift(self, request, context):
username = context["job_binary_username"]
password = context["job_binary_password"]
extra = {
"user": username,
"password": password
}
return extra
def get_unique_binary_name(self, request, base_name):
try:
internals = saharaclient.job_binary_internal_list(request)
except Exception:
internals = []
exceptions.handle(request,
_("Failed to fetch internal binary list"))
names = [internal.name for internal in internals]
if base_name in names:
return "%s_%s" % (base_name, uuid.uuid1())
return base_name

View 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 JobBinariesPanel(horizon.Panel):
name = _("Job Binaries")
slug = 'data_processing.job_binaries'
permissions = ('openstack.services.data_processing',)
dashboard.Project.register(JobBinariesPanel)

View File

@ -0,0 +1,79 @@
# 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 tables
from openstack_dashboard.api import sahara as saharaclient
from saharaclient.api import base as api_base
LOG = logging.getLogger(__name__)
class CreateJobBinary(tables.LinkAction):
name = "create job binary"
verbose_name = _("Create Job Binary")
url = "horizon:project:data_processing.job_binaries:create-job-binary"
classes = ("btn-launch", "ajax-modal")
class DeleteJobBinary(tables.BatchAction):
name = "delete"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Job binary")
data_type_plural = _("Job binaries")
classes = ('btn-danger', 'btn-terminate')
def action(self, request, obj_id):
jb = saharaclient.job_binary_get(request, obj_id)
(jb_type, jb_internal_id) = jb.url.split("://")
if jb_type == "internal-db":
try:
saharaclient.job_binary_internal_delete(request,
jb_internal_id)
except api_base.APIException:
# nothing to do for job-binary-internal if
# it does not exist.
pass
saharaclient.job_binary_delete(request, obj_id)
class DownloadJobBinary(tables.LinkAction):
name = "download job binary"
verbose_name = _("Download Job Binary")
url = "horizon:project:data_processing.job_binaries:download"
classes = ("btn-edit")
class JobBinariesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:project:data_processing.job_binaries:details"))
type = tables.Column("url",
verbose_name=_("Url"))
description = tables.Column("description",
verbose_name=_("Description"))
class Meta:
name = "job_binaries"
verbose_name = _("Job Binaries")
table_actions = (CreateJobBinary,
DeleteJobBinary)
row_actions = (DeleteJobBinary, DownloadJobBinary)

View File

@ -0,0 +1,45 @@
# 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 exceptions
from horizon import tabs
from openstack_dashboard.api import sahara as saharaclient
LOG = logging.getLogger(__name__)
class JobBinaryDetailsTab(tabs.Tab):
name = _("General Info")
slug = "job_binaries_details_tab"
template_name = ("project/data_processing.job_binaries/_details.html")
def get_context_data(self, request):
job_binary_id = self.tab_group.kwargs['job_binary_id']
try:
job_binary = saharaclient.job_binary_get(request, job_binary_id)
except Exception:
job_binary = {}
exceptions.handle(request,
_("Unable to fetch job binary."))
return {"job_binary": job_binary}
class JobBinaryDetailsTabs(tabs.TabGroup):
slug = "job_binary_details"
tabs = (JobBinaryDetailsTab,)
sticky = True

View File

@ -0,0 +1,27 @@
{% extends "horizon/common/_modal_form.html" %}
{% load url from future %}
{% load i18n %}
{% block form_id %}create-job-binary{% endblock %}
{% block form_action %}{% url 'horizon:project:data_processing.job_binaries:create-job-binary' %}{% endblock %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-header %}{% trans "Create Job Binary" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="form-help-block">
{{ form.get_help_text }}
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" id="upload_file_btn" type="submit" value="{% trans "Create" %}"/>
<a href="{% url 'horizon:project:data_processing.job_binaries:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -0,0 +1,32 @@
{% load i18n horizon %}
<div class="well">
<p>
{% blocktrans %}<b>Important</b>: The name that you give your job binary will be the name used in your job execution.
If your binary requires a particular name or extension (ie: ".jar"), be sure to include it here.{% endblocktrans %}
</p>
<p>
{% blocktrans %}Select the storage type for your job binary.{% endblocktrans %}
<ul>
<li>{% blocktrans %}Data Processing internal database{% endblocktrans %}</li>
<li>{% blocktrans %}Swift{% endblocktrans %}</li>
</ul>
</p>
<p>
{% blocktrans %}For Data Processing internal job binaries, you may choose from the following:{% endblocktrans %}
<ul>
<li>{% blocktrans %}Choose an existing file{% endblocktrans %}</li>
<li>{% blocktrans %}Upload a new file{% endblocktrans %}</li>
<li>{% blocktrans %}Create a script to be uploaded dynamically{% endblocktrans %}</ul>
</ul>
</p>
<p>
{% blocktrans %}For Object Store job binaries, you must:{% endblocktrans %}
<ul>
<li>{% blocktrans %}Enter the URL for the file{% endblocktrans %}</li>
<li>{% blocktrans %}Enter the username and password required to access that file{% endblocktrans %}</li>
</ul>
</p>
<p>
{% blocktrans %}You may also enter an optional description for your job binary.{% endblocktrans %}
</p>
</div>

View File

@ -0,0 +1,19 @@
{% load i18n %}
<h3>{% trans "Job Binary Overview" %}</h3>
<div class="status row-fluid detail">
<dl>
<dt>{% trans "Name" %}</dt>
<dd>{{ job_binary.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ job_binary.id }}</dd>
<dt>{% trans "URL" %}</dt>
<dd>{{ job_binary.url }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ job_binary.description|default:_("None") }}</dd>
<dt>{% trans "Project id" %}</dt>
<dd>{{ job_binary.tenant_id }}</dd>
<dt>{% trans "Create time" %}</dt>
<dd>{{ job_binary.created_at }}</dd>
</dl>
<a href="{% url 'horizon:project:data_processing.job_binaries:download' job_binary.id %}">{% trans "Download job binary" %}</a>
</div>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Job Binary" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Create Job Binary") %}
{% endblock page_header %}
{% block main %}
{% include 'project/data_processing.job_binaries/_create.html' %}
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Job Binary Details" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Job Binary Details") %}
{% endblock page_header %}
{% block main %}
<div class="row-fluid">
<div class="span12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Data Processing" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Job Binaries") %}
{% endblock page_header %}
{% block main %}
<style type="text/css">
#id_job_binary_url {
width: 200px !important; }
.form-help-block {
float: left;
text-align: left;
width: 300px; }
</style>
<div class="job_binaries">
{{ job_binaries_table.render }}
</div>
{% include "project/data_processing.job_binaries/job_binaries_form_script.html" %}
{% endblock %}

View File

@ -0,0 +1,74 @@
<script type="text/javascript">
addHorizonLoadEvent(function () {
horizon.modals.addModalInitFunction(function (modal) {
hide_extra_fields();
function hide_extra_fields() {
$("[name=job_binary_username]").closest(".control-group").hide();
$("[name=job_binary_password]").closest(".control-group").hide();
$("[name=job_binary_file]").closest(".control-group").hide();
$("[name=job_binary_url]").closest(".control-group").hide();
$("[name=job_binary_internal]").closest(".control-group").hide();
}
$("#id_job_binary_type").change(function() {
var label = $("#id_job_binary_url_label");
var bin_file = $("[name=job_binary_file]").closest(".control-group");
var bin_choice = $("[name=job_binary_internal]").closest(".control-group");
var bin_url = $("[name=job_binary_url]").closest(".control-group");
var username = $("[name=job_binary_username]").closest(".control-group");
var password = $("[name=job_binary_password]").closest(".control-group");
var script_name = $("[name=job_binary_script_name]").closest(".control-group");
var script_text = $("[name=job_binary_script]").closest(".control-group");
switch($(this).val()) {
case "internal-db":
label.html("internal-db://");
username.hide();
password.hide();
bin_file.show();
bin_choice.show();
bin_url.hide();
script_name.hide();
script_text.hide();
break;
case "swift":
username.show();
password.show();
bin_file.hide();
bin_choice.hide();
bin_url.show();
label.html("swift://");
script_name.hide();
script_text.hide();
break;
}
});
$("#id_job_binary_type").change();
$("[name=job_binary_internal]").change(function() {
var bin_file = $("[name=job_binary_file]").closest(".control-group");
var bin_choice = $("[name=job_binary_internal]").closest(".control-group");
var bin_url = $("[name=job_binary_url]").closest(".control-group");
var script_name = $("[name=job_binary_script_name]").closest(".control-group");
var script_text = $("[name=job_binary_script]").closest(".control-group");
switch($(this).val()) {
case "%%%NEWSCRIPT%%%":
bin_file.hide();
script_name.show();
script_text.show();
break;
case "%%%UPLOADFILE%%%":
bin_file.show();
script_name.hide();
script_text.hide();
break;
default:
bin_file.hide();
script_name.hide();
script_text.hide();
break;
}
});
$("#job_binary_internal").change();
});
});
</script>

View File

@ -0,0 +1,80 @@
# 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_binaries:index')
DETAILS_URL = reverse(
'horizon:project:data_processing.job_binaries:details', args=['id'])
class DataProcessingJobBinaryTests(test.TestCase):
@test.create_stubs({api.sahara: ('job_binary_list',)})
def test_index(self):
api.sahara.job_binary_list(IsA(http.HttpRequest)) \
.AndReturn(self.job_binaries.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res,
'project/data_processing.job_binaries/job_binaries.html')
self.assertContains(res, 'Job Binaries')
self.assertContains(res, 'Name')
self.assertContains(res, 'example.pig')
@test.create_stubs({api.sahara: ('job_binary_get',)})
def test_details(self):
api.sahara.job_binary_get(IsA(http.HttpRequest), IsA(unicode)) \
.AndReturn(self.job_binaries.list()[0])
self.mox.ReplayAll()
res = self.client.get(DETAILS_URL)
self.assertTemplateUsed(res,
'project/data_processing.job_binaries/details.html')
self.assertContains(res, 'Job Binary Details')
@test.create_stubs({api.sahara: ('job_binary_list',
'job_binary_get',
'job_binary_internal_delete',
'job_binary_delete',)})
def test_delete(self):
jb_list = (api.sahara.job_binary_list(IsA(http.HttpRequest))
.AndReturn(self.job_binaries.list()))
api.sahara.job_binary_get(IsA(http.HttpRequest), IsA(unicode)) \
.AndReturn(self.job_binaries.list()[0])
api.sahara.job_binary_delete(IsA(http.HttpRequest), jb_list[0].id)
int_id = jb_list[0].url.split("//")[1]
api.sahara.job_binary_internal_delete(IsA(http.HttpRequest), int_id)
self.mox.ReplayAll()
form_data = {"action": "job_binaries__delete__%s" % jb_list[0].id}
res = self.client.post(INDEX_URL, form_data)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.sahara: ('job_binary_get',
'job_binary_get_file')})
def test_download(self):
jb = api.sahara.job_binary_get(IsA(http.HttpRequest), IsA(unicode)) \
.AndReturn(self.job_binaries.list()[0])
api.sahara.job_binary_get_file(IsA(http.HttpRequest), jb.id) \
.AndReturn("TEST FILE CONTENT")
self.mox.ReplayAll()
context = {'job_binary_id': jb.id}
url = reverse('horizon:project:data_processing.job_binaries:download',
kwargs={'job_binary_id': jb.id})
res = self.client.get(url, context)
self.assertTrue(res.has_header('content-disposition'))

View File

@ -0,0 +1,35 @@
# 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.conf.urls import patterns # noqa
from django.conf.urls import url # noqa
import openstack_dashboard.dashboards.project.data_processing. \
job_binaries.views as views
urlpatterns = patterns('',
url(r'^$', views.JobBinariesView.as_view(),
name='index'),
url(r'^$', views.JobBinariesView.as_view(),
name='job-binaries'),
url(r'^create-job-binary$',
views.CreateJobBinaryView.as_view(),
name='create-job-binary'),
url(r'^(?P<job_binary_id>[^/]+)$',
views.JobBinaryDetailsView.as_view(),
name='details'),
url(r'^(?P<job_binary_id>[^/]+)/download/$',
views.DownloadJobBinaryView.as_view(),
name='download'))

View File

@ -0,0 +1,93 @@
# 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.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django import http
from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _
from django.views import generic
from horizon import exceptions
from horizon import forms
from horizon import tables
from horizon import tabs
from openstack_dashboard.api import sahara as saharaclient
import openstack_dashboard.dashboards.project.data_processing. \
job_binaries.forms as job_binary_forms
from openstack_dashboard.dashboards.project.data_processing.job_binaries \
import tables as jb_tables
import openstack_dashboard.dashboards.project.data_processing.job_binaries. \
tabs as _tabs
LOG = logging.getLogger(__name__)
class JobBinariesView(tables.DataTableView):
table_class = jb_tables.JobBinariesTable
template_name = 'project/data_processing.job_binaries/job_binaries.html'
def get_data(self):
try:
job_binaries = saharaclient.job_binary_list(self.request)
except Exception:
job_binaries = []
exceptions.handle(self.request,
_("Unable to fetch job binary list."))
return job_binaries
class CreateJobBinaryView(forms.ModalFormView):
form_class = job_binary_forms.JobBinaryCreateForm
success_url = reverse_lazy(
'horizon:project:data_processing.job_binaries:index')
classes = ("ajax-modal")
template_name = "project/data_processing.job_binaries/create.html"
class JobBinaryDetailsView(tabs.TabView):
tab_group_class = _tabs.JobBinaryDetailsTabs
template_name = 'project/data_processing.job_binaries/details.html'
def get_context_data(self, **kwargs):
context = super(JobBinaryDetailsView, self)\
.get_context_data(**kwargs)
return context
def get_data(self):
pass
class DownloadJobBinaryView(generic.View):
def get(self, request, job_binary_id=None):
try:
jb = saharaclient.job_binary_get(request, job_binary_id)
data = saharaclient.job_binary_get_file(request, job_binary_id)
except Exception:
redirect = reverse(
'horizon:project:data_processing.job_binaries:index')
exceptions.handle(self.request,
_('Unable to fetch job binary: %(exc)s'),
redirect=redirect)
response = http.HttpResponse(mimetype='application/binary')
response['Content-Disposition'] = \
'attachment; filename=%s' % defaultfilters.slugify(jb.name)
response.write(data)
response['Content-Length'] = str(len(data))
return response

View File

@ -15,6 +15,7 @@ from openstack_dashboard.test.test_data import utils
from saharaclient.api import cluster_templates from saharaclient.api import cluster_templates
from saharaclient.api import clusters from saharaclient.api import clusters
from saharaclient.api import data_sources from saharaclient.api import data_sources
from saharaclient.api import job_binaries
from saharaclient.api import node_group_templates from saharaclient.api import node_group_templates
from saharaclient.api import plugins from saharaclient.api import plugins
@ -25,6 +26,7 @@ def data(TEST):
TEST.cluster_templates = utils.TestDataContainer() TEST.cluster_templates = utils.TestDataContainer()
TEST.clusters = utils.TestDataContainer() TEST.clusters = utils.TestDataContainer()
TEST.data_sources = utils.TestDataContainer() TEST.data_sources = utils.TestDataContainer()
TEST.job_binaries = utils.TestDataContainer()
plugin1_dict = { plugin1_dict = {
"description": "vanilla plugin", "description": "vanilla plugin",
@ -261,3 +263,18 @@ def data(TEST):
data_sources.DataSourceManager(None), data_source2_dict) data_sources.DataSourceManager(None), data_source2_dict)
TEST.data_sources.add(data_source1) TEST.data_sources.add(data_source1)
TEST.data_sources.add(data_source2) TEST.data_sources.add(data_source2)
#Job Binaries
job_binary1_dict = {
"created_at": "2014-06-05 18:15:15.581285",
"description": "",
"id": "3f3a07ac-7d6f-49e8-8669-40b25ee891b7",
"name": "example.pig",
"tenant_id": "429ad8447c2d47bc8e0382d244e1d1df",
"updated_at": None,
"url": "internal-db://80121dea-f8bd-4ad3-bcc7-096f4bfc722d"
}
job_binary1 = job_binaries.JobBinaries(
job_binaries.JobBinariesManager(None), job_binary1_dict)
TEST.job_binaries.add(job_binary1)