Guided cluster creation mode for Sahara
Adding a guide for cluster creation in data processing. This is meant to weave the existing panels and forms together in a way that can be understood by all users. Partial-Implements: blueprint data-processing-rework-ui Change-Id: Icfd0c53e90fdb7a520a034ffcc20a3752976f54c
This commit is contained in:
parent
4dec85fccc
commit
22bdf25e23
@ -69,7 +69,8 @@ class DataProcessingPanels(horizon.PanelGroup):
|
||||
'data_processing.job_binaries',
|
||||
'data_processing.data_sources',
|
||||
'data_processing.data_image_registry',
|
||||
'data_processing.data_plugins',)
|
||||
'data_processing.data_plugins',
|
||||
'data_processing.wizard',)
|
||||
|
||||
|
||||
class Project(horizon.Dashboard):
|
||||
|
@ -166,28 +166,39 @@ class ConfigureNodegroupsAction(workflows.Action):
|
||||
super(ConfigureNodegroupsAction, self). \
|
||||
__init__(request, *args, **kwargs)
|
||||
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
plugin = request.REQUEST.get("plugin_name")
|
||||
version = request.REQUEST.get("hadoop_version")
|
||||
if plugin and not version:
|
||||
version_name = plugin + "_version"
|
||||
version = request.REQUEST.get(version_name)
|
||||
|
||||
self.templates = saharaclient.nodegroup_template_find(
|
||||
request, plugin_name=plugin, hadoop_version=hadoop_version)
|
||||
if not plugin or not version:
|
||||
self.templates = saharaclient.nodegroup_template_find(request)
|
||||
else:
|
||||
self.templates = saharaclient.nodegroup_template_find(
|
||||
request, plugin_name=plugin, hadoop_version=version)
|
||||
|
||||
deletable = request.REQUEST.get("deletable", dict())
|
||||
|
||||
request_source = None
|
||||
if 'forms_ids' in request.POST:
|
||||
request_source = request.POST
|
||||
elif 'forms_ids' in request.REQUEST:
|
||||
request_source = request.REQUEST
|
||||
if request_source:
|
||||
self.groups = []
|
||||
for id in json.loads(request.POST['forms_ids']):
|
||||
for id in json.loads(request_source['forms_ids']):
|
||||
group_name = "group_name_" + str(id)
|
||||
template_id = "template_id_" + str(id)
|
||||
count = "count_" + str(id)
|
||||
serialized = "serialized_" + str(id)
|
||||
self.groups.append({"name": request.POST[group_name],
|
||||
"template_id": request.POST[template_id],
|
||||
"count": request.POST[count],
|
||||
self.groups.append({"name": request_source[group_name],
|
||||
"template_id": request_source[template_id],
|
||||
"count": request_source[count],
|
||||
"id": id,
|
||||
"deletable": deletable.get(
|
||||
request.POST[group_name], "true"),
|
||||
"serialized": request.POST[serialized]})
|
||||
request_source[group_name], "true"),
|
||||
"serialized": request_source[serialized]})
|
||||
|
||||
whelpers.build_node_group_fields(self,
|
||||
group_name,
|
||||
@ -233,7 +244,6 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
|
||||
ConfigureClusterTemplate._cls_registry = set([])
|
||||
|
||||
hlps = helpers.Helpers(request)
|
||||
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
general_parameters = hlps.get_cluster_general_configs(
|
||||
@ -300,6 +310,13 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
|
||||
node_groups,
|
||||
context["anti_affinity_info"],
|
||||
)
|
||||
|
||||
hlps = helpers.Helpers(request)
|
||||
if hlps.is_from_guide():
|
||||
request.session["guide_cluster_template_name"] = (
|
||||
context["general_cluster_template_name"])
|
||||
self.success_url = (
|
||||
"horizon:project:data_processing.wizard:cluster_guide")
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
|
@ -30,6 +30,12 @@ urlpatterns = patterns('',
|
||||
url(r'^configure-cluster$',
|
||||
views.ConfigureClusterView.as_view(),
|
||||
name='configure-cluster'),
|
||||
url(r'^configure-cluster'
|
||||
'/(?P<plugin_name>[^/]+)'
|
||||
'/(?P<hadoop_version>[^/]+)'
|
||||
'/(?P<cluster_template_name>[^/]+)/$',
|
||||
views.ConfigureClusterView.as_view(),
|
||||
name='configure-cluster'),
|
||||
url(r'^(?P<cluster_id>[^/]+)$',
|
||||
views.ClusterDetailsView.as_view(),
|
||||
name='details'),
|
||||
|
@ -79,6 +79,11 @@ class ConfigureClusterView(workflows.WorkflowView):
|
||||
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
|
||||
|
@ -22,7 +22,7 @@ from openstack_dashboard.dashboards.project.data_processing.utils \
|
||||
import openstack_dashboard.dashboards.project.data_processing.utils. \
|
||||
workflow_helpers as whelpers
|
||||
|
||||
|
||||
from django.core import urlresolvers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from openstack_dashboard.api import sahara as saharaclient
|
||||
@ -162,7 +162,19 @@ class GeneralConfigAction(workflows.Action):
|
||||
choices.append(("", _("No Templates Available")))
|
||||
# cluster_template_id comes from cluster templates table, when
|
||||
# Create Cluster from template is clicked there
|
||||
selected_template_id = request.REQUEST.get("cluster_template_id", None)
|
||||
selected_template_name = None
|
||||
resolver_match = urlresolvers.resolve(request.path)
|
||||
if "cluster_template_name" in resolver_match.kwargs:
|
||||
selected_template_name = (
|
||||
resolver_match.kwargs["cluster_template_name"])
|
||||
if selected_template_name:
|
||||
for template in templates:
|
||||
if template.name == selected_template_name:
|
||||
selected_template_id = template.id
|
||||
break
|
||||
else:
|
||||
selected_template_id = (
|
||||
request.REQUEST.get("cluster_template_id", None))
|
||||
|
||||
for template in templates:
|
||||
if template.id == selected_template_id:
|
||||
|
@ -31,6 +31,12 @@ urlpatterns = patterns('sahara.nodegroup_templates.views',
|
||||
url(r'^configure-nodegroup-template$',
|
||||
views.ConfigureNodegroupTemplateView.as_view(),
|
||||
name='configure-nodegroup-template'),
|
||||
url(r'^configure-nodegroup-template'
|
||||
'/(?P<plugin_name>[^/]+)/'
|
||||
'(?P<hadoop_version>[^/]+)/'
|
||||
'(?P<guide_template_type>[^/]+)/$',
|
||||
views.ConfigureNodegroupTemplateView.as_view(),
|
||||
name='configure-nodegroup-template-defaults'),
|
||||
url(r'^(?P<template_id>[^/]+)$',
|
||||
views.NodegroupTemplateDetailsView.as_view(),
|
||||
name='details'),
|
||||
|
@ -86,6 +86,11 @@ class ConfigureNodegroupTemplateView(workflows.WorkflowView):
|
||||
"project/data_processing.nodegroup_templates/configure.html")
|
||||
page_title = _("Create Node Group Template")
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(ConfigureNodegroupTemplateView, self).get_initial()
|
||||
initial.update(self.kwargs)
|
||||
return initial
|
||||
|
||||
|
||||
class CopyNodegroupTemplateView(workflows.WorkflowView):
|
||||
workflow_class = copy_flow.CopyNodegroupTemplate
|
||||
|
@ -13,7 +13,9 @@
|
||||
|
||||
import logging
|
||||
|
||||
from django.core import urlresolvers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from saharaclient.api import base as api_base
|
||||
|
||||
from horizon import exceptions
|
||||
@ -147,6 +149,13 @@ class GeneralConfigAction(workflows.Action):
|
||||
for param in node_parameters:
|
||||
self.fields[param.name] = workflow_helpers.build_control(param)
|
||||
|
||||
resolver_match = urlresolvers.resolve(request.path)
|
||||
if "guide_template_type" in resolver_match.kwargs:
|
||||
self.fields["guide_template_type"] = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(),
|
||||
initial=resolver_match.kwargs["guide_template_type"])
|
||||
|
||||
def populate_flavor_choices(self, request, context):
|
||||
flavors = nova_utils.flavor_list(request)
|
||||
if flavors:
|
||||
@ -314,7 +323,7 @@ class ConfigureNodegroupTemplate(workflow_helpers.ServiceParametersWorkflow,
|
||||
volumes_availability_zone = \
|
||||
context["general_volumes_availability_zone"]
|
||||
|
||||
saharaclient.nodegroup_template_create(
|
||||
ngt = saharaclient.nodegroup_template_create(
|
||||
request,
|
||||
name=context["general_nodegroup_name"],
|
||||
plugin_name=plugin,
|
||||
@ -330,6 +339,16 @@ class ConfigureNodegroupTemplate(workflow_helpers.ServiceParametersWorkflow,
|
||||
security_groups=context["security_groups"],
|
||||
auto_security_group=context["security_autogroup"],
|
||||
availability_zone=context["general_availability_zone"])
|
||||
|
||||
hlps = helpers.Helpers(request)
|
||||
if hlps.is_from_guide():
|
||||
guide_type = context["general_guide_template_type"]
|
||||
request.session[guide_type + "_name"] = (
|
||||
context["general_nodegroup_name"])
|
||||
request.session[guide_type + "_id"] = ngt.id
|
||||
self.success_url = (
|
||||
"horizon:project:data_processing.wizard:cluster_guide")
|
||||
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
|
@ -84,3 +84,23 @@ class Helpers(object):
|
||||
'cluster', service)
|
||||
|
||||
return parameters
|
||||
|
||||
def is_from_guide(self):
|
||||
referer = self.request.environ.get("HTTP_REFERER")
|
||||
if referer and "/wizard/" in referer:
|
||||
return True
|
||||
return False
|
||||
|
||||
def reset_guide(self):
|
||||
try:
|
||||
self.request.session.update(
|
||||
{"plugin_name": None,
|
||||
"plugin_version": None,
|
||||
"master_name": None,
|
||||
"master_id": None,
|
||||
"worker_name": None,
|
||||
"worker_id": None,
|
||||
"guide_cluster_template_name": None})
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
@ -10,9 +10,9 @@
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
|
||||
from django.core import urlresolvers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import forms
|
||||
@ -163,8 +163,13 @@ def get_security_groups(request, security_group_ids):
|
||||
|
||||
|
||||
def get_plugin_and_hadoop_version(request):
|
||||
plugin_name = request.REQUEST["plugin_name"]
|
||||
hadoop_version = request.REQUEST["hadoop_version"]
|
||||
if request.REQUEST.get("plugin_name"):
|
||||
plugin_name = request.REQUEST["plugin_name"]
|
||||
hadoop_version = request.REQUEST["hadoop_version"]
|
||||
else:
|
||||
resolver_match = urlresolvers.resolve(request.path)
|
||||
plugin_name = resolver_match.kwargs["plugin_name"]
|
||||
hadoop_version = resolver_match.kwargs["hadoop_version"]
|
||||
return (plugin_name, hadoop_version)
|
||||
|
||||
|
||||
|
@ -0,0 +1,86 @@
|
||||
# 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 import template
|
||||
from django.template import defaultfilters
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from openstack_dashboard.api import sahara as saharaclient
|
||||
from openstack_dashboard.dashboards.project.data_processing.utils \
|
||||
import helpers
|
||||
|
||||
|
||||
class ChoosePluginForm(forms.SelfHandlingForm):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(ChoosePluginForm, self).__init__(request, *args, **kwargs)
|
||||
self._generate_plugin_version_fields(request)
|
||||
self.help_text_template = ("project/data_processing.wizard/"
|
||||
"_plugin_select_help.html")
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
hlps = helpers.Helpers(request)
|
||||
hlps.reset_guide()
|
||||
plugin_name = context["plugin_name"]
|
||||
request.session["plugin_name"] = plugin_name
|
||||
request.session["plugin_version"] = (
|
||||
context[plugin_name + "_version"])
|
||||
messages.success(request, "Cluster type chosen")
|
||||
return True
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Unable to set cluster type"))
|
||||
return False
|
||||
|
||||
def _generate_plugin_version_fields(self, request):
|
||||
sahara = saharaclient.client(request)
|
||||
plugins = sahara.plugins.list()
|
||||
plugin_choices = [(plugin.name, plugin.title) for plugin in plugins]
|
||||
|
||||
self.fields["plugin_name"] = forms.ChoiceField(
|
||||
label=_("Plugin Name"),
|
||||
choices=plugin_choices,
|
||||
widget=forms.Select(attrs={"class": "switchable",
|
||||
"data-slug": "plugin"}))
|
||||
|
||||
for plugin in plugins:
|
||||
field_name = plugin.name + "_version"
|
||||
choice_field = forms.ChoiceField(
|
||||
label=_("Version"),
|
||||
required=False,
|
||||
choices=[(version, version) for version in plugin.versions],
|
||||
widget=forms.Select(
|
||||
attrs={"class": "switched",
|
||||
"data-switch-on": "plugin",
|
||||
"data-plugin-" + plugin.name: plugin.title})
|
||||
)
|
||||
self.fields[field_name] = choice_field
|
||||
|
||||
def get_help_text(self, extra_context=None):
|
||||
text = ""
|
||||
extra_context = extra_context or {}
|
||||
if self.help_text_template:
|
||||
tmpl = template.loader.get_template(self.help_text_template)
|
||||
context = template.RequestContext(self.request, extra_context)
|
||||
text += tmpl.render(context)
|
||||
else:
|
||||
text += defaultfilters.linebreaks(force_text(self.help_text))
|
||||
return defaultfilters.safe(text)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Choose plugin type and version")
|
@ -0,0 +1,27 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.project import dashboard
|
||||
|
||||
|
||||
class WizardPanel(horizon.Panel):
|
||||
name = _("Guides")
|
||||
slug = 'data_processing.wizard'
|
||||
permissions = ('openstack.services.data-processing',)
|
||||
|
||||
|
||||
dashboard.Project.register(WizardPanel)
|
@ -0,0 +1,31 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
|
||||
{% load url from future %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}create-job-binary{% endblock %}
|
||||
{% block form_action %}
|
||||
{% url 'horizon:project:data_processing.wizard:plugin_select' %}
|
||||
{% endblock %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Choose plugin and version" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="form-help-block right">
|
||||
{{ form.get_help_text }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" id="plugin_select_btn"
|
||||
type="submit" value="{% trans "Select" %}"/>
|
||||
<a href="{% url 'horizon:project:data_processing.wizard:cluster_guide' %}"
|
||||
class="btn btn-default secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -0,0 +1,5 @@
|
||||
{% load i18n horizon %}
|
||||
<p class="well">
|
||||
{% blocktrans %}Select which plugin and version that you
|
||||
want to use to create your cluster.{% endblocktrans %}
|
||||
</p>
|
@ -0,0 +1,168 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
{% block title %}{% trans "Data Processing" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Guided Cluster Creation") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="guide">
|
||||
<ol>
|
||||
<li>
|
||||
<div>
|
||||
<div>{% blocktrans %}The first step is to determine which type of
|
||||
cluster you want to run. You may have several choices
|
||||
available depending on the configuration of your system.
|
||||
Click on "choose plugin" to bring up the list of data
|
||||
processing plugins. There you will be able to choose the
|
||||
data processing plugin along with the version number.
|
||||
Choosing this up front will allow the rest of the cluster
|
||||
creation steps to focus only on options that are pertinent
|
||||
to your desired cluster type.{% endblocktrans %}
|
||||
</div>
|
||||
<a class="btn btn-default btn-small btn-create btn-inline ajax-modal" href="
|
||||
{% url 'horizon:project:data_processing.wizard:plugin_select' %}">{% trans "Choose plugin" %}</a>
|
||||
<div>{% trans "Current choice:" %}
|
||||
{% if request.session.plugin_name and request.session.plugin_version %}
|
||||
<span class="text-success">
|
||||
{% trans "Plugin:" %}
|
||||
{{ request.session.plugin_name }}
|
||||
{% trans "Version:" %}
|
||||
{{ request.session.plugin_version }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
{% trans "No plugin chosen" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<hr/>
|
||||
<li>
|
||||
<div>
|
||||
<div>{% blocktrans %}Next, you need to define the different
|
||||
types of machines in your cluster. This is done by
|
||||
defining a Node Group Template for each type of
|
||||
machine. A very common case is where you
|
||||
need to have one or more machines running a "master"
|
||||
set of processes while another set of machines need
|
||||
to be running the "worker" processes. Here,
|
||||
you will define the Node Group Template for your
|
||||
"master" node(s).
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<div>
|
||||
<a class="btn btn-default btn-small btn-create btn-inline ajax-modal
|
||||
{% if not request.session.plugin_name or not request.session.plugin_version %} disabled {% endif %}"
|
||||
href="{% if request.session.plugin_name and request.session.plugin_version %}{% url 'horizon:project:data_processing.nodegroup_templates:configure-nodegroup-template-defaults' request.session.plugin_name request.session.plugin_version 'master' %}{% endif %}">
|
||||
<span class="fa fa-plus"></span> {% trans "Create a Master Node Group Template" %}</a>
|
||||
</div>
|
||||
<div>{% trans "Current choice:" %}
|
||||
{% if request.session.master_name %}
|
||||
<span class="text-success">
|
||||
{% trans "Master Node Group Template:" %}
|
||||
{{ request.session.master_name }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
{% trans "No Master Node Group Template Created" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<hr/>
|
||||
<li>
|
||||
<div>
|
||||
<div>{% blocktrans %}Repeat the Node Group Template
|
||||
creation process, but this time you are creating
|
||||
your "worker" Node Group Template.{% endblocktrans %}
|
||||
</div>
|
||||
<div>
|
||||
<a class="btn btn-default btn-small btn-create btn-inline ajax-modal
|
||||
{% if not request.session.master_name %} disabled{% endif %}"
|
||||
href="{% if request.session.plugin_name and request.session.plugin_version %}{% url 'horizon:project:data_processing.nodegroup_templates:configure-nodegroup-template-defaults' request.session.plugin_name request.session.plugin_version 'worker' %}{% endif %}">
|
||||
<span class="fa fa-plus"></span> {% trans "Create a Worker Node Group Template" %}</a>
|
||||
</div>
|
||||
<div>{% trans "Current choice:" %}
|
||||
{% if request.session.worker_name %}
|
||||
<span class="text-success">
|
||||
{% trans "Worker Node Group Template:" %}
|
||||
{{ request.session.worker_name }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
{% trans "No Worker Node Group Template Created" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<hr/>
|
||||
<li>
|
||||
<div>
|
||||
<div>{% blocktrans %}Now you need to set the layout of your
|
||||
cluster. By
|
||||
creating a Cluster Template, you will be choosing the
|
||||
number of instances of each Node Group Template that
|
||||
will appear in your cluster. Additionally,
|
||||
you will have a chance to set any cluster-specific
|
||||
configuration items in the additional tabs on the
|
||||
create Cluster Template form.{% endblocktrans %}
|
||||
</div>
|
||||
<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">
|
||||
<span class="fa fa-plus"></span> {% trans "Create a Cluster Template" %}</a>
|
||||
</div>
|
||||
<div>{% trans "Current choice:" %}
|
||||
{% if request.session.guide_cluster_template_name %}
|
||||
<span class="text-success">
|
||||
{% trans "Worker Node Group Template:" %}
|
||||
{{ request.session.guide_cluster_template_name }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-danger">
|
||||
{% trans "No Cluster Template Created" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<hr/>
|
||||
<li>
|
||||
<div>
|
||||
<div>{% blocktrans %}You are now ready to
|
||||
launch your cluster. When you click on the link
|
||||
below, you will need to give your cluster a name,
|
||||
choose the Cluster Template to use and choose which
|
||||
image to use to build your instances. After you
|
||||
click on "Create", your instances will begin to
|
||||
spawn. Your cluster should be operational in a few
|
||||
minutes.{% endblocktrans %}
|
||||
</div>
|
||||
<div>
|
||||
<a class="btn btn-default btn-small btn-create btn-inline ajax-modal
|
||||
{% if not request.session.guide_cluster_template_name %} disabled{% endif %}"
|
||||
href="{% if request.session.guide_cluster_template_name %}{% url 'horizon:project:data_processing.clusters:configure-cluster' request.session.plugin_name request.session.plugin_version request.session.guide_cluster_template_name%}{% endif %}">
|
||||
<span class="fa fa-plus"></span> {% trans "Launch a Cluster" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<div>
|
||||
<a id="reset_cluster_guide_btn"
|
||||
class=" btn btn-default btn-small btn-create btn-inline btn-danger"
|
||||
title="{% trans "Reset Cluster Guide" %}"
|
||||
href="{% url 'horizon:project:data_processing.wizard:reset_cluster_guide' True %}">
|
||||
{% trans "Reset Cluster Creation Guide" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Choose plugin and version" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Choose plugin and version") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'project/data_processing.wizard/_plugin_select.html' %}
|
||||
{% endblock %}
|
@ -0,0 +1,38 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
{% block title %}{% trans "Data Processing" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Data Processing Guides") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="guide">
|
||||
<ul>
|
||||
<li>
|
||||
<div>
|
||||
<div>{% blocktrans %}
|
||||
Each of the Data Processing frameworks require a cluster of machines
|
||||
in order to do the work they are assigned. A cluster is
|
||||
formed by creating a set of Node Group Templates, combining
|
||||
those into a Cluster Template and then launching a Cluster.
|
||||
You can do each of those steps manually, or you can follow
|
||||
this guide to help take you through the steps of
|
||||
Cluster creation.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<div>
|
||||
<a id="cluster_guide_btn"
|
||||
class=" btn btn-default btn-small btn-create btn-inline"
|
||||
title="{% trans "Cluster Guide" %}"
|
||||
href="{% url 'horizon:project:data_processing.wizard:cluster_guide' %}">
|
||||
{% trans "Cluster Creation Guide" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
@ -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 django.core.urlresolvers import reverse
|
||||
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
INDEX_URL = reverse(
|
||||
'horizon:project:data_processing.wizard:index')
|
||||
CLUSTER_GUIDE_URL = reverse(
|
||||
'horizon:project:data_processing.wizard:cluster_guide')
|
||||
CLUSTER_GUIDE_RESET_URL = reverse(
|
||||
'horizon:project:data_processing.wizard:reset_cluster_guide',
|
||||
kwargs={"reset_cluster_guide": "true"})
|
||||
|
||||
|
||||
class DataProcessingClusterGuideTests(test.TestCase):
|
||||
def test_index(self):
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/data_processing.wizard/wizard.html')
|
||||
self.assertContains(res, 'Data Processing Guides')
|
||||
self.assertContains(res, 'Cluster Creation Guide')
|
||||
|
||||
def test_cluster_guide(self):
|
||||
res = self.client.get(CLUSTER_GUIDE_URL)
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/data_processing.wizard/cluster_guide.html')
|
||||
self.assertContains(res, 'Guided Cluster Creation')
|
||||
self.assertContains(res, 'Current choice')
|
||||
|
||||
def test_cluster_guide_reset(self):
|
||||
res = self.client.get(CLUSTER_GUIDE_RESET_URL)
|
||||
self.assertRedirectsNoFollow(res, CLUSTER_GUIDE_URL)
|
@ -0,0 +1,32 @@
|
||||
# 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
|
||||
|
||||
from openstack_dashboard.dashboards.project. \
|
||||
data_processing.wizard import views
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', views.WizardView.as_view(), name='index'),
|
||||
url(r'^cluster_guide$',
|
||||
views.ClusterGuideView.as_view(),
|
||||
name='cluster_guide'),
|
||||
url(r'^cluster_guide/(?P<reset_cluster_guide>[^/]+)/$',
|
||||
views.ResetClusterGuideView.as_view(),
|
||||
name='reset_cluster_guide'),
|
||||
url(r'^plugin_select$',
|
||||
views.PluginSelectView.as_view(),
|
||||
name='plugin_select'),
|
||||
)
|
@ -0,0 +1,64 @@
|
||||
# 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 django import http
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views import generic
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import views as horizon_views
|
||||
|
||||
from openstack_dashboard.dashboards.project.data_processing.utils \
|
||||
import helpers
|
||||
import openstack_dashboard.dashboards.project.data_processing.wizard \
|
||||
.forms as wizforms
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WizardView(horizon_views.APIView):
|
||||
template_name = 'project/data_processing.wizard/wizard.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
try:
|
||||
context["test"] = "test data"
|
||||
except Exception:
|
||||
msg = _('Unable show guides.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return context
|
||||
|
||||
|
||||
class ClusterGuideView(horizon_views.APIView):
|
||||
template_name = 'project/data_processing.wizard/cluster_guide.html'
|
||||
|
||||
|
||||
class ResetClusterGuideView(generic.RedirectView):
|
||||
pattern_name = 'horizon:project:data_processing.wizard:cluster_guide'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if kwargs["reset_cluster_guide"]:
|
||||
hlps = helpers.Helpers(request)
|
||||
hlps.reset_guide()
|
||||
return http.HttpResponseRedirect(reverse_lazy(self.pattern_name))
|
||||
|
||||
|
||||
class PluginSelectView(forms.ModalFormView):
|
||||
form_class = wizforms.ChoosePluginForm
|
||||
success_url = reverse_lazy(
|
||||
'horizon:project:data_processing.wizard:cluster_guide')
|
||||
classes = ("ajax-modal")
|
||||
template_name = "project/data_processing.wizard/plugin_select.html"
|
Loading…
Reference in New Issue
Block a user