From db3d7cd35c69ef7ebe901e1f5e5417e010bb71bd Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Tue, 7 Oct 2014 16:57:14 +0200 Subject: [PATCH] New node registration and upload dialogs According to the new wireframes. The values for autodiscoverable parameters now default to empty values. Change-Id: I1c6430cc0165c7df612c85c31e8ad17d82870b5c --- tuskar_ui/forms.py | 2 +- tuskar_ui/infrastructure/nodes/forms.py | 241 ++++++++---------- .../nodes/templates/nodes/_auto_discover.html | 20 -- .../templates/nodes/_auto_discover_csv.html | 12 +- .../_auto_discover_nodes_formset_form.html | 31 --- .../templates/nodes/_nodes_formset_form.html | 55 ++-- .../nodes/templates/nodes/_upload.html | 24 ++ .../nodes/templates/nodes/auto_discover.html | 11 - .../templates/nodes/auto_discover_csv.html | 11 - .../nodes/templates/nodes/index.html | 14 +- .../nodes/templates/nodes/upload.html | 11 + tuskar_ui/infrastructure/nodes/tests.py | 13 +- tuskar_ui/infrastructure/nodes/urls.py | 4 +- tuskar_ui/infrastructure/nodes/views.py | 53 ++-- .../infrastructure/js/tuskar.menu_formset.js | 18 +- .../scss/_individual_pages.scss | 10 +- 16 files changed, 247 insertions(+), 283 deletions(-) delete mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html delete mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_nodes_formset_form.html create mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/_upload.html delete mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html delete mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover_csv.html create mode 100644 tuskar_ui/infrastructure/nodes/templates/nodes/upload.html diff --git a/tuskar_ui/forms.py b/tuskar_ui/forms.py index 6a17b0371..acdd4ce3e 100644 --- a/tuskar_ui/forms.py +++ b/tuskar_ui/forms.py @@ -109,7 +109,7 @@ class SelfHandlingFormset(forms.formsets.BaseFormSet): def handle(self, request, data): success = True for form in self: - form_success = form.handle(request, data) + form_success = form.handle(request, form.cleaned_data) if not form_success: success = False else: diff --git a/tuskar_ui/infrastructure/nodes/forms.py b/tuskar_ui/infrastructure/nodes/forms.py index c529a0cd8..b2bfd2444 100644 --- a/tuskar_ui/infrastructure/nodes/forms.py +++ b/tuskar_ui/infrastructure/nodes/forms.py @@ -11,7 +11,6 @@ # 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 csv import django.forms @@ -24,6 +23,7 @@ import tuskar_ui.forms CPU_ARCH_CHOICES = [ + ('', _("unspecified")), ('amd64', _("amd64")), ('x86', _("x86")), ('x86_64', _("x86_64")), @@ -36,9 +36,7 @@ DRIVER_CHOICES = [ def get_driver_info_dict(data): driver = data['driver'] - driver_dict = { - 'driver': driver - } + driver_dict = {'driver': driver} if driver == 'ipmi': driver_dict.update( # TODO(rdopieralski) If ipmi_address is no longer required, @@ -56,18 +54,22 @@ def get_driver_info_dict(data): return driver_dict -def auto_discover_node(request, kwargs): - node = api.node.Node.create( - request, - **kwargs +def create_node(request, data): + kwargs = get_driver_info_dict(data) + kwargs.update( + cpu_arch=data.get('cpu_arch'), + cpus=data.get('cpus'), + memory_mb=data.get('memory_mb'), + local_gb=data.get('local_gb'), + mac_addresses=data['mac_addresses'].split(), ) - api.node.Node.set_maintenance(request, - node.uuid, - True) - api.node.Node.discover(request, [node.uuid]) + node = api.node.Node.create(request, **kwargs) + if data.get('do_autodiscovery', False): + api.node.Node.set_maintenance(request, node.uuid, True) + api.node.Node.discover(request, [node.uuid]) -class BaseNodeForm(django.forms.Form): +class NodeForm(django.forms.Form): id = django.forms.IntegerField( label="", required=False, @@ -139,6 +141,46 @@ class BaseNodeForm(django.forms.Form): 'rows': 2, }), ) + do_autodiscovery = django.forms.BooleanField( + label=_("Discover missing attributes"), + required=False, + ) + mac_addresses = tuskar_ui.forms.MultiMACField( + label=_("NIC MAC Addresses"), + required=False, + widget=django.forms.Textarea(attrs={ + 'placeholder': _('unspecified'), + 'rows': '2', + }), + ) + cpu_arch = django.forms.ChoiceField( + label=_("Architecture"), + required=False, + choices=CPU_ARCH_CHOICES, + widget=django.forms.Select( + attrs={'placeholder': _('unspecified')}), + ) + cpus = django.forms.IntegerField( + label=_("CPUs"), + required=False, + min_value=0, + widget=tuskar_ui.forms.NumberInput( + attrs={'placeholder': _('unspecified')}), + ) + memory_mb = django.forms.IntegerField( + label=_("Memory"), + required=False, + min_value=0, + widget=tuskar_ui.forms.NumberInput( + attrs={'placeholder': _('unspecified')}), + ) + local_gb = django.forms.IntegerField( + label=_("Local Disk"), + required=False, + min_value=0, + widget=tuskar_ui.forms.NumberInput( + attrs={'placeholder': _('unspecified')}), + ) def get_name(self): try: @@ -149,22 +191,10 @@ class BaseNodeForm(django.forms.Form): name = _("Undefined node") return name - def create_node(self, request, data): - kwargs = get_driver_info_dict(data) - kwargs.update( - cpu_arch=data.get('cpu_arch'), - cpus=data.get('cpus'), - memory_mb=data.get('memory_mb'), - local_gb=data.get('local_gb'), - mac_addresses=data['mac_addresses'].split(), - ) - api.node.Node.create(request, **kwargs) - def handle(self, request, data): success = True - data = self.cleaned_data try: - self.create_node(request, data) + create_node(request, data) except Exception: success = False exceptions.handle(request, _('Unable to register node.')) @@ -172,123 +202,68 @@ class BaseNodeForm(django.forms.Form): # have to unregister nodes, delete ports, etc? return success + def clean_ipmi_username(self): + return self.cleaned_data.get('ipmi_username') or None -class RegisterNodeForm(BaseNodeForm): - mac_addresses = tuskar_ui.forms.MultiMACField( - label=_("NIC MAC Addresses"), - widget=django.forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': '2', - }), - ) - cpu_arch = django.forms.ChoiceField( - label=_("Architecture"), - required=True, - choices=CPU_ARCH_CHOICES, - widget=django.forms.Select( - attrs={'class': 'form-control'}), - ) - cpus = django.forms.IntegerField( - label=_("CPUs"), - required=True, - min_value=1, - initial=1, - widget=tuskar_ui.forms.NumberInput( - attrs={'class': 'form-control'}), - ) - memory_mb = django.forms.IntegerField( - label=_("Memory"), - required=True, - min_value=1, - initial=1, - widget=tuskar_ui.forms.NumberInput( - attrs={'class': 'form-control'}), - ) - local_gb = django.forms.IntegerField( - label=_("Local Disk"), - required=True, - min_value=1, - initial=1, - widget=tuskar_ui.forms.NumberInput( - attrs={'class': 'form-control'}), - ) + def clean_ipmi_password(self): + return self.cleaned_data.get('ipmi_password') or None - -class AutoDiscoverNodeForm(BaseNodeForm): - mac_addresses = tuskar_ui.forms.MultiMACField( - label=_("NIC MAC Addresses"), - required=False, - widget=django.forms.Textarea(attrs={ - 'class': 'form-control switched', - 'data-switch-on': 'driver', - 'data-driver-pxe_ssh': _("PXE + SSH"), - 'rows': 2, - }), - ) - - def create_node(self, request, data): - kwargs = get_driver_info_dict(data) - kwargs.update( - mac_addresses=data['mac_addresses'].split(), - ) - auto_discover_node(request, kwargs) + def clean(self): + cleaned_data = super(NodeForm, self).clean() + if not cleaned_data.get('do_autodiscovery', False): + for field_name in [ + 'mac_addresses', + 'cpu_arch', + 'cpus', + 'memory_mb', + 'local_gb', + ]: + if not cleaned_data.get(field_name): + self._errors[field_name] = self.error_class([( + u"This field is required " + u"when autodiscovery is disabled." + )]) + return cleaned_data class BaseNodeFormset(tuskar_ui.forms.SelfHandlingFormset): - def clean(self): for form in self: if not form.cleaned_data: raise django.forms.ValidationError( _("Please provide node data for all nodes.")) - if not form.cleaned_data.get('ipmi_username'): - form.cleaned_data['ipmi_username'] = None - if not form.cleaned_data.get('ipmi_password'): - form.cleaned_data['ipmi_password'] = None + + +class UploadNodeForm(forms.SelfHandlingForm): + csv_file = forms.FileField(label=_("CSV File"), required=True) + + def handle(self, request, data): + return True + + def get_data(self): + data = [] + for row in csv.reader(self.cleaned_data['csv_file']): + driver = row[0].strip() + if driver == 'pxe_ssh': + node = dict( + ssh_address=row[1], + ssh_username=row[2], + ssh_key_contents=row[3], + mac_addresses=row[4], + driver=driver, + do_autodiscovery=True, + ) + elif driver == 'ipmi': + node = dict( + ipmi_address=row[1], + ipmi_username=row[2], + ipmi_password=row[3], + driver=driver, + do_autodiscovery=True, + ) + data.append(node) + return data RegisterNodeFormset = django.forms.formsets.formset_factory( - RegisterNodeForm, extra=1, - formset=BaseNodeFormset) - - -AutoDiscoverNodeFormset = django.forms.formsets.formset_factory( - AutoDiscoverNodeForm, extra=1, - formset=BaseNodeFormset) - - -class AutoDiscoverCSVNodeForm(forms.SelfHandlingForm): - csv_file = forms.FileField(label=_("CSV File"), - required=False) - - def handle(self, request, data): - success = True - all_node_data = csv.reader(data['csv_file']) - for node_data in all_node_data: - driver = node_data[0] - kwargs = { - 'driver': driver - } - if driver == 'pxe_ssh': - kwargs.update( - ssh_address=node_data[1], - ssh_username=node_data[2], - ssh_key_contents=node_data[3], - mac_addresses=node_data[4].split() - ) - else: - kwargs.update( - ipmi_address=node_data[1], - ipmi_username=node_data[2], - ipmi_password=node_data[3], - ) - - try: - auto_discover_node(request, kwargs) - except Exception: - success = False - exceptions.handle(request, _('Unable to register node.')) - # TODO(tzumainn) If there is a failure between steps, do we - # have to unregister nodes, delete ports, etc? - - return success + NodeForm, extra=1, formset=BaseNodeFormset) diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html b/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html deleted file mode 100644 index 1e06ba3b0..000000000 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% load url from future %} - -{% block form_id %}autodiscover_nodes_form{% endblock %} -{% block form_action %}{% url 'horizon:infrastructure:nodes:auto-discover' %}{% endblock %} -{% block modal_id %}autodiscover_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_csv.html b/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_csv.html index 9e4c49786..b6d7f14b2 100644 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_csv.html +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_csv.html @@ -3,9 +3,10 @@ {% load url from future %} {% block form_id %}autodiscover_csv_nodes_form{% endblock %} -{% block form_action %}{% url 'horizon:infrastructure:nodes:auto-discover-csv' %}{% endblock %} +{% block form_action %}{% url 'horizon:infrastructure:nodes:register' %}{% endblock %} + {% block modal_id %}autodiscover_csv_nodes_modal{% endblock %} -{% block modal-header %}{% trans "Auto-Discover Nodes (Upload CSV)" %}{% endblock %} +{% block modal-header %}{% trans "Upload Nodes" %}{% endblock %} {% block form_attrs %}enctype="multipart/form-data"{% endblock %} {% block modal-body %} @@ -13,9 +14,10 @@ {% 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 deleted file mode 100644 index c8ef4bbe9..000000000 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/_auto_discover_nodes_formset_form.html +++ /dev/null @@ -1,31 +0,0 @@ -
-
-
-

Node Detail

-
-
- {% 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 %} - {% 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 %} - {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.mac_addresses %} -
-
-
- - diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html b/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html index 4802dc048..1bb851e0c 100644 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/_nodes_formset_form.html @@ -1,32 +1,31 @@ -
-
+
+ {% include 'horizon/common/_form_errors.html' with form=form %}
-

Node Detail

+

{% trans "Node Detail" %}

-
-
Power Management
- {% 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 %} - {% 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 %} -
-
-
Networking
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.mac_addresses required=True %} -
-
-
-
Hardware
+
{% trans "Power Management" %}
+ {% 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 %} + {% 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 %} +
+
+
- -
-
+
{% trans "Networking" %}
+ {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.mac_addresses required=True %} +
{% trans "Hardware" %}
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.cpu_arch required=True %} {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.cpus extra_text=_('units') required=True %} {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.memory_mb extra_text=_('MB') required=True %} @@ -45,5 +44,11 @@ $form.find('input[name$="-ipmi_address"]').change(function () { $nav_link.html($(this).val() || undefined_name); }); + + $form.find('input[name$="-do_autodiscovery"]').change(function () { + var $this = $(this); + $this.closest('.panel').find( + '.form-group .row').toggleClass('required', !($this.attr('checked'))); + }); }); diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/_upload.html b/tuskar_ui/infrastructure/nodes/templates/nodes/_upload.html new file mode 100644 index 000000000..7638cb49e --- /dev/null +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/_upload.html @@ -0,0 +1,24 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% load url from future %} + +{% block form_id %}upload_nodes_form{% endblock %} +{% block form_action %}{% url 'horizon:infrastructure:nodes:register' %}?upload{% endblock %} + +{% block modal_id %}upload_nodes_modal{% endblock %} +{% block modal-header %}{% trans "Upload Nodes" %}{% endblock %} +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-body %} + {% include "horizon/common/_form_fields.html" %} +{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} + diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html b/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html deleted file mode 100644 index e52ebaae1..000000000 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "infrastructure/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/auto_discover_csv.html b/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover_csv.html deleted file mode 100644 index bde32008e..000000000 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/auto_discover_csv.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "infrastructure/base.html" %} -{% load i18n %} -{% block title %}{% trans "Auto-Discover Nodes (Upload CSV)" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Auto-Discover Nodes (Upload CSV)") %} -{% endblock %} - -{% block main %} - {% include "infrastructure/nodes/_auto_discover_csv.html" %} -{% endblock %} diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/index.html b/tuskar_ui/infrastructure/nodes/templates/nodes/index.html index 3519a9b79..362c876a7 100644 --- a/tuskar_ui/infrastructure/nodes/templates/nodes/index.html +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/index.html @@ -11,18 +11,16 @@
diff --git a/tuskar_ui/infrastructure/nodes/templates/nodes/upload.html b/tuskar_ui/infrastructure/nodes/templates/nodes/upload.html new file mode 100644 index 000000000..a95bcedea --- /dev/null +++ b/tuskar_ui/infrastructure/nodes/templates/nodes/upload.html @@ -0,0 +1,11 @@ +{% extends "infrastructure/base.html" %} +{% load i18n %} +{% block title %}{% trans "Upload Nodes" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Upload Nodes") %} +{% endblock %} + +{% block main %} + {% include "infrastructure/nodes/_upload.html" %} +{% endblock %} diff --git a/tuskar_ui/infrastructure/nodes/tests.py b/tuskar_ui/infrastructure/nodes/tests.py index aff4f5f2b..fab635aac 100644 --- a/tuskar_ui/infrastructure/nodes/tests.py +++ b/tuskar_ui/infrastructure/nodes/tests.py @@ -17,7 +17,7 @@ import json from django.core import urlresolvers from horizon import exceptions as horizon_exceptions -from mock import patch, call # noqa +from mock import patch, call, ANY # noqa from openstack_dashboard.test import helpers from openstack_dashboard.test.test_data import utils @@ -157,11 +157,11 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): 'create.return_value': node, }) as Node: res = self.client.post(REGISTER_URL, data) + self.assertNoFormErrors(res) 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, + ANY, ipmi_address=u'127.0.0.1', cpu_arch='x86', cpus=1, @@ -173,7 +173,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): driver='ipmi', ), call( - request, + ANY, ipmi_address=u'127.0.0.2', cpu_arch='x86', cpus=4, @@ -216,10 +216,9 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): }) as Node: res = self.client.post(REGISTER_URL, data) 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, + ANY, ipmi_address=u'127.0.0.1', cpu_arch='x86', cpus=1, @@ -231,7 +230,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase): driver='ipmi', ), call( - request, + ANY, ipmi_address=u'127.0.0.2', cpu_arch='x86', cpus=4, diff --git a/tuskar_ui/infrastructure/nodes/urls.py b/tuskar_ui/infrastructure/nodes/urls.py index d1dc09356..c26dd16e1 100644 --- a/tuskar_ui/infrastructure/nodes/urls.py +++ b/tuskar_ui/infrastructure/nodes/urls.py @@ -22,9 +22,7 @@ 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'^auto-discover-csv/$', views.AutoDiscoverCSVView.as_view(), + urls.url(r'^auto-discover-csv/$', views.UploadView.as_view(), name='auto-discover-csv'), urls.url(r'^nodes_performance/$', views.PerformanceView.as_view(), name='nodes_performance'), diff --git a/tuskar_ui/infrastructure/nodes/views.py b/tuskar_ui/infrastructure/nodes/views.py index 6f80da87c..0db92b2ec 100644 --- a/tuskar_ui/infrastructure/nodes/views.py +++ b/tuskar_ui/infrastructure/nodes/views.py @@ -14,9 +14,11 @@ import json from django.core.urlresolvers import reverse_lazy -from django import http +import django.forms +import django.http from django.utils.translation import ugettext_lazy as _ from django.views.generic import base +from horizon import exceptions from horizon import forms as horizon_forms from horizon import tabs as horizon_tabs from horizon.utils import memoized @@ -50,32 +52,35 @@ class RegisterView(horizon_forms.ModalFormView): return [] def get_form(self, form_class): - return form_class(self.request.POST or None, - initial=self.get_data(), - prefix=self.form_prefix) + initial = [] + if self.request.FILES: + csv_form = forms.UploadNodeForm(self.request, + files=self.request.FILES) + if csv_form.is_valid(): + initial = csv_form.get_data() + formset = forms.RegisterNodeFormset( + None, + initial=initial, + prefix=self.form_prefix, + ) + formset.extra = 0 + return formset + return forms.RegisterNodeFormset( + self.request.POST or None, + initial=initial, + 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' +class UploadView(horizon_forms.ModalFormView): + form_class = forms.UploadNodeForm + template_name = 'infrastructure/nodes/upload.html' success_url = reverse_lazy( 'horizon:infrastructure:nodes:index') - def get_data(self): - return [] - - def get_form(self, form_class): - return form_class(self.request.POST or None, - initial=self.get_data(), - prefix=self.form_prefix) - - -class AutoDiscoverCSVView(horizon_forms.ModalFormView): - form_class = forms.AutoDiscoverCSVNodeForm - template_name = 'infrastructure/nodes/auto_discover_csv.html' - success_url = reverse_lazy( - 'horizon:infrastructure:nodes:index') + def post(self, request, *args, **kwargs): + # This form's POST is handled in RegisterView. + raise exceptions.NotFound() class DetailView(horizon_tabs.TabView): @@ -138,5 +143,5 @@ class PerformanceView(base.TemplateView): date_from=date_from, date_to=date_to, stats_attr=stats_attr, barchart=barchart) - return http.HttpResponse(json.dumps(json_output), - content_type='application/json') + return django.http.HttpResponse( + json.dumps(json_output), content_type='application/json') diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js index d3d8bc891..ef9c3b4c7 100644 --- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js +++ b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js @@ -32,7 +32,7 @@ tuskar.menu_formset = (function () { function add_delete_link($nav_item) { var $form = $content.find($nav_item.find('a').attr('href')); - $nav_item.prepend(''); + $nav_item.prepend(''); $nav_item.find('span.delete-icon:first').click(function () { var count; $form.remove(); @@ -53,13 +53,24 @@ tuskar.menu_formset = (function () { $nav.append('
  • Undefined node
  • '); $new_nav = $nav.find('li > a:last'); add_delete_link($new_nav.parent()); - $new_nav.click(function () { $(this).tab('show'); }); + $new_nav.click(function () { + $(this).tab('show'); + $('select.switchable').trigger('change'); + }); $new_nav.tab('show'); + $('select.switchable').trigger('change'); } // Connect all signals. $('a.add-node-link').click(add_node); - $nav.find('li').each(function () { add_delete_link($(this)); }); + $nav.find('li').each(function () { + add_delete_link($(this)); + }); + $nav.find('li a').click(function () { + window.setTimeout(function () { + $('select.switchable').trigger('change'); + }, 0); + }); // Activate the first field that has errors. $content.find('.control-group.error').each(function () { @@ -68,6 +79,7 @@ tuskar.menu_formset = (function () { activated = true; } }); + }; return module; diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss index 88861707f..63f47ec93 100644 --- a/tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss +++ b/tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss @@ -78,10 +78,18 @@ $link-color: #428bca; font-weight: normal; } + .panel { + margin: 8px -8px 8px -8px; + padding: 8px; + .panel-heading { + margin: -8px -8px 0 -8px; + } + } + fieldset .form-group { width: 100%; input, textarea, select { - width: 150px; + width: 100%; margin-left: 5px; } }