Add tempest case to test container operaton

Change-Id: I6ceb5aeb7babd8119424ad4b792aa4e39a5c23a2
This commit is contained in:
Your Name 2016-08-31 03:06:57 +00:00 committed by Eli Qiao
parent 902fa22f67
commit bb369db11e
10 changed files with 377 additions and 27 deletions

View File

@ -4,14 +4,66 @@ Tempest Plugin
This directory contains Tempest tests to cover Zun project.
Tempest installation
--------------------
To install Tempest you can issue the following commands::
$ git clone https://github.com/openstack/tempest/
$ cd tempest/
$ pip install .
The folder you are into now will be called ``<TEMPEST_DIR>`` from now onwards.
Please note that although it is fully working outside a virtual environment, it
is recommended to install within a `venv`.
Zun Tempest testing sutup
-------------------------
Before using zun tempest plugin, you need to install zun first::
$ pip install -e <ZUN_SRC_DIR>
To list all Zun tempest cases, go to tempest directory, then run::
$ testr list-tests zun
Need to adopt tempest.conf, an example as follows::
$ cat /etc/tempest/tempest.conf
[auth]
use_dynamic_credentials=True
admin_username=admin
admin_password=123
admin_project_name=admin
[identity]
disable_ssl_certificate_validation=True
uri=http://127.0.0.1:5000/v2.0/
auth_version=v2
region=RegionOne
[identity-feature-enabled]
api_v2 = true
api_v3 = false
trust = false
[oslo_concurrency]
lock_path = /tmp/
[container_management]
catalog_type = container
[debug]
trace_requests=true
To run only these tests in tempest, go to tempest directory, then run::
$ ./run_tempest.sh -N -- zun
To run a single test case, go to tempest directory, then run with test case name, e.g.::
$ ./run_tempest.sh -- -N zun.tests.tempest.api.test_basic.TestBasic.test_basic
$ ./run_tempest.sh -- -N zun.tests.tempest.api.test_containers.TestContainer.test_create_list_delete

View File

@ -0,0 +1,94 @@
# 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 six.moves.urllib import parse
from tempest import config
from tempest.lib.common import rest_client
from tempest.lib.services.compute import keypairs_client
from tempest import manager
from zun.tests.tempest.api.models import container_model
CONF = config.CONF
class Manager(manager.Manager):
def __init__(self, credentials=None, service=None):
super(Manager, self).__init__(credentials=credentials)
params = {'service': CONF.container_management.catalog_type,
'region': CONF.identity.region}
self.keypairs_client = keypairs_client.KeyPairsClient(
self.auth_provider, **params)
self.container_client = ZunClient(self.auth_provider)
class ZunClient(rest_client.RestClient):
def __init__(self, auth_provider):
super(ZunClient, self).__init__(
auth_provider=auth_provider,
service=CONF.container_management.catalog_type,
region=CONF.identity.region,
disable_ssl_certificate_validation=True
)
@classmethod
def deserialize(cls, resp, body, model_type):
return resp, model_type.from_json(body)
@classmethod
def add_filters(cls, url, filters):
"""add_filters adds dict values (filters) to url as query parameters
:param url: base URL for the request
:param filters: dict with var:val pairs to add as parameters to URL
:returns: url string
"""
return url + "?" + parse(filters)
@classmethod
def containers_uri(cls, filters=None):
url = "/containers/"
if filters:
url = cls.add_filters(url, filters)
return url
@classmethod
def container_uri(cls, container_id):
"""Construct container uri
"""
return "{0}/{1}".format(cls.containers_uri(), container_id)
def post_container(self, model, **kwargs):
"""Makes POST /container request
"""
resp, body = self.post(
self.containers_uri(),
body=model.to_json(), **kwargs)
return self.deserialize(resp, body, container_model.ContainerEntity)
def get_container(self, container_id):
resp, body = self.get(self.container_uri(container_id))
return self.deserialize(resp, body, container_model.ContainerEntity)
def list_containers(self, filters=None, **kwargs):
resp, body = self.get(self.containers_uri(filters), **kwargs)
return self.deserialize(resp, body,
container_model.ContainerCollection)
def delete_container(self, container_id, **kwargs):
self.delete(self.container_uri(container_id), **kwargs)

View File

@ -0,0 +1,70 @@
# 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.
import json
class BaseModel(object):
"""Superclass responsible for converting json data to/from model"""
@classmethod
def from_json(cls, json_str):
return cls.from_dict(json.loads(json_str))
def to_json(self):
return json.dumps(self.to_dict())
@classmethod
def from_dict(cls, data):
model = cls()
for key in data:
setattr(model, key, data.get(key))
return model
def to_dict(self):
result = {}
for key in self.__dict__:
result[key] = getattr(self, key)
if isinstance(result[key], BaseModel):
result[key] = result[key].to_dict()
return result
def __str__(self):
return "%s" % self.to_dict()
class EntityModel(BaseModel):
"""Superclass resposible from converting dict to instance of model"""
@classmethod
def from_dict(cls, data):
model = super(EntityModel, cls).from_dict(data)
if hasattr(model, cls.ENTITY_NAME):
val = getattr(model, cls.ENTITY_NAME)
setattr(model, cls.ENTITY_NAME, cls.MODEL_TYPE.from_dict(val))
return model
class CollectionModel(BaseModel):
"""Superclass resposible from converting dict to list of models"""
@classmethod
def from_dict(cls, data):
model = super(CollectionModel, cls).from_dict(data)
collection = []
if hasattr(model, cls.COLLECTION_NAME):
for d in getattr(model, cls.COLLECTION_NAME):
collection.append(cls.MODEL_TYPE.from_dict(d))
setattr(model, cls.COLLECTION_NAME, collection)
return model

View File

@ -0,0 +1,62 @@
# 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.
import random
import socket
import string
import struct
from tempest.lib.common.utils import data_utils
from zun.tests.tempest.api.models import container_model
def random_int(min_int=1, max_int=100):
return random.randrange(min_int, max_int)
def gen_random_port():
return random_int(49152, 65535)
def gen_docker_volume_size(min_int=3, max_int=5):
return random_int(min_int, max_int)
def gen_fake_ssh_pubkey():
chars = "".join(
random.choice(string.ascii_uppercase +
string.ascii_letters + string.digits + '/+=')
for _ in range(372))
return "ssh-rsa " + chars
def gen_random_ip():
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
def gen_url(scheme="http", domain="example.com", port=80):
return "%s://%s:%s" % (scheme, domain, port)
def contaienr_data(**kwargs):
data = {
'name': data_utils.rand_name('container'),
'image': 'cirros:latest',
'command': 'sleep 10000',
'memory': '100m',
'environment': {}
}
data.update(kwargs)
model = container_model.ContainerEntity.from_dict(data)
return model

View File

@ -0,0 +1,30 @@
# 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 zun.tests.tempest.api.common import base_model
class ContainerData(base_model.BaseModel):
"""Data that encapsulates container attributes"""
pass
class ContainerEntity(base_model.EntityModel):
"""Entity Model that represents a single instance of ContainerData"""
ENTITY_NAME = 'container'
MODEL_TYPE = ContainerData
class ContainerCollection(base_model.CollectionModel):
"""Collection Model that represents a list of ContainerData objects"""
COLLECTION_NAME = 'containerlists'
MODEL_TYPE = ContainerData

View File

@ -1,24 +0,0 @@
# 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 tempest.lib import decorators
from zun.tests.tempest import base
class TestBasic(base.BaseZunTest):
@decorators.idempotent_id('a04f61f2-15ae-4200-83b7-1f311b101f65')
def test_basic(self):
# This is a basic test used to verify zun tempest plugin
# works. Remove it after real test cases being added.
pass

View File

@ -0,0 +1,67 @@
# 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 tempest.lib import decorators
from zun.tests.tempest.api import clients
from zun.tests.tempest.api.common import datagen
from zun.tests.tempest import base
class TestContainer(base.BaseZunTest):
@classmethod
def get_client_manager(cls, credential_type=None, roles=None,
force_new=None):
manager = super(TestContainer, cls).get_client_manager(
credential_type=credential_type,
roles=roles,
force_new=force_new
)
return clients.Manager(manager.credentials)
@classmethod
def setup_clients(cls):
super(TestContainer, cls).setup_clients()
cls.container_client = cls.os.container_client
@classmethod
def resource_setup(cls):
super(TestContainer, cls).resource_setup()
def _create_container(self, **kwargs):
model = datagen.contaienr_data(**kwargs)
return self.container_client.post_container(model)
def _delete_container(self, container_id):
self.container_client.delete_container(container_id)
@decorators.idempotent_id('a04f61f2-15ae-4200-83b7-1f311b101f35')
def test_container_create_list_delete(self):
resp, container = self._create_container()
self.assertEqual(202, resp.status)
resp, model = self.container_client.list_containers()
self.assertEqual(200, resp.status)
self.assertGreater(len(model.containers), 0)
self._delete_container(container.uuid)
resp, model = self.container_client.list_containers()
self.assertEqual(200, resp.status)
self.assertEqual(len(model.containers), 0)

View File

@ -13,7 +13,6 @@
from oslo_config import cfg
service_available_group = cfg.OptGroup(name="service_available",
title="Available OpenStack Services")
@ -28,7 +27,7 @@ container_management_group = cfg.OptGroup(
ContainerManagementGroup = [
cfg.StrOpt("catalog_type",
default="container_management",
default="container",
help="Catalog type of the container management service."),
cfg.IntOpt("wait_timeout",
default=60,