diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 0000000..c6d1843 --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,36 @@ +- project: + check: + jobs: + - refstack-client-devstack-tempestconf + - openstack-tox-py35 + gate: + jobs: + - refstack-client-devstack-tempestconf + - openstack-tox-py35 + +- job: + name: refstack-client-devstack-tempestconf + parent: devstack + description: | + Refstack client job for testing python-tempestconf and RefStack Integration + required-projects: + - openstack/refstack-client + - openstack/tempest + - openstack-dev/devstack + - openstack/python-tempestconf + roles: + - zuul: openstack/python-tempestconf + - zuul: openstack/tempest + - zuul: openstack-dev/devstack + - zuul: openstack/refstack-client + run: playbooks/tempestconf-refstack-devstack.yaml + vars: + user: demo + cloud_user: devstack + test_demo: True + cloud_admin: devstack-admin + irrelevant-files: + - config_tempest/tests/.*$ + - ^doc/.*$ + - ^releasenotes/.*$ + - ^.*\.rst$ diff --git a/README.rst b/README.rst index fa96a1c..da528b6 100644 --- a/README.rst +++ b/README.rst @@ -59,11 +59,24 @@ Usage source .venv/bin/activate -4. Validate your setup by running a short test:: +4. Generate tempest.conf using refstack-client:: + + refstack-client config --use-test-accounts + + The above command will create the tempest.conf in `etc` folder. + + Note: If account file is not available, then: + * Source the keystonerc file containing cloud credentials and run:: + + refstack-client config + + It will create accounts.yaml and temepst.conf file in `etc` folder. + +5. Validate your setup by running a short test:: refstack-client test -c -v -- --regex tempest.api.identity.v3.test_tokens.TokensV3Test.test_create_token -5. Run tests. +6. Run tests. To run the entire API test set:: diff --git a/playbooks/tempestconf-refstack-devstack.yaml b/playbooks/tempestconf-refstack-devstack.yaml new file mode 100644 index 0000000..17e362a --- /dev/null +++ b/playbooks/tempestconf-refstack-devstack.yaml @@ -0,0 +1,40 @@ +- hosts: all + roles: + - run-devstack + +- hosts: tempest + vars: + set_auth_url: "OS_AUTH_URL=$SERVICE_PROTOCOL://$SERVICE_HOST/identity/v3" + devstack_base_dir: "/opt/stack" + aditional_tempestconf_params: "auth.tempest_roles Member" + tasks: + - name: Setup Tempest Run Directory + include_role: + name: setup-tempest-run-dir + - name: Setup Tempest Data Directory + include_role: + name: setup-tempest-data-dir + - name: ACL devstack files + include_role: + name: acl-devstack-files + - name: Generate configuration file for Tempest as admin + include_role: + name: generate-tempestconf-file + vars: + output_path: "/etc/openstack/tempest_admin.conf" + source_credentials_commands: "export HOST_IP={{ ansible_default_ipv4.address }}; source {{ devstack_base_dir }}/devstack/openrc admin admin; {{ set_auth_url }}" + user: admin + - name: Generate accounts.yaml file for Demo + include_role: + name: generate-accounts-file + vars: + aditional_tempestconf_params: "auth.tempest_roles Member" + source_credentials_commands: "export HOST_IP={{ ansible_default_ipv4.address }}; source {{ devstack_base_dir }}/devstack/openrc admin admin; {{ set_auth_url }}" + accounts_file_destination: "/etc/openstack" + tempest_config_file: "/etc/openstack/tempest_admin.conf" + - name: Generate tempest.conf using refstack-client and run tempest tests + include_role: + name: generate-tempestconf-refstack + vars: + source_credentials_commands: "export HOST_IP={{ ansible_default_ipv4.address }}; source {{ devstack_base_dir }}/devstack/openrc {{ user }} {{ user }}; {{ set_auth_url }}" + user: demo diff --git a/refstack_client/refstack_client.py b/refstack_client/refstack_client.py index 9d87d13..13a8746 100755 --- a/refstack_client/refstack_client.py +++ b/refstack_client/refstack_client.py @@ -42,6 +42,11 @@ from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding +from config_tempest import main +from config_tempest import constants as C +from keystoneauth1 import exceptions as KE +from openstack import exceptions as OSE + import requests import requests.exceptions from six import moves @@ -408,6 +413,74 @@ def post_results(self, url, content, sign_with=None): resp = response.json() print('Test results uploaded!\nURL: %s' % resp.get('url', '')) + def generate_tempest_config(self): + '''Generate tempest.conf for a deployed OpenStack Cloud.''' + self.logger.info("Generating tempest.conf") + start_time = time.time() + + # Write tempest.conf in refstack_client folder + if not self.args.out: + config_path = os.path.join(self.refstack_dir, 'tempest.conf') + else: + config_path = self.args.out + + # Generate Tempest configuration + try: + cloud_creds = main.get_cloud_creds(self.args) + except KE.MissingRequiredOptions as e: + self.logger.error("Credentials are not sourced - %s" % e) + except OSE.ConfigException: + self.logger.error("Named cloud %s was not found" + % self.args.os_cloud) + + # tempestconf arguments + kwargs = {'non_admin': True, + 'test_accounts': self.args.test_accounts, + 'image_path': self.args.image, + 'network_id': self.args.network_id, + 'out': config_path, + 'cloud_creds': cloud_creds} + + # Look for extra overrides to be replaced in tempest.conf + # (TODO:chkumar246) volume-feature-enabled.api_v2=True is deprecated + # in ROCKY release, but it is required for interop tests and it is out + # of scope of python-tempestconf, adding it hardcoded here as a extra + # overrides. + cinder_overrides = "volume-feature-enabled.api_v2=True" + overrides_format = cinder_overrides.replace('=', ' ').split() + if self.args.overrides: + if cinder_overrides not in self.args.overrides: + overrides = self.args.overrides.replace('=', ' ').split(',') + extra_overrides = overrides.append(overrides_format) + else: + extra_overrides = overrides_format + kwargs.update({'overrides': main.parse_overrides(extra_overrides)}) + + # Generate accounts.yaml if accounts.file is not given + if not self.args.test_accounts: + account_file = os.path.join(self.refstack_dir, 'accounts.yaml') + kwargs.update({'create_accounts_file': account_file}) + self.logger.info('Account file will be generated at %s.' + % account_file) + + # Generate tempest.conf + main.config_tempest(**kwargs) + + if os.path.isfile(config_path): + end_time = time.time() + elapsed = end_time - start_time + duration = int(elapsed) + self.logger.info('Tempest Configuration successfully generated ' + 'in %s second at %s' % (duration, config_path)) + else: + try: + import config_tempest # noqa + self.logging.warning('There is an error in syntax, please ' + 'check $ refstack-client config -h') + except ImportError: + self.logger.warning('Please make sure python-tempestconf' + 'python package is installed') + def test(self): '''Execute Tempest test against the cloud.''' self._prep_test() @@ -707,6 +780,62 @@ def parse_cli_args(args=None): parser_subunit_upload.set_defaults(func="upload_subunit") + # Config Command + parser_config = subparsers.add_parser( + 'config', parents=[shared_args, network_args], + help='Generate tempest.conf for a cloud') + + parser_config.add_argument('--use-test-accounts', + action='store', + required=False, + dest='test_accounts', + type=str, + help='Path of the accounts.yaml file.') + + parser_config.add_argument('--network-id', + action='store', + required=False, + dest='network_id', + help='The ID of an existing network in our ' + 'openstack instance with external ' + 'connectivity') + + parser_config.add_argument('--image', + action='store', + required=False, + dest='image', + help='An image name chosen from `$ openstack ' + 'image list` or a filepath/URL of an ' + 'image to be uploaded to glance and set ' + 'as a reference to be used by tests. The ' + 'name of the image is the leaf name of ' + 'the path. Default is %s' + % C.DEFAULT_IMAGE) + + parser_config.add_argument('--out', + action='store', + required=False, + dest='out', + help='File path to write tempest.conf') + + parser_config.add_argument('--os-cloud', + action='store', + required=False, + dest='os_cloud', + help='Named cloud to connect to.') + + parser_config.add_argument('--overrides', + action='store', + required=False, + dest='overrides', + help='Comma seperated values which needs to be' + 'overridden in tempest.conf.' + 'Example --overrides' + 'compute.image_ref=,' + 'compute.flavor_ref=') + + parser_config.set_defaults(func='generate_tempest_config') + # Test command parser_test = subparsers.add_parser( 'test', parents=[shared_args, network_args], diff --git a/roles/generate-tempestconf-refstack/defaults/main.yaml b/roles/generate-tempestconf-refstack/defaults/main.yaml new file mode 100644 index 0000000..3b1dda2 --- /dev/null +++ b/roles/generate-tempestconf-refstack/defaults/main.yaml @@ -0,0 +1,3 @@ +cloud_user: "devstack" +virtualenvs: + refstack_client: "~/.virtualenvs/.refstack_client" diff --git a/roles/generate-tempestconf-refstack/tasks/main.yaml b/roles/generate-tempestconf-refstack/tasks/main.yaml new file mode 100644 index 0000000..42d9a20 --- /dev/null +++ b/roles/generate-tempestconf-refstack/tasks/main.yaml @@ -0,0 +1,49 @@ +- block: + - name: Install refstack-client and python-tempestconf + shell: | + set -ex + export PATH=$PATH:/usr/local/sbin:/usr/sbin + ./setup_env + args: + chdir: "{{ refstack_client_src_relative_path }}" + executable: /bin/bash + + - name: Print Tempest account file + shell: | + set -ex + cat /etc/openstack/accounts.yaml + + - name: Generate tempest configuration file + shell: | + set -ex + export PATH=$PATH:/usr/local/sbin:/usr/sbin + source .venv/bin/activate + {{ source_credentials_commands }} + printenv + refstack-client config --use-test-accounts /etc/openstack/accounts.yaml \ + --image http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img \ + --out /tmp/tempest.conf + args: + chdir: "{{ refstack_client_src_relative_path }}" + executable: /bin/bash + + - name: Print generated tempest.conf + shell: | + set -ex + cat /tmp/tempest.conf + + - name: Run refstack-client tests + shell: | + set -ex + export PATH=$PATH:/usr/local/sbin:/usr/sbin + source .venv/bin/activate + printenv + refstack-client test -c /tmp/tempest.conf \ + -v --test-list "https://refstack.openstack.org/api/v1/guidelines/2017.09/tests?target=platform&type=required&alias=true&flag=false" + args: + chdir: "{{ refstack_client_src_relative_path }}" + executable: /bin/bash + + vars: + refstack_client_src_relative_path: "{{ zuul.projects['git.openstack.org/openstack/refstack-client'].src_dir }}" + tempestconf_src_relative_path: "{{ zuul.projects['git.openstack.org/openstack/python-tempestconf'].src_dir }}" diff --git a/setup_env b/setup_env index dcb7b6a..f8f2f02 100755 --- a/setup_env +++ b/setup_env @@ -62,7 +62,7 @@ done WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TEMPEST_DIR=${REFSTACK_CLIENT_TEMPEST_DIR:-${WORKDIR}/.tempest} - +TEMPESTCONF_DIR=${REFSTACK_CLIENT_TEMPEST_DIR:-${WORKDIR}/.tempestconf} # Checkout tempest on specified tag if [ -d "${TEMPEST_DIR}" ]; then [ ${QUIET_MODE} ] && echo 'Looks like RefStack client is already installed' && exit 0 @@ -108,7 +108,9 @@ else exit 1 fi -git clone https://github.com/openstack/tempest.git ${TEMPEST_DIR} +git clone https://git.openstack.org/openstack/python-tempestconf.git ${TEMPESTCONF_DIR} + +git clone https://git.openstack.org/openstack/tempest.git ${TEMPEST_DIR} cd ${TEMPEST_DIR} git checkout $CHECKOUT_POINT || if [ $? -ne 0 ]; then exit 1; fi @@ -165,6 +167,9 @@ cd .. rm -rf virtualenv-${VENV_VERSION} rm virtualenv-${VENV_VERSION}.tar.gz ${WORKDIR}/.venv/bin/python -m pip install -e . +cd ${TEMPESTCONF_DIR} +${WORKDIR}/.venv/bin/python -m pip install -e . +cd .. ${TEMPEST_DIR}/.venv/bin/python -m pip install ${TEMPEST_DIR} # Add additional packages to find more tests by tempest diff --git a/tox.ini b/tox.ini index fe5371a..e191ce6 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,14 @@ setenv = VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = + bash -c "if [ -d ./.tox/py27 ]; then \ + pip install -q -U -e 'git+https://git.openstack.org/openstack/python-tempestconf@master#egg=python_tempestconf' ;\ + elif [ -d ./.tox/py35 ]; then \ + pip3 install -q -U -e 'git+https://git.openstack.org/openstack/python-tempestconf@master#egg=python_tempestconf' ; fi " stestr run {posargs} + +whitelist_externals = + bash distribute = false [testenv:pep8]