diff --git a/blazar_dashboard/api/client.py b/blazar_dashboard/api/client.py index dad3ccf..e35a35a 100644 --- a/blazar_dashboard/api/client.py +++ b/blazar_dashboard/api/client.py @@ -124,6 +124,12 @@ def host_create(request, name, **kwargs): return Host(host) +def host_update(request, host_id, values): + """Update a host.""" + host = blazarclient(request).host.update(host_id, values) + return Host(host) + + def host_delete(request, host_id): """Delete a host.""" blazarclient(request).host.delete(host_id) diff --git a/blazar_dashboard/content/hosts/forms.py b/blazar_dashboard/content/hosts/forms.py new file mode 100644 index 0000000..956dbea --- /dev/null +++ b/blazar_dashboard/content/hosts/forms.py @@ -0,0 +1,64 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 logging + +from django.utils.translation import ugettext_lazy as _ +from horizon import exceptions +from horizon import forms +from horizon import messages + +from blazar_dashboard import api + +LOG = logging.getLogger(__name__) + + +class UpdateForm(forms.SelfHandlingForm): + + class Meta(object): + name = _('Update Host Parameters') + + host_id = forms.CharField( + label=_('Host ID'), widget=forms.widgets.HiddenInput, required=True) + values = forms.CharField( + label=_("Values to Update"), + required=True, + help_text=_('Enter values to update in JSON'), + widget=forms.Textarea( + attrs={'rows': 5}), + max_length=511) + + def handle(self, request, data): + try: + api.client.host_update(self.request, host_id=data.get('host_id'), + values=data.get('values')) + messages.success(request, _("Host was successfully updated.")) + return True + except Exception as e: + LOG.error('Error updating host: %s', e) + exceptions.handle(request, + message="An error occurred while updating this" + " host: %s. Please try again." % e) + + def clean(self): + cleaned_data = super(UpdateForm, self).clean() + + values = cleaned_data.get('values') + try: + values = eval(values) + cleaned_data['values'] = values + except (SyntaxError, NameError): + raise forms.ValidationError( + _('Values must written in JSON') + ) + + return cleaned_data diff --git a/blazar_dashboard/content/hosts/tables.py b/blazar_dashboard/content/hosts/tables.py index 0ee1be9..2760ec5 100644 --- a/blazar_dashboard/content/hosts/tables.py +++ b/blazar_dashboard/content/hosts/tables.py @@ -26,6 +26,13 @@ class CreateHosts(tables.LinkAction): icon = "plus" +class UpdateHost(tables.LinkAction): + name = "update" + verbose_name = _("Update Host") + url = "horizon:admin:hosts:update" + classes = ("btn-create", "ajax-modal") + + class DeleteHost(tables.DeleteAction): name = "delete" data_type_singular = _("Host") @@ -66,4 +73,4 @@ class HostsTable(tables.DataTable): name = "hosts" verbose_name = _("Hosts") table_actions = (CreateHosts, DeleteHost,) - row_actions = (DeleteHost,) + row_actions = (UpdateHost, DeleteHost,) diff --git a/blazar_dashboard/content/hosts/templates/hosts/_update.html b/blazar_dashboard/content/hosts/templates/hosts/_update.html new file mode 100644 index 0000000..7631bf5 --- /dev/null +++ b/blazar_dashboard/content/hosts/templates/hosts/_update.html @@ -0,0 +1,24 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_id %}update_host{% endblock %} +{% block form_action %}{% url 'horizon:admin:hosts:update' host.id %}{% endblock %} + +{% block modal_id %}update_host_modal{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description" %}:

+

{% trans "Update extra capabilities of the host with the provided values." %}

+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/blazar_dashboard/content/hosts/templates/hosts/update.html b/blazar_dashboard/content/hosts/templates/hosts/update.html new file mode 100644 index 0000000..2728be3 --- /dev/null +++ b/blazar_dashboard/content/hosts/templates/hosts/update.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update Host" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Update Host") %} +{% endblock page_header %} + +{% block main %} + {% include 'admin/hosts/_update.html' %} +{% endblock %} diff --git a/blazar_dashboard/content/hosts/tests.py b/blazar_dashboard/content/hosts/tests.py index c4c8894..46752c8 100644 --- a/blazar_dashboard/content/hosts/tests.py +++ b/blazar_dashboard/content/hosts/tests.py @@ -27,6 +27,8 @@ DETAIL_TEMPLATE = 'admin/hosts/detail.html' DETAIL_URL_BASE = 'horizon:admin:hosts:detail' CREATE_URL = reverse('horizon:admin:hosts:create') CREATE_TEMPLATE = 'admin/hosts/create.html' +UPDATE_URL_BASE = 'horizon:admin:hosts:update' +UPDATE_TEMPLATE = 'admin/hosts/update.html' class HostsTests(test.BaseAdminViewTests): @@ -134,6 +136,53 @@ class HostsTests(test.BaseAdminViewTests): self.assertMessageCount(success=(len(host_names) + 1)) self.assertRedirectsNoFollow(res, INDEX_URL) + @test.create_stubs({blazar_api.client: ('host_get', 'host_update')}) + def test_update_host(self): + host = self.hosts.get(hypervisor_hostname='compute-1') + blazar_api.client.host_get( + IsA(http.HttpRequest), + host['id'] + ).AndReturn(host) + blazar_api.client.host_update( + IsA(http.HttpRequest), + host_id=host['id'], + values={"key": "updated"} + ) + form_data = { + 'host_id': host['id'], + 'values': '{"key": "updated"}' + } + self.mox.ReplayAll() + + res = self.client.post(reverse(UPDATE_URL_BASE, args=[host['id']]), + form_data) + self.assertNoFormErrors(res) + self.assertMessageCount(success=1) + self.assertRedirectsNoFollow(res, INDEX_URL) + + @test.create_stubs({blazar_api.client: ('host_get', 'host_update')}) + def test_update_host_error(self): + host = self.hosts.get(hypervisor_hostname='compute-1') + blazar_api.client.host_get( + IsA(http.HttpRequest), + host['id'] + ).AndReturn(host) + blazar_api.client.host_update( + IsA(http.HttpRequest), + host_id=host['id'], + values={"key": "updated"} + ).AndRaise(self.exceptions.blazar) + form_data = { + 'host_id': host['id'], + 'values': '{"key": "updated"}' + } + self.mox.ReplayAll() + + res = self.client.post(reverse(UPDATE_URL_BASE, args=[host['id']]), + form_data) + self.assertNoFormErrors(res) + self.assertContains(res, 'An error occurred while updating') + @test.create_stubs({blazar_api.client: ('host_list', 'host_delete')}) def test_delete_host(self): hosts = self.hosts.list() diff --git a/blazar_dashboard/content/hosts/urls.py b/blazar_dashboard/content/hosts/urls.py index 34d7a60..3d9588d 100644 --- a/blazar_dashboard/content/hosts/urls.py +++ b/blazar_dashboard/content/hosts/urls.py @@ -18,5 +18,7 @@ from blazar_dashboard.content.hosts import views urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^create/$', views.CreateView.as_view(), name='create'), - url(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='detail') + url(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='detail'), + url(r'^(?P[^/]+)/update$', views.UpdateView.as_view(), + name='update'), ] diff --git a/blazar_dashboard/content/hosts/views.py b/blazar_dashboard/content/hosts/views.py index 0b1d46d..0ca9b79 100644 --- a/blazar_dashboard/content/hosts/views.py +++ b/blazar_dashboard/content/hosts/views.py @@ -10,13 +10,17 @@ # License for the specific language governing permissions and limitations # under the License. +from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import exceptions +from horizon import forms from horizon import tables from horizon import tabs +from horizon.utils import memoized from horizon import workflows from blazar_dashboard import api +from blazar_dashboard.content.hosts import forms as project_forms from blazar_dashboard.content.hosts import tables as project_tables from blazar_dashboard.content.hosts import tabs as project_tabs from blazar_dashboard.content.hosts import workflows as project_workflows @@ -45,3 +49,35 @@ class CreateView(workflows.WorkflowView): workflow_class = project_workflows.CreateHostsWorkflow template_name = 'admin/hosts/create.html' page_title = _("Create Hosts") + + +class UpdateView(forms.ModalFormView): + form_class = project_forms.UpdateForm + template_name = 'admin/hosts/update.html' + success_url = reverse_lazy('horizon:admin:hosts:index') + + def get_initial(self): + initial = super(UpdateView, self).get_initial() + + initial['host'] = self.get_object() + if initial['host']: + initial['host_id'] = initial['host'].id + initial['name'] = initial['host'].hypervisor_hostname + + return initial + + def get_context_data(self, **kwargs): + context = super(UpdateView, self).get_context_data(**kwargs) + context['host'] = self.get_object() + return context + + @memoized.memoized_method + def get_object(self): + host_id = self.kwargs['host_id'] + try: + host = api.client.host_get(self.request, host_id) + except Exception: + msg = _("Unable to retrieve host.") + redirect = reverse_lazy('horizon:admin:hosts:index') + exceptions.handle(self.request, msg, redirect=redirect) + return host