diff --git a/tuskar_ui/infrastructure/roles/templates/roles/info.html b/tuskar_ui/infrastructure/roles/templates/roles/info.html
new file mode 100644
index 000000000..2909bd1cf
--- /dev/null
+++ b/tuskar_ui/infrastructure/roles/templates/roles/info.html
@@ -0,0 +1,11 @@
+
+
+
+
+ {% include "horizon/common/_horizontal_fields.html" %}
+
+
+
+ {{ step.get_help_text }}
+
+
diff --git a/tuskar_ui/infrastructure/roles/tests.py b/tuskar_ui/infrastructure/roles/tests.py
index ec653ef4e..5fd2aeb0e 100644
--- a/tuskar_ui/infrastructure/roles/tests.py
+++ b/tuskar_ui/infrastructure/roles/tests.py
@@ -134,8 +134,11 @@ class RolesTest(test.BaseAdminViewTests):
images = self.glanceclient_images.list()
data = {
+ 'name': 'controller',
+ 'description': 'The controller node role.',
'flavor': self.novaclient_flavors.first().name,
'image': self.glanceclient_images.first().id,
+ 'nodes': '0',
}
with contextlib.nested(
diff --git a/tuskar_ui/infrastructure/roles/views.py b/tuskar_ui/infrastructure/roles/views.py
index 726ce9fb0..7ff6068d1 100644
--- a/tuskar_ui/infrastructure/roles/views.py
+++ b/tuskar_ui/infrastructure/roles/views.py
@@ -14,12 +14,10 @@
import json
from django.core.urlresolvers import reverse
-from django.core.urlresolvers import reverse_lazy
from django import http
from django.utils.translation import ugettext_lazy as _
from django.views.generic import base
from glanceclient import exc as glance_exc
-from horizon import exceptions as horizon_exceptions
from horizon import tables as horizon_tables
from horizon import utils
from horizon import workflows
@@ -127,20 +125,19 @@ class DetailView(horizon_tables.DataTableView, views.RoleMixin,
return context
-class UpdateView(workflows.WorkflowView):
+class UpdateView(workflows.WorkflowView, views.StackMixin, views.RoleMixin):
workflow_class = role_workflows.UpdateRole
def get_initial(self):
- role_id = self.kwargs['role_id']
+ plan = self.get_plan()
+ role = self.get_role()
- try:
- # Get initial role information
- plan = api.tuskar.Plan.get_the_plan(self.request)
- role = api.tuskar.Role.get(self.request, role_id)
- except Exception:
- horizon_exceptions.handle(self.request,
- _('Unable to retrieve role details.'),
- redirect=reverse_lazy(INDEX_URL))
+ stack = self.get_stack()
+ if stack:
+ resources = stack.resources(role=role, with_joins=True)
+ role_nodes = len(resources)
+ else:
+ role_nodes = 0
role_flavor = role.flavor(plan)
role_flavor = '' if role_flavor is None else role_flavor.name
@@ -148,11 +145,18 @@ class UpdateView(workflows.WorkflowView):
role_image = role.image(plan)
role_image = '' if role_image is None else role_image.id
- return {'role_id': role.id,
- 'name': role.name,
- 'flavor': role_flavor,
- 'image': role_image,
- }
+ free_nodes = len(api.node.Node.list(self.request, associated=False,
+ maintenance=False))
+ available_nodes = role_nodes + free_nodes
+
+ return {
+ 'role_id': role.id,
+ 'name': role.name,
+ 'flavor': role_flavor,
+ 'image': role_image,
+ 'nodes': role_nodes,
+ 'available_nodes': available_nodes,
+ }
class PerformanceView(base.TemplateView, views.RoleMixin, views.StackMixin):
diff --git a/tuskar_ui/infrastructure/roles/workflows.py b/tuskar_ui/infrastructure/roles/workflows.py
index 089f06cdf..2a138c108 100644
--- a/tuskar_ui/infrastructure/roles/workflows.py
+++ b/tuskar_ui/infrastructure/roles/workflows.py
@@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.core.urlresolvers import reverse_lazy
+import django.forms
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
@@ -26,25 +27,28 @@ from tuskar_ui.utils import utils as tuskar_utils
class UpdateRoleInfoAction(workflows.Action):
+ # TODO(rdopiera) Make the name and description editable.
name = forms.CharField(
label=_("Name"),
- widget=tuskar_ui.forms.LabelWidget(),
required=False,
+ widget=tuskar_ui.forms.StaticTextWidget
)
-
description = forms.CharField(
label=_("Description"),
- widget=tuskar_ui.forms.LabelWidget(),
required=False,
+ widget=tuskar_ui.forms.StaticTextWidget
)
-
flavor = forms.ChoiceField(
label=_("Flavor"),
)
-
image = forms.ChoiceField(
label=_("Image"),
)
+ nodes = forms.IntegerField(
+ label=_("Number of Nodes"),
+ required=False,
+ initial=0,
+ )
class Meta(object):
name = _("Overall Settings")
@@ -54,6 +58,13 @@ class UpdateRoleInfoAction(workflows.Action):
def __init__(self, request, context, *args, **kwargs):
super(UpdateRoleInfoAction, self).__init__(request, context, *args,
**kwargs)
+ self.available_nodes = context['available_nodes']
+ self.fields['nodes'].widget = tuskar_ui.forms.NumberInput(attrs={
+ 'min': 0,
+ 'max': self.available_nodes,
+ })
+ self.fields['nodes'].help_text = _(
+ "{0} nodes available").format(self.available_nodes)
if not utils.matching_deployment_mode():
del self.fields['flavor']
@@ -70,6 +81,24 @@ class UpdateRoleInfoAction(workflows.Action):
choices = [(i.id, i.name) for i in images]
return [('', _('Unknown'))] + choices
+ def clean_nodes(self):
+ new_count = int(self.cleaned_data['nodes'] or 0)
+ if new_count > self.available_nodes:
+ raise django.forms.ValidationError(_(
+ "There are only {0} nodes available "
+ "for the selected flavor."
+ ).format(self.available_nodes))
+ return str(new_count)
+
+ def handle(self, request, context):
+ return {
+ 'name': self.cleaned_data['name'],
+ 'description': self.cleaned_data['description'],
+ 'flavor': self.cleaned_data.get('flavor'),
+ 'image': self.cleaned_data['image'],
+ 'nodes': self.cleaned_data['nodes'],
+ }
+
class UpdateRoleConfigAction(workflows.Action):
class Meta(object):
@@ -92,8 +121,9 @@ class UpdateRoleConfigAction(workflows.Action):
class UpdateRoleInfo(workflows.Step):
action_class = UpdateRoleInfoAction
- depends_on = ("role_id",)
- contributes = ("name", "flavor", "image",)
+ depends_on = ("role_id", "available_nodes")
+ contributes = ("name", "description", "flavor", "image", "nodes")
+ template_name = 'infrastructure/roles/info.html'
class UpdateRoleConfig(workflows.Step):
@@ -141,9 +171,10 @@ class UpdateRole(workflows.Workflow):
parameters = data['parameters']
parameters[role.image_id_parameter_name] = data['image']
+ parameters[role.node_count_parameter_name] = data['nodes']
if utils.matching_deployment_mode():
parameters[role.flavor_parameter_name] = data['flavor']
plan.patch(request, plan.uuid, parameters)
- # success
+ # TODO(rdopiera) Find out how to update role's name and description.
return True
diff --git a/tuskar_ui/infrastructure/views.py b/tuskar_ui/infrastructure/views.py
index 7c0219bc0..b22aade3b 100644
--- a/tuskar_ui/infrastructure/views.py
+++ b/tuskar_ui/infrastructure/views.py
@@ -28,10 +28,13 @@ class ItemCountMixin(object):
class StackMixin(object):
+ @memoized.memoized
+ def get_plan(self):
+ return api.tuskar.Plan.get_the_plan(self.request)
+
@memoized.memoized
def get_stack(self):
- plan = api.tuskar.Plan.get_the_plan(self.request)
- return api.heat.Stack.get_by_plan(self.request, plan)
+ return api.heat.Stack.get_by_plan(self.request, self.get_plan())
class RoleMixin(object):