From d2060199b22005fc46fa11bb76467a06e76c5b89 Mon Sep 17 00:00:00 2001 From: Chad Roberts Date: Tue, 3 Sep 2013 10:52:23 -0400 Subject: [PATCH] Adds EDP support in the UI for job execution First pass at job execution in the UI. Job binaries tab has been added, still needs work to handle internal and external sources as well as overall refinement of controls. Now includes job_binaries as a form rather than a workflow and forces a unique name for binary-internal uploads. Implements: blueprint edp-savanna-dashboard-ui Change-Id: Id32f7c5d5458918bd832070c53271c042d886f86 --- savannadashboard/api/base.py | 1 + savannadashboard/api/client.py | 7 + savannadashboard/api/job_binaries.py | 47 ++++++ savannadashboard/api/job_binaries_internal.py | 42 +++++ savannadashboard/api/job_executions.py | 36 ++++ savannadashboard/api/job_origins.py | 10 +- savannadashboard/api/jobs.py | 11 ++ savannadashboard/dashboard.py | 2 + savannadashboard/data_sources/views.py | 3 - .../data_sources/workflows/create.py | 1 - savannadashboard/job_binaries/__init__.py | 0 savannadashboard/job_binaries/forms.py | 154 ++++++++++++++++++ savannadashboard/job_binaries/panel.py | 30 ++++ savannadashboard/job_binaries/tables.py | 63 +++++++ savannadashboard/job_binaries/tabs.py | 44 +++++ savannadashboard/job_binaries/urls.py | 34 ++++ savannadashboard/job_binaries/views.py | 63 +++++++ savannadashboard/job_executions/__init__.py | 0 savannadashboard/job_executions/panel.py | 30 ++++ savannadashboard/job_executions/tables.py | 62 +++++++ savannadashboard/job_executions/tabs.py | 44 +++++ savannadashboard/job_executions/urls.py | 31 ++++ savannadashboard/job_executions/views.py | 51 ++++++ savannadashboard/job_origins/tables.py | 2 - .../job_origins/workflows/create.py | 108 +++++++++--- savannadashboard/jobs/tables.py | 21 ++- savannadashboard/jobs/urls.py | 3 + savannadashboard/jobs/views.py | 14 ++ savannadashboard/jobs/workflows/launch.py | 154 ++++++++++++++++++ .../templates/data_sources/data_sources.html | 2 +- .../templates/job_binaries/_create.html | 21 +++ .../job_binaries/_create_job_binary_help.html | 0 .../templates/job_binaries/_details.html | 18 ++ .../templates/job_binaries/create.html | 11 ++ .../templates/job_binaries/details.html | 15 ++ .../templates/job_binaries/job_binaries.html | 84 ++++++++++ .../templates/job_executions/_details.html | 28 ++++ .../templates/job_executions/details.html | 15 ++ .../job_executions/job_executions.html | 15 ++ .../templates/job_origins/_details.html | 8 - .../templates/job_origins/job_origins.html | 34 +++- .../jobs/_launch_job_configure_help.html | 0 .../templates/jobs/_launch_job_help.html | 0 savannadashboard/templates/jobs/jobs.html | 2 +- savannadashboard/templates/jobs/launch.html | 11 ++ 45 files changed, 1281 insertions(+), 51 deletions(-) create mode 100644 savannadashboard/api/job_binaries.py create mode 100644 savannadashboard/api/job_binaries_internal.py create mode 100644 savannadashboard/api/job_executions.py create mode 100644 savannadashboard/job_binaries/__init__.py create mode 100644 savannadashboard/job_binaries/forms.py create mode 100644 savannadashboard/job_binaries/panel.py create mode 100644 savannadashboard/job_binaries/tables.py create mode 100644 savannadashboard/job_binaries/tabs.py create mode 100644 savannadashboard/job_binaries/urls.py create mode 100644 savannadashboard/job_binaries/views.py create mode 100644 savannadashboard/job_executions/__init__.py create mode 100644 savannadashboard/job_executions/panel.py create mode 100644 savannadashboard/job_executions/tables.py create mode 100644 savannadashboard/job_executions/tabs.py create mode 100644 savannadashboard/job_executions/urls.py create mode 100644 savannadashboard/job_executions/views.py create mode 100644 savannadashboard/jobs/workflows/launch.py create mode 100644 savannadashboard/templates/job_binaries/_create.html create mode 100644 savannadashboard/templates/job_binaries/_create_job_binary_help.html create mode 100644 savannadashboard/templates/job_binaries/_details.html create mode 100644 savannadashboard/templates/job_binaries/create.html create mode 100644 savannadashboard/templates/job_binaries/details.html create mode 100644 savannadashboard/templates/job_binaries/job_binaries.html create mode 100644 savannadashboard/templates/job_executions/_details.html create mode 100644 savannadashboard/templates/job_executions/details.html create mode 100644 savannadashboard/templates/job_executions/job_executions.html create mode 100644 savannadashboard/templates/jobs/_launch_job_configure_help.html create mode 100644 savannadashboard/templates/jobs/_launch_job_help.html create mode 100644 savannadashboard/templates/jobs/launch.html diff --git a/savannadashboard/api/base.py b/savannadashboard/api/base.py index 72dcd239..361c3183 100644 --- a/savannadashboard/api/base.py +++ b/savannadashboard/api/base.py @@ -78,6 +78,7 @@ class ResourceManager(object): resp = self.api.client.put(url, json.dumps(data)) if resp.status_code != 202: self._raise_api_exception(resp) + return get_json(resp) def _list(self, url, response_key): resp = self.api.client.get(url) diff --git a/savannadashboard/api/client.py b/savannadashboard/api/client.py index e0751d57..f8841ddc 100644 --- a/savannadashboard/api/client.py +++ b/savannadashboard/api/client.py @@ -22,6 +22,9 @@ from savannadashboard.api import clusters from savannadashboard.api import data_sources from savannadashboard.api import httpclient from savannadashboard.api import images +from savannadashboard.api import job_binaries +from savannadashboard.api import job_binaries_internal +from savannadashboard.api import job_executions from savannadashboard.api import job_origins from savannadashboard.api import jobs from savannadashboard.api import node_group_templates @@ -77,3 +80,7 @@ class Client(object): self.jobs = jobs.JobManager(self) self.job_origins = job_origins.JobOriginManager(self) self.data_sources = data_sources.DataSourceManager(self) + self.job_executions = job_executions.JobExecutionManager(self) + self.job_binaries = job_binaries.JobBinaryManager(self) + self.job_binaries_internal =\ + job_binaries_internal.JobBinaryInternalManager(self) diff --git a/savannadashboard/api/job_binaries.py b/savannadashboard/api/job_binaries.py new file mode 100644 index 00000000..6a5d1f61 --- /dev/null +++ b/savannadashboard/api/job_binaries.py @@ -0,0 +1,47 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard.api import base + + +class JobBinary(base.Resource): + resource_name = 'Job Binary' + defaults = {} + + +class JobBinaryManager(base.ResourceManager): + resource_class = JobBinary + + def create(self, name, url, description, extra): + data = { + "name": name, + "url": url, + "description": description, + "extra": extra + } + + return self._create('/job-binaries', data) + + def list(self): + return self._list('/job-binaries', 'binaries') + + def get(self, job_binary_id): + return self._get('/job-binaries/%s' % job_binary_id, + 'resource') + + def delete(self, job_binary_id): + self._delete('/job-binaries/%s' % job_binary_id) diff --git a/savannadashboard/api/job_binaries_internal.py b/savannadashboard/api/job_binaries_internal.py new file mode 100644 index 00000000..1d08fd82 --- /dev/null +++ b/savannadashboard/api/job_binaries_internal.py @@ -0,0 +1,42 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard.api import base + + +class JobBinaryInternal(base.Resource): + resource_name = 'Job Binary Internal' + defaults = {} + + +class JobBinaryInternalManager(base.ResourceManager): + resource_class = JobBinaryInternal + + def create(self, name, data): + resp = self.api.client.put('/job-binary-internals/%s' % name, data) + if resp.status_code != 202: + self._raise_api_exception(resp) + return resp.json() + + def list(self): + return self._list('/job-binary-internals', 'binaries') + + def get(self, job_binary_id): + return self._get('/job-binary-internals/%s' % job_binary_id, + 'resource') + + def delete(self, job_binary_id): + self._delete('/job-binary-internals/%s' % job_binary_id) diff --git a/savannadashboard/api/job_executions.py b/savannadashboard/api/job_executions.py new file mode 100644 index 00000000..938b4250 --- /dev/null +++ b/savannadashboard/api/job_executions.py @@ -0,0 +1,36 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard.api import base + + +class JobExecution(base.Resource): + resource_name = 'JobExecution' + + +class JobExecutionManager(base.ResourceManager): + resource_class = JobExecution + + def list(self): + return self._list('/job-executions', 'job_executions') + + def get(self, obj_id): + return self._get('/job-executions/%s' % obj_id, + 'resource') + + def delete(self, obj_id): + self._delete('/job-executions/%s' % obj_id) diff --git a/savannadashboard/api/job_origins.py b/savannadashboard/api/job_origins.py index c26bc1d7..66046ac5 100644 --- a/savannadashboard/api/job_origins.py +++ b/savannadashboard/api/job_origins.py @@ -26,17 +26,13 @@ class JobOrigin(base.Resource): class JobOriginManager(base.ResourceManager): resource_class = JobOrigin - def create(self, name, storage_type, username, password, - location, description): + def create(self, name, mains, libs, description): data = { - 'credentials': {'user': username, - 'password': password - }, 'name': name, 'description': description, - 'storage_type': storage_type, - 'url': location + 'mains': mains, + 'libs': libs # TODO(croberts)fix when api is ready } self._create('/job-origins', data) diff --git a/savannadashboard/api/jobs.py b/savannadashboard/api/jobs.py index d1989327..5de4a380 100644 --- a/savannadashboard/api/jobs.py +++ b/savannadashboard/api/jobs.py @@ -48,3 +48,14 @@ class JobManager(base.ResourceManager): def delete(self, job_id): self._delete('/jobs/%s' % job_id) + + def launch(self, job_id, cluster_id, input_id, output_id, configs): + url = "/jobs/%s/execute" % job_id + data = { + "input_id": input_id, + "output_id": output_id, + "cluster_id": cluster_id, + "job_configs": configs + } + + return self._create(url, data) diff --git a/savannadashboard/dashboard.py b/savannadashboard/dashboard.py index a77a9eb1..fb8e3762 100644 --- a/savannadashboard/dashboard.py +++ b/savannadashboard/dashboard.py @@ -31,8 +31,10 @@ class SavannaDashboard(horizon.Dashboard): panels = ('clusters', 'cluster_templates', 'nodegroup_templates', + 'job_executions', 'jobs', 'job_origins', + 'job_binaries', 'data_sources', 'image_registry', 'plugins') diff --git a/savannadashboard/data_sources/views.py b/savannadashboard/data_sources/views.py index dc3b815f..6b5b5c6f 100644 --- a/savannadashboard/data_sources/views.py +++ b/savannadashboard/data_sources/views.py @@ -39,9 +39,6 @@ class DataSourcesView(tables.DataTableView): data_sources = savanna.data_sources.list() return data_sources - class FakeDataSource(object): - pass - class CreateDataSourceView(workflows.WorkflowView): workflow_class = create_flow.CreateDataSource diff --git a/savannadashboard/data_sources/workflows/create.py b/savannadashboard/data_sources/workflows/create.py index bdb6b305..3c4ad25a 100644 --- a/savannadashboard/data_sources/workflows/create.py +++ b/savannadashboard/data_sources/workflows/create.py @@ -62,7 +62,6 @@ class GeneralConfigAction(workflows.Action): class GeneralConfig(workflows.Step): action_class = GeneralConfigAction - contributes = ("hidden_configure_field", ) def contribute(self, data, context): for k, v in data.items(): diff --git a/savannadashboard/job_binaries/__init__.py b/savannadashboard/job_binaries/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/savannadashboard/job_binaries/forms.py b/savannadashboard/job_binaries/forms.py new file mode 100644 index 00000000..edd067d0 --- /dev/null +++ b/savannadashboard/job_binaries/forms.py @@ -0,0 +1,154 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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.forms.util import flatatt +from django.forms import widgets + +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext as _ + +from horizon import forms +from horizon import messages + +import savannadashboard.api.base as api_base +from savannadashboard.api import client as savannaclient + +import uuid + +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, + "savanna-db://", + ('' % flatatt(final_attrs))) + return mark_safe(output) + + +class JobBinaryCreateForm(forms.SelfHandlingForm): + job_binary_name = forms.CharField(label=_("Name"), + required=True) + + job_binary_type = forms.ChoiceField(label=_("Type"), + required=True) + + job_binary_url = forms.CharField(label=_("URL"), + required=False, + widget=LabeledInput()) + job_binary_savanna_internal = forms.ChoiceField(label=_("Savanna binary"), + required=False) + + job_binary_file = forms.FileField(label=_("Upload File"), + required=False) + + 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) + + def __init__(self, request, *args, **kwargs): + super(JobBinaryCreateForm, self).__init__(request, *args, **kwargs) + + self.fields["job_binary_type"].choices =\ + [("savanna-db", "Savanna internal database"), + ("swift-internal", "Swift internal"), + ("swift-external", "Swift external")] + + self.fields["job_binary_savanna_internal"].choices =\ + self.populate_job_binary_savanna_internal_choices(request) + + def populate_job_binary_savanna_internal_choices(self, request): + savanna = savannaclient.Client(request) + job_binaries = savanna.job_binaries_internal.list() + + choices = [(job_binary.id, job_binary.name) + for job_binary in job_binaries] + choices.insert(0, ('', 'Upload a new file')) + + return choices + + def handle(self, request, context): + try: + savanna = savannaclient.Client(request) + extra = {} + bin_url = "%s://%s" % (context["job_binary_type"], + context["job_binary_url"]) + if(context["job_binary_type"] == "savanna-db"): + bin_url = self.handle_savanna(request, context) + if(context["job_binary_type"] == "swift-internal"): + extra = self.handle_swift_internal(request, context) + + savanna.job_binaries.create( + context["job_binary_name"], + bin_url, + context["job_binary_description"], + extra) + messages.success(request, "Successfully created job binary") + return True + except api_base.APIException as e: + messages.error(request, str(e)) + return False + except Exception as e: + messages.error(request, str(e)) + return True + + class Meta: + name = _("Create Job Binary") + help_text_template = \ + ("job_binaries/_create_job_binary_help.html") + + def handle_savanna(self, request, context): + savanna = savannaclient.Client(request) + + bin_id = context["job_binary_savanna_internal"] + if(bin_id == ""): + result = savanna.job_binaries_internal.create( + self.get_unique_binary_name( + request, request.FILES["job_binary_file"].name), + request.FILES["job_binary_file"].read()) + bin_id = result["resource"]["id"] + + return "savanna-db://%s" % bin_id + + def handle_swift_internal(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): + savanna = savannaclient.Client(request) + internals = savanna.job_binaries_internal.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/savannadashboard/job_binaries/panel.py b/savannadashboard/job_binaries/panel.py new file mode 100644 index 00000000..2c9a3b17 --- /dev/null +++ b/savannadashboard/job_binaries/panel.py @@ -0,0 +1,30 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard import dashboard + + +class JobBinariesPanel(horizon.Panel): + name = _("Job Binaries") + slug = 'job_binaries' + + +dashboard.SavannaDashboard.register(JobBinariesPanel) diff --git a/savannadashboard/job_binaries/tables.py b/savannadashboard/job_binaries/tables.py new file mode 100644 index 00000000..28856afa --- /dev/null +++ b/savannadashboard/job_binaries/tables.py @@ -0,0 +1,63 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard.api import client as savannaclient + +LOG = logging.getLogger(__name__) + + +class CreateJobBinary(tables.LinkAction): + name = "create job binary" + verbose_name = _("Create Job Binary") + url = "horizon:savanna: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): + savanna = savannaclient.Client(request) + savanna.job_binaries.delete(obj_id) + + +class JobBinariesTable(tables.DataTable): + name = tables.Column("name", + verbose_name=_("Name"), + link=("horizon:savanna: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,) diff --git a/savannadashboard/job_binaries/tabs.py b/savannadashboard/job_binaries/tabs.py new file mode 100644 index 00000000..431ebca5 --- /dev/null +++ b/savannadashboard/job_binaries/tabs.py @@ -0,0 +1,44 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard.api import client as savannaclient + +LOG = logging.getLogger(__name__) + + +class GeneralTab(tabs.Tab): + name = _("General Info") + slug = "job_binaries_details_tab" + template_name = ("job_binaries/_details.html") + + def get_context_data(self, request): + job_binary_id = self.tab_group.kwargs['job_binary_id'] + savanna = savannaclient.Client(request) + job_binary = savanna.job_binaries.get(job_binary_id) + return {"job_binary": job_binary} + + +class JobBinaryDetailsTabs(tabs.TabGroup): + slug = "job_binary_details" + tabs = (GeneralTab,) + sticky = True diff --git a/savannadashboard/job_binaries/urls.py b/savannadashboard/job_binaries/urls.py new file mode 100644 index 00000000..ef562828 --- /dev/null +++ b/savannadashboard/job_binaries/urls.py @@ -0,0 +1,34 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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.defaults import patterns +from django.conf.urls.defaults import url + +import savannadashboard.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')) diff --git a/savannadashboard/job_binaries/views.py b/savannadashboard/job_binaries/views.py new file mode 100644 index 00000000..a43259fd --- /dev/null +++ b/savannadashboard/job_binaries/views.py @@ -0,0 +1,63 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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_lazy + +from horizon import forms +from horizon import tables +from horizon import tabs + +from savannadashboard.api import client as savannaclient + +import savannadashboard.job_binaries.forms as job_binary_forms +from savannadashboard.job_binaries.tables import JobBinariesTable +import savannadashboard.job_binaries.tabs as _tabs + + +LOG = logging.getLogger(__name__) + + +class JobBinariesView(tables.DataTableView): + table_class = JobBinariesTable + template_name = 'job_binaries/job_binaries.html' + + def get_data(self): + savanna = savannaclient.Client(self.request) + job_binaries = savanna.job_binaries.list() + return job_binaries + + +class CreateJobBinaryView(forms.ModalFormView): + form_class = job_binary_forms.JobBinaryCreateForm + success_url = reverse_lazy('horizon:savanna:job_binaries:index') + classes = ("ajax-modal") + template_name = "job_binaries/create.html" + + +class JobBinaryDetailsView(tabs.TabView): + tab_group_class = _tabs.JobBinaryDetailsTabs + template_name = '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 diff --git a/savannadashboard/job_executions/__init__.py b/savannadashboard/job_executions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/savannadashboard/job_executions/panel.py b/savannadashboard/job_executions/panel.py new file mode 100644 index 00000000..66caa820 --- /dev/null +++ b/savannadashboard/job_executions/panel.py @@ -0,0 +1,30 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard import dashboard + + +class JobExecutionsPanel(horizon.Panel): + name = _("Job Executions") + slug = 'job_executions' + + +dashboard.SavannaDashboard.register(JobExecutionsPanel) diff --git a/savannadashboard/job_executions/tables.py b/savannadashboard/job_executions/tables.py new file mode 100644 index 00000000..df83926f --- /dev/null +++ b/savannadashboard/job_executions/tables.py @@ -0,0 +1,62 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard.api import client as savannaclient + +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): + savanna = savannaclient.Client(request) + savanna.job_executions.delete(obj_id) + + +class JobExecutionsTable(tables.DataTable): + class StatusColumn(tables.Column): + def get_data(self, datum): + return datum.info['status'] + + name = tables.Column("id", + verbose_name=_("ID"), + display_choices=(("id", "ID"), ("name", "Name")), + link=("horizon:savanna:job_executions:details")) + + status = StatusColumn("info", + verbose_name=_("Status")) + + def get_object_display(self, datum): + return datum.id + + class Meta: + name = "job_executions" + verbose_name = _("Job Executions") + table_actions = [DeleteJobExecution] + row_actions = [DeleteJobExecution] diff --git a/savannadashboard/job_executions/tabs.py b/savannadashboard/job_executions/tabs.py new file mode 100644 index 00000000..819bbd4e --- /dev/null +++ b/savannadashboard/job_executions/tabs.py @@ -0,0 +1,44 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 savannadashboard.api import client as savannaclient + +LOG = logging.getLogger(__name__) + + +class GeneralTab(tabs.Tab): + name = _("General Info") + slug = "job_execution_tab" + template_name = ("job_executions/_details.html") + + def get_context_data(self, request): + job_execution_id = self.tab_group.kwargs['job_execution_id'] + savanna = savannaclient.Client(request) + job_execution = savanna.job_executions.get(job_execution_id) + return {"job_execution": job_execution} + + +class JobExecutionDetailsTabs(tabs.TabGroup): + slug = "job_execution_details" + tabs = (GeneralTab,) + sticky = True diff --git a/savannadashboard/job_executions/urls.py b/savannadashboard/job_executions/urls.py new file mode 100644 index 00000000..692e0719 --- /dev/null +++ b/savannadashboard/job_executions/urls.py @@ -0,0 +1,31 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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.defaults import patterns +from django.conf.urls.defaults import url + +import savannadashboard.job_executions.views as views + +urlpatterns = patterns('', + url(r'^$', views.JobExecutionsView.as_view(), + name='index'), + url(r'^$', views.JobExecutionsView.as_view(), + name='job-executions'), + url(r'^(?P[^/]+)$', + views.JobExecutionDetailsView.as_view(), + name='details')) diff --git a/savannadashboard/job_executions/views.py b/savannadashboard/job_executions/views.py new file mode 100644 index 00000000..33e79c4f --- /dev/null +++ b/savannadashboard/job_executions/views.py @@ -0,0 +1,51 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 horizon import tables +from horizon import tabs + +from savannadashboard.api import client as savannaclient + +from savannadashboard.job_executions.tables import JobExecutionsTable +import savannadashboard.job_executions.tabs as _tabs + +LOG = logging.getLogger(__name__) + + +class JobExecutionsView(tables.DataTableView): + table_class = JobExecutionsTable + template_name = 'job_executions/job_executions.html' + + def get_data(self): + savanna = savannaclient.Client(self.request) + jobs = savanna.job_executions.list() + return jobs + + +class JobExecutionDetailsView(tabs.TabView): + tab_group_class = _tabs.JobExecutionDetailsTabs + template_name = 'job_executions/details.html' + + def get_context_data(self, **kwargs): + context = super(JobExecutionDetailsView, self)\ + .get_context_data(**kwargs) + return context + + def get_data(self): + pass diff --git a/savannadashboard/job_origins/tables.py b/savannadashboard/job_origins/tables.py index cef1c2e6..7bf90ef2 100644 --- a/savannadashboard/job_origins/tables.py +++ b/savannadashboard/job_origins/tables.py @@ -50,8 +50,6 @@ class JobOriginsTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name"), link=("horizon:savanna:job_origins:details")) - type = tables.Column("type", - verbose_name=_("Type")) description = tables.Column("description", verbose_name=_("Description")) diff --git a/savannadashboard/job_origins/workflows/create.py b/savannadashboard/job_origins/workflows/create.py index b8c61d80..21db51e0 100644 --- a/savannadashboard/job_origins/workflows/create.py +++ b/savannadashboard/job_origins/workflows/create.py @@ -17,6 +17,8 @@ import logging +from django.forms.util import flatatt +from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _ from horizon import forms @@ -24,40 +26,85 @@ from horizon import workflows from savannadashboard.api import client as savannaclient + LOG = logging.getLogger(__name__) +class AddExtraSelectWidget(forms.Select): + def render(self, name, value, attrs=None, choices=()): + if value is None: + value = '' + final_attrs = self.build_attrs(attrs, name=name) + output = [u'' % flatatt(final_attrs)] + options = self.render_options(choices, [value]) + if options: + output.append(options) + output.append('') + output.append("") + return mark_safe("".join(output)) + + class GeneralConfigAction(workflows.Action): + NUM_LOCATION_FIELDS = 10 + FIELDS_BEFORE_MAIN = 2 + local_binary_choices = [] + extra_binary_count = forms.CharField(widget=forms.HiddenInput()) + job_origin_name = forms.CharField(label=_("Name"), required=True) - job_origin_credential_user = forms.CharField(label=_("Origin username"), - required=True) - - job_origin_credential_pass = forms.CharField( - widget=forms.PasswordInput(attrs={'autocomplete': 'off'}), - label=_("Origin password"), - required=True) - - job_storage_type = forms.ChoiceField( - label=_("Job Storage Type"), - required=True, - choices=[("internal", "Savanna DB"), - ("swift", "Swift"), - ("hdfs", "HDFS")], - widget=forms.Select( - attrs={"class": "job_storage_type_choice"})) - - job_origin_location = forms.CharField(label=_("Job Storage Location"), - required=True) - job_origin_description = forms.CharField(label=_("Description"), required=False, widget=forms.Textarea) + def populate_job_origin_local_db_choices(self, request): + savanna = savannaclient.Client(request) + job_binaries = savanna.job_binaries.list() + + choices = [(job_binary.id, job_binary.name) + for job_binary in job_binaries] + choices.insert(0, ('', 'NONE')) + + return choices + def __init__(self, request, *args, **kwargs): + extra_fields = kwargs.pop('extra', 0) + super(GeneralConfigAction, self).__init__(request, *args, **kwargs) + self.fields['extra_binary_count'].initial = extra_fields + self.local_binary_choices =\ + self.populate_job_origin_local_db_choices(request) + + for i in range(self.NUM_LOCATION_FIELDS, 0, -1): + self.fields.insert( + self.FIELDS_BEFORE_MAIN, 'job_origin_main_%s' % i, + forms.ChoiceField( + label=_("Main Binary"), + required=False, + choices=self.local_binary_choices, + initial=(None, "None"), + widget=AddExtraSelectWidget( + attrs={"class": "job_origin_main"}))) + + for i in range(self.NUM_LOCATION_FIELDS, 0, -1): + self.fields.insert( + self.FIELDS_BEFORE_MAIN + self.NUM_LOCATION_FIELDS, + 'job_origin_lib_%s' % i, + forms.ChoiceField( + label=_("Library"), + required=False, + choices=self.local_binary_choices, + initial=(None, "None"), + widget=AddExtraSelectWidget( + attrs={"class": "job_origin_lib"}))) + + self.fields["extra_locations"] = forms.CharField( + widget=forms.HiddenInput(), + initial=self.NUM_LOCATION_FIELDS) + class Meta: name = _("Create Job Origin") help_text_template = \ @@ -66,7 +113,6 @@ class GeneralConfigAction(workflows.Action): class GeneralConfig(workflows.Step): action_class = GeneralConfigAction - contributes = ("hidden_configure_field", ) def contribute(self, data, context): for k, v in data.items(): @@ -86,11 +132,23 @@ class CreateJobOrigin(workflows.Workflow): def handle(self, request, context): savanna = savannaclient.Client(request) + main_locations = [] + lib_locations = [] + + extra_count = 2 + for i in range(1, extra_count + 1): + if(context["general_job_origin_main_%s" % i] != ""): + main_locations.append( + context["general_job_origin_main_%s" % i]) + + for i in range(1, extra_count + 1): + if(context["general_job_origin_lib_%s" % i] != ""): + lib_locations.append( + context["general_job_origin_lib_%s" % i]) + savanna.job_origins.create( context["general_job_origin_name"], - context["general_job_storage_type"], - context["general_job_origin_credential_user"], - context["general_job_origin_credential_pass"], - context["general_job_origin_location"], + main_locations, + lib_locations, context["general_job_origin_description"]) return True diff --git a/savannadashboard/jobs/tables.py b/savannadashboard/jobs/tables.py index f9a284b9..2d934b98 100644 --- a/savannadashboard/jobs/tables.py +++ b/savannadashboard/jobs/tables.py @@ -17,6 +17,8 @@ import logging +from django.core import urlresolvers +from django.utils import http from django.utils.translation import ugettext_lazy as _ from horizon import tables @@ -46,6 +48,23 @@ class DeleteJob(tables.BatchAction): savanna.jobs.delete(obj_id) +class LaunchJob(tables.LinkAction): + name = "launch-job" + verbose_name = _("Launch Job") + 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.id}) + return "?".join([base_url, params]) + + class JobsTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name"), @@ -60,4 +79,4 @@ class JobsTable(tables.DataTable): verbose_name = _("Jobs") table_actions = (CreateJob, DeleteJob) - row_actions = (DeleteJob,) + row_actions = (LaunchJob, DeleteJob,) diff --git a/savannadashboard/jobs/urls.py b/savannadashboard/jobs/urls.py index 7306a6cf..4289404d 100644 --- a/savannadashboard/jobs/urls.py +++ b/savannadashboard/jobs/urls.py @@ -29,6 +29,9 @@ urlpatterns = patterns('', url(r'^create-job$', views.CreateJobView.as_view(), name='create-job'), + url(r'^launch-job$', + views.LaunchJobView.as_view(), + name='launch-job'), url(r'^(?P[^/]+)$', views.JobDetailsView.as_view(), name='details')) diff --git a/savannadashboard/jobs/views.py b/savannadashboard/jobs/views.py index 57525597..c62d666f 100644 --- a/savannadashboard/jobs/views.py +++ b/savannadashboard/jobs/views.py @@ -26,6 +26,7 @@ from savannadashboard.api import client as savannaclient from savannadashboard.jobs.tables import JobsTable import savannadashboard.jobs.tabs as _tabs import savannadashboard.jobs.workflows.create as create_flow +import savannadashboard.jobs.workflows.launch as launch_flow LOG = logging.getLogger(__name__) @@ -59,3 +60,16 @@ class JobDetailsView(tabs.TabView): def get_data(self): pass + + +class LaunchJobView(workflows.WorkflowView): + workflow_class = launch_flow.LaunchJob + success_url = \ + "horizon:savanna:jobs" + classes = ("ajax-modal") + template_name = "jobs/launch.html" + + def get_context_data(self, **kwargs): + context = super(LaunchJobView, self)\ + .get_context_data(**kwargs) + return context diff --git a/savannadashboard/jobs/workflows/launch.py b/savannadashboard/jobs/workflows/launch.py new file mode 100644 index 00000000..b93317dd --- /dev/null +++ b/savannadashboard/jobs/workflows/launch.py @@ -0,0 +1,154 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Red Hat Inc. +# +# 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 as _ + +from horizon import forms +from horizon import workflows + +from savannadashboard.api import client as savannaclient + +LOG = logging.getLogger(__name__) + + +class GeneralConfigAction(workflows.Action): + cluster = forms.ChoiceField( + label=_("Cluster"), + required=True, + initial=(None, "None"), + widget=forms.Select(attrs={"class": "cluster_choice"})) + + job_input = forms.ChoiceField( + label=_("Input"), + required=True, + initial=(None, "None"), + widget=forms.Select(attrs={"class": "job_input_choice"})) + + job_output = forms.ChoiceField( + label=_("Output"), + required=True, + initial=(None, "None"), + widget=forms.Select(attrs={"class": "job_output_choice"})) + + def __init__(self, request, *args, **kwargs): + super(GeneralConfigAction, self).__init__(request, *args, **kwargs) + + if request.REQUEST.get("job_id", None) is None: + self.fields["job"] = forms.ChoiceField( + label=_("Job"), + required=True) + self.fields["job"].choices = self.populate_job_choices(request) + else: + self.fields["job"] = forms.CharField( + widget=forms.HiddenInput(), + initial=request.REQUEST.get("job_id", None)) + + def populate_cluster_choices(self, request, context): + savanna = savannaclient.Client(request) + clusters = savanna.clusters.list() + + choices = [(cluster.id, cluster.name) + for cluster in clusters] + + return choices + + def populate_job_input_choices(self, request, context): + return self.get_data_source_choices(request, context) + + def populate_job_output_choices(self, request, context): + return self.get_data_source_choices(request, context) + + def get_data_source_choices(self, request, context): + savanna = savannaclient.Client(request) + data_sources = savanna.data_sources.list() + + choices = [(data_source.id, data_source.name) + for data_source in data_sources] + + return choices + + def populate_job_choices(self, request): + savanna = savannaclient.Client(request) + jobs = savanna.jobs.list() + + choices = [(job.id, job.name) + for job in jobs] + + return choices + + class Meta: + name = _("Job") + help_text_template = \ + ("jobs/_launch_job_help.html") + + +class JobConfigAction(workflows.Action): + config = forms.CharField( + label=_("Job Config"), + widget=forms.Textarea()) + + def __init__(self, request, *args, **kwargs): + super(JobConfigAction, self).__init__(request, *args, **kwargs) + # TODO(croberts) pre-populate config with job-type + # appropriate config settings + + class Meta: + name = _("Configure") + help_text_template = \ + ("jobs/_launch_job_configure_help.html") + + +class GeneralConfig(workflows.Step): + action_class = GeneralConfigAction + + def contribute(self, data, context): + for k, v in data.items(): + context["general_" + k] = v + + return context + + +class JobConfig(workflows.Step): + action_class = JobConfigAction + + def contribute(self, data, context): + for k, v in data.items(): + context["job_" + k] = v + + return context + + +class LaunchJob(workflows.Workflow): + slug = "launch_job" + name = _("Launch Job") + finalize_button_name = _("Launch") + success_message = _("Job launched") + failure_message = _("Could not launch job") + success_url = "horizon:savanna:jobs:index" + default_steps = (GeneralConfig, JobConfig) + + def handle(self, request, context): + savanna = savannaclient.Client(request) + savanna.jobs.launch( + context["general_job"], + context["general_cluster"], + context["general_job_input"], + context["general_job_output"], + context["job_config"]) + return True diff --git a/savannadashboard/templates/data_sources/data_sources.html b/savannadashboard/templates/data_sources/data_sources.html index ad7b8c23..43e08bbf 100644 --- a/savannadashboard/templates/data_sources/data_sources.html +++ b/savannadashboard/templates/data_sources/data_sources.html @@ -8,7 +8,7 @@ {% block main %} -
+
{{ data_sources_table.render }}
diff --git a/savannadashboard/templates/job_binaries/_create.html b/savannadashboard/templates/job_binaries/_create.html new file mode 100644 index 00000000..bd998b61 --- /dev/null +++ b/savannadashboard/templates/job_binaries/_create.html @@ -0,0 +1,21 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_id %}create-job-binary{% endblock %} +{% block form_action %}{% url horizon:savanna: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" %} +
+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} \ No newline at end of file diff --git a/savannadashboard/templates/job_binaries/_create_job_binary_help.html b/savannadashboard/templates/job_binaries/_create_job_binary_help.html new file mode 100644 index 00000000..e69de29b diff --git a/savannadashboard/templates/job_binaries/_details.html b/savannadashboard/templates/job_binaries/_details.html new file mode 100644 index 00000000..51266d27 --- /dev/null +++ b/savannadashboard/templates/job_binaries/_details.html @@ -0,0 +1,18 @@ +{% load i18n sizeformat %} +

{% 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 "Tenant id" %}
+
{{ job_binary.tenant_id }}
+
{% trans "Create time" %}
+
{{ job_binary.created_at }}
+
+
diff --git a/savannadashboard/templates/job_binaries/create.html b/savannadashboard/templates/job_binaries/create.html new file mode 100644 index 00000000..604c268b --- /dev/null +++ b/savannadashboard/templates/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=_("Savanna - Create Job Binary") %} +{% endblock page_header %} + +{% block main %} + {% include 'job_binaries/_create.html' %} +{% endblock %} \ No newline at end of file diff --git a/savannadashboard/templates/job_binaries/details.html b/savannadashboard/templates/job_binaries/details.html new file mode 100644 index 00000000..d61ebf3f --- /dev/null +++ b/savannadashboard/templates/job_binaries/details.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n sizeformat %} +{% block title %}{% trans "Job Binary Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Savanna - Job Binary Details") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} \ No newline at end of file diff --git a/savannadashboard/templates/job_binaries/job_binaries.html b/savannadashboard/templates/job_binaries/job_binaries.html new file mode 100644 index 00000000..e187961b --- /dev/null +++ b/savannadashboard/templates/job_binaries/job_binaries.html @@ -0,0 +1,84 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Savanna" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Savanna - Job Binaries") %} +{% endblock page_header %} + +{% block main %} + + +
+ {{ job_binaries_table.render }} +
+ + + +{% endblock %} diff --git a/savannadashboard/templates/job_executions/_details.html b/savannadashboard/templates/job_executions/_details.html new file mode 100644 index 00000000..36d5399b --- /dev/null +++ b/savannadashboard/templates/job_executions/_details.html @@ -0,0 +1,28 @@ +{% load i18n sizeformat %} +

{% trans "Job Execution Overview" %}

+
+
+
{% trans "Status" %}
+
{{ job_execution.info.status }}
+
{% trans "Id" %}
+
{{ job_execution.id }}
+
{% trans "Job Id" %}
+
{{ job_execution.job_id }}
+
{% trans "Input Id" %}
+
{{ job_execution.input_id }}
+
{% trans "Output Id" %}
+
{{ job_execution.output_id }}
+
{% trans "Cluster Id" %}
+
{{ job_execution.cluster_id }}
+
{% trans "Last Updated" %}
+
{{ job_execution.updated_at }}
+
{% trans "Return Code" %}
+
{{ job_execution.return_code }}
+
{% trans "Oozie Job Id" %}
+
{{ job_execution.oozie_job_id }}
+
{% trans "Created" %}
+
{{ job_execution.created_at }}
+
{% trans "Tenant Id" %}
+
{{ job_execution.tenant_id }}
+
+
diff --git a/savannadashboard/templates/job_executions/details.html b/savannadashboard/templates/job_executions/details.html new file mode 100644 index 00000000..854c6d93 --- /dev/null +++ b/savannadashboard/templates/job_executions/details.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n sizeformat %} +{% block title %}{% trans "Job Execution Details" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Savanna - Job Execution Details") %} +{% endblock page_header %} + +{% block main %} +
+
+ {{ tab_group.render }} +
+
+{% endblock %} \ No newline at end of file diff --git a/savannadashboard/templates/job_executions/job_executions.html b/savannadashboard/templates/job_executions/job_executions.html new file mode 100644 index 00000000..6ea08ae6 --- /dev/null +++ b/savannadashboard/templates/job_executions/job_executions.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Savanna" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Savanna - Job Executions") %} +{% endblock page_header %} + +{% block main %} + +
+ {{ job_executions_table.render }} +
+ +{% endblock %} diff --git a/savannadashboard/templates/job_origins/_details.html b/savannadashboard/templates/job_origins/_details.html index 1a97d3b0..61d6ff25 100644 --- a/savannadashboard/templates/job_origins/_details.html +++ b/savannadashboard/templates/job_origins/_details.html @@ -6,14 +6,6 @@
{{ job_origin.name }}
{% trans "ID" %}
{{ job_origin.id }}
-
{% trans "URL" %}
-
{{ job_origin.url }}
-
{% trans "Storage type" %}
-
{{ job_origin.storage_type }}
-
{% trans "Username" %}
-
{{ job_origin.credentials.user|default:"None" }}
-
{% trans "Password" %}
-
{{ job_origin.credentials.password|default:"None" }}
{% trans "Description" %}
{{ job_origin.description|default:"None" }}
{% trans "Tenant id" %}
diff --git a/savannadashboard/templates/job_origins/job_origins.html b/savannadashboard/templates/job_origins/job_origins.html index cea18a22..5d389f0a 100644 --- a/savannadashboard/templates/job_origins/job_origins.html +++ b/savannadashboard/templates/job_origins/job_origins.html @@ -7,9 +7,39 @@ {% endblock page_header %} {% block main %} - -
+ +
{{ job_origins_table.render }}
+ {% endblock %} diff --git a/savannadashboard/templates/jobs/_launch_job_configure_help.html b/savannadashboard/templates/jobs/_launch_job_configure_help.html new file mode 100644 index 00000000..e69de29b diff --git a/savannadashboard/templates/jobs/_launch_job_help.html b/savannadashboard/templates/jobs/_launch_job_help.html new file mode 100644 index 00000000..e69de29b diff --git a/savannadashboard/templates/jobs/jobs.html b/savannadashboard/templates/jobs/jobs.html index 00f18704..f6f1f439 100644 --- a/savannadashboard/templates/jobs/jobs.html +++ b/savannadashboard/templates/jobs/jobs.html @@ -8,7 +8,7 @@ {% block main %} -
+
{{ jobs_table.render }}
diff --git a/savannadashboard/templates/jobs/launch.html b/savannadashboard/templates/jobs/launch.html new file mode 100644 index 00000000..fe43bc14 --- /dev/null +++ b/savannadashboard/templates/jobs/launch.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Launch Job" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Savanna - Launch Job") %} +{% endblock page_header %} + +{% block main %} + {% include 'horizon/common/_workflow.html' %} +{% endblock %} \ No newline at end of file