diff --git a/manual-test.rc b/manual-test.rc
index f7e77710..7016c453 100644
--- a/manual-test.rc
+++ b/manual-test.rc
@@ -30,4 +30,4 @@ export ANSIBLE_SSH_ARGS="-o ControlMaster=no \
   -o ForwardAgent=yes"
 
 echo "Run manual functional tests by executing the following:"
-echo "# ./.tox/functional/bin/ansible-playbook -i tests/inventory tests/test.yml -e \"rolename=$(pwd)\""
+echo "# ./.tox/functional/bin/ansible-playbook -i tests/inventory tests/test.yml"
diff --git a/tests/test-keystone-functional.yml b/tests/test-keystone-functional.yml
index 66733431..9fdf02b8 100644
--- a/tests/test-keystone-functional.yml
+++ b/tests/test-keystone-functional.yml
@@ -70,7 +70,7 @@
       when: keystone_ssl_key_stats.stat.mode != "0640"
 
   vars_files:
-    - playbooks/test-vars.yml
+    - common/test-vars.yml
 
 # Run tempest identity tests on one keystone host.
 - name: Playbook for functional testing keystone
@@ -86,4 +86,4 @@
         RUN_TEMPEST_OPTS: "--serial"
 
   vars_files:
-    - playbooks/test-vars.yml
+    - common/test-vars.yml
diff --git a/tests/test.yml b/tests/test.yml
index 3cbdae4b..569a7031 100644
--- a/tests/test.yml
+++ b/tests/test.yml
@@ -14,22 +14,22 @@
 # limitations under the License.
 
 # Prepare the user ssh keys
-- include: playbooks/test-prepare-keys.yml
+- include: common/test-prepare-keys.yml
 
 # Prepare the host
-- include: playbooks/test-prepare-host.yml
+- include: common/test-prepare-host.yml
 
 # Prepare the containers
-- include: playbooks/test-prepare-containers.yml
+- include: common/test-prepare-containers.yml
 
 # Install RabbitMQ/MariaDB
-- include: playbooks/test-install-infra.yml
+- include: common/test-install-infra.yml
 
 # Install Keystone
-- include: playbooks/test-install-keystone.yml
+- include: common/test-install-keystone.yml
 
 # Install Tempest
-- include: playbooks/test-install-tempest.yml
+- include: common/test-install-tempest.yml
 
 # Test Keystone
 - include: test-keystone-functional.yml
diff --git a/tox.ini b/tox.ini
index 8a46e1fa..cc8c38ce 100644
--- a/tox.ini
+++ b/tox.ini
@@ -22,12 +22,11 @@ passenv =
     NO_PROXY
 whitelist_externals =
     bash
-    git
-    rm
-    wget
 setenv =
-    VIRTUAL_ENV={envdir}
     PYTHONUNBUFFERED=1
+    ROLE_NAME=os_keystone
+    VIRTUAL_ENV={envdir}
+    WORKING_DIR={toxinidir}
 
 
 [testenv:docs]
@@ -53,17 +52,17 @@ commands =
     {posargs}
 
 
+[testenv:tests_clone]
+commands =
+    bash -c "if [ ! -d "{toxinidir}/tests/common" ]; then \
+               git clone https://git.openstack.org/openstack/openstack-ansible-tests {toxinidir}/tests/common; \
+             fi"
+
+
 [testenv:pep8]
 commands =
-    # Run hacking/flake8 check for all python files
-    bash -c "grep --recursive --binary-files=without-match \
-        --files-with-match '^.!.*python$' \
-        --exclude-dir .eggs \
-        --exclude-dir .git \
-        --exclude-dir .tox \
-        --exclude-dir *.egg-info \
-        --exclude-dir doc \
-        {toxinidir} | xargs flake8 --verbose"
+    {[testenv:tests_clone]commands}
+    bash -c "{toxinidir}/tests/common/test-pep8.sh"
 
 
 [flake8]
@@ -76,71 +75,27 @@ ignore=F403,H303
 
 [testenv:bashate]
 commands =
-    # Run bashate check for all bash scripts
-    # Ignores the following rules:
-    #     E003: Indent not multiple of 4 (we prefer to use multiples of 2)
-    #     E006: Line longer than 79 columns (as many scripts use jinja
-    #           templating, this is very difficult)
-    #     E040: Syntax error determined using `bash -n` (as many scripts
-    #           use jinja templating, this will often fail and the syntax
-    #           error will be discovered in execution anyway)
-    bash -c "grep --recursive --binary-files=without-match \
-        --files-with-match '^.!.*\(ba\)\?sh$' \
-        --exclude-dir .tox \
-        --exclude-dir .git \
-        {toxinidir} | xargs bashate --error . --verbose --ignore=E003,E006,E040"
+    {[testenv:tests_clone]commands}
+    bash -c "{toxinidir}/tests/common/test-bashate.sh"
 
 
 [testenv:ansible]
 deps =
     {[testenv]deps}
-    ansible==2.1.1
-    ansible-lint>=2.7.0,<3.0.0
-setenv =
-    {[testenv]setenv}
-    ANSIBLE_HOST_KEY_CHECKING = False
-    ANSIBLE_SSH_CONTROL_PATH = /tmp/%%h-%%r
-    # TODO (odyssey4me) These are only here as they are non-standard folder
-    # names for Ansible 1.9.x. We are using the standard folder names for
-    # Ansible v2.x. We can remove this when we move to Ansible 2.x.
-    ANSIBLE_ACTION_PLUGINS = {homedir}/.ansible/plugins/action
-    ANSIBLE_CALLBACK_PLUGINS = {homedir}/.ansible/plugins/callback
-    ANSIBLE_FILTER_PLUGINS = {homedir}/.ansible/plugins/filter
-    ANSIBLE_LOOKUP_PLUGINS = {homedir}/.ansible/plugins/lookup
-    # This is required as the default is the current path or a path specified
-    # in ansible.cfg
-    ANSIBLE_LIBRARY = {homedir}/.ansible/plugins/library
-    # This is required as the default is '/etc/ansible/roles' or a path
-    # specified in ansible.cfg
-    ANSIBLE_ROLES_PATH = {homedir}/.ansible/roles:{toxinidir}/..
-    ANSIBLE_TRANSPORT = "ssh"
-    PYTHONUNBUFFERED = 1
+    -rhttp://git.openstack.org/cgit/openstack/openstack-ansible-tests/plain/test-ansible-deps.txt
 commands =
-    rm -rf {homedir}/.ansible/plugins
-    git clone https://git.openstack.org/openstack/openstack-ansible-plugins \
-              {homedir}/.ansible/plugins
-    rm -rf {homedir}/.ansible/roles
-    ansible-galaxy install \
-                   --role-file={toxinidir}/tests/ansible-role-requirements.yml \
-                   --force
-    rm -rf {homedir}/.ansible/roles/os_keystone
-    bash -c "ln -s {toxinidir} {homedir}/.ansible/roles/os_keystone"
-    rm -rf {toxinidir}/tests/playbooks
-    git clone https://git.openstack.org/openstack/openstack-ansible-tests \
-              {toxinidir}/tests/playbooks
+    {[testenv:tests_clone]commands}
+    bash -c "{toxinidir}/tests/common/test-ansible-env-prep.sh"
 
 
 [testenv:ansible-syntax]
 deps =
     {[testenv:ansible]deps}
-setenv =
-    {[testenv:ansible]setenv}
 commands =
     {[testenv:ansible]commands}
     ansible-playbook -i {toxinidir}/tests/inventory \
                      --syntax-check \
                      --list-tasks \
-                     -e "keystone_rolename={toxinidir}" \
                      {toxinidir}/tests/test.yml
 
 
@@ -162,10 +117,7 @@ install_command =
 
 [testenv:func_logs]
 commands =
-    bash -c 'mkdir -p {toxinidir}/logs'
-    bash -c 'rsync --archive --verbose --ignore-errors /var/log/ /openstack/log/ {toxinidir}/logs/ || true'
-    bash -c 'find "{toxinidir}/logs/" -type f | sed "p;s|$|.txt|" | xargs -n2 mv'
-    bash -c 'command gzip --best --recursive "{toxinidir}/logs/"'
+    bash -c "{toxinidir}/tests/common/test-log-collect.sh"
 
 
 [testenv:functional]
@@ -179,13 +131,10 @@ install_command =
     {[testenv:func_base]install_command}
 deps =
     {[testenv:ansible]deps}
-setenv =
-    {[testenv:ansible]setenv}
 commands =
     {[testenv:ansible]commands}
     ansible-playbook -i {toxinidir}/tests/inventory \
                      -e @{toxinidir}/tests/keystone-overrides.yml \
-                     -e "keystone_rolename={toxinidir}" \
                      {toxinidir}/tests/test.yml -vvvv
     {[testenv:func_logs]commands}
 
@@ -201,13 +150,10 @@ install_command =
     {[testenv:func_base]install_command}
 deps =
     {[testenv:ansible]deps}
-setenv =
-    {[testenv:ansible]setenv}
 commands =
     {[testenv:ansible]commands}
     ansible-playbook -i {toxinidir}/tests/inventory \
                      -e @{toxinidir}/tests/keystone-overrides.yml \
-                     -e "keystone_rolename={toxinidir}" \
                      -e "keystone_mod_wsgi_enabled=False" \
                      {toxinidir}/tests/test.yml -vvvv
     {[testenv:func_logs]commands}
@@ -224,13 +170,10 @@ install_command =
     {[testenv:func_base]install_command}
 deps =
     {[testenv:ansible]deps}
-setenv =
-    {[testenv:ansible]setenv}
 commands =
     {[testenv:ansible]commands}
     ansible-playbook -i {toxinidir}/tests/inventory \
                      -e @{toxinidir}/tests/keystone-overrides.yml \
-                     -e "keystone_rolename={toxinidir}" \
                      -e "keystone_apache_enabled=False" \
                      -e "keystone_mod_wsgi_enabled=False" \
                      {toxinidir}/tests/test.yml -vvvv
@@ -240,8 +183,6 @@ commands =
 [testenv:linters]
 deps =
     {[testenv:ansible]deps}
-setenv =
-    {[testenv:ansible]setenv}
 commands =
     {[testenv:pep8]commands}
     {[testenv:bashate]commands}