Reorganizing cluster panels into tabs

First patch in reorganizing the sahara UI to be more
tab oriented rather than death by a thousand panels.
There are other patches that will complete the work,
 but this patch addresses the cluster-oriented panels
 by moving them under a single panel named Clusters.

Change-Id: Iae4bf4b5127fb29153ec5335070cde7d344e059e
Partial-Implements: bp reduce-number-of-panels
This commit is contained in:
Chad Roberts 2016-01-12 15:40:22 -05:00
parent ecdd89583a
commit 5aa8b83bc9
95 changed files with 851 additions and 849 deletions

View File

@ -1,28 +0,0 @@
# 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 ClusterTemplatesPanel(horizon.Panel):
name = _("Cluster Templates")
slug = 'data_processing.cluster_templates'
permissions = (('openstack.services.data-processing',
'openstack.services.data_processing'),)
dashboard.Project.register(ClusterTemplatesPanel)

View File

@ -1,43 +0,0 @@
# 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
from django.conf.urls import url
import sahara_dashboard.content. \
data_processing.cluster_templates.views as views
urlpatterns = patterns('',
url(r'^$', views.ClusterTemplatesView.as_view(),
name='index'),
url(r'^$', views.ClusterTemplatesView.as_view(),
name='cluster-templates'),
url(r'^upload_file$',
views.UploadFileView.as_view(),
name='upload_file'),
url(r'^create-cluster-template$',
views.CreateClusterTemplateView.as_view(),
name='create-cluster-template'),
url(r'^configure-cluster-template$',
views.ConfigureClusterTemplateView.as_view(),
name='configure-cluster-template'),
url(r'^(?P<template_id>[^/]+)$',
views.ClusterTemplateDetailsView.as_view(),
name='details'),
url(r'^(?P<template_id>[^/]+)/copy$',
views.CopyClusterTemplateView.as_view(),
name='copy'),
url(r'^(?P<template_id>[^/]+)/edit$',
views.EditClusterTemplateView.as_view(),
name='edit'))

View File

@ -35,7 +35,7 @@ class ClusterTemplatesFilterAction(tables.FilterAction):
class UploadFile(tables.LinkAction):
name = 'upload_file'
verbose_name = _("Upload Template")
url = 'horizon:project:data_processing.cluster_templates:upload_file'
url = 'horizon:project:data_processing.clusters:upload_file'
classes = ("btn-launch", "ajax-modal")
icon = "upload"
@ -59,14 +59,14 @@ class CreateCluster(tables.LinkAction):
class CopyTemplate(tables.LinkAction):
name = "copy"
verbose_name = _("Copy Template")
url = "horizon:project:data_processing.cluster_templates:copy"
url = "horizon:project:data_processing.clusters:ct-copy"
classes = ("ajax-modal", )
class EditTemplate(tables.LinkAction):
name = "edit"
verbose_name = _("Edit Template")
url = "horizon:project:data_processing.cluster_templates:edit"
url = "horizon:project:data_processing.clusters:ct-edit"
classes = ("ajax-modal", )
@ -94,7 +94,7 @@ class DeleteTemplate(tables.DeleteAction):
class CreateClusterTemplate(tables.LinkAction):
name = "create"
verbose_name = _("Create Template")
url = ("horizon:project:data_processing.cluster_templates:"
url = ("horizon:project:data_processing.clusters:"
"create-cluster-template")
classes = ("ajax-modal", "create-clustertemplate-btn")
icon = "plus"
@ -103,7 +103,7 @@ class CreateClusterTemplate(tables.LinkAction):
class ConfigureClusterTemplate(tables.LinkAction):
name = "configure"
verbose_name = _("Configure Cluster Template")
url = ("horizon:project:data_processing.cluster_templates:"
url = ("horizon:project:data_processing.clusters:"
"configure-cluster-template")
classes = ("ajax-modal", "configure-clustertemplate-btn")
icon = "plus"
@ -152,7 +152,7 @@ class ClusterTemplatesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:project:data_processing."
"cluster_templates:details"))
"clusters:ct-details"))
plugin_name = tables.Column("plugin_name",
verbose_name=_("Plugin"))
hadoop_version = tables.Column("hadoop_version",

View File

@ -22,16 +22,41 @@ from openstack_dashboard.api import nova
from sahara_dashboard.api import sahara as saharaclient
from sahara_dashboard.content. \
data_processing.utils import workflow_helpers as helpers
from sahara_dashboard.content.data_processing.clusters.cluster_templates \
import tables as cluster_template_tables
from sahara_dashboard.content.data_processing \
import tabs as sahara_tabs
LOG = logging.getLogger(__name__)
class ClusterTemplatesTab(sahara_tabs.SaharaTableTab):
table_classes = (cluster_template_tables.ClusterTemplatesTable, )
name = _("Cluster Templates")
slug = "clusters_templates_tab"
template_name = "horizon/common/_detail_table.html"
def get_cluster_templates_data(self):
try:
table = self._tables['cluster_templates']
search_opts = {}
filter = self.get_server_filter_info(table.request, table)
if filter['value'] and filter['field']:
search_opts = {filter['field']: filter['value']}
cluster_templates = saharaclient.cluster_template_list(
self.request, search_opts)
except Exception:
cluster_templates = []
exceptions.handle(self.request,
_("Unable to fetch cluster template list"))
return cluster_templates
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "cluster_template_details_tab"
template_name = (
"project/data_processing.cluster_templates/_details.html")
template_name = "cluster_templates/_details.html"
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']
@ -47,8 +72,7 @@ class ClusterTemplateConfigsDetails(tabs.Tab):
name = _("Configuration Details")
slug = "cluster_template_configs_details_tab"
template_name = (
"project/data_processing.cluster_templates/"
"_cluster_template_configs_details.html")
"cluster_templates/_cluster_template_configs_details.html")
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']
@ -63,8 +87,7 @@ class ClusterTemplateConfigsDetails(tabs.Tab):
class NodeGroupsTab(tabs.Tab):
name = _("Node Groups")
slug = "cluster_template_nodegroups_tab"
template_name = (
"project/data_processing.cluster_templates/_nodegroups_details.html")
template_name = "cluster_templates/_nodegroups_details.html"
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']

View File

@ -26,9 +26,10 @@ from sahara_dashboard import api
from sahara_dashboard.test import helpers as test
INDEX_URL = reverse('horizon:project:data_processing.cluster_templates:index')
INDEX_URL = reverse('horizon:project:data_processing.clusters:'
'cluster-templates-tab')
DETAILS_URL = reverse(
'horizon:project:data_processing.cluster_templates:details', args=['id'])
'horizon:project:data_processing.clusters:ct-details', args=['id'])
class DataProcessingClusterTemplateTests(test.TestCase):
@ -38,9 +39,7 @@ class DataProcessingClusterTemplateTests(test.TestCase):
.AndReturn(self.cluster_templates.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res,
'project/data_processing.cluster_templates/'
'cluster_templates.html')
self.assertTemplateUsed(res, 'clusters/index.html')
self.assertContains(res, 'Cluster Templates')
self.assertContains(res, 'Name')
@ -78,7 +77,7 @@ class DataProcessingClusterTemplateTests(test.TestCase):
.MultipleTimes().AndReturn(ngts)
self.mox.ReplayAll()
url = reverse('horizon:project:data_processing.cluster_templates:copy',
url = reverse('horizon:project:data_processing.clusters:ct-copy',
args=[ct.id])
res = self.client.get(url)
workflow = res.context['workflow']
@ -140,7 +139,7 @@ class DataProcessingClusterTemplateTests(test.TestCase):
.AndReturn(new_ct)
self.mox.ReplayAll()
url = reverse('horizon:project:data_processing.cluster_templates:edit',
url = reverse('horizon:project:data_processing.clusters:ct-edit',
args=[ct.id])
def serialize(obj):

View File

@ -23,40 +23,25 @@ from horizon.utils import memoized
from horizon import workflows
from sahara_dashboard.api import sahara as saharaclient
from sahara_dashboard.content.data_processing. \
from sahara_dashboard.content.data_processing.clusters. \
cluster_templates import forms as cluster_forms
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.tables as ct_tables
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.tabs as _tabs
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.copy as copy_flow
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.create as create_flow
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.edit as edit_flow
class ClusterTemplatesView(tables.DataTableView):
table_class = ct_tables.ClusterTemplatesTable
template_name = (
'project/data_processing.cluster_templates/cluster_templates.html')
template_name = 'cluster_templates/cluster_templates.html'
page_title = _("Cluster Templates")
def get_data(self):
try:
search_opts = {}
filter = self.get_server_filter_info(self.request)
if filter['value'] and filter['field']:
search_opts = {filter['field']: filter['value']}
cluster_templates = saharaclient.cluster_template_list(
self.request, search_opts)
except Exception:
cluster_templates = []
exceptions.handle(self.request,
_("Unable to fetch cluster template list"))
return cluster_templates
class ClusterTemplateDetailsView(tabs.TabView):
tab_group_class = _tabs.ClusterTemplateDetailsTabs
@ -72,7 +57,7 @@ class ClusterTemplateDetailsView(tabs.TabView):
msg = _('Unable to retrieve details for '
'cluster template "%s".') % ct_id
redirect = reverse("horizon:project:data_processing."
"cluster_templates:cluster-templates")
"clusters:cluster-templates-tab")
exceptions.handle(self.request, msg, redirect=redirect)
def get_context_data(self, **kwargs):
@ -90,33 +75,34 @@ class ClusterTemplateDetailsView(tabs.TabView):
class UploadFileView(forms.ModalFormView):
form_class = cluster_forms.UploadFileForm
template_name = (
'project/data_processing.cluster_templates/upload_file.html')
template_name = 'cluster_templates/upload_file.html'
success_url = reverse_lazy(
'horizon:project:data_processing.cluster_templates:index')
'horizon:project:data_processing.clusters:cluster-templates-tab')
page_title = _("Upload Template")
class CreateClusterTemplateView(workflows.WorkflowView):
workflow_class = create_flow.CreateClusterTemplate
success_url = ("horizon:project:data_processing.cluster_templates"
success_url = ("horizon:project:data_processing.clusters"
":create-cluster-template")
classes = ("ajax-modal",)
template_name = "project/data_processing.cluster_templates/create.html"
template_name = "cluster_templates/create.html"
page_title = _("Create Cluster Template")
class ConfigureClusterTemplateView(workflows.WorkflowView):
workflow_class = create_flow.ConfigureClusterTemplate
success_url = "horizon:project:data_processing.cluster_templates"
template_name = "project/data_processing.cluster_templates/configure.html"
success_url = ("horizon:project:data_processing.clusters"
":cluster-templates-tab")
template_name = "cluster_templates/configure.html"
page_title = _("Configure Cluster Template")
class CopyClusterTemplateView(workflows.WorkflowView):
workflow_class = copy_flow.CopyClusterTemplate
success_url = "horizon:project:data_processing.cluster_templates"
template_name = "project/data_processing.cluster_templates/configure.html"
success_url = ("horizon:project:data_processing.clusters"
":cluster-templates-tab")
template_name = "cluster_templates/configure.html"
page_title = _("Copy Cluster Template")
def get_context_data(self, **kwargs):
@ -147,5 +133,6 @@ class CopyClusterTemplateView(workflows.WorkflowView):
class EditClusterTemplateView(CopyClusterTemplateView):
workflow_class = edit_flow.EditClusterTemplate
success_url = "horizon:project:data_processing.cluster_templates"
template_name = "project/data_processing.cluster_templates/configure.html"
success_url = ("horizon:project:data_processing.clusters:"
"cluster-templates-tab")
template_name = "cluster_templates/configure.html"

View File

@ -19,7 +19,7 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.create as create_flow
import sahara_dashboard.content.data_processing.utils. \
workflow_helpers as wf_helpers

View File

@ -14,6 +14,7 @@
import base64
import json
from django.core import urlresolvers
from django.utils.translation import ugettext_lazy as _
from saharaclient.api import base as api_base
@ -67,7 +68,7 @@ class SelectPluginAction(workflows.Action):
class Meta(object):
name = _("Select plugin and hadoop version for cluster template")
help_text_template = ("project/data_processing.cluster_templates/"
help_text_template = ("cluster_templates/"
"_create_general_help.html")
@ -81,8 +82,12 @@ class CreateClusterTemplate(workflows.Workflow):
finalize_button_name = _("Next")
success_message = _("Created")
failure_message = _("Could not create")
success_url = "horizon:project:data_processing.cluster_templates:index"
default_steps = (SelectPlugin,)
success_url = "horizon:project:data_processing.clusters:clusters-tab"
default_steps = (SelectPlugin, )
def get_success_url(self):
url = urlresolvers.reverse(self.success_url)
return url
class GeneralConfigAction(workflows.Action):
@ -149,8 +154,7 @@ class GeneralConfigAction(workflows.Action):
class Meta(object):
name = _("Details")
help_text_template = ("project/data_processing.cluster_templates/"
"_configure_general_help.html")
help_text_template = ("cluster_templates/_configure_general_help.html")
class GeneralConfig(workflows.Step):
@ -232,8 +236,7 @@ class ConfigureNodegroupsAction(workflows.Action):
class ConfigureNodegroups(workflows.Step):
action_class = ConfigureNodegroupsAction
contributes = ("hidden_nodegroups_field", )
template_name = ("project/data_processing.cluster_templates/"
"cluster_node_groups_template.html")
template_name = ("cluster_templates/cluster_node_groups_template.html")
def contribute(self, data, context):
for k, v in data.items():
@ -302,7 +305,8 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
finalize_button_name = _("Create")
success_message = _("Created Cluster Template %s")
name_property = "general_cluster_template_name"
success_url = "horizon:project:data_processing.cluster_templates:index"
success_url = ("horizon:project:data_processing.clusters:"
"cluster-templates-tab")
default_steps = (GeneralConfig,
ConfigureNodegroups)

View File

@ -20,9 +20,9 @@ from horizon import exceptions
from horizon import forms
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.create as create_flow
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.copy as copy_flow
import sahara_dashboard.content.data_processing. \
utils.workflow_helpers as whelpers

View File

@ -134,8 +134,7 @@ def rich_status_filter(status_dict):
return status_dict["status"]
# Error is rendered with a template containing an error description.
return render_to_string(
"project/data_processing.clusters/_rich_status.html", status_dict)
return render_to_string("clusters/_rich_status.html", status_dict)
class ConfigureCluster(tables.LinkAction):
@ -172,7 +171,7 @@ class ClustersTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:project:data_processing."
"clusters:details"))
"clusters:cluster-details"))
plugin = tables.Column("plugin_name",
verbose_name=_("Plugin"))

View File

@ -19,6 +19,10 @@ from horizon import exceptions
from horizon import tables
from horizon import tabs
from sahara_dashboard.content.data_processing.clusters.clusters \
import tables as cluster_tables
from sahara_dashboard.content.data_processing \
import tabs as sahara_tabs
from sahara_dashboard.content.data_processing.utils \
import workflow_helpers as helpers
@ -32,10 +36,31 @@ from sahara_dashboard.api import sahara as saharaclient
LOG = logging.getLogger(__name__)
class ClustersTab(sahara_tabs.SaharaTableTab):
table_classes = (cluster_tables.ClustersTable, )
name = _("Clusters")
slug = "clusters_tab"
template_name = "horizon/common/_detail_table.html"
def get_clusters_data(self):
try:
table = self._tables['clusters']
search_opts = {}
filter = self.get_server_filter_info(table.request, table)
if filter['value'] and filter['field']:
search_opts = {filter['field']: filter['value']}
clusters = saharaclient.cluster_list(self.request, search_opts)
except Exception:
clusters = []
exceptions.handle(self.request,
_("Unable to fetch cluster list"))
return clusters
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "cluster_details_tab"
template_name = "project/data_processing.clusters/_details.html"
template_name = "clusters/_details.html"
def get_context_data(self, request):
cluster_id = self.tab_group.kwargs['cluster_id']
@ -79,7 +104,7 @@ class ClusterConfigsDetails(tabs.Tab):
name = _("Configuration Details")
slug = "cluster_configs_details_tab"
template_name = (
"project/data_processing.clusters/_cluster_configs_details.html")
"clusters/_cluster_configs_details.html")
def get_context_data(self, request):
cluster_id = self.tab_group.kwargs['cluster_id']
@ -101,8 +126,7 @@ def build_link(url):
class NodeGroupsTab(tabs.Tab):
name = _("Node Groups")
slug = "cluster_nodegroups_tab"
template_name = (
"project/data_processing.clusters/_nodegroups_details.html")
template_name = "clusters/_nodegroups_details.html"
def get_context_data(self, request):
cluster_id = self.tab_group.kwargs['cluster_id']
@ -167,7 +191,7 @@ class InstancesTable(tables.DataTable):
class InstancesTab(tabs.TableTab):
name = _("Instances")
slug = "cluster_instances_tab"
template_name = "project/data_processing.clusters/_instances_details.html"
template_name = "clusters/_instances_details.html"
table_classes = (InstancesTable, )
def get_cluster_instances_data(self):
@ -197,7 +221,7 @@ class InstancesTab(tabs.TableTab):
class EventLogTab(tabs.Tab):
name = _("Cluster Events")
slug = "cluster_event_log"
template_name = "project/data_processing.clusters/_event_log.html"
template_name = "clusters/_event_log.html"
def get_context_data(self, request, **kwargs):
cluster_id = self.tab_group.kwargs['cluster_id']

View File

@ -21,7 +21,7 @@ from sahara_dashboard.test import helpers as test
from sahara_dashboard import api
INDEX_URL = reverse('horizon:project:data_processing.clusters:index')
INDEX_URL = reverse('horizon:project:data_processing.clusters:clusters-tab')
DETAILS_URL = reverse(
'horizon:project:data_processing.clusters:details', args=['id'])
@ -33,8 +33,7 @@ class DataProcessingClusterTests(test.TestCase):
.AndReturn(self.clusters.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(
res, 'project/data_processing.clusters/clusters.html')
self.assertTemplateUsed(res, 'clusters/index.html')
self.assertContains(res, 'Clusters')
self.assertContains(res, 'Name')

View File

@ -0,0 +1,257 @@
# 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 datetime import datetime
import json
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.utils.translation import ugettext as _
from django.views.generic import base as django_base
import six
from horizon import exceptions
from horizon import tables
from horizon import tabs
from horizon.utils import memoized
from horizon import workflows
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing.clusters.clusters. \
tables as c_tables
import sahara_dashboard.content.data_processing.clusters.clusters. \
tabs as _tabs
import sahara_dashboard.content.data_processing.clusters.clusters. \
workflows.create as create_flow
import sahara_dashboard.content.data_processing.clusters.clusters. \
workflows.scale as scale_flow
import sahara_dashboard.content.data_processing.clusters.clusters. \
workflows.update as update_flow
from saharaclient.api.base import APIException
class ClustersView(tables.DataTableView):
table_class = c_tables.ClustersTable
template_name = 'clusters/clusters.html'
page_title = _("Clusters")
def get_data(self):
try:
search_opts = {}
filter = self.get_server_filter_info(self.request)
if filter['value'] and filter['field']:
search_opts = {filter['field']: filter['value']}
clusters = saharaclient.cluster_list(self.request, search_opts)
except Exception:
clusters = []
exceptions.handle(self.request,
_("Unable to fetch cluster list"))
return clusters
class ClusterDetailsView(tabs.TabView):
tab_group_class = _tabs.ClusterDetailsTabs
template_name = 'horizon/common/_detail.html'
page_title = "{{ cluster.name|default:cluster.id }}"
@memoized.memoized_method
def get_object(self):
cl_id = self.kwargs["cluster_id"]
try:
return saharaclient.cluster_get(self.request, cl_id)
except Exception:
msg = _('Unable to retrieve details for cluster "%s".') % cl_id
redirect = reverse(
"horizon:project:data_processing.clusters:clusters-tab")
exceptions.handle(self.request, msg, redirect=redirect)
def get_context_data(self, **kwargs):
context = super(ClusterDetailsView, self).get_context_data(**kwargs)
context['cluster'] = self.get_object()
return context
class ClusterEventsView(django_base.View):
_date_format = "%Y-%m-%dT%H:%M:%S"
@staticmethod
def _created_at_key(obj):
return datetime.strptime(obj["created_at"],
ClusterEventsView._date_format)
def get(self, request, *args, **kwargs):
cluster_id = kwargs.get("cluster_id")
try:
cluster = saharaclient.cluster_get(request, cluster_id,
show_progress=True)
node_group_mapping = {}
for node_group in cluster.node_groups:
node_group_mapping[node_group["id"]] = node_group["name"]
provision_steps = cluster.provision_progress
# Sort by create time
provision_steps = sorted(provision_steps,
key=ClusterEventsView._created_at_key,
reverse=True)
for step in provision_steps:
# Sort events of the steps also
step["events"] = sorted(step["events"],
key=ClusterEventsView._created_at_key,
reverse=True)
successful_events_count = 0
for event in step["events"]:
if event["node_group_id"]:
event["node_group_name"] = node_group_mapping[
event["node_group_id"]]
event_result = _("Unknown")
if event["successful"] is True:
successful_events_count += 1
event_result = _("Completed Successfully")
elif event["successful"] is False:
event_result = _("Failed")
event["result"] = event_result
if not event["event_info"]:
event["event_info"] = _("No info available")
start_time = datetime.strptime(step["created_at"],
self._date_format)
end_time = datetime.now()
# Clear out microseconds. There is no need for that precision.
end_time = end_time.replace(microsecond=0)
if step["successful"] is not None:
updated_at = step["updated_at"]
end_time = datetime.strptime(updated_at,
self._date_format)
step["duration"] = six.text_type(end_time - start_time)
result = _("In progress")
step["completed"] = successful_events_count
if step["successful"] is True:
step["completed"] = step["total"]
result = _("Completed Successfully")
elif step["successful"] is False:
result = _("Failed")
step["result"] = result
status = cluster.status.lower()
need_update = status not in ("active", "error")
except APIException:
# Cluster is not available. Returning empty event log.
need_update = False
provision_steps = []
context = {"provision_steps": provision_steps,
"need_update": need_update}
return HttpResponse(json.dumps(context),
content_type='application/json')
class CreateClusterView(workflows.WorkflowView):
workflow_class = create_flow.CreateCluster
success_url = \
"horizon:project:data_processing.clusters:create-cluster"
classes = ("ajax-modal",)
template_name = "clusters/create.html"
page_title = _("Launch Cluster")
class ConfigureClusterView(workflows.WorkflowView):
workflow_class = create_flow.ConfigureCluster
success_url = "horizon:project:data_processing.clusters-tab"
template_name = "clusters/configure.html"
page_title = _("Configure Cluster")
def get_initial(self):
initial = super(ConfigureClusterView, self).get_initial()
initial.update(self.kwargs)
return initial
class ScaleClusterView(workflows.WorkflowView):
workflow_class = scale_flow.ScaleCluster
success_url = "horizon:project:data_processing.clusters-tab"
classes = ("ajax-modal",)
template_name = "clusters/scale.html"
page_title = _("Scale Cluster")
def get_context_data(self, **kwargs):
context = super(ScaleClusterView, self)\
.get_context_data(**kwargs)
context["cluster_id"] = kwargs["cluster_id"]
return context
def get_object(self, *args, **kwargs):
if not hasattr(self, "_object"):
template_id = self.kwargs['cluster_id']
try:
template = saharaclient.cluster_template_get(self.request,
template_id)
except Exception:
template = None
exceptions.handle(self.request,
_("Unable to fetch cluster template."))
self._object = template
return self._object
def get_initial(self):
initial = super(ScaleClusterView, self).get_initial()
initial.update({'cluster_id': self.kwargs['cluster_id']})
return initial
class UpdateClusterSharesView(workflows.WorkflowView):
workflow_class = update_flow.UpdateShares
success_url = "horizon:project:data_processing.clusters"
classes = ("ajax-modal",)
template_name = "clusters/update.html"
page_title = _("Update Cluster Shares")
def get_context_data(self, **kwargs):
context = super(UpdateClusterSharesView, self)\
.get_context_data(**kwargs)
context["cluster_id"] = kwargs["cluster_id"]
return context
def get_object(self, *args, **kwargs):
if not hasattr(self, "_object"):
cluster_id = self.kwargs['cluster_id']
try:
cluster = saharaclient.cluster_get(self.request, cluster_id)
except Exception:
cluster = None
exceptions.handle(self.request,
_("Unable to fetch cluster."))
self._object = cluster
return self._object
def get_initial(self):
initial = super(UpdateClusterSharesView, self).get_initial()
initial.update({
'cluster_id': self.kwargs['cluster_id'],
'cluster': self.get_object()})
return initial

View File

@ -26,23 +26,22 @@ import sahara_dashboard.content.data_processing.utils. \
from django.utils.translation import ugettext_lazy as _
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.create as t_flows
from saharaclient.api import base as api_base
KEYPAIR_IMPORT_URL = "horizon:project:access_and_security:keypairs:import"
BASE_IMAGE_URL = "horizon:project:data_processing.data_image_registry:register"
BASE_IMAGE_URL = "horizon:project:data_processing.clusters:register"
TEMPLATE_UPLOAD_URL = (
"horizon:project:data_processing.cluster_templates:upload_file")
"horizon:project:data_processing.clusters:upload_file")
class SelectPluginAction(t_flows.SelectPluginAction):
class Meta(object):
name = _("Select plugin and hadoop version for cluster")
help_text_template = (
"project/data_processing.clusters/_create_general_help.html")
help_text_template = "clusters/_create_general_help.html"
class SelectPlugin(t_flows.SelectPlugin):
@ -52,7 +51,7 @@ class SelectPlugin(t_flows.SelectPlugin):
class CreateCluster(t_flows.CreateClusterTemplate):
slug = "create_cluster"
name = _("Launch Cluster")
success_url = "horizon:project:data_processing.cluster_templates:index"
success_url = "horizon:project:data_processing.clusters:clusters-tab"
default_steps = (SelectPlugin,)
@ -206,8 +205,7 @@ class GeneralConfigAction(workflows.Action):
class Meta(object):
name = _("Configure Cluster")
help_text_template = \
("project/data_processing.clusters/_configure_general_help.html")
help_text_template = "clusters/_configure_general_help.html"
class GeneralConfig(workflows.Step):
@ -227,7 +225,7 @@ class ConfigureCluster(whelpers.StatusFormatMixin, workflows.Workflow):
finalize_button_name = _("Launch")
success_message = _("Launched Cluster %s")
name_property = "general_cluster_name"
success_url = "horizon:project:data_processing.clusters:index"
success_url = "horizon:project:data_processing.clusters:clusters-tab"
default_steps = (GeneralConfig, )
def handle(self, request, context):

View File

@ -19,9 +19,9 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.create as clt_create_flow
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
clusters.workflows.create as cl_create_flow
from sahara_dashboard.content.data_processing.utils import workflow_helpers

View File

@ -23,13 +23,12 @@ from sahara_dashboard.api import sahara as saharaclient
class EditTagsAction(tables.LinkAction):
name = "edit_tags"
verbose_name = _("Edit Tags")
url = "horizon:project:data_processing.data_image_registry:edit_tags"
url = "horizon:project:data_processing.clusters:edit_tags"
classes = ("ajax-modal",)
def tags_to_string(image):
template_name = (
'project/data_processing.data_image_registry/_list_tags.html')
template_name = 'data_image_registry/_list_tags.html'
context = {"image": image}
return template.loader.render_to_string(template_name, context)
@ -37,7 +36,7 @@ def tags_to_string(image):
class RegisterImage(tables.LinkAction):
name = "register"
verbose_name = _("Register Image")
url = "horizon:project:data_processing.data_image_registry:register"
url = "horizon:project:data_processing.clusters:register"
classes = ("ajax-modal",)
icon = "plus"

View File

@ -0,0 +1,43 @@
# 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 sahara_dashboard.api import sahara as saharaclient
from sahara_dashboard.content.data_processing.clusters.data_image_registry \
import tables as data_image_registry_tables
LOG = logging.getLogger(__name__)
class DataImageRegistryTab(tabs.TableTab):
table_classes = (data_image_registry_tables.ImageRegistryTable, )
name = _("Data Image Registry")
slug = "data_image_registry_tab"
template_name = "horizon/common/_detail_table.html"
def get_image_registry_data(self):
try:
images = saharaclient.image_list(self.request)
except Exception:
images = []
msg = _('Unable to retrieve image list')
exceptions.handle(self.request, msg)
return images

View File

@ -21,9 +21,9 @@ from sahara_dashboard.test import helpers as test
from sahara_dashboard import api
INDEX_URL = reverse(
'horizon:project:data_processing.data_image_registry:index')
'horizon:project:data_processing.clusters:image-registry-tab')
REGISTER_URL = reverse(
'horizon:project:data_processing.data_image_registry:register')
'horizon:project:data_processing.clusters:register')
class DataProcessingImageRegistryTests(test.TestCase):
@ -35,9 +35,7 @@ class DataProcessingImageRegistryTests(test.TestCase):
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(
res,
'project/data_processing.data_image_registry/image_registry.html')
self.assertTemplateUsed(res, 'clusters/index.html')
self.assertContains(res, 'Image Registry')
self.assertContains(res, 'Image')
self.assertContains(res, 'Tags')
@ -117,7 +115,7 @@ class DataProcessingImageRegistryTests(test.TestCase):
self.mox.ReplayAll()
edit_tags_url = reverse(
'horizon:project:data_processing.data_image_registry:edit_tags',
'horizon:project:data_processing.clusters:edit_tags',
args=[image.id])
res = self.client.post(
edit_tags_url,

View File

@ -23,17 +23,16 @@ from horizon.utils import memoized
from sahara_dashboard.api import sahara as saharaclient
from sahara_dashboard.content. \
data_processing.data_image_registry.forms import EditTagsForm
data_processing.clusters.data_image_registry.forms import EditTagsForm
from sahara_dashboard.content. \
data_processing.data_image_registry.forms import RegisterImageForm
from sahara_dashboard.content. \
data_processing.data_image_registry.tables import ImageRegistryTable
data_processing.clusters.data_image_registry.forms import RegisterImageForm
from sahara_dashboard.content.data_processing. \
clusters.data_image_registry.tables import ImageRegistryTable
class ImageRegistryView(tables.DataTableView):
table_class = ImageRegistryTable
template_name = (
'project/data_processing.data_image_registry/image_registry.html')
template_name = 'data_image_registry/image_registry.html'
page_title = _("Image Registry")
def get_data(self):
@ -74,10 +73,9 @@ def update_context_with_plugin_tags(request, context):
class EditTagsView(forms.ModalFormView):
form_class = EditTagsForm
template_name = (
'project/data_processing.data_image_registry/edit_tags.html')
template_name = 'data_image_registry/edit_tags.html'
success_url = reverse_lazy(
'horizon:project:data_processing.data_image_registry:index')
'horizon:project:data_processing.clusters:image-registry-tab')
page_title = _("Edit Image Tags")
def get_context_data(self, **kwargs):
@ -108,10 +106,9 @@ class EditTagsView(forms.ModalFormView):
class RegisterImageView(forms.ModalFormView):
form_class = RegisterImageForm
template_name = (
'project/data_processing.data_image_registry/register_image.html')
template_name = 'data_image_registry/register_image.html'
success_url = reverse_lazy(
'horizon:project:data_processing.data_image_registry:index')
'horizon:project:data_processing.clusters:image-registry-tab')
page_title = _("Register Image")
def get_context_data(self, **kwargs):

View File

@ -32,7 +32,7 @@ class NodeGroupTemplatesFilterAction(tables.FilterAction):
class CreateNodegroupTemplate(tables.LinkAction):
name = "create"
verbose_name = _("Create Template")
url = ("horizon:project:data_processing.nodegroup_templates:"
url = ("horizon:project:data_processing.clusters:"
"create-nodegroup-template")
classes = ("ajax-modal", "create-nodegrouptemplate-btn")
icon = "plus"
@ -41,7 +41,7 @@ class CreateNodegroupTemplate(tables.LinkAction):
class ConfigureNodegroupTemplate(tables.LinkAction):
name = "configure"
verbose_name = _("Configure Template")
url = ("horizon:project:data_processing.nodegroup_templates:"
url = ("horizon:project:data_processing.clusters:"
"configure-nodegroup-template")
classes = ("ajax-modal", "configure-nodegrouptemplate-btn")
icon = "plus"
@ -51,14 +51,14 @@ class ConfigureNodegroupTemplate(tables.LinkAction):
class CopyTemplate(tables.LinkAction):
name = "copy"
verbose_name = _("Copy Template")
url = "horizon:project:data_processing.nodegroup_templates:copy"
url = "horizon:project:data_processing.clusters:copy"
classes = ("ajax-modal", )
class EditTemplate(tables.LinkAction):
name = "edit"
verbose_name = _("Edit Template")
url = "horizon:project:data_processing.nodegroup_templates:edit"
url = "horizon:project:data_processing.clusters:edit"
classes = ("ajax-modal", )
@ -121,7 +121,7 @@ class NodegroupTemplatesTable(tables.DataTable):
name = tables.Column(
"name",
verbose_name=_("Name"),
link="horizon:project:data_processing.nodegroup_templates:details")
link="horizon:project:data_processing.clusters:details")
plugin_name = tables.Column("plugin_name",
verbose_name=_("Plugin"))
hadoop_version = tables.Column("hadoop_version",

View File

@ -22,18 +22,42 @@ from openstack_dashboard.api import network
from openstack_dashboard.api import nova
from sahara_dashboard.api import sahara as saharaclient
from sahara_dashboard.content.data_processing \
import tabs as sahara_tabs
from sahara_dashboard.content. \
data_processing.utils import workflow_helpers as helpers
from sahara_dashboard.content.data_processing.clusters.nodegroup_templates \
import tables as node_group_template_tables
LOG = logging.getLogger(__name__)
class NodeGroupTemplatesTab(sahara_tabs.SaharaTableTab):
table_classes = (node_group_template_tables.NodegroupTemplatesTable, )
name = _("Node Group Templates")
slug = "node_group_templates_tab"
template_name = "horizon/common/_detail_table.html"
def get_nodegroup_templates_data(self):
try:
table = self._tables['nodegroup_templates']
search_opts = {}
filter = self.get_server_filter_info(table.request, table)
if filter['value'] and filter['field']:
search_opts = {filter['field']: filter['value']}
node_group_templates = saharaclient.nodegroup_template_list(
self.request, search_opts)
except Exception:
node_group_templates = []
exceptions.handle(self.request,
_("Unable to fetch node group template list"))
return node_group_templates
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "nodegroup_template_details_tab"
template_name = (
"project/data_processing.nodegroup_templates/_details.html")
template_name = "nodegroup_templates/_details.html"
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']
@ -79,8 +103,7 @@ class GeneralTab(tabs.Tab):
class ConfigsTab(tabs.Tab):
name = _("Service Configurations")
slug = "nodegroup_template_service_configs_tab"
template_name = (
"project/data_processing.nodegroup_templates/_service_confs.html")
template_name = "nodegroup_templates/_service_confs.html"
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']

View File

@ -21,18 +21,18 @@ from openstack_dashboard import api as dash_api
from sahara_dashboard import api
from sahara_dashboard.content.data_processing.utils \
import workflow_helpers
from sahara_dashboard.content.data_processing.\
from sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.workflows import create as create_workflow
from sahara_dashboard.test import helpers as test
INDEX_URL = reverse(
'horizon:project:data_processing.nodegroup_templates:index')
'horizon:project:data_processing.clusters:nodegroup-templates-tab')
DETAILS_URL = reverse(
'horizon:project:data_processing.nodegroup_templates:details',
'horizon:project:data_processing.clusters:details',
args=['id'])
CREATE_URL = reverse(
'horizon:project:data_processing.nodegroup_templates:' +
'horizon:project:data_processing.clusters:' +
'configure-nodegroup-template')
@ -62,7 +62,7 @@ class DataProcessingNodeGroupTests(test.TestCase):
self.mox.ReplayAll()
url = reverse(
'horizon:project:data_processing.nodegroup_templates:copy',
'horizon:project:data_processing.clusters:copy',
args=[ngt.id])
res = self.client.get(url)
@ -73,10 +73,9 @@ class DataProcessingNodeGroupTests(test.TestCase):
api.sahara.nodegroup_template_list(IsA(http.HttpRequest), {}) \
.AndReturn(self.nodegroup_templates.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res,
'project/data_processing.nodegroup_templates/'
'nodegroup_templates.html')
res = self.client.get(INDEX_URL +
"?tab=cluster_tabs__node_group_templates_tab")
self.assertTemplateUsed(res, 'clusters/index.html')
self.assertContains(res, 'Node Group Templates')
self.assertContains(res, 'Name')
self.assertContains(res, 'Plugin')
@ -232,7 +231,7 @@ class DataProcessingNodeGroupTests(test.TestCase):
configs = self.plugins_configs.first()
new_name = ngt.name + '-updated'
UPDATE_URL = reverse(
'horizon:project:data_processing.nodegroup_templates:edit',
'horizon:project:data_processing.clusters:edit',
kwargs={'template_id': ngt.id})
self.mox.StubOutWithMock(
workflow_helpers, 'parse_configs_from_context')

View File

@ -22,22 +22,21 @@ from horizon import workflows
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.tables as _tables
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.tabs as _tabs
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.workflows.copy as copy_flow
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.workflows.create as create_flow
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.workflows.edit as edit_flow
class NodegroupTemplatesView(tables.DataTableView):
table_class = _tables.NodegroupTemplatesTable
template_name = (
'project/data_processing.nodegroup_templates/nodegroup_templates.html')
template_name = 'nodegroup_templates/nodegroup_templates.html'
page_title = _("Node Group Templates")
def get_data(self):
@ -69,7 +68,7 @@ class NodegroupTemplateDetailsView(tabs.TabView):
msg = _('Unable to retrieve details for '
'node group template "%s".') % ngt_id
redirect = reverse("horizon:project:data_processing."
"nodegroup_templates:nodegroup-templates")
"clusters:nodegroup-templates")
exceptions.handle(self.request, msg, redirect=redirect)
def get_context_data(self, **kwargs):
@ -88,18 +87,18 @@ class NodegroupTemplateDetailsView(tabs.TabView):
class CreateNodegroupTemplateView(workflows.WorkflowView):
workflow_class = create_flow.CreateNodegroupTemplate
success_url = (
"horizon:project:data_processing.nodegroup_templates:"
"horizon:project:data_processing.clusters:"
"create-nodegroup-template")
classes = ("ajax-modal",)
template_name = "project/data_processing.nodegroup_templates/create.html"
template_name = "nodegroup_templates/create.html"
page_title = _("Create Node Group Template")
class ConfigureNodegroupTemplateView(workflows.WorkflowView):
workflow_class = create_flow.ConfigureNodegroupTemplate
success_url = "horizon:project:data_processing.nodegroup_templates"
template_name = (
"project/data_processing.nodegroup_templates/configure.html")
success_url = ("horizon:project:"
"data_processing.clusters:nodegroup-templates-tab")
template_name = "nodegroup_templates/configure.html"
page_title = _("Create Node Group Template")
def get_initial(self):
@ -110,9 +109,9 @@ class ConfigureNodegroupTemplateView(workflows.WorkflowView):
class CopyNodegroupTemplateView(workflows.WorkflowView):
workflow_class = copy_flow.CopyNodegroupTemplate
success_url = "horizon:project:data_processing.nodegroup_templates"
template_name = (
"project/data_processing.nodegroup_templates/configure.html")
success_url = ("horizon:project:"
"data_processing.clusters:nodegroup-templates-tab")
template_name = "nodegroup_templates/configure.html"
def get_context_data(self, **kwargs):
context = super(CopyNodegroupTemplateView, self)\
@ -142,6 +141,6 @@ class CopyNodegroupTemplateView(workflows.WorkflowView):
class EditNodegroupTemplateView(CopyNodegroupTemplateView):
workflow_class = edit_flow.EditNodegroupTemplate
success_url = "horizon:project:data_processing.nodegroup_templates"
template_name = (
"project/data_processing.nodegroup_templates/configure.html")
success_url = ("horizon:project:"
"data_processing.clusters:nodegroup-templates-tab")
template_name = "nodegroup_templates/configure.html"

View File

@ -17,7 +17,7 @@ from horizon import exceptions
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.workflows.create as create_flow

View File

@ -228,9 +228,7 @@ class GeneralConfigAction(workflows.Action):
class Meta(object):
name = _("Configure Node Group Template")
help_text_template = (
"project/data_processing.nodegroup_templates"
"/_configure_general_help.html")
help_text_template = "nodegroup_templates/_configure_general_help.html"
class SecurityConfigAction(workflows.Action):
@ -419,7 +417,8 @@ class ConfigureNodegroupTemplate(workflow_helpers.ServiceParametersWorkflow,
finalize_button_name = _("Create")
success_message = _("Created Node Group Template %s")
name_property = "general_nodegroup_name"
success_url = "horizon:project:data_processing.nodegroup_templates:index"
success_url = ("horizon:project:data_processing.clusters:"
"nodegroup-templates-tab")
default_steps = (GeneralConfig, SelectNodeProcesses, SecurityConfig, )
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
@ -559,8 +558,7 @@ class SelectPluginAction(workflows.Action,
class Meta(object):
name = _("Select plugin and hadoop version")
help_text_template = ("project/data_processing.nodegroup_templates"
"/_create_general_help.html")
help_text_template = "nodegroup_templates/_create_general_help.html"
class SelectPlugin(workflows.Step):
@ -581,5 +579,6 @@ class CreateNodegroupTemplate(workflows.Workflow):
finalize_button_name = _("Next")
success_message = _("Created")
failure_message = _("Could not create")
success_url = "horizon:project:data_processing.nodegroup_templates:index"
success_url = ("horizon:project:data_processing.clusters:"
"nodegroup-templates-tab")
default_steps = (SelectPlugin,)

View File

@ -19,9 +19,9 @@ from horizon import forms
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.workflows.create as create_flow
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
nodegroup_templates.workflows.copy as copy_flow
from sahara_dashboard.content.data_processing.utils \
import workflow_helpers

View File

@ -13,7 +13,7 @@
</dl>
<dl class="dl-horizontal">
<dt>{% trans "Plugin" %}</dt>
<dd><a href="{% url 'horizon:project:data_processing.data_plugins:details' template.plugin_name %}">{{ template.plugin_name }}</a></dd>
<dd><a href="{% url 'horizon:project:data_processing.jobs:details' template.plugin_name %}">{{ template.plugin_name }}</a></dd>
<dt>{% trans "Version" %}</dt>
<dd>{{ template.hadoop_version }}</dd>
<dt>{% trans "Use auto-configuration" %}</dt>

View File

@ -12,7 +12,7 @@
<dt>{% trans "Template" %}</dt>
{% if node_group.node_group_template_id %}
<dd><a href="{% url 'horizon:project:data_processing.nodegroup_templates:details' node_group.node_group_template_id %}">{{ node_group.node_group_template.name }} </a></dd>
<dd><a href="{% url 'horizon:project:data_processing.clusters:details' node_group.node_group_template_id %}">{{ node_group.node_group_template.name }} </a></dd>
{% else %}
<dd>{% trans "Template not specified" %}</dd>
{% endif %}

View File

@ -4,7 +4,7 @@
{% load i18n %}
{% block form_id %}upload_file{% endblock %}
{% block form_action %}{% url 'horizon:project:data_processing.cluster_templates:upload_file' %}{% endblock %}
{% block form_action %}{% url 'horizon:project:data_processing.clusters:upload_file' %}{% endblock %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-header %}{% trans "Upload Template" %}{% endblock %}
@ -19,5 +19,5 @@
{% block modal-footer %}
<input class="btn btn-primary pull-right" id="upload_file_btn" type="submit" value="{% trans "Upload" %}"/>
<a href="{% url 'horizon:project:data_processing.cluster_templates:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
<a href="{% url 'horizon:project:data_processing.clusters:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -3,5 +3,5 @@
{% block title %}{% trans "Upload Template" %}{% endblock %}
{% block main %}
{% include 'project/data_processing.cluster_templates/_upload_file.html' %}
{% include 'cluster_templates/_upload_file.html' %}
{% endblock %}

View File

@ -29,19 +29,21 @@
<dl class="dl-horizontal">
<dt>{% trans "Plugin" %}</dt>
<dd><a href="{% url 'horizon:project:data_processing.data_plugins:details' cluster.plugin_name %}">{{ cluster.plugin_name }}</a></dd>
<dd><a href="{% url 'horizon:project:data_processing.jobs:plugin-details' cluster.plugin_name %}">{{ cluster.plugin_name }}</a></dd>
<dt>{% trans "Version" %}</dt>
<dd>{{ cluster.hadoop_version }}</dd>
</dl>
<dl class="dl-horizontal">
<dt>{% trans "Template" %}</dt>
{% if cluster_template %}
<dd><a href="{% url 'horizon:project:data_processing.cluster_templates:details' cluster_template.id %}">{{ cluster_template.name }} </a></dd>
<dd><a href="{% url 'horizon:project:data_processing.clusters:ct-details' cluster_template.id %}">{{ cluster_template.name }} </a></dd>
{% else %}
<dd>{% trans "Template not specified" %}</dd>
{% endif %}
<dt>{% trans "Base Image" %}</dt>
<dd><a href="{% url 'horizon:project:images:images:detail' base_image.id %}">{{ base_image.name }}</a></dd>
{% if base_image.id %}
<dt>{% trans "Base Image" %}</dt>
<dd><a href="{% url 'horizon:project:images:images:detail' base_image.id %}">{{ base_image.name }}</a></dd>
{% endif %}
{% if network %}
<dt>{% trans "Neutron Management Network" %}</dt>
<dd>{{ network }}</dd>

View File

@ -18,7 +18,7 @@
<dt>{% trans "Template" %}</dt>
{% if node_group.node_group_template_id %}
<dd><a href="{% url 'horizon:project:data_processing.nodegroup_templates:details' node_group.node_group_template_id %}">{{ node_group.node_group_template.name }} </a></dd>
<dd><a href="{% url 'horizon:project:data_processing.clusters:details' node_group.node_group_template_id %}">{{ node_group.node_group_template.name }} </a></dd>
{% else %}
<dd>{% trans "Template not specified" %}</dd>
{% endif %}

View File

@ -3,5 +3,5 @@
{% block title %}{% trans "Launch Cluster" %}{% endblock %}
{% block main %}
{% include 'templates/data_processing.clusters/_create_cluster.html' %}
{% include 'clusters/_create_cluster.html' %}
{% endblock %}

View File

@ -0,0 +1,155 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Clusters" %}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
<script type="text/javascript">
addHorizonLoadEvent(function () {
horizon.modals.addModalInitFunction(function (modal) {
if ($("#id_tags_list").val()) {
var tags = JSON.parse($("#id_tags_list").val());
$.each(tags, function (i, tag) {
var tagspan = '<span class="label label-info" style="float: left;display: block; margin: 2px;">' +
tag +
'<i class="fa fa-close" onclick="remove_tag(this);"></i></span>';
$("#image_tags_list").append(tagspan);
});
}
});
});
</script>
<script type="text/javascript">
addHorizonLoadEvent(function () {
horizon.modals.addModalInitFunction(function (modal) {
// ensure that we only do this for the cluster template form
if (($(modal).find("#create_cluster_template__selectpluginaction")).length > 0) {
var $navbar = $(modal).find(".nav-tabs");
if ($navbar.find("li").size() == 1) {
// hide tab bar for plugin/version modal wizard
$navbar.hide();
}
var add_ng_btn_label = '{% trans "Add Node Group" %}';
$(".hidden_nodegroups_field").after("<input type='button' id='add_nodegroup' value='" + add_ng_btn_label + "'/><br/>");
$("#add_nodegroup").click(function() {
$(".hidden_nodegroups_field").val("create_nodegroup");
$(".hidden_configure_field").val("create_nodegroup");
var form = $(".hidden_nodegroups_field").closest("form");
form.submit();
});
$(".hidden_nodegroups_field").val("");
$(".hidden_configure_field").val("");
if ($(modal).find(".hidden_create_field").length > 0) {
var form = $(".hidden_create_field").closest("form");
var successful = false;
form.submit(function (e) {
var oldHref = $(".configure-clustertemplate-btn")[0].href;
var plugin = $("#id_plugin_name option:selected").val();
var version = $("#id_" + plugin + "_version option:selected").val();
form.find(".close").click();
$(".configure-clustertemplate-btn")[0].href = oldHref +
"?plugin_name=" + encodeURIComponent(plugin) +
"&hadoop_version=" + encodeURIComponent(version);
$(".configure-clustertemplate-btn").click();
$(".configure-clustertemplate-btn")[0].href = oldHref;
return false;
});
$(".plugin_version_choice").closest(".form-group").hide();
}
}
//display version for selected plugin
$(document).on('change', '.plugin_name_choice', switch_versions);
function switch_versions() {
$(".plugin_version_choice").closest(".form-group").hide();
var plugin = $(this);
$("." + plugin.val() + "_version_choice").closest(".form-group").show();
}
$(".plugin_name_choice").change();
});
});
</script>
<script type="text/javascript">
addHorizonLoadEvent(function () {
function get_service_tab(service) {
return $("a").filter(function (idx, e) {
return $(e).attr("data-target") && $(e).attr("data-target").indexOf('_'+service.toLowerCase()+'-') != -1
}).closest("li");
}
// replace form submit with ajax POST and trigger next workflow
horizon.modals.addModalInitFunction(function (modal) {
// ensure that we only do this for the nodegroup template form
if (($(modal).find("#create_nodegroup_template__selectpluginaction")).length > 0) {
var $navbar = $(modal).find(".nav-tabs");
if ($navbar.find("li").size() == 1) {
// hide tab bar for plugin/version modal wizard
$navbar.hide();
}
if ($(modal).find(".hidden_create_field").length > 0) {
var form = $(".hidden_create_field").closest("form");
var successful = false;
form.submit(function (e) {
var oldHref = $(".configure-nodegrouptemplate-btn")[0].href;
var plugin = $("#id_plugin_name option:selected").val();
var version = $("#id_" + plugin + "_version option:selected").val();
form.find(".close").click();
$(".configure-nodegrouptemplate-btn")[0].href = oldHref +
"?plugin_name=" + encodeURIComponent(plugin) +
"&hadoop_version=" + encodeURIComponent(version);
$(".configure-nodegrouptemplate-btn").click();
$(".configure-nodegrouptemplate-btn")[0].href = oldHref;
return false;
});
$(".plugin_version_choice").closest(".form-group").hide();
}
}
//display version for selected plugin
$(document).on('change', '.plugin_name_choice', switch_versions);
function switch_versions() {
$(".plugin_version_choice").closest(".form-group").hide();
var plugin = $(this);
$("." + plugin.val() + "_version_choice").closest(".form-group").show();
}
$(".plugin_name_choice").change();
//handle node processes change
$("input").filter(function (idx, e) {
return $(e).attr("name") && $(e).attr("name").indexOf("processes") != -1
})
.change(function () {
var process_service = $(this).val();
var service = $(this).val().split(":")[0];
var enabled = false;
$(this).closest("ul").find("input").each(function (idx, el) {
if ($(el).val().split(":")[0] != service) {
return;
}
enabled |= $(el).is(':checked');
});
if (enabled) {
get_service_tab(service).show();
} else {
get_service_tab(service).hide();
}
}).change();
//general tab should be active
get_service_tab("generalconfigaction").find("a").click();
});
});
</script>
{% endblock %}

View File

@ -4,7 +4,7 @@
{% load i18n %}
{% block form_id %}edit_tags_form{% endblock %}
{% block form_action %}{% url 'horizon:project:data_processing.data_image_registry:edit_tags' image.id %}{% endblock %}
{% block form_action %}{% url 'horizon:project:data_processing.clusters:edit_tags' image.id %}{% endblock %}
{% block modal-header %}{% trans "Edit Image Tags" %}{% endblock %}
@ -15,14 +15,14 @@
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
{% include 'project/data_processing.data_image_registry/_tag_form.html' %}
{% include 'data_image_registry/_tag_form.html' %}
</div>
<div class="right">
{% include 'project/data_processing.data_image_registry/_help.html' %}
{% include 'data_image_registry/_help.html' %}
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" id="edit_image_tags_btn" type="submit" value="{% trans "Done" %}"/>
<a href="{% url 'horizon:project:data_processing.data_image_registry:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
<a href="{% url 'horizon:project:data_processing.clusters:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -4,7 +4,7 @@
{% load i18n %}
{% block form_id %}register_image_form{% endblock %}
{% block form_action %}{% url 'horizon:project:data_processing.data_image_registry:register' %}{% endblock %}
{% block form_action %}{% url 'horizon:project:data_processing.clusters:register' %}{% endblock %}
{% block modal-header %}{% trans "Register Image" %}{% endblock %}
@ -12,15 +12,15 @@
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
{% include 'project/data_processing.data_image_registry/_tag_form.html' %}
{% include 'data_image_registry/_tag_form.html' %}
</fieldset>
</div>
<div class="right">
{% include 'project/data_processing.data_image_registry/_help.html' %}
{% include 'data_image_registry/_help.html' %}
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" id="edit_image_tags_btn" type="submit" value="{% trans "Done" %}"/>
<a href="{% url 'horizon:project:data_processing.data_image_registry:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
<a href="{% url 'horizon:project:data_processing.clusters:index' %}" class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -3,5 +3,5 @@
{% block title %}{% trans "Edit Image Tags" %}{% endblock %}
{% block main %}
{% include 'project/data_processing.data_image_registry/_edit_tags.html' %}
{% include 'data_image_registry/_edit_tags.html' %}
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Data Processing" %}{% endblock %}
{% block main %}
<div class="image_registry">
{{ image_registry_table.render }}
</div>
{% endblock %}

View File

@ -3,5 +3,5 @@
{% block title %}{% trans "Register Image" %}{% endblock %}
{% block main %}
{% include 'project/data_processing.data_image_registry/_register_image.html' %}
{% include 'data_image_registry/_register_image.html' %}
{% endblock %}

View File

@ -34,7 +34,7 @@
{% endif %}
<dl class="dl-horizontal">
<dt>{% trans "Plugin" %}</dt>
<dd><a href="{% url 'horizon:project:data_processing.data_plugins:details' template.plugin_name %}">{{ template.plugin_name }}</a></dd>
<dd><a href="{% url 'horizon:project:data_processing.jobs:details' template.plugin_name %}">{{ template.plugin_name }}</a></dd>
<dt>{% trans "Version" %}</dt>
<dd>{{ template.hadoop_version }}</dd>
</dl>

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Data Processing" %}{% endblock %}
{% block main %}
<div class="nodegroup_templates">
{{ nodegroup_templates_table.render }}
</div>
{% endblock %}

View File

@ -16,28 +16,85 @@ from django.conf.urls import patterns
from django.conf.urls import url
import sahara_dashboard.content.data_processing.clusters.views as views
import sahara_dashboard.content.data_processing.clusters.data_image_registry. \
views as image_views
import sahara_dashboard.content.data_processing.clusters.nodegroup_templates. \
views as ngt_views
import sahara_dashboard.content.data_processing.clusters.cluster_templates. \
views as ct_views
import sahara_dashboard.content.data_processing.clusters.clusters. \
views as cluster_views
urlpatterns = patterns('',
url(r'^$', views.ClustersView.as_view(),
url(r'^$', views.IndexView.as_view(),
name='index'),
url(r'^$', views.ClustersView.as_view(),
name='clusters'),
url(r'^\?tab=cluster_tabs__data_image_registry_tab$',
views.IndexView.as_view(),
name='image-registry-tab'),
url(r'^\?tab=cluster_tabs__node_group_templates_tab$',
views.IndexView.as_view(),
name='nodegroup-templates-tab'),
url(r'^\?tab=cluster_tabs__clusters_templates_tab$',
views.IndexView.as_view(),
name='cluster-templates-tab'),
url(r'^\?tab=cluster_tabs__clusters_tab$',
views.IndexView.as_view(),
name='clusters-tab'),
url(r'^upload_file$',
ct_views.UploadFileView.as_view(),
name='upload_file'),
url(r'^create-cluster-template$',
ct_views.CreateClusterTemplateView.as_view(),
name='create-cluster-template'),
url(r'^create-nodegroup-template$',
ngt_views.CreateNodegroupTemplateView.as_view(),
name='create-nodegroup-template'),
url(r'^configure-cluster-template$',
ct_views.ConfigureClusterTemplateView.as_view(),
name='configure-cluster-template'),
url(r'^configure-nodegroup-template$',
ngt_views.ConfigureNodegroupTemplateView.as_view(),
name='configure-nodegroup-template'),
url(r'^cluster-template/(?P<template_id>[^/]+)$',
ct_views.ClusterTemplateDetailsView.as_view(),
name='ct-details'),
url(r'^node-group-template/(?P<template_id>[^/]+)$',
ngt_views.NodegroupTemplateDetailsView.as_view(),
name='details'),
url(r'^cluster-template/(?P<template_id>[^/]+)/copy$',
ct_views.CopyClusterTemplateView.as_view(),
name='ct-copy'),
url(r'^cluster-template/(?P<template_id>[^/]+)/edit$',
ct_views.EditClusterTemplateView.as_view(),
name='ct-edit'),
url(r'^node-group-template/'
'(?P<template_id>[^/]+)/copy$',
ngt_views.CopyNodegroupTemplateView.as_view(),
name='copy'),
url(r'^node-group-template/'
'(?P<template_id>[^/]+)/edit$',
ngt_views.EditNodegroupTemplateView.as_view(),
name='edit'),
url(r'^create-cluster$',
views.CreateClusterView.as_view(),
cluster_views.CreateClusterView.as_view(),
name='create-cluster'),
url(r'^configure-cluster$',
views.ConfigureClusterView.as_view(),
cluster_views.ConfigureClusterView.as_view(),
name='configure-cluster'),
url(r'^(?P<cluster_id>[^/]+)$',
views.ClusterDetailsView.as_view(),
name='details'),
url(r'^(?P<cluster_id>[^/]+)/events$',
views.ClusterEventsView.as_view(),
url(r'^cluster/(?P<cluster_id>[^/]+)$',
cluster_views.ClusterDetailsView.as_view(),
name='cluster-details'),
url(r'^cluster/(?P<cluster_id>[^/]+)/events$',
cluster_views.ClusterEventsView.as_view(),
name='events'),
url(r'^(?P<cluster_id>[^/]+)/scale$',
views.ScaleClusterView.as_view(),
url(r'^cluster/(?P<cluster_id>[^/]+)/scale$',
cluster_views.ScaleClusterView.as_view(),
name='scale'),
url(r'^(?P<cluster_id>[^/]+)/update_shares$',
views.UpdateClusterSharesView.as_view(),
name='update-shares'))
url(r'^cluster/(?P<cluster_id>[^/]+)/update_shares$',
cluster_views.UpdateClusterSharesView.as_view(),
name='update-shares'),
url(r'^edit_tags/(?P<image_id>[^/]+)/$',
image_views.EditTagsView.as_view(),
name='edit_tags'),
url(r'^register/$',
image_views.RegisterImageView.as_view(),
name='register'))

View File

@ -11,256 +11,29 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime
import json
import logging
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.utils.translation import ugettext as _
from django.views.generic import base as django_base
import six
from horizon import exceptions
from horizon import tables
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from horizon.utils import memoized
from horizon import workflows
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing.clusters. \
tables as c_tables
import sahara_dashboard.content.data_processing.clusters. \
tabs as _tabs
import sahara_dashboard.content.data_processing.clusters. \
workflows.create as create_flow
import sahara_dashboard.content.data_processing.clusters. \
workflows.scale as scale_flow
import sahara_dashboard.content.data_processing.clusters. \
workflows.update as update_flow
from saharaclient.api.base import APIException
LOG = logging.getLogger(__name__)
from sahara_dashboard.content.data_processing.clusters.cluster_templates \
import tabs as cluster_templates_tabs
from sahara_dashboard.content.data_processing.clusters.clusters \
import tabs as clusters_tabs
from sahara_dashboard.content.data_processing.clusters.data_image_registry \
import tabs as data_image_registry_tabs
from sahara_dashboard.content.data_processing.clusters.nodegroup_templates \
import tabs as node_group_templates_tabs
class ClustersView(tables.DataTableView):
table_class = c_tables.ClustersTable
template_name = 'project/data_processing.clusters/clusters.html'
class ClusterTabs(tabs.TabGroup):
slug = "cluster_tabs"
tabs = (clusters_tabs.ClustersTab,
cluster_templates_tabs.ClusterTemplatesTab,
node_group_templates_tabs.NodeGroupTemplatesTab,
data_image_registry_tabs.DataImageRegistryTab,)
sticky = True
class IndexView(tabs.TabbedTableView):
tab_group_class = ClusterTabs
template_name = "clusters/index.html"
page_title = _("Clusters")
def get_data(self):
try:
search_opts = {}
filter = self.get_server_filter_info(self.request)
if filter['value'] and filter['field']:
search_opts = {filter['field']: filter['value']}
clusters = saharaclient.cluster_list(self.request, search_opts)
except Exception:
clusters = []
exceptions.handle(self.request,
_("Unable to fetch cluster list"))
return clusters
class ClusterDetailsView(tabs.TabView):
tab_group_class = _tabs.ClusterDetailsTabs
template_name = 'horizon/common/_detail.html'
page_title = "{{ cluster.name|default:cluster.id }}"
@memoized.memoized_method
def get_object(self):
cl_id = self.kwargs["cluster_id"]
try:
return saharaclient.cluster_get(self.request, cl_id)
except Exception:
msg = _('Unable to retrieve details for cluster "%s".') % cl_id
redirect = reverse(
"horizon:project:data_processing.clusters:clusters")
exceptions.handle(self.request, msg, redirect=redirect)
def get_context_data(self, **kwargs):
context = super(ClusterDetailsView, self).get_context_data(**kwargs)
cluster = self.get_object()
context['cluster'] = cluster
context['actions'] = self._get_actions(cluster)
return context
def _get_actions(self, cluster):
table = c_tables.ClustersTable(self.request)
return table.render_row_actions(cluster)
class ClusterEventsView(django_base.View):
_date_format = "%Y-%m-%dT%H:%M:%S"
@staticmethod
def _created_at_key(obj):
return datetime.strptime(obj["created_at"],
ClusterEventsView._date_format)
def get(self, request, *args, **kwargs):
cluster_id = kwargs.get("cluster_id")
try:
cluster = saharaclient.cluster_get(request, cluster_id,
show_progress=True)
node_group_mapping = {}
for node_group in cluster.node_groups:
node_group_mapping[node_group["id"]] = node_group["name"]
provision_steps = cluster.provision_progress
# Sort by create time
provision_steps = sorted(provision_steps,
key=ClusterEventsView._created_at_key,
reverse=True)
for step in provision_steps:
# Sort events of the steps also
step["events"] = sorted(step["events"],
key=ClusterEventsView._created_at_key,
reverse=True)
successful_events_count = 0
for event in step["events"]:
if event["node_group_id"]:
event["node_group_name"] = node_group_mapping[
event["node_group_id"]]
event_result = _("Unknown")
if event["successful"] is True:
successful_events_count += 1
event_result = _("Completed Successfully")
elif event["successful"] is False:
event_result = _("Failed")
event["result"] = event_result
if not event["event_info"]:
event["event_info"] = _("No info available")
start_time = datetime.strptime(step["created_at"],
self._date_format)
end_time = datetime.now()
# Clear out microseconds. There is no need for that precision.
end_time = end_time.replace(microsecond=0)
if step["successful"] is not None:
updated_at = step["updated_at"]
end_time = datetime.strptime(updated_at,
self._date_format)
step["duration"] = six.text_type(end_time - start_time)
result = _("In progress")
step["completed"] = successful_events_count
if step["successful"] is True:
step["completed"] = step["total"]
result = _("Completed Successfully")
elif step["successful"] is False:
result = _("Failed")
step["result"] = result
status = cluster.status.lower()
need_update = status not in ("active", "error")
except APIException:
# Cluster is not available. Returning empty event log.
need_update = False
provision_steps = []
context = {"provision_steps": provision_steps,
"need_update": need_update}
return HttpResponse(json.dumps(context),
content_type='application/json')
class CreateClusterView(workflows.WorkflowView):
workflow_class = create_flow.CreateCluster
success_url = \
"horizon:project:data_processing.clusters:create-cluster"
classes = ("ajax-modal",)
template_name = "project/data_processing.clusters/create.html"
page_title = _("Launch Cluster")
class ConfigureClusterView(workflows.WorkflowView):
workflow_class = create_flow.ConfigureCluster
success_url = "horizon:project:data_processing.clusters"
template_name = "project/data_processing.clusters/configure.html"
page_title = _("Configure Cluster")
def get_initial(self):
initial = super(ConfigureClusterView, self).get_initial()
initial.update(self.kwargs)
return initial
class ScaleClusterView(workflows.WorkflowView):
workflow_class = scale_flow.ScaleCluster
success_url = "horizon:project:data_processing.clusters"
classes = ("ajax-modal",)
template_name = "project/data_processing.clusters/scale.html"
page_title = _("Scale Cluster")
def get_context_data(self, **kwargs):
context = super(ScaleClusterView, self)\
.get_context_data(**kwargs)
context["cluster_id"] = kwargs["cluster_id"]
return context
def get_object(self, *args, **kwargs):
if not hasattr(self, "_object"):
template_id = self.kwargs['cluster_id']
try:
template = saharaclient.cluster_template_get(self.request,
template_id)
except Exception:
template = None
exceptions.handle(self.request,
_("Unable to fetch cluster template."))
self._object = template
return self._object
def get_initial(self):
initial = super(ScaleClusterView, self).get_initial()
initial.update({'cluster_id': self.kwargs['cluster_id']})
return initial
class UpdateClusterSharesView(workflows.WorkflowView):
workflow_class = update_flow.UpdateShares
success_url = "horizon:project:data_processing.clusters"
classes = ("ajax-modal",)
template_name = "project/data_processing.clusters/update.html"
page_title = _("Update Cluster Shares")
def get_context_data(self, **kwargs):
context = super(UpdateClusterSharesView, self)\
.get_context_data(**kwargs)
context["cluster_id"] = kwargs["cluster_id"]
return context
def get_object(self, *args, **kwargs):
if not hasattr(self, "_object"):
cluster_id = self.kwargs['cluster_id']
try:
cluster = saharaclient.cluster_get(self.request, cluster_id)
except Exception:
cluster = None
exceptions.handle(self.request,
_("Unable to fetch cluster."))
self._object = cluster
return self._object
def get_initial(self):
initial = super(UpdateClusterSharesView, self).get_initial()
initial.update({
'cluster_id': self.kwargs['cluster_id'],
'cluster': self.get_object()})
return initial

View File

@ -1,28 +0,0 @@
# 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 ImageRegistryPanel(horizon.Panel):
name = _("Image Registry")
slug = 'data_processing.data_image_registry'
permissions = (('openstack.services.data-processing',
'openstack.services.data_processing'),)
dashboard.Project.register(ImageRegistryPanel)

View File

@ -1,24 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Data Processing" %}{% endblock %}
{% block main %}
<div class="image_registry">
{{ image_registry_table.render }}
</div>
<script type="text/javascript">
addHorizonLoadEvent(function () {
horizon.modals.addModalInitFunction(function (modal) {
var tags = JSON.parse($("#id_tags_list").val());
$.each(tags, function(i, tag) {
var tagspan = '<span class="label label-info" style="float: left;display: block; margin: 2px;">' +
tag +
'<i class="fa fa-close" onclick="remove_tag(this);"></i></span>';
$("#image_tags_list").append(tagspan);
});
});
});
</script>
{% endblock %}

View File

@ -1,33 +0,0 @@
# 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
from django.conf.urls import url
import sahara_dashboard.content. \
data_processing.data_image_registry.views as views
urlpatterns = patterns('',
url(r'^$', views.ImageRegistryView.as_view(),
name='index'),
url(r'^$', views.ImageRegistryView.as_view(),
name='image_registry'),
url(r'^edit_tags/(?P<image_id>[^/]+)/$',
views.EditTagsView.as_view(),
name='edit_tags'),
url(r'^register/$',
views.RegisterImageView.as_view(),
name='register'),
)

View File

@ -21,10 +21,10 @@ from horizon import forms
from horizon import workflows
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing. \
import sahara_dashboard.content.data_processing.clusters. \
cluster_templates.workflows.create as t_flows
import sahara_dashboard.content.data_processing. \
clusters.workflows.create as c_flow
clusters.clusters.workflows.create as c_flow
from sahara_dashboard.content.data_processing.utils \
import acl as acl_utils
import sahara_dashboard.content.data_processing. \

View File

@ -1,28 +0,0 @@
# 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 NodegroupTemplatesPanel(horizon.Panel):
name = _("Node Group Templates")
slug = 'data_processing.nodegroup_templates'
permissions = (('openstack.services.data-processing',
'openstack.services.data_processing'),)
dashboard.Project.register(NodegroupTemplatesPanel)

View File

@ -1,81 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Data Processing" %}{% endblock %}
{% block main %}
<div class="nodegroup_templates">
{{ nodegroup_templates_table.render }}
</div>
<script type="text/javascript">
addHorizonLoadEvent(function () {
function get_service_tab(service) {
return $("a").filter(function (idx, e) {
return $(e).attr("data-target") && $(e).attr("data-target").indexOf('_'+service.toLowerCase()+'-') != -1
}).closest("li");
}
// replace form submit with ajax POST and trigger next workflow
horizon.modals.addModalInitFunction(function (modal) {
var $navbar = $(modal).find(".nav-tabs");
if ($navbar.find("li").size() == 1) {
// hide tab bar for plugin/version modal wizard
$navbar.hide();
}
if ($(modal).find(".hidden_create_field").length > 0) {
var form = $(".hidden_create_field").closest("form");
var successful = false;
form.submit(function (e) {
var oldHref = $(".configure-nodegrouptemplate-btn")[0].href;
var plugin = $("#id_plugin_name option:selected").val();
var version = $("#id_" + plugin + "_version option:selected").val();
form.find(".close").click();
$(".configure-nodegrouptemplate-btn")[0].href = oldHref +
"?plugin_name=" + encodeURIComponent(plugin) +
"&hadoop_version=" + encodeURIComponent(version);
$(".configure-nodegrouptemplate-btn").click();
$(".configure-nodegrouptemplate-btn")[0].href = oldHref;
return false;
});
$(".plugin_version_choice").closest(".form-group").hide();
}
//display version for selected plugin
$(document).on('change', '.plugin_name_choice', switch_versions);
function switch_versions() {
$(".plugin_version_choice").closest(".form-group").hide();
var plugin = $(this);
$("." + plugin.val() + "_version_choice").closest(".form-group").show();
}
$(".plugin_name_choice").change();
//handle node processes change
$("input").filter(function (idx, e) {
return $(e).attr("name") && $(e).attr("name").indexOf("processes") != -1
})
.change(function () {
var process_service = $(this).val();
var service = $(this).val().split(":")[0];
var enabled = false;
$(this).closest("ul").find("input").each(function (idx, el) {
if ($(el).val().split(":")[0] != service) {
return;
}
enabled |= $(el).is(':checked');
});
if (enabled) {
get_service_tab(service).show();
} else {
get_service_tab(service).hide();
}
}).change();
//general tab should be active
get_service_tab("generalconfigaction").find("a").click();
});
});
</script>
{% endblock %}

View File

@ -1,43 +0,0 @@
# 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
from django.conf.urls import url
import sahara_dashboard.content. \
data_processing.nodegroup_templates.views as views
urlpatterns = patterns('sahara.nodegroup_templates.views',
url(r'^$', views.NodegroupTemplatesView.as_view(),
name='index'),
url(r'^nodegroup-templates$',
views.NodegroupTemplatesView.as_view(),
name='nodegroup-templates'),
url(r'^create-nodegroup-template$',
views.CreateNodegroupTemplateView.as_view(),
name='create-nodegroup-template'),
url(r'^configure-nodegroup-template$',
views.ConfigureNodegroupTemplateView.as_view(),
name='configure-nodegroup-template'),
url(r'^(?P<template_id>[^/]+)$',
views.NodegroupTemplateDetailsView.as_view(),
name='details'),
url(r'^(?P<template_id>[^/]+)/copy$',
views.CopyNodegroupTemplateView.as_view(),
name='copy'),
url(r'^(?P<template_id>[^/]+)/edit$',
views.EditNodegroupTemplateView.as_view(),
name='edit')
)

View File

@ -0,0 +1,44 @@
# 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 horizon import tabs
class SaharaTableTab(tabs.TableTab):
def get_server_filter_info(self, request, table):
filter_action = table._meta._filter_action
if filter_action is None or filter_action.filter_type != 'server':
return None
param_name = filter_action.get_param_name()
filter_string = request.POST.get(param_name)
filter_string_session = request.session.get(param_name, "")
changed = (filter_string is not None
and filter_string != filter_string_session)
if filter_string is None:
filter_string = filter_string_session
filter_field_param = param_name + '_field'
filter_field = request.POST.get(filter_field_param)
filter_field_session = request.session.get(filter_field_param)
if filter_field is None and filter_field_session is not None:
filter_field = filter_field_session
setattr(table.base_actions["filter"], "filter_string", filter_string)
setattr(table.base_actions["filter"], "filter_field", filter_field)
filter_info = {
'action': filter_action,
'value_param': param_name,
'value': filter_string,
'field_param': filter_field_param,
'field': filter_field,
'changed': changed
}
return filter_info

View File

@ -89,9 +89,7 @@ def _create_step_action(name, title, parameters, advanced_fields=None,
class_fields[ad_field_name] = ad_field_value
action_meta = type('Meta', (object, ),
dict(help_text_template=("project"
"/data_processing."
"nodegroup_templates/"
dict(help_text_template=("nodegroup_templates/"
"_fields_help.html")))
class_fields['Meta'] = action_meta

View File

@ -128,7 +128,7 @@
<div>
<a class="btn btn-default btn-small btn-create btn-inline ajax-modal
{% if not request.session.worker_name %} disabled{% endif %}"
href="{% url 'horizon:project:data_processing.cluster_templates:configure-cluster-template' %}?plugin_name={{ request.session.plugin_name }}&hadoop_version={{ request.session.plugin_version }}&forms_ids=[0,1]&count_0=1&group_name_0={{ request.session.master_name }}&template_id_0={{ request.session.master_id }}&count_1=1&group_name_1={{ request.session.worker_name }}&template_id_1={{ request.session.worker_id }}&serialized_0=null&serialized_1=null">
href="{% url 'horizon:project:data_processing.clusters:configure-cluster-template' %}?plugin_name={{ request.session.plugin_name }}&hadoop_version={{ request.session.plugin_version }}&forms_ids=[0,1]&count_0=1&group_name_0={{ request.session.master_name }}&template_id_0={{ request.session.master_id }}&count_1=1&group_name_1={{ request.session.worker_name }}&template_id_1={{ request.session.worker_id }}&serialized_0=null&serialized_1=null">
<span class="fa fa-plus"></span> {% trans "Create a Cluster Template" %}</a>
</div>
<div>{% trans "Current choice:" %}

View File

@ -25,7 +25,8 @@ ADD_PANEL = \
'content.data_processing.clusters.panel.ClustersPanel')
ADD_INSTALLED_APPS = \
["sahara_dashboard.content.data_processing", ]
["sahara_dashboard.content.data_processing",
"sahara_dashboard.content.data_processing.clusters", ]
ADD_JS_FILES = [
'dashboard/project/data_processing/data_processing.event_log.js'

View File

@ -1,29 +0,0 @@
# 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 sahara_dashboard import exceptions
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'data_processing.cluster_templates'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'project'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'data_processing'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
('sahara_dashboard.'
'content.data_processing.cluster_templates.panel.ClusterTemplatesPanel')
ADD_EXCEPTIONS = {
'recoverable': exceptions.RECOVERABLE
}

View File

@ -1,30 +0,0 @@
# 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 sahara_dashboard import exceptions
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'data_processing.nodegroup_templates'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'project'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'data_processing'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
('sahara_dashboard.'
'content.data_processing.nodegroup_templates.panel.'
'NodegroupTemplatesPanel')
ADD_EXCEPTIONS = {
'recoverable': exceptions.RECOVERABLE
}

View File

@ -1,29 +0,0 @@
# 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 sahara_dashboard import exceptions
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'data_processing.data_image_registry'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'project'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'data_processing'
# Python panel class of the PANEL to be added.
ADD_PANEL = \
('sahara_dashboard.'
'content.data_processing.data_image_registry.panel.ImageRegistryPanel')
ADD_EXCEPTIONS = {
'recoverable': exceptions.RECOVERABLE
}