From 986fa7418607912cc6a6be05628f801e64f02bac Mon Sep 17 00:00:00 2001 From: Tzu-Mainn Chen Date: Tue, 26 Aug 2014 16:41:31 +0200 Subject: [PATCH] Add autodiscovery form Change-Id: I00b5685ddae7b45d564ff8f756beee3a1fca94ef --- tuskar_ui/api/node.py | 16 ++--- tuskar_ui/infrastructure/nodes/forms.py | 71 ++++++++++++++++++- tuskar_ui/infrastructure/nodes/tabs.py | 6 +- .../nodes/templates/nodes/_auto_discover.html | 20 ++++++ .../_auto_discover_nodes_formset_form.html | 26 +++++++ .../nodes/templates/nodes/auto_discover.html | 11 +++ .../nodes/templates/nodes/index.html | 6 ++ tuskar_ui/infrastructure/nodes/urls.py | 2 + tuskar_ui/infrastructure/nodes/views.py | 19 +++++ 9 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html create mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_nodes_formset_form.html create mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html diff --git a/tuskar_ui/api/node.py b/tuskar_ui/api/node.py index 77c1c20a5..41bed4b4b 100644 --- a/tuskar_ui/api/node.py +++ b/tuskar_ui/api/node.py @@ -70,10 +70,10 @@ class IronicNode(base.APIResourceWrapper): self._request = request @classmethod - def create(cls, request, ipmi_address, cpu_arch, cpus, memory_mb, - local_gb, mac_addresses, ipmi_username=None, ipmi_password=None, - ssh_address=None, ssh_username=None, ssh_key_contents=None, - driver=None): + def create(cls, request, ipmi_address=None, cpu_arch=None, cpus=None, + memory_mb=None, local_gb=None, mac_addresses=[], + ipmi_username=None, ipmi_password=None, ssh_address=None, + ssh_username=None, ssh_key_contents=None, driver=None): """Create a Node in Ironic """ if driver == 'pxe_ssh': @@ -447,10 +447,10 @@ class Node(base.APIResourceWrapper): self._instance = kwargs['instance'] @classmethod - def create(cls, request, ipmi_address, cpu_arch, cpus, memory_mb, - local_gb, mac_addresses, ipmi_username=None, ipmi_password=None, - driver=None, ssh_address=None, ssh_username=None, - ssh_key_contents=None): + def create(cls, request, ipmi_address=None, cpu_arch=None, cpus=None, + memory_mb=None, local_gb=None, mac_addresses=[], + ipmi_username=None, ipmi_password=None, ssh_address=None, + ssh_username=None, ssh_key_contents=None, driver=None): return cls(NodeClient(request).node_class.create( request, ipmi_address, cpu_arch, cpus, memory_mb, local_gb, mac_addresses, ipmi_username=ipmi_username, diff --git a/tuskar_ui/infrastructure/nodes/forms.py b/tuskar_ui/infrastructure/nodes/forms.py index d3543f918..d46daebd2 100644 --- a/tuskar_ui/infrastructure/nodes/forms.py +++ b/tuskar_ui/infrastructure/nodes/forms.py @@ -145,8 +145,8 @@ class NodeForm(django.forms.Form): def get_name(self): try: - # FIXME(lsmola) show somethign meaningful here - name = self.fields['ipmi_address'].value() + name = (self.fields['ipmi_address'].value() or + self.fields['ssh_address'].value()) except AttributeError: # when the field is not bound name = _("Undefined node") @@ -208,3 +208,70 @@ class BaseNodeFormset(django.forms.formsets.BaseFormSet): NodeFormset = django.forms.formsets.formset_factory(NodeForm, extra=1, formset=BaseNodeFormset) + + +class AutoDiscoverNodeForm(django.forms.Form): + id = django.forms.IntegerField( + label="", + required=False, + widget=django.forms.HiddenInput(), + ) + ssh_address = django.forms.IPAddressField( + label=_("SSH Address"), + required=False, + widget=django.forms.TextInput, + ) + ssh_username = django.forms.CharField( + label=_("SSH User"), + required=False, + widget=django.forms.TextInput, + ) + ssh_key_contents = django.forms.CharField( + label=_("SSH Key Contents"), + required=False, + widget=django.forms.Textarea(attrs={ + 'rows': 2, + }), + ) + + def get_name(self): + try: + name = self.fields['ssh_address'].value() + except AttributeError: + # when the field is not bound + name = _("Undefined node") + return name + + +class BaseAutoDiscoverNodeFormset(BaseNodeFormset): + def handle(self, request, data): + success = True + for form in self: + data = form.cleaned_data + try: + node = api.node.Node.create( + request, + ssh_address=data['ssh_address'], + ssh_username=data.get('ssh_username'), + ssh_key_contents=data.get('ssh_key_contents'), + driver='pxe_ssh' + ) + api.node.Node.set_maintenance(request, + node.uuid, + True) + #TODO(tzumainn): now we need to boot the node + except Exception: + success = False + exceptions.handle(request, _('Unable to register node.')) + # TODO(rdopieralski) Somehow find out if any port creation + # failed and remove the mac addresses that succeeded from + # the form. + else: + # TODO(rdopieralski) Remove successful nodes from formset. + pass + return success + + +AutoDiscoverNodeFormset = django.forms.formsets.formset_factory( + AutoDiscoverNodeForm, extra=1, + formset=BaseAutoDiscoverNodeFormset) diff --git a/tuskar_ui/infrastructure/nodes/tabs.py b/tuskar_ui/infrastructure/nodes/tabs.py index 066431177..e9662bb67 100644 --- a/tuskar_ui/infrastructure/nodes/tabs.py +++ b/tuskar_ui/infrastructure/nodes/tabs.py @@ -29,9 +29,9 @@ class OverviewTab(tabs.Tab): def get_context_data(self, request): nodes = api.node.Node.list(request) - cpus = sum(int(node.cpus) for node in nodes) - memory_mb = sum(int(node.memory_mb) for node in nodes) - local_gb = sum(int(node.local_gb) for node in nodes) + cpus = sum(int(node.cpus or 0) for node in nodes) + memory_mb = sum(int(node.memory_mb or 0) for node in nodes) + local_gb = sum(int(node.local_gb or 0) for node in nodes) deployed_nodes = api.node.Node.list(request, associated=True) free_nodes = api.node.Node.list(request, associated=False) deployed_nodes_error = api.node.filter_nodes( diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html b/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html new file mode 100644 index 000000000..00faaa311 --- /dev/null +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html @@ -0,0 +1,20 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% load url from future %} + +{% block form_id %}auto_discover_nodes_form{% endblock %} +{% block form_action %}{% url 'horizon:infrastructure:nodes:auto-discover' %}{% endblock %} +{% block modal_id %}register_nodes_modal{% endblock %} +{% block modal-header %}{% trans "Auto-Discover Nodes" %}{% endblock %} + +{% block modal-body %} +{% include "formset_table/menu_formset.html" with formset=form form_template="infrastructure/nodes/_auto_discover_nodes_formset_form.html" %} +{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} + diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_nodes_formset_form.html b/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_nodes_formset_form.html new file mode 100644 index 000000000..5f9049d0a --- /dev/null +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_nodes_formset_form.html @@ -0,0 +1,26 @@ +{% load i18n %} + +
+
+
+
{% trans 'Power Management' %}
+ {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ssh_address %} + {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ssh_username %} + {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ssh_key_contents %} +
+
+
+ + diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html b/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html new file mode 100644 index 000000000..c605dbe28 --- /dev/null +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} +{% load i18n %} +{% block title %}{% trans "Auto-Discover Nodes" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Auto-Discover Nodes") %} +{% endblock %} + +{% block main %} + {% include "infrastructure/nodes/_auto_discover.html" %} +{% endblock %} diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/index.html b/tuskar_ui/infrastructure/nodes/templates/nodes/index.html index f340b14b1..8436e40a5 100644 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/index.html +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/index.html @@ -15,6 +15,12 @@ {% trans 'Register Nodes' %} + {% if ironic_enabled %} + + + {% trans 'Auto-Discover Nodes' %} + + {% endif %} {{ tab_group.render }} diff --git a/tuskar_ui/infrastructure/nodes/urls.py b/tuskar_ui/infrastructure/nodes/urls.py index 3b9b890d5..8ef7ee21d 100644 --- a/tuskar_ui/infrastructure/nodes/urls.py +++ b/tuskar_ui/infrastructure/nodes/urls.py @@ -22,6 +22,8 @@ urlpatterns = urls.patterns( urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^register/$', views.RegisterView.as_view(), name='register'), + urls.url(r'^auto-discover/$', views.AutoDiscoverView.as_view(), + name='auto-discover'), urls.url(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='detail'), urls.url(r'^(?P[^/]+)/performance/$', diff --git a/tuskar_ui/infrastructure/nodes/views.py b/tuskar_ui/infrastructure/nodes/views.py index 1f636bcf9..ee8126064 100644 --- a/tuskar_ui/infrastructure/nodes/views.py +++ b/tuskar_ui/infrastructure/nodes/views.py @@ -35,6 +35,12 @@ class IndexView(horizon_tabs.TabbedTableView): tab_group_class = tabs.NodeTabs template_name = 'infrastructure/nodes/index.html' + def get_context_data(self, **kwargs): + context = super(IndexView, self).get_context_data(**kwargs) + context['ironic_enabled'] = api.node.NodeClient.ironic_enabled( + self.request) + return context + class RegisterView(horizon_forms.ModalFormView): form_class = forms.NodeFormset @@ -52,6 +58,19 @@ class RegisterView(horizon_forms.ModalFormView): prefix=self.form_prefix) +class AutoDiscoverView(horizon_forms.ModalFormView): + form_class = forms.AutoDiscoverNodeFormset + form_prefix = 'auto_discover_nodes' + template_name = 'infrastructure/nodes/auto_discover.html' + success_url = reverse_lazy( + 'horizon:infrastructure:nodes:index') + + def get_form(self, form_class): + return form_class(self.request.POST or None, + initial=[], + prefix=self.form_prefix) + + class DetailView(horizon_views.APIView): template_name = 'infrastructure/nodes/details.html'