Add a panel and table to display Containers

Change-Id: I52f36e3a6f02a83e11d717ac4d247f04fdb3a177
Implements: blueprint containers-table-view
This commit is contained in:
Rob Cresswell 2015-10-19 17:25:21 +01:00
parent 564f9c2e18
commit 0caaa72286
14 changed files with 447 additions and 4 deletions

View File

@ -118,7 +118,7 @@ class Containers(generic.View):
item under this is a Container.
"""
result = magnum.container_list(request)
return {'items': [n.to_dict() for n in result]}
return {'items': [change_to_id(n.to_dict()) for n in result]}
@rest_utils.ajax(data_required=True)
def delete(self, request):

View File

View File

@ -0,0 +1,25 @@
# Copyright 2015 Cisco Systems
#
# 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
from magnum_ui import dashboard
class Containers(horizon.Panel):
name = _("Containers")
slug = "containers"
dashboard.Containers.register(Containers)

View File

@ -0,0 +1,14 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}
{% trans "Containers" %}
{% endblock %}
{% block page_header %}
<hz-page-header header="'Containers' | translate"/>
{% endblock page_header %}
{% block main %}
<ng-include src="'{{ STATIC_URL }}dashboard/containers/containers/table/table.html'"></ng-include>
{% endblock %}

View File

@ -0,0 +1,30 @@
# Copyright 2015 Cisco Systems, Inc.
#
# 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.core.urlresolvers import reverse
import horizon
from magnum_ui.containers.panel import Containers as containers_panel
from openstack_dashboard.test import helpers as test
class ContainerTests(test.TestCase):
def test_registration(self):
dashboard = horizon.get_dashboard('containers')
registered_panel = dashboard.get_panel('containers')
self.assertEqual(registered_panel.slug, containers_panel.slug)
def test_index(self):
index = reverse('horizon:containers:containers:index')
res = self.client.get(index)
self.assertTemplateUsed(res, 'containers/containers/index.html')

View File

@ -0,0 +1,21 @@
# Copyright 2015 Cisco Systems, Inc.
#
# 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 magnum_ui.containers import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
]

View File

@ -0,0 +1,21 @@
# Copyright 2015 Cisco Systems
#
# 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 horizon import views
from magnum_ui.api.rest import magnum # noqa
class IndexView(views.APIView):
template_name = 'containers/containers/index.html'

View File

@ -20,7 +20,7 @@ import horizon
class Containers(horizon.Dashboard):
name = _("Containers")
slug = "containers"
panels = ('bay', 'baymodel')
panels = ('bay', 'baymodel', 'containers')
default_panel = 'bay'
horizon.register(Containers)

View File

@ -27,7 +27,8 @@
angular
.module('horizon.dashboard.containers', [
'horizon.dashboard.containers.bay',
'horizon.dashboard.containers.baymodel'
'horizon.dashboard.containers.baymodel',
'horizon.dashboard.containers.containers'
])
.config(config);

View File

@ -0,0 +1,32 @@
/**
* Copyright 2015 Cisco Systems, Inc.
*
* 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.containers.containers
* @ngModule
*
* @description
* Provides all the services and widgets require to display the containers
* panel
*/
angular
.module('horizon.dashboard.containers.containers', []);
})();

View File

@ -0,0 +1,25 @@
/**
* Copyright 2015 Cisco Systems, Inc.
*
* 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.dashboard.containers.containers', function() {
it('should exist', function() {
expect(angular.module('horizon.dashboard.containers.containers')).toBeDefined();
});
});
})();

View File

@ -0,0 +1,80 @@
/**
* Copyright 2015 Cisco Systems, Inc.
*
* 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 containersContainersTableController
* @ngController
*
* @description
* Controller for the Magnum containers table
*/
angular
.module('horizon.dashboard.containers.containers')
.controller('containersContainersTableController', containersContainersTableController);
containersContainersTableController.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.magnum'
];
function containersContainersTableController($scope, magnum) {
var ctrl = this;
ctrl.icontainers = [];
ctrl.containers = [];
ctrl.singleDelete = singleDelete;
ctrl.batchDelete = batchDelete;
init();
function init() {
magnum.getContainers().success(getContainersSuccess);
}
function getContainersSuccess(response) {
ctrl.containers = response.items;
}
function singleDelete(container) {
magnum.deleteContainer(container.id).success(function() {
ctrl.containers.splice(ctrl.containers.indexOf(container), 1);
});
}
function batchDelete() {
var ids = [];
for (var id in $scope.selected) {
if ($scope.selected[id].checked) {
ids.push(id);
}
}
magnum.deleteContainers(ids).success(function() {
for (var c in ids) {
var todelete = ctrl.containers.filter(function(obj) {
return obj.id == ids[c];
});
ctrl.containers.splice(ctrl.containers.indexOf(todelete[0]), 1);
}
$scope.selected = {};
});
}
}
})();

View File

@ -0,0 +1,164 @@
<table ng-controller="containersContainersTableController as table"
hz-table ng-cloak
st-table="table.icontainers"
st-safe-src="table.containers"
default-sort="name"
default-sort-reverse="false"
class="table-striped table-rsp table-detail modern">
<thead>
<!--
Table-batch-actions:
This is where batch actions like searching, creating, and deleting.
-->
<tr>
<th colspan="100" class="search-header">
<hz-search-bar group-classes="input-group-sm" icon-classes="fa-search">
<action-list class="btn-addon">
<action
action-classes="'btn btn-default btn-sm btn-danger'"
disabled="numSelected === 0"
callback="table.batchDelete">
<i class="fa fa-trash-o"></i>
<translate>Delete Containers</translate>
</action>
</action-list>
</hz-search-bar>
</th>
</tr>
<!--
Table-column-headers:
The headers for the table columns
-->
<tr>
<th class=select-col>
<input type="checkbox" hz-select-all="table.icontainers">
</th>
<th class="expander"></th>
<th class="rsp-p1" st-sort="name" st-sort-default>
<translate>Name</translate>
</th>
<th class="rsp-p1" st-sort="id" >
<translate>UUID</translate>
</th>
<th class="rsp-p1" st-sort="status" >
<translate>Status</translate>
</th>
<th class="rsp-p3" st-sort="bay" >
<translate>Bay UUID</translate>
</th>
<th class="rsp-p2" st-sort="image" >
<translate>Image</translate>
</th>
<th class="rsp-p3" st-sort="memory" >
<translate>Memory</translate>
</th>
<th class="action-col">
<translate>Actions</translate>
</th>
</tr>
</thead>
<tbody>
<!--
Table-rows:
This is where we declaratively define the table columns.
Include select-col if you want to select all.
Include expander if you want to inline details.
Include action-col if you want to perform actions.
rsp-p1 rsp-p2 are responsive priority as user resizes window.
-->
<tr ng-repeat-start="c in table.icontainers track by c.id"
nt-class="{'st-selected': checked[c.id]}">
<td class="select-col">
<input type="checkbox"
ng-model="selected[c.id].checked"
hz-select="c">
</td>
<td class="expander">
<i class="fa fa-chevron-right"
hz-expand-detail
duration="200">
</i>
</td>
<td class="rsp-p1">{$ c.name $}</td>
<td class="rsp-p1">{$ c.id $}</td>
<td class="rsp-p1">{$ c.status $}</td>
<td class="rsp-p3">{$ c.bay_uuid $}</td>
<td class="rsp-p2">{$ c.image $}</td>
<td class="rsp-p3">{$ c.memory $}</td>
<td class="action-col">
<!--
Table-row-action-column:
Actions taken here applies to a single item/row.
-->
<action-list dropdown>
<action
button-type="split-button"
action-classes="'btn btn-default btn-danger btn-sm'"
callback="table.singleDelete" item="c">
<translate>Delete</translate>
</action>
</action-list>
</td>
</tr>
<tr ng-repeat-end class="detail-row">
<!--
Table-row-details:
Provides detail view of specific view, with more information than can be
displayed in the table alone.
-->
<td class="detail" colspan="100">
<dl class=dl-horizontal>
<dt><translate>Name</translate></dt>
<dd>{$ c.name $}</dd>
<dt><translate>UUID</translate></dt>
<dd>{$ c.id $}</dd>
<dt><translate>Status</translate></dt>
<dd>{$ c.status $}</dd>
<dt><translate>Bay UUID</translate></dt>
<dd>{$ c.bay_uuid $}</dd>
<dt><translate>Image</translate></dt>
<dd>{$ c.image $}</dd>
<dt><translate>Memory</translate></dt>
<dd>{$ c.memory $}</dd>
<dt><translate>Command</translate></dt>
<dd>{$ c.command $}</dd>
</dl>
</td>
</tr>
</tbody>
<tfoot>
<td colspan="100">
<span class="display">{$ table.icontainers.length|itemCount $}</span>
<div st-pagination="" st-items-by-page="10"
st-displayed-pages="10"></div>
</td>
</tfoot>
</table>

View File

@ -33,7 +33,11 @@
deleteBays: deleteBays,
getBayModels: getBayModels,
deleteBayModel: deleteBayModel,
deleteBayModels: deleteBayModels
deleteBayModels: deleteBayModels,
getContainers: getContainers,
deleteContainer: deleteContainer,
deleteContainers: deleteContainers,
};
return service;
@ -94,6 +98,32 @@
toastService.add('error', gettext('Unable to delete the BayModels.'))
})
}
////////////////
// Containers //
////////////////
function getContainers() {
return apiService.get('/api/containers/containers/')
.error(function() {
toastService.add('error', gettext('Unable to retrieve the Containers.'));
});
}
function deleteContainer(id) {
return apiService.delete('/api/containers/containers/', [id])
.error(function() {
toastService.add('error', gettext('Unable to delete the Container.'));
});
}
function deleteContainers(ids) {
return apiService.delete('/api/containers/containers/', ids)
.error(function() {
toastService.add('error', gettext('Unable to delete the Containers.'));
});
}
}
}());