Add create action to containers panel

This patch adds create button to containers table action,
and container create form as workflow dialog.

Implements: blueprint container-create

Change-Id: I7e47e80109ed5b25b62f5fb774ec3a7610c8caef
This commit is contained in:
shu-mutou 2015-11-06 14:32:34 +09:00
parent 552a9ee56f
commit c955cba77c
18 changed files with 532 additions and 4 deletions

View File

@ -106,14 +106,14 @@ def bay_show(request, id):
return magnumclient(request).bays.get(id)
def container_create(request, bay_id, **kwargs):
def container_create(request, bay_uuid, **kwargs):
"""Creates a container object
:param request: Request context
:param bay_id: ID of a bay (Required)
:param bay_uuid: ID of a bay (Required)
:param kwargs: Image ID, Name, Command, Memory
:returns: Container object
"""
return magnumclient(request).containers.create(bay_id=bay_id, **kwargs)
return magnumclient(request).containers.create(bay_uuid=bay_uuid, **kwargs)
def container_delete(request, id):

View File

@ -7,3 +7,4 @@
@import "baymodel/baymodel";
@import "bay/bay";
@import "containers/containers";

View File

@ -0,0 +1,2 @@
// Custom Style Variables
@import "create/info/info";

View File

@ -0,0 +1,74 @@
/**
* 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';
angular
.module('horizon.dashboard.containers.containers')
.factory('containerModel', containerModel);
containerModel.$inject = [
'horizon.app.core.openstack-service-api.magnum'
];
function containerModel(magnum) {
var model = {
newContainerSpec: {},
// API methods
init: init,
createContainer: createContainer
};
function initNewContainerSpec() {
model.newContainerSpec = {
name: null,
bay_uuid: null,
image: null,
memory: null,
memorysize: null,
memoryunit: "m",
command: null
};
}
function init() {
// Reset the new Bay spec
initNewContainerSpec();
}
function createContainer() {
var finalSpec = angular.copy(model.newContainerSpec);
cleanNullProperties(finalSpec);
return magnum.createContainer(finalSpec);
}
function cleanNullProperties(finalSpec) {
// Initially clean fields that don't have any value.
for (var key in finalSpec) {
if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null
|| key === "memorysize" || key === "memoryunit") {
delete finalSpec[key];
}
}
}
return model;
}
})();

View File

@ -0,0 +1,57 @@
/**
* 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';
angular
.module('horizon.dashboard.containers.containers')
.factory('horizon.dashboard.containers.containers.workflow', containerWorkflow);
containerWorkflow.$inject = [
'horizon.dashboard.containers.basePath',
'horizon.app.core.workflow.factory'
];
function containerWorkflow(basePath, dashboardWorkflow) {
return dashboardWorkflow({
title: gettext('Create Container'),
steps: [
{
title: gettext('Info'),
templateUrl: basePath + 'containers/create/info/info.html',
helpUrl: basePath + 'containers/create/info/info.help.html',
formName: 'containerInfoForm'
},
{
title: gettext('Spec'),
templateUrl: basePath + 'containers/create/spec/spec.html',
helpUrl: basePath + 'containers/create/spec/spec.help.html',
formName: 'containerSpecForm'
}
],
btnText: {
finish: gettext('Create')
},
btnIcon: {
finish: 'fa fa-cloud-download'
}
});
}
})();

View File

@ -0,0 +1,89 @@
/**
* Copyright 2015 NEC Corporation
*
* 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 controller
* @name createContainerInfoController
* @ngController
*
* @description
* Controller for the container info step in create workflow
*/
angular
.module('horizon.dashboard.containers.containers')
.controller('createContainerInfoController', createContainerInfoController);
createContainerInfoController.$inject = [
'$q',
'$scope',
'horizon.dashboard.containers.basePath',
'horizon.app.core.openstack-service-api.magnum'
];
function createContainerInfoController($q, $scope, basePath, magnum) {
var ctrl = this;
ctrl.bays = [{id:"", name: gettext("Choose a Bay")}];
$scope.model.newContainerSpec.bay_uuid = "";
$scope.baydetail = {
name: "",
id: "",
baymodel: "",
master_count: "",
node_count: "",
discovery_url: "",
timeout: ""
};
$scope.changeBay = function(){
// show Bay Detail
if(!$scope.model.newContainerSpec.bay_uuid){
$("#bay_detail").hide();
$("#bay_detail_none").show();
} else {
angular.forEach(ctrl.bays, function(bay, idx){
if($scope.model.newContainerSpec.bay_uuid === bay.id){
$("#bay_detail").show();
$("#bay_detail_none").hide();
$scope.baydetail.name = bay.name;
$scope.baydetail.id = bay.id;
$scope.baydetail.baymodel_id = bay.baymodel_id;
$scope.baydetail.master_count = bay.master_count;
$scope.baydetail.node_count = bay.node_count;
$scope.baydetail.discovery_url = bay.discovery_url;
$scope.baydetail.timeout = bay.timeout;
}
});
}
};
init();
$("#bay_detail").hide();
$("#bay_detail_none").show();
function init() {
magnum.getBays({paginate: false}).success(onGetBays);
}
function onGetBays(response) {
Array.prototype.push.apply(ctrl.bays, response.items);
}
}
})();

View File

@ -0,0 +1,4 @@
<div>
<h3 translate>Description:</h3>
<p translate>Specify container name and choose bay.</p>
</div>

View File

@ -0,0 +1,47 @@
<div ng-controller="createContainerInfoController as ctrl">
<h1 translate>Container Details</h1>
<div class="content">
<div translate class="subtitle">Please provide the name of the Container.</div>
<div class="form-group">
<div class="form-field container-name">
<label class="on-top" translate>Container Name</label>
<input name="container-name" type="text" class="form-control input-sm"
ng-model="model.newContainerSpec.name"
placeholder="{$ 'Name of the container to create.'|translate $}">
</div>
<div class="form-field required container-model">
<label class="on-top" translate>Bay</label>
<select class="form-control" name="container-bay"
ng-model="model.newContainerSpec.bay_uuid"
ng-required="true"
ng-options="bay.id as bay.name for bay in ctrl.bays"
ng-change="changeBay()">
</select>
</div>
</div>
<h2 translate class="section-title">Bay Detail</h2>
<div class="subtitle" id="bay_detail_none" translate>
Choose a Bay
</div>
<div class="subtitle" id="bay_detail">
<dl class=dl-horizontal>
<dt translate>Name</dt>
<dd>{$ baydetail.name $}</dd>
<dt translate>ID</dt>
<dd>{$ baydetail.id $}</dd>
<dt translate>BayModel ID</dt>
<dd>{$ baydetail.baymodel_id $}</dd>
<dt translate>Master Count</dt>
<dd>{$ baydetail.master_count $}</dd>
<dt translate>Node Count</dt>
<dd>{$ baydetail.node_count $}</dd>
<dt translate>Timeout</dt>
<dd>{$ baydetail.timeout $}</dd>
</dl>
</div>
</div>
</div>

View File

@ -0,0 +1,13 @@
.container-memory {
.input-group {
width: 100%;
.container-memorysize {
width: 70%;
}
.container-memoryunit {
width: 30%;
}
}
}

View File

@ -0,0 +1,71 @@
/**
* 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 containersContainerModalController
* @ngController
*
* @description
* Controller for the container create modal
*/
angular
.module('horizon.dashboard.containers.containers')
.controller('containersContainerModalController', containersContainerModalController);
containersContainerModalController.$inject = [
'$modal',
'$window',
'horizon.dashboard.containers.basePath',
'horizon.app.core.openstack-service-api.magnum'
];
function containersContainerModalController($modal, $window, basePath, magnum) {
var ctrl = this;
ctrl.openContainerCreateWizard = openContainerCreateWizard;
function openContainerCreateWizard(launchContext) {
var options = {
controller: 'ModalContainerController',
backdrop: 'static',
template: '<wizard ng-controller="createContainerWizardController"></wizard>',
windowClass: 'modal-dialog-wizard',
resolve: {
launchContext: function() {
return launchContext;
}
}
};
var launchInstanceModal = $modal.open(options);
var handleModalClose = function (redirectPropertyName) {
return function () {
if (launchContext && launchContext[redirectPropertyName]) {
$window.location.href = launchContext[redirectPropertyName];
}
};
};
launchInstanceModal.result.then(
handleModalClose('successUrl'),
handleModalClose('dismissUrl')
);
}
}
})();

View File

@ -0,0 +1,61 @@
/**
* Copyright 2015 NEC Corporation
*
* 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 controller
* @name createContainerSpecController
* @ngController
*
* @description
* Controller for the container spec step in create workflow
*/
angular
.module('horizon.dashboard.containers.containers')
.controller('createContainerSpecController', createContainerSpecController);
createContainerSpecController.$inject = [
'$q',
'$scope',
'horizon.dashboard.containers.basePath',
'horizon.app.core.openstack-service-api.magnum'
];
function createContainerSpecController($q, $scope, basePath, magnum) {
var ctrl = this;
ctrl.memoryunits = [{unit: "b", label: gettext("bytes")},
{unit: "k", label: gettext("KB")},
{unit: "m", label: gettext("MB")},
{unit: "g", label: gettext("GB")}];
$scope.changeMemory = function(){
if($scope.model.newContainerSpec.memorysize > 0){
$scope.model.newContainerSpec.memory = $scope.model.newContainerSpec.memorysize + $scope.model.newContainerSpec.memoryunit;
}else{
$scope.model.newContainerSpec.memory = "";
}
};
$scope.changeMemoryUnit = function(){
$scope.changeMemory();
};
$scope.changeMemorySize = function(){
$scope.changeMemory();
};
}
})();

View File

@ -0,0 +1,4 @@
<div>
<h3 translate>Description:</h3>
<p translate>Specify the specs for the container.</p>
</div>

View File

@ -0,0 +1,44 @@
<div ng-controller="createContainerSpecController as ctrl">
<h1 translate>Container Spec</h1>
<div class="content">
<div translate class="subtitle">Please provide the specs for this Container.</div>
<div class="form-group">
<div class="form-field required container-image">
<label class="on-top" translate>Image</label>
<input name="container-image" type="text"
class="form-control input-sm"
ng-required="true"
ng-model="model.newContainerSpec.image"
placeholder="{$ 'Name of the container image.'|translate $}">
</div>
<div class="form-field container-memory">
<label class="on-top" translate>Memory</label>
<div class="input-group">
<input name="container-memorysize" type="number" ng-pattern="/^[0-9]+$/"
class="form-control form-inline input-sm container-memorysize"
ng-model="model.newContainerSpec.memorysize"
placeholder="{$ 'The container memory size.'|translate $}"
ng-change="changeMemorySize()">
<select name="container-memoryunit"
class="form-control form-inline input-sm container-memoryunit"
ng-options="mu.unit as mu.label for mu in ctrl.memoryunits"
ng-model="model.newContainerSpec.memoryunit"
ng-change="changeMemoryUnit()">
</select>
</div>
<input name="container-memory" type="hidden"
ng-model="model.newContainerSpec.memory">
</div>
<div class="form-field container-command">
<label class="on-top" translate>Command</label>
<input name="container-command" type="text"
class="form-control input-sm"
ng-model="model.newContainerSpec.command"
placeholder="{$ 'Send command to the contaier in a line.'|translate $}">
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
/**
* 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 createContainerWizardController
* @ngController
*
* @description
* Controller for the container create modal
*/
angular
.module('horizon.dashboard.containers.containers')
.controller('createContainerWizardController', createContainerWizardController);
createContainerWizardController.$inject = [
'$scope',
'containerModel',
'horizon.dashboard.containers.containers.workflow'
];
function createContainerWizardController($scope, model, workflow) {
$scope.workflow = workflow;
$scope.model = model;
$scope.model.init();
$scope.submit = $scope.model.createContainer;
}
})();

View File

@ -20,6 +20,14 @@
</tr>
<tr>
<th colspan="100" class="action-col">
<action-list class="btn-addon">
<a href="javascipt:void(0);"
class="btn btn-default btn-sm btn-launch ng-scope"
ng-controller="containersContainerModalController as modal"
ng-click="modal.openContainerCreateWizard({successUrl: '/containers/containers/'})">
<span class="fa fa-plus"> <translate>Create Container</translate></span>
</a>
</action-list>
<action-list class="btn-addon">
<action
action-classes="'btn btn-default btn-sm btn-danger'"

View File

@ -34,6 +34,7 @@
getBayModels: getBayModels,
deleteBayModel: deleteBayModel,
deleteBayModels: deleteBayModels,
createContainer: createContainer,
getContainers: getContainers,
deleteContainer: deleteContainer,
deleteContainers: deleteContainers,
@ -104,6 +105,13 @@
// Containers //
////////////////
function createContainer(params) {
return apiService.post('/api/containers/containers/', params)
.error(function() {
toastService.add('error', gettext('Unable to create Container.'));
});
}
function getContainers() {
return apiService.get('/api/containers/containers/')
.error(function() {

View File

@ -54,7 +54,7 @@ class MagnumApiTests(test.APITestCase):
def test_container_create(self):
container = self.magnum_containers.first()
form_data = {'bay_id': container['bay'],
form_data = {'bay_uuid': container['bay'],
'name': container['name']}
magnumclient = self.stub_magnumclient()