Merge "Ignore local.conf in root of repo"
diff --git a/.zuul.yaml b/.zuul.yaml
index 9aafcdb..02d3df9 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -581,7 +581,7 @@
 - project:
     templates:
       - integrated-gate
-      - integrated-gate-py35
+      - integrated-gate-py3
       - publish-openstack-docs-pti
     check:
       jobs:
diff --git a/clean.sh b/clean.sh
index a29ebd9..d6c6b40 100755
--- a/clean.sh
+++ b/clean.sh
@@ -123,7 +123,7 @@
     sudo rm -rf $LOGDIR
 fi
 
-# Clean out the sytemd user unit files if systemd was used.
+# Clean out the systemd user unit files if systemd was used.
 if [[ "$USE_SYSTEMD" = "True" ]]; then
     sudo find $SYSTEMD_DIR -type f -name '*devstack@*service' -delete
     # Make systemd aware of the deletion.
diff --git a/doc/source/guides/devstack-with-ldap.rst b/doc/source/guides/devstack-with-ldap.rst
index ec41141..4c54723 100644
--- a/doc/source/guides/devstack-with-ldap.rst
+++ b/doc/source/guides/devstack-with-ldap.rst
@@ -12,14 +12,14 @@
 LDAP support in keystone is read-only. You can use it to back an entire
 OpenStack deployment to a single LDAP server, or you can use it to back
 separate LDAP servers to specific keystone domains. Users within those domains
-will can authenticate against keystone, assume role assignments, and interact
-with other OpenStack services.
+can authenticate against keystone, assume role assignments, and interact with
+other OpenStack services.
 
 Configuration
 =============
 
 To deploy an OpenLDAP server, make sure ``ldap`` is added to the list of
-``ENABLED_SERVICES``::
+``ENABLED_SERVICES`` in the ``local.conf`` file::
 
     enable_service ldap
 
@@ -35,9 +35,9 @@
 
 At this point, devstack should have everything it needs to deploy OpenLDAP,
 bootstrap it with a minimal set of users, and configure it to back to a domain
-in keystone::
+in keystone. You can do this by running the ``stack.sh`` script::
 
-    ./stack.sh
+    $ ./stack.sh
 
 Once ``stack.sh`` completes, you should have a running keystone deployment with
 a basic set of users. It is important to note that not all users will live
@@ -63,7 +63,7 @@
 To list all users in LDAP directly, you can use ``ldapsearch`` with the LDAP
 user bootstrapped by devstack::
 
-    ldapsearch -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+    $ ldapsearch -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
         -H ldap://localhost -b dc=openstack,dc=org
 
 As you can see, devstack creates an OpenStack domain called ``openstack.org``
@@ -93,7 +93,7 @@
 
 Now, we use the ``Manager`` user to create a user for Peter in LDAP::
 
-    ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+    $ ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
         -H ldap://localhost -c -f peter.ldif.in
 
 We should be able to assign Peter roles on projects. After Peter has some level
@@ -125,7 +125,7 @@
 We can use the same basic steps to remove users from LDAP, but instead of using
 LDIFs, we can just pass the ``dn`` of the user we want to delete::
 
-    ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+    $ ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
         -H ldap://localhost cn=peter,ou=Users,dc=openstack,dc=org
 
 Group Management
@@ -153,7 +153,7 @@
 We can create the group using the same ``ldapadd`` command as we did with
 users::
 
-    ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+    $ ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
         -H ldap://localhost -c -f guardian-group.ldif.in
 
 If we check the group membership in Horizon, we'll see that only Peter is a
@@ -167,7 +167,7 @@
 
 Just like users, groups can be deleted using the ``dn``::
 
-    ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+    $ ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
         -H ldap://localhost cn=guardians,ou=UserGroups,dc=openstack,dc=org
 
 Note that this operation will not remove users within that group. It will only
diff --git a/doc/source/plugins.rst b/doc/source/plugins.rst
index 89b9381..c694729 100644
--- a/doc/source/plugins.rst
+++ b/doc/source/plugins.rst
@@ -58,7 +58,7 @@
   plugin's name, which is the name that should be used by users on
   "enable_plugin" lines.  It should generally be the last component of
   the git repo path (e.g., if the plugin's repo is
-  openstack/devstack-foo, then the name here should be "foo") ::
+  openstack/foo, then the name here should be "foo") ::
 
     define_plugin <YOUR PLUGIN>
 
@@ -148,7 +148,7 @@
 
 ``devstack/settings``::
 
-    # settings file for template
+  # settings file for template
   enable_service template
 
 
diff --git a/functions b/functions
index 051c816..187ad23 100644
--- a/functions
+++ b/functions
@@ -739,7 +739,7 @@
 
     # Mount the disk with mount options to make it as efficient as possible
     if ! egrep -q ${storage_data_dir} /proc/mounts; then
-        sudo mount -t xfs -o loop,noatime,nodiratime,nobarrier,logbufs=8  \
+        sudo mount -t xfs -o loop,noatime,nodiratime,logbufs=8  \
             ${disk_image} ${storage_data_dir}
     fi
 }
diff --git a/functions-common b/functions-common
index af95bfb..e5962db 100644
--- a/functions-common
+++ b/functions-common
@@ -92,7 +92,6 @@
         --file $CLOUDS_YAML \
         --os-cloud devstack \
         --os-region-name $REGION_NAME \
-        --os-identity-api-version 3 \
         $CA_CERT_ARG \
         --os-auth-url $KEYSTONE_SERVICE_URI \
         --os-username demo \
@@ -104,7 +103,6 @@
         --file $CLOUDS_YAML \
         --os-cloud devstack-alt \
         --os-region-name $REGION_NAME \
-        --os-identity-api-version 3 \
         $CA_CERT_ARG \
         --os-auth-url $KEYSTONE_SERVICE_URI \
         --os-username alt_demo \
@@ -116,13 +114,23 @@
         --file $CLOUDS_YAML \
         --os-cloud devstack-admin \
         --os-region-name $REGION_NAME \
-        --os-identity-api-version 3 \
         $CA_CERT_ARG \
         --os-auth-url $KEYSTONE_SERVICE_URI \
         --os-username admin \
         --os-password $ADMIN_PASSWORD \
         --os-project-name admin
 
+    # admin with a system-scoped token -> devstack-system
+    $PYTHON $TOP_DIR/tools/update_clouds_yaml.py \
+        --file $CLOUDS_YAML \
+        --os-cloud devstack-system-admin \
+        --os-region-name $REGION_NAME \
+        $CA_CERT_ARG \
+        --os-auth-url $KEYSTONE_SERVICE_URI \
+        --os-username admin \
+        --os-password $ADMIN_PASSWORD \
+        --os-system-scope all
+
     # CLean up any old clouds.yaml files we had laying around
     rm -f $(eval echo ~"$STACK_USER")/.config/openstack/clouds.yaml
 }
diff --git a/inc/python b/inc/python
index 5fb7245..f1df101 100644
--- a/inc/python
+++ b/inc/python
@@ -115,13 +115,12 @@
     echo $classifier
 }
 
-# python3_enabled_for() checks if the service(s) specified as arguments are
-# enabled by the user in ``ENABLED_PYTHON3_PACKAGES``.
+# python3_enabled_for() assumes the service(s) specified as arguments are
+# enabled for python 3 unless explicitly disabled. See python3_disabled_for().
 #
 # Multiple services specified as arguments are ``OR``'ed together; the test
 # is a short-circuit boolean, i.e it returns on the first match.
 #
-# Uses global ``ENABLED_PYTHON3_PACKAGES``
 # python3_enabled_for dir [dir ...]
 function python3_enabled_for {
     local xtrace
@@ -132,7 +131,9 @@
     local dirs=$@
     local dir
     for dir in ${dirs}; do
-        [[ ,${ENABLED_PYTHON3_PACKAGES}, =~ ,${dir}, ]] && enabled=0
+        if ! python3_disabled_for "${dir}"; then
+            enabled=0
+        fi
     done
 
     $xtrace
@@ -163,42 +164,29 @@
     return $enabled
 }
 
-# enable_python3_package() adds the repositories passed as argument to the
-# ``ENABLED_PYTHON3_PACKAGES`` list, if they are not already present.
+# enable_python3_package() -- no-op for backwards compatibility
 #
 # For example:
 #   enable_python3_package nova
 #
-# Uses global ``ENABLED_PYTHON3_PACKAGES``
 # enable_python3_package dir [dir ...]
 function enable_python3_package {
     local xtrace
     xtrace=$(set +o | grep xtrace)
     set +o xtrace
 
-    local tmpsvcs="${ENABLED_PYTHON3_PACKAGES}"
-    local python3
-    for dir in $@; do
-        if [[ ,${DISABLED_PYTHON3_PACKAGES}, =~ ,${dir}, ]]; then
-            warn $LINENO "Attempt to enable_python3_package ${dir} when it has been disabled"
-            continue
-        fi
-        if ! python3_enabled_for $dir; then
-            tmpsvcs+=",$dir"
-        fi
-    done
-    ENABLED_PYTHON3_PACKAGES=$(_cleanup_service_list "$tmpsvcs")
+    echo "It is no longer necessary to call enable_python3_package()."
 
     $xtrace
 }
 
-# disable_python3_package() prepares the services passed as argument to be
-# removed from the ``ENABLED_PYTHON3_PACKAGES`` list, if they are present.
+# disable_python3_package() adds the services passed as argument to
+# the ``DISABLED_PYTHON3_PACKAGES`` list.
 #
 # For example:
 #   disable_python3_package swift
 #
-# Uses globals ``ENABLED_PYTHON3_PACKAGES`` and ``DISABLED_PYTHON3_PACKAGES``
+# Uses global ``DISABLED_PYTHON3_PACKAGES``
 # disable_python3_package dir [dir ...]
 function disable_python3_package {
     local xtrace
@@ -206,16 +194,11 @@
     set +o xtrace
 
     local disabled_svcs="${DISABLED_PYTHON3_PACKAGES}"
-    local enabled_svcs=",${ENABLED_PYTHON3_PACKAGES},"
     local dir
     for dir in $@; do
         disabled_svcs+=",$dir"
-        if python3_enabled_for $dir; then
-            enabled_svcs=${enabled_svcs//,$dir,/,}
-        fi
     done
     DISABLED_PYTHON3_PACKAGES=$(_cleanup_service_list "$disabled_svcs")
-    ENABLED_PYTHON3_PACKAGES=$(_cleanup_service_list "$enabled_svcs")
 
     $xtrace
 }
@@ -295,7 +278,7 @@
                 if python3_disabled_for ${package_dir##*/}; then
                     echo "Explicitly using $PYTHON2_VERSION version to install $package_dir based on DISABLED_PYTHON3_PACKAGES"
                 elif python3_enabled_for ${package_dir##*/}; then
-                    echo "Explicitly using $PYTHON3_VERSION version to install $package_dir based on ENABLED_PYTHON3_PACKAGES"
+                    echo "Using $PYTHON3_VERSION version to install $package_dir based on default behavior"
                     sudo_pip="$sudo_pip LC_ALL=en_US.UTF-8"
                     cmd_pip=$(get_pip_command $PYTHON3_VERSION)
                 elif [[ -d "$package_dir" ]]; then
diff --git a/lib/etcd3 b/lib/etcd3
index 26d07fd..c65a522 100644
--- a/lib/etcd3
+++ b/lib/etcd3
@@ -46,6 +46,9 @@
         cmd+=" --listen-peer-urls http://0.0.0.0:$ETCD_PEER_PORT "
     fi
     cmd+=" --listen-client-urls http://$SERVICE_HOST:$ETCD_PORT"
+    if [ "$ENABLE_DEBUG_LOG_LEVEL" == "True" ]; then
+        cmd+=" --debug"
+    fi
 
     local unitfile="$SYSTEMD_DIR/$ETCD_SYSTEMD_SERVICE"
     write_user_unit_file $ETCD_SYSTEMD_SERVICE "$cmd" "" "root"
diff --git a/lib/swift b/lib/swift
index 3b3e608..e2ee0cb 100644
--- a/lib/swift
+++ b/lib/swift
@@ -607,7 +607,7 @@
     # Mount the disk with mount options to make it as efficient as possible
     mkdir -p ${SWIFT_DATA_DIR}/drives/sdb1
     if ! egrep -q ${SWIFT_DATA_DIR}/drives/sdb1 /proc/mounts; then
-        sudo mount -t xfs -o loop,noatime,nodiratime,nobarrier,logbufs=8  \
+        sudo mount -t xfs -o loop,noatime,nodiratime,logbufs=8  \
             ${SWIFT_DISK_IMAGE} ${SWIFT_DATA_DIR}/drives/sdb1
     fi
 
diff --git a/roles/write-devstack-local-conf/library/devstack_local_conf.py b/roles/write-devstack-local-conf/library/devstack_local_conf.py
index bba7e31..b1ad2dd 100644
--- a/roles/write-devstack-local-conf/library/devstack_local_conf.py
+++ b/roles/write-devstack-local-conf/library/devstack_local_conf.py
@@ -155,8 +155,8 @@
                 continue
             self.loadDevstackPluginInfo(settings)
 
-    define_re = re.compile(r'^define_plugin\s+(\w+).*')
-    require_re = re.compile(r'^plugin_requires\s+(\w+)\s+(\w+).*')
+    define_re = re.compile(r'^define_plugin\s+(\S+).*')
+    require_re = re.compile(r'^plugin_requires\s+(\S+)\s+(\S+).*')
     def loadDevstackPluginInfo(self, fn):
         name = None
         reqs = set()
diff --git a/roles/write-devstack-local-conf/library/test.py b/roles/write-devstack-local-conf/library/test.py
index 791552d..88d404b 100644
--- a/roles/write-devstack-local-conf/library/test.py
+++ b/roles/write-devstack-local-conf/library/test.py
@@ -78,12 +78,12 @@
         with open(os.path.join(
                 self.tmpdir,
                 'foo-plugin', 'devstack', 'settings'), 'w') as f:
-            f.write('define_plugin foo\n')
+            f.write('define_plugin foo-plugin\n')
         with open(os.path.join(
                 self.tmpdir,
                 'bar-plugin', 'devstack', 'settings'), 'w') as f:
-            f.write('define_plugin bar\n')
-            f.write('plugin_requires bar foo\n')
+            f.write('define_plugin bar-plugin\n')
+            f.write('plugin_requires bar-plugin foo-plugin\n')
 
         localrc = {'test_localrc': '1'}
         local_conf = {'install':
@@ -94,8 +94,8 @@
         # We use ordereddict here to make sure the plugins are in the
         # *wrong* order for testing.
         plugins = OrderedDict([
-            ('bar', 'git://git.openstack.org/openstack/bar-plugin'),
-            ('foo', 'git://git.openstack.org/openstack/foo-plugin'),
+            ('bar-plugin', 'git://git.openstack.org/openstack/bar-plugin'),
+            ('foo-plugin', 'git://git.openstack.org/openstack/foo-plugin'),
             ])
         p = dict(localrc=localrc,
                  local_conf=local_conf,
@@ -104,6 +104,22 @@
                  plugins=plugins,
                  base_dir=self.tmpdir,
                  path=os.path.join(self.tmpdir, 'test.local.conf'))
+        lc = LocalConf(p.get('localrc'),
+                       p.get('local_conf'),
+                       p.get('base_services'),
+                       p.get('services'),
+                       p.get('plugins'),
+                       p.get('base_dir'),
+                       p.get('projects'),
+                       p.get('project'))
+        lc.write(p['path'])
+
+        plugins = []
+        with open(p['path']) as f:
+            for line in f:
+                if line.startswith('enable_plugin'):
+                    plugins.append(line.split()[1])
+        self.assertEqual(['foo-plugin', 'bar-plugin'], plugins)
 
     def test_libs_from_git(self):
         "Test that LIBS_FROM_GIT is auto-generated"
diff --git a/stack.sh b/stack.sh
index 144c233..54a4f98 100755
--- a/stack.sh
+++ b/stack.sh
@@ -221,7 +221,7 @@
 
 # Warn users who aren't on an explicitly supported distro, but allow them to
 # override check and attempt installation with ``FORCE=yes ./stack``
-if [[ ! ${DISTRO} =~ (xenial|artful|bionic|stretch|jessie|f27|f28|opensuse-42.3|opensuse-15.0|opensuse-tumbleweed|rhel7) ]]; then
+if [[ ! ${DISTRO} =~ (xenial|artful|bionic|stretch|jessie|f28|f29|opensuse-42.3|opensuse-15.0|opensuse-tumbleweed|rhel7) ]]; then
     echo "WARNING: this script has not been tested on $DISTRO"
     if [[ "$FORCE" != "yes" ]]; then
         die $LINENO "If you wish to run this script anyway run with FORCE=yes"
diff --git a/stackrc b/stackrc
index 8463313..8f66ba7 100644
--- a/stackrc
+++ b/stackrc
@@ -129,15 +129,9 @@
 # Control whether Python 3 should be used at all.
 export USE_PYTHON3=$(trueorfalse False USE_PYTHON3)
 
-# Control whether Python 3 is enabled for specific services by the
-# base name of the directory from which they are installed. See
-# enable_python3_package to edit this variable and use_python3_for to
-# test membership.
-export ENABLED_PYTHON3_PACKAGES="nova,glance,cinder,uwsgi,python-openstackclient,openstacksdk"
-
 # Explicitly list services not to run under Python 3. See
 # disable_python3_package to edit this variable.
-export DISABLED_PYTHON3_PACKAGES=""
+export DISABLED_PYTHON3_PACKAGES="swift"
 
 # When Python 3 is supported by an application, adding the specific
 # version of Python 3 to this variable will install the app using that
diff --git a/tests/test_python.sh b/tests/test_python.sh
index 8652798..1f5453c 100755
--- a/tests/test_python.sh
+++ b/tests/test_python.sh
@@ -12,14 +12,9 @@
 echo "Testing Python 3 functions"
 
 # Initialize variables manipulated by functions under test.
-export ENABLED_PYTHON3_PACKAGES=""
 export DISABLED_PYTHON3_PACKAGES=""
 
-assert_false "should not be enabled yet" python3_enabled_for testpackage1
-
-enable_python3_package testpackage1
-assert_equal "$ENABLED_PYTHON3_PACKAGES" "testpackage1"  "unexpected result"
-assert_true "should be enabled" python3_enabled_for testpackage1
+assert_true "should be enabled by default" python3_enabled_for testpackage1
 
 assert_false "should not be disabled yet" python3_disabled_for testpackage2
 
diff --git a/tools/update_clouds_yaml.py b/tools/update_clouds_yaml.py
index eb7265f..9187c66 100755
--- a/tools/update_clouds_yaml.py
+++ b/tools/update_clouds_yaml.py
@@ -41,12 +41,19 @@
                 'auth_url': args.os_auth_url,
                 'username': args.os_username,
                 'password': args.os_password,
-                'project_name': args.os_project_name,
             },
         }
-        if args.os_identity_api_version == '3':
+        if args.os_project_name and args.os_system_scope:
+            print(
+                "WARNING: os_project_name and os_system_scope were both"
+                " given. os_system_scope will take priority.")
+        if args.os_project_name and not args.os_system_scope:
+            self._cloud_data['auth']['project_name'] = args.os_project_name
+        if args.os_identity_api_version == '3' and not args.os_system_scope:
             self._cloud_data['auth']['user_domain_id'] = 'default'
             self._cloud_data['auth']['project_domain_id'] = 'default'
+        if args.os_system_scope:
+            self._cloud_data['auth']['system_scope'] = args.os_system_scope
         if args.os_cacert:
             self._cloud_data['cacert'] = args.os_cacert
 
@@ -83,12 +90,13 @@
     parser.add_argument('--os-cloud', required=True)
     parser.add_argument('--os-region-name', default='RegionOne')
     parser.add_argument('--os-identity-api-version', default='3')
-    parser.add_argument('--os-volume-api-version', default='2')
+    parser.add_argument('--os-volume-api-version', default='3')
     parser.add_argument('--os-cacert')
     parser.add_argument('--os-auth-url', required=True)
     parser.add_argument('--os-username', required=True)
     parser.add_argument('--os-password', required=True)
-    parser.add_argument('--os-project-name', required=True)
+    parser.add_argument('--os-project-name')
+    parser.add_argument('--os-system-scope')
 
     args = parser.parse_args()