From 65c4895c36194ff192dee277769dc18e4cc448f8 Mon Sep 17 00:00:00 2001 From: Rajat Vig Date: Mon, 12 Oct 2015 23:31:15 -0700 Subject: [PATCH] Add angular flavors panel Adding a new panel to the admin dashboard that will use angular js instead of the django templates To test set DISABLED = False in _2081_admin_flavors_panel.py Co-Authored-By: Rajat Vig Co-Authored-By: Errol Pais Co-Authored-By: Kristine Brown Change-Id: I9394ddfe3791aeb7a52194f37e1e668e33c0325b Partially-Implements: blueprint ng-flavors --- .../static/framework/util/filters/filters.js | 12 ++++ .../framework/util/filters/filters.spec.js | 2 + .../dashboards/admin/ngflavors/__init__.py | 0 .../dashboards/admin/ngflavors/panel.py | 24 +++++++ .../ngflavors/templates/ngflavors/index.html | 11 +++ .../dashboards/admin/ngflavors/urls.py | 25 +++++++ .../dashboards/admin/ngflavors/views.py | 19 ++++++ .../static/dashboard/admin/admin.module.js | 1 + .../flavors/filters/has-extras.filter.js | 44 ++++++++++++ .../flavors/filters/has-extras.filter.spec.js | 43 ++++++++++++ .../dashboard/admin/flavors/flavors.module.js | 30 +++++++++ .../admin/flavors/flavors.module.spec.js | 24 +++++++ .../flavors/table/flavors-table.controller.js | 56 ++++++++++++++++ .../table/flavors-table.controller.spec.js | 67 +++++++++++++++++++ .../admin/flavors/table/flavors-table.html | 59 ++++++++++++++++ .../enabled/_2081_admin_flavors_panel.py | 31 +++++++++ 16 files changed, 448 insertions(+) create mode 100644 openstack_dashboard/dashboards/admin/ngflavors/__init__.py create mode 100644 openstack_dashboard/dashboards/admin/ngflavors/panel.py create mode 100644 openstack_dashboard/dashboards/admin/ngflavors/templates/ngflavors/index.html create mode 100644 openstack_dashboard/dashboards/admin/ngflavors/urls.py create mode 100644 openstack_dashboard/dashboards/admin/ngflavors/views.py create mode 100644 openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.js create mode 100644 openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.spec.js create mode 100644 openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.js create mode 100644 openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.spec.js create mode 100644 openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.js create mode 100644 openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.spec.js create mode 100644 openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.html create mode 100644 openstack_dashboard/enabled/_2081_admin_flavors_panel.py diff --git a/horizon/static/framework/util/filters/filters.js b/horizon/static/framework/util/filters/filters.js index fb7e873b02..a79d4a4a2d 100644 --- a/horizon/static/framework/util/filters/filters.js +++ b/horizon/static/framework/util/filters/filters.js @@ -51,8 +51,14 @@ */ function gbFilter() { return function (input) { + var tb = 1024; + if (isNaN(input) || null === input) { return ''; + } else if (input >= tb) { + return interpolate(gettext("%s TB"), [parseFloat(Number(input / tb).toFixed(2))]); + } else if (input === '') { + return interpolate(gettext("0 GB")); } else { return interpolate(gettext("%s GB"), [input.toString()]); } @@ -68,8 +74,14 @@ */ function mbFilter() { return function (input) { + var gb = 1024; + if (isNaN(input) || null === input) { return ''; + } else if (input >= gb) { + return interpolate(gettext("%s GB"), [parseFloat(Number(input / gb).toFixed(2))]); + } else if (input === '') { + return interpolate(gettext("0 MB")); } else { return interpolate(gettext("%s MB"), [input.toString()]); } diff --git a/horizon/static/framework/util/filters/filters.spec.js b/horizon/static/framework/util/filters/filters.spec.js index 7d228c68f6..18b142948b 100644 --- a/horizon/static/framework/util/filters/filters.spec.js +++ b/horizon/static/framework/util/filters/filters.spec.js @@ -47,6 +47,7 @@ it('returns given numeric value properly', function () { expect(gbFilter(12)).toBe('12 GB'); + expect(gbFilter(1200)).toBe('1.17 TB'); expect(gbFilter(-12)).toBe('-12 GB'); expect(gbFilter(12.12)).toBe('12.12 GB'); }); @@ -68,6 +69,7 @@ it('returns given numeric value properly', function () { expect(mbFilter(12)).toBe('12 MB'); + expect(mbFilter(1200)).toBe('1.17 GB'); expect(mbFilter(-12)).toBe('-12 MB'); expect(mbFilter(12.12)).toBe('12.12 MB'); }); diff --git a/openstack_dashboard/dashboards/admin/ngflavors/__init__.py b/openstack_dashboard/dashboards/admin/ngflavors/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openstack_dashboard/dashboards/admin/ngflavors/panel.py b/openstack_dashboard/dashboards/admin/ngflavors/panel.py new file mode 100644 index 0000000000..f485bee2b4 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/ngflavors/panel.py @@ -0,0 +1,24 @@ +# (c) Copyright 2015 Hewlett-Packard Development Company, L.P. +# (c) Copyright 2015 ThoughtWorks, Inc. +# +# 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. + +from django.utils.translation import ugettext_lazy as _ + +import horizon + + +class NGFlavors(horizon.Panel): + name = _("Flavors") + slug = 'ngflavors' + permissions = ('openstack.services.compute',) diff --git a/openstack_dashboard/dashboards/admin/ngflavors/templates/ngflavors/index.html b/openstack_dashboard/dashboards/admin/ngflavors/templates/ngflavors/index.html new file mode 100644 index 0000000000..5825686a28 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/ngflavors/templates/ngflavors/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Flavors" %}{% endblock %} + +{% block page_header %} + +{% endblock %} + +{% block main %} + +{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/ngflavors/urls.py b/openstack_dashboard/dashboards/admin/ngflavors/urls.py new file mode 100644 index 0000000000..c5b5daf8a3 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/ngflavors/urls.py @@ -0,0 +1,25 @@ +# (c) Copyright 2015 Hewlett-Packard Development Company, L.P. +# (c) Copyright 2015 ThoughtWorks, Inc. +# +# 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. + +from django.conf.urls import patterns +from django.conf.urls import url + +from openstack_dashboard.dashboards.admin.ngflavors import views + + +urlpatterns = patterns( + 'openstack_dashboard.dashboards.admin.ngflavors.views', + url(r'^$', views.IndexView.as_view(), name='index'), +) diff --git a/openstack_dashboard/dashboards/admin/ngflavors/views.py b/openstack_dashboard/dashboards/admin/ngflavors/views.py new file mode 100644 index 0000000000..3f7039a924 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/ngflavors/views.py @@ -0,0 +1,19 @@ +# (c) Copyright 2015 Hewlett-Packard Development Company, L.P. +# (c) Copyright 2015 ThoughtWorks, Inc. +# +# 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 horizon.views as views + + +class IndexView(views.HorizonTemplateView): + template_name = 'admin/ngflavors/index.html' diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/admin.module.js b/openstack_dashboard/dashboards/admin/static/dashboard/admin/admin.module.js index f7ae6a6130..b068dca036 100644 --- a/openstack_dashboard/dashboards/admin/static/dashboard/admin/admin.module.js +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/admin.module.js @@ -25,6 +25,7 @@ */ angular .module('horizon.dashboard.admin', [ + 'horizon.dashboard.admin.flavors' ]) .config(config); diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.js b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.js new file mode 100644 index 0000000000..ca20ba6fbb --- /dev/null +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.js @@ -0,0 +1,44 @@ +/** + * + * 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.dashboard.admin.flavors') + .filter('hasExtras', hasExtrasFilter); + + hasExtrasFilter.$inject = []; + + /** + * @ngdoc filter + * @name hasExtrasFilter + * @description + * If input is defined and has more than one property return 'Yes' else return 'No' + * + */ + function hasExtrasFilter(gettext) { + return function check(input) { + if (input && + angular.isObject(input) && + !angular.isArray(input) && + Object.keys(input).length > 0) { + return true; + } + + return false; + }; + } + +})(); diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.spec.js b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.spec.js new file mode 100644 index 0000000000..5cd9503936 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/filters/has-extras.filter.spec.js @@ -0,0 +1,43 @@ +/** + * + * 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'; + + describe('horizon.dashboard.admin.flavors.hasExtras', function() { + + var hasExtras; + + beforeEach(module('horizon.framework.util.i18n')); + beforeEach(module('horizon.dashboard.admin.flavors')); + + beforeEach(inject(function(_hasExtrasFilter_) { + hasExtras = _hasExtrasFilter_; + })); + + it('returns Yes when key is present', function() { + var input = { 1: 'test' }; + expect(hasExtras(input)).toBeTruthy(); + }); + + it('returns No when object is undefined or has no properties', function() { + expect(hasExtras(undefined)).not.toBeTruthy(); + expect(hasExtras({})).not.toBeTruthy(); + expect(hasExtras('string')).not.toBeTruthy(); + expect(hasExtras(1)).not.toBeTruthy(); + expect(hasExtras([1])).not.toBeTruthy(); + }); + }); + +})(); diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.js b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.js new file mode 100644 index 0000000000..2f1f106745 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.js @@ -0,0 +1,30 @@ +/** + * + * 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'; + + /** + * @ngdoc horizon.dashboard.admin.flavors + * @ngModule + * + * @description + * Provides all of the services and widgets required + * to support and display the flavors panel. + */ + angular + .module('horizon.dashboard.admin.flavors', []); + +})(); diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.spec.js b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.spec.js new file mode 100644 index 0000000000..5a35e76c5f --- /dev/null +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/flavors.module.spec.js @@ -0,0 +1,24 @@ +/** + * + * 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'; + + describe('horizon.dashboard.admin.flavors', function () { + it('should exist', function () { + expect(angular.module('horizon.dashboard.admin.flavors')).toBeDefined(); + }); + }); + +})(); diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.js b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.js new file mode 100644 index 0000000000..b8bc4487c1 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.js @@ -0,0 +1,56 @@ +/** + * + * 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.dashboard.admin.flavors') + .controller('FlavorsTableController', FlavorsTableController); + + FlavorsTableController.$inject = [ + 'horizon.app.core.openstack-service-api.nova' + ]; + + /** + * @ngdoc FlavorsTableController + * @ngController + * + * @description + * Controller for the flavors panel. + * Serves as the focal point for table actions. + */ + function FlavorsTableController( + nova + ) { + var ctrl = this; + + ctrl.flavors = []; + ctrl.iflavors = []; + + init(); + + //////////////////////////////// + + function init() { + nova.getFlavors(true, true).then(onGetFlavors); + } + + function onGetFlavors(response) { + ctrl.flavors = response.data.items; + } + + } +})(); diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.spec.js b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.spec.js new file mode 100644 index 0000000000..6cda2da8a3 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.controller.spec.js @@ -0,0 +1,67 @@ +/** + * + * 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'; + + describe('horizon.dashboard.admin.flavors.controller.FlavorsTableController', function () { + + var flavors = [{id: '1'}, {id: '2'}]; + + var novaAPI = { + getFlavors: function(params) { + var deferred = $q.defer(); + deferred.resolve({data: {items: flavors}}); + return deferred.promise; + } + }; + + var controller, $q, $scope; + + beforeEach(module('horizon.framework')); + + beforeEach(module('horizon.app.core.openstack-service-api', function($provide) { + $provide.value('horizon.app.core.openstack-service-api.nova', novaAPI); + })); + + beforeEach(module('horizon.dashboard.admin', function($provide) { + $provide.constant('horizon.dashboard.admin.basePath', '/a/sample/path/'); + })); + + beforeEach(module('horizon.dashboard.admin.flavors')); + + beforeEach(inject(function ($injector, _$rootScope_) { + $scope = _$rootScope_.$new(); + $q = $injector.get('$q'); + controller = $injector.get('$controller'); + })); + + function createController() { + return controller('FlavorsTableController', {}); + } + + it('should invoke nova apis', function() { + spyOn(novaAPI, 'getFlavors').and.callThrough(); + + var ctrl = createController(); + $scope.$apply(); + + expect(novaAPI.getFlavors).toHaveBeenCalled(); + expect(ctrl.flavors).toEqual(flavors); + expect(ctrl.iflavors).toBeDefined(); + }); + + }); + +})(); diff --git a/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.html b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.html new file mode 100644 index 0000000000..b887affeeb --- /dev/null +++ b/openstack_dashboard/dashboards/admin/static/dashboard/admin/flavors/table/flavors-table.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + Flavor NameVCPUsRAMRoot DiskEphemeral DiskSwap DiskIDPublicMetadata
+ + {$ flavor.name $}{$ flavor.vcpus $}{$ flavor.ram | mb $}{$ flavor.disk | gb $}{$ flavor.ephemeral | gb $}{$ flavor.swap | mb $}{$ flavor.id $}{$ flavor.is_public | yesno $}{$ flavor.extras | hasExtras | yesno $} +
diff --git a/openstack_dashboard/enabled/_2081_admin_flavors_panel.py b/openstack_dashboard/enabled/_2081_admin_flavors_panel.py new file mode 100644 index 0000000000..7efcbd2066 --- /dev/null +++ b/openstack_dashboard/enabled/_2081_admin_flavors_panel.py @@ -0,0 +1,31 @@ +# (c) Copyright 2015 Hewlett-Packard Development Company, L.P. +# (c) Copyright 2015 ThoughtWorks, Inc. +# +# 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. + +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'admin' + +# The slug of the panel group the PANEL is associated with. +# If you want the panel to show up without a panel group, +# use the panel group "default". +PANEL_GROUP = 'admin' + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'ngflavors' + +# If set to True, this settings file will not be added to the settings. +DISABLED = True + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'openstack_dashboard.dashboards.admin.ngflavors.panel.NGFlavors'