Merge "Make allow_{resize|migrate}_to_same_host configurable"
diff --git a/files/apts/horizon b/files/apts/horizon
index 03df3cb..5d06928 100644
--- a/files/apts/horizon
+++ b/files/apts/horizon
@@ -17,3 +17,4 @@
 python-coverage
 python-cherrypy3 # why?
 python-migrate
+libpcre3-dev  # pyScss
diff --git a/files/rpms/horizon b/files/rpms/horizon
index fe3a2f4..7add23a 100644
--- a/files/rpms/horizon
+++ b/files/rpms/horizon
@@ -19,3 +19,4 @@
 python-sqlalchemy
 python-webob
 pyxattr
+pcre-devel  # pyScss
diff --git a/functions-common b/functions-common
index e48ceaf..48edba8 100644
--- a/functions-common
+++ b/functions-common
@@ -1645,6 +1645,16 @@
     setup_package_with_req_sync $project_dir -e
 }
 
+# determine if a project as specified by directory is in
+# projects.txt. This will not be an exact match because we throw away
+# the namespacing when we clone, but it should be good enough in all
+# practical ways.
+function is_in_projects_txt {
+    local project_dir=$1
+    local project_name=$(basename $project_dir)
+    return grep "/$project_name\$" $REQUIREMENTS_DIR/projects.txt >/dev/null
+}
+
 # ``pip install -e`` the package, which processes the dependencies
 # using pip before running `setup.py develop`
 #
@@ -1663,8 +1673,19 @@
     local update_requirements=$(cd $project_dir && git diff --exit-code >/dev/null || echo "changed")
 
     if [[ $update_requirements != "changed" ]]; then
-        (cd $REQUIREMENTS_DIR; \
-            python update.py $project_dir)
+        if [[ "$REQUIREMENTS_MODE" == "soft" ]]; then
+            if is_in_projects_txt $project_dir; then
+                (cd $REQUIREMENTS_DIR; \
+                    python update.py $project_dir)
+            else
+                # soft update projects not found in requirements project.txt
+                (cd $REQUIREMENTS_DIR; \
+                    python update.py -s $project_dir)
+            fi
+        else
+            (cd $REQUIREMENTS_DIR; \
+                python update.py $project_dir)
+        fi
     fi
 
     setup_package $project_dir $flags
diff --git a/lib/config b/lib/config
index a4d59a3..a4d0328 100644
--- a/lib/config
+++ b/lib/config
@@ -82,8 +82,6 @@
     local matchgroup=$2
     local configfile=$3
 
-    # note in the awk below, \x27 is ascii for ' -- this avoids
-    # having to do nasty quoting games
     get_meta_section $file $matchgroup $configfile | \
     $CONFIG_AWK_CMD -v configfile=$configfile '
         BEGIN {
@@ -140,13 +138,13 @@
                 for (attr_no = cfg_sec_attr_count[sno] - 1; attr_no >=0; attr_no--) {
                     attr = cfg_sec_attr_name[sno, attr_no]
                     if (cfg_attr_count[section, attr] == 1)
-                        print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr, 0] "\x27"
+                        print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, 0] "\""
                     else {
                         # For multiline, invoke the ini routines in the reverse order
                         count = cfg_attr_count[section, attr]
-                        print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr, count - 1] "\x27"
+                        print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\""
                         for (l = count -2; l >= 0; l--)
-                            print "iniadd_literal " configfile " " section " " attr " \x27" cfg_attr[section, attr, l] "\x27"
+                            print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\""
                     }
                 }
             }
diff --git a/stackrc b/stackrc
index 6cec8e8..15b0951 100644
--- a/stackrc
+++ b/stackrc
@@ -116,6 +116,17 @@
 # Zero disables timeouts
 GIT_TIMEOUT=${GIT_TIMEOUT:-0}
 
+# Requirements enforcing mode
+#
+# - strict (default) : ensure all project requirements files match
+#   what's in global requirements.
+#
+# - soft : enforce requirements on everything in
+#   requirements/projects.txt, but do soft updates on all other
+#   repositories (i.e. sync versions for requirements that are in g-r,
+#   but pass through any extras)
+REQUIREMENTS_MODE=${REQUIREMENTS_MODE:-strict}
+
 # Repositories
 # ------------
 
@@ -522,11 +533,11 @@
     esac
 fi
 
-# Trove needs a custom image for it's work
+# Trove needs a custom image for its work
 if [[ "$ENABLED_SERVICES" =~ 'tr-api' ]]; then
     case "$VIRT_DRIVER" in
         libvirt|baremetal|ironic|xenapi)
-            TROVE_GUEST_IMAGE_URL=${TROVE_GUEST_IMAGE_URL:-"http://tarballs.openstack.org/trove/images/ubuntu_mysql.qcow2/ubuntu_mysql.qcow2"}
+            TROVE_GUEST_IMAGE_URL=${TROVE_GUEST_IMAGE_URL:-"http://tarballs.openstack.org/trove/images/ubuntu/mysql.qcow2"}
             IMAGE_URLS+=",${TROVE_GUEST_IMAGE_URL}"
             ;;
         *)
diff --git a/tests/test_config.sh b/tests/test_config.sh
index cd74cee..3252104 100755
--- a/tests/test_config.sh
+++ b/tests/test_config.sh
@@ -92,9 +92,9 @@
 [fff]
 type=new
 
-[[test-quote|test-quote.conf]]
+[[test-env|test-env.conf]]
 [foo]
-foo="foo bar" "baz"
+foo=\${FOO_BAR_BAZ}
 
 [[test5|test-equals.conf]]
 [DEFAULT]
@@ -126,9 +126,11 @@
 
 [[test-multiline|test-multiline.conf]]
 [multi]
-cfg_item1 = "ab":"cd", "ef":   "gh"
+cfg_item1 = ab:cd:ef:gh
 cfg_item1 = abcd
 cfg_item2 = efgh
+cfg_item2 = \${FOO_BAR_BAZ}
+
 EOF
 
 echo -n "get_meta_section_files: test0 doesn't exist: "
@@ -236,14 +238,17 @@
 
 echo -n "merge_config_file test-multiline: "
 rm -f test-multiline.conf
+FOO_BAR_BAZ="foo bar baz"
 merge_config_file test.conf test-multiline test-multiline.conf
 VAL=$(cat test-multiline.conf)
 EXPECT_VAL='
 [multi]
-cfg_item1 = "ab":"cd", "ef":   "gh"
+cfg_item1 = ab:cd:ef:gh
 cfg_item1 = abcd
-cfg_item2 = efgh'
+cfg_item2 = efgh
+cfg_item2 = foo bar baz'
 check_result "$VAL" "$EXPECT_VAL"
+unset FOO_BAR_BAZ
 
 echo -n "merge_config_group test2: "
 rm test2a.conf
@@ -275,14 +280,16 @@
 attribute = value"
 check_result "$VAL" "$EXPECT_VAL"
 
-echo -n "merge_config_file test-quote: "
-rm -f test-quote.conf
-merge_config_file test.conf test-quote test-quote.conf
-VAL=$(cat test-quote.conf)
+echo -n "merge_config_file test-env: "
+rm -f test-env.conf
+FOO_BAR_BAZ="foo bar baz"
+merge_config_file test.conf test-env test-env.conf
+VAL=$(cat test-env.conf)
 EXPECT_VAL='
 [foo]
-foo = "foo bar" "baz"'
+foo = foo bar baz'
 check_result "$VAL" "$EXPECT_VAL"
+unset FOO_BAR_BAZ
 
 echo -n "merge_config_group test4 variable filename: "
 setup_test4
@@ -332,6 +339,8 @@
 servers = 10.11.12.13:80"
 check_result "$VAL" "$EXPECT_VAL"
 
-rm -f test.conf test1c.conf test2a.conf test-quote.conf test-space.conf test-equals.conf test-strip.conf test-colon.conf
-rm -f test-multiline.conf test-multi-sections.conf
+rm -f test.conf test1c.conf test2a.conf \
+    test-space.conf test-equals.conf test-strip.conf \
+    test-colon.conf test-env.conf test-multiline.conf \
+    test-multi-sections.conf
 rm -rf test-etc
diff --git a/tox.ini b/tox.ini
index b6f2d96..c8d3909 100644
--- a/tox.ini
+++ b/tox.ini
@@ -10,7 +10,19 @@
 [testenv:bashate]
 deps = bashate
 whitelist_externals = bash
-commands = bash -c "find {toxinidir} -not -wholename \*.tox/\* -and \( -name \*.sh -or -name \*rc -or -name functions\* -or \( -wholename lib/\* -and -not -name \*.md \) \) -print0 | xargs -0 bashate -v"
+commands = bash -c "find {toxinidir}          \
+         -not \( -type d -name .?\* -prune \) \ # prune all 'dot' dirs
+         -not \( -type d -name doc -prune \)  \ # skip documentation
+         -type f                              \ # only files
+         -not -name \*~                       \ # skip editors, readme, etc
+         -not -name \*.md                     \
+         \(                                   \
+          -name \*.sh -or                     \
+          -name \*rc -or                      \
+          -name functions\* -or               \
+          -wholename \*/lib/\*                \ # /lib files are shell, but
+         \)                                   \ #   have no extension
+         -print0 | xargs -0 bashate -v"
 
 [testenv:docs]
 deps =