Merge "Cleanup create_accounts functions"
diff --git a/.gitignore b/.gitignore
index b0a65f5..67ab722 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,8 +8,8 @@
 .tox
 .stackenv
 accrc
-docs/files
-docs/html
+doc/files
+doc/build
 files/*.gz
 files/*.qcow2
 files/images
diff --git a/docs/source/assets/css/bootstrap.css b/doc/source/assets/css/bootstrap.css
similarity index 100%
rename from docs/source/assets/css/bootstrap.css
rename to doc/source/assets/css/bootstrap.css
diff --git a/docs/source/assets/css/local.css b/doc/source/assets/css/local.css
similarity index 100%
rename from docs/source/assets/css/local.css
rename to doc/source/assets/css/local.css
diff --git a/docs/source/assets/images/devstack.png b/doc/source/assets/images/devstack.png
similarity index 100%
rename from docs/source/assets/images/devstack.png
rename to doc/source/assets/images/devstack.png
Binary files differ
diff --git a/docs/source/assets/images/header_bg.png b/doc/source/assets/images/header_bg.png
similarity index 100%
rename from docs/source/assets/images/header_bg.png
rename to doc/source/assets/images/header_bg.png
Binary files differ
diff --git a/docs/source/assets/images/logo.png b/doc/source/assets/images/logo.png
similarity index 100%
rename from docs/source/assets/images/logo.png
rename to doc/source/assets/images/logo.png
Binary files differ
diff --git a/docs/source/assets/images/quickstart.png b/doc/source/assets/images/quickstart.png
similarity index 100%
rename from docs/source/assets/images/quickstart.png
rename to doc/source/assets/images/quickstart.png
Binary files differ
diff --git a/docs/source/assets/images/small_logo.png b/doc/source/assets/images/small_logo.png
similarity index 100%
rename from docs/source/assets/images/small_logo.png
rename to doc/source/assets/images/small_logo.png
Binary files differ
diff --git a/docs/source/assets/js/bootstrap.js b/doc/source/assets/js/bootstrap.js
similarity index 100%
rename from docs/source/assets/js/bootstrap.js
rename to doc/source/assets/js/bootstrap.js
diff --git a/docs/source/assets/js/bootstrap.min.js b/doc/source/assets/js/bootstrap.min.js
similarity index 100%
rename from docs/source/assets/js/bootstrap.min.js
rename to doc/source/assets/js/bootstrap.min.js
diff --git a/docs/source/assets/js/jquery-1.7.1.min.js b/doc/source/assets/js/jquery-1.7.1.min.js
similarity index 100%
rename from docs/source/assets/js/jquery-1.7.1.min.js
rename to doc/source/assets/js/jquery-1.7.1.min.js
diff --git a/docs/source/changes.html b/doc/source/changes.html
similarity index 100%
rename from docs/source/changes.html
rename to doc/source/changes.html
diff --git a/docs/source/configuration.html b/doc/source/configuration.html
similarity index 100%
rename from docs/source/configuration.html
rename to doc/source/configuration.html
diff --git a/docs/source/contributing.html b/doc/source/contributing.html
similarity index 100%
rename from docs/source/contributing.html
rename to doc/source/contributing.html
diff --git a/docs/source/eucarc.html b/doc/source/eucarc.html
similarity index 100%
rename from docs/source/eucarc.html
rename to doc/source/eucarc.html
diff --git a/docs/source/exerciserc.html b/doc/source/exerciserc.html
similarity index 100%
rename from docs/source/exerciserc.html
rename to doc/source/exerciserc.html
diff --git a/docs/source/faq.html b/doc/source/faq.html
similarity index 100%
rename from docs/source/faq.html
rename to doc/source/faq.html
diff --git a/docs/source/guides/multinode-lab.html b/doc/source/guides/multinode-lab.html
similarity index 99%
rename from docs/source/guides/multinode-lab.html
rename to doc/source/guides/multinode-lab.html
index db8be08..62232ef 100644
--- a/docs/source/guides/multinode-lab.html
+++ b/doc/source/guides/multinode-lab.html
@@ -247,7 +247,7 @@
         The support in DevStack is geared toward a minimal installation but can be used for
         testing.  To implement a true multi-node test of Swift required more than DevStack provides.  
         Enabling it is as simple as enabling the <code>swift</code> service in <code>local.conf</code>:
-        <pre>enable_service swift</pre>
+        <pre>enable_service s-proxy s-object s-container s-account</pre>
 
         <p>Swift will put its data files in <code>SWIFT_DATA_DIR</code> (default <code>/opt/stack/data/swift</code>).
         The size of the data 'partition' created (really a loop-mounted file) is set by
diff --git a/docs/source/guides/pxe-boot.html b/doc/source/guides/pxe-boot.html
similarity index 100%
rename from docs/source/guides/pxe-boot.html
rename to doc/source/guides/pxe-boot.html
diff --git a/docs/source/guides/ramdisk.html b/doc/source/guides/ramdisk.html
similarity index 100%
rename from docs/source/guides/ramdisk.html
rename to doc/source/guides/ramdisk.html
diff --git a/docs/source/guides/single-machine.html b/doc/source/guides/single-machine.html
similarity index 100%
rename from docs/source/guides/single-machine.html
rename to doc/source/guides/single-machine.html
diff --git a/docs/source/guides/single-vm.html b/doc/source/guides/single-vm.html
similarity index 100%
rename from docs/source/guides/single-vm.html
rename to doc/source/guides/single-vm.html
diff --git a/docs/source/guides/usb-boot.html b/doc/source/guides/usb-boot.html
similarity index 100%
rename from docs/source/guides/usb-boot.html
rename to doc/source/guides/usb-boot.html
diff --git a/docs/source/index.html b/doc/source/index.html
similarity index 100%
rename from docs/source/index.html
rename to doc/source/index.html
diff --git a/docs/source/local.conf.html b/doc/source/local.conf.html
similarity index 100%
rename from docs/source/local.conf.html
rename to doc/source/local.conf.html
diff --git a/docs/source/localrc.html b/doc/source/localrc.html
similarity index 100%
rename from docs/source/localrc.html
rename to doc/source/localrc.html
diff --git a/docs/source/openrc.html b/doc/source/openrc.html
similarity index 100%
rename from docs/source/openrc.html
rename to doc/source/openrc.html
diff --git a/docs/source/overview.html b/doc/source/overview.html
similarity index 100%
rename from docs/source/overview.html
rename to doc/source/overview.html
diff --git a/docs/source/plugins.html b/doc/source/plugins.html
similarity index 100%
rename from docs/source/plugins.html
rename to doc/source/plugins.html
diff --git a/docs/source/stackrc.html b/doc/source/stackrc.html
similarity index 96%
rename from docs/source/stackrc.html
rename to doc/source/stackrc.html
index 23a48c5..d83fbc1 100644
--- a/docs/source/stackrc.html
+++ b/doc/source/stackrc.html
@@ -66,8 +66,8 @@
             Certificate Authentication), Cinder (Scheduler, API, Volume), Horizon, MySQL, RabbitMQ, Tempest.
             <pre>ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-cond,cinder,c-sch,c-api,c-vol,n-sch,n-novnc,n-xvnc,n-cauth,horizon,rabbit,tempest,$DATABASE_TYPE</pre>
             Other services that are not enabled by default can be enabled in
-            <code>localrc</code>. For example, to add Swift:
-            <pre>enable_service swift</pre>
+            <code>localrc</code>. For example, to add Swift, use the following service names:
+            <pre>enable_service s-proxy s-object s-container s-account</pre>
             A service can similarly be disabled:
             <pre>disable_service horizon</pre></dd>
 
diff --git a/functions b/functions
index bbde27d..bb40a48 100644
--- a/functions
+++ b/functions
@@ -48,10 +48,10 @@
         fi
         image="$FILES/${image_fname}"
     else
-        # File based URL (RFC 1738): file://host/path
+        # File based URL (RFC 1738): ``file://host/path``
         # Remote files are not considered here.
-        # *nix: file:///home/user/path/file
-        # windows: file:///C:/Documents%20and%20Settings/user/path/file
+        # unix: ``file:///home/user/path/file``
+        # windows: ``file:///C:/Documents%20and%20Settings/user/path/file``
         image=$(echo $image_url | sed "s/^file:\/\///g")
         if [[ ! -f $image || "$(stat -c "%s" $image)" == "0" ]]; then
             echo "Not found: $image_url"
@@ -100,7 +100,7 @@
         if [[ "$vmdk_create_type" = "monolithicSparse" ]]; then
             vmdk_disktype="sparse"
         elif [[ "$vmdk_create_type" = "monolithicFlat" || "$vmdk_create_type" = "vmfs" ]]; then
-            # Attempt to retrieve the *-flat.vmdk
+            # Attempt to retrieve the ``*-flat.vmdk``
             local flat_fname="$(head -25 $image | { grep -G 'RW\|RDONLY [0-9]+ FLAT\|VMFS' $image || true; })"
             flat_fname="${flat_fname#*\"}"
             flat_fname="${flat_fname%?}"
diff --git a/functions-common b/functions-common
index c597651..1f90da7 100644
--- a/functions-common
+++ b/functions-common
@@ -31,6 +31,7 @@
 # - ``TRACK_DEPENDS``
 # - ``UNDO_REQUIREMENTS``
 # - ``http_proxy``, ``https_proxy``, ``no_proxy``
+#
 
 # Save trace setting
 XTRACE=$(set +o | grep xtrace)
@@ -118,6 +119,33 @@
     [ -n "$line" ]
 }
 
+# Add another config line for a multi-line option.
+# It's normally called after iniset of the same option and assumes
+# that the section already exists.
+#
+# Note that iniset_multiline requires all the 'lines' to be supplied
+# in the argument list. Doing that will cause incorrect configuration
+# if spaces are used in the config values.
+#
+# iniadd_literal config-file section option value
+function iniadd_literal {
+    local xtrace=$(set +o | grep xtrace)
+    set +o xtrace
+    local file=$1
+    local section=$2
+    local option=$3
+    local value=$4
+
+    [[ -z $section || -z $option ]] && return
+
+    # Add it
+    sed -i -e "/^\[$section\]/ a\\
+$option = $value
+" "$file"
+
+    $xtrace
+}
+
 # Set an option in an INI file
 # iniset config-file section option value
 function iniset {
@@ -1270,7 +1298,8 @@
         # - the pid of the background process is saved in the usual place
         # - the server process is brought back to the foreground
         # - if the server process exits prematurely the fg command errors
-        #   and a message is written to stdout and the service failure file
+        # and a message is written to stdout and the service failure file
+        #
         # The pid saved can be used in stop_process() as a process group
         # id to kill off all child processes
         if [[ -n "$group" ]]; then
diff --git a/lib/ceph b/lib/ceph
index 30ca903..2e68ce5 100644
--- a/lib/ceph
+++ b/lib/ceph
@@ -197,8 +197,12 @@
     fi
     sudo ceph -c ${CEPH_CONF_FILE} auth get-or-create client.${GLANCE_CEPH_USER} mon "allow r" osd "allow class-read object_prefix rbd_children, allow rwx pool=${GLANCE_CEPH_POOL}" | sudo tee ${CEPH_CONF_DIR}/ceph.client.${GLANCE_CEPH_USER}.keyring
     sudo chown ${STACK_USER}:$(id -g -n $whoami) ${CEPH_CONF_DIR}/ceph.client.${GLANCE_CEPH_USER}.keyring
+
+    # NOTE(eharney): When Glance has fully migrated to Glance store,
+    # default_store can be removed from [DEFAULT].  (See lib/glance.)
     iniset $GLANCE_API_CONF DEFAULT default_store rbd
     iniset $GLANCE_API_CONF DEFAULT show_image_direct_url True
+    iniset $GLANCE_API_CONF glance_store default_store rbd
     iniset $GLANCE_API_CONF glance_store stores "file, http, rbd"
     iniset $GLANCE_API_CONF glance_store rbd_store_ceph_conf $CEPH_CONF_FILE
     iniset $GLANCE_API_CONF glance_store rbd_store_user $GLANCE_CEPH_USER
diff --git a/lib/cinder_backends/xiv b/lib/cinder_backends/xiv
index dbdb96c..ee5da2d 100644
--- a/lib/cinder_backends/xiv
+++ b/lib/cinder_backends/xiv
@@ -16,6 +16,7 @@
 #
 # Authors:
 #   Alon Marx <alonma@il.ibm.com>
+#
 
 # lib/cinder_plugins/xiv
 # Configure the xiv_ds8k driver for xiv testing
@@ -61,7 +62,7 @@
     fi
 
     # For reference:
-    # XIV_DS8K_BACKEND='IBM-XIV_'${SAN_IP}'_'${SAN_CLUSTERNAME}'_'${CONNECTION_TYPE}
+    # ``XIV_DS8K_BACKEND='IBM-XIV_'${SAN_IP}'_'${SAN_CLUSTERNAME}'_'${CONNECTION_TYPE}``
     iniset $CINDER_CONF DEFAULT xiv_ds8k_driver_version $XIV_DRIVER_VERSION
 
     iniset $CINDER_CONF $be_name san_ip $SAN_IP
diff --git a/lib/config b/lib/config
index 0baa4cc..6e8219c 100644
--- a/lib/config
+++ b/lib/config
@@ -82,9 +82,15 @@
     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 { section = "" }
+        BEGIN {
+            section = ""
+            last_section = ""
+            section_count = 0
+        }
         /^\[.+\]/ {
             gsub("[][]", "", $1);
             section=$1
@@ -94,11 +100,58 @@
             next
         }
         /^[^ \t]+/ {
-            split($0, d, " *= *")
-            print "iniset " configfile " " section " " d[1] " \"" d[2] "\""
+            # get offset of first '=' in $0
+            eq_idx = index($0, "=")
+            # extract attr & value from $0
+            attr = substr($0, 1, eq_idx - 1)
+            value = substr($0, eq_idx + 1)
+            # only need to strip trailing whitespace from attr
+            sub(/[ \t]*$/, "", attr)
+            # need to strip leading & trailing whitespace from value
+            sub(/^[ \t]*/, "", value)
+            sub(/[ \t]*$/, "", value)
+
+            # cfg_attr_count: number of config lines per [section, attr]
+            # cfg_attr: two dimensional array to keep all the config lines per [section, attr]
+            # cfg_section: keep the section names in the same order as they appear in local.conf
+            # cfg_sec_attr_name: keep the attr names in the same order as they appear in local.conf
+            if (! (section, attr) in cfg_attr_count) {
+                if (section != last_section) {
+                    cfg_section[section_count++] = section
+                    last_section = section
+                }
+                attr_count = cfg_sec_attr_count[section_count - 1]++
+                cfg_sec_attr_name[section_count - 1][attr_count] = attr
+
+                cfg_attr[section, attr][0] = value
+                cfg_attr_count[section, attr] = 1
+            } else {
+                lno = cfg_attr_count[section, attr]++
+                cfg_attr[section, attr][lno] = value
+            }
+        }
+        END {
+            # Process each section in order
+            for (sno = 0; sno < section_count; sno++) {
+                section = cfg_section[sno]
+                # The ini routines simply append a config item immediately
+                # after the section header. To keep the same order as defined
+                # in local.conf, invoke the ini routines in the reverse order
+                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"
+                    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"
+                        for (l = count -2; l >= 0; l--)
+                            print "iniadd_literal " configfile " " section " " attr " \x27" cfg_attr[section, attr][l] "\x27"
+                    }
+                }
+            }
         }
     ' | while read a; do eval "$a"; done
-
 }
 
 
diff --git a/lib/dib b/lib/dib
index d39d801..30b31ec 100644
--- a/lib/dib
+++ b/lib/dib
@@ -20,13 +20,28 @@
 # set up default directories
 DIB_DIR=$DEST/diskimage-builder
 TIE_DIR=$DEST/tripleo-image-elements
+
+# NOTE: Setting DIB_APT_SOURCES assumes you will be building
+# Debian/Ubuntu based images. Leave unset for other flavors.
+DIB_APT_SOURCES=${DIB_APT_SOURCES:-""}
+DIB_BUILD_OFFLINE=$(trueorfalse False $DIB_BUILD_OFFLINE)
 DIB_IMAGE_CACHE=$DATA_DIR/diskimage-builder/image-create
 DIB_PIP_REPO=$DATA_DIR/diskimage-builder/pip-repo
 DIB_PIP_REPO_PORT=${DIB_PIP_REPO_PORT:-8899}
+
 OCC_DIR=$DEST/os-collect-config
 ORC_DIR=$DEST/os-refresh-config
 OAC_DIR=$DEST/os-apply-config
 
+# Include the apt-sources element in builds if we have an
+# alternative sources.list specified.
+if [ -n "$DIB_APT_SOURCES" ]; then
+    if [ ! -e "$DIB_APT_SOURCES" ]; then
+        die $LINENO "DIB_APT_SOURCES set but not found at $DIB_APT_SOURCES"
+    fi
+    EXTRA_ELEMENTS="apt-sources"
+fi
+
 # Functions
 # ---------
 
@@ -106,9 +121,11 @@
 
     # The disk-image-create command to run
     ELEMENTS_PATH=$elements_path \
+    DIB_APT_SOURCES=$DIB_APT_SOURCES \
+    DIB_OFFLINE=$DIB_BUILD_OFFLINE \
     PYPI_MIRROR_URL=$pypi_mirror_url \
     PYPI_MIRROR_URL_1=$pypi_mirror_url_1 \
-    disk-image-create -a amd64 $image_elements \
+    disk-image-create -a amd64 $image_elements $EXTRA_ELEMENTS \
         --image-cache $DIB_IMAGE_CACHE \
         -o $image_path
 
diff --git a/lib/heat b/lib/heat
index 737598d..53eca25 100644
--- a/lib/heat
+++ b/lib/heat
@@ -276,7 +276,7 @@
                 --os-identity-api-version=3 domain create heat \
                 --description "Owns users and projects created by heat" \
                 | grep ' id ' | get_field 2)
-            iniset $HEAT_CONF DEFAULT stack_user_domain ${D_ID}
+            iniset $HEAT_CONF DEFAULT stack_user_domain_id ${D_ID}
 
             openstack --os-token $OS_TOKEN --os-url=$ks_endpoint_v3 \
                 --os-identity-api-version=3 user create --password $SERVICE_PASSWORD \
diff --git a/lib/neutron b/lib/neutron
index a48f519..2253eda 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -186,9 +186,10 @@
 
 # Distributed Virtual Router (DVR) configuration
 # Can be:
-#     legacy   - No DVR functionality
-#     dvr_snat - Controller or single node DVR
-#     dvr      - Compute node in multi-node DVR
+# - ``legacy``   - No DVR functionality
+# - ``dvr_snat`` - Controller or single node DVR
+# - ``dvr``      - Compute node in multi-node DVR
+#
 Q_DVR_MODE=${Q_DVR_MODE:-legacy}
 if [[ "$Q_DVR_MODE" != "legacy" ]]; then
     Q_ML2_PLUGIN_MECHANISM_DRIVERS=openvswitch,linuxbridge,l2population
diff --git a/lib/neutron_plugins/oneconvergence b/lib/neutron_plugins/oneconvergence
index e5f0d71..4fd8c7c 100644
--- a/lib/neutron_plugins/oneconvergence
+++ b/lib/neutron_plugins/oneconvergence
@@ -1,5 +1,6 @@
 # Neutron One Convergence plugin
-# ---------------------------
+# ------------------------------
+
 # Save trace setting
 OC_XTRACE=$(set +o | grep xtrace)
 set +o xtrace
diff --git a/lib/nova b/lib/nova
index cd9544b..f47dc97 100644
--- a/lib/nova
+++ b/lib/nova
@@ -704,7 +704,7 @@
 
     if [[ "$VIRT_DRIVER" = 'libvirt' ]]; then
         # The group **$LIBVIRT_GROUP** is added to the current user in this script.
-        # sg' will be used in run_process to execute nova-compute as a member of the
+        # ``sg`` is used in run_process to execute nova-compute as a member of the
         # **$LIBVIRT_GROUP** group.
         run_process n-cpu "$NOVA_BIN_DIR/nova-compute --config-file $compute_cell_conf" $LIBVIRT_GROUP
     elif [[ "$VIRT_DRIVER" = 'fake' ]]; then
@@ -761,7 +761,14 @@
 }
 
 function stop_nova_compute {
-    stop_process n-cpu
+    if [ "$VIRT_DRIVER" == "fake" ]; then
+        local i
+        for i in `seq 1 $NUMBER_FAKE_NOVA_COMPUTE`; do
+            stop_process n-cpu-${i}
+        done
+    else
+        stop_process n-cpu
+    fi
     if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
         stop_nova_hypervisor
     fi
diff --git a/lib/nova_plugins/functions-libvirt b/lib/nova_plugins/functions-libvirt
index 6b9db48..d3c4eab 100644
--- a/lib/nova_plugins/functions-libvirt
+++ b/lib/nova_plugins/functions-libvirt
@@ -10,7 +10,7 @@
 set +o xtrace
 
 # Defaults
-# -------
+# --------
 
 # if we should turn on massive libvirt debugging
 DEBUG_LIBVIRT=$(trueorfalse False $DEBUG_LIBVIRT)
diff --git a/lib/opendaylight b/lib/opendaylight
index 1541ac1..cc29deb 100644
--- a/lib/opendaylight
+++ b/lib/opendaylight
@@ -3,9 +3,9 @@
 
 # Dependencies:
 #
-# - ``functions`` file
-# # ``DEST`` must be defined
-# # ``STACK_USER`` must be defined
+# ``functions`` file
+# ``DEST`` must be defined
+# ``STACK_USER`` must be defined
 
 # ``stack.sh`` calls the entry points in this order:
 #
@@ -55,7 +55,7 @@
 
 # Default arguments for OpenDaylight. This is typically used to set
 # Java memory options.
-#   ODL_ARGS=Xmx1024m -XX:MaxPermSize=512m
+# ``ODL_ARGS=Xmx1024m -XX:MaxPermSize=512m``
 ODL_ARGS=${ODL_ARGS:-"-XX:MaxPermSize=384m"}
 
 # How long to pause after ODL starts to let it complete booting
@@ -64,8 +64,6 @@
 # The physical provider network to device mapping
 ODL_PROVIDER_MAPPINGS=${ODL_PROVIDER_MAPPINGS:-physnet1:eth1}
 
-# Set up default directories
-
 
 # Entry Points
 # ------------
@@ -139,6 +137,7 @@
     # The flags to ODL have the following meaning:
     #   -of13: runs ODL using OpenFlow 1.3 protocol support.
     #   -virt ovsdb: Runs ODL in "virtualization" mode with OVSDB support
+
     # NOTE(chdent): Leaving this as screen_it instead of run_process until
     # the right thing for this service is determined.
     screen_it odl-server "cd $ODL_DIR/opendaylight && JAVA_HOME=$JHOME ./run.sh $ODL_ARGS -of13 -virt ovsdb"
diff --git a/lib/swift b/lib/swift
index 21ed920..15bd2a9 100644
--- a/lib/swift
+++ b/lib/swift
@@ -557,9 +557,11 @@
 
 function create_swift_accounts {
     # Defines specific passwords used by tools/create_userrc.sh
-    local swiftusertest1_password=testing
-    local swiftusertest2_password=testing2
-    local swiftusertest3_password=testing3
+    # As these variables are used by create_userrc.sh, they must be exported
+    # The _password suffix is expected by create_userrc.sh
+    export swiftusertest1_password=testing
+    export swiftusertest2_password=testing2
+    export swiftusertest3_password=testing3
 
     KEYSTONE_CATALOG_BACKEND=${KEYSTONE_CATALOG_BACKEND:-sql}
 
diff --git a/lib/tls b/lib/tls
index 15e8692..fdb798f 100644
--- a/lib/tls
+++ b/lib/tls
@@ -433,7 +433,7 @@
 
 
 # Cleanup Functions
-# ===============
+# =================
 
 
 # Stops all stud processes. This should be done only after all services
diff --git a/stack.sh b/stack.sh
index 0cec623..3b5fb74 100755
--- a/stack.sh
+++ b/stack.sh
@@ -234,15 +234,35 @@
 
 if [[ is_fedora && ( $DISTRO == "rhel6" || $DISTRO == "rhel7" ) ]]; then
     # RHEL requires EPEL for many Open Stack dependencies
-    if [[ $DISTRO == "rhel7" ]]; then
-        EPEL_RPM=${RHEL7_EPEL_RPM:-"http://dl.fedoraproject.org/pub/epel/beta/7/x86_64/epel-release-7-1.noarch.rpm"}
-    elif [[ $DISTRO == "rhel6" ]]; then
-        EPEL_RPM=${RHEL6_EPEL_RPM:-"http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm"}
-    fi
     if ! sudo yum repolist enabled epel | grep -q 'epel'; then
         echo "EPEL not detected; installing"
-        yum_install ${EPEL_RPM} || \
+        # This trick installs the latest epel-release from a bootstrap
+        # repo, then removes itself (as epel-release installed the
+        # "real" repo).
+        #
+        # you would think that rather than this, you could use
+        # $releasever directly in .repo file we create below.  However
+        # RHEL gives a $releasever of "6Server" which breaks the path;
+        # see https://bugzilla.redhat.com/show_bug.cgi?id=1150759
+        if [[ $DISTRO == "rhel7" ]]; then
+            epel_ver="7"
+        elif [[ $DISTRO == "rhel6" ]]; then
+            epel_ver="6"
+        fi
+
+        cat <<EOF | sudo tee /etc/yum.repos.d/epel-bootstrap.repo
+[epel]
+name=Bootstrap EPEL
+mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-$epel_ver&arch=\$basearch
+failovermethod=priority
+enabled=0
+gpgcheck=0
+EOF
+        # bare yum call due to --enablerepo
+        sudo yum --enablerepo=epel -y install epel-release || \
             die $LINENO "Error installing EPEL repo, cannot continue"
+        # epel rpm has installed it's version
+        sudo rm -f /etc/yum.repos.d/epel-bootstrap.repo
     fi
 
     # ... and also optional to be enabled
diff --git a/stackrc b/stackrc
index af45c77..3899525 100644
--- a/stackrc
+++ b/stackrc
@@ -120,7 +120,7 @@
 # ------------
 
 # Base GIT Repo URL
-# Another option is http://review.openstack.org/p
+# Another option is https://git.openstack.org
 GIT_BASE=${GIT_BASE:-git://git.openstack.org}
 
 ##############
diff --git a/tests/test_config.sh b/tests/test_config.sh
index 2634ce0..696e57f 100755
--- a/tests/test_config.sh
+++ b/tests/test_config.sh
@@ -91,6 +91,40 @@
 [[test4|\$TEST4_DIR/\$TEST4_FILE]]
 [fff]
 type=new
+
+[[test-quote|test-quote.conf]]
+[foo]
+foo="foo bar" "baz"
+
+[[test5|test-equals.conf]]
+[DEFAULT]
+drivers = driver=python.import.path.Driver
+
+[[test6|test-strip.conf]]
+[DEFAULT]
+# next line has trailing space
+attr = strip_trailing_space 
+
+[[test-multi-sections|test-multi-sections.conf]]
+[sec-1]
+cfg_item1 = abcd
+cfg_item2 = efgh
+
+[sec-2]
+cfg_item1 = abcd
+cfg_item3 = /1/2/3/4:5
+cfg_item4 = end
+
+[sec-3]
+cfg_item5 = 5555
+cfg_item6 = 6666
+cfg_item5 = 5555another
+
+[[test-multiline|test-multiline.conf]]
+[multi]
+cfg_item1 = "ab":"cd", "ef":   "gh"
+cfg_item1 = abcd
+cfg_item2 = efgh
 EOF
 
 echo -n "get_meta_section_files: test0 doesn't exist: "
@@ -172,8 +206,39 @@
 # iniset adds a blank line if it creates the file...
 EXPECT_VAL="
 [ddd]
-additional = true
-type = new"
+type = new
+additional = true"
+check_result "$VAL" "$EXPECT_VAL"
+
+echo -n "merge_config_file test-multi-sections: "
+rm -f test-multi-sections.conf
+merge_config_file test.conf test-multi-sections test-multi-sections.conf
+VAL=$(cat test-multi-sections.conf)
+EXPECT_VAL='
+[sec-1]
+cfg_item1 = abcd
+cfg_item2 = efgh
+
+[sec-2]
+cfg_item1 = abcd
+cfg_item3 = /1/2/3/4:5
+cfg_item4 = end
+
+[sec-3]
+cfg_item5 = 5555
+cfg_item5 = 5555another
+cfg_item6 = 6666'
+check_result "$VAL" "$EXPECT_VAL"
+
+echo -n "merge_config_file test-multiline: "
+rm -f test-multiline.conf
+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 = abcd
+cfg_item2 = efgh'
 check_result "$VAL" "$EXPECT_VAL"
 
 echo -n "merge_config_group test2: "
@@ -183,8 +248,8 @@
 # iniset adds a blank line if it creates the file...
 EXPECT_VAL="
 [ddd]
-additional = true
-type = new"
+type = new
+additional = true"
 check_result "$VAL" "$EXPECT_VAL"
 
 echo -n "merge_config_group test2 no conf file: "
@@ -206,6 +271,15 @@
 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)
+EXPECT_VAL='
+[foo]
+foo = "foo bar" "baz"'
+check_result "$VAL" "$EXPECT_VAL"
+
 echo -n "merge_config_group test4 variable filename: "
 setup_test4
 merge_config_group test.conf test4
@@ -225,5 +299,26 @@
 type = new"
 check_result "$VAL" "$EXPECT_VAL"
 
-rm -f test.conf test1c.conf test2a.conf test-space.conf
+echo -n "merge_config_file test5 equals in value: "
+rm -f test-equals.conf
+merge_config_file test.conf test5 test-equals.conf
+VAL=$(cat test-equals.conf)
+# iniset adds a blank line if it creates the file...
+EXPECT_VAL="
+[DEFAULT]
+drivers = driver=python.import.path.Driver"
+check_result "$VAL" "$EXPECT_VAL"
+
+echo -n "merge_config_file test6 value stripped: "
+rm -f test-strip.conf
+merge_config_file test.conf test6 test-strip.conf
+VAL=$(cat test-strip.conf)
+# iniset adds a blank line if it creates the file...
+EXPECT_VAL="
+[DEFAULT]
+attr = strip_trailing_space"
+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
+rm -f test-multiline.conf test-multi-sections.conf
 rm -rf test-etc
diff --git a/tools/build_docs.sh b/tools/build_docs.sh
index 96bd892..f52b179 100755
--- a/tools/build_docs.sh
+++ b/tools/build_docs.sh
@@ -4,7 +4,7 @@
 #
 # - Install shocco if not found on PATH and INSTALL_SHOCCO is set
 # - Clone MASTER_REPO branch MASTER_BRANCH
-# - Re-creates ``docs/html`` directory from existing repo + new generated script docs
+# - Re-creates ``doc/build/html`` directory from existing repo + new generated script docs
 
 # Usage:
 ## build_docs.sh [-o <out-dir>] [-g] [master|<repo> [<branch>]]
@@ -29,8 +29,8 @@
 # http://devstack.org is a GitHub gh-pages site in the https://github.com/cloudbuilders/devtack.git repo
 GH_PAGES_REPO=git@github.com:cloudbuilders/devstack.git
 
-DOCS_SOURCE=docs/source
-HTML_BUILD=docs/html
+DOCS_SOURCE=doc/source
+HTML_BUILD=doc/build/html
 
 # Keep track of the devstack directory
 TOP_DIR=$(cd $(dirname "$0")/.. && pwd)
@@ -136,7 +136,7 @@
     mkdir -p $FQ_HTML_BUILD/`dirname $f`;
     $SHOCCO $f > $FQ_HTML_BUILD/$f.html
 done
-echo "$FILES" >docs/files
+echo "$FILES" >doc/files
 
 if [[ -n $GH_UPDATE ]]; then
     GH_ROOT=$(mktemp -d work-gh-XXXX)
diff --git a/tools/create_userrc.sh b/tools/create_userrc.sh
index 863fe03..b43fd88 100755
--- a/tools/create_userrc.sh
+++ b/tools/create_userrc.sh
@@ -245,7 +245,7 @@
 }
 
 if [ $MODE != "create" ]; then
-# looks like I can't ask for all tenant related to a specified user
+    # looks like I can't ask for all tenant related to a specified user
     openstack project list --long --quote none -f csv | grep ',True' | grep -v "${SKIP_TENANT}" | while IFS=, read tenant_id tenant_name desc enabled; do
         openstack user list --project $tenant_id --long --quote none -f csv | grep ',True' | while IFS=, read user_id user_name project email enabled; do
             if [ $MODE = one -a "$user_name" != "$USER_NAME" ]; then
@@ -253,9 +253,9 @@
             fi
 
             # Checks for a specific password defined for an user.
-            # Example for an username johndoe:
-            #                     JOHNDOE_PASSWORD=1234
-            eval SPECIFIC_UPASSWORD="\$${USER_NAME^^}_PASSWORD"
+            # Example for an username johndoe: JOHNDOE_PASSWORD=1234
+            # This mechanism is used by lib/swift
+            eval SPECIFIC_UPASSWORD="\$${user_name}_password"
             if [ -n "$SPECIFIC_UPASSWORD" ]; then
                 USER_PASS=$SPECIFIC_UPASSWORD
             fi
diff --git a/tox.ini b/tox.ini
index 325adae..3677631 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,4 +13,11 @@
 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"
 
 [testenv:docs]
-commands = python setup.py build_sphinx
+deps =
+   Pygments
+   docutils
+whitelist_externals = bash
+setenv =
+  TOP_DIR={toxinidir}
+  INSTALL_SHOCCO=true
+commands = bash tools/build_docs.sh