From 6b5ef0dab21661b27c323851af3bb6cdb011c0aa Mon Sep 17 00:00:00 2001 From: AleptNmarata Date: Thu, 2 Feb 2017 17:54:53 +0000 Subject: [PATCH] Add OSC commands functional test This patch adds the basic files and only one test now. Consecutive patches will add more test cases. Change-Id: Iebc28e9781357c11a664c6b0d203a4c20a63a6ca --- .gitignore | 3 + requirements.txt | 1 + test-requirements.txt | 2 + tools/run_functional.sh | 29 +++ tox.ini | 10 + zunclient/tests/functional/base.py | 193 ++++++++++++++++++ .../tests/functional/hooks/post_test_hook.sh | 18 +- zunclient/tests/functional/osc/__init__.py | 0 zunclient/tests/functional/osc/v1/__init__.py | 0 zunclient/tests/functional/osc/v1/base.py | 45 ++++ .../tests/functional/osc/v1/test_container.py | 32 +++ 11 files changed, 331 insertions(+), 2 deletions(-) create mode 100755 tools/run_functional.sh create mode 100644 zunclient/tests/functional/base.py create mode 100644 zunclient/tests/functional/osc/__init__.py create mode 100644 zunclient/tests/functional/osc/v1/__init__.py create mode 100644 zunclient/tests/functional/osc/v1/base.py create mode 100644 zunclient/tests/functional/osc/v1/test_container.py diff --git a/.gitignore b/.gitignore index dfcb0007..a51cc84e 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ ChangeLog # Files created by releasenotes build releasenotes/build + +# Others +test.conf diff --git a/requirements.txt b/requirements.txt index b7d8dd35..5ce59d2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ pbr>=1.8 # Apache-2.0 PrettyTable>=0.7.1,<0.8 # BSD +python-openstackclient>=3.3.0 # Apache-2.0 osc-lib>=1.2.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 74c544e0..1b324a3d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,12 +5,14 @@ bandit>=1.0.1 # Apache-2.0 coverage>=4.0 # Apache-2.0 doc8 # Apache-2.0 +ddt>=1.0.1 # MIT hacking<0.12,>=0.11.0 # Apache-2.0 oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 os-testr>=0.8.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD sphinx!=1.3b1,<1.4,>=1.2.1 # BSD +tempest>=14.0.0 # Apache-2.0 testresources>=0.2.4 # Apache-2.0/BSD testrepository>=0.0.18 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD diff --git a/tools/run_functional.sh b/tools/run_functional.sh new file mode 100755 index 00000000..8a0c380a --- /dev/null +++ b/tools/run_functional.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +FUNC_TEST_DIR=$(dirname $0)/../zunclient/tests/functional/ +CONFIG_FILE=$FUNC_TEST_DIR/test.conf + +if [[ -n "$OS_AUTH_TOKEN" ]] && [[ -n "$ZUN_URL" ]]; then +cat <$CONFIG_FILE +[functional] +api_version = 1 +auth_strategy=noauth +os_auth_token=$OS_AUTH_TOKEN +zun_url=$ZUN_URL +END +else +cat <$CONFIG_FILE +[functional] +api_version = 1 +os_auth_url=$OS_AUTH_URL +os_identity_api_version = $OS_IDENTITY_API_VERSION +os_username=$OS_USERNAME +os_password=$OS_PASSWORD +os_project_name=$OS_PROJECT_NAME +os_user_domain_id=$OS_USER_DOMAIN_ID +os_project_domain_id=$OS_PROJECT_DOMAIN_ID +os_service_type=container +os_endpoint_type=public +END +fi +tox -e functional diff --git a/tox.ini b/tox.ini index 4e205ac2..eefe866d 100644 --- a/tox.ini +++ b/tox.ini @@ -70,3 +70,13 @@ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build [hacking] import_exceptions = zunclient._i18n + +[testenv:functional] +commands = ostestr {posargs} +setenv = + {[testenv]setenv} + OS_TEST_PATH = ./zunclient/tests/functional/osc/v1 +# The OS_CACERT environment variable should be passed to the test +# environments to specify a CA bundle file to use in verifying a +# TLS (https) server certificate. +passenv = OS_* diff --git a/zunclient/tests/functional/base.py b/zunclient/tests/functional/base.py new file mode 100644 index 00000000..ef140a92 --- /dev/null +++ b/zunclient/tests/functional/base.py @@ -0,0 +1,193 @@ +# Copyright (c) 2016 Mirantis, 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. + +import os + +import six +import six.moves.configparser as config_parser +from tempest.lib.cli import base + +DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'test.conf') + + +class FunctionalTestBase(base.ClientTestBase): + """Container base class, calls to zunclient.""" + + def setUp(self): + super(FunctionalTestBase, self).setUp() + self.client = self._get_clients() + + def _get_clients(self): + # NOTE(aarefiev): {toxinidir} is a current working directory, so + # the tox env path is {toxinidir}/.tox + cli_dir = os.path.join(os.path.abspath('.'), '.tox/functional/bin') + + config = self._get_config() + if config.get('os_auth_url'): + client = base.CLIClient(cli_dir=cli_dir, + username=config['os_username'], + password=config['os_password'], + tenant_name=config['os_project_name'], + uri=config['os_auth_url']) + for keystone_object in 'user', 'project': + domain_attr = 'os_%s_domain_id' % keystone_object + if config.get(domain_attr): + setattr(self, domain_attr, config[domain_attr]) + else: + self.zun_url = config['zun_url'] + self.os_auth_token = config['os_auth_token'] + client = base.CLIClient(cli_dir=cli_dir, + zun_url=self.zun_url, + os_auth_token=self.os_auth_token) + return client + + def _get_config(self): + config_file = os.environ.get('ZUNCLIENT_TEST_CONFIG', + DEFAULT_CONFIG_FILE) + # SafeConfigParser was deprecated in Python 3.2 + if six.PY3: + config = config_parser.ConfigParser() + else: + config = config_parser.SafeConfigParser() + if not config.read(config_file): + self.skipTest('Skipping, no test config found @ %s' % config_file) + try: + auth_strategy = config.get('functional', 'auth_strategy') + except config_parser.NoOptionError: + auth_strategy = 'keystone' + if auth_strategy not in ['keystone', 'noauth']: + raise self.fail( + 'Invalid auth type specified: %s in functional must be ' + 'one of: [keystone, noauth]' % auth_strategy) + + conf_settings = [] + keystone_v3_conf_settings = [] + if auth_strategy == 'keystone': + conf_settings += ['os_auth_url', 'os_username', + 'os_password', 'os_project_name'] + keystone_v3_conf_settings += ['os_user_domain_id', + 'os_project_domain_id'] + else: + conf_settings += ['os_auth_token', 'zun_url'] + + cli_flags = {} + missing = [] + for c in conf_settings + keystone_v3_conf_settings: + try: + cli_flags[c] = config.get('functional', c) + except config_parser.NoOptionError: + # NOTE(vdrok): Here we ignore the absence of KS v3 options as + # v2 may be used. Keystone client will do the actual check of + # the parameters' correctness. + if c not in keystone_v3_conf_settings: + missing.append(c) + if missing: + self.fail('Missing required setting in test.conf (%(conf)s) for ' + 'auth_strategy=%(auth)s: %(missing)s' % + {'conf': config_file, + 'auth': auth_strategy, + 'missing': ','.join(missing)}) + return cli_flags + + def _cmd_no_auth(self, cmd, action, flags='', params=''): + """Execute given command with noauth attributes. + + :param cmd: command to be executed + :type cmd: string + :param action: command on cli to run + :type action: string + :param flags: optional cli flags to use + :type flags: string + :param params: optional positional args to use + :type params: string + """ + flags = ('--os_auth_token %(token)s --zun_url %(url)s %(flags)s' + % + {'token': self.os_auth_token, + 'url': self.zun_url, + 'flags': flags}) + return base.execute(cmd, action, flags, params, + cli_dir=self.client.cli_dir) + + def _zun(self, action, flags='', params=''): + """Execute zun command for the given action. + + :param action: the cli command to run using Container + :type action: string + :param flags: any optional cli flags to use + :type flags: string + :param params: any optional positional args to use + :type params: string + """ + flags += ' --os-endpoint-type publicURL' + if hasattr(self, 'os_auth_token'): + return self._cmd_no_auth('appcontainer', action, flags, params) + else: + for keystone_object in 'user', 'project': + domain_attr = 'os_%s_domain_id' % keystone_object + if hasattr(self, domain_attr): + flags += ' --os-%(ks_obj)s-domain-id %(value)s' % { + 'ks_obj': keystone_object, + 'value': getattr(self, domain_attr) + } + return self.client.cmd_with_auth('zun', + action, flags, params) + + def _zun_osc(self, action, flags='', params=''): + """Execute container commands via OpenStack Client.""" + config = self._get_config() + identity_api_version = config.get('functional', + 'os_identity_api_version') + flags += ' --os-identity-api-version {0}'.format(identity_api_version) + + for keystone_object in 'user', 'project': + domain_attr = 'os_%s_domain_id' % keystone_object + if hasattr(self, domain_attr): + flags += ' --os-%(ks_obj)s-domain-id %(value)s' % { + 'ks_obj': keystone_object, + 'value': getattr(self, domain_attr) + } + return self.client.cmd_with_auth( + 'openstack', action, flags, params) + + def zun(self, action, flags='', params='', parse=True): + """Return parsed list of dicts with basic item info. + + :param action: the cli command to run using Container + :type action: string + :param flags: any optional cli flags to use + :type flags: string + :param params: any optional positional args to use + :type params: string + :param parse: return parsed list or raw output + :type parse: bool + """ + output = self._zun(action=action, flags=flags, params=params) + return self.parser.listing(output) if parse else output + + def get_table_headers(self, action, flags='', params=''): + output = self._zun(action=action, flags=flags, params=params) + table = self.parser.table(output) + return table['headers'] + + def assertTableHeaders(self, field_names, table_headers): + """Assert that field_names and table_headers are equal. + + :param field_names: field names from the output table of the cmd + :param table_headers: table headers output from cmd + """ + self.assertEqual(sorted(field_names), sorted(table_headers)) + + def list_containers(self, params=''): + return self.zun('container-list', params=params) diff --git a/zunclient/tests/functional/hooks/post_test_hook.sh b/zunclient/tests/functional/hooks/post_test_hook.sh index a9e2b763..0ccf6473 100755 --- a/zunclient/tests/functional/hooks/post_test_hook.sh +++ b/zunclient/tests/functional/hooks/post_test_hook.sh @@ -59,11 +59,25 @@ cat etc/tempest.conf sudo -E tox -eall-plugin -- zun.tests.tempest.api --concurrency=1 -# NOTE(Namrata): Add more tests which uses OSC. +echo "Running OSC commands test for Zun" + +export ZUNCLIENT_DIR="$BASE/new/python-zunclient" + +sudo chown -R jenkins:stack $ZUNCLIENT_DIR + +# Go to the zunclient dir +cd $ZUNCLIENT_DIR + +# Run tests +set +e +source $BASE/new/devstack/openrc admin admin +sudo -E -H -u jenkins ./tools/run_functional.sh + EXIT_CODE=$? +set -e + popd - $XTRACE exit $EXIT_CODE diff --git a/zunclient/tests/functional/osc/__init__.py b/zunclient/tests/functional/osc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/zunclient/tests/functional/osc/v1/__init__.py b/zunclient/tests/functional/osc/v1/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/zunclient/tests/functional/osc/v1/base.py b/zunclient/tests/functional/osc/v1/base.py new file mode 100644 index 00000000..e800b022 --- /dev/null +++ b/zunclient/tests/functional/osc/v1/base.py @@ -0,0 +1,45 @@ +# 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 + +from zunclient.tests.functional import base + + +class TestCase(base.FunctionalTestBase): + + def openstack(self, *args, **kwargs): + return self._zun_osc(*args, **kwargs) + + def get_opts(self, fields=None, output_format='json'): + """Get options for OSC output fields format. + + :param List fields: List of fields to get + :param String output_format: Select output format + :return: String of formatted options + """ + if not fields: + return ' -f {0}'.format(output_format) + return ' -f {0} {1}'.format(output_format, + ' '.join(['-c ' + it for it in fields])) + + def container_list(self, fields=None, params=''): + """List Container. + + :param List fields: List of fields to show + :param String params: Additional kwargs + :return: list of JSON node objects + """ + opts = self.get_opts(fields=fields) + output = self.openstack('appcontainer list {0} {1}' + .format(opts, params)) + return json.loads(output) diff --git a/zunclient/tests/functional/osc/v1/test_container.py b/zunclient/tests/functional/osc/v1/test_container.py new file mode 100644 index 00000000..fe5a42b9 --- /dev/null +++ b/zunclient/tests/functional/osc/v1/test_container.py @@ -0,0 +1,32 @@ +# Copyright (c) 2016 Mirantis, 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. + +import ddt + +from zunclient.tests.functional.osc.v1 import base + + +@ddt.ddt +class ContainerTests(base.TestCase): + """Functional tests for container commands.""" + + def setUp(self): + super(ContainerTests, self).setUp() + + def test_list(self): + """Check container list command. + + """ + container_list = self.container_list() + self.assertFalse(container_list)