Merge "Actually get node profiles from the API"
This commit is contained in:
commit
c66b5a1596
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user