Trunks panel: admin panel
Enable admin panel for trunks. For the admin panel the delete action and the readonly operations are enabled (table view and details view). Change-Id: Icfc01612cc60798e4b0ff7379a9c8b83d3f1d60b Implements: blueprint neutron-trunk-ui
This commit is contained in:
45
openstack_dashboard/dashboards/admin/trunks/panel.py
Normal file
45
openstack_dashboard/dashboards/admin/trunks/panel.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Copyright 2017 Ericsson
|
||||||
|
#
|
||||||
|
# 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 _
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
|
||||||
|
from openstack_dashboard.api import neutron
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Trunks(horizon.Panel):
|
||||||
|
name = _("Trunks")
|
||||||
|
slug = "trunks"
|
||||||
|
permissions = ('openstack.services.network',)
|
||||||
|
policy_rules = (("trunk", "context_is_admin"),)
|
||||||
|
|
||||||
|
def allowed(self, context):
|
||||||
|
request = context['request']
|
||||||
|
try:
|
||||||
|
return (
|
||||||
|
super(Trunks, self).allowed(context)
|
||||||
|
and request.user.has_perms(self.permissions)
|
||||||
|
and neutron.is_extension_supported(request,
|
||||||
|
extension_alias='trunk')
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
LOG.error("Call to list enabled services failed. This is likely "
|
||||||
|
"due to a problem communicating with the Neutron "
|
||||||
|
"endpoint. Trunks admin panel will not be displayed.")
|
||||||
|
return False
|
26
openstack_dashboard/dashboards/admin/trunks/urls.py
Normal file
26
openstack_dashboard/dashboards/admin/trunks/urls.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2017 Ericsson
|
||||||
|
#
|
||||||
|
# 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 url
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon.browsers.views import AngularIndexView
|
||||||
|
|
||||||
|
|
||||||
|
title = _("Trunks")
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', AngularIndexView.as_view(title=title), name='index'),
|
||||||
|
url(r'^(?P<trunk_id>[^/]+)/$',
|
||||||
|
AngularIndexView.as_view(title=title), name='detail'),
|
||||||
|
]
|
9
openstack_dashboard/enabled/_2340_admin_trunks_panel.py
Normal file
9
openstack_dashboard/enabled/_2340_admin_trunks_panel.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = 'trunks'
|
||||||
|
# The slug of the dashboard the PANEL associated with. Required.
|
||||||
|
PANEL_DASHBOARD = 'admin'
|
||||||
|
# The slug of the panel group the PANEL is associated with.
|
||||||
|
PANEL_GROUP = 'network'
|
||||||
|
|
||||||
|
# Python panel class of the PANEL to be added.
|
||||||
|
ADD_PANEL = 'openstack_dashboard.dashboards.admin.trunks.panel.Trunks'
|
@@ -90,6 +90,26 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can suppress errors in case of deleting trunks', function() {
|
||||||
|
spyOn(apiService, 'delete').and.callFake(function() {
|
||||||
|
return {
|
||||||
|
success: function(c) {
|
||||||
|
c();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
error: function(c) {
|
||||||
|
c();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
spyOn(toastService, 'add').and.callThrough();
|
||||||
|
|
||||||
|
service.deleteTrunk('42', true).error(function() {
|
||||||
|
expect(toastService.add).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var tests = [
|
var tests = [
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@@ -34,7 +34,8 @@
|
|||||||
// angular-schema-form would have made many things easier, but it wasn't
|
// angular-schema-form would have made many things easier, but it wasn't
|
||||||
// really an option because it does not have a transfer-table widget.
|
// really an option because it does not have a transfer-table widget.
|
||||||
'horizon.framework.widgets.modal.wizard-modal.service',
|
'horizon.framework.widgets.modal.wizard-modal.service',
|
||||||
'horizon.framework.widgets.toast.service'
|
'horizon.framework.widgets.toast.service',
|
||||||
|
'$location'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +53,8 @@
|
|||||||
resourceType,
|
resourceType,
|
||||||
actionResultService,
|
actionResultService,
|
||||||
wizardModalService,
|
wizardModalService,
|
||||||
toast
|
toast,
|
||||||
|
$location
|
||||||
) {
|
) {
|
||||||
var service = {
|
var service = {
|
||||||
perform: perform,
|
perform: perform,
|
||||||
@@ -64,11 +66,24 @@
|
|||||||
////////////
|
////////////
|
||||||
|
|
||||||
function allowed() {
|
function allowed() {
|
||||||
return policy.ifAllowed(
|
// NOTE(lajos katona): in case of admin let's disable create action.
|
||||||
|
// TODO(lajos katona): make possible to create/edit from admin panel
|
||||||
|
var fromNonAdminUrl = ($location.url().indexOf('admin') === -1);
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
policy.ifAllowed(
|
||||||
{rules: [
|
{rules: [
|
||||||
['network', 'create_trunk']
|
['network', 'create_trunk']
|
||||||
]}
|
]}
|
||||||
);
|
).then(function(result) {
|
||||||
|
if (fromNonAdminUrl) {
|
||||||
|
deferred.resolve(result);
|
||||||
|
} else {
|
||||||
|
deferred.reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function perform() {
|
function perform() {
|
||||||
|
@@ -21,13 +21,15 @@
|
|||||||
|
|
||||||
var $q, $scope, service, modalWaitSpinnerService, deferred, $timeout;
|
var $q, $scope, service, modalWaitSpinnerService, deferred, $timeout;
|
||||||
|
|
||||||
var policyAPI = {
|
var location = {
|
||||||
ifAllowed: function() {
|
url: function() {
|
||||||
return {
|
return "project/trunks";
|
||||||
success: function(callback) {
|
|
||||||
callback({allowed: true});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var policyAPI = {
|
||||||
|
ifAllowed: function() {
|
||||||
|
return $q.when({allowed: true});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,6 +82,7 @@
|
|||||||
neutronAPI);
|
neutronAPI);
|
||||||
$provide.value('horizon.app.core.openstack-service-api.userSession',
|
$provide.value('horizon.app.core.openstack-service-api.userSession',
|
||||||
userSession);
|
userSession);
|
||||||
|
$provide.value('$location', location);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject(function($injector, $rootScope, _$q_, _$timeout_) {
|
beforeEach(inject(function($injector, $rootScope, _$q_, _$timeout_) {
|
||||||
@@ -93,13 +96,31 @@
|
|||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should check the policy if the user is allowed to create trunks', function() {
|
it('should check the policy if the user is allowed to create trunks', function(done) {
|
||||||
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
||||||
var allowed = service.allowed();
|
spyOn(location, 'url').and.callThrough();
|
||||||
expect(allowed).toBeTruthy();
|
|
||||||
|
service.allowed().then(function(result) {
|
||||||
|
expect(result).toBeTruthy();
|
||||||
expect(policyAPI.ifAllowed).toHaveBeenCalledWith(
|
expect(policyAPI.ifAllowed).toHaveBeenCalledWith(
|
||||||
{ rules: [['network', 'create_trunk']] }
|
{ rules: [['network', 'create_trunk']] }
|
||||||
);
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$digest();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Allowed should be rejected in case of admin', function(done) {
|
||||||
|
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
||||||
|
spyOn(location, 'url').and.returnValue('admin/trunks');
|
||||||
|
|
||||||
|
service.allowed().then(null, function(result) {
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$digest();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('open the modal with the correct parameters', function() {
|
it('open the modal with the correct parameters', function() {
|
||||||
|
@@ -32,7 +32,8 @@
|
|||||||
'horizon.app.core.trunks.resourceType',
|
'horizon.app.core.trunks.resourceType',
|
||||||
'horizon.framework.util.actions.action-result.service',
|
'horizon.framework.util.actions.action-result.service',
|
||||||
'horizon.framework.widgets.modal.wizard-modal.service',
|
'horizon.framework.widgets.modal.wizard-modal.service',
|
||||||
'horizon.framework.widgets.toast.service'
|
'horizon.framework.widgets.toast.service',
|
||||||
|
'$rootScope'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,8 +52,17 @@
|
|||||||
resourceType,
|
resourceType,
|
||||||
actionResultService,
|
actionResultService,
|
||||||
wizardModalService,
|
wizardModalService,
|
||||||
toast
|
toast,
|
||||||
|
$rootScope
|
||||||
) {
|
) {
|
||||||
|
// Note(lajos katona): To have a workaround for the fact that on the details
|
||||||
|
// page there is no way to find out if we are in the project or the admin
|
||||||
|
// dashboard, try to fetch the previous url by catching the locationChangesucces
|
||||||
|
// event.
|
||||||
|
var urlFromLocationChangeNonAdmin = true;
|
||||||
|
$rootScope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl) {
|
||||||
|
urlFromLocationChangeNonAdmin = (oldUrl.indexOf('admin') === -1);
|
||||||
|
});
|
||||||
var service = {
|
var service = {
|
||||||
perform: perform,
|
perform: perform,
|
||||||
allowed: allowed
|
allowed: allowed
|
||||||
@@ -62,18 +72,31 @@
|
|||||||
////////////
|
////////////
|
||||||
|
|
||||||
function allowed() {
|
function allowed() {
|
||||||
return policy.ifAllowed(
|
// NOTE(lajos katona): in case of admin let's disable edit action.
|
||||||
|
// TODO(lajos katona): make possible to create/edit from admin panel
|
||||||
|
var fromNonAdminUrl = ($location.url().indexOf('admin') === -1);
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
policy.ifAllowed(
|
||||||
{rules: [
|
{rules: [
|
||||||
['network', 'add_subports'],
|
['network', 'add_subports'],
|
||||||
['network', 'remove_subports']
|
['network', 'remove_subports']
|
||||||
]}
|
]}
|
||||||
);
|
).then(function(result) {
|
||||||
|
if (fromNonAdminUrl && urlFromLocationChangeNonAdmin) {
|
||||||
|
deferred.resolve(result);
|
||||||
|
} else {
|
||||||
|
deferred.reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function perform(selected) {
|
function perform(selected) {
|
||||||
var params = {};
|
var params = {};
|
||||||
|
|
||||||
if ($location.url().indexOf('admin') === -1) {
|
if (($location.url().indexOf('admin') === -1) && urlFromLocationChangeNonAdmin) {
|
||||||
params = {project_id: userSession.project_id};
|
params = {project_id: userSession.project_id};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,13 +21,15 @@
|
|||||||
|
|
||||||
var $q, $scope, service, modalWaitSpinnerService, deferred, $timeout;
|
var $q, $scope, service, modalWaitSpinnerService, deferred, $timeout;
|
||||||
|
|
||||||
var policyAPI = {
|
var location = {
|
||||||
ifAllowed: function() {
|
url: function() {
|
||||||
return {
|
return "project/trunks";
|
||||||
success: function(callback) {
|
|
||||||
callback({allowed: true});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var policyAPI = {
|
||||||
|
ifAllowed: function() {
|
||||||
|
return $q.when({allowed: true});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -85,6 +87,7 @@
|
|||||||
neutronAPI);
|
neutronAPI);
|
||||||
$provide.value('horizon.app.core.openstack-service-api.userSession',
|
$provide.value('horizon.app.core.openstack-service-api.userSession',
|
||||||
userSession);
|
userSession);
|
||||||
|
$provide.value('$location', location);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject(function($injector, $rootScope, _$q_, _$timeout_) {
|
beforeEach(inject(function($injector, $rootScope, _$q_, _$timeout_) {
|
||||||
@@ -98,13 +101,31 @@
|
|||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should check the policy if the user is allowed to update trunks', function() {
|
it('should check the policy if the user is allowed to update trunks', function(done) {
|
||||||
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
||||||
var allowed = service.allowed();
|
spyOn(location, 'url').and.callThrough();
|
||||||
expect(allowed).toBeTruthy();
|
|
||||||
|
service.allowed().then(function(result) {
|
||||||
|
expect(result).toBeTruthy();
|
||||||
expect(policyAPI.ifAllowed).toHaveBeenCalledWith(
|
expect(policyAPI.ifAllowed).toHaveBeenCalledWith(
|
||||||
{ rules: [['network', 'add_subports'], ['network', 'remove_subports']] }
|
{ rules: [['network', 'add_subports'], ['network', 'remove_subports']] }
|
||||||
);
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$digest();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Allowed should be rejected in case of admin', function(done) {
|
||||||
|
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
||||||
|
spyOn(location, 'url').and.returnValue('admin/trunks');
|
||||||
|
|
||||||
|
service.allowed().then(null, function(result) {
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.$digest();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('open the modal with the correct parameters', function() {
|
it('open the modal with the correct parameters', function() {
|
||||||
|
@@ -184,6 +184,14 @@
|
|||||||
redirectTo: goToAngularDetails
|
redirectTo: goToAngularDetails
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/admin/trunks', {
|
||||||
|
templateUrl: path + 'panel.html'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/admin/trunk/:id/detail', {
|
||||||
|
redirectTo: goToAngularDetails
|
||||||
|
});
|
||||||
|
|
||||||
function goToAngularDetails(params) {
|
function goToAngularDetails(params) {
|
||||||
return detailRoute + 'OS::Neutron::Trunk/' + params.id;
|
return detailRoute + 'OS::Neutron::Trunk/' + params.id;
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,8 @@
|
|||||||
'horizon.app.core.openstack-service-api.neutron',
|
'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',
|
'horizon.app.core.detailRoute',
|
||||||
'$location'
|
'$location',
|
||||||
|
'$window'
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
* but do not need to be restricted to such use. Each exposed function
|
* but do not need to be restricted to such use. Each exposed function
|
||||||
* is documented below.
|
* is documented below.
|
||||||
*/
|
*/
|
||||||
function trunksService(neutron, userSession, detailRoute, $location) {
|
function trunksService(neutron, userSession, detailRoute, $location, $window) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getDetailsPath: getDetailsPath,
|
getDetailsPath: getDetailsPath,
|
||||||
@@ -68,7 +69,17 @@
|
|||||||
return userSession.get().then(getTrunksForProject);
|
return userSession.get().then(getTrunksForProject);
|
||||||
|
|
||||||
function getTrunksForProject(userSession) {
|
function getTrunksForProject(userSession) {
|
||||||
|
var locationURLNotAdmin = ($location.url().indexOf('admin') === -1);
|
||||||
|
// Note(lajoskatona): To list all trunks in case of
|
||||||
|
// the listing is for the Admin panel, check here the
|
||||||
|
// location.url.
|
||||||
|
// there should be a better way to check for admin or project panel??
|
||||||
|
if (locationURLNotAdmin) {
|
||||||
params.project_id = userSession.project_id;
|
params.project_id = userSession.project_id;
|
||||||
|
} else {
|
||||||
|
delete params.project_id;
|
||||||
|
}
|
||||||
|
|
||||||
return neutron.getTrunks(params).then(addTrackBy);
|
return neutron.getTrunks(params).then(addTrackBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,10 +123,10 @@
|
|||||||
function getTrunkError(trunk) {
|
function getTrunkError(trunk) {
|
||||||
// TODO(bence romsics): When you delete a trunk from the details
|
// TODO(bence romsics): When you delete a trunk from the details
|
||||||
// view then it cannot be re-read (of course) and we handle that
|
// view then it cannot be re-read (of course) and we handle that
|
||||||
// by a hard-coded redirect to the project panel. This is okay
|
// by window.histoy.back(). This is a workaround and must be deleted
|
||||||
// for now. But when we want this panel to work for admin too,
|
// as soon as there is a final solution for the promels with ngDetails
|
||||||
// we should not hard-code this anymore.
|
// pages.
|
||||||
$location.url('project/trunks');
|
$window.history.back();
|
||||||
return trunk;
|
return trunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,19 +18,20 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('trunks service', function() {
|
describe('trunks service', function() {
|
||||||
var service, _location_;
|
var service, neutron, session, _location_;
|
||||||
|
|
||||||
beforeEach(module('horizon.framework.util'));
|
beforeEach(module('horizon.framework.util'));
|
||||||
beforeEach(module('horizon.framework.conf'));
|
beforeEach(module('horizon.framework.conf'));
|
||||||
beforeEach(module('horizon.app.core.trunks'));
|
beforeEach(module('horizon.app.core.trunks'));
|
||||||
beforeEach(inject(function($injector, $location) {
|
beforeEach(inject(function($injector, $location) {
|
||||||
service = $injector.get('horizon.app.core.trunks.service');
|
service = $injector.get('horizon.app.core.trunks.service');
|
||||||
|
neutron = $injector.get('horizon.app.core.openstack-service-api.neutron');
|
||||||
|
session = $injector.get('horizon.app.core.openstack-service-api.userSession');
|
||||||
_location_ = $location;
|
_location_ = $location;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('getTrunkPromise', function() {
|
describe('getTrunkPromise', function() {
|
||||||
it('provides a promise', inject(function($q, $injector, $timeout) {
|
it('provides a promise', inject(function($q, $timeout) {
|
||||||
var neutron = $injector.get('horizon.app.core.openstack-service-api.neutron');
|
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
spyOn(neutron, 'getTrunk').and.returnValue(deferred.promise);
|
spyOn(neutron, 'getTrunk').and.returnValue(deferred.promise);
|
||||||
var result = service.getTrunkPromise({});
|
var result = service.getTrunkPromise({});
|
||||||
@@ -40,21 +41,7 @@
|
|||||||
expect(result.$$state.value.data.updated_at).toBe('May29');
|
expect(result.$$state.value.data.updated_at).toBe('May29');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('redirects back to panel on failure', inject(function($q, $injector, $timeout) {
|
it('provides a promise that gets translated', inject(function($q, $timeout) {
|
||||||
var neutron = $injector.get('horizon.app.core.openstack-service-api.neutron');
|
|
||||||
var deferred = $q.defer();
|
|
||||||
spyOn(neutron, 'getTrunk').and.returnValue(deferred.promise);
|
|
||||||
spyOn(_location_, 'url');
|
|
||||||
service.getTrunkPromise({});
|
|
||||||
deferred.reject();
|
|
||||||
$timeout.flush();
|
|
||||||
expect(neutron.getTrunk).toHaveBeenCalled();
|
|
||||||
expect(_location_.url).toHaveBeenCalledWith('project/trunks');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('provides a promise that gets translated', inject(function($q, $injector, $timeout) {
|
|
||||||
var neutron = $injector.get('horizon.app.core.openstack-service-api.neutron');
|
|
||||||
var session = $injector.get('horizon.app.core.openstack-service-api.userSession');
|
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
var deferredSession = $q.defer();
|
var deferredSession = $q.defer();
|
||||||
var updatedAt = new Date('November 15, 2017');
|
var updatedAt = new Date('November 15, 2017');
|
||||||
@@ -64,7 +51,24 @@
|
|||||||
deferred.resolve({data: {items: [{id: 1, updated_at: updatedAt}]}});
|
deferred.resolve({data: {items: [{id: 1, updated_at: updatedAt}]}});
|
||||||
deferredSession.resolve({project_id: '42'});
|
deferredSession.resolve({project_id: '42'});
|
||||||
$timeout.flush();
|
$timeout.flush();
|
||||||
expect(neutron.getTrunks).toHaveBeenCalled();
|
expect(neutron.getTrunks).toHaveBeenCalledWith({project_id: '42'});
|
||||||
|
expect(result.$$state.value.data.items[0].updated_at).toBe(updatedAt);
|
||||||
|
expect(result.$$state.value.data.items[0].id).toBe(1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('removes project_id in case of calling from admin panel',
|
||||||
|
inject(function($q, $timeout) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var deferredSession = $q.defer();
|
||||||
|
var updatedAt = new Date('November 15, 2017');
|
||||||
|
spyOn(neutron, 'getTrunks').and.returnValue(deferred.promise);
|
||||||
|
spyOn(session, 'get').and.returnValue(deferredSession.promise);
|
||||||
|
spyOn(_location_, 'url').and.returnValue('/admin/trunks');
|
||||||
|
var result = service.getTrunksPromise({project_id: '43'});
|
||||||
|
deferred.resolve({data: {items: [{id: 1, updated_at: updatedAt}]}});
|
||||||
|
deferredSession.resolve({project_id: '42'});
|
||||||
|
$timeout.flush();
|
||||||
|
expect(neutron.getTrunks).toHaveBeenCalledWith({});
|
||||||
expect(result.$$state.value.data.items[0].updated_at).toBe(updatedAt);
|
expect(result.$$state.value.data.items[0].updated_at).toBe(updatedAt);
|
||||||
expect(result.$$state.value.data.items[0].id).toBe(1);
|
expect(result.$$state.value.data.items[0].id).toBe(1);
|
||||||
}));
|
}));
|
||||||
|
@@ -297,11 +297,16 @@ TEST_GLOBAL_MOCKS_ON_PANELS = {
|
|||||||
'.aggregates.panel.Aggregates.can_access'),
|
'.aggregates.panel.Aggregates.can_access'),
|
||||||
'return_value': True,
|
'return_value': True,
|
||||||
},
|
},
|
||||||
'trunk': {
|
'trunk-project': {
|
||||||
'method': ('openstack_dashboard.dashboards.project'
|
'method': ('openstack_dashboard.dashboards.project'
|
||||||
'.trunks.panel.Trunks.can_access'),
|
'.trunks.panel.Trunks.can_access'),
|
||||||
'return_value': True,
|
'return_value': True,
|
||||||
},
|
},
|
||||||
|
'trunk-admin': {
|
||||||
|
'method': ('openstack_dashboard.dashboards.admin'
|
||||||
|
'.trunks.panel.Trunks.can_access'),
|
||||||
|
'return_value': True,
|
||||||
|
},
|
||||||
'qos': {
|
'qos': {
|
||||||
'method': ('openstack_dashboard.dashboards.project'
|
'method': ('openstack_dashboard.dashboards.project'
|
||||||
'.network_qos.panel.NetworkQoS.can_access'),
|
'.network_qos.panel.NetworkQoS.can_access'),
|
||||||
|
@@ -2,7 +2,8 @@
|
|||||||
features:
|
features:
|
||||||
- |
|
- |
|
||||||
[:blueprint:`neutron-trunk-ui`]
|
[:blueprint:`neutron-trunk-ui`]
|
||||||
Neutron trunk feature is now supported in the project dashboard.
|
Neutron trunk feature is now supported. It is supported in both the
|
||||||
|
project and admin dashboards.
|
||||||
The panel will be displayed if Neutron API extension 'trunk' is available.
|
The panel will be displayed if Neutron API extension 'trunk' is available.
|
||||||
It displays information about trunks. The details page for each trunk also
|
It displays information about trunks. The details page for each trunk also
|
||||||
shows information about subports of that trunk.
|
shows information about subports of that trunk.
|
||||||
|
Reference in New Issue
Block a user