135 lines
5.0 KiB
Python
135 lines
5.0 KiB
Python
# -*- coding: utf8 -*-
|
|
#
|
|
# 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 django.forms
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from horizon.utils import memoized
|
|
import horizon.workflows
|
|
from openstack_dashboard import api as horizon_api
|
|
|
|
from tuskar_ui import api
|
|
import tuskar_ui.forms
|
|
|
|
|
|
def get_role_id_and_profile_id_from_field_name(field_name):
|
|
"""Extract the ids of overcloud role and node profile from the field
|
|
name.
|
|
"""
|
|
_count, role_id, profile_id = field_name.split('__', 2)
|
|
return role_id, profile_id
|
|
|
|
|
|
def get_field_name_from_role_id_and_profile_id(role_id, profile_id=''):
|
|
"""Compose the ids of overcloud role and node profile into a field name."""
|
|
return 'count__%s__%s' % (role_id, profile_id)
|
|
|
|
|
|
class Action(horizon.workflows.Action):
|
|
class Meta:
|
|
slug = 'undeployed_overview'
|
|
name = _("Overview")
|
|
|
|
def _get_profile_names(self):
|
|
# Get all flavors in one call, instead of getting them one by one.
|
|
try:
|
|
flavors = horizon_api.nova.flavor_list(self.request, None)
|
|
except Exception:
|
|
horizon.exceptions.handle(self.request,
|
|
_('Unable to retrieve flavor list.'))
|
|
flavors = []
|
|
return dict((str(flavor.id), flavor.name) for flavor in flavors)
|
|
|
|
def _get_profiles(self, role, profile_names):
|
|
# TODO(rdopieralski) Get a list of hardware profiles for each
|
|
# role here, when we support multiple profiles per role.
|
|
if role.flavor_id:
|
|
profiles = [(
|
|
role.flavor_id,
|
|
profile_names.get(str(role.flavor_id), role.flavor_id),
|
|
)]
|
|
else:
|
|
profiles = []
|
|
return profiles
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(Action, self).__init__(*args, **kwargs)
|
|
profile_names = self._get_profile_names()
|
|
for role in self._get_roles():
|
|
if role.name == 'Controller':
|
|
initial = 1
|
|
attrs = {'readonly': 'readonly'}
|
|
else:
|
|
initial = 0
|
|
attrs = {}
|
|
profiles = self._get_profiles(role, profile_names)
|
|
if not profiles:
|
|
name = get_field_name_from_role_id_and_profile_id(str(role.id))
|
|
attrs = {'readonly': 'readonly'}
|
|
self.fields[name] = django.forms.IntegerField(
|
|
label='', initial=initial, min_value=initial,
|
|
widget=tuskar_ui.forms.NumberPickerInput(attrs=attrs))
|
|
for profile_id, label in profiles:
|
|
name = get_field_name_from_role_id_and_profile_id(
|
|
str(role.id), profile_id)
|
|
self.fields[name] = django.forms.IntegerField(
|
|
label=label, initial=initial, min_value=initial,
|
|
widget=tuskar_ui.forms.NumberPickerInput(attrs=attrs))
|
|
|
|
def roles_fieldset(self):
|
|
"""Iterates over lists of fields for each role."""
|
|
for role in self._get_roles():
|
|
yield (
|
|
role.id,
|
|
role.name,
|
|
list(tuskar_ui.forms.fieldset(
|
|
self, prefix=get_field_name_from_role_id_and_profile_id(
|
|
str(role.id)))),
|
|
)
|
|
|
|
@memoized.memoized
|
|
def _get_roles(self):
|
|
"""Retrieve the list of all overcloud roles."""
|
|
return api.OvercloudRole.list(self.request)
|
|
|
|
def clean(self):
|
|
for key, value in self.cleaned_data.iteritems():
|
|
if not key.startswith('count_'):
|
|
continue
|
|
role_id, profile = get_role_id_and_profile_id_from_field_name(key)
|
|
if int(value) and not profile:
|
|
raise django.forms.ValidationError(
|
|
_("Can't deploy nodes without a node profile assigned."))
|
|
return self.cleaned_data
|
|
|
|
|
|
class Step(horizon.workflows.Step):
|
|
action_class = Action
|
|
contributes = ('role_counts',)
|
|
template_name = 'infrastructure/overcloud/undeployed_overview.html'
|
|
help_text = _("Nothing deployed yet. Design your first deployment.")
|
|
|
|
def get_free_nodes(self):
|
|
"""Get the count of nodes that are not assigned yet."""
|
|
return len(api.Node.list(self.workflow.request, False))
|
|
|
|
def contribute(self, data, context):
|
|
counts = {}
|
|
for key, value in data.iteritems():
|
|
if not key.startswith('count_'):
|
|
continue
|
|
count, role_id, profile = key.split('__', 2)
|
|
counts[role_id, profile] = int(value)
|
|
context['role_counts'] = counts
|
|
return context
|