Merge "Actually get node profiles from the API"

This commit is contained in:
Jenkins 2014-02-27 08:53:38 +00:00 committed by Gerrit Code Review
commit c66b5a1596
7 changed files with 175 additions and 61 deletions

View File

@ -769,3 +769,14 @@ class OvercloudRole(base.APIResourceWrapper):
"""
role = tuskarclient(request).overcloud_roles.get(role_id)
return cls(role)
def update(self, request, **kwargs):
"""Update the selected attributes of Tuskar OvercloudRole.
:param request: request object
:type request: django.http.HttpRequest
"""
for attr in kwargs:
if attr not in self._attrs:
raise TypeError('Invalid parameter %r' % attr)
tuskarclient(request).overcloud_roles.update(self.id, **kwargs)

View File

@ -17,6 +17,7 @@ from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.forms
import horizon.messages
from openstack_dashboard import api as horizon_api
from tuskar_ui import api
@ -35,11 +36,15 @@ class UndeployOvercloud(horizon.forms.SelfHandlingForm):
return True
# TODO(rdopieralski) Get the list of flavors
def get_flavors():
yield (None, '----')
yield ('xxx', 'Some Hardware Profile')
yield ('yyy', 'Other Hardware Profile')
def get_flavor_choices(request):
empty = [('', '----')]
try:
flavors = horizon_api.nova.flavor_list(request, None)
except Exception:
horizon.exceptions.handle(request,
_('Unable to retrieve flavor list.'))
return empty
return empty + [(flavor.id, flavor.name) for flavor in flavors]
class OvercloudRoleForm(horizon.forms.SelfHandlingForm):
@ -58,8 +63,18 @@ class OvercloudRoleForm(horizon.forms.SelfHandlingForm):
widget=django.forms.TextInput(
attrs={'readonly': 'readonly', 'disabled': 'disabled'}))
flavor_id = django.forms.ChoiceField(
label=_("Node Profile"), required=False, choices=get_flavors())
label=_("Node Profile"), required=False, choices=())
def __init__(self, *args, **kwargs):
super(OvercloudRoleForm, self).__init__(*args, **kwargs)
self.fields['flavor_id'].choices = get_flavor_choices(self.request)
def handle(self, request, context):
# TODO(rdopieralski) Associate the flavor with the role
try:
role = api.OvercloudRole.get(request, context['id'])
role.update(request, flavor_id=context['flavor_id'])
except Exception:
horizon.exceptions.handle(request,
_('Unable to update the role.'))
return False
return True

View File

@ -16,21 +16,25 @@
<tr>
{% if forloop.first %}
<td rowspan="{{ fields|length }}">
<a
href="{% url 'horizon:infrastructure:overcloud:role_edit' role_id %}"
class="ajax-modal"
><i class="icon-pencil"></i></a>
{% if editable %}
<a
href="{% url 'horizon:infrastructure:overcloud:role_edit' role_id %}"
class="ajax-modal"
><i class="icon-pencil"></i></a>
{% endif %}
{{ label }}
</td>
{% endif %}
<td>
{% if field.field.label %}
{{ field.label }}
{% else %}
{% elif editable %}
(<a
href="{% url 'horizon:infrastructure:overcloud:role_edit' role_id %}"
class="ajax-modal"
>{% trans "Add a node profile" %}</a>)
{% else %}
({% trans "No node profile" %})
{% endif %}
</td>
<td>

View File

@ -11,7 +11,7 @@
</div>
<h2>{% trans "Roles" %}</h2>
<div class="widget">
{% include 'infrastructure/overcloud/node_counts.html' with form=form %}
{% include 'infrastructure/overcloud/node_counts.html' with form=form editable=True %}
</div>
</div>
<div class="span4">

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
import contextlib
from django.core import urlresolvers
@ -101,13 +102,20 @@ class OvercloudTests(test.BaseAdminViewTests):
def test_create_get(self):
roles = TEST_DATA.tuskarclient_overcloud_roles.list()
with contextlib.nested(patch('tuskar_ui.api.OvercloudRole', **{
'spec_set': ['list'],
'list.return_value': roles,
}), patch('tuskar_ui.api.Node', **{
'spec_set': ['list'],
'list.return_value': [],
})):
with contextlib.nested(
patch('tuskar_ui.api.OvercloudRole', **{
'spec_set': ['list'],
'list.return_value': roles,
}),
patch('tuskar_ui.api.Node', **{
'spec_set': ['list'],
'list.return_value': [],
}),
patch('openstack_dashboard.api.nova', **{
'spec_set': ['flavor_list'],
'flavor_list.return_value': [],
}),
):
res = self.client.get(CREATE_URL)
self.assertTemplateUsed(
res, 'infrastructure/_fullscreen_workflow_base.html')
@ -117,22 +125,30 @@ class OvercloudTests(test.BaseAdminViewTests):
def test_create_post(self):
oc = None
roles = TEST_DATA.tuskarclient_overcloud_roles.list()
old_flavor_id = roles[0].flavor_id
roles[0].flavor_id = 'default'
data = {
'count__1__default': '1',
'count__2__default': '0',
'count__3__default': '0',
'count__4__default': '0',
'count__2__': '0',
'count__3__': '0',
'count__4__': '0',
}
with contextlib.nested(
patch('tuskar_ui.api.OvercloudRole', **{
'spec_set': ['list'],
'list.side_effect': lambda request: roles,
}),
patch('tuskar_ui.api.Overcloud', **{
'list.return_value': roles,
}), patch('tuskar_ui.api.Overcloud', **{
'spec_set': ['create'],
'create.return_value': oc,
'create.side_effect': lambda *args: oc,
}), patch('tuskar_ui.api.Node', **{
'spec_set': ['list'],
'list.return_value': [],
}),
) as (OvercloudRole, Overcloud):
patch('openstack_dashboard.api.nova', **{
'spec_set': ['flavor_list'],
'flavor_list.return_value': [],
}),
) as (OvercloudRole, Overcloud, Node, nova):
oc = Overcloud
res = self.client.post(CREATE_URL, data)
request = Overcloud.create.call_args_list[0][0][0]
@ -141,9 +157,9 @@ class OvercloudTests(test.BaseAdminViewTests):
[
call(request, {
('1', 'default'): 1,
('2', 'default'): 0,
('3', 'default'): 0,
('4', 'default'): 0,
('2', ''): 0,
('3', ''): 0,
('4', ''): 0,
}, {
'NeutronPublicInterfaceRawDevice': '',
'NovaComputeDriver': '',
@ -175,6 +191,7 @@ class OvercloudTests(test.BaseAdminViewTests):
'Flavor': '',
}),
])
roles[0].flavor_id = old_flavor_id
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_detail_get(self):
@ -308,7 +325,11 @@ class OvercloudTests(test.BaseAdminViewTests):
for role in roles
],
}),
) as (OvercloudRole, Overcloud):
patch('openstack_dashboard.api.nova', **{
'spec_set': ['flavor_list'],
'flavor_list.return_value': [],
}),
) as (OvercloudRole, Overcloud, nova):
oc = Overcloud
url = urlresolvers.reverse(
'horizon:infrastructure:overcloud:scale', args=(oc.id,))
@ -319,12 +340,14 @@ class OvercloudTests(test.BaseAdminViewTests):
def test_scale_post(self):
oc = None
roles = TEST_DATA.tuskarclient_overcloud_roles.list()
old_flavor_id = roles[0].flavor_id
roles[0].flavor_id = 'default'
data = {
'overcloud_id': '1',
'count__1__default': '1',
'count__2__default': '0',
'count__3__default': '0',
'count__4__default': '0',
'count__2__': '0',
'count__3__': '0',
'count__4__': '0',
}
with contextlib.nested(
patch('tuskar_ui.api.OvercloudRole', **{
@ -341,7 +364,11 @@ class OvercloudTests(test.BaseAdminViewTests):
for role in roles
],
}),
) as (OvercloudRole, Overcloud):
patch('openstack_dashboard.api.nova', **{
'spec_set': ['flavor_list'],
'flavor_list.return_value': [],
}),
) as (OvercloudRole, Overcloud, nova):
oc = Overcloud
url = urlresolvers.reverse(
'horizon:infrastructure:overcloud:scale', args=(oc.id,))
@ -353,21 +380,28 @@ class OvercloudTests(test.BaseAdminViewTests):
# [
# call(request, {
# ('1', 'default'): 1,
# ('2', 'default'): 0,
# ('3', 'default'): 0,
# ('4', 'default'): 0,
# ('2', ''): 0,
# ('3', ''): 0,
# ('4', ''): 0,
# }),
# ])
roles[0].flavor_id = old_flavor_id
self.assertRedirectsNoFollow(res, DETAIL_URL)
def test_role_edit_get(self):
role = TEST_DATA.tuskarclient_overcloud_roles.first()
url = urlresolvers.reverse(
'horizon:infrastructure:overcloud:role_edit', args=(role.id,))
with patch('tuskar_ui.api.OvercloudRole', **{
'spec_set': ['get'],
'get.return_value': role,
}):
with contextlib.nested(
patch('tuskar_ui.api.OvercloudRole', **{
'spec_set': ['get'],
'get.return_value': role,
}),
patch('openstack_dashboard.api.nova', **{
'spec_set': ['flavor_list'],
'flavor_list.return_value': [],
}),
):
res = self.client.get(url)
self.assertTemplateUsed(
res, 'infrastructure/overcloud/role_edit.html')
@ -375,17 +409,42 @@ class OvercloudTests(test.BaseAdminViewTests):
res, 'infrastructure/overcloud/_role_edit.html')
def test_role_edit_post(self):
role = TEST_DATA.tuskarclient_overcloud_roles.first()
url = urlresolvers.reverse(
'horizon:infrastructure:overcloud:role_edit', args=(role.id,))
data = {
'id': '1',
'flavor_id': 'xxx',
}
with patch('tuskar_ui.api.OvercloudRole', **{
'spec_set': ['get'],
'get.return_value': role,
}):
# TODO(rdopieralski) Check if the role got associated with flavor.
role = None
Flavor = collections.namedtuple('Flavor', 'id name')
flavor = Flavor('xxx', 'Xxx')
with contextlib.nested(
patch('tuskar_ui.api.OvercloudRole', **{
'spec_set': [
'get',
'update',
'id',
'name',
'description',
'image_name',
'flavor_id',
],
'get.side_effect': lambda *args: role,
'name': 'Compute',
'description': '...',
'image_name': '',
'id': 1,
'flavor_id': '',
}),
patch('openstack_dashboard.api.nova', **{
'spec_set': ['flavor_list'],
'flavor_list.return_value': [flavor],
}),
) as (OvercloudRole, nova):
role = OvercloudRole
url = urlresolvers.reverse(
'horizon:infrastructure:overcloud:role_edit', args=(role.id,))
data = {
'id': str(role.id),
'flavor_id': flavor.id,
}
res = self.client.post(url, data)
request = OvercloudRole.update.call_args_list[0][0][0]
self.assertListEqual(
OvercloudRole.update.call_args_list,
[call(request, flavor_id=flavor.id)])
self.assertRedirectsNoFollow(res, CREATE_URL)

View File

@ -16,6 +16,7 @@ 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
@ -39,8 +40,31 @@ class Action(horizon.workflows.Action):
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
@ -48,18 +72,16 @@ class Action(horizon.workflows.Action):
else:
initial = 0
attrs = {}
# TODO(rdopieralski) Get a list of hardware profiles for each
# role here.
profiles = [(_("Default"), 'default')]
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 label, profile in profiles:
for profile_id, label in profiles:
name = get_field_name_from_role_id_and_profile_id(
str(role.id), profile)
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))

View File

@ -369,25 +369,28 @@ def data(TEST):
'name': 'Controller',
'description': 'controller overcloud role',
'image_name': 'overcloud-control',
'flavor_id': None,
'flavor_id': '',
})
r_2 = overcloud_roles.OvercloudRole(
overcloud_roles.OvercloudRoleManager(None),
{'id': 2,
'name': 'Compute',
'description': 'compute overcloud role',
'flavor_id': '',
'image_name': 'overcloud-compute'})
r_3 = overcloud_roles.OvercloudRole(
overcloud_roles.OvercloudRoleManager(None),
{'id': 3,
'name': 'Object Storage',
'description': 'object storage overcloud role',
'flavor_id': '',
'image_name': 'overcloud-object-storage'})
r_4 = overcloud_roles.OvercloudRole(
overcloud_roles.OvercloudRoleManager(None),
{'id': 4,
'name': 'Block Storage',
'description': 'block storage overcloud role',
'flavor_id': '',
'image_name': 'overcloud-block-storage'})
TEST.tuskarclient_overcloud_roles.add(r_1, r_2, r_3, r_4)