Merge "Add angular server groups panel"
This commit is contained in:
commit
da089052b0
|
@ -30,7 +30,8 @@ MICROVERSION_FEATURES = {
|
||||||
"nova": {
|
"nova": {
|
||||||
"locked_attribute": ["2.9", "2.42"],
|
"locked_attribute": ["2.9", "2.42"],
|
||||||
"instance_description": ["2.19", "2.42"],
|
"instance_description": ["2.19", "2.42"],
|
||||||
"remote_console_mks": ["2.8", "2.53"]
|
"remote_console_mks": ["2.8", "2.53"],
|
||||||
|
"servergroup_soft_policies": ["2.15", "2.60"]
|
||||||
},
|
},
|
||||||
"cinder": {
|
"cinder": {
|
||||||
"consistency_groups": ["2.0", "3.10"],
|
"consistency_groups": ["2.0", "3.10"],
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ServerGroups(horizon.Panel):
|
||||||
|
name = _("Server Groups")
|
||||||
|
slug = "server_groups"
|
||||||
|
permissions = ('openstack.services.compute',)
|
||||||
|
policy_rules = (("compute", "os_compute_api:os-server-groups:index"),)
|
||||||
|
|
||||||
|
def allowed(self, context):
|
||||||
|
request = context['request']
|
||||||
|
try:
|
||||||
|
return (
|
||||||
|
super(ServerGroups, self).allowed(context)
|
||||||
|
and request.user.has_perms(self.permissions)
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception("Call to list enabled services failed. This is "
|
||||||
|
"likely due to a problem communicating with the "
|
||||||
|
"Nova endpoint. Server Groups panel will not be "
|
||||||
|
"displayed.")
|
||||||
|
return False
|
|
@ -0,0 +1,22 @@
|
||||||
|
# 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 import views
|
||||||
|
|
||||||
|
|
||||||
|
title = _("Server Groups")
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.AngularIndexView.as_view(title=title), name='index'),
|
||||||
|
]
|
|
@ -0,0 +1,10 @@
|
||||||
|
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||||
|
PANEL = 'server_groups'
|
||||||
|
# The slug of the dashboard the PANEL associated with. Required.
|
||||||
|
PANEL_DASHBOARD = 'project'
|
||||||
|
# The slug of the panel group the PANEL is associated with.
|
||||||
|
PANEL_GROUP = 'compute'
|
||||||
|
|
||||||
|
# Python panel class of the PANEL to be added.
|
||||||
|
ADD_PANEL = ('openstack_dashboard.dashboards.project.server_groups'
|
||||||
|
'.panel.ServerGroups')
|
|
@ -40,6 +40,7 @@
|
||||||
'horizon.app.core.metadata',
|
'horizon.app.core.metadata',
|
||||||
'horizon.app.core.network_qos',
|
'horizon.app.core.network_qos',
|
||||||
'horizon.app.core.openstack-service-api',
|
'horizon.app.core.openstack-service-api',
|
||||||
|
'horizon.app.core.server_groups',
|
||||||
'horizon.app.core.trunks',
|
'horizon.app.core.trunks',
|
||||||
'horizon.app.core.workflow',
|
'horizon.app.core.workflow',
|
||||||
'horizon.framework.conf',
|
'horizon.framework.conf',
|
||||||
|
|
|
@ -296,6 +296,12 @@
|
||||||
"path": "/api/nova/servers/",
|
"path": "/api/nova/servers/",
|
||||||
"error": "Unable to retrieve instances."
|
"error": "Unable to retrieve instances."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"func": 'getServerGroups',
|
||||||
|
"method": 'get',
|
||||||
|
"path": '/api/nova/servergroups/',
|
||||||
|
"error": 'Unable to retrieve server groups.'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"func": "getExtensions",
|
"func": "getExtensions",
|
||||||
"method": "get",
|
"method": "get",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<hz-resource-panel resource-type-name="OS::Nova::ServerGroup">
|
||||||
|
<hz-resource-table resource-type-name="OS::Nova::ServerGroup"></hz-resource-table>
|
||||||
|
</hz-resource-panel>
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* 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.server_groups
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Provides all of the services and widgets required
|
||||||
|
* to support and display server groups related content.
|
||||||
|
*/
|
||||||
|
angular
|
||||||
|
.module('horizon.app.core.server_groups', [
|
||||||
|
'horizon.framework.conf',
|
||||||
|
'horizon.app.core'
|
||||||
|
])
|
||||||
|
.constant('horizon.app.core.server_groups.resourceType', 'OS::Nova::ServerGroup')
|
||||||
|
.run(run)
|
||||||
|
.config(config);
|
||||||
|
|
||||||
|
run.$inject = [
|
||||||
|
'horizon.app.core.server_groups.resourceType',
|
||||||
|
'horizon.app.core.server_groups.service',
|
||||||
|
'horizon.framework.conf.resource-type-registry.service'
|
||||||
|
];
|
||||||
|
|
||||||
|
function run(serverGroupResourceType,
|
||||||
|
serverGroupsService,
|
||||||
|
registry) {
|
||||||
|
registry.getResourceType(serverGroupResourceType)
|
||||||
|
.setNames(gettext('Server Group'), gettext('Server Groups'))
|
||||||
|
.setProperties(serverGroupProperties())
|
||||||
|
.setListFunction(serverGroupsService.getServerGroupsPromise)
|
||||||
|
.tableColumns
|
||||||
|
.append({
|
||||||
|
id: 'name',
|
||||||
|
priority: 1,
|
||||||
|
sortDefault: true
|
||||||
|
})
|
||||||
|
// The name is not unique, so we need to show the ID to
|
||||||
|
// distinguish.
|
||||||
|
.append({
|
||||||
|
id: 'id',
|
||||||
|
priority: 1
|
||||||
|
})
|
||||||
|
.append({
|
||||||
|
id: 'policy',
|
||||||
|
priority: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.getResourceType(serverGroupResourceType).filterFacets
|
||||||
|
.append({
|
||||||
|
label: gettext('Name'),
|
||||||
|
name: 'name',
|
||||||
|
singleton: true
|
||||||
|
})
|
||||||
|
.append({
|
||||||
|
label: gettext('ID'),
|
||||||
|
name: 'id',
|
||||||
|
singleton: true
|
||||||
|
})
|
||||||
|
.append({
|
||||||
|
label: gettext('Policy'),
|
||||||
|
name: 'policy',
|
||||||
|
singleton: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name serverGroupProperties
|
||||||
|
* @description resource properties for server group module
|
||||||
|
*/
|
||||||
|
function serverGroupProperties() {
|
||||||
|
return {
|
||||||
|
name: gettext('Name'),
|
||||||
|
id: gettext('ID'),
|
||||||
|
policy: gettext('Policy')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
config.$inject = [
|
||||||
|
'$windowProvider',
|
||||||
|
'$routeProvider'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name config
|
||||||
|
* @param {Object} $windowProvider
|
||||||
|
* @param {Object} $routeProvider
|
||||||
|
* @description Routes used by this module.
|
||||||
|
* @returns {undefined} Returns nothing
|
||||||
|
*/
|
||||||
|
function config($windowProvider, $routeProvider) {
|
||||||
|
var path = $windowProvider.$get().STATIC_URL + 'app/core/server_groups/';
|
||||||
|
|
||||||
|
$routeProvider.when('/project/server_groups', {
|
||||||
|
templateUrl: path + 'panel.html'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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.app.core.server_groups', function () {
|
||||||
|
it('should exist', function () {
|
||||||
|
expect(angular.module('horizon.app.core.server_groups')).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loading the module', function () {
|
||||||
|
var registry;
|
||||||
|
|
||||||
|
beforeEach(module('horizon.app.core.server_groups'));
|
||||||
|
beforeEach(inject(function($injector) {
|
||||||
|
registry = $injector.get('horizon.framework.conf.resource-type-registry.service');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('registers names', function() {
|
||||||
|
expect(registry.getResourceType('OS::Nova::ServerGroup').getName()).toBe("Server Groups");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set facets for search', function () {
|
||||||
|
var names = registry.getResourceType('OS::Nova::ServerGroup').filterFacets
|
||||||
|
.map(getName);
|
||||||
|
expect(names).toContain('name');
|
||||||
|
expect(names).toContain('id');
|
||||||
|
expect(names).toContain('policy');
|
||||||
|
|
||||||
|
function getName(x) {
|
||||||
|
return x.name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* 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.server_groups')
|
||||||
|
.factory('horizon.app.core.server_groups.service', serverGroupsService);
|
||||||
|
|
||||||
|
serverGroupsService.$inject = [
|
||||||
|
'horizon.app.core.openstack-service-api.nova'
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @ngdoc factory
|
||||||
|
* @name horizon.app.core.server_groups.service
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* This service provides functions that are used through the Server Groups
|
||||||
|
* features. These are primarily used in the module registrations
|
||||||
|
* but do not need to be restricted to such use. Each exposed function
|
||||||
|
* is documented below.
|
||||||
|
*/
|
||||||
|
function serverGroupsService(nova) {
|
||||||
|
return {
|
||||||
|
getServerGroupsPromise: getServerGroupsPromise
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @ngdoc function
|
||||||
|
* @name getServerGroupPolicies
|
||||||
|
* @description
|
||||||
|
* Returns a list for the server group policies.
|
||||||
|
*/
|
||||||
|
function getServerGroupPolicies() {
|
||||||
|
return nova.isFeatureSupported('servergroup_soft_policies')
|
||||||
|
.then(isSoftPoliciesSupported);
|
||||||
|
|
||||||
|
function isSoftPoliciesSupported(response) {
|
||||||
|
var policies = {
|
||||||
|
'affinity': gettext('Affinity'),
|
||||||
|
'anti-affinity': gettext('Anti Affinity')
|
||||||
|
};
|
||||||
|
if (response.data) {
|
||||||
|
policies['soft-anti-affinity'] = gettext('Soft Anti Affinity');
|
||||||
|
policies['soft-affinity'] = gettext('Soft Affinity');
|
||||||
|
}
|
||||||
|
return policies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @ngdoc function
|
||||||
|
* @name getServerGroupsPromise
|
||||||
|
* @description
|
||||||
|
* Rreturns a promise for the matching server groups.
|
||||||
|
* This is used in displaying lists of Server Groups.
|
||||||
|
*/
|
||||||
|
function getServerGroupsPromise() {
|
||||||
|
return nova.getServerGroups().then(modifyResponse);
|
||||||
|
|
||||||
|
function modifyResponse(response) {
|
||||||
|
return getServerGroupPolicies().then(modifyItems);
|
||||||
|
|
||||||
|
function modifyItems(policies) {
|
||||||
|
response.data.items.map(function (item) {
|
||||||
|
// When creating a server group, the back-end limit
|
||||||
|
// server group can only have one policy.
|
||||||
|
item.policy = policies[item.policies[0]];
|
||||||
|
});
|
||||||
|
return {data: {items: response.data.items}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* 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('server groups service', function() {
|
||||||
|
var $q, $timeout, nova, service;
|
||||||
|
|
||||||
|
beforeEach(module('horizon.app.core.server_groups'));
|
||||||
|
beforeEach(inject(function($injector) {
|
||||||
|
$q = $injector.get('$q');
|
||||||
|
$timeout = $injector.get('$timeout');
|
||||||
|
nova = $injector.get('horizon.app.core.openstack-service-api.nova');
|
||||||
|
service = $injector.get('horizon.app.core.server_groups.service');
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('getServerGroupsPromise', function() {
|
||||||
|
it("provides a promise when soft policies are supported", inject(function() {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var deferredPolicies = $q.defer();
|
||||||
|
spyOn(nova, 'getServerGroups').and.returnValue(deferred.promise);
|
||||||
|
spyOn(nova, 'isFeatureSupported').and.returnValue(deferredPolicies.promise);
|
||||||
|
var result = service.getServerGroupsPromise({});
|
||||||
|
deferred.resolve({data: {items: [{id: '1', policies: ['affinity']}]}});
|
||||||
|
deferredPolicies.resolve({data: true});
|
||||||
|
$timeout.flush();
|
||||||
|
|
||||||
|
expect(nova.getServerGroups).toHaveBeenCalled();
|
||||||
|
expect(nova.isFeatureSupported).toHaveBeenCalled();
|
||||||
|
expect(result.$$state.value.data.items[0].id).toBe('1');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("provides a promise when soft policies are not supported", inject(function() {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var deferredPolicies = $q.defer();
|
||||||
|
spyOn(nova, 'getServerGroups').and.returnValue(deferred.promise);
|
||||||
|
spyOn(nova, 'isFeatureSupported').and.returnValue(deferredPolicies.promise);
|
||||||
|
var result = service.getServerGroupsPromise({});
|
||||||
|
deferred.resolve({data: {items: [{id: '1', policies: ['affinity']}]}});
|
||||||
|
deferredPolicies.resolve({data: false});
|
||||||
|
$timeout.flush();
|
||||||
|
|
||||||
|
expect(nova.getServerGroups).toHaveBeenCalled();
|
||||||
|
expect(nova.isFeatureSupported).toHaveBeenCalled();
|
||||||
|
expect(result.$$state.value.data.items[0].id).toBe('1');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
|
@ -310,6 +310,11 @@ TEST_GLOBAL_MOCKS_ON_PANELS = {
|
||||||
'.domains.panel.Domains.can_access'),
|
'.domains.panel.Domains.can_access'),
|
||||||
'return_value': True,
|
'return_value': True,
|
||||||
},
|
},
|
||||||
|
'server_groups': {
|
||||||
|
'method': ('openstack_dashboard.dashboards.project'
|
||||||
|
'.server_groups.panel.ServerGroups.can_access'),
|
||||||
|
'return_value': True,
|
||||||
|
},
|
||||||
'trunk-project': {
|
'trunk-project': {
|
||||||
'method': ('openstack_dashboard.dashboards.project'
|
'method': ('openstack_dashboard.dashboards.project'
|
||||||
'.trunks.panel.Trunks.can_access'),
|
'.trunks.panel.Trunks.can_access'),
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
[`blueprint ng-server-groups <https://blueprints.launchpad.net/horizon/+spec/ng-server-groups>`_]
|
||||||
|
This blueprint add angular server groups panel below the
|
||||||
|
Project->Compute panel group. The panel turns on if Nova API
|
||||||
|
extension 'ServerGroups' is available. It displays information about
|
||||||
|
server groups.
|
Loading…
Reference in New Issue