Merge "Add containers panel for admin"

This commit is contained in:
Zuul 2018-10-31 03:43:11 +00:00 committed by Gerrit Code Review
commit bb1685ad84
20 changed files with 277 additions and 70 deletions

View File

@ -21,3 +21,8 @@ from zun_ui.api import rest_api # noqa: F401
class Containers(horizon.Panel):
name = _("Containers")
slug = "container.containers"
class ContainersForAdmin(horizon.Panel):
name = _("Containers")
slug = "container.containers"

View File

@ -0,0 +1,21 @@
# 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 panel to be added to HORIZON_CONFIG. Required.
PANEL = 'container.containers'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'container'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'zun_ui.content.container.containers.panel.ContainersForAdmin'

View File

@ -25,8 +25,10 @@
.factory('horizon.dashboard.container.containers.create.service', createService);
createService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.workflow',
'horizon.framework.util.actions.action-result.service',
@ -37,7 +39,7 @@
];
function createService(
policy, zun, resourceType, workflow,
$q, policy, zun, adminActions, resourceType, workflow,
actionResult, gettext, $qExtensions, modal, toast
) {
var message = {
@ -66,7 +68,14 @@
}
function allowed() {
return policy.ifAllowed({ rules: [['container', 'add_container']] });
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("create") >= 0;
}
return $q.all([
policy.ifAllowed({ rules: [['container', 'add_container']] }),
$qExtensions.booleanAsPromise(adminAction)
]);
}
function submit(context) {

View File

@ -37,6 +37,7 @@
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.modal.deleteModalService',
'horizon.framework.widgets.toast.service',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.events',
'horizon.dashboard.container.containers.validStates'
@ -44,7 +45,7 @@
function deleteForceService(
$location, $q, zun, policy, actionResult, gettext, $qExtensions, deleteModal,
toast, resourceType, events, validStates
toast, adminActions, resourceType, events, validStates
) {
var scope;
var context = {
@ -67,9 +68,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.delete_force.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("delete_force") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.delete_force.indexOf(container.status) >= 0
)
]);
}
// delete selected resource objects

View File

@ -37,6 +37,7 @@
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.modal.deleteModalService',
'horizon.framework.widgets.toast.service',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.events',
'horizon.dashboard.container.containers.validStates'
@ -44,7 +45,7 @@
function deleteStopService(
$location, $q, zun, policy, actionResult, gettext, $qExtensions, deleteModal,
toast, resourceType, events, validStates
toast, adminActions, resourceType, events, validStates
) {
var scope;
var context = {
@ -67,9 +68,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.delete_stop.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("delete_stop") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.delete_stop.indexOf(container.status) >= 0
)
]);
}
// delete selected resource objects

View File

@ -39,6 +39,7 @@
'horizon.framework.widgets.modal.deleteModalService',
'horizon.framework.widgets.table.events',
'horizon.framework.widgets.toast.service',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.events',
'horizon.dashboard.container.containers.validStates'
@ -46,7 +47,7 @@
function deleteService(
$location, $q, $rootScope, zun, policy, actionResult, gettext, $qExtensions, deleteModal,
tableEvents, toast, resourceType, events, validStates
tableEvents, toast, adminActions, resourceType, events, validStates
) {
var scope;
var context = {
@ -69,15 +70,24 @@
}
function allowed(container) {
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("delete") >= 0;
}
// only row actions pass in container
// otherwise, assume it is a batch action
var state;
if (container) {
return $qExtensions.booleanAsPromise(
state = $qExtensions.booleanAsPromise(
validStates.delete.indexOf(container.status) >= 0
);
} else {
return $qExtensions.booleanAsPromise(true);
state = $qExtensions.booleanAsPromise(true);
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
state
]);
}
// delete selected resource objects

View File

@ -28,7 +28,9 @@
executeContainerService);
executeContainerService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
'horizon.framework.util.actions.action-result.service',
@ -40,7 +42,8 @@
];
function executeContainerService(
zun, resourceType, validStates, actionResult, gettext, $qExtensions, modal, waitSpinner, toast
$q, zun, adminActions, resourceType, validStates, actionResult,
gettext, $qExtensions, modal, waitSpinner, toast
) {
// schema
var schema = {
@ -123,9 +126,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.execute.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("execute") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.execute.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -28,7 +28,9 @@
killContainerService);
killContainerService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.basePath',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
@ -40,7 +42,8 @@
];
function killContainerService(
zun, basePath, resourceType, validStates, actionResult, gettext, $qExtensions, modal, toast
$q, zun, adminActions, basePath, resourceType, validStates,
actionResult, gettext, $qExtensions, modal, toast
) {
// schema
var schema = {
@ -98,9 +101,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.kill.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("kill") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.kill.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -21,10 +21,12 @@
manageSecurityGroup);
manageSecurityGroup.$inject = [
"$q",
"horizon.app.core.openstack-service-api.neutron",
"horizon.app.core.openstack-service-api.security-group",
"horizon.app.core.openstack-service-api.zun",
"horizon.dashboard.container.basePath",
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
'horizon.framework.util.actions.action-result.service',
@ -35,8 +37,8 @@
];
function manageSecurityGroup(
neutron, securityGroup, zun, basePath, resourceType, validStates, actionResult,
gettext, $qExtensions, modal, toast
$q, neutron, securityGroup, zun, basePath, adminActions, resourceType, validStates,
actionResult, gettext, $qExtensions, modal, toast
) {
// title for dialog
var title = gettext("Manage Security Groups: container %s");
@ -93,9 +95,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.manage_security_groups.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("manage_security_groups") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.manage_security_groups.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -26,7 +26,9 @@
.factory('horizon.dashboard.container.containers.pause.service', pauseService);
pauseService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
'horizon.framework.util.actions.action-result.service',
@ -35,7 +37,7 @@
];
function pauseService(
zun, resourceType, validStates, actionResult, $qExtensions, toast
$q, zun, adminActions, resourceType, validStates, actionResult, $qExtensions, toast
) {
var message = {
@ -58,9 +60,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.pause.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("pause") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.pause.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -26,7 +26,9 @@
.factory('horizon.dashboard.container.containers.rebuild.service', rebuildService);
rebuildService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.basePath',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
@ -38,7 +40,8 @@
];
function rebuildService(
zun, basePath, resourceType, validStates, actionResult, gettext, $qExtensions, modal, toast
$q, zun, adminActions, basePath, resourceType, validStates,
actionResult, gettext, $qExtensions, modal, toast
) {
var imageDrivers = [
{value: "", name: gettext("Select image driver for changing image.")},
@ -115,9 +118,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.rebuild.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("rebuild") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.rebuild.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -26,7 +26,9 @@
.factory('horizon.dashboard.container.containers.restart.service', restartService);
restartService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.basePath',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
@ -38,7 +40,8 @@
];
function restartService(
zun, basePath, resourceType, validStates, actionResult, gettext, $qExtensions, modal, toast
$q, zun, adminActions, basePath, resourceType, validStates,
actionResult, gettext, $qExtensions, modal, toast
) {
// schema
var schema = {
@ -95,9 +98,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.restart.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("restart") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.restart.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -26,7 +26,9 @@
.factory('horizon.dashboard.container.containers.start.service', startService);
startService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
'horizon.framework.util.actions.action-result.service',
@ -35,7 +37,7 @@
];
function startService(
zun, resourceType, validStates, actionResult, $qExtensions, toast
$q, zun, adminActions, resourceType, validStates, actionResult, $qExtensions, toast
) {
var message = {
@ -58,9 +60,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.start.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("start") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.start.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -26,7 +26,9 @@
.factory('horizon.dashboard.container.containers.stop.service', stopService);
stopService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.basePath',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
@ -38,7 +40,8 @@
];
function stopService(
zun, basePath, resourceType, validStates, actionResult, gettext, $qExtensions, modal, toast
$q, zun, adminActions, basePath, resourceType, validStates,
actionResult, gettext, $qExtensions, modal, toast
) {
// schema
var schema = {
@ -95,9 +98,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.stop.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("stop") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.stop.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -26,7 +26,9 @@
.factory('horizon.dashboard.container.containers.unpause.service', unpauseService);
unpauseService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
'horizon.framework.util.actions.action-result.service',
@ -35,7 +37,7 @@
];
function unpauseService(
zun, resourceType, validStates, actionResult, $qExtensions, toast
$q, zun, adminActions, resourceType, validStates, actionResult, $qExtensions, toast
) {
var message = {
@ -58,9 +60,16 @@
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.unpause.indexOf(container.status) >= 0
);
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("unpause") >= 0;
}
return $q.all([
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.unpause.indexOf(container.status) >= 0
)
]);
}
function perform(selected) {

View File

@ -28,6 +28,7 @@
'$q',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.adminActions',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
'horizon.dashboard.container.containers.workflow',
@ -39,7 +40,7 @@
];
function updateService(
$q, policy, zun, resourceType, validStates, workflow,
$q, policy, zun, adminActions, resourceType, validStates, workflow,
actionResult, gettext, $qExtensions, modal, toast
) {
var message = {
@ -70,8 +71,13 @@
}
function allowed(container) {
var adminAction = true;
if (zun.isAdmin()) {
adminAction = adminActions.indexOf("update") >= 0;
}
return $q.all([
policy.ifAllowed({ rules: [['container', 'edit_container']] }),
$qExtensions.booleanAsPromise(adminAction),
$qExtensions.booleanAsPromise(
validStates.update.indexOf(container.status) >= 0
)

View File

@ -31,6 +31,7 @@
])
.constant('horizon.dashboard.container.containers.events', events())
.constant('horizon.dashboard.container.containers.validStates', validStates())
.constant('horizon.dashboard.container.containers.adminActions', adminActions())
.constant('horizon.dashboard.container.containers.resourceType', 'OS::Zun::Container')
.run(run)
.config(config);
@ -66,6 +67,10 @@
execute: [states.RUNNING],
kill: [states.RUNNING],
delete: [states.CREATED, states.ERROR, states.STOPPED, states.DELETED, states.DEAD],
/* NOTE(shu-mutow): Docker does not allow us to delete PAUSED container.
* There are ways to delete paused container in server,
* but we are according to Docker's policy as now.
*/
delete_force: [
states.CREATED, states.CREATING, states.ERROR, states.RUNNING,
states.STOPPED, states.UNKNOWN, states.DELETED, states.DEAD,
@ -79,6 +84,10 @@
};
}
function adminActions() {
return ["update", "start", "stop", "restart", "rebuild", "kill", "delete_force"];
}
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.app.core.openstack-service-api.zun',
@ -91,7 +100,7 @@
registry.getResourceType(resourceType)
.setNames(gettext('Container'), gettext('Containers'))
.setSummaryTemplateUrl(basePath + 'details/drawer.html')
.setDefaultIndexUrl('/project/container/containers/')
.setDefaultIndexUrl(containerService.getDefaultIndexUrl())
.setProperties(containerProperties())
.setListFunction(containerService.getContainersPromise)
.tableColumns
@ -196,8 +205,12 @@
function config($provide, $windowProvider, $routeProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/container/containers/';
$provide.constant('horizon.dashboard.container.containers.basePath', path);
$routeProvider.when('/project/container/containers', {
templateUrl: path + 'panel.html'
});
$routeProvider
.when('/project/container/containers', {
templateUrl: path + 'panel.html'
})
.when('/admin/container/containers', {
templateUrl: path + 'panel.html'
});
}
})();

View File

@ -19,8 +19,10 @@
.factory('horizon.dashboard.container.containers.service', containersService);
containersService.$inject = [
'$location',
'horizon.app.core.detailRoute',
'horizon.app.core.openstack-service-api.zun'
'horizon.app.core.openstack-service-api.zun',
'horizon.framework.util.navigations.service'
];
/*
@ -31,13 +33,30 @@
* This service provides functions that are used through
* the containers features.
*/
function containersService(detailRoute, zun) {
function containersService($location, detailRoute, zun, navigation) {
return {
getDefaultIndexUrl: getDefaultIndexUrl,
getDetailsPath: getDetailsPath,
getContainerPromise: getContainerPromise,
getContainersPromise: getContainersPromise
};
function getDefaultIndexUrl() {
var dashboard, breadcrumbDashboard;
var path = "/container/containers";
if (zun.isAdmin()) {
dashboard = "/admin";
breadcrumbDashboard = gettext("Admin");
} else {
dashboard = "/project";
breadcrumbDashboard = gettext("Project");
}
var url = dashboard + path + "/";
navigation.setBreadcrumb([
breadcrumbDashboard, gettext("Container"), gettext("Containers")]);
navigation.expandNavigationByUrl(url);
return url;
}
/*
* @ngdoc function
* @name getDetailsPath
@ -46,7 +65,11 @@
* Returns the relative path to the details view.
*/
function getDetailsPath(item) {
return detailRoute + 'OS::Zun::Container/' + item.id;
var detailsPath = detailRoute + 'OS::Zun::Container/' + item.id;
if ($location.url() === '/admin/container/containers') {
detailsPath = detailsPath + "?nav=/admin/container/containers/";
}
return detailsPath;
}
/*

View File

@ -27,6 +27,7 @@
.run(registerDetails);
registerDetails.$inject = [
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.basePath',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.service',
@ -34,6 +35,7 @@
];
function registerDetails(
zun,
basePath,
resourceType,
containerService,
@ -46,17 +48,21 @@
id: 'containerDetailsOverview',
name: gettext('Overview'),
template: basePath + 'details/overview.html'
})
.append({
id: 'containerDetailsLogs',
name: gettext('Logs'),
template: basePath + 'details/logs.html'
})
.append({
id: 'containerDetailsConsole',
name: gettext('Console'),
template: basePath + 'details/console.html'
});
if (!zun.isAdmin()) {
registry.getResourceType(resourceType)
.detailsViews
.append({
id: 'containerDetailsLogs',
name: gettext('Logs'),
template: basePath + 'details/logs.html'
})
.append({
id: 'containerDetailsConsole',
name: gettext('Console'),
template: basePath + 'details/console.html'
});
}
}
})();

View File

@ -19,12 +19,13 @@
.factory('horizon.app.core.openstack-service-api.zun', ZunAPI);
ZunAPI.$inject = [
'$location',
'horizon.framework.util.http.service',
'horizon.framework.widgets.toast.service',
'horizon.framework.util.i18n.gettext'
];
function ZunAPI(apiService, toastService, gettext) {
function ZunAPI($location, apiService, toastService, gettext) {
var containersPath = '/api/zun/containers/';
var zunAvailabilityZonesPath = '/api/zun/availability_zones/';
var capsulesPath = '/api/zun/capsules/';
@ -61,7 +62,8 @@
getImages: getImages,
deleteImage: deleteImage,
getHosts: getHosts,
getHost: getHost
getHost: getHost,
isAdmin: isAdmin
};
return service;
@ -270,5 +272,15 @@
toastService.add('error', message);
};
}
function isAdmin() {
var isAdmin = false;
if ($location.url().startsWith("/admin") ||
$location.url().endsWith("?nav=%2Fadmin%2Fcontainer%2Fcontainers%2F")
) {
isAdmin = true;
}
return isAdmin;
}
}
}());