Merge "Correct configuration setting using NUMBER_FAKE_NOVA_COMPUTE"
diff --git a/.zuul.yaml b/.zuul.yaml
index f03d30a..a90df2f 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -111,6 +111,12 @@
         '/etc/glusterfs/glusterd.vol': 'logs'
         '/etc/resolv.conf': 'logs'
         '/var/log/unbound.log': 'logs'
+      extensions_to_txt:
+        conf: True
+        log: True
+        localrc: True
+        stackenv: True
+        summary: True
     pre-run: playbooks/pre.yaml
     run: playbooks/devstack.yaml
     post-run: playbooks/post.yaml
@@ -187,11 +193,25 @@
       - playbooks/tox/pre.yaml
     run: playbooks/tox/run.yaml
 
+- job:
+    name: devstack-unit-tests
+    description: |
+      Runs unit tests on devstack project.
+
+      It runs  ``run_tests.sh``.
+    pre-run: playbooks/unit-tests/pre.yaml
+    run: playbooks/unit-tests/run.yaml
+
 - project:
     check:
       jobs:
         - devstack
         - devstack-multinode
+        - devstack-unit-tests
     gate:
       jobs:
         - devstack
+        - devstack-unit-tests
+    experimental:
+      jobs:
+        - nova-next
diff --git a/doc/requirements.txt b/doc/requirements.txt
index e140bc0..f65e9df 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -8,3 +8,4 @@
 blockdiag
 sphinxcontrib-blockdiag
 sphinxcontrib-nwdiag
+zuul-sphinx>=0.2.0
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 780237f..e9708fa 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -26,7 +26,7 @@
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = [ 'openstackdocstheme', 'sphinxcontrib.blockdiag', 'sphinxcontrib.nwdiag' ]
+extensions = [ 'sphinx.ext.autodoc', 'zuul_sphinx', 'openstackdocstheme', 'sphinxcontrib.blockdiag', 'sphinxcontrib.nwdiag' ]
 
 # openstackdocstheme options
 repository_name = 'openstack-dev/devstack'
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 47087c5..9254c23 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -139,6 +139,8 @@
 Enable :doc:`devstack plugins <plugins>` to support additional
 services, features, and configuration not present in base devstack.
 
+Use devstack in your CI with :doc:`Ansible roles <roles>` for Zuul V3.
+
 Get :doc:`the big picture <overview>` of what we are trying to do
 with devstack, and help us by :doc:`contributing to the project
 <hacking>`.
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
index d830507..591e226 100644
--- a/doc/source/plugin-registry.rst
+++ b/doc/source/plugin-registry.rst
@@ -157,6 +157,7 @@
 python-openstacksdk                    `git://git.openstack.org/openstack/python-openstacksdk <https://git.openstack.org/cgit/openstack/python-openstacksdk>`__
 qinling                                `git://git.openstack.org/openstack/qinling <https://git.openstack.org/cgit/openstack/qinling>`__
 rally                                  `git://git.openstack.org/openstack/rally <https://git.openstack.org/cgit/openstack/rally>`__
+rally-openstack                        `git://git.openstack.org/openstack/rally-openstack <https://git.openstack.org/cgit/openstack/rally-openstack>`__
 sahara                                 `git://git.openstack.org/openstack/sahara <https://git.openstack.org/cgit/openstack/sahara>`__
 sahara-dashboard                       `git://git.openstack.org/openstack/sahara-dashboard <https://git.openstack.org/cgit/openstack/sahara-dashboard>`__
 scalpels                               `git://git.openstack.org/openstack/scalpels <https://git.openstack.org/cgit/openstack/scalpels>`__
@@ -169,6 +170,7 @@
 tacker                                 `git://git.openstack.org/openstack/tacker <https://git.openstack.org/cgit/openstack/tacker>`__
 tap-as-a-service                       `git://git.openstack.org/openstack/tap-as-a-service <https://git.openstack.org/cgit/openstack/tap-as-a-service>`__
 tap-as-a-service-dashboard             `git://git.openstack.org/openstack/tap-as-a-service-dashboard <https://git.openstack.org/cgit/openstack/tap-as-a-service-dashboard>`__
+tatu                                   `git://git.openstack.org/openstack/tatu <https://git.openstack.org/cgit/openstack/tatu>`__
 telemetry-tempest-plugin               `git://git.openstack.org/openstack/telemetry-tempest-plugin <https://git.openstack.org/cgit/openstack/telemetry-tempest-plugin>`__
 tricircle                              `git://git.openstack.org/openstack/tricircle <https://git.openstack.org/cgit/openstack/tricircle>`__
 trio2o                                 `git://git.openstack.org/openstack/trio2o <https://git.openstack.org/cgit/openstack/trio2o>`__
diff --git a/doc/source/roles.rst b/doc/source/roles.rst
new file mode 100644
index 0000000..5baa1e4
--- /dev/null
+++ b/doc/source/roles.rst
@@ -0,0 +1,4 @@
+Roles
+=====
+
+.. zuul:autoroles::
diff --git a/lib/cinder b/lib/cinder
index 96a7d5b..75486ad 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -372,7 +372,7 @@
             get_or_create_endpoint \
                 "block-storage" \
                 "$REGION_NAME" \
-                "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/"
+                "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/volume/"
 
             get_or_create_endpoint \
                 "volume" \
diff --git a/lib/nova b/lib/nova
index a6ad907..8691c8e 100644
--- a/lib/nova
+++ b/lib/nova
@@ -82,6 +82,10 @@
     NOVA_SERVICE_PROTOCOL="https"
 fi
 
+# Whether to use TLS for comms between the VNC/SPICE/serial proxy
+# services and the compute node
+NOVA_CONSOLE_PROXY_COMPUTE_TLS=${NOVA_CONSOLE_PROXY_COMPUTE_TLS:-False}
+
 # Public facing bits
 NOVA_SERVICE_HOST=${NOVA_SERVICE_HOST:-$SERVICE_HOST}
 NOVA_SERVICE_PORT=${NOVA_SERVICE_PORT:-8774}
@@ -197,6 +201,13 @@
     return 1
 }
 
+# is_nova_console_proxy_compute_tls_enabled() - Test if the Nova Console Proxy
+# service has TLS enabled
+function is_nova_console_proxy_compute_tls_enabled {
+    [[ ${NOVA_CONSOLE_PROXY_COMPUTE_TLS} = "True" ]] && return 0
+    return 1
+}
+
 # Helper to clean iptables rules
 function clean_iptables {
     # Delete rules
@@ -524,6 +535,17 @@
         iniset $NOVA_CONF vnc server_proxyclient_address "$VNCSERVER_PROXYCLIENT_ADDRESS"
         iniset $NOVA_CONF vnc novncproxy_host "$NOVA_SERVICE_LISTEN_ADDRESS"
         iniset $NOVA_CONF vnc xvpvncproxy_host "$NOVA_SERVICE_LISTEN_ADDRESS"
+
+        if is_nova_console_proxy_compute_tls_enabled ; then
+            iniset $NOVA_CONF vnc auth_schemes "vencrypt"
+            iniset $NOVA_CONF vnc vencrypt_client_key "/etc/pki/nova-novnc/client-key.pem"
+            iniset $NOVA_CONF vnc vencrypt_client_cert "/etc/pki/nova-novnc/client-cert.pem"
+            iniset $NOVA_CONF vnc vencrypt_ca_certs "/etc/pki/nova-novnc/ca-cert.pem"
+
+            sudo mkdir -p /etc/pki/nova-novnc
+            deploy_int_CA /etc/pki/nova-novnc/ca-cert.pem
+            deploy_int_cert /etc/pki/nova-novnc/client-cert.pem /etc/pki/nova-novnc/client-key.pem
+        fi
     else
         iniset $NOVA_CONF vnc enabled false
     fi
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index 147ed8b..fcb4777 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -149,6 +149,18 @@
         fi
     fi
 
+    if is_nova_console_proxy_compute_tls_enabled ; then
+        if is_service_enabled n-novnc ; then
+            echo "vnc_tls = 1" | sudo tee -a $QEMU_CONF
+            echo "vnc_tls_x509_verify = 1" | sudo tee -a $QEMU_CONF
+
+            sudo mkdir -p /etc/pki/libvirt-vnc
+            sudo chown libvirt-qemu:libvirt-qemu /etc/pki/libvirt-vnc
+            deploy_int_CA /etc/pki/libvirt-vnc/ca-cert.pem
+            deploy_int_cert /etc/pki/libvirt-vnc/server-cert.pem /etc/pki/libvirt-vnc/server-key.pem
+        fi
+    fi
+
     # Service needs to be started on redhat/fedora -- do a restart for
     # sanity after fiddling the config.
     restart_service $LIBVIRT_DAEMON
diff --git a/lib/tls b/lib/tls
index 0bc389b..a72b708 100644
--- a/lib/tls
+++ b/lib/tls
@@ -340,6 +340,24 @@
     fi
 }
 
+# Deploy the service cert & key to a service specific
+# location
+function deploy_int_cert {
+    local cert_target_file=$1
+    local key_target_file=$2
+
+    sudo cp "$INT_CA_DIR/$DEVSTACK_CERT_NAME.crt" "$cert_target_file"
+    sudo cp "$INT_CA_DIR/private/$DEVSTACK_CERT_NAME.key" "$key_target_file"
+}
+
+# Deploy the intermediate CA cert bundle file to a service
+# specific location
+function deploy_int_CA {
+    local ca_target_file=$1
+
+    sudo cp "$INT_CA_DIR/ca-chain.pem" "$ca_target_file"
+}
+
 # If a non-system python-requests is installed then it will use the
 # built-in CA certificate store rather than the distro-specific
 # CA certificate store. Detect this and symlink to the correct
diff --git a/playbooks/post.yaml b/playbooks/post.yaml
index d8ee99e..9e66f20 100644
--- a/playbooks/post.yaml
+++ b/playbooks/post.yaml
@@ -24,12 +24,6 @@
     # capture-system-logs should be the last role before stage-output
     - capture-system-logs
     - role: stage-output
-      extensions_to_txt:
-        - conf
-        - log
-        - localrc
-        - stackenv
-        - summary
     # NOTE(andreaf) We need fetch-devstack-log-dir only as long as the base job
     # starts pulling logs for us from {{ ansible_user_dir }}/logs.
     # Meanwhile we already store things in ansible_user_dir and use
diff --git a/playbooks/unit-tests/pre.yaml b/playbooks/unit-tests/pre.yaml
new file mode 100644
index 0000000..cfa1676
--- /dev/null
+++ b/playbooks/unit-tests/pre.yaml
@@ -0,0 +1,13 @@
+- hosts: all
+
+  tasks:
+
+    - name: Install prerequisites
+      shell:
+        chdir: '{{ zuul.project.src_dir }}'
+        executable: /bin/bash
+        cmd: |
+          set -e
+          set -x
+          echo "IPV4_ADDRS_SAFE_TO_USE=10.1.0.0/20" >> localrc
+          ./tools/install_prereqs.sh
diff --git a/playbooks/unit-tests/run.yaml b/playbooks/unit-tests/run.yaml
new file mode 100644
index 0000000..181521f
--- /dev/null
+++ b/playbooks/unit-tests/run.yaml
@@ -0,0 +1,12 @@
+- hosts: all
+
+  tasks:
+
+    - name: Run run_tests.sh
+      shell:
+        chdir: '{{ zuul.project.src_dir }}'
+        executable: /bin/bash
+        cmd: |
+          set -e
+          set -x
+          ./run_tests.sh
diff --git a/roles/apache-logs-conf/tasks/main.yaml b/roles/apache-logs-conf/tasks/main.yaml
index 7fd490e..bcc1353 100644
--- a/roles/apache-logs-conf/tasks/main.yaml
+++ b/roles/apache-logs-conf/tasks/main.yaml
@@ -10,11 +10,13 @@
       path: "/var/log/apache2"
       file_type: any
     register: debian_suse_apache_logs
+
   - name: Dereference files
     stat:
       path: "{{ item.path }}"
     with_items: "{{ debian_suse_apache_logs.files }}"
     register: debian_suse_apache_deref_logs
+
   - name: Create hard links
     file:
       src: "{{ item.stat.lnk_source | default(item.stat.path) }}"
@@ -24,6 +26,7 @@
     when:
       - item.stat.isreg or item.stat.islnk
   when: ansible_os_family in ('Debian', 'Suse')
+  no_log: true
 
 - name: Link apache logs on RedHat
   block:
@@ -32,11 +35,13 @@
       path: "/var/log/httpd"
       file_type: any
     register: redhat_apache_logs
+
   - name: Dereference files
     stat:
       path: "{{ item.path }}"
     with_items: "{{ redhat_apache_logs.files }}"
     register: redhat_apache_deref_logs
+
   - name: Create hard links
     file:
       src: "{{ item.stat.lnk_source | default(item.stat.path) }}"
@@ -46,6 +51,7 @@
     when:
       - item.stat.isreg or item.stat.islnk
   when: ansible_os_family == 'Redhat'
+  no_log: true
 
 - name: Ensure {{ stage_dir }}/apache_config apache_config exists
   file:
@@ -57,19 +63,21 @@
     apache_config_paths:
       'Debian': '/etc/apache2/sites-enabled/'
       'Suse': '/etc/apache2/conf.d/'
-      'Redhat': '/etc/httpd/conf.d/'
+      'RedHat': '/etc/httpd/conf.d/'
 
 - name: Discover configurations
   find:
     path: "{{ apache_config_paths[ansible_os_family] }}"
     file_type: any
   register: apache_configs
+  no_log: true
 
 - name: Dereference configurations
   stat:
     path: "{{ item.path }}"
   with_items: "{{ apache_configs.files }}"
   register: apache_configs_deref
+  no_log: true
 
 - name: Link configurations
   file:
@@ -78,3 +86,4 @@
     state: hard
   with_items: "{{ apache_configs_deref.results }}"
   when: item.stat.isreg or item.stat.islnk
+  no_log: true
diff --git a/roles/devstack-project-conf/tasks/main.yaml b/roles/devstack-project-conf/tasks/main.yaml
index 9c6e06b..917cdbc 100644
--- a/roles/devstack-project-conf/tasks/main.yaml
+++ b/roles/devstack-project-conf/tasks/main.yaml
@@ -8,6 +8,7 @@
     path: "/etc/{{ item.value.short_name }}"
   with_dict: "{{ zuul.projects }}"
   register: project_configs
+  no_log: true
 
 - name: Copy configuration files
   command: cp -pRL {{ item.stat.path }} {{ stage_dir }}/etc/{{ item.item.value.short_name }}
diff --git a/roles/run-devstack/README.rst b/roles/run-devstack/README.rst
index e53f060..d77eb15 100644
--- a/roles/run-devstack/README.rst
+++ b/roles/run-devstack/README.rst
@@ -6,9 +6,3 @@
    :default: /opt/stack
 
    The devstack base directory.
-
-.. zuul:rolevar:: devstack_early_log
-   :default: /opt/stack/log/devstack-early.txt
-
-   The full devstack log that includes the whatever stack.sh logs before
-   the LOGFILE variable in local.conf is honoured.
diff --git a/roles/run-devstack/defaults/main.yaml b/roles/run-devstack/defaults/main.yaml
index dc4528f..fea05c8 100644
--- a/roles/run-devstack/defaults/main.yaml
+++ b/roles/run-devstack/defaults/main.yaml
@@ -1,2 +1 @@
 devstack_base_dir: /opt/stack
-devstack_early_log: /opt/stack/logs/devstack-early.txt
diff --git a/roles/run-devstack/tasks/main.yaml b/roles/run-devstack/tasks/main.yaml
index f532129..64f769c 100644
--- a/roles/run-devstack/tasks/main.yaml
+++ b/roles/run-devstack/tasks/main.yaml
@@ -1,5 +1,5 @@
 - name: Run devstack
-  shell: ./stack.sh 2>&1 {{ devstack_early_log }}
+  shell: ./stack.sh 2>&1
   args:
     chdir: "{{devstack_base_dir}}/devstack"
   become: true
diff --git a/stackrc b/stackrc
index c243d27..b7105d3 100644
--- a/stackrc
+++ b/stackrc
@@ -13,6 +13,18 @@
 # Source required DevStack functions and globals
 source $RC_DIR/functions
 
+# Set the target branch. This is used so that stable branching
+# does not need to update each repo below.
+TARGET_BRANCH=master
+
+# Cycle trailing projects need to branch later than the others.
+TRAILING_TARGET_BRANCH=master
+
+# And some repos do not create stable branches, so this is used
+# to make it explicit and avoid accidentally setting to a stable
+# branch.
+BRANCHLESS_TARGET_BRANCH=master
+
 # Destination path for installation
 DEST=/opt/stack
 
@@ -256,35 +268,35 @@
 
 # block storage service
 CINDER_REPO=${CINDER_REPO:-${GIT_BASE}/openstack/cinder.git}
-CINDER_BRANCH=${CINDER_BRANCH:-master}
+CINDER_BRANCH=${CINDER_BRANCH:-$TARGET_BRANCH}
 
 # image catalog service
 GLANCE_REPO=${GLANCE_REPO:-${GIT_BASE}/openstack/glance.git}
-GLANCE_BRANCH=${GLANCE_BRANCH:-master}
+GLANCE_BRANCH=${GLANCE_BRANCH:-$TARGET_BRANCH}
 
 # django powered web control panel for openstack
 HORIZON_REPO=${HORIZON_REPO:-${GIT_BASE}/openstack/horizon.git}
-HORIZON_BRANCH=${HORIZON_BRANCH:-master}
+HORIZON_BRANCH=${HORIZON_BRANCH:-$TARGET_BRANCH}
 
 # unified auth system (manages accounts/tokens)
 KEYSTONE_REPO=${KEYSTONE_REPO:-${GIT_BASE}/openstack/keystone.git}
-KEYSTONE_BRANCH=${KEYSTONE_BRANCH:-master}
+KEYSTONE_BRANCH=${KEYSTONE_BRANCH:-$TARGET_BRANCH}
 
 # neutron service
 NEUTRON_REPO=${NEUTRON_REPO:-${GIT_BASE}/openstack/neutron.git}
-NEUTRON_BRANCH=${NEUTRON_BRANCH:-master}
+NEUTRON_BRANCH=${NEUTRON_BRANCH:-$TARGET_BRANCH}
 
 # neutron fwaas service
 NEUTRON_FWAAS_REPO=${NEUTRON_FWAAS_REPO:-${GIT_BASE}/openstack/neutron-fwaas.git}
-NEUTRON_FWAAS_BRANCH=${NEUTRON_FWAAS_BRANCH:-master}
+NEUTRON_FWAAS_BRANCH=${NEUTRON_FWAAS_BRANCH:-$TARGET_BRANCH}
 
 # compute service
 NOVA_REPO=${NOVA_REPO:-${GIT_BASE}/openstack/nova.git}
-NOVA_BRANCH=${NOVA_BRANCH:-master}
+NOVA_BRANCH=${NOVA_BRANCH:-$TARGET_BRANCH}
 
 # object storage service
 SWIFT_REPO=${SWIFT_REPO:-${GIT_BASE}/openstack/swift.git}
-SWIFT_BRANCH=${SWIFT_BRANCH:-master}
+SWIFT_BRANCH=${SWIFT_BRANCH:-$TARGET_BRANCH}
 
 ##############
 #
@@ -294,11 +306,11 @@
 
 # consolidated openstack requirements
 REQUIREMENTS_REPO=${REQUIREMENTS_REPO:-${GIT_BASE}/openstack/requirements.git}
-REQUIREMENTS_BRANCH=${REQUIREMENTS_BRANCH:-master}
+REQUIREMENTS_BRANCH=${REQUIREMENTS_BRANCH:-$TARGET_BRANCH}
 
 # Tempest test suite
 TEMPEST_REPO=${TEMPEST_REPO:-${GIT_BASE}/openstack/tempest.git}
-TEMPEST_BRANCH=${TEMPEST_BRANCH:-master}
+TEMPEST_BRANCH=${TEMPEST_BRANCH:-$BRANCHLESS_TARGET_BRANCH}
 
 
 ##############
@@ -310,56 +322,56 @@
 
 # volume client
 GITREPO["python-cinderclient"]=${CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-cinderclient.git}
-GITBRANCH["python-cinderclient"]=${CINDERCLIENT_BRANCH:-master}
+GITBRANCH["python-cinderclient"]=${CINDERCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # os-brick client for local volume attachement
 GITREPO["python-brick-cinderclient-ext"]=${BRICK_CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-brick-cinderclient-ext.git}
-GITBRANCH["python-brick-cinderclient-ext"]=${BRICK_CINDERCLIENT_BRANCH:-master}
+GITBRANCH["python-brick-cinderclient-ext"]=${BRICK_CINDERCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # python barbican client library
 GITREPO["python-barbicanclient"]=${BARBICANCLIENT_REPO:-${GIT_BASE}/openstack/python-barbicanclient.git}
-GITBRANCH["python-barbicanclient"]=${BARBICANCLIENT_BRANCH:-master}
+GITBRANCH["python-barbicanclient"]=${BARBICANCLIENT_BRANCH:-$TARGET_BRANCH}
 GITDIR["python-barbicanclient"]=$DEST/python-barbicanclient
 
 # python glance client library
 GITREPO["python-glanceclient"]=${GLANCECLIENT_REPO:-${GIT_BASE}/openstack/python-glanceclient.git}
-GITBRANCH["python-glanceclient"]=${GLANCECLIENT_BRANCH:-master}
+GITBRANCH["python-glanceclient"]=${GLANCECLIENT_BRANCH:-$TARGET_BRANCH}
 
 # ironic client
 GITREPO["python-ironicclient"]=${IRONICCLIENT_REPO:-${GIT_BASE}/openstack/python-ironicclient.git}
-GITBRANCH["python-ironicclient"]=${IRONICCLIENT_BRANCH:-master}
+GITBRANCH["python-ironicclient"]=${IRONICCLIENT_BRANCH:-$TARGET_BRANCH}
 # ironic plugin is out of tree, but nova uses it. set GITDIR here.
 GITDIR["python-ironicclient"]=$DEST/python-ironicclient
 
 # the base authentication plugins that clients use to authenticate
 GITREPO["keystoneauth"]=${KEYSTONEAUTH_REPO:-${GIT_BASE}/openstack/keystoneauth.git}
-GITBRANCH["keystoneauth"]=${KEYSTONEAUTH_BRANCH:-master}
+GITBRANCH["keystoneauth"]=${KEYSTONEAUTH_BRANCH:-$TARGET_BRANCH}
 
 # python keystone client library to nova that horizon uses
 GITREPO["python-keystoneclient"]=${KEYSTONECLIENT_REPO:-${GIT_BASE}/openstack/python-keystoneclient.git}
-GITBRANCH["python-keystoneclient"]=${KEYSTONECLIENT_BRANCH:-master}
+GITBRANCH["python-keystoneclient"]=${KEYSTONECLIENT_BRANCH:-$TARGET_BRANCH}
 
 # neutron client
 GITREPO["python-neutronclient"]=${NEUTRONCLIENT_REPO:-${GIT_BASE}/openstack/python-neutronclient.git}
-GITBRANCH["python-neutronclient"]=${NEUTRONCLIENT_BRANCH:-master}
+GITBRANCH["python-neutronclient"]=${NEUTRONCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # python client library to nova that horizon (and others) use
 GITREPO["python-novaclient"]=${NOVACLIENT_REPO:-${GIT_BASE}/openstack/python-novaclient.git}
-GITBRANCH["python-novaclient"]=${NOVACLIENT_BRANCH:-master}
+GITBRANCH["python-novaclient"]=${NOVACLIENT_BRANCH:-$TARGET_BRANCH}
 
 # python swift client library
 GITREPO["python-swiftclient"]=${SWIFTCLIENT_REPO:-${GIT_BASE}/openstack/python-swiftclient.git}
-GITBRANCH["python-swiftclient"]=${SWIFTCLIENT_BRANCH:-master}
+GITBRANCH["python-swiftclient"]=${SWIFTCLIENT_BRANCH:-$TARGET_BRANCH}
 
 # consolidated openstack python client
 GITREPO["python-openstackclient"]=${OPENSTACKCLIENT_REPO:-${GIT_BASE}/openstack/python-openstackclient.git}
-GITBRANCH["python-openstackclient"]=${OPENSTACKCLIENT_BRANCH:-master}
+GITBRANCH["python-openstackclient"]=${OPENSTACKCLIENT_BRANCH:-$TARGET_BRANCH}
 # this doesn't exist in a lib file, so set it here
 GITDIR["python-openstackclient"]=$DEST/python-openstackclient
 
 # placement-api CLI
 GITREPO["osc-placement"]=${OSC_PLACEMENT_REPO:-${GIT_BASE}/openstack/osc-placement.git}
-GITBRANCH["osc-placement"]=${OSC_PLACEMENT_BRANCH:-master}
+GITBRANCH["osc-placement"]=${OSC_PLACEMENT_BRANCH:-$TARGET_BRANCH}
 
 
 ###################
@@ -371,119 +383,119 @@
 
 # castellan key manager interface
 GITREPO["castellan"]=${CASTELLAN_REPO:-${GIT_BASE}/openstack/castellan.git}
-GITBRANCH["castellan"]=${CASTELLAN_BRANCH:-master}
+GITBRANCH["castellan"]=${CASTELLAN_BRANCH:-$TARGET_BRANCH}
 
 # cliff command line framework
 GITREPO["cliff"]=${CLIFF_REPO:-${GIT_BASE}/openstack/cliff.git}
-GITBRANCH["cliff"]=${CLIFF_BRANCH:-master}
+GITBRANCH["cliff"]=${CLIFF_BRANCH:-$TARGET_BRANCH}
 
 # async framework/helpers
 GITREPO["futurist"]=${FUTURIST_REPO:-${GIT_BASE}/openstack/futurist.git}
-GITBRANCH["futurist"]=${FUTURIST_BRANCH:-master}
+GITBRANCH["futurist"]=${FUTURIST_BRANCH:-$TARGET_BRANCH}
 
 # debtcollector deprecation framework/helpers
 GITREPO["debtcollector"]=${DEBTCOLLECTOR_REPO:-${GIT_BASE}/openstack/debtcollector.git}
-GITBRANCH["debtcollector"]=${DEBTCOLLECTOR_BRANCH:-master}
+GITBRANCH["debtcollector"]=${DEBTCOLLECTOR_BRANCH:-$TARGET_BRANCH}
 
 # helpful state machines
 GITREPO["automaton"]=${AUTOMATON_REPO:-${GIT_BASE}/openstack/automaton.git}
-GITBRANCH["automaton"]=${AUTOMATON_BRANCH:-master}
+GITBRANCH["automaton"]=${AUTOMATON_BRANCH:-$TARGET_BRANCH}
 
 # oslo.cache
 GITREPO["oslo.cache"]=${OSLOCACHE_REPO:-${GIT_BASE}/openstack/oslo.cache.git}
-GITBRANCH["oslo.cache"]=${OSLOCACHE_BRANCH:-master}
+GITBRANCH["oslo.cache"]=${OSLOCACHE_BRANCH:-$TARGET_BRANCH}
 
 # oslo.concurrency
 GITREPO["oslo.concurrency"]=${OSLOCON_REPO:-${GIT_BASE}/openstack/oslo.concurrency.git}
-GITBRANCH["oslo.concurrency"]=${OSLOCON_BRANCH:-master}
+GITBRANCH["oslo.concurrency"]=${OSLOCON_BRANCH:-$TARGET_BRANCH}
 
 # oslo.config
 GITREPO["oslo.config"]=${OSLOCFG_REPO:-${GIT_BASE}/openstack/oslo.config.git}
-GITBRANCH["oslo.config"]=${OSLOCFG_BRANCH:-master}
+GITBRANCH["oslo.config"]=${OSLOCFG_BRANCH:-$TARGET_BRANCH}
 
 # oslo.context
 GITREPO["oslo.context"]=${OSLOCTX_REPO:-${GIT_BASE}/openstack/oslo.context.git}
-GITBRANCH["oslo.context"]=${OSLOCTX_BRANCH:-master}
+GITBRANCH["oslo.context"]=${OSLOCTX_BRANCH:-$TARGET_BRANCH}
 
 # oslo.db
 GITREPO["oslo.db"]=${OSLODB_REPO:-${GIT_BASE}/openstack/oslo.db.git}
-GITBRANCH["oslo.db"]=${OSLODB_BRANCH:-master}
+GITBRANCH["oslo.db"]=${OSLODB_BRANCH:-$TARGET_BRANCH}
 
 # oslo.i18n
 GITREPO["oslo.i18n"]=${OSLOI18N_REPO:-${GIT_BASE}/openstack/oslo.i18n.git}
-GITBRANCH["oslo.i18n"]=${OSLOI18N_BRANCH:-master}
+GITBRANCH["oslo.i18n"]=${OSLOI18N_BRANCH:-$TARGET_BRANCH}
 
 # oslo.log
 GITREPO["oslo.log"]=${OSLOLOG_REPO:-${GIT_BASE}/openstack/oslo.log.git}
-GITBRANCH["oslo.log"]=${OSLOLOG_BRANCH:-master}
+GITBRANCH["oslo.log"]=${OSLOLOG_BRANCH:-$TARGET_BRANCH}
 
 # oslo.messaging
 GITREPO["oslo.messaging"]=${OSLOMSG_REPO:-${GIT_BASE}/openstack/oslo.messaging.git}
-GITBRANCH["oslo.messaging"]=${OSLOMSG_BRANCH:-master}
+GITBRANCH["oslo.messaging"]=${OSLOMSG_BRANCH:-$TARGET_BRANCH}
 
 # oslo.middleware
 GITREPO["oslo.middleware"]=${OSLOMID_REPO:-${GIT_BASE}/openstack/oslo.middleware.git}
-GITBRANCH["oslo.middleware"]=${OSLOMID_BRANCH:-master}
+GITBRANCH["oslo.middleware"]=${OSLOMID_BRANCH:-$TARGET_BRANCH}
 
 # oslo.policy
 GITREPO["oslo.policy"]=${OSLOPOLICY_REPO:-${GIT_BASE}/openstack/oslo.policy.git}
-GITBRANCH["oslo.policy"]=${OSLOPOLICY_BRANCH:-master}
+GITBRANCH["oslo.policy"]=${OSLOPOLICY_BRANCH:-$TARGET_BRANCH}
 
 # oslo.privsep
 GITREPO["oslo.privsep"]=${OSLOPRIVSEP_REPO:-${GIT_BASE}/openstack/oslo.privsep.git}
-GITBRANCH["oslo.privsep"]=${OSLOPRIVSEP_BRANCH:-master}
+GITBRANCH["oslo.privsep"]=${OSLOPRIVSEP_BRANCH:-$TARGET_BRANCH}
 
 # oslo.reports
 GITREPO["oslo.reports"]=${OSLOREPORTS_REPO:-${GIT_BASE}/openstack/oslo.reports.git}
-GITBRANCH["oslo.reports"]=${OSLOREPORTS_BRANCH:-master}
+GITBRANCH["oslo.reports"]=${OSLOREPORTS_BRANCH:-$TARGET_BRANCH}
 
 # oslo.rootwrap
 GITREPO["oslo.rootwrap"]=${OSLORWRAP_REPO:-${GIT_BASE}/openstack/oslo.rootwrap.git}
-GITBRANCH["oslo.rootwrap"]=${OSLORWRAP_BRANCH:-master}
+GITBRANCH["oslo.rootwrap"]=${OSLORWRAP_BRANCH:-$TARGET_BRANCH}
 
 # oslo.serialization
 GITREPO["oslo.serialization"]=${OSLOSERIALIZATION_REPO:-${GIT_BASE}/openstack/oslo.serialization.git}
-GITBRANCH["oslo.serialization"]=${OSLOSERIALIZATION_BRANCH:-master}
+GITBRANCH["oslo.serialization"]=${OSLOSERIALIZATION_BRANCH:-$TARGET_BRANCH}
 
 # oslo.service
 GITREPO["oslo.service"]=${OSLOSERVICE_REPO:-${GIT_BASE}/openstack/oslo.service.git}
-GITBRANCH["oslo.service"]=${OSLOSERVICE_BRANCH:-master}
+GITBRANCH["oslo.service"]=${OSLOSERVICE_BRANCH:-$TARGET_BRANCH}
 
 # oslo.utils
 GITREPO["oslo.utils"]=${OSLOUTILS_REPO:-${GIT_BASE}/openstack/oslo.utils.git}
-GITBRANCH["oslo.utils"]=${OSLOUTILS_BRANCH:-master}
+GITBRANCH["oslo.utils"]=${OSLOUTILS_BRANCH:-$TARGET_BRANCH}
 
 # oslo.versionedobjects
 GITREPO["oslo.versionedobjects"]=${OSLOVERSIONEDOBJECTS_REPO:-${GIT_BASE}/openstack/oslo.versionedobjects.git}
-GITBRANCH["oslo.versionedobjects"]=${OSLOVERSIONEDOBJECTS_BRANCH:-master}
+GITBRANCH["oslo.versionedobjects"]=${OSLOVERSIONEDOBJECTS_BRANCH:-$TARGET_BRANCH}
 
 # oslo.vmware
 GITREPO["oslo.vmware"]=${OSLOVMWARE_REPO:-${GIT_BASE}/openstack/oslo.vmware.git}
-GITBRANCH["oslo.vmware"]=${OSLOVMWARE_BRANCH:-master}
+GITBRANCH["oslo.vmware"]=${OSLOVMWARE_BRANCH:-$TARGET_BRANCH}
 
 # osprofiler
 GITREPO["osprofiler"]=${OSPROFILER_REPO:-${GIT_BASE}/openstack/osprofiler.git}
-GITBRANCH["osprofiler"]=${OSPROFILER_BRANCH:-master}
+GITBRANCH["osprofiler"]=${OSPROFILER_BRANCH:-$TARGET_BRANCH}
 
 # pycadf auditing library
 GITREPO["pycadf"]=${PYCADF_REPO:-${GIT_BASE}/openstack/pycadf.git}
-GITBRANCH["pycadf"]=${PYCADF_BRANCH:-master}
+GITBRANCH["pycadf"]=${PYCADF_BRANCH:-$TARGET_BRANCH}
 
 # stevedore plugin manager
 GITREPO["stevedore"]=${STEVEDORE_REPO:-${GIT_BASE}/openstack/stevedore.git}
-GITBRANCH["stevedore"]=${STEVEDORE_BRANCH:-master}
+GITBRANCH["stevedore"]=${STEVEDORE_BRANCH:-$TARGET_BRANCH}
 
 # taskflow plugin manager
 GITREPO["taskflow"]=${TASKFLOW_REPO:-${GIT_BASE}/openstack/taskflow.git}
-GITBRANCH["taskflow"]=${TASKFLOW_BRANCH:-master}
+GITBRANCH["taskflow"]=${TASKFLOW_BRANCH:-$TARGET_BRANCH}
 
 # tooz plugin manager
 GITREPO["tooz"]=${TOOZ_REPO:-${GIT_BASE}/openstack/tooz.git}
-GITBRANCH["tooz"]=${TOOZ_BRANCH:-master}
+GITBRANCH["tooz"]=${TOOZ_BRANCH:-$TARGET_BRANCH}
 
 # pbr drives the setuptools configs
 GITREPO["pbr"]=${PBR_REPO:-${GIT_BASE}/openstack-dev/pbr.git}
-GITBRANCH["pbr"]=${PBR_BRANCH:-master}
+GITBRANCH["pbr"]=${PBR_BRANCH:-$TARGET_BRANCH}
 
 
 ##################
@@ -494,65 +506,65 @@
 
 # cursive library
 GITREPO["cursive"]=${CURSIVE_REPO:-${GIT_BASE}/openstack/cursive.git}
-GITBRANCH["cursive"]=${CURSIVE_BRANCH:-master}
+GITBRANCH["cursive"]=${CURSIVE_BRANCH:-$TARGET_BRANCH}
 
 # glance store library
 GITREPO["glance_store"]=${GLANCE_STORE_REPO:-${GIT_BASE}/openstack/glance_store.git}
-GITBRANCH["glance_store"]=${GLANCE_STORE_BRANCH:-master}
+GITBRANCH["glance_store"]=${GLANCE_STORE_BRANCH:-$TARGET_BRANCH}
 
 # keystone middleware
 GITREPO["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_REPO:-${GIT_BASE}/openstack/keystonemiddleware.git}
-GITBRANCH["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_BRANCH:-master}
+GITBRANCH["keystonemiddleware"]=${KEYSTONEMIDDLEWARE_BRANCH:-$TARGET_BRANCH}
 
 # s3 support for swift
 SWIFT3_REPO=${SWIFT3_REPO:-${GIT_BASE}/openstack/swift3.git}
-SWIFT3_BRANCH=${SWIFT3_BRANCH:-master}
+SWIFT3_BRANCH=${SWIFT3_BRANCH:-$TARGET_BRANCH}
 
 # ceilometer middleware
 GITREPO["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_REPO:-${GIT_BASE}/openstack/ceilometermiddleware.git}
-GITBRANCH["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_BRANCH:-master}
+GITBRANCH["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_BRANCH:-$TARGET_BRANCH}
 GITDIR["ceilometermiddleware"]=$DEST/ceilometermiddleware
 
 # os-brick library to manage local volume attaches
 GITREPO["os-brick"]=${OS_BRICK_REPO:-${GIT_BASE}/openstack/os-brick.git}
-GITBRANCH["os-brick"]=${OS_BRICK_BRANCH:-master}
+GITBRANCH["os-brick"]=${OS_BRICK_BRANCH:-$TARGET_BRANCH}
 
 # os-client-config to manage clouds.yaml and friends
 GITREPO["os-client-config"]=${OS_CLIENT_CONFIG_REPO:-${GIT_BASE}/openstack/os-client-config.git}
-GITBRANCH["os-client-config"]=${OS_CLIENT_CONFIG_BRANCH:-master}
+GITBRANCH["os-client-config"]=${OS_CLIENT_CONFIG_BRANCH:-$TARGET_BRANCH}
 GITDIR["os-client-config"]=$DEST/os-client-config
 
 # os-vif library to communicate between Neutron to Nova
 GITREPO["os-vif"]=${OS_VIF_REPO:-${GIT_BASE}/openstack/os-vif.git}
-GITBRANCH["os-vif"]=${OS_VIF_BRANCH:-master}
+GITBRANCH["os-vif"]=${OS_VIF_BRANCH:-$TARGET_BRANCH}
 
 # osc-lib OpenStackClient common lib
 GITREPO["osc-lib"]=${OSC_LIB_REPO:-${GIT_BASE}/openstack/osc-lib.git}
-GITBRANCH["osc-lib"]=${OSC_LIB_BRANCH:-master}
+GITBRANCH["osc-lib"]=${OSC_LIB_BRANCH:-$TARGET_BRANCH}
 
 # python-openstacksdk OpenStack Python SDK
 GITREPO["python-openstacksdk"]=${OPENSTACKSDK_REPO:-${GIT_BASE}/openstack/python-openstacksdk.git}
-GITBRANCH["python-openstacksdk"]=${OPENSTACKSDK_BRANCH:-master}
+GITBRANCH["python-openstacksdk"]=${OPENSTACKSDK_BRANCH:-$TARGET_BRANCH}
 
 # ironic common lib
 GITREPO["ironic-lib"]=${IRONIC_LIB_REPO:-${GIT_BASE}/openstack/ironic-lib.git}
-GITBRANCH["ironic-lib"]=${IRONIC_LIB_BRANCH:-master}
+GITBRANCH["ironic-lib"]=${IRONIC_LIB_BRANCH:-$TARGET_BRANCH}
 # this doesn't exist in a lib file, so set it here
 GITDIR["ironic-lib"]=$DEST/ironic-lib
 
 # diskimage-builder tool
 GITREPO["diskimage-builder"]=${DIB_REPO:-${GIT_BASE}/openstack/diskimage-builder.git}
-GITBRANCH["diskimage-builder"]=${DIB_BRANCH:-master}
+GITBRANCH["diskimage-builder"]=${DIB_BRANCH:-$TARGET_BRANCH}
 GITDIR["diskimage-builder"]=$DEST/diskimage-builder
 
 # neutron-lib library containing neutron stable non-REST interfaces
 GITREPO["neutron-lib"]=${NEUTRON_LIB_REPO:-${GIT_BASE}/openstack/neutron-lib.git}
-GITBRANCH["neutron-lib"]=${NEUTRON_LIB_BRANCH:-master}
+GITBRANCH["neutron-lib"]=${NEUTRON_LIB_BRANCH:-$TARGET_BRANCH}
 GITDIR["neutron-lib"]=$DEST/neutron-lib
 
 # os-traits library for resource provider traits in the placement service
 GITREPO["os-traits"]=${OS_TRAITS_REPO:-${GIT_BASE}/openstack/os-traits.git}
-GITBRANCH["os-traits"]=${OS_TRAITS_BRANCH:-master}
+GITBRANCH["os-traits"]=${OS_TRAITS_BRANCH:-$TARGET_BRANCH}
 
 ##################
 #
@@ -562,19 +574,19 @@
 
 # run-parts script required by os-refresh-config
 DIB_UTILS_REPO=${DIB_UTILS_REPO:-${GIT_BASE}/openstack/dib-utils.git}
-DIB_UTILS_BRANCH=${DIB_UTILS_BRANCH:-master}
+DIB_UTILS_BRANCH=${DIB_UTILS_BRANCH:-$BRANCHLESS_TARGET_BRANCH}
 
 # os-apply-config configuration template tool
 OAC_REPO=${OAC_REPO:-${GIT_BASE}/openstack/os-apply-config.git}
-OAC_BRANCH=${OAC_BRANCH:-master}
+OAC_BRANCH=${OAC_BRANCH:-$TRAILING_TARGET_BRANCH}
 
 # os-collect-config configuration agent
 OCC_REPO=${OCC_REPO:-${GIT_BASE}/openstack/os-collect-config.git}
-OCC_BRANCH=${OCC_BRANCH:-master}
+OCC_BRANCH=${OCC_BRANCH:-$TRAILING_TARGET_BRANCH}
 
 # os-refresh-config configuration run-parts tool
 ORC_REPO=${ORC_REPO:-${GIT_BASE}/openstack/os-refresh-config.git}
-ORC_BRANCH=${ORC_BRANCH:-master}
+ORC_BRANCH=${ORC_BRANCH:-$TRAILING_TARGET_BRANCH}
 
 
 #################
@@ -587,7 +599,7 @@
 
 # ironic python agent
 IRONIC_PYTHON_AGENT_REPO=${IRONIC_PYTHON_AGENT_REPO:-${GIT_BASE}/openstack/ironic-python-agent.git}
-IRONIC_PYTHON_AGENT_BRANCH=${IRONIC_PYTHON_AGENT_BRANCH:-master}
+IRONIC_PYTHON_AGENT_BRANCH=${IRONIC_PYTHON_AGENT_BRANCH:-$TARGET_BRANCH}
 
 # a websockets/html5 or flash powered VNC console for vm instances
 NOVNC_REPO=${NOVNC_REPO:-https://github.com/novnc/noVNC.git}
@@ -595,7 +607,7 @@
 
 # a websockets/html5 or flash powered SPICE console for vm instances
 SPICE_REPO=${SPICE_REPO:-http://anongit.freedesktop.org/git/spice/spice-html5.git}
-SPICE_BRANCH=${SPICE_BRANCH:-master}
+SPICE_BRANCH=${SPICE_BRANCH:-$BRANCHLESS_TARGET_BRANCH}
 
 # Global flag used to configure Tempest and potentially other services if
 # volume multiattach is supported. In Queens, only the libvirt compute driver
diff --git a/tests/test_refs.sh b/tests/test_refs.sh
index 65848cd..0f9aa4a 100755
--- a/tests/test_refs.sh
+++ b/tests/test_refs.sh
@@ -15,10 +15,10 @@
 
 echo "Ensuring we don't have crazy refs"
 
-REFS=`grep BRANCH stackrc | grep -v -- '-master' | grep -v 'NOVNC_BRANCH'`
+REFS=`grep BRANCH stackrc | grep -v 'TARGET_BRANCH' | grep -v 'NOVNC_BRANCH'`
 rc=$?
 if [[ $rc -eq 0 ]]; then
-    echo "Branch defaults must be master. Found:"
+    echo "Branch defaults must be one of the *TARGET_BRANCH values. Found:"
     echo $REFS
     exit 1
 fi