From cebf3b34821f46e0ed250a3fad2657acc89ef9a0 Mon Sep 17 00:00:00 2001 From: Logan V Date: Mon, 19 Mar 2018 15:24:08 -0500 Subject: [PATCH] Add base role tests Depends-On: https://review.openstack.org/#/c/554402/ Change-Id: I3b270cc65aedaf9ef7375289b0ff7373b2c8d705 Signed-off-by: Kevin Carter --- bindep.txt | 41 ++++++++----- doc/source/conf.py | 30 +++------ releasenotes/source/conf.py | 29 ++++----- run_tests.sh | 87 +++++++++++---------------- setup.cfg | 2 +- tasks/main.yml | 26 ++++++++ tests/test.yml | 87 ++++++++++++++++++++++++++- tests/tests-repo-clone.sh | 117 ++++++++++++++++++++++++++++++++++++ vars/main.yml | 20 ++++++ vars/redhat-7.yml | 17 ++++++ zuul.d/project.yaml | 30 +++++++++ 11 files changed, 383 insertions(+), 103 deletions(-) create mode 100755 tests/tests-repo-clone.sh create mode 100644 vars/main.yml create mode 100644 vars/redhat-7.yml create mode 100644 zuul.d/project.yaml diff --git a/bindep.txt b/bindep.txt index 59ab45d..5a05c5a 100644 --- a/bindep.txt +++ b/bindep.txt @@ -2,40 +2,51 @@ # before the execution of any tests. # # See the following for details: -# - http://docs.openstack.org/infra/bindep/ -# - https://github.com/openstack-infra/bindep +# - https://docs.openstack.org/infra/bindep/ +# - https://git.openstack.org/cgit/openstack-infra/bindep # # Even if the role does not make use of this facility, it # is better to have this file empty, otherwise OpenStack-CI # will fall back to installing its default packages which # will potentially be detrimental to the tests executed. +# +# Note: +# This file is maintained in the openstack-ansible-tests repository. +# https://git.openstack.org/cgit/openstack/openstack-ansible-tests/tree/bindep.txt +# If you need to remove or add extra dependencies, you should modify +# the central file instead and once your change is accepted then update +# this file as well. The purpose of this file is to ensure that Python and +# Ansible have all their necessary binary requirements on the test host before +# tox executes. Any binary requirements needed by services/roles should be +# installed by those roles in their applicable package install tasks, not through +# using this file. +# + +# The gcc compiler +gcc # Base requirements for Ubuntu -build-essential [platform:dpkg] git-core [platform:dpkg] libssl-dev [platform:dpkg] libffi-dev [platform:dpkg] python2.7 [platform:dpkg] +python-apt [platform:dpkg] python-dev [platform:dpkg] +python3 [platform:dpkg] +python3-apt [platform:dpkg] +python3-dev [platform:dpkg] -# Base requirements for CentOS -gcc [platform:rpm] +# Base requirements for RPM distros gcc-c++ [platform:rpm] git [platform:rpm] -python-devel [platform:rpm] libffi-devel [platform:rpm] openssl-devel [platform:rpm] +python-devel [platform:rpm] +python2-dnf [platform:fedora] # For SELinux -libselinux-python [platform:rpm] - -# For SSL SNI support -python-pyasn1 [platform:dpkg] -python-openssl [platform:dpkg] -python-ndg-httpsclient [platform:ubuntu !platform:ubuntu-trusty] -python2-pyasn1 [platform:rpm] -python2-pyOpenSSL [platform:rpm] -python-ndg_httpsclient [platform:rpm] +libselinux-python [platform:redhat] +libsemanage-python [platform:redhat] # Required for compressing collected log files in CI gzip diff --git a/doc/source/conf.py b/doc/source/conf.py index dc68a3e..b711b39 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -22,7 +22,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import openstackdocstheme import pbr.version import os @@ -40,8 +39,8 @@ import os # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + 'openstackdocstheme', 'sphinx.ext.autodoc', - 'oslosphinx', 'sphinxmark' ] @@ -62,15 +61,17 @@ master_doc = 'index' # General information about the project. author = 'OpenStack-Ansible Contributors' category = 'Miscellaneous' -copyright = '2014-2018, OpenStack-Ansible Contributors' +copyright = '2014-2016, OpenStack-Ansible Contributors' description = 'OpenStack-Ansible deploys OpenStack environments using Ansible.' project = 'OpenStack-Ansible' -role_name = 'os_nova' +role_name = 'systemd_networkd' target_name = 'openstack-ansible-' + role_name title = 'OpenStack-Ansible Documentation: ' + role_name + 'role' # The link to the browsable source code (for the left hand menu) -oslosphinx_cgit_link = 'https://git.openstack.org/cgit/openstack/' + target_name +oslosphinx_cgit_link = ( + "https://git.openstack.org/cgit/openstack/{}".format(target_name) +) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -83,21 +84,10 @@ release = version_info.version_string_with_vcs() # The short X.Y version. version = version_info.canonical_version_string() -# A few variables have to be set for the log-a-bug feature. -# giturl: The location of conf.py on Git. Must be set manually. -# gitsha: The SHA checksum of the bug description. -# Automatically extracted from git log. -# bug_tag: Tag for categorizing the bug. Must be set manually. -# These variables are passed to the logabug code via html_context. -giturl = ("https://git.openstack.org/cgit/openstack/{0}" - "/tree/doc/source").format(target_name) -git_cmd = "/usr/bin/git log | head -n1 | cut -f2 -d' '" -gitsha = os.popen(git_cmd).read().strip('\n') +# openstackdocstheme options +repository_name = 'openstack/' + target_name bug_project = project.lower() -bug_title = "Documentation bug" -html_context = {"gitsha": gitsha, "giturl": giturl, - "bug_tag": "docs", "bug_title": bug_title, - "bug_project": bug_project} +bug_tag = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -156,7 +146,7 @@ html_theme = 'openstackdocs' # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [openstackdocstheme.get_html_theme_path()] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index 5034fb4..fb2a460 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -22,8 +22,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import pbr.version - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -38,7 +36,7 @@ import pbr.version # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'oslosphinx', + 'openstackdocstheme', 'reno.sphinxext', ] @@ -57,7 +55,7 @@ master_doc = 'index' # General information about the project. author = 'OpenStack-Ansible Contributors' category = 'Miscellaneous' -copyright = '2014-2018, OpenStack-Ansible Contributors' +copyright = '2014-2016, OpenStack-Ansible Contributors' description = 'OpenStack-Ansible deploys OpenStack environments using Ansible.' project = 'OpenStack-Ansible' role_name = 'systemd_networkd' @@ -65,18 +63,21 @@ target_name = 'openstack-ansible-' + role_name title = 'OpenStack-Ansible Release Notes: ' + role_name + 'role' # The link to the browsable source code (for the left hand menu) -oslosphinx_cgit_link = 'https://git.openstack.org/cgit/openstack/' + target_name +oslosphinx_cgit_link = ( + "https://git.openstack.org/cgit/openstack/{}".format(target_name) +) -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version_info = pbr.version.VersionInfo(target_name) +# Release notes do not need a version number in the title, +# they cover multiple versions. # The full version, including alpha/beta/rc tags. -release = version_info.version_string_with_vcs() +release = '' # The short X.Y version. -version = version_info.canonical_version_string() +version = '' + +# openstackdocstheme options +repository_name = 'openstack/' + target_name +bug_project = project.lower() +bug_tag = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -121,7 +122,7 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/run_tests.sh b/run_tests.sh index ca109b8..4b72d3f 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2018, Rackspace US, Inc. +# Copyright 2015, Rackspace US, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,63 +12,48 @@ # 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. +# +# Note: +# This file is maintained in the openstack-ansible-tests repository. +# https://git.openstack.org/cgit/openstack/openstack-ansible-tests/tree/run_tests.sh +# If you need to modify this file, update the one in the openstack-ansible-tests +# repository and then update this file as well. The purpose of this file is to +# prepare the host and then execute all the tox tests. +# -set -xeuo pipefail +## Shell Opts ---------------------------------------------------------------- +set -xeu -FUNCTIONAL_TEST=${FUNCTIONAL_TEST:-true} +## Vars ---------------------------------------------------------------------- -# Install python2 for Ubuntu 16.04 and CentOS 7 -if which apt-get; then - sudo apt-get update && sudo apt-get install -y python -fi +export WORKING_DIR=${WORKING_DIR:-$(pwd)} -if which yum; then - sudo yum install -y python -fi +## Main ---------------------------------------------------------------------- -# Install pip. -if ! which pip; then - curl --silent --show-error --retry 5 \ - https://bootstrap.pypa.io/get-pip.py | sudo python2.7 -fi +source /etc/os-release || source /usr/lib/os-release -# Install bindep and tox with pip. -sudo pip install bindep tox +install_pkg_deps() { + pkg_deps="git" -# CentOS 7 requires two additional packages: -# redhat-lsb-core - for bindep profile support -# epel-release - required to install python-ndg_httpsclient/python2-pyasn1 -if which yum; then - sudo yum -y install redhat-lsb-core epel-release -fi + # Prefer dnf over yum for CentOS. + which dnf &>/dev/null && RHT_PKG_MGR='dnf' || RHT_PKG_MGR='yum' -# Get a list of packages to install with bindep. If packages need to be -# installed, bindep exits with an exit code of 1. -BINDEP_PKGS=$(bindep -b -f bindep.txt test || true) -echo "Packages to install: ${BINDEP_PKGS}" + case ${ID,,} in + *suse*) pkg_mgr_cmd="zypper -n in" ;; + centos|rhel|fedora) pkg_mgr_cmd="${RHT_PKG_MGR} install -y" ;; + ubuntu|debian) pkg_mgr_cmd="apt-get install -y" ;; + *) echo "unsupported distribution: ${ID,,}"; exit 1 ;; + esac -# Install a list of OS packages provided by bindep. -if which apt-get; then - sudo apt-get update - DEBIAN_FRONTEND=noninteractive \ - sudo apt-get -q --option "Dpkg::Options::=--force-confold" \ - --assume-yes install $BINDEP_PKGS -elif which yum; then - # Don't run yum with an empty list of packages. - # It will fail and cause the script to exit with an error. - if [[ ${#BINDEP_PKGS} > 0 ]]; then - sudo yum install -y $BINDEP_PKGS - fi -fi + eval sudo $pkg_mgr_cmd $pkg_deps +} + +# Install the host distro package dependencies +install_pkg_deps + +# Clone the tests repo for access to the common test script +source tests/tests-repo-clone.sh + +# Execute the common test script +source tests/common/run_tests_common.sh -# Loop through each tox environment and run tests. -for tox_env in $(awk -F= '/envlist/ { gsub(",", " "); print $2 }' tox.ini); do - echo "Executing tox environment: ${tox_env}" - if [[ ${tox_env} == ansible-functional ]]; then - if ${FUNCTIONAL_TEST}; then - tox -e ${tox_env} - fi - else - tox -e ${tox_env} - fi -done diff --git a/setup.cfg b/setup.cfg index 1f21389..4d99b64 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,7 +2,7 @@ name = systemd_networkd summary = systemd_networkd for OpenStack Ansible description-file = - README.rst + README.md author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = http://docs.openstack.org/developer/ansible-systemd_networkd/ diff --git a/tasks/main.yml b/tasks/main.yml index 8ecce4c..052acd4 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -13,6 +13,32 @@ # See the License for the specific language governing permissions and # limitations under the License. +- name: Gather variables for each operating system + include_vars: "{{ item }}" + with_first_found: + - files: + - "{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml" + - "{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml" + - "{{ ansible_os_family | lower }}-{{ ansible_distribution_major_version | lower }}.yml" + - "{{ ansible_distribution | lower }}.yml" + - "{{ ansible_os_family | lower }}.yml" + skip: true + tags: + - always + +- name: Install networkd distro packages + package: + name: "{{ systemd_networkd_distro_packages }}" + state: "present" + update_cache: "{{ (ansible_pkg_mgr == 'apt') | ternary('yes', omit) }}" + cache_valid_time: "{{ (ansible_pkg_mgr == 'apt') | ternary(600, omit) }}" + when: + - systemd_networkd_distro_packages | length > 0 + register: install_packages + until: install_packages|success + retries: 3 + delay: 2 + - name: Create systemd-networkd directory file: path: "/etc/systemd/network" diff --git a/tests/test.yml b/tests/test.yml index 5c34458..544b2e2 100644 --- a/tests/test.yml +++ b/tests/test.yml @@ -1,5 +1,6 @@ --- # Copyright 2018, Rackspace US, Inc. +# Copyright 2018, Logan Vig # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +14,89 @@ # See the License for the specific language governing permissions and # limitations under the License. -- hosts: localhost +- name: Playbook for role testing + hosts: localhost + connection: local + become: true + gather_facts: true + roles: + - role: "systemd_networkd" + post_tasks: + # Pause for 5 minutes to build app cache. + - name: Allow networkd to spin up + pause: + seconds: 15 + vars: + systemd_networkd_distro_packages: + yum: + - systemd-networkd + systemd_run_networkd: yes + systemd_netdevs: + - NetDev: + Name: dummy0 + Kind: dummy + - NetDev: + Name: dummy1 + Kind: dummy + - NetDev: + Name: bond0 + Kind: bond + Bond: + Mode: 802.3ad + TransmitHashPolicy: layer3+4 + MIIMonitorSec: 1s + LACPTransmitRate: fast + - NetDev: + Name: br-dummy + Kind: bridge + systemd_networks: + - interface: "dummy0" + bond: "bond0" + mtu: 9000 + - interface: "dummy1" + bond: "bond0" + mtu: 9000 + - interface: "bond0" + bridge: "br-dummy" + mtu: 9000 + - interface: "br-dummy" + address: "10.0.0.100" + netmask: "255.255.255.0" + gateway: "10.0.0.1" + mtu: 9000 + usedns: true + static_routes: + - gateway: "10.1.0.1" + cidr: "10.1.0.0/24" + config_overrides: + Network: + ConfigureWithoutCarrier: true + +- name: Test networkd + hosts: localhost + connection: local + become: true gather_facts: true tasks: - - debug: msg=testing \ No newline at end of file + - name: Interface check + assert: + that: + - ansible_dummy0['active'] == true + - ansible_dummy0['type'] == 'ether' + - ansible_dummy0['mtu'] == 9000 + - ansible_dummy1['active'] == true + - ansible_dummy1['type'] == 'ether' + - ansible_dummy1['mtu'] == 9000 + - name: Bond check + assert: + that: + - ansible_bond0['active'] == true + - ansible_bond0['type'] == 'bonding' + - ansible_bond0['mtu'] == 9000 + - name: Bridge check + assert: + that: + - ansible_br_dummy['active'] == true + - ansible_br_dummy['type'] == 'bridge' + - ansible_br_dummy['ipv4']['address'] == '10.0.0.100' + - ansible_br_dummy['ipv4']['netmask'] == '255.255.255.0' diff --git a/tests/tests-repo-clone.sh b/tests/tests-repo-clone.sh new file mode 100755 index 0000000..5ad509c --- /dev/null +++ b/tests/tests-repo-clone.sh @@ -0,0 +1,117 @@ +#!/bin/bash +# Copyright 2017, Rackspace US, 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. + +# PURPOSE: +# This script clones the openstack-ansible-tests repository to the +# tests/common folder in order to be able to re-use test components +# for role testing. + +# WARNING: +# This file is maintained in the openstack-ansible-tests repository: +# https://git.openstack.org/cgit/openstack/openstack-ansible-tests +# If you need to change this script, then propose the change there. +# Once it merges, the change will be replicated to the other repositories. + +## Shell Opts ---------------------------------------------------------------- + +set -e + +## Vars ---------------------------------------------------------------------- + +export TESTING_HOME=${TESTING_HOME:-$HOME} +export WORKING_DIR=${WORKING_DIR:-$(pwd)} +export CLONE_UPGRADE_TESTS=${CLONE_UPGRADE_TESTS:-no} +export ZUUL_TESTS_CLONE_LOCATION="/home/zuul/src/git.openstack.org/openstack/openstack-ansible-tests" + +## Functions ----------------------------------------------------------------- + +function create_tests_clonemap { + +# Prepare the clonemap for zuul-cloner to use +cat > ${TESTING_HOME}/tests-clonemap.yaml << EOF +clonemap: + - name: openstack/openstack-ansible-tests + dest: ${WORKING_DIR}/tests/common +EOF + +} + +## Main ---------------------------------------------------------------------- + +# If zuul-cloner is present, use it so that we +# also include any dependent patches from the +# tests repo noted in the commit message. +# We only want to use zuul-cloner if we detect +# zuul v2 running, so we check for the presence +# of the ZUUL_REF environment variable. +# ref: http://git.openstack.org/cgit/openstack-infra/zuul/tree/zuul/ansible/filter/zuul_filters.py?h=feature/zuulv3#n17 +if [[ -x /usr/zuul-env/bin/zuul-cloner ]] && [[ "${ZUUL_REF:-none}" != "none" ]]; then + + # Prepare the clonemap for zuul-cloner to use + create_tests_clonemap + + # Execute the clone + /usr/zuul-env/bin/zuul-cloner \ + --cache-dir /opt/git \ + --map ${TESTING_HOME}/tests-clonemap.yaml \ + git://git.openstack.org \ + openstack/openstack-ansible-tests + + # Clean up the clonemap. + rm -f ${TESTING_HOME}/tests-clonemap.yaml + +# Alternatively, use a simple git-clone. We do +# not re-clone if the directory exists already +# to prevent overwriting any local changes which +# may have been made. +elif [[ ! -d tests/common ]]; then + + # The tests repo doesn't need a clone, we can just + # symlink it. As zuul v3 clones into a folder called + # 'workspace' we have to use one of its environment + # variables to determine the project name. + if [[ "${ZUUL_SHORT_PROJECT_NAME:-none}" == "openstack-ansible-tests" ]] ||\ + [[ "$(basename ${WORKING_DIR})" == "openstack-ansible-tests" ]]; then + ln -s ${WORKING_DIR} ${WORKING_DIR}/tests/common + + # In zuul v3 any dependent repository is placed into + # /home/zuul/src/git.openstack.org, so we check to see + # if there is a tests checkout there already. If so, we + # symlink that and use it. + elif [[ -d "${ZUUL_TESTS_CLONE_LOCATION}" ]]; then + ln -s "${ZUUL_TESTS_CLONE_LOCATION}" ${WORKING_DIR}/tests/common + + # Otherwise we're clearly not in zuul or using a previously setup + # repo in some way, so just clone it from upstream. + else + git clone \ + https://git.openstack.org/openstack/openstack-ansible-tests \ + ${WORKING_DIR}/tests/common + fi +fi + +# If this test set includes an upgrade test, the +# previous stable release tests repo must also be +# cloned. +# Note: +# Dependent patches to the previous stable release +# tests repo are not supported. +if [[ "${CLONE_UPGRADE_TESTS}" == "yes" ]]; then + if [[ ! -d "${WORKING_DIR}/tests/common/previous" ]]; then + git clone -b stable/queens \ + https://git.openstack.org/openstack/openstack-ansible-tests \ + ${WORKING_DIR}/tests/common/previous + fi +fi diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..b8f0239 --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,20 @@ +--- +# Copyright 2018, Logan Vig +# +# 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. + +## APT Cache options +cache_timeout: 600 + +# Supporting package required to make systemd-networkd function +systemd_networkd_distro_packages: [] diff --git a/vars/redhat-7.yml b/vars/redhat-7.yml new file mode 100644 index 0000000..2aaa301 --- /dev/null +++ b/vars/redhat-7.yml @@ -0,0 +1,17 @@ +--- +# Copyright 2018, Logan Vig +# +# 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. + +systemd_networkd_distro_packages: + - systemd-networkd diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml new file mode 100644 index 0000000..6d4e2f4 --- /dev/null +++ b/zuul.d/project.yaml @@ -0,0 +1,30 @@ +# Copyright 2017, Rackspace US, 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. + +- project: + check: + jobs: + - openstack-ansible-linters + - openstack-ansible-functional-centos-7 + - openstack-ansible-functional-opensuse-423 + - openstack-ansible-functional-ubuntu-xenial + experimental: + jobs: + - openstack-ansible-integrated-deploy-aio + gate: + jobs: + - openstack-ansible-linters + - openstack-ansible-functional-centos-7 + - openstack-ansible-functional-opensuse-423 + - openstack-ansible-functional-ubuntu-xenial