Add a "driver" field to the Register Nodes dialog

The choice of the driver determines the visibility of the
driver-dependent fields.

Partial-bug: #1342156
Change-Id: I7f68b47c46c12e7f2bf91e475a62e4740ecc3946
This commit is contained in:
Radomir Dopieralski 2014-07-15 15:34:45 +02:00
parent e67faf8c1f
commit 1865cf987a
5 changed files with 113 additions and 50 deletions

View File

@ -63,15 +63,10 @@ class IronicNode(base.APIResourceWrapper):
@classmethod
def create(cls, request, ipmi_address, architecture, cpu, ram, local_disk,
mac_addresses, ipmi_username=None, ipmi_password=None):
mac_addresses, ipmi_username=None, ipmi_password=None,
driver=None):
"""Create a Node in Ironic
:param request: request object
:type request: django.http.HttpRequest
:param ipmi_address: IPMI address
:type ipmi_address: str
:param cpu: number of cores
:type cpu: int
@ -84,12 +79,6 @@ class IronicNode(base.APIResourceWrapper):
:param mac_addresses: list of mac addresses
:type mac_addresses: list of str
:param ipmi_username: IPMI username
:type ipmi_username: str
:param ipmi_password: IPMI password
:type ipmi_password: str
:return: the created Node object
:rtype: tuskar_ui.api.node.IronicNode
"""
@ -193,15 +182,10 @@ class BareMetalNode(base.APIResourceWrapper):
@classmethod
def create(cls, request, ipmi_address, architecture, cpu, ram, local_disk,
mac_addresses, ipmi_username=None, ipmi_password=None):
mac_addresses, ipmi_username=None, ipmi_password=None,
driver=None):
"""Create a Nova BareMetalNode
:param request: request object
:type request: django.http.HttpRequest
:param ipmi_address: IPMI address
:type ipmi_address: str
:param cpu: number of cores
:type cpu: int
@ -214,12 +198,6 @@ class BareMetalNode(base.APIResourceWrapper):
:param mac_addresses: list of mac addresses
:type mac_addresses: list of str
:param ipmi_username: IPMI username
:type ipmi_username: str
:param ipmi_password: IPMI password
:type ipmi_password: str
:return: the created BareMetalNode object
:rtype: tuskar_ui.api.node.BareMetalNode
"""
@ -414,11 +392,12 @@ class Node(base.APIResourceWrapper):
@classmethod
def create(cls, request, ipmi_address, architecture, cpu, ram, local_disk,
mac_addresses, ipmi_username=None, ipmi_password=None):
mac_addresses, ipmi_username=None, ipmi_password=None,
driver=None):
return cls(NodeClient(request).node_class.create(
request, ipmi_address, architecture, cpu, ram, local_disk,
mac_addresses, ipmi_username=ipmi_username,
ipmi_password=ipmi_password))
ipmi_password=ipmi_password, driver=driver))
@classmethod
@handle_errors(_("Unable to retrieve node"))

View File

@ -24,6 +24,10 @@ ARCHITECTURE_CHOICES = [
('x86', _("x86")),
('x86_64', _("x86_64")),
]
DRIVER_CHOICES = [
('ipmi', _("IPMI Driver")),
('dummy', _("Dummy Driver")),
]
class NodeForm(django.forms.Form):
@ -32,21 +36,43 @@ class NodeForm(django.forms.Form):
required=False,
widget=django.forms.HiddenInput(),
)
driver = django.forms.ChoiceField(
label=_("Driver"),
choices=DRIVER_CHOICES,
required=True,
widget=django.forms.Select(attrs={
'class': 'input input-medium switchable',
'data-slug': 'driver',
}),
)
ipmi_address = django.forms.IPAddressField(
label=_("IPMI Address"),
required=False,
widget=django.forms.TextInput(attrs={'class': 'input input-medium'}),
widget=django.forms.TextInput(attrs={
'class': 'switched',
'data-switch-on': 'driver',
'data-driver-ipmi': 'ipmi',
}),
)
ipmi_username = django.forms.CharField(
label=_("IPMI User"),
required=False,
widget=django.forms.TextInput(attrs={'class': 'input input-medium'}),
widget=django.forms.TextInput(attrs={
'class': 'input input-medium switched',
'data-switch-on': 'driver',
'data-driver-ipmi': 'ipmi',
}),
)
ipmi_password = django.forms.CharField(
label=_("IPMI Password"),
required=False,
widget=django.forms.PasswordInput(
render_value=False, attrs={'class': 'input input-medium'}),
widget=django.forms.PasswordInput(render_value=False, attrs={
'class': 'input input-medium switched',
'data-switch-on': 'driver',
'data-driver-ipmi': 'ipmi',
}),
)
mac_addresses = tuskar_ui.forms.MultiMACField(
label=_("NIC MAC Addresses"),
@ -102,17 +128,21 @@ class BaseNodeFormset(django.forms.formsets.BaseFormSet):
def handle(self, request, data):
success = True
for form in self:
data = form.cleaned_data
try:
api.node.Node.create(
request,
form.cleaned_data['ipmi_address'],
form.cleaned_data.get('architecture'),
form.cleaned_data.get('cpus'),
form.cleaned_data.get('memory'),
form.cleaned_data.get('local_disk'),
form.cleaned_data['mac_addresses'].split(),
form.cleaned_data.get('ipmi_username'),
form.cleaned_data.get('ipmi_password'),
# TODO(rdopieralski) If ipmi_address is no longer required,
# then we will need to use something else here?
ipmi_address=data['ipmi_address'],
architecture=data.get('architecture'),
cpu=data.get('cpus'),
ram=data.get('memory'),
local_disk=data.get('local_disk'),
mac_addresses=data['mac_addresses'].split(),
ipmi_username=data.get('ipmi_username'),
ipmi_password=data.get('ipmi_password'),
driver=form.cleaned_data.get('driver'),
)
except Exception:
success = False
@ -135,5 +165,6 @@ class BaseNodeFormset(django.forms.formsets.BaseFormSet):
if not form.cleaned_data.get('ipmi_password'):
form.cleaned_data['ipmi_password'] = None
NodeFormset = django.forms.formsets.formset_factory(NodeForm, extra=1,
formset=BaseNodeFormset)

View File

@ -1,12 +1,13 @@
<div class="well well-small tab-pane{% if active %} active{% endif %}"
id="tab-{{ form.prefix }}">
<div class="form form-inline">
<div class="form form-inline"><fieldset>
<div class="row-fluid">
<h4>Node Detail</h4>
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.node_tags %}
</div>
<div class="row-fluid">
<h5>Power Management</h5>
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.driver required=True %}
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ipmi_address %}
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ipmi_username %}
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ipmi_password %}
@ -29,7 +30,7 @@
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.memory extra_text=_('MB') required=True %}
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.local_disk extra_text=_('GB') required=True %}
</div>
</div>
</fieldset></div>
</div>
<script type="text/javascript">

View File

@ -164,6 +164,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
'register_nodes-INITIAL_FORMS': 1,
'register_nodes-MAX_NUM_FORMS': 1000,
'register_nodes-0-driver': 'ipmi',
'register_nodes-0-ipmi_address': '127.0.0.1',
'register_nodes-0-ipmi_username': 'username',
'register_nodes-0-ipmi_password': 'password',
@ -173,6 +174,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
'register_nodes-0-memory': '2',
'register_nodes-0-local_disk': '3',
'register_nodes-1-driver': 'ipmi',
'register_nodes-1-ipmi_address': '127.0.0.2',
'register_nodes-1-mac_addresses': 'de:ad:be:ef:ca:ff',
'register_nodes-1-architecture': 'x86',
@ -188,10 +190,30 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
self.assertRedirectsNoFollow(res, INDEX_URL)
request = Node.create.call_args_list[0][0][0] # This is a hack.
self.assertListEqual(Node.create.call_args_list, [
call(request, u'127.0.0.1', 'x86', 1, 2, 3,
['DE:AD:BE:EF:CA:FE'], u'username', u'password'),
call(request, u'127.0.0.2', 'x86', 4, 5, 6,
['DE:AD:BE:EF:CA:FF'], None, None),
call(
request,
ipmi_address=u'127.0.0.1',
architecture='x86',
cpu=1,
ram=2,
local_disk=3,
mac_addresses=['DE:AD:BE:EF:CA:FE'],
ipmi_username=u'username',
ipmi_password=u'password',
driver='ipmi',
),
call(
request,
ipmi_address=u'127.0.0.2',
architecture='x86',
cpu=4,
ram=5,
local_disk=6,
mac_addresses=['DE:AD:BE:EF:CA:FF'],
ipmi_username=None,
ipmi_password=None,
driver='ipmi',
),
])
def test_register_post_exception(self):
@ -200,6 +222,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
'register_nodes-INITIAL_FORMS': 1,
'register_nodes-MAX_NUM_FORMS': 1000,
'register_nodes-0-driver': 'ipmi',
'register_nodes-0-ipmi_address': '127.0.0.1',
'register_nodes-0-ipmi_username': 'username',
'register_nodes-0-ipmi_password': 'password',
@ -209,6 +232,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
'register_nodes-0-memory': '2',
'register_nodes-0-local_disk': '3',
'register_nodes-1-driver': 'ipmi',
'register_nodes-1-ipmi_address': '127.0.0.2',
'register_nodes-1-mac_addresses': 'de:ad:be:ef:ca:ff',
'register_nodes-1-architecture': 'x86',
@ -224,10 +248,30 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
self.assertEqual(res.status_code, 200)
request = Node.create.call_args_list[0][0][0] # This is a hack.
self.assertListEqual(Node.create.call_args_list, [
call(request, u'127.0.0.1', 'x86', 1, 2, 3,
['DE:AD:BE:EF:CA:FE'], u'username', u'password'),
call(request, u'127.0.0.2', 'x86', 4, 5, 6,
['DE:AD:BE:EF:CA:FF'], None, None),
call(
request,
ipmi_address=u'127.0.0.1',
architecture='x86',
cpu=1,
ram=2,
local_disk=3,
mac_addresses=['DE:AD:BE:EF:CA:FE'],
ipmi_username=u'username',
ipmi_password=u'password',
driver='ipmi',
),
call(
request,
ipmi_address=u'127.0.0.2',
architecture='x86',
cpu=4,
ram=5,
local_disk=6,
mac_addresses=['DE:AD:BE:EF:CA:FF'],
ipmi_username=None,
ipmi_password=None,
driver='ipmi',
),
])
self.assertTemplateUsed(
res, 'infrastructure/nodes/register.html')

View File

@ -66,8 +66,16 @@
.form label.checkbox {
font-weight: normal;
}
fieldset .form-field {
input, textarea, select {
width: 150px;
margin-left: 5px;
}
}
}
#detail__overview {
ul {
list-style: disc;