diff --git a/openstack_dashboard/dashboards/project/dashboard.py b/openstack_dashboard/dashboards/project/dashboard.py index 1f0fbe0225..c3b45a3b42 100644 --- a/openstack_dashboard/dashboards/project/dashboard.py +++ b/openstack_dashboard/dashboards/project/dashboard.py @@ -65,7 +65,8 @@ class DataProcessingPanels(horizon.PanelGroup): 'data_processing.nodegroup_templates', 'data_processing.cluster_templates', 'data_processing.clusters', - 'data_processing.data_sources', ) + 'data_processing.data_sources', + 'data_processing.job_binaries', ) class Project(horizon.Dashboard): diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/__init__.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/forms.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/forms.py new file mode 100644 index 0000000000..050744976d --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/forms.py @@ -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 = "%s%s" %\ + ("id_%s_label" % name, + "internal-db://", + ('' % 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 diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/panel.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/panel.py new file mode 100644 index 0000000000..bdbec37d1c --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/panel.py @@ -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) diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/tables.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/tables.py new file mode 100644 index 0000000000..449ae47c1c --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/tables.py @@ -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) diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/tabs.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/tabs.py new file mode 100644 index 0000000000..5ee175b689 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/tabs.py @@ -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 diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_create.html b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_create.html new file mode 100644 index 0000000000..658dbbe92d --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_create.html @@ -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 %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+ {{ form.get_help_text }} +
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_create_job_binary_help.html b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_create_job_binary_help.html new file mode 100644 index 0000000000..39be0de1c3 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_create_job_binary_help.html @@ -0,0 +1,32 @@ +{% load i18n horizon %} +
+

+ {% blocktrans %}Important: 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 %} +

+

+ {% blocktrans %}Select the storage type for your job binary.{% endblocktrans %} +

+

+

+ {% blocktrans %}For Data Processing internal job binaries, you may choose from the following:{% endblocktrans %} +

+ +

+

+ {% blocktrans %}For Object Store job binaries, you must:{% endblocktrans %} +

+

+

+ {% blocktrans %}You may also enter an optional description for your job binary.{% endblocktrans %} +

+
\ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_details.html b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_details.html new file mode 100644 index 0000000000..e942cd3dcc --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/_details.html @@ -0,0 +1,19 @@ +{% load i18n %} +

{% trans "Job Binary Overview" %}

+
+
+
{% trans "Name" %}
+
{{ job_binary.name }}
+
{% trans "ID" %}
+
{{ job_binary.id }}
+
{% trans "URL" %}
+
{{ job_binary.url }}
+
{% trans "Description" %}
+
{{ job_binary.description|default:_("None") }}
+
{% trans "Project id" %}
+
{{ job_binary.tenant_id }}
+
{% trans "Create time" %}
+
{{ job_binary.created_at }}
+
+ {% trans "Download job binary" %} +
diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/create.html b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/create.html new file mode 100644 index 0000000000..af1b14c581 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/create.html @@ -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 %} \ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/details.html b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/details.html new file mode 100644 index 0000000000..c4e61589be --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/details.html @@ -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 %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} \ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/job_binaries.html b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/job_binaries.html new file mode 100644 index 0000000000..686060c664 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/job_binaries.html @@ -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 %} + + +
+ {{ job_binaries_table.render }} +
+ +{% include "project/data_processing.job_binaries/job_binaries_form_script.html" %} + +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/job_binaries_form_script.html b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/job_binaries_form_script.html new file mode 100644 index 0000000000..89a7a33a99 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/templates/data_processing.job_binaries/job_binaries_form_script.html @@ -0,0 +1,74 @@ + + diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/tests.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/tests.py new file mode 100644 index 0000000000..2a997656a2 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/tests.py @@ -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')) diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/urls.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/urls.py new file mode 100644 index 0000000000..0b29ba82b1 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/urls.py @@ -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[^/]+)$', + views.JobBinaryDetailsView.as_view(), + name='details'), + url(r'^(?P[^/]+)/download/$', + views.DownloadJobBinaryView.as_view(), + name='download')) diff --git a/openstack_dashboard/dashboards/project/data_processing/job_binaries/views.py b/openstack_dashboard/dashboards/project/data_processing/job_binaries/views.py new file mode 100644 index 0000000000..44917cb301 --- /dev/null +++ b/openstack_dashboard/dashboards/project/data_processing/job_binaries/views.py @@ -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 diff --git a/openstack_dashboard/test/test_data/sahara_data.py b/openstack_dashboard/test/test_data/sahara_data.py index 97bb085f20..0455a30442 100644 --- a/openstack_dashboard/test/test_data/sahara_data.py +++ b/openstack_dashboard/test/test_data/sahara_data.py @@ -15,6 +15,7 @@ from openstack_dashboard.test.test_data import utils from saharaclient.api import cluster_templates from saharaclient.api import clusters from saharaclient.api import data_sources +from saharaclient.api import job_binaries from saharaclient.api import node_group_templates from saharaclient.api import plugins @@ -25,6 +26,7 @@ def data(TEST): TEST.cluster_templates = utils.TestDataContainer() TEST.clusters = utils.TestDataContainer() TEST.data_sources = utils.TestDataContainer() + TEST.job_binaries = utils.TestDataContainer() plugin1_dict = { "description": "vanilla plugin", @@ -261,3 +263,18 @@ def data(TEST): data_sources.DataSourceManager(None), data_source2_dict) TEST.data_sources.add(data_source1) 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)