diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index 9f611217f2..b2b08bab54 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -1616,3 +1616,11 @@ def policy_list(request, **kwargs): policies = neutronclient(request).list_qos_policies( **kwargs).get('policies') return [QoSPolicy(p) for p in policies] + + +@profiler.trace +def policy_get(request, policy_id, **kwargs): + """Get QoS policy for a given policy id.""" + policy = neutronclient(request).show_qos_policy( + policy_id, **kwargs).get('policy') + return QoSPolicy(policy) diff --git a/openstack_dashboard/api/rest/neutron.py b/openstack_dashboard/api/rest/neutron.py index bbe9b52d1e..215ff0614c 100644 --- a/openstack_dashboard/api/rest/neutron.py +++ b/openstack_dashboard/api/rest/neutron.py @@ -274,3 +274,15 @@ class QoSPolicies(generic.View): result = api.neutron.policy_list(request, project_id=request.user.project_id) return {'items': [p.to_dict() for p in result]} + + +@urls.register +class QoSPolicy(generic.View): + """API for a single QoS Policy.""" + url_regex = r'neutron/qos_policy/(?P[^/]+)/$' + + @rest_utils.ajax() + def get(self, request, policy_id): + """Get a specific policy""" + policy = api.neutron.policy_get(request, policy_id) + return policy.to_dict() diff --git a/openstack_dashboard/dashboards/project/network_qos/urls.py b/openstack_dashboard/dashboards/project/network_qos/urls.py index b275bcd42b..96b6ceda87 100644 --- a/openstack_dashboard/dashboards/project/network_qos/urls.py +++ b/openstack_dashboard/dashboards/project/network_qos/urls.py @@ -15,8 +15,8 @@ from django.utils.translation import ugettext_lazy as _ from horizon.browsers import views -title = _("Network QoS Policies") +title = _("Network QoS Policies") urlpatterns = [ url(r'^$', views.AngularIndexView.as_view(title=title), name='index'), ] diff --git a/openstack_dashboard/static/app/core/network_qos/details/details.module.js b/openstack_dashboard/static/app/core/network_qos/details/details.module.js new file mode 100644 index 0000000000..ff00966df1 --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/details/details.module.js @@ -0,0 +1,54 @@ +/** + * 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 overview + * @ngname horizon.app.core.network_qos.details + * + * @description + * Provides details features for policies. + */ + angular + .module('horizon.app.core.network_qos.details', [ + 'horizon.framework.conf', + 'horizon.app.core' + ]) + .run(registerPolicyDetails); + + registerPolicyDetails.$inject = [ + 'horizon.app.core.network_qos.basePath', + 'horizon.app.core.network_qos.resourceType', + 'horizon.app.core.network_qos.service', + 'horizon.framework.conf.resource-type-registry.service' + ]; + + function registerPolicyDetails( + basePath, + qosResourceType, + qosService, + registry + ) { + registry.getResourceType(qosResourceType) + .setLoadFunction(qosService.getPolicyPromise) + .detailsViews.append({ + id: 'policyDetailsOverview', + name: gettext('Overview'), + template: basePath + 'details/overview.html' + }); + } + +})(); diff --git a/openstack_dashboard/static/app/core/network_qos/details/drawer.html b/openstack_dashboard/static/app/core/network_qos/details/drawer.html new file mode 100644 index 0000000000..10a46744ad --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/details/drawer.html @@ -0,0 +1,19 @@ +
+ + + +
+
+ + +
+
+
diff --git a/openstack_dashboard/static/app/core/network_qos/details/overview.controller.js b/openstack_dashboard/static/app/core/network_qos/details/overview.controller.js new file mode 100644 index 0000000000..542cf74de4 --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/details/overview.controller.js @@ -0,0 +1,71 @@ +/* + * 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.app.core.network_qos') + .controller('NetworkQoSOverviewController', NetworkQoSOverviewController); + + NetworkQoSOverviewController.$inject = [ + 'horizon.app.core.network_qos.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.userSession', + '$scope' + ]; + + function NetworkQoSOverviewController( + qosResourceTypeCode, + registry, + userSession, + $scope + ) { + var ctrl = this; + + ctrl.resourceType = registry.getResourceType(qosResourceTypeCode); + ctrl.tableConfig = { + selectAll: false, + expand: false, + trackId: 'id', + /* + * getTableColumns here won't work as that will give back the + * columns for the policy, but here we need columns only for the + * policy rules, which is a (list of) dictionary(ies) in the + * policy dictionary. + */ + columns: [ + {id: 'id', title: gettext('Rule ID'), priority: 1, sortDefault: true}, + {id: 'type', title: gettext('Type'), priority: 1}, + {id: 'direction', title: gettext('Direction'), priority: 1}, + {id: 'max_kbps', title: gettext('Max Kbps'), priority: 1}, + {id: 'max_burst_kbps', title: gettext('Max Burst Kbits'), priority: 1}, + {id: 'min_kbps', title: gettext('Min Kbps'), priority: 1}, + {id: 'dscp_mark', title: gettext('DSCP Mark'), priority: 1} + ] + }; + + $scope.context.loadPromise.then(onGetPolicy); + + function onGetPolicy(response) { + ctrl.policy = response.data; + + userSession.get().then(setProject); + + function setProject(session) { + ctrl.projectId = session.project_id; + } + } + } + +})(); diff --git a/openstack_dashboard/static/app/core/network_qos/details/overview.controller.spec.js b/openstack_dashboard/static/app/core/network_qos/details/overview.controller.spec.js new file mode 100644 index 0000000000..4fc47e77f7 --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/details/overview.controller.spec.js @@ -0,0 +1,74 @@ +/** + * 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('network qos overview controller', function() { + var ctrl; + var sessionObj = {project_id: '12'}; + var neutron = { + getNamespaces: angular.noop + }; + + beforeEach(module('horizon.app.core.network_qos')); + beforeEach(module('horizon.framework.conf')); + beforeEach(inject(function($controller, $q, $injector) { + var session = $injector.get('horizon.app.core.openstack-service-api.userSession'); + var deferred = $q.defer(); + var sessionDeferred = $q.defer(); + deferred.resolve({data: {rules: [{'a': 'apple'}, [], {}]}}); + sessionDeferred.resolve(sessionObj); + spyOn(neutron, 'getNamespaces').and.returnValue(deferred.promise); + spyOn(session, 'get').and.returnValue(sessionDeferred.promise); + ctrl = $controller('NetworkQoSOverviewController', + { + '$scope': {context: {loadPromise: deferred.promise}} + } + ); + })); + + it('sets ctrl.resourceType', function() { + expect(ctrl.resourceType).toBeDefined(); + }); + + it('sets ctrl.policy.rules (metadata)', inject(function($timeout) { + $timeout.flush(); + expect(ctrl.policy).toBeDefined(); + expect(ctrl.policy.rules).toBeDefined(); + expect(ctrl.policy.rules[0]).toEqual({'a': 'apple'}); + })); + + it('sets ctrl.policy.rules propValue if empty array', inject(function($timeout) { + $timeout.flush(); + expect(ctrl.policy).toBeDefined(); + expect(ctrl.policy.rules).toBeDefined(); + expect(ctrl.policy.rules[1]).toEqual([]); + })); + + it('sets ctrl.policy.rules propValue if empty object', inject(function($timeout) { + $timeout.flush(); + expect(ctrl.policy).toBeDefined(); + expect(ctrl.policy.rules).toBeDefined(); + expect(ctrl.policy.rules[2]).toEqual({}); + })); + + it('sets ctrl.projectId', inject(function($timeout) { + $timeout.flush(); + expect(ctrl.projectId).toBe(sessionObj.project_id); + })); + + }); + +})(); diff --git a/openstack_dashboard/static/app/core/network_qos/details/overview.html b/openstack_dashboard/static/app/core/network_qos/details/overview.html new file mode 100644 index 0000000000..55a1112133 --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/details/overview.html @@ -0,0 +1,37 @@ +
+
+
+

Policy Details

+
+ + +
+
+

Ownership

+
+ + +
+
+
+
+

Rules

+
+
+ + +
+
+
+
diff --git a/openstack_dashboard/static/app/core/network_qos/qos.module.js b/openstack_dashboard/static/app/core/network_qos/qos.module.js index af4d820da8..d2c0b41d02 100644 --- a/openstack_dashboard/static/app/core/network_qos/qos.module.js +++ b/openstack_dashboard/static/app/core/network_qos/qos.module.js @@ -25,7 +25,8 @@ */ angular .module('horizon.app.core.network_qos', [ - 'ngRoute' + 'ngRoute', + 'horizon.app.core.network_qos.details' ]) .constant('horizon.app.core.network_qos.resourceType', 'OS::Neutron::QoSPolicy') .run(run) @@ -33,26 +34,26 @@ run.$inject = [ 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.network_qos.basePath', 'horizon.app.core.network_qos.service', 'horizon.app.core.network_qos.resourceType' ]; function run(registry, + basePath, qosService, qosResourceType) { registry.getResourceType(qosResourceType) .setNames(gettext('QoS Policy'), gettext('QoS Policies')) + .setSummaryTemplateUrl(basePath + 'details/drawer.html') .setProperties(qosProperties(qosService)) .setListFunction(qosService.getPoliciesPromise) .tableColumns .append({ id: 'name', priority: 1, - sortDefault: true - }) - .append({ - id: 'id', - priority: 1 + sortDefault: true, + urlFunction: qosService.getDetailsPath }) .append({ id: 'description', @@ -70,11 +71,6 @@ singleton: true, persistent: true }) - .append({ - label: gettext('Policy ID'), - name: 'id', - singleton: true - }) .append({ label: gettext('Description'), name: 'description', @@ -100,27 +96,46 @@ name: gettext('Policy Name'), id: gettext('Policy ID'), description: gettext('Description'), - shared: { label: gettext('Shared'), filters: ['yesno'] } + shared: { label: gettext('Shared'), filters: ['yesno'] }, + tenant_id: gettext('Tenant ID'), + project_id: gettext('Project ID'), + created_at: gettext('Created At'), + updated_at: gettext('Updated At'), + rules: gettext('Rules'), + revision_number: gettext('Revision Number') }; } config.$inject = [ '$provide', '$windowProvider', - '$routeProvider' + '$routeProvider', + 'horizon.app.core.detailRoute' ]; /** * @name horizon.dashboard.project.network_qos.basePath + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @param {Object} detailRoute * @description Base path for the QoS code */ - function config($provide, $windowProvider, $routeProvider) { + function config($provide, $windowProvider, $routeProvider, detailRoute) { var path = $windowProvider.$get().STATIC_URL + 'app/core/network_qos/'; $provide.constant('horizon.app.core.network_qos.basePath', path); - $routeProvider.when('/project/network_qos', { + $routeProvider + .when('/project/network_qos', { templateUrl: path + 'panel.html' + }) + .when('/project/network_qos/:policy_id', { + redirectTo: goToAngularDetails }); + + function goToAngularDetails(params) { + return detailRoute + 'OS::Neutron::QoSPolicy/' + params.id; + } } })(); diff --git a/openstack_dashboard/static/app/core/network_qos/qos.service.js b/openstack_dashboard/static/app/core/network_qos/qos.service.js index def6ccb2ab..d9f921b6a5 100644 --- a/openstack_dashboard/static/app/core/network_qos/qos.service.js +++ b/openstack_dashboard/static/app/core/network_qos/qos.service.js @@ -21,7 +21,8 @@ qosService.$inject = [ '$filter', 'horizon.app.core.openstack-service-api.neutron', - 'horizon.app.core.openstack-service-api.userSession' + 'horizon.app.core.openstack-service-api.userSession', + 'horizon.app.core.detailRoute' ]; /* @@ -34,13 +35,30 @@ * but do not need to be restricted to such use. Each exposed function * is documented below. */ - function qosService($filter, neutron, userSession) { + function qosService($filter, + neutron, + userSession, + detailRoute) { var version; return { - getPoliciesPromise: getPoliciesPromise + getDetailsPath: getDetailsPath, + getPoliciesPromise: getPoliciesPromise, + getPolicyPromise: getPolicyPromise }; + /* + * @ngdoc function + * @name getDetailsPath + * @param item {Object} - The QoS Policy object + * @description + * Given an QoS Policy object, returns the relative path to the details + * view. + */ + function getDetailsPath(item) { + return detailRoute + 'OS::Neutron::QoSPolicy/' + item.id; + } + /* * @ngdoc function * @name getPoliciesPromise @@ -69,6 +87,16 @@ } } + /* + * @ngdoc function + * @name getPolicyPromise + * @description + * Given an id, returns a promise for the policy data. + */ + function getPolicyPromise(identifier) { + return neutron.getQosPolicy(identifier); + } + } })(); diff --git a/openstack_dashboard/static/app/core/network_qos/qos.service.spec.js b/openstack_dashboard/static/app/core/network_qos/qos.service.spec.js index cdbc4d902e..75eb35f379 100644 --- a/openstack_dashboard/static/app/core/network_qos/qos.service.spec.js +++ b/openstack_dashboard/static/app/core/network_qos/qos.service.spec.js @@ -51,6 +51,18 @@ })); }); + describe('getPolicyPromise', function() { + it("provides a promise", inject(function($q, $injector) { + var neutron = $injector.get('horizon.app.core.openstack-service-api.neutron'); + var deferred = $q.defer(); + spyOn(neutron, 'getQosPolicy').and.returnValue(deferred.promise); + var result = service.getPolicyPromise({}); + deferred.resolve({data: {id: 1, name: 'policy1'}}); + expect(neutron.getQosPolicy).toHaveBeenCalled(); + expect(result.$$state.value.data.name).toBe('policy1'); + })); + }); + }); })(); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js index 642368fa58..ac15cd6dc1 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js @@ -43,6 +43,7 @@ getExtensions: getExtensions, getNetworks: getNetworks, getPorts: getPorts, + getQosPolicy: getQosPolicy, getQoSPolicies: getQoSPolicies, getSubnets: getSubnets, getTrunks: getTrunks, @@ -338,10 +339,23 @@ // QoS policies + /** + * @name horizon.app.core.openstack-service-api.neutron.getQosPolicy + * @description get a single qos policy by ID. + * @param {string} id + * Specifies the id of the policy to request. + * @returns {Object} The result of the API call + */ + function getQosPolicy(id) { + return apiService.get('/api/neutron/qos_policy/' + id + '/') + .error(function () { + toastService.add('error', gettext('Unable to retrieve the qos policy.')); + }); + } + /** * @name horizon.app.core.openstack-service-api.neutron.getQoSPolicies - * @description - * Get a list of qos policies. + * @description get a list of qos policies. * * The listing result is an object with property "items". Each item is * a QoS policy. diff --git a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js index 9903a3357e..020435735d 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js @@ -162,6 +162,15 @@ 42 ] }, + { + "func": "getQosPolicy", + "method": "get", + "path": "/api/neutron/qos_policy/1/", + "error": "Unable to retrieve the qos policy.", + "testInput": [ + 1 + ] + }, { "func": "getQoSPolicies", "method": "get",