Remove CLI testing once and for all

The cli tests have been marked for removal for ~6 months and the
framework was the first thing included in tempest lib. There has
been more than enough time for all the projects to pick this up
in the client repos. So let's remove it all!

As part of this a couple of missing entries for tempest's
requirements.txt were found. These dependencies were being installed
by the clients so the fact they were missing was never noticed prior
to this. This commit also adds these missing entries back into the
requirements file.

Change-Id: I4f8638f1c048bbdb598dd181f4af272ef9923806
This commit is contained in:
Matthew Treinish 2015-04-29 12:23:01 -04:00
parent 7af67608af
commit 464d287f5b
No known key found for this signature in database
GPG Key ID: FD12A0F214C9E177
22 changed files with 6 additions and 786 deletions

View File

@ -18,7 +18,7 @@ Tempest Design Principles that we strive to live by.
incorrect assessment of their cloud. Explicit is always better. incorrect assessment of their cloud. Explicit is always better.
- Tempest uses OpenStack public interfaces. Tests in Tempest should - Tempest uses OpenStack public interfaces. Tests in Tempest should
only touch public interfaces, API calls (native or 3rd party), only touch public interfaces, API calls (native or 3rd party),
public CLI or libraries. or libraries.
- Tempest should not touch private or implementation specific - Tempest should not touch private or implementation specific
interfaces. This means not directly going to the database, not interfaces. This means not directly going to the database, not
directly hitting the hypervisors, not testing extensions not directly hitting the hypervisors, not testing extensions not

View File

@ -1 +0,0 @@
../../../tempest/cli/README.rst

View File

@ -201,27 +201,6 @@
#build_interval = 1 #build_interval = 1
[cli]
#
# From tempest.config
#
# enable cli tests (boolean value)
#enabled = true
# directory where python client binaries are located (string value)
#cli_dir = /usr/local/bin
# Whether the tempest run location has access to the *-manage
# commands. In a pure blackbox environment it will not. (boolean
# value)
#has_manage = true
# Number of seconds to wait on a CLI timeout (integer value)
#timeout = 15
[compute] [compute]
# #

View File

@ -9,10 +9,8 @@ testtools>=0.9.36,!=1.2.0
boto>=2.32.1 boto>=2.32.1
paramiko>=1.13.0 paramiko>=1.13.0
netaddr>=0.7.12 netaddr>=0.7.12
python-glanceclient>=0.15.0
python-cinderclient>=1.1.0
python-heatclient>=0.3.0
testrepository>=0.0.18 testrepository>=0.0.18
pyOpenSSL>=0.11
oslo.concurrency>=1.8.0,<1.9.0 # Apache-2.0 oslo.concurrency>=1.8.0,<1.9.0 # Apache-2.0
oslo.config>=1.9.3,<1.10.0 # Apache-2.0 oslo.config>=1.9.3,<1.10.0 # Apache-2.0
oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0 oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0
@ -24,3 +22,4 @@ iso8601>=0.1.9
fixtures>=0.3.14 fixtures>=0.3.14
testscenarios>=0.4 testscenarios>=0.4
tempest-lib>=0.5.0 tempest-lib>=0.5.0
PyYAML>=3.1.0

View File

@ -14,7 +14,6 @@ to make this clear.
| tempest/ | tempest/
| api/ - API tests | api/ - API tests
| cli/ - CLI tests
| scenario/ - complex scenario tests | scenario/ - complex scenario tests
| stress/ - stress tests | stress/ - stress tests
| thirdparty/ - 3rd party api tests | thirdparty/ - 3rd party api tests
@ -38,16 +37,6 @@ projects themselves, possibly as functional tests in their unit test
frameworks. frameworks.
:ref:`cli_field_guide`
----------------------
CLI tests use the openstack CLI to interact with the OpenStack
cloud. CLI testing in unit tests is somewhat difficult because unlike
server testing, there is no access to server code to
instantiate. Tempest seems like a logical place for this, as it
prereqs having a running OpenStack cloud.
:ref:`scenario_field_guide` :ref:`scenario_field_guide`
--------------------------- ---------------------------

View File

@ -1,50 +0,0 @@
.. _cli_field_guide:
Tempest Field Guide to CLI tests
================================
What are these tests?
---------------------
The cli tests test the various OpenStack command line interface tools
to ensure that they minimally function. The current scope is read only
operations on a cloud that are hard to test via unit tests.
Why are these tests in tempest?
-------------------------------
These tests exist here because it is extremely difficult to build a
functional enough environment in the python-\*client unit tests to
provide this kind of testing. Because we already put up a cloud in the
gate with devstack + tempest it was decided it was better to have
these as a side tree in tempest instead of another QA effort which
would split review time.
Scope of these tests
--------------------
This should stay limited to the scope of testing the cli. Functional
testing of the cloud should be elsewhere, this is about exercising the
cli code.
Example of a good test
----------------------
Tests should be isolated to a single command in one of the python
clients.
Tests should not modify the cloud.
If a test is validating the cli for bad data, it should do it with
assertRaises.
A reasonable example of an existing test is as follows::
def test_admin_list(self):
self.nova('list')
self.nova('list', params='--all-tenants 1')
self.nova('list', params='--all-tenants 0')
self.assertRaises(subprocess.CalledProcessError,
self.nova,
'list',
params='--all-tenants bad')

View File

@ -1,126 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# 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 functools
from tempest_lib.cli import base
from tempest_lib.cli import output_parser
import testtools
from tempest.common import credentials
from tempest import config
from tempest import exceptions
from tempest.openstack.common import versionutils
from tempest import test
CONF = config.CONF
def check_client_version(client, version):
"""Checks if the client's version is compatible with the given version
@param client: The client to check.
@param version: The version to compare against.
@return: True if the client version is compatible with the given version
parameter, False otherwise.
"""
current_version = base.execute(client, '', params='--version',
merge_stderr=True, cli_dir=CONF.cli.cli_dir)
if not current_version.strip():
raise exceptions.TempestException('"%s --version" output was empty' %
client)
return versionutils.is_compatible(version, current_version,
same_major=False)
def min_client_version(*args, **kwargs):
"""A decorator to skip tests if the client used isn't of the right version.
@param client: The client command to run. For python-novaclient, this is
'nova', for python-cinderclient this is 'cinder', etc.
@param version: The minimum version required to run the CLI test.
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*func_args, **func_kwargs):
if not check_client_version(kwargs['client'], kwargs['version']):
msg = "requires %s client version >= %s" % (kwargs['client'],
kwargs['version'])
raise testtools.TestCase.skipException(msg)
return func(*func_args, **func_kwargs)
return wrapper
return decorator
class ClientTestBase(test.BaseTestCase):
@classmethod
def skip_checks(cls):
super(ClientTestBase, cls).skip_checks()
if not CONF.identity_feature_enabled.api_v2:
raise cls.skipException("CLI clients rely on identity v2 API, "
"which is configured as not available")
@classmethod
def resource_setup(cls):
if not CONF.cli.enabled:
msg = "cli testing disabled"
raise cls.skipException(msg)
super(ClientTestBase, cls).resource_setup()
cls.isolated_creds = credentials.get_isolated_credentials(cls.__name__)
cls.creds = cls.isolated_creds.get_admin_creds()
def _get_clients(self):
clients = base.CLIClient(self.creds.username,
self.creds.password,
self.creds.tenant_name,
CONF.identity.uri, CONF.cli.cli_dir)
return clients
# TODO(mtreinish): The following code is basically copied from tempest-lib.
# The base cli test class in tempest-lib 0.0.1 doesn't work as a mixin like
# is needed here. The code below should be removed when tempest-lib
# provides a way to provide this functionality
def setUp(self):
super(ClientTestBase, self).setUp()
self.clients = self._get_clients()
self.parser = output_parser
def assertTableStruct(self, items, field_names):
"""Verify that all items has keys listed in field_names.
:param items: items to assert are field names in the output table
:type items: list
:param field_names: field names from the output table of the cmd
:type field_names: list
"""
for item in items:
for field in field_names:
self.assertIn(field, item)
def assertFirstLineStartsWith(self, lines, beginning):
"""Verify that the first line starts with a string
:param lines: strings for each line of output
:type lines: list
:param beginning: verify this is at the beginning of the first line
:type beginning: string
"""
self.assertTrue(lines[0].startswith(beginning),
msg=('Beginning of first line has invalid content: %s'
% lines[:3]))

View File

@ -1 +0,0 @@
This directory consists of simple read only python client tests.

View File

@ -1,18 +0,0 @@
HeatTemplateFormatVersion: '2012-12-12'
Description: Minimal template to test validation
Parameters:
InstanceImage:
Description: Glance image name
Type: String
InstanceType:
Description: Nova instance type
Type: String
Default: m1.small
AllowedValues: [m1.tiny, m1.small, m1.medium, m1.large, m1.nano, m1.xlarge, m1.micro]
ConstraintDescription: must be a valid nova instance type.
Resources:
InstanceResource:
Type: OS::Nova::Server
Properties:
flavor: {Ref: InstanceType}
image: {Ref: InstanceImage}

View File

@ -1,19 +0,0 @@
heat_template_version: 2013-05-23
description: A minimal HOT test template
parameters:
instance_image:
description: Glance image name
type: string
instance_type:
description: Nova instance type
type: string
default: m1.small
constraints:
- allowed_values: [m1.small, m1.medium, m1.large]
description: instance_type must be one of m1.small, m1.medium or m1.large
resources:
instance:
type: OS::Nova::Server
properties:
image: { get_param: instance_image }
flavor: { get_param: instance_type }

View File

@ -1,106 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# 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 re
from oslo_log import log as logging
from tempest_lib import exceptions
from tempest import cli
from tempest import config
from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
class SimpleReadOnlyGlanceClientTest(cli.ClientTestBase):
"""Basic, read-only tests for Glance CLI client.
Checks return values and output of read-only commands.
These tests do not presume any content, nor do they create
their own. They only verify the structure of output if present.
"""
@classmethod
def resource_setup(cls):
if not CONF.service_available.glance:
msg = ("%s skipped as Glance is not available" % cls.__name__)
raise cls.skipException(msg)
super(SimpleReadOnlyGlanceClientTest, cls).resource_setup()
def glance(self, *args, **kwargs):
return self.clients.glance(*args,
endpoint_type=CONF.image.endpoint_type,
**kwargs)
@test.idempotent_id('c6bd9bf9-717f-4458-8d74-05b682ea7adf')
def test_glance_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
self.glance,
'this-does-not-exist')
@test.idempotent_id('72bcdaf3-11cd-48cb-bb8e-62b329acc1ef')
def test_glance_image_list(self):
out = self.glance('image-list')
endpoints = self.parser.listing(out)
self.assertTableStruct(endpoints, [
'ID', 'Name', 'Disk Format', 'Container Format',
'Size', 'Status'])
@test.idempotent_id('965d294c-8772-4899-ba33-26ee23406135')
def test_glance_member_list(self):
tenant_name = '--tenant-id %s' % CONF.identity.admin_tenant_name
out = self.glance('member-list',
params=tenant_name)
endpoints = self.parser.listing(out)
self.assertTableStruct(endpoints,
['Image ID', 'Member ID', 'Can Share'])
@test.idempotent_id('43b80ee5-4297-47f3-ab4c-6f81b9c6edb3')
def test_glance_help(self):
help_text = self.glance('help')
lines = help_text.split('\n')
self.assertFirstLineStartsWith(lines, 'usage: glance')
commands = []
cmds_start = lines.index('Positional arguments:')
cmds_end = lines.index('Optional arguments:')
command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
for line in lines[cmds_start:cmds_end]:
match = command_pattern.match(line)
if match:
commands.append(match.group(1))
commands = set(commands)
wanted_commands = set(('image-create', 'image-delete', 'help',
'image-download', 'image-show', 'image-update',
'member-create', 'member-delete',
'member-list', 'image-list'))
self.assertFalse(wanted_commands - commands)
# Optional arguments:
@test.idempotent_id('3b2359ea-3719-4b47-81e5-44a042572b11')
def test_glance_version(self):
self.glance('', flags='--version')
@test.idempotent_id('1a52d3bd-3edf-4d67-b3da-999a5d9e0c5e')
def test_glance_debug_list(self):
self.glance('image-list', flags='--debug')
@test.idempotent_id('6f42b076-f9a7-4e2b-a729-579f53e7814e')
def test_glance_timeout(self):
self.glance('image-list', flags='--timeout %d' % CONF.cli.timeout)

View File

@ -1,117 +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.
import json
import os
from oslo_log import log as logging
import yaml
import tempest.cli
from tempest import config
from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
class SimpleReadOnlyHeatClientTest(tempest.cli.ClientTestBase):
"""Basic, read-only tests for Heat CLI client.
Basic smoke test for the heat CLI commands which do not require
creating or modifying stacks.
"""
@classmethod
def resource_setup(cls):
if (not CONF.service_available.heat):
msg = ("Skipping all Heat cli tests because it is "
"not available")
raise cls.skipException(msg)
super(SimpleReadOnlyHeatClientTest, cls).resource_setup()
cls.heat_template_path = os.path.join(os.path.dirname(
os.path.dirname(os.path.realpath(__file__))),
'heat_templates/heat_minimal.yaml')
def heat(self, *args, **kwargs):
return self.clients.heat(
*args, endpoint_type=CONF.orchestration.endpoint_type, **kwargs)
@test.idempotent_id('0ae034bb-ce35-45e8-b7aa-3e339cd3140f')
def test_heat_stack_list(self):
self.heat('stack-list')
@test.idempotent_id('a360d069-7250-4aed-9721-0a6f2db7c3fa')
def test_heat_stack_list_debug(self):
self.heat('stack-list', flags='--debug')
@test.idempotent_id('e1b7c177-5ab4-4d3f-8a26-ea01ebbd2b8c')
def test_heat_resource_template_fmt_default(self):
ret = self.heat('resource-template OS::Nova::Server')
self.assertIn('Type: OS::Nova::Server', ret)
@test.idempotent_id('93f82f76-aab2-4910-9359-11cf48f2a46b')
def test_heat_resource_template_fmt_arg_short_yaml(self):
ret = self.heat('resource-template -F yaml OS::Nova::Server')
self.assertIn('Type: OS::Nova::Server', ret)
self.assertIsInstance(yaml.safe_load(ret), dict)
@test.idempotent_id('7356a98c-e14d-43f0-8c25-c9f7daa0aafa')
def test_heat_resource_template_fmt_arg_long_json(self):
ret = self.heat('resource-template --format json OS::Nova::Server')
self.assertIn('"Type": "OS::Nova::Server"', ret)
self.assertIsInstance(json.loads(ret), dict)
@test.idempotent_id('2fd99d20-beff-4667-b42e-de9095f671d7')
def test_heat_resource_type_list(self):
ret = self.heat('resource-type-list')
rsrc_types = self.parser.listing(ret)
self.assertTableStruct(rsrc_types, ['resource_type'])
@test.idempotent_id('62f60dbf-d139-4698-b230-a09fb531d643')
def test_heat_resource_type_show(self):
rsrc_schema = self.heat('resource-type-show OS::Nova::Server')
# resource-type-show returns a json resource schema
self.assertIsInstance(json.loads(rsrc_schema), dict)
@test.idempotent_id('6ca16ff7-9d5f-4448-a8c2-4cecc7b5ba3a')
def test_heat_template_validate_yaml(self):
ret = self.heat('template-validate -f %s' % self.heat_template_path)
# On success template-validate returns a json representation
# of the template parameters
self.assertIsInstance(json.loads(ret), dict)
@test.idempotent_id('35241014-16ea-4cb6-ad3e-4ee5f41446de')
def test_heat_template_validate_hot(self):
ret = self.heat('template-validate -f %s' % self.heat_template_path)
self.assertIsInstance(json.loads(ret), dict)
@test.idempotent_id('34d43e0a-36dc-4ea8-9b85-0189e3de89d8')
def test_heat_help(self):
self.heat('help')
@tempest.cli.min_client_version(client='heat', version='0.2.7')
@test.idempotent_id('c122c08b-839d-49d1-afd1-bc546b2d18d3')
def test_heat_bash_completion(self):
self.heat('bash-completion')
@test.idempotent_id('1b045e12-2fa0-4895-9282-00668428dfbe')
def test_heat_help_cmd(self):
# Check requesting help for a specific command works
help_text = self.heat('help resource-template')
lines = help_text.split('\n')
self.assertFirstLineStartsWith(lines, 'usage: heat resource-template')
@test.idempotent_id('c7837f8f-d0a8-47fd-b75b-14ba3e3fa9a2')
def test_heat_version(self):
self.heat('', flags='--version')

View File

@ -1,220 +0,0 @@
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# 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 logging
import re
from tempest_lib import exceptions
import testtools
from tempest import cli
from tempest import clients
from tempest import config
from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
class SimpleReadOnlyCinderClientTest(cli.ClientTestBase):
"""Basic, read-only tests for Cinder CLI client.
Checks return values and output of read-only commands.
These tests do not presume any content, nor do they create
their own. They only verify the structure of output if present.
"""
@classmethod
def resource_setup(cls):
if not CONF.service_available.cinder:
msg = ("%s skipped as Cinder is not available" % cls.__name__)
raise cls.skipException(msg)
super(SimpleReadOnlyCinderClientTest, cls).resource_setup()
id_cl = clients.AdminManager().identity_client
tenant = id_cl.get_tenant_by_name(CONF.identity.admin_tenant_name)
cls.admin_tenant_id = tenant['id']
def cinder(self, *args, **kwargs):
return self.clients.cinder(*args,
endpoint_type=CONF.volume.endpoint_type,
**kwargs)
@test.idempotent_id('229bc6dc-d804-4668-b753-b590caf63061')
def test_cinder_fake_action(self):
self.assertRaises(exceptions.CommandFailed,
self.cinder,
'this-does-not-exist')
@test.idempotent_id('77140216-14db-4fc5-a246-e2a587e9e99b')
def test_cinder_absolute_limit_list(self):
roles = self.parser.listing(self.cinder('absolute-limits'))
self.assertTableStruct(roles, ['Name', 'Value'])
@test.idempotent_id('2206b9ce-1a36-4a0a-a129-e5afc7cee1dd')
def test_cinder_backup_list(self):
backup_list = self.parser.listing(self.cinder('backup-list'))
self.assertTableStruct(backup_list, ['ID', 'Volume ID', 'Status',
'Name', 'Size', 'Object Count',
'Container'])
@test.idempotent_id('c7f50346-cd99-4e0b-953f-796ff5f47295')
def test_cinder_extra_specs_list(self):
extra_specs_list = self.parser.listing(self.cinder('extra-specs-list'))
self.assertTableStruct(extra_specs_list, ['ID', 'Name', 'extra_specs'])
@test.idempotent_id('9de694cb-b40b-442c-a30c-5f9873e144f7')
def test_cinder_volumes_list(self):
list = self.parser.listing(self.cinder('list'))
self.assertTableStruct(list, ['ID', 'Status', 'Name', 'Size',
'Volume Type', 'Bootable',
'Attached to'])
self.cinder('list', params='--all-tenants 1')
self.cinder('list', params='--all-tenants 0')
self.assertRaises(exceptions.CommandFailed,
self.cinder,
'list',
params='--all-tenants bad')
@test.idempotent_id('56f7c15c-ee82-4f23-bbe8-ce99b66da493')
def test_cinder_quota_class_show(self):
"""This CLI can accept and string as param."""
roles = self.parser.listing(self.cinder('quota-class-show',
params='abc'))
self.assertTableStruct(roles, ['Property', 'Value'])
@test.idempotent_id('a919a811-b7f0-47a7-b4e5-f3eb674dd200')
def test_cinder_quota_defaults(self):
"""This CLI can accept and string as param."""
roles = self.parser.listing(self.cinder('quota-defaults',
params=self.admin_tenant_id))
self.assertTableStruct(roles, ['Property', 'Value'])
@test.idempotent_id('18166673-ffa8-4df3-b60c-6375532288bc')
def test_cinder_quota_show(self):
"""This CLI can accept and string as param."""
roles = self.parser.listing(self.cinder('quota-show',
params=self.admin_tenant_id))
self.assertTableStruct(roles, ['Property', 'Value'])
@test.idempotent_id('b2c66ed9-ca96-4dc4-94cc-8083e664e516')
def test_cinder_rate_limits(self):
rate_limits = self.parser.listing(self.cinder('rate-limits'))
self.assertTableStruct(rate_limits, ['Verb', 'URI', 'Value', 'Remain',
'Unit', 'Next_Available'])
@test.idempotent_id('7a19955b-807c-481a-a2ee-9d76733eac28')
@testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
'Volume snapshot not available.')
def test_cinder_snapshot_list(self):
snapshot_list = self.parser.listing(self.cinder('snapshot-list'))
self.assertTableStruct(snapshot_list, ['ID', 'Volume ID', 'Status',
'Name', 'Size'])
@test.idempotent_id('6e54ecd9-7ba9-490d-8e3b-294b67139e73')
def test_cinder_type_list(self):
type_list = self.parser.listing(self.cinder('type-list'))
self.assertTableStruct(type_list, ['ID', 'Name'])
@test.idempotent_id('2c363583-24a0-4980-b9cb-b50c0d241e82')
def test_cinder_list_extensions(self):
roles = self.parser.listing(self.cinder('list-extensions'))
self.assertTableStruct(roles, ['Name', 'Summary', 'Alias', 'Updated'])
@test.idempotent_id('691bd6df-30ad-4be7-927b-a02d62aaa38a')
def test_cinder_credentials(self):
credentials = self.parser.listing(self.cinder('credentials'))
self.assertTableStruct(credentials, ['User Credentials', 'Value'])
@test.idempotent_id('5c6d71a3-4904-4a3a-aec9-7fd4aa830e95')
def test_cinder_availability_zone_list(self):
zone_list = self.parser.listing(self.cinder('availability-zone-list'))
self.assertTableStruct(zone_list, ['Name', 'Status'])
@test.idempotent_id('9b0fd5a6-f955-42b9-a42f-6f542a80b9a3')
def test_cinder_endpoints(self):
out = self.cinder('endpoints')
tables = self.parser.tables(out)
for table in tables:
headers = table['headers']
self.assertTrue(2 >= len(headers))
self.assertEqual('Value', headers[1])
@test.idempotent_id('301b5ae1-9591-4e9f-999c-d525a9bdf822')
def test_cinder_service_list(self):
service_list = self.parser.listing(self.cinder('service-list'))
self.assertTableStruct(service_list, ['Binary', 'Host', 'Zone',
'Status', 'State', 'Updated_at'])
@test.idempotent_id('7260ae52-b462-461e-9048-36d0bccf92c6')
def test_cinder_transfer_list(self):
transfer_list = self.parser.listing(self.cinder('transfer-list'))
self.assertTableStruct(transfer_list, ['ID', 'Volume ID', 'Name'])
@test.idempotent_id('0976dea8-14f3-45a9-8495-3617fc4fbb13')
def test_cinder_bash_completion(self):
self.cinder('bash-completion')
@test.idempotent_id('b7c00361-be80-4512-8735-5f98fc54f2a9')
def test_cinder_qos_list(self):
qos_list = self.parser.listing(self.cinder('qos-list'))
self.assertTableStruct(qos_list, ['ID', 'Name', 'Consumer', 'specs'])
@test.idempotent_id('2e92dc6e-22b5-4d94-abfc-b543b0c50a89')
def test_cinder_encryption_type_list(self):
encrypt_list = self.parser.listing(self.cinder('encryption-type-list'))
self.assertTableStruct(encrypt_list, ['Volume Type ID', 'Provider',
'Cipher', 'Key Size',
'Control Location'])
@test.idempotent_id('0ee6cb4c-8de6-4811-a7be-7f4bb75b80cc')
def test_admin_help(self):
help_text = self.cinder('help')
lines = help_text.split('\n')
self.assertFirstLineStartsWith(lines, 'usage: cinder')
commands = []
cmds_start = lines.index('Positional arguments:')
cmds_end = lines.index('Optional arguments:')
command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
for line in lines[cmds_start:cmds_end]:
match = command_pattern.match(line)
if match:
commands.append(match.group(1))
commands = set(commands)
wanted_commands = set(('absolute-limits', 'list', 'help',
'quota-show', 'type-list', 'snapshot-list'))
self.assertFalse(wanted_commands - commands)
# Optional arguments:
@test.idempotent_id('2fd6f530-183c-4bda-8918-1e59e36c26b9')
def test_cinder_version(self):
self.cinder('', flags='--version')
@test.idempotent_id('306bac51-c443-4426-a6cf-583a953fcd68')
def test_cinder_debug_list(self):
self.cinder('list', flags='--debug')
@test.idempotent_id('6d97fcd2-5dd1-429d-af70-030c949d86cd')
def test_cinder_retries_list(self):
self.cinder('list', flags='--retries 3')
@test.idempotent_id('95a2850c-35b4-4159-bb93-51647a5ad232')
def test_cinder_region_list(self):
region = CONF.volume.region
if not region:
region = CONF.identity.region
self.cinder('list', flags='--os-region-name ' + region)

View File

@ -1091,25 +1091,6 @@ BaremetalGroup = [
help="Timeout for unprovisioning an Ironic node.") help="Timeout for unprovisioning an Ironic node.")
] ]
cli_group = cfg.OptGroup(name='cli', title="cli Configuration Options")
CLIGroup = [
cfg.BoolOpt('enabled',
default=True,
help="enable cli tests"),
cfg.StrOpt('cli_dir',
default='/usr/local/bin',
help="directory where python client binaries are located"),
cfg.BoolOpt('has_manage',
default=True,
help=("Whether the tempest run location has access to the "
"*-manage commands. In a pure blackbox environment "
"it will not.")),
cfg.IntOpt('timeout',
default=15,
help="Number of seconds to wait on a CLI timeout"),
]
negative_group = cfg.OptGroup(name='negative', title="Negative Test Options") negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
NegativeGroup = [ NegativeGroup = [
@ -1148,7 +1129,6 @@ _opts = [
(debug_group, DebugGroup), (debug_group, DebugGroup),
(baremetal_group, BaremetalGroup), (baremetal_group, BaremetalGroup),
(input_scenario_group, InputScenarioGroup), (input_scenario_group, InputScenarioGroup),
(cli_group, CLIGroup),
(negative_group, NegativeGroup) (negative_group, NegativeGroup)
] ]
@ -1212,7 +1192,6 @@ class TempestConfigPrivate(object):
self.debug = _CONF.debug self.debug = _CONF.debug
self.baremetal = _CONF.baremetal self.baremetal = _CONF.baremetal
self.input_scenario = _CONF['input-scenario'] self.input_scenario = _CONF['input-scenario']
self.cli = _CONF.cli
self.negative = _CONF.negative self.negative = _CONF.negative
_CONF.set_default('domain_name', self.identity.admin_domain_name, _CONF.set_default('domain_name', self.identity.admin_domain_name,
group='identity') group='identity')

View File

@ -25,7 +25,7 @@ def load_tests(loader, tests, pattern):
suite = unittest.TestSuite() suite = unittest.TestSuite()
base_path = os.path.split(os.path.dirname(os.path.abspath(__file__)))[0] base_path = os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]
base_path = os.path.split(base_path)[0] base_path = os.path.split(base_path)[0]
for test_dir in ['./tempest/api', './tempest/cli', './tempest/scenario', for test_dir in ['./tempest/api', './tempest/scenario',
'./tempest/thirdparty']: './tempest/thirdparty']:
if not pattern: if not pattern:
suite.addTests(loader.discover(test_dir, top_level_dir=base_path)) suite.addTests(loader.discover(test_dir, top_level_dir=base_path))

View File

@ -1,68 +0,0 @@
# Copyright 2014 IBM Corp.
#
# 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 mock
from tempest_lib.cli import base as cli_base
import testtools
from tempest import cli
from tempest import config
from tempest import exceptions
from tempest.tests import base
from tempest.tests import fake_config
class TestMinClientVersion(base.TestCase):
"""Tests for the min_client_version decorator.
"""
def setUp(self):
super(TestMinClientVersion, self).setUp()
self.useFixture(fake_config.ConfigFixture())
self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
def _test_min_version(self, required, installed, expect_skip):
@cli.min_client_version(client='nova', version=required)
def fake(self, expect_skip):
if expect_skip:
# If we got here, the decorator didn't raise a skipException as
# expected so we need to fail.
self.fail('Should not have gotten past the decorator.')
with mock.patch.object(cli_base, 'execute',
return_value=installed) as mock_cmd:
if expect_skip:
self.assertRaises(testtools.TestCase.skipException, fake,
self, expect_skip)
else:
fake(self, expect_skip)
mock_cmd.assert_called_once_with('nova', '', params='--version',
cli_dir='/usr/local/bin',
merge_stderr=True)
def test_min_client_version(self):
# required, installed, expect_skip
cases = (('2.17.0', '2.17.0', False),
('2.17.0', '2.18.0', False),
('2.18.0', '2.17.0', True))
for case in cases:
self._test_min_version(*case)
@mock.patch.object(cli_base, 'execute', return_value=' ')
def test_check_client_version_empty_output(self, mock_execute):
# Tests that an exception is raised if the command output is empty.
self.assertRaises(exceptions.TempestException,
cli.check_client_version, 'nova', '2.18.0')

View File

@ -47,7 +47,7 @@ deps = {[tempestenv]deps}
# See the testrepostiory bug: https://bugs.launchpad.net/testrepository/+bug/1208610 # See the testrepostiory bug: https://bugs.launchpad.net/testrepository/+bug/1208610
commands = commands =
find . -type f -name "*.pyc" -delete find . -type f -name "*.pyc" -delete
bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}' bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
[testenv:full-serial] [testenv:full-serial]
sitepackages = {[tempestenv]sitepackages} sitepackages = {[tempestenv]sitepackages}
@ -57,7 +57,7 @@ deps = {[tempestenv]deps}
# See the testrepostiory bug: https://bugs.launchpad.net/testrepository/+bug/1208610 # See the testrepostiory bug: https://bugs.launchpad.net/testrepository/+bug/1208610
commands = commands =
find . -type f -name "*.pyc" -delete find . -type f -name "*.pyc" -delete
bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}' bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
[testenv:heat-slow] [testenv:heat-slow]
sitepackages = {[tempestenv]sitepackages} sitepackages = {[tempestenv]sitepackages}