UI for Cluster Scaling implementation.
Scaling form added. Rest client updated. Waiting for backend to be merged. Change-Id: Ia64680cc8404e3b8b89818a805b6a4932f6d539a
This commit is contained in:
parent
92c9a55587
commit
a50990ba79
@ -18,7 +18,7 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
LOG = logging.Logger(__name__)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Resource(object):
|
||||
@ -73,6 +73,13 @@ class ResourceManager(object):
|
||||
raise RuntimeError('Unable to create %s, server returned code %s' %
|
||||
(resource_name, resp.status_code))
|
||||
|
||||
def _update(self, url, data):
|
||||
resp = self.api.client.put(url, json.dumps(data))
|
||||
if resp.status_code != 202:
|
||||
resource_name = self.resource_class.resource_name
|
||||
raise RuntimeError('Unable to update %s, server returned code %s' %
|
||||
(resource_name, resp.status_code))
|
||||
|
||||
def _list(self, url, response_key):
|
||||
resp = self.api.client.get(url)
|
||||
|
||||
|
@ -64,6 +64,9 @@ class ClusterManager(base.ResourceManager):
|
||||
|
||||
self._create('/clusters', data)
|
||||
|
||||
def scale(self, cluster_id, scale_object):
|
||||
return self._put('/clusters/%s' % cluster_id, scale_object)
|
||||
|
||||
def list(self):
|
||||
return self._list('/clusters', 'clusters')
|
||||
|
||||
|
@ -32,6 +32,11 @@ class HTTPClient(object):
|
||||
headers={'x-auth-token': self.token,
|
||||
'content-type': 'application/json'})
|
||||
|
||||
def put(self, url, body):
|
||||
return requests.put(self.base_url + url, body,
|
||||
headers={'x-auth-token': self.token,
|
||||
'content-type': 'application/json'})
|
||||
|
||||
def delete(self, url):
|
||||
return requests.delete(self.base_url + url,
|
||||
headers={'x-auth-token': self.token})
|
||||
|
@ -18,10 +18,10 @@
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from horizon import forms
|
||||
|
||||
from savannadashboard.api import client as savannaclient
|
||||
import savannadashboard.cluster_templates.workflows.create as create_flow
|
||||
from savannadashboard.utils.workflow_helpers import build_node_group_fields
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -63,23 +63,13 @@ class CopyClusterTemplate(create_flow.ConfigureClusterTemplate):
|
||||
{"name": templ_ng["name"],
|
||||
"template_id": templ_ng["node_group_template_id"],
|
||||
"count": templ_ng["count"],
|
||||
"id": id})
|
||||
"id": id,
|
||||
"deletable": "true"})
|
||||
|
||||
ng_action.fields[group_name] = forms.CharField(
|
||||
label=_("Name"),
|
||||
required=True,
|
||||
widget=forms.TextInput())
|
||||
|
||||
ng_action.fields[template_id] = forms.CharField(
|
||||
label=_("Node group template"),
|
||||
required=True,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
ng_action.fields[count] = forms.IntegerField(
|
||||
label=_("Count"),
|
||||
required=True,
|
||||
min_value=1,
|
||||
widget=forms.HiddenInput())
|
||||
build_node_group_fields(ng_action,
|
||||
group_name,
|
||||
template_id,
|
||||
count)
|
||||
|
||||
elif isinstance(step, create_flow.GeneralConfig):
|
||||
fields = step.action.fields
|
||||
|
@ -174,6 +174,8 @@ class ConfigureNodegroupsAction(workflows.Action):
|
||||
plugin_name=plugin,
|
||||
hadoop_version=hadoop_version)
|
||||
|
||||
deletable = request.REQUEST.get("deletable", dict())
|
||||
|
||||
if 'forms_ids' in request.POST:
|
||||
self.groups = []
|
||||
for id in json.loads(request.POST['forms_ids']):
|
||||
@ -183,23 +185,14 @@ class ConfigureNodegroupsAction(workflows.Action):
|
||||
self.groups.append({"name": request.POST[group_name],
|
||||
"template_id": request.POST[template_id],
|
||||
"count": request.POST[count],
|
||||
"id": id})
|
||||
"id": id,
|
||||
"deletable": deletable.get(
|
||||
request.POST[group_name], "true")})
|
||||
|
||||
self.fields[group_name] = forms.CharField(
|
||||
label=_("Name"),
|
||||
required=True,
|
||||
widget=forms.TextInput())
|
||||
|
||||
self.fields[template_id] = forms.CharField(
|
||||
label=_("Node group template"),
|
||||
required=True,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
self.fields[count] = forms.IntegerField(
|
||||
label=_("Count"),
|
||||
required=True,
|
||||
min_value=1,
|
||||
widget=forms.HiddenInput())
|
||||
whelpers.build_node_group_fields(self,
|
||||
group_name,
|
||||
template_id,
|
||||
count)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(ConfigureNodegroupsAction, self).clean()
|
||||
|
@ -1,29 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
|
||||
|
||||
class CreateClusterForm(forms.SelfHandlingForm):
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
#TODO(akuznetsov): launch cluster
|
||||
return True
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
return False
|
@ -33,6 +33,16 @@ class CreateCluster(tables.LinkAction):
|
||||
classes = ("btn-launch", "ajax-modal")
|
||||
|
||||
|
||||
class ScaleCluster(tables.LinkAction):
|
||||
name = "scale"
|
||||
verbose_name = _("Scale Cluster")
|
||||
url = "horizon:savanna:clusters:scale"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
def allowed(self, request, cluster=None):
|
||||
return cluster.status == "Active"
|
||||
|
||||
|
||||
class DeleteCluster(tables.BatchAction):
|
||||
name = "delete"
|
||||
action_present = _("Delete")
|
||||
@ -92,4 +102,5 @@ class ClustersTable(tables.DataTable):
|
||||
table_actions = (CreateCluster,
|
||||
ConfigureCluster,
|
||||
DeleteCluster)
|
||||
row_actions = (DeleteCluster,)
|
||||
row_actions = (ScaleCluster,
|
||||
DeleteCluster,)
|
||||
|
@ -34,4 +34,7 @@ urlpatterns = patterns('',
|
||||
name='configure-cluster'),
|
||||
url(r'^(?P<cluster_id>[^/]+)$',
|
||||
views.ClusterDetailsView.as_view(),
|
||||
name='details'))
|
||||
name='details'),
|
||||
url(r'^(?P<cluster_id>[^/]+)/scale$',
|
||||
views.ScaleClusterView.as_view(),
|
||||
name='scale'))
|
||||
|
@ -25,9 +25,8 @@ from savannadashboard.api import client as savannaclient
|
||||
|
||||
from savannadashboard.clusters.tables import ClustersTable
|
||||
import savannadashboard.clusters.tabs as _tabs
|
||||
from savannadashboard.clusters.workflows import ConfigureCluster
|
||||
from savannadashboard.clusters.workflows import CreateCluster
|
||||
|
||||
import savannadashboard.clusters.workflows.create as create_flow
|
||||
import savannadashboard.clusters.workflows.scale as scale_flow
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -56,7 +55,7 @@ class ClusterDetailsView(tabs.TabView):
|
||||
|
||||
|
||||
class CreateClusterView(workflows.WorkflowView):
|
||||
workflow_class = CreateCluster
|
||||
workflow_class = create_flow.CreateCluster
|
||||
success_url = \
|
||||
"horizon:savanna:clusters:create-cluster"
|
||||
classes = ("ajax-modal")
|
||||
@ -64,6 +63,33 @@ class CreateClusterView(workflows.WorkflowView):
|
||||
|
||||
|
||||
class ConfigureClusterView(workflows.WorkflowView):
|
||||
workflow_class = ConfigureCluster
|
||||
workflow_class = create_flow.ConfigureCluster
|
||||
success_url = "horizon:savanna:clusters"
|
||||
template_name = "clusters/configure.html"
|
||||
|
||||
|
||||
class ScaleClusterView(workflows.WorkflowView):
|
||||
workflow_class = scale_flow.ScaleCluster
|
||||
success_url = "horizon:savanna:clusters"
|
||||
classes = ("ajax-modal")
|
||||
template_name = "clusters/scale.html"
|
||||
|
||||
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']
|
||||
savanna = savannaclient.Client(self.request)
|
||||
template = savanna.cluster_templates.get(template_id)
|
||||
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
|
||||
|
0
savannadashboard/clusters/workflows/__init__.py
Normal file
0
savannadashboard/clusters/workflows/__init__.py
Normal file
@ -164,25 +164,6 @@ class ConfigureCluster(workflows.Workflow):
|
||||
success_url = "horizon:savanna:clusters:index"
|
||||
default_steps = (GeneralConfig, )
|
||||
|
||||
def is_valid(self):
|
||||
if self.context["general_hidden_configure_field"] \
|
||||
== "create_nodegroup":
|
||||
return False
|
||||
missing = self.depends_on - set(self.context.keys())
|
||||
if missing:
|
||||
raise exceptions.WorkflowValidationError(
|
||||
"Unable to complete the workflow. The values %s are "
|
||||
"required but not present." % ", ".join(missing))
|
||||
|
||||
steps_valid = True
|
||||
for step in self.steps:
|
||||
if not step.action.is_valid():
|
||||
steps_valid = False
|
||||
step.has_errors = True
|
||||
if not steps_valid:
|
||||
return steps_valid
|
||||
return self.validate(self.context)
|
||||
|
||||
def format_status_message(self, message):
|
||||
return message % self.context["general_cluster_name"]
|
||||
|
149
savannadashboard/clusters/workflows/scale.py
Normal file
149
savannadashboard/clusters/workflows/scale.py
Normal file
@ -0,0 +1,149 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from horizon import exceptions
|
||||
|
||||
from savannadashboard.api import client as savannaclient
|
||||
|
||||
import savannadashboard.cluster_templates.workflows.create as clt_create_flow
|
||||
import savannadashboard.clusters.workflows.create as cl_create_flow
|
||||
from savannadashboard.utils.workflow_helpers import build_node_group_fields
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NodeGroupsStep(clt_create_flow.ConfigureNodegroups):
|
||||
pass
|
||||
|
||||
|
||||
class ScaleCluster(cl_create_flow.ConfigureCluster):
|
||||
slug = "scale_cluster"
|
||||
name = _("Scale Cluster")
|
||||
finalize_button_name = _("Scale")
|
||||
success_url = "horizon:savanna:clusters:index"
|
||||
default_steps = (NodeGroupsStep, )
|
||||
|
||||
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
|
||||
ScaleCluster._cls_registry = set([])
|
||||
|
||||
savanna = savannaclient.Client(request)
|
||||
|
||||
cluster_id = context_seed["cluster_id"]
|
||||
cluster = savanna.clusters.get(cluster_id)
|
||||
|
||||
self.success_message = "Scaling cluster started for %s" % cluster.name
|
||||
self.failure_message = "Could not start scaling for %s" % cluster.name
|
||||
|
||||
plugin = cluster.plugin_name
|
||||
hadoop_version = cluster.hadoop_version
|
||||
|
||||
#init deletable nodegroups
|
||||
deletable = dict()
|
||||
for group in cluster.node_groups:
|
||||
deletable[group["name"]] = "false"
|
||||
|
||||
request.GET = request.GET.copy()
|
||||
request.GET.update({"cluster_id": cluster_id})
|
||||
request.GET.update({"plugin_name": plugin})
|
||||
request.GET.update({"hadoop_version": hadoop_version})
|
||||
request.GET.update({"deletable": deletable})
|
||||
|
||||
super(ScaleCluster, self).__init__(request, context_seed,
|
||||
entry_point, *args,
|
||||
**kwargs)
|
||||
|
||||
#init Node Groups
|
||||
|
||||
for step in self.steps:
|
||||
if isinstance(step, clt_create_flow.ConfigureNodegroups):
|
||||
ng_action = step.action
|
||||
template_ngs = cluster.node_groups
|
||||
|
||||
if 'forms_ids' not in request.POST:
|
||||
ng_action.groups = []
|
||||
for id in range(0, len(template_ngs), 1):
|
||||
group_name = "group_name_" + str(id)
|
||||
template_id = "template_id_" + str(id)
|
||||
count = "count_" + str(id)
|
||||
templ_ng = template_ngs[id]
|
||||
ng_action.groups.append(
|
||||
{"name": templ_ng["name"],
|
||||
"template_id": templ_ng["node_group_template_id"],
|
||||
"count": templ_ng["count"],
|
||||
"id": id,
|
||||
"deletable": "false"})
|
||||
|
||||
build_node_group_fields(ng_action,
|
||||
group_name,
|
||||
template_id,
|
||||
count)
|
||||
|
||||
def format_status_message(self, message):
|
||||
return message
|
||||
|
||||
def handle(self, request, context):
|
||||
savanna = savannaclient.Client(request)
|
||||
cluster_id = request.GET["cluster_id"]
|
||||
cluster = savanna.clusters.get(cluster_id)
|
||||
|
||||
existing_node_groups = set([])
|
||||
for ng in cluster.node_groups:
|
||||
existing_node_groups.add(ng["name"])
|
||||
|
||||
scale_object = dict()
|
||||
|
||||
ids = json.loads(context["ng_forms_ids"])
|
||||
|
||||
for _id in ids:
|
||||
name = context["ng_group_name_%s" % _id]
|
||||
template_id = context["ng_template_id_%s" % _id]
|
||||
count = context["ng_count_%s" % _id]
|
||||
|
||||
if name not in existing_node_groups:
|
||||
if "add_node_groups" not in scale_object:
|
||||
scale_object["add_node_groups"] = []
|
||||
|
||||
scale_object["add_node_groups"].append(
|
||||
{"name": name,
|
||||
"node_group_template_id": template_id,
|
||||
"count": int(count)})
|
||||
else:
|
||||
old_count = None
|
||||
for ng in cluster.node_groups:
|
||||
if name == ng["name"]:
|
||||
old_count = ng["count"]
|
||||
break
|
||||
|
||||
if old_count != count:
|
||||
if "resize_node_groups" not in scale_object:
|
||||
scale_object["resize_node_groups"] = []
|
||||
|
||||
scale_object["resize_node_groups"].append(
|
||||
{"name": name,
|
||||
"count": int(count)}
|
||||
)
|
||||
try:
|
||||
savanna.clusters.scale(cluster_id, scale_object)
|
||||
return True
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
return False
|
@ -1,22 +1,30 @@
|
||||
<script>
|
||||
var template = '<tr id_attr="$id"><td>' +
|
||||
'<div class="input control-group" style="padding-right:15px;">' +
|
||||
'<input id="template_id_$id" value="$template_id" type="hidden" name="template_id_$id">' +
|
||||
'<input id="group_name_$id" value="$group_name" type="text" name="group_name_$id">' +
|
||||
'<div class="input control-group" style="padding-right:15px;">' +
|
||||
'<input id="template_id_$id" value="$template_id" type="hidden" name="template_id_$id">' +
|
||||
'<input id="group_name_$id" value="$group_name" type="text" name="group_name_$id" class="input-medium">' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div class="input control-group" style="padding-right:15px;"><input disabled value="$template_name" class="input-medium" /></div>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div class="input control-group btn-group input-append" style="float:left;padding-right:5px;">' +
|
||||
'<input id="count_$id" class="count-field" value="$node_count" type="text" max="4" maxlength="4" name="count_$id" size="4" style="width:50px">' +
|
||||
'<div class="btn dec-btn" data-count-id="count_$id"><i class="icon-minus"></i></div>' +
|
||||
'<div class="btn inc-btn" data-count-id="count_$id"><i class="icon-plus"></i></div>' +
|
||||
'</div>' +
|
||||
'<div class="input" style="float:left">' +
|
||||
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div class="input" style="padding-bottom:7px;padding-right:15px;">$template_name</div>' +
|
||||
'<div class="input control-group" style="float:left;padding-right:5px;">' +
|
||||
'<input type="button" class="btn btn-danger" id="delete_btn_$id" data-toggle="dropdown" onclick="delete_node_group(this)" value="Remove" style="margin-bottom: 10px"/>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<div class="input control-group" style="float:left;padding-right:5px;">' +
|
||||
'<input id="count_$id" value="$node_count" type="text" max="4" maxlength="4" name="count_$id" size="4" style="width:50px">' +
|
||||
'</div>' +
|
||||
'<div class="input" style="float:left">' +
|
||||
'<button class="btn btn-danger" data-toggle="dropdown" onclick="delete_node_group(this)">-</button>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
|
||||
'</tr>';
|
||||
|
||||
function mark_element_as_wrong(id){
|
||||
$("#"+id).parent("div").addClass("error");
|
||||
@ -38,7 +46,7 @@
|
||||
$("#forms_ids").val(JSON.stringify(ids));
|
||||
}
|
||||
|
||||
function add_node(node_count, group_name, template_id, id) {
|
||||
function add_node(node_count, group_name, template_id, id, deletable) {
|
||||
var template_name = $("select option[value='" + template_id + "']").html();
|
||||
var tmp = template.
|
||||
replace(/\$id/g, id).
|
||||
@ -47,6 +55,10 @@
|
||||
replace(/\$node_count/g, node_count).
|
||||
replace(/\$template_name/g, template_name);
|
||||
$("#node-templates tbody").append(tmp);
|
||||
if (!deletable) {
|
||||
$("#delete_btn_" + id).remove();
|
||||
$("#group_name_" + id).prop('readonly', true);
|
||||
}
|
||||
$("#node-templates").show();
|
||||
set_nodes_ids();
|
||||
}
|
||||
@ -57,7 +69,8 @@
|
||||
}
|
||||
var template_id = $("#template_id option:selected").val();
|
||||
var template_name = $("#template_id option:selected").html();
|
||||
add_node(node_count, template_name, template_id, get_next_id());
|
||||
add_node(node_count, template_name, template_id, get_next_id(), true);
|
||||
$(".count-field").change();
|
||||
}
|
||||
function delete_node_group(el) {
|
||||
var tr = $(el).parents("tr")[0];
|
||||
@ -104,9 +117,38 @@
|
||||
<script>
|
||||
|
||||
{% for group in form.groups %}
|
||||
add_node("{{ group.count }}", "{{ group.name }}", "{{ group.template_id }}", "{{ group.id }}");
|
||||
add_node("{{ group.count }}", "{{ group.name }}", "{{ group.template_id }}", "{{ group.id }}", {{ group.deletable }});
|
||||
{% endfor %}
|
||||
{% for field_id in form.errors_fields %}
|
||||
mark_element_as_wrong("{{ field_id }}");
|
||||
{% endfor %}
|
||||
|
||||
var handlers_registred;
|
||||
$(function() {
|
||||
if (!handlers_registred) {
|
||||
handlers_registred = true;
|
||||
$(".inc-btn").live("click", function(e) {
|
||||
var id = $(this).attr("data-count-id");
|
||||
$("#" + id).val(parseInt($("#" + id).val()) + 1);
|
||||
$(".count-field").change();
|
||||
});
|
||||
$(".dec-btn").live("click", function(e) {
|
||||
var id = $(this).attr("data-count-id");
|
||||
var val = parseInt($("#" + id).val());
|
||||
if (val > 1) {
|
||||
$("#" + id).val(val - 1);
|
||||
}
|
||||
$(".count-field").change();
|
||||
});
|
||||
}
|
||||
|
||||
$(".count-field").live("change", function() {
|
||||
var val = $(this).val();
|
||||
if (val > 1) {
|
||||
$(this).parent("div").find(".dec-btn").removeClass("disabled");
|
||||
} else {
|
||||
$(this).parent("div").find(".dec-btn").addClass("disabled");
|
||||
}
|
||||
}).change();
|
||||
});
|
||||
</script>
|
@ -28,32 +28,6 @@
|
||||
form.submit();
|
||||
});
|
||||
|
||||
var actions_table = $(".hidden_nodegroups_field").parent();
|
||||
actions_table.attr("style", "width: 100%");
|
||||
actions_table.find(".control-group").attr("style", "width: 30%; display: inline-block");
|
||||
actions_table.find(".input").children().attr("style", "width: 85%");
|
||||
actions_table.find(".count-field").closest(".control-group")
|
||||
.after("<div style='display: inline-block' class='control-group'><div class='input'><span class='btn btn-small btn-danger ng-remove-btn'><i class='icon-remove-sign'>   </i></span></div></div>");
|
||||
|
||||
$(".ng-remove-btn").on("click", function(event) {
|
||||
var div = $(this).closest(".control-group");
|
||||
div.hide();
|
||||
//count
|
||||
var count = div.prev();
|
||||
count.hide();
|
||||
//template
|
||||
var template = div.prev().prev();
|
||||
template.hide();
|
||||
//name
|
||||
var name = div.prev().prev().prev();
|
||||
name.hide();
|
||||
|
||||
var idx = count.find("input").attr("data-count-idx");
|
||||
console.log(idx);
|
||||
$(".hidden_to_delete_field").val($(".hidden_to_delete_field").val() + "," + idx);
|
||||
|
||||
});
|
||||
|
||||
|
||||
$(".hidden_nodegroups_field").val("");
|
||||
$(".hidden_configure_field").val("");
|
||||
|
11
savannadashboard/templates/clusters/scale.html
Normal file
11
savannadashboard/templates/clusters/scale.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Scale Cluster" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Savanna - Scale Cluster") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'horizon/common/_workflow.html' %}
|
||||
{% endblock %}<!DOCTYPE html>
|
@ -103,6 +103,24 @@ def _create_step_action(name, title, parameters, advanced_fields=None,
|
||||
return step
|
||||
|
||||
|
||||
def build_node_group_fields(action, name, template, count):
|
||||
action.fields[name] = forms.CharField(
|
||||
label=_("Name"),
|
||||
required=True,
|
||||
widget=forms.TextInput())
|
||||
|
||||
action.fields[template] = forms.CharField(
|
||||
label=_("Node group cluster"),
|
||||
required=True,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
action.fields[count] = forms.IntegerField(
|
||||
label=_("Count"),
|
||||
required=True,
|
||||
min_value=1,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
|
||||
def parse_configs_from_context(context, defaults):
|
||||
configs_dict = dict()
|
||||
for key, val in context.items():
|
||||
|
Loading…
Reference in New Issue
Block a user