Add capsule panel

This patch adds capsule panel that includes capsule list view.
Other views and actions will be implemented in subsequent patches.

To enable this panel, copy enabled file[1] to horizon enabled
directory[2] and restart horizon.

[1] zun_ui/enabled/_1332_project_container_capsules_panel.py
[2] openstack_dashboard/local/enabled/

Change-Id: I6d89704a8fe4fa32cee52bbf2c38e0b10cd01f1a
Partial-Implements: blueprint capsule
This commit is contained in:
Shu Muto 2018-06-13 16:19:20 +09:00
parent a667cd4bf0
commit 247af4b88b
14 changed files with 442 additions and 0 deletions

View File

@ -266,6 +266,12 @@ def availability_zone_list(request):
return list
def capsule_list(request, limit=None, marker=None, sort_key=None,
sort_dir=None):
return zunclient(request).capsules.list(limit, marker, sort_key,
sort_dir)
def image_list(request, limit=None, marker=None, sort_key=None,
sort_dir=None, detail=False):
# FIXME(shu-mutou): change "detail" param to True, if it enabled.

View File

@ -163,6 +163,22 @@ class AvailabilityZones(generic.View):
return {'items': [i.to_dict() for i in result]}
@urls.register
class Capsules(generic.View):
"""API for Capsules"""
url_regex = r'zun/capsules/$'
@rest_utils.ajax()
def get(self, request):
"""Get a list of the Capsules.
The returned result is an object with property 'items' and each
item under this is a Capsules.
"""
result = client.capsule_list(request)
return {'items': [i.to_dict() for i in result]}
@urls.register
class Images(generic.View):
"""API for Zun Images"""

View File

@ -0,0 +1,19 @@
# 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.utils.translation import ugettext_lazy as _
import horizon
class Capsules(horizon.Panel):
name = _("Capsules")
slug = "container.capsules"

View File

@ -0,0 +1,20 @@
# 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 = _("Capsules")
urlpatterns = [
url('', views.AngularIndexView.as_view(title=title), name='index'),
]

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.capsules'
# 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 = 'project'
# Python panel class of the PANEL to be added.
ADD_PANEL = 'zun_ui.content.container.capsules.panel.Capsules'

View File

@ -0,0 +1,58 @@
/**
* 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.dashboard.container.capsules.actions
*
* @description
* Provides all of the actions for capsules.
*/
angular.module('horizon.dashboard.container.capsules.actions',
[
'horizon.framework',
'horizon.dashboard.container'
])
.run(registerCapsuleActions);
registerCapsuleActions.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.framework.util.i18n.gettext',
'horizon.dashboard.container.capsules.actions.refresh.service',
'horizon.dashboard.container.capsules.resourceType'
];
function registerCapsuleActions(
registry,
gettext,
refreshCapsuleService,
resourceType
) {
var capsulesResourceType = registry.getResourceType(resourceType);
// FIXME(shu-mutow): refresh action is dummy. remove it when add other action.
capsulesResourceType.itemActions
.append({
id: 'refreshCapsuleAction',
service: refreshCapsuleService,
template: {
text: gettext('Refresh')
}
});
}
})();

View File

@ -0,0 +1,68 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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 factory
* @name horizon.dashboard.container.capsules.refresh.service
* @Description
* refresh container.
*/
angular
.module('horizon.dashboard.container.capsules.actions')
.factory('horizon.dashboard.container.capsules.actions.refresh.service', refreshService);
refreshService.$inject = [
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.capsules.resourceType',
'horizon.framework.util.actions.action-result.service',
'horizon.framework.util.q.extensions'
];
function refreshService(
zun, resourceType, actionResult, $qExtensions
) {
var service = {
initAction: initAction,
allowed: allowed,
perform: perform
};
return service;
//////////////
// include this function in your service
// if you plan to emit events to the parent controller
function initAction() {
}
function allowed() {
return $qExtensions.booleanAsPromise(true);
}
function perform(selected) {
// refresh selected capsule
return $qExtensions.booleanAsPromise(true).then(success);
function success() {
var result = actionResult.getActionResult().updated(resourceType, selected.id);
return result.result;
}
}
}
})();

View File

@ -0,0 +1,150 @@
/**
* 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
* @name horizon.dashboard.container.capsules
* @ngModule
* @description
* Provides all the services and widgets require to display the capsules
* panel
*/
angular
.module('horizon.dashboard.container.capsules', [
'ngRoute',
'horizon.dashboard.container.capsules.actions'
])
.constant('horizon.dashboard.container.capsules.events', events())
.constant('horizon.dashboard.container.capsules.resourceType', 'OS::Zun::Capsule')
.run(run)
.config(config);
/**
* @ngdoc constant
* @name horizon.dashboard.container.capsules.events
* @description A list of events used by Capsules
* @returns {Object} Event constants
*/
function events() {
return {
CREATE_SUCCESS: 'horizon.dashboard.container.capsules.CREATE_SUCCESS',
DELETE_SUCCESS: 'horizon.dashboard.container.capsules.DELETE_SUCCESS'
};
}
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.capsules.basePath',
'horizon.dashboard.container.capsules.resourceType',
'horizon.dashboard.container.capsules.service'
];
function run(registry, zun, basePath, resourceType, capsuleService) {
registry.getResourceType(resourceType)
.setNames(gettext('Capsule'), gettext('Capsules'))
// for detail summary view on table row.
.setSummaryTemplateUrl(basePath + 'drawer.html')
// for table row items and detail summary view.
.setProperties(capsuleProperties())
.setListFunction(capsuleService.getCapsulesPromise)
.tableColumns
.append({
id: 'meta_name',
priority: 1,
sortDefault: true
})
.append({
id: 'id',
priority: 2
})
.append({
id: 'status',
priority: 1
})
.append({
id: 'cpu',
priority: 3
})
.append({
id: 'memory',
priority: 3
});
// for magic-search
registry.getResourceType(resourceType).filterFacets
.append({
'label': gettext('ID'),
'name': 'id',
'singleton': true
})
.append({
'label': gettext('Name'),
'name': 'meta_name',
'singleton': true
})
.append({
'label': gettext('Status'),
'name': 'status',
'singleton': true
});
}
function capsuleProperties() {
return {
'addresses': {label: gettext('Addresses'), filters: ['noValue', 'json'] },
'capsule_versionid': {label: gettext('Capsule Version ID'), filters: ['noValue'] },
'containers': {label: gettext('Containers'), filters: ['noValue', 'json'] },
'container_uuids': {label: gettext('Container UUIDs'), filters: ['noValue', 'json'] },
'cpu': {label: gettext('CPU'), filters: ['noValue'] },
'created_at': { label: gettext('Created'), filters: ['simpleDate'] },
'id': {label: gettext('ID'), filters: ['noValue'] },
'links': {label: gettext('Links'), filters: ['noValue', 'json'] },
'memory': { label: gettext('Memory'), filters: ['noValue'] },
'meta_labels': {label: gettext('Labels'), filters: ['noValue', 'json'] },
'meta_name': { label: gettext('Name'), filters: ['noName'] },
'project_id': { label: gettext('Project ID'), filters: ['noValue'] },
'restart_policy': { label: gettext('Restart Policy'), filters: ['noValue'] },
'status': { label: gettext('Status'), filters: ['noValue'] },
'status_reason': { label: gettext('Status Reason'), filters: ['noValue'] },
'updated_at': { label: gettext('Updated'), filters: ['simpleDate'] },
'user_id': { label: gettext('User ID'), filters: ['noValue'] },
'volumes_info': {label: gettext('Volumes Info'), filters: ['noValue', 'json'] }
};
}
config.$inject = [
'$provide',
'$windowProvider',
'$routeProvider'
];
/**
* @name config
* @param {Object} $provide
* @param {Object} $windowProvider
* @param {Object} $routeProvider
* @description Routes used by this module.
* @returns {undefined} Returns nothing
*/
function config($provide, $windowProvider, $routeProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/container/capsules/';
$provide.constant('horizon.dashboard.container.capsules.basePath', path);
$routeProvider.when('/project/container/capsules', {
templateUrl: path + 'panel.html'
});
}
})();

View File

@ -0,0 +1,61 @@
/*
* 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.dashboard.container.capsules')
.factory('horizon.dashboard.container.capsules.service', capsulesService);
capsulesService.$inject = [
'horizon.app.core.detailRoute',
'horizon.app.core.openstack-service-api.zun'
];
/*
* @ngdoc factory
* @name horizon.dashboard.container.capsules.service
*
* @description
* This service provides functions that are used through
* the capsules of container features.
*/
function capsulesService(detailRoute, zun) {
return {
getCapsulesPromise: getCapsulesPromise
};
/*
* @ngdoc function
* @name getCapsulesPromise
* @description
* Given filter/query parameters, returns a promise for the matching
* capsules. This is used in displaying lists of capsules.
*/
function getCapsulesPromise(params) {
return zun.getCapsules(params).then(modifyResponse);
}
function modifyResponse(response) {
return {data: {items: response.data.items.map(modifyItem)}};
}
function modifyItem(item) {
item.id = item.uuid;
item.trackBy = item.uuid;
item.trackBy = item.trackBy.concat(item.updated_at);
return item;
}
}
})();

View File

@ -0,0 +1,7 @@
<hz-resource-property-list
resource-type-name="OS::Zun::Capsule"
item="item"
property-groups="[['id', 'cpu', 'memory', 'capsule_versionid'],
['meta_labels', 'created_at', 'updated_at'],
['addresses', 'volumes_info', 'status_reason']]">
</hz-resource-property-list>

View File

@ -0,0 +1,4 @@
<hz-resource-panel resource-type-name="OS::Zun::Capsule">
<hz-resource-table resource-type-name="OS::Zun::Capsule"
track-by="trackBy"></hz-resource-table>
</hz-resource-panel>

View File

@ -24,6 +24,7 @@
angular
.module('horizon.dashboard.container', [
'horizon.dashboard.container.containers',
'horizon.dashboard.container.capsules',
'horizon.dashboard.container.images',
'ngRoute'
])

View File

@ -27,6 +27,7 @@
function ZunAPI(apiService, toastService, gettext) {
var containersPath = '/api/zun/containers/';
var zunAvailabilityZonesPath = '/api/zun/availability_zones/';
var capsulesPath = '/api/zun/capsules/';
var imagesPath = '/api/zun/images/';
var hostsPath = '/api/zun/hosts/';
var service = {
@ -52,6 +53,7 @@
detachNetwork: detachNetwork,
updatePortSecurityGroup: updatePortSecurityGroup,
getZunAvailabilityZones: getZunAvailabilityZones,
getCapsules: getCapsules,
pullImage: pullImage,
getImages: getImages,
deleteImage: deleteImage,
@ -197,6 +199,15 @@
return apiService.get(zunAvailabilityZonesPath).error(error(msg));
}
//////////////
// Capsules //
//////////////
function getCapsules() {
var msg = gettext('Unable to retrieve the Capsules.');
return apiService.get(capsulesPath).error(error(msg));
}
////////////
// Images //
////////////