Merge "Add angular server groups panel"

This commit is contained in:
Zuul 2018-03-16 14:15:29 +00:00 committed by Gerrit Code Review
commit da089052b0
14 changed files with 411 additions and 1 deletions

View File

@ -30,7 +30,8 @@ MICROVERSION_FEATURES = {
"nova": {
"locked_attribute": ["2.9", "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": {
"consistency_groups": ["2.0", "3.10"],

View File

@ -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

View File

@ -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'),
]

View File

@ -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')

View File

@ -40,6 +40,7 @@
'horizon.app.core.metadata',
'horizon.app.core.network_qos',
'horizon.app.core.openstack-service-api',
'horizon.app.core.server_groups',
'horizon.app.core.trunks',
'horizon.app.core.workflow',
'horizon.framework.conf',

View File

@ -296,6 +296,12 @@
"path": "/api/nova/servers/",
"error": "Unable to retrieve instances."
},
{
"func": 'getServerGroups',
"method": 'get',
"path": '/api/nova/servergroups/',
"error": 'Unable to retrieve server groups.'
},
{
"func": "getExtensions",
"method": "get",

View File

@ -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>

View File

@ -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'
});
}
})();

View File

@ -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;
}
});
});
})();

View File

@ -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}};
}
}
}
}
})();

View File

@ -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');
}));
});
});
})();

View File

@ -310,6 +310,11 @@ TEST_GLOBAL_MOCKS_ON_PANELS = {
'.domains.panel.Domains.can_access'),
'return_value': True,
},
'server_groups': {
'method': ('openstack_dashboard.dashboards.project'
'.server_groups.panel.ServerGroups.can_access'),
'return_value': True,
},
'trunk-project': {
'method': ('openstack_dashboard.dashboards.project'
'.trunks.panel.Trunks.can_access'),

View File

@ -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.