From 3c2ce866df58eccb4eef44ae766997236c074ff1 Mon Sep 17 00:00:00 2001 From: Szymon Wroblewski Date: Tue, 19 May 2015 16:14:59 +0200 Subject: [PATCH] Angularize metadata update modals Update table action to use new angular modal dialog. Remove "Metadata Update" views. Implemenents: blueprint angularize-metadata-update-modals Change-Id: I7b3d2eab8c0830dc44a45901981eb0e71009c038 --- .../modal-form-update-metadata-ctrl.js | 48 -------- .../common/_modal_form_update_metadata.html | 29 ----- .../dashboards/admin/aggregates/constants.py | 4 - .../dashboards/admin/aggregates/forms.py | 31 ----- .../dashboards/admin/aggregates/tables.py | 14 ++- .../aggregates/_update_metadata.html | 11 -- .../templates/aggregates/update_metadata.html | 7 -- .../dashboards/admin/aggregates/tests.py | 81 ------------ .../dashboards/admin/aggregates/urls.py | 2 - .../dashboards/admin/aggregates/views.py | 53 -------- .../dashboards/admin/flavors/constants.py | 3 - .../dashboards/admin/flavors/forms.py | 50 -------- .../dashboards/admin/flavors/tables.py | 35 ++++-- .../templates/flavors/_update_metadata.html | 11 -- .../templates/flavors/update_metadata.html | 7 -- .../dashboards/admin/flavors/tests.py | 116 ------------------ .../dashboards/admin/flavors/urls.py | 2 - .../dashboards/admin/flavors/views.py | 58 --------- .../dashboards/admin/images/forms.py | 39 ------ .../dashboards/admin/images/tables.py | 14 ++- .../templates/images/_update_metadata.html | 1 - .../templates/images/update_metadata.html | 7 -- .../dashboards/admin/images/tests.py | 64 ---------- .../dashboards/admin/images/urls.py | 2 - .../dashboards/admin/images/views.py | 83 ------------- 25 files changed, 51 insertions(+), 721 deletions(-) delete mode 100644 horizon/static/framework/util/tech-debt/modal-form-update-metadata-ctrl.js delete mode 100644 horizon/templates/horizon/common/_modal_form_update_metadata.html delete mode 100644 openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/_update_metadata.html delete mode 100644 openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html delete mode 100644 openstack_dashboard/dashboards/admin/flavors/forms.py delete mode 100644 openstack_dashboard/dashboards/admin/flavors/templates/flavors/_update_metadata.html delete mode 100644 openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html delete mode 100644 openstack_dashboard/dashboards/admin/images/templates/images/_update_metadata.html delete mode 100644 openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html diff --git a/horizon/static/framework/util/tech-debt/modal-form-update-metadata-ctrl.js b/horizon/static/framework/util/tech-debt/modal-form-update-metadata-ctrl.js deleted file mode 100644 index a2c7994819..0000000000 --- a/horizon/static/framework/util/tech-debt/modal-form-update-metadata-ctrl.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * 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. - */ -(function () { - 'use strict'; - - angular - .module('horizon.framework.util.tech-debt') - .controller('hzModalFormUpdateMetadataController', hzModalFormUpdateMetadataController); - - hzModalFormUpdateMetadataController.$inject = ['$scope', '$window']; - - function hzModalFormUpdateMetadataController($scope, $window) { - var ctrl = this; - - ctrl.tree = null; - ctrl.available = $window.available_metadata.namespaces; - ctrl.existing = $window.existing_metadata; - - ctrl.saveMetadata = function () { - var metadata = []; - angular.forEach(ctrl.tree.getExisting(), function (value, key) { - metadata.push({ - key: key, - value: value - - }); - - }); - - ctrl.metadata = angular.toJson(metadata); - - }; - - } - -}()); diff --git a/horizon/templates/horizon/common/_modal_form_update_metadata.html b/horizon/templates/horizon/common/_modal_form_update_metadata.html deleted file mode 100644 index af654972b3..0000000000 --- a/horizon/templates/horizon/common/_modal_form_update_metadata.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} - -{% load i18n %} - -{% block form_name %}metadataForm{% endblock %} -{% block form_validation %}novalidate{% endblock %} -{% block ng_controller %}hzModalFormUpdateMetadataController as metadataCtrl{% endblock %} - -{% block modal-body %} - - -{% endblock %} - -{% block modal-footer %} -
- - {% trans "Cancel" %} - -
-{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/aggregates/constants.py b/openstack_dashboard/dashboards/admin/aggregates/constants.py index 41e54fe907..4b915c2798 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/constants.py +++ b/openstack_dashboard/dashboards/admin/aggregates/constants.py @@ -17,9 +17,5 @@ AGGREGATES_CREATE_URL = 'horizon:admin:aggregates:create' AGGREGATES_CREATE_VIEW_TEMPLATE = 'admin/aggregates/create.html' AGGREGATES_MANAGE_HOSTS_URL = 'horizon:admin:aggregates:manage_hosts' AGGREGATES_MANAGE_HOSTS_TEMPLATE = 'admin/aggregates/manage_hosts.html' -AGGREGATES_UPDATE_METADATA_URL = 'horizon:admin:aggregates:update_metadata' -AGGREGATES_UPDATE_METADATA_TEMPLATE = 'admin/aggregates/update_metadata.html' -AGGREGATES_UPDATE_METADATA_SUBTEMPLATE = \ - 'admin/aggregates/_update_metadata.html' AGGREGATES_UPDATE_URL = 'horizon:admin:aggregates:update' AGGREGATES_UPDATE_VIEW_TEMPLATE = 'admin/aggregates/update.html' diff --git a/openstack_dashboard/dashboards/admin/aggregates/forms.py b/openstack_dashboard/dashboards/admin/aggregates/forms.py index bd261868c6..07d88704c3 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/forms.py +++ b/openstack_dashboard/dashboards/admin/aggregates/forms.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import json - from django.utils.translation import ugettext_lazy as _ from horizon import exceptions @@ -49,32 +47,3 @@ class UpdateAggregateForm(forms.SelfHandlingForm): exceptions.handle(request, _('Unable to update the aggregate.')) return True - - -class UpdateMetadataForm(forms.SelfHandlingForm): - - def handle(self, request, data): - id = self.initial['id'] - old_metadata = self.initial['metadata'] - - try: - new_metadata = json.loads(self.data['metadata']) - - metadata = dict( - (item['key'], str(item['value'])) - for item in new_metadata - ) - - for key in old_metadata: - if key not in metadata: - metadata[key] = None - - api.nova.aggregate_set_metadata(request, id, metadata) - message = _('Metadata successfully updated.') - messages.success(request, message) - except Exception: - msg = _('Unable to update the aggregate metadata.') - exceptions.handle(request, msg) - - return False - return True diff --git a/openstack_dashboard/dashboards/admin/aggregates/tables.py b/openstack_dashboard/dashboards/admin/aggregates/tables.py index dfbbc53098..7d4e61b169 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/tables.py +++ b/openstack_dashboard/dashboards/admin/aggregates/tables.py @@ -62,9 +62,19 @@ class ManageHostsAction(tables.LinkAction): class UpdateMetadataAction(tables.LinkAction): name = "update-metadata" verbose_name = _("Update Metadata") - url = constants.AGGREGATES_UPDATE_METADATA_URL - classes = ("ajax-modal",) + ajax = False icon = "pencil" + attrs = {"ng-controller": "MetadataModalHelperController as modal"} + + def __init__(self, attrs=None, **kwargs): + kwargs['preempt'] = True + super(UpdateMetadataAction, self).__init__(attrs, **kwargs) + + def get_link_url(self, datum): + image_id = self.table.get_object_id(datum) + self.attrs['ng-click'] = ( + "modal.openMetadataModal('aggregate', '%s', true)" % image_id) + return "javascript:void(0);" class UpdateAggregateAction(tables.LinkAction): diff --git a/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/_update_metadata.html b/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/_update_metadata.html deleted file mode 100644 index 9f2e0c414f..0000000000 --- a/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/_update_metadata.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'horizon/common/_modal_form_update_metadata.html' %} -{% load i18n %} -{% load url from future %} -{% block title %}{% trans "Update Aggregate Metadata" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Aggregate Metadata") %} -{% endblock page_header %} - -{% block form_action %}{% url 'horizon:admin:aggregates:update_metadata' id %}{% endblock %} -{% block modal-header %}{% trans "Update Aggregate Metadata" %}{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html b/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html deleted file mode 100644 index de3dbfd735..0000000000 --- a/openstack_dashboard/dashboards/admin/aggregates/templates/aggregates/update_metadata.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Update Aggregate Metadata" %}{% endblock %} - -{% block main %} - {% include 'admin/aggregates/_update_metadata.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/aggregates/tests.py b/openstack_dashboard/dashboards/admin/aggregates/tests.py index a716f1f87b..e95b1b17e1 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/tests.py +++ b/openstack_dashboard/dashboards/admin/aggregates/tests.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import json - import mock from django.core.urlresolvers import reverse @@ -451,82 +449,3 @@ class ManageHostsTests(test.BaseAdminViewTests): form_data, addAggregate=False, cleanAggregates=True) - - -class HostAggregateMetadataTests(test.BaseAdminViewTests): - - @test.create_stubs({api.nova: ('aggregate_get',), - api.glance: ('metadefs_namespace_list', - 'metadefs_namespace_get')}) - def test_host_aggregate_metadata_get(self): - aggregate = self.aggregates.first() - api.nova.aggregate_get( - IsA(http.HttpRequest), - str(aggregate.id) - ).AndReturn(aggregate) - - namespaces = self.metadata_defs.list() - - api.glance.metadefs_namespace_list( - IsA(http.HttpRequest), - filters={'resource_types': ['OS::Nova::Aggregate']} - ).AndReturn((namespaces, False, False)) - - for namespace in namespaces: - api.glance.metadefs_namespace_get( - IsA(http.HttpRequest), - namespace.namespace, - 'OS::Nova::Aggregate' - ).AndReturn(namespace) - - self.mox.ReplayAll() - - res = self.client.get( - reverse(constants.AGGREGATES_UPDATE_METADATA_URL, - args=[aggregate.id])) - - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed( - res, - constants.AGGREGATES_UPDATE_METADATA_TEMPLATE - ) - self.assertTemplateUsed( - res, - constants.AGGREGATES_UPDATE_METADATA_SUBTEMPLATE - ) - self.assertContains(res, 'namespace_1') - self.assertContains(res, 'namespace_2') - self.assertContains(res, 'namespace_3') - self.assertContains(res, 'namespace_4') - - @test.create_stubs({api.nova: ('aggregate_get', 'aggregate_set_metadata')}) - def test_host_aggregate_metadata_update(self): - aggregate = self.aggregates.first() - aggregate.metadata = {'key': 'test_key', 'value': 'test_value'} - - api.nova.aggregate_get( - IsA(http.HttpRequest), - str(aggregate.id) - ).AndReturn(aggregate) - - api.nova.aggregate_set_metadata( - IsA(http.HttpRequest), - str(aggregate.id), - {'value': None, 'key': None, 'test_key': 'test_value'} - ).AndReturn(None) - - self.mox.ReplayAll() - - form_data = {"metadata": json.dumps([aggregate.metadata])} - - res = self.client.post( - reverse(constants.AGGREGATES_UPDATE_METADATA_URL, - args=(aggregate.id,)), form_data) - - self.assertEqual(res.status_code, 302) - self.assertNoFormErrors(res) - self.assertMessageCount(success=1) - self.assertRedirectsNoFollow( - res, - reverse(constants.AGGREGATES_INDEX_URL) - ) diff --git a/openstack_dashboard/dashboards/admin/aggregates/urls.py b/openstack_dashboard/dashboards/admin/aggregates/urls.py index 49650b5c78..50ac748bda 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/urls.py +++ b/openstack_dashboard/dashboards/admin/aggregates/urls.py @@ -25,8 +25,6 @@ urlpatterns = patterns( views.CreateView.as_view(), name='create'), url(r'^(?P[^/]+)/update/$', views.UpdateView.as_view(), name='update'), - url(r'^(?P[^/]+)/update_metadata/$', - views.UpdateMetadataView.as_view(), name='update_metadata'), url(r'^(?P[^/]+)/manage_hosts/$', views.ManageHostsView.as_view(), name='manage_hosts'), ) diff --git a/openstack_dashboard/dashboards/admin/aggregates/views.py b/openstack_dashboard/dashboards/admin/aggregates/views.py index fd0b2b270a..515739450e 100644 --- a/openstack_dashboard/dashboards/admin/aggregates/views.py +++ b/openstack_dashboard/dashboards/admin/aggregates/views.py @@ -10,16 +10,12 @@ # License for the specific language governing permissions and limitations # under the License. -import json - -from django.core.urlresolvers import reverse 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.utils import memoized from horizon import workflows from openstack_dashboard import api @@ -101,55 +97,6 @@ class UpdateView(forms.ModalFormView): return self._object -class UpdateMetadataView(forms.ModalFormView): - template_name = constants.AGGREGATES_UPDATE_METADATA_TEMPLATE - form_class = aggregate_forms.UpdateMetadataForm - success_url = reverse_lazy(constants.AGGREGATES_INDEX_URL) - page_title = _("Update Aggregate Metadata") - - def get_initial(self): - aggregate = self.get_object() - return {'id': self.kwargs["id"], 'metadata': aggregate.metadata} - - def get_context_data(self, **kwargs): - context = super(UpdateMetadataView, self).get_context_data(**kwargs) - - aggregate = self.get_object() - context['existing_metadata'] = json.dumps(aggregate.metadata) - - resource_type = 'OS::Nova::Aggregate' - namespaces = [] - try: - # metadefs_namespace_list() returns a tuple with list as 1st elem - namespaces = [ - api.glance.metadefs_namespace_get(self.request, x.namespace, - resource_type) - for x in api.glance.metadefs_namespace_list( - self.request, - filters={'resource_types': [resource_type]} - )[0] - ] - - except Exception: - msg = _('Unable to retrieve available metadata for aggregate.') - exceptions.handle(self.request, msg) - - context['available_metadata'] = json.dumps({'namespaces': namespaces}) - context['id'] = self.kwargs['id'] - return context - - @memoized.memoized_method - def get_object(self): - aggregate_id = self.kwargs['id'] - try: - return api.nova.aggregate_get(self.request, aggregate_id) - except Exception: - msg = _('Unable to retrieve the aggregate to be ' - 'updated.') - exceptions.handle( - self.request, msg, redirect=reverse(INDEX_URL)) - - class ManageHostsView(workflows.WorkflowView): template_name = constants.AGGREGATES_MANAGE_HOSTS_TEMPLATE workflow_class = aggregate_workflows.ManageAggregateHostsWorkflow diff --git a/openstack_dashboard/dashboards/admin/flavors/constants.py b/openstack_dashboard/dashboards/admin/flavors/constants.py index 8d304d06e9..4f6ffcee4e 100644 --- a/openstack_dashboard/dashboards/admin/flavors/constants.py +++ b/openstack_dashboard/dashboards/admin/flavors/constants.py @@ -16,6 +16,3 @@ FLAVORS_CREATE_URL = 'horizon:admin:flavors:create' FLAVORS_CREATE_VIEW_TEMPLATE = 'admin/flavors/create.html' FLAVORS_UPDATE_URL = 'horizon:admin:flavors:update' FLAVORS_UPDATE_VIEW_TEMPLATE = 'admin/flavors/update.html' -FLAVORS_UPDATE_METADATA_URL = 'horizon:admin:flavors:update_metadata' -FLAVORS_UPDATE_METADATA_TEMPLATE = 'admin/flavors/update_metadata.html' -FLAVORS_UPDATE_METADATA_SUBTEMPLATE = 'admin/flavors/_update_metadata.html' diff --git a/openstack_dashboard/dashboards/admin/flavors/forms.py b/openstack_dashboard/dashboards/admin/flavors/forms.py deleted file mode 100644 index 81057ea386..0000000000 --- a/openstack_dashboard/dashboards/admin/flavors/forms.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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 json - -from django.utils.translation import ugettext_lazy as _ - -from horizon import exceptions -from horizon import forms -from horizon import messages - -from openstack_dashboard import api - - -class UpdateMetadataForm(forms.SelfHandlingForm): - - def handle(self, request, data): - id = self.initial['id'] - old_metadata = self.initial['metadata'] - - try: - new_metadata = json.loads(self.data['metadata']) - - metadata = dict( - (item['key'], str(item['value'])) - for item in new_metadata - ) - api.nova.flavor_extra_set(request, id, metadata) - - remove_keys = [key for key in old_metadata if key not in metadata] - - api.nova.flavor_extra_delete(request, id, remove_keys) - - message = _('Metadata successfully updated.') - messages.success(request, message) - except Exception: - exceptions.handle(request, - _('Unable to update the flavor metadata.')) - return False - return True diff --git a/openstack_dashboard/dashboards/admin/flavors/tables.py b/openstack_dashboard/dashboards/admin/flavors/tables.py index 4b11e8599a..4b68d2e172 100644 --- a/openstack_dashboard/dashboards/admin/flavors/tables.py +++ b/openstack_dashboard/dashboards/admin/flavors/tables.py @@ -66,11 +66,29 @@ class UpdateFlavor(tables.LinkAction): class UpdateMetadata(tables.LinkAction): - url = "horizon:admin:flavors:update_metadata" name = "update_metadata" verbose_name = _("Update Metadata") - classes = ("ajax-modal",) + ajax = False icon = "pencil" + attrs = {"ng-controller": "MetadataModalHelperController as modal"} + + def __init__(self, **kwargs): + kwargs['preempt'] = True + super(UpdateMetadata, self).__init__(**kwargs) + + def get_link_url(self, datum): + obj_id = self.table.get_object_id(datum) + self.attrs['ng-click'] = ( + "modal.openMetadataModal('flavor', '%s', true)" % obj_id) + return "javascript:void(0);" + + +class UpdateMetadataColumn(tables.Column): + def get_link_url(self, datum): + obj_id = self.table.get_object_id(datum) + self.link_attrs['ng-click'] = ( + "modal.openMetadataModal('flavor', '%s', true)" % obj_id) + return "javascript:void(0);" class ModifyAccess(tables.LinkAction): @@ -138,12 +156,13 @@ class FlavorsTable(tables.DataTable): verbose_name=_("Public"), empty_value=False, filters=(filters.yesno, filters.capfirst)) - extra_specs = tables.Column(get_extra_specs, - verbose_name=_("Metadata"), - link="horizon:admin:flavors:update_metadata", - link_classes=("ajax-modal",), - empty_value=False, - filters=(filters.yesno, filters.capfirst)) + extra_specs = UpdateMetadataColumn( + get_extra_specs, + verbose_name=_("Metadata"), + link=True, + empty_value=False, + filters=(filters.yesno, filters.capfirst), + link_attrs={'ng-controller': 'MetadataModalHelperController as modal'}) class Meta(object): name = "flavors" diff --git a/openstack_dashboard/dashboards/admin/flavors/templates/flavors/_update_metadata.html b/openstack_dashboard/dashboards/admin/flavors/templates/flavors/_update_metadata.html deleted file mode 100644 index 10c77f11f7..0000000000 --- a/openstack_dashboard/dashboards/admin/flavors/templates/flavors/_update_metadata.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'horizon/common/_modal_form_update_metadata.html' %} -{% load i18n %} -{% load url from future %} -{% block title %}{% trans "Update Flavor Metadata" %}{% endblock %} - -{% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Update Flavor Metadata") %} -{% endblock page_header %} - -{% block form_action %}{% url 'horizon:admin:flavors:update_metadata' id %}{% endblock %} -{% block modal-header %}{% trans "Update Flavor Metadata" %}{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html b/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html deleted file mode 100644 index f0b6c66489..0000000000 --- a/openstack_dashboard/dashboards/admin/flavors/templates/flavors/update_metadata.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Update Flavor Metadata" %}{% endblock %} - -{% block main %} - {% include 'admin/flavors/_update_metadata.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/flavors/tests.py b/openstack_dashboard/dashboards/admin/flavors/tests.py index b9922f9880..1f7a8d438a 100644 --- a/openstack_dashboard/dashboards/admin/flavors/tests.py +++ b/openstack_dashboard/dashboards/admin/flavors/tests.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import json - from django.core.urlresolvers import reverse from django import http from mox3.mox import IsA # noqa @@ -764,117 +762,3 @@ class UpdateFlavorWorkflowTests(BaseFlavorWorkflowTests): data = {'eph_gb': -1} self.generic_update_flavor_invalid_data_form_fails(override_data=data, error_msg=error) - - -class FlavorUpdateMetadataViewTest(test.BaseAdminViewTests): - @test.create_stubs({api.nova: ('flavor_get_extras',), - api.glance: ('metadefs_namespace_list', - 'metadefs_namespace_get')}) - def test_flavor_metadata_get(self): - # - flavor = self.flavors.list()[3] - - namespaces = self.metadata_defs.list() - - api.nova.flavor_get_extras( - IsA(http.HttpRequest), - flavor.id - ).AndReturn([flavor.extra_specs]) - api.glance.metadefs_namespace_list( - IsA(http.HttpRequest), - filters={ - 'resource_types': ['OS::Nova::Flavor'] - } - ).AndReturn((namespaces, False, False)) - - for namespace in namespaces: - api.glance.metadefs_namespace_get( - IsA(http.HttpRequest), - namespace.namespace, - 'OS::Nova::Flavor' - ).AndReturn(namespace) - - self.mox.ReplayAll() - res = self.client.get( - reverse( - constants.FLAVORS_UPDATE_METADATA_URL, - kwargs={'id': flavor.id} - ) - ) - - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed( - res, - constants.FLAVORS_UPDATE_METADATA_TEMPLATE - ) - self.assertTemplateUsed( - res, - constants.FLAVORS_UPDATE_METADATA_SUBTEMPLATE - ) - self.assertContains(res, 'namespace_1') - self.assertContains(res, 'namespace_2') - self.assertContains(res, 'namespace_3') - self.assertContains(res, 'namespace_4') - - @test.create_stubs({api.nova: ('flavor_get_extras', - 'flavor_extra_set', - 'flavor_extra_delete')}) - def test_flavor_metadata_update(self): - # - flavor = self.flavors.list()[3] - - api.nova.flavor_get_extras( - IsA(http.HttpRequest), - flavor.id - ).AndReturn([flavor.extra_specs]) - api.nova.flavor_extra_set( - IsA(http.HttpRequest), - flavor.id, - {'key_mock': 'value_mock'} - ).AndReturn(None) - api.nova.flavor_extra_delete( - IsA(http.HttpRequest), - flavor.id, - [] - ).AndReturn(None) - - self.mox.ReplayAll() - - metadata = [{'value': 'value_mock', 'key': 'key_mock'}] - formData = {'metadata': json.dumps(metadata)} - - res = self.client.post( - reverse( - constants.FLAVORS_UPDATE_METADATA_URL, - kwargs={'id': flavor.id} - ), - formData - ) - - self.assertEqual(res.status_code, 302) - self.assertNoFormErrors(res) - self.assertRedirectsNoFollow(res, reverse(constants.FLAVORS_INDEX_URL)) - self.assertMessageCount(success=1) - - @test.create_stubs({api.nova: ('flavor_get_extras',)}) - def test_flavor_metadata_get_get_extras_fails(self): - # - flavor = self.flavors.list()[3] - - api.nova.flavor_get_extras( - IsA(http.HttpRequest), - flavor.id - ).AndRaise(self.exceptions.nova) - - self.mox.ReplayAll() - - res = self.client.get( - reverse( - constants.FLAVORS_UPDATE_METADATA_URL, - kwargs={'id': flavor.id} - ) - ) - - self.assertEqual(res.status_code, 302) - self.assertRedirectsNoFollow(res, reverse(constants.FLAVORS_INDEX_URL)) - self.assertMessageCount(error=1) diff --git a/openstack_dashboard/dashboards/admin/flavors/urls.py b/openstack_dashboard/dashboards/admin/flavors/urls.py index daf563bfe6..1e3e52563c 100644 --- a/openstack_dashboard/dashboards/admin/flavors/urls.py +++ b/openstack_dashboard/dashboards/admin/flavors/urls.py @@ -26,7 +26,5 @@ urlpatterns = patterns( 'openstack_dashboard.dashboards.admin.flavors.views', url(r'^$', views.IndexView.as_view(), name='index'), url(r'^create/$', views.CreateView.as_view(), name='create'), - url(r'^(?P[^/]+)/update_metadata/$', - views.UpdateMetadataView.as_view(), name='update_metadata'), url(r'^(?P[^/]+)/update/$', views.UpdateView.as_view(), name='update'), ) diff --git a/openstack_dashboard/dashboards/admin/flavors/views.py b/openstack_dashboard/dashboards/admin/flavors/views.py index fec58e5983..70b8ac4083 100644 --- a/openstack_dashboard/dashboards/admin/flavors/views.py +++ b/openstack_dashboard/dashboards/admin/flavors/views.py @@ -16,22 +16,15 @@ # License for the specific language governing permissions and limitations # under the License. -import json - -from django.core.urlresolvers import reverse 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.utils import memoized from horizon import workflows from openstack_dashboard import api -from openstack_dashboard.dashboards.admin.flavors \ - import forms as project_forms from openstack_dashboard.dashboards.admin.flavors \ import tables as project_tables from openstack_dashboard.dashboards.admin.flavors \ @@ -88,54 +81,3 @@ class UpdateView(workflows.WorkflowView): 'disk_gb': flavor.disk, 'swap_mb': flavor.swap or 0, 'eph_gb': getattr(flavor, 'OS-FLV-EXT-DATA:ephemeral', None)} - - -class UpdateMetadataView(forms.ModalFormView): - template_name = "admin/flavors/update_metadata.html" - form_class = project_forms.UpdateMetadataForm - success_url = reverse_lazy('horizon:admin:flavors:index') - page_title = _("Update Flavor Metadata") - - def get_initial(self): - extra_specs_dict = self.get_object() - return {'id': self.kwargs["id"], 'metadata': extra_specs_dict} - - def get_context_data(self, **kwargs): - context = super(UpdateMetadataView, self).get_context_data(**kwargs) - - extra_specs_dict = self.get_object() - - context['existing_metadata'] = json.dumps(extra_specs_dict) - - resource_type = 'OS::Nova::Flavor' - - namespaces = [] - try: - # metadefs_namespace_list() returns a tuple with list as 1st elem - namespaces = [ - api.glance.metadefs_namespace_get(self.request, x.namespace, - resource_type) - for x in api.glance.metadefs_namespace_list( - self.request, - filters={'resource_types': [resource_type]} - )[0] - ] - - except Exception: - msg = _('Unable to retrieve available metadata for flavors.') - exceptions.handle(self.request, msg) - - context['available_metadata'] = json.dumps({'namespaces': namespaces}) - context['id'] = self.kwargs['id'] - return context - - @memoized.memoized_method - def get_object(self): - flavor_id = self.kwargs['id'] - try: - extra_specs = api.nova.flavor_get_extras(self.request, flavor_id) - return dict((i.key, i.value) for i in extra_specs) - except Exception: - msg = _('Unable to retrieve the flavor metadata.') - exceptions.handle(self.request, msg, - redirect=reverse(INDEX_URL)) diff --git a/openstack_dashboard/dashboards/admin/images/forms.py b/openstack_dashboard/dashboards/admin/images/forms.py index b6603a2ea3..fdbbede46a 100644 --- a/openstack_dashboard/dashboards/admin/images/forms.py +++ b/openstack_dashboard/dashboards/admin/images/forms.py @@ -16,16 +16,6 @@ # License for the specific language governing permissions and limitations # under the License. - -import json - -from django.utils.translation import ugettext_lazy as _ - -from horizon import exceptions -from horizon import forms -from horizon import messages - -from openstack_dashboard import api from openstack_dashboard.dashboards.project.images.images \ import forms as images_forms @@ -36,32 +26,3 @@ class AdminCreateImageForm(images_forms.CreateImageForm): class AdminUpdateImageForm(images_forms.UpdateImageForm): pass - - -class UpdateMetadataForm(forms.SelfHandlingForm): - - def handle(self, request, data): - id = self.initial['id'] - old_metadata = self.initial['metadata'] - - try: - new_metadata = json.loads(self.data['metadata']) - - metadata = dict( - (item['key'], str(item['value'])) - for item in new_metadata - ) - - remove_props = [key for key in old_metadata if key not in metadata] - - api.glance.image_update_properties(request, - id, - remove_props, - **metadata) - message = _('Metadata successfully updated.') - messages.success(request, message) - except Exception: - exceptions.handle(request, - _('Unable to update the image metadata.')) - return False - return True diff --git a/openstack_dashboard/dashboards/admin/images/tables.py b/openstack_dashboard/dashboards/admin/images/tables.py index a44aca96b6..c23e08ab7d 100644 --- a/openstack_dashboard/dashboards/admin/images/tables.py +++ b/openstack_dashboard/dashboards/admin/images/tables.py @@ -41,11 +41,21 @@ class AdminEditImage(project_tables.EditImage): class UpdateMetadata(tables.LinkAction): - url = "horizon:admin:images:update_metadata" name = "update_metadata" verbose_name = _("Update Metadata") - classes = ("ajax-modal",) + ajax = False icon = "pencil" + attrs = {"ng-controller": "MetadataModalHelperController as modal"} + + def __init__(self, attrs=None, **kwargs): + kwargs['preempt'] = True + super(UpdateMetadata, self).__init__(attrs, **kwargs) + + def get_link_url(self, datum): + image_id = self.table.get_object_id(datum) + self.attrs['ng-click'] = ( + "modal.openMetadataModal('image', '%s', true)" % image_id) + return "javascript:void(0);" class UpdateRow(tables.Row): diff --git a/openstack_dashboard/dashboards/admin/images/templates/images/_update_metadata.html b/openstack_dashboard/dashboards/admin/images/templates/images/_update_metadata.html deleted file mode 100644 index 6ff460c6e2..0000000000 --- a/openstack_dashboard/dashboards/admin/images/templates/images/_update_metadata.html +++ /dev/null @@ -1 +0,0 @@ -{% extends 'horizon/common/_modal_form_update_metadata.html' %} diff --git a/openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html b/openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html deleted file mode 100644 index e6515c6deb..0000000000 --- a/openstack_dashboard/dashboards/admin/images/templates/images/update_metadata.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Update Image Metadata" %}{% endblock %} - -{% block main %} - {% include 'admin/images/_update_metadata.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/images/tests.py b/openstack_dashboard/dashboards/admin/images/tests.py index fc327b4932..d4298fb804 100644 --- a/openstack_dashboard/dashboards/admin/images/tests.py +++ b/openstack_dashboard/dashboards/admin/images/tests.py @@ -12,8 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -import json - from django.conf import settings from django.core.urlresolvers import reverse from django import http @@ -26,10 +24,6 @@ from openstack_dashboard.test import helpers as test from openstack_dashboard.dashboards.admin.images import tables -IMAGE_METADATA_URL = reverse('horizon:admin:images:update_metadata', - kwargs={ - "id": "007e7d55-fe1e-4c5c-bf08-44b4a4964822"}) - class ImageCreateViewTest(test.BaseAdminViewTests): @test.create_stubs({api.glance: ('image_list_detailed',)}) @@ -138,64 +132,6 @@ class ImagesViewTest(test.BaseAdminViewTests): 1) self.assertContains(res, 'test_tenant', 2, 200) - @test.create_stubs({api.glance: ('image_get', - 'metadefs_namespace_list', - 'metadefs_namespace_get')}) - def test_images_metadata_get(self): - image = self.images.first() - - api.glance.image_get( - IsA(http.HttpRequest), - image.id - ).AndReturn(image) - - namespaces = self.metadata_defs.list() - - api.glance.metadefs_namespace_list(IsA(http.HttpRequest), filters={ - 'resource_types': ['OS::Glance::Image']}).AndReturn( - (namespaces, False, False)) - - for namespace in namespaces: - api.glance.metadefs_namespace_get( - IsA(http.HttpRequest), - namespace.namespace, - 'OS::Glance::Image' - ).AndReturn(namespace) - - self.mox.ReplayAll() - res = self.client.get(IMAGE_METADATA_URL) - - self.assertTemplateUsed(res, 'admin/images/update_metadata.html') - self.assertContains(res, 'namespace_1') - self.assertContains(res, 'namespace_2') - self.assertContains(res, 'namespace_3') - self.assertContains(res, 'namespace_4') - - @test.create_stubs({api.glance: ('image_get', 'image_update_properties')}) - def test_images_metadata_update(self): - image = self.images.first() - - api.glance.image_get( - IsA(http.HttpRequest), - image.id - ).AndReturn(image) - api.glance.image_update_properties( - IsA(http.HttpRequest), image.id, ['image_type'], - hw_machine_type='mock_value').AndReturn(None) - - self.mox.ReplayAll() - - metadata = [{"value": "mock_value", "key": "hw_machine_type"}] - formData = {"metadata": json.dumps(metadata)} - - res = self.client.post(IMAGE_METADATA_URL, formData) - - self.assertNoFormErrors(res) - self.assertMessageCount(success=1) - self.assertRedirectsNoFollow( - res, reverse('horizon:admin:images:index') - ) - @override_settings(API_RESULT_PAGE_SIZE=2) @test.create_stubs({api.glance: ('image_list_detailed',), api.keystone: ('tenant_list',)}) diff --git a/openstack_dashboard/dashboards/admin/images/urls.py b/openstack_dashboard/dashboards/admin/images/urls.py index 73995a1b96..b8db5fa772 100644 --- a/openstack_dashboard/dashboards/admin/images/urls.py +++ b/openstack_dashboard/dashboards/admin/images/urls.py @@ -28,8 +28,6 @@ urlpatterns = patterns( url(r'^create/$', views.CreateView.as_view(), name='create'), url(r'^(?P[^/]+)/update/$', views.UpdateView.as_view(), name='update'), - url(r'^(?P[^/]+)/update_metadata/$', - views.UpdateMetadataView.as_view(), name='update_metadata'), url(r'^(?P[^/]+)/detail/$', views.DetailView.as_view(), name='detail') ) diff --git a/openstack_dashboard/dashboards/admin/images/views.py b/openstack_dashboard/dashboards/admin/images/views.py index 8ad26540dd..1eaa6a9de9 100644 --- a/openstack_dashboard/dashboards/admin/images/views.py +++ b/openstack_dashboard/dashboards/admin/images/views.py @@ -16,21 +16,16 @@ # License for the specific language governing permissions and limitations # under the License. -import json import logging from oslo_utils import units -import six -from django import conf from django.core.urlresolvers import reverse 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.utils import memoized from openstack_dashboard import api from openstack_dashboard.dashboards.project.images.images import views @@ -149,81 +144,3 @@ class DetailView(views.DetailView): context["url"] = reverse('horizon:admin:images:index') context["actions"] = table.render_row_actions(context["image"]) return context - - -class UpdateMetadataView(forms.ModalFormView): - template_name = "admin/images/update_metadata.html" - modal_header = _("Update Image") - form_id = "update_image_form" - form_class = project_forms.UpdateMetadataForm - submit_url = "horizon:admin:images:update_metadata" - success_url = reverse_lazy('horizon:admin:images:index') - page_title = _("Update Image Metadata") - - def get_initial(self): - image = self.get_object() - return {'id': self.kwargs["id"], 'metadata': image.properties} - - def get_context_data(self, **kwargs): - context = super(UpdateMetadataView, self).get_context_data(**kwargs) - - image = self.get_object() - reserved_props = getattr(conf.settings, - 'IMAGE_RESERVED_CUSTOM_PROPERTIES', []) - image.properties = dict((k, v) - for (k, v) in six.iteritems(image.properties) - if k not in reserved_props) - context['existing_metadata'] = json.dumps(image.properties) - args = (self.kwargs['id'],) - context['submit_url'] = reverse(self.submit_url, args=args) - - resource_type = 'OS::Glance::Image' - namespaces = [] - try: - # metadefs_namespace_list() returns a tuple with list as 1st elem - available_namespaces = [x.namespace for x in - api.glance.metadefs_namespace_list( - self.request, - filters={"resource_types": - [resource_type]} - )[0]] - for namespace in available_namespaces: - details = api.glance.metadefs_namespace_get(self.request, - namespace, - resource_type) - # Filter out reserved custom properties from namespace - if reserved_props: - if hasattr(details, 'properties'): - details.properties = dict( - (k, v) - for (k, v) in six.iteritems(details.properties) - if k not in reserved_props - ) - - if hasattr(details, 'objects'): - for obj in details.objects: - obj['properties'] = dict( - (k, v) - for (k, v) in six.iteritems(obj['properties']) - if k not in reserved_props - ) - - namespaces.append(details) - - except Exception: - msg = _('Unable to retrieve available properties for image.') - exceptions.handle(self.request, msg) - - context['available_metadata'] = json.dumps({'namespaces': namespaces}) - context['id'] = self.kwargs['id'] - return context - - @memoized.memoized_method - def get_object(self): - image_id = self.kwargs['id'] - try: - return api.glance.image_get(self.request, image_id) - except Exception: - msg = _('Unable to retrieve the image to be updated.') - exceptions.handle(self.request, msg, - redirect=reverse('horizon:admin:images:index'))