Use zunclient instead of stub

Use python-zunclient instead of stub client in skeleton.
Then Zun-UI interacts with Zun API.

This patch enable CRUD operation of containers.

Change-Id: Idc869c0d4d40895e42ccf941f7fcb9d6d1dfb662
Implements: blueprint use-zunclient
This commit is contained in:
Shu Muto 2016-10-04 16:16:49 +09:00
parent 325380b7b2
commit 543a2a02f4
11 changed files with 170 additions and 70 deletions

View File

@ -8,8 +8,8 @@
# #
# PBR should always appear first # PBR should always appear first
pbr>=1.6 # Apache-2.0 pbr>=1.6 # Apache-2.0
# If python-higginsclient will be created, we will use it. # If python-zunclient will be released, we will use it.
#python-higginsclient>=0.1.0 # Apache-2.0 #python-zunclient>=0.0.1 # Apache-2.0
Babel>=2.3.4 # BSD Babel>=2.3.4 # BSD
Django<1.9,>=1.8 # BSD Django<1.9,>=1.8 # BSD
django-babel>=0.5.1 # BSD django-babel>=0.5.1 # BSD

View File

@ -16,6 +16,13 @@ deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
commands = python manage.py test {posargs} --settings=zun_ui.test.settings commands = python manage.py test {posargs} --settings=zun_ui.test.settings
# Until python-zunclient released, install from github temporary.
[testenv:py27]
basepython = python2.7
commands =
pip install git+https://github.com/openstack/python-zunclient.git
python manage.py test {posargs} --settings=zun_ui.test.settings
[testenv:pep8] [testenv:pep8]
commands = flake8 {posargs} commands = flake8 {posargs}

View File

@ -11,45 +11,20 @@
# under the License. # under the License.
from __future__ import absolute_import
import logging
# from zunclient.v1 import client as zun_client
from horizon import exceptions from horizon import exceptions
from horizon.utils.memoized import memoized from horizon.utils.memoized import memoized
# from openstack_dashboard.api import base import logging
from openstack_dashboard.api import base
# for stab, should remove when use CLI API from zunclient.v1 import client as zun_client
import copy
import uuid
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CONTAINER_CREATE_ATTRS = ['name'] CONTAINER_CREATE_ATTRS = ['name', 'image', 'command', 'memory', 'environment']
STUB_DATA = {}
# for stab, should be removed when use CLI API
class StubResponse(object):
def __init__(self, info):
self._info = info
def __repr__(self):
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
def to_dict(self):
return copy.deepcopy(self._info)
@memoized @memoized
def zunclient(request): def zunclient(request):
pass
""""
zun_url = "" zun_url = ""
try: try:
zun_url = base.url_for(request, 'container') zun_url = base.url_for(request, 'container')
@ -64,7 +39,6 @@ def zunclient(request):
input_auth_token=request.user.token.id, input_auth_token=request.user.token.id,
zun_url=zun_url) zun_url=zun_url)
return c return c
"""
def container_create(request, **kwargs): def container_create(request, **kwargs):
@ -75,41 +49,28 @@ def container_create(request, **kwargs):
else: else:
raise exceptions.BadRequest( raise exceptions.BadRequest(
"Key must be in %s" % ",".join(CONTAINER_CREATE_ATTRS)) "Key must be in %s" % ",".join(CONTAINER_CREATE_ATTRS))
if key == "labels": if key == "environment":
labels = {} envs = {}
vals = value.split(",") vals = value.split(",")
for v in vals: for v in vals:
kv = v.split("=", 1) kv = v.split("=", 1)
labels[kv[0]] = kv[1] envs[kv[0]] = kv[1]
args["labels"] = labels args["environment"] = envs
# created = zunclient(request).containers.create(**args) return zunclient(request).containers.create(**args)
# create dummy response
args["uuid"] = uuid.uuid1().hex
created = StubResponse(args)
for k in args:
setattr(created, k, args[k])
STUB_DATA[created.uuid] = created
return created
def container_delete(request, id): def container_delete(request, id, force=False):
# deleted = zunclient(request).containers.delete(id) # TODO(shu-mutou): force option should be provided by user.
deleted = STUB_DATA.pop(id) return zunclient(request).containers.delete(id, force)
return deleted
def container_list(request, limit=None, marker=None, sort_key=None, def container_list(request, limit=None, marker=None, sort_key=None,
sort_dir=None, detail=True): sort_dir=None, detail=True):
# list = zunclient(request).containers.list(limit, marker, sort_key, # TODO(shu-mutou): detail option should be added, if it is
# sort_dir, detail) # implemented in Zun API
list = [STUB_DATA[data] for data in STUB_DATA] return zunclient(request).containers.list(limit, marker, sort_key,
return list sort_dir)
def container_show(request, id): def container_show(request, id):
# show = zunclient(request).containers.get(id) return zunclient(request).containers.get(id)
show = STUB_DATA.get(id)
return show

View File

@ -65,6 +65,12 @@
.setProperty('id', { .setProperty('id', {
label: gettext('ID') label: gettext('ID')
}) })
.setProperty('image', {
label: gettext('Image')
})
.setProperty('status', {
label: gettext('Status')
})
.setListFunction(listFunction) .setListFunction(listFunction)
.tableColumns .tableColumns
.append({ .append({
@ -77,6 +83,14 @@
.append({ .append({
id: 'id', id: 'id',
priority: 2 priority: 2
})
.append({
id: 'image',
priority: 2
})
.append({
id: 'status',
priority: 2
}); });
// for magic-search // for magic-search
registry.getResourceType(resourceType).filterFacets registry.getResourceType(resourceType).filterFacets
@ -89,6 +103,16 @@
'label': gettext('ID'), 'label': gettext('ID'),
'name': 'id', 'name': 'id',
'singleton': true 'singleton': true
})
.append({
'label': gettext('Image'),
'name': 'image',
'singleton': true
})
.append({
'label': gettext('Status'),
'name': 'status',
'singleton': true
}); });
function listFunction(params) { function listFunction(params) {

View File

@ -34,7 +34,14 @@
function initNewContainerSpec() { function initNewContainerSpec() {
model.newContainerSpec = { model.newContainerSpec = {
uuid: null,
name: null, name: null,
image: null,
command: null,
memory: null,
memory_size: null,
memory_unit: "m",
environment: null
}; };
} }
@ -56,7 +63,8 @@
// Not only "null", blank too. // Not only "null", blank too.
for (var key in finalSpec) { for (var key in finalSpec) {
if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null
|| finalSpec[key] === "") { || finalSpec[key] === ""
|| key === "memory_size" || key === "memory_unit") {
delete finalSpec[key]; delete finalSpec[key];
} }
} }

View File

@ -36,5 +36,23 @@
function createContainerInfoController($q, $scope, basePath, zun, gettext) { function createContainerInfoController($q, $scope, basePath, zun, gettext) {
var ctrl = this; var ctrl = this;
ctrl.memory_units = [{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.memory_size > 0){
$scope.model.newContainerSpec.memory = $scope.model.newContainerSpec.memory_size + $scope.model.newContainerSpec.memory_unit;
}else{
$scope.model.newContainerSpec.memory = null;
}
};
$scope.changeMemoryUnit = function(){
$scope.changeMemory();
};
$scope.changeMemorySize = function(){
$scope.changeMemory();
};
} }
})(); })();

View File

@ -1,4 +1,13 @@
<dl> <dl>
<!-- TODO(shu-mutou): descript more! -->
<dt translate>Container Name</dt> <dt translate>Container Name</dt>
<dd translate>An arbitrary human-readable name</dd> <dd translate>An arbitrary human-readable name</dd>
<dt translate>Image</dt>
<dd translate>Name or ID of container image</dd>
<dt translate>Command</dt>
<dd translate>Command sent to the container</dd>
<dt translate>Memory</dt>
<dd translate>The container memory size</dd>
<dt translate>Environment Variables</dt>
<dd translate>The environment variables in comma separated KEY=VALUE pairs</dd>
</dl> </dl>

View File

@ -1,6 +1,6 @@
<div ng-controller="createContainerInfoController as ctrl"> <div ng-controller="createContainerInfoController as ctrl">
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-6">
<div class="form-group"> <div class="form-group">
<label class="control-label" for="container-name" translate>Container Name</label> <label class="control-label" for="container-name" translate>Container Name</label>
<input name="container-name" type="text" class="form-control" id="container-name" <input name="container-name" type="text" class="form-control" id="container-name"
@ -8,5 +8,51 @@
placeholder="{$ 'Name of the container to create.'|translate $}"> placeholder="{$ 'Name of the container to create.'|translate $}">
</div> </div>
</div> </div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label" for="container-image">
<translate>Image</translate>
<span class="hz-icon-required fa fa-asterisk"></span>
</label>
<input name="container-image" type="text" class="form-control" id="container-image"
ng-model="model.newContainerSpec.image"
ng-required="true"
placeholder="{$ 'Name or ID of the container image.'|translate $}">
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<label class="control-label" for="container-command" translate>Command</label>
<input name="container-command" type="text" class="form-control" id="container-command"
ng-model="model.newContainerSpec.command"
placeholder="{$ 'A command that will be sent to the container.'|translate $}">
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label" for="container-memory-size" translate>Memory Size</label>
<input name="container-memory-size" type="number" min="1"
class="form-control" ng-model="model.newContainerSpec.memory_size"
placeholder="{$ 'The container memory size.'|translate $}"
ng-change="changeMemorySize()" id="container-memory-size">
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label" for="container-memory-unit" translate>Memory Unit</label>
<select name="container-memory-unit" id="container-memory-unit"
class="form-control" ng-options="mu.unit as mu.label for mu in ctrl.memory_units"
ng-model="model.newContainerSpec.memory_unit" ng-change="changeMemoryUnit()">
</select>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<label class="control-label" for="container-environment" translate>Environment Variables</label>
<input name="container-environment" type="text" class="form-control" id="container-environment"
ng-model="model.newContainerSpec.environment"
placeholder="{$ 'KEY1=VALUE1,KEY2=VALUE2...'|translate $}">
</div>
</div>
</div> </div>
</div> </div>

View File

@ -28,12 +28,9 @@
.controller('horizon.dashboard.container.containers.DrawerController', controller); .controller('horizon.dashboard.container.containers.DrawerController', controller);
controller.$inject = [ controller.$inject = [
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.resourceType'
]; ];
function controller(zun, resourceType) { function controller() {
var ctrl = this;
} }
})(); })();

View File

@ -5,12 +5,24 @@
<dt translate>ID</dt> <dt translate>ID</dt>
<dd>{$ item.id $}</dd> <dd>{$ item.id $}</dd>
</dl> </dl>
<dl class="col-md-4">
<dt translate>Image</dt>
<dd>{$ item.image $}</dd>
</dl>
<dl class="col-md-3">
<dt translate>Status</dt>
<dd>{$ item.status $}</dd>
</dl>
</span> </span>
</div> </div>
<div class="row"> <div class="row">
<dl class="col-md-5"> <dl class="col-md-9">
<dt translate>Name</dt> <dt translate>Command</dt>
<dd>{$ item.name $}</dd> <dd>{$ item.command $}</dd>
</dl>
<dl class="col-md-3">
<dt translate>Memory</dt>
<dd>{$ item.memory $}</dd>
</dl> </dl>
</div> </div>
</div> </div>

View File

@ -6,6 +6,24 @@
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt translate>Name</dt> <dt translate>Name</dt>
<dd>{$ ctrl.container.name|noName $}</dd> <dd>{$ ctrl.container.name|noName $}</dd>
<dt translate>Status</dt>
<dd>{$ ctrl.container.status $}</dd>
<dt translate>Image</dt>
<dd>{$ ctrl.container.image $}</dd>
<dt translate>Command</dt>
<dd>{$ ctrl.container.command $}</dd>
<dt translate>Memory</dt>
<dd>{$ ctrl.container.memory $}</dd>
</dl>
</div>
<div class="col-md-6 detail">
<h3 translate>Environment</h3>
<hr>
<dl class="dl-horizontal">
<div ng-repeat="(key, value) in ctrl.container.environment">
<dt>{$ key $}</dt>
<dd>{$ value $}</dd>
</div>
</dl> </dl>
</div> </div>
</div> </div>