Merge "Fix missing --container-format options"
diff --git a/.gitignore b/.gitignore
index b80b476..b0a65f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*.pem
.localrc.auto
.prereqs
+.tox
.stackenv
accrc
docs/files
diff --git a/MAINTAINERS.rst b/MAINTAINERS.rst
index 0891d02..bdd9e78 100644
--- a/MAINTAINERS.rst
+++ b/MAINTAINERS.rst
@@ -28,20 +28,54 @@
.. contents:: :local:
-Fedora/CentOS/RHEL
-~~~~~~~~~~~~~~~~~~
- * Ian Wienand <iwienand@redhat.com>
+Ceph
+~~~~
-Xen
-~~~
+* Sebastien Han <sebastien.han@enovance.com>
Cinder
~~~~~~
+Fedora/CentOS/RHEL
+~~~~~~~~~~~~~~~~~~
+
+* Ian Wienand <iwienand@redhat.com>
+
Neutron
~~~~~~~
-tempest
+OpenDaylight
+~~~~~~~~~~~~
+
+* Kyle Mestery <kmestery@cisco.com>
+
+Sahara
+~~~~~~
+
+* Sergey Lukjanov <slukjanov@mirantis.com>
+
+SUSE
+~~~~
+
+* Ralf Haferkamp <rhafer@suse.de>
+* Vincent Untz <vuntz@suse.com>
+
+Tempest
~~~~~~~
+Trove
+~~~~~
+
+* Nikhil Manchanda <SlickNik@gmail.com>
+* Michael Basnight <mbasnight@gmail.com>
+
+Xen
+~~~
+* Bob Ball <bob.ball@citrix.com>
+
+Zaqar (Marconi)
+~~~~~~~~~~~~~~~
+
+* Flavio Percoco <flaper87@gmail.com>
+* Malini Kamalambal <malini.kamalambal@rackspace.com>
diff --git a/docs/source/index.html b/docs/source/index.html
index 1a31df1..dada57d 100644
--- a/docs/source/index.html
+++ b/docs/source/index.html
@@ -287,8 +287,8 @@
<td><a href="lib/ldap.html" class="btn btn-small btn-primary table-action">Read »</a></td>
</tr>
<tr>
- <td>lib/marconi</td>
- <td><a href="lib/marconi.html" class="btn btn-small btn-primary table-action">Read »</a></td>
+ <td>lib/zaqar</td>
+ <td><a href="lib/zaqar.html" class="btn btn-small btn-primary table-action">Read »</a></td>
</tr>
<tr>
<td>lib/neutron</td>
@@ -351,8 +351,8 @@
<td><a href="extras.d/50-ironic.html" class="btn btn-small btn-primary table-action">Read »</a></td>
</tr>
<tr>
- <td>extras.d/70-marconi.sh</td>
- <td><a href="extras.d/70-marconi.html" class="btn btn-small btn-primary table-action">Read »</a></td>
+ <td>extras.d/70-zaqar.sh</td>
+ <td><a href="extras.d/70-zaqar.html" class="btn btn-small btn-primary table-action">Read »</a></td>
</tr>
<tr>
<td>extras.d/70-sahara.sh</td>
@@ -517,9 +517,6 @@
<td>exercises/horizon.sh</td>
<td><a href="exercises/horizon.sh.html" class="btn btn-small btn-primary table-action">Read »</a></td>
</tr>
- <td>exercises/marconi.sh</td>
- <td><a href="exercises/marconi.sh.html" class="btn btn-small btn-primary table-action">Read »</a></td>
- </tr>
<tr>
<td>exercises/neutron-adv-test.sh</td>
<td><a href="exercises/neutron-adv-test.sh.html" class="btn btn-small btn-primary table-action">Read »</a></td>
@@ -545,6 +542,10 @@
<td>exercises/volumes.sh</td>
<td><a href="exercises/volumes.sh.html" class="btn btn-small btn-primary table-action">Read »</a></td>
</tr>
+ <tr>
+ <td>exercises/zaqar.sh</td>
+ <td><a href="exercises/zaqar.sh.html" class="btn btn-small btn-primary table-action">Read »</a></td>
+ </tr>
</tbody>
</table>
diff --git a/exercises/marconi.sh b/exercises/zaqar.sh
similarity index 86%
rename from exercises/marconi.sh
rename to exercises/zaqar.sh
index 9d83a99..6996f34 100755
--- a/exercises/marconi.sh
+++ b/exercises/zaqar.sh
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
-# **marconi.sh**
+# **zaqar.sh**
-# Sanity check that Marconi started if enabled
+# Sanity check that Zaqar started if enabled
echo "*********************************************************************"
echo "Begin DevStack Exercise: $0"
@@ -33,9 +33,9 @@
# Import exercise configuration
source $TOP_DIR/exerciserc
-is_service_enabled marconi-server || exit 55
+is_service_enabled zaqar-server || exit 55
-curl http://$SERVICE_HOST:8888/v1/ 2>/dev/null | grep -q 'queue_name' || die $LINENO "Marconi API not functioning!"
+curl http://$SERVICE_HOST:8888/v1/ 2>/dev/null | grep -q 'queue_name' || die $LINENO "Zaqar API not functioning!"
set +o xtrace
echo "*********************************************************************"
diff --git a/extras.d/40-dib.sh b/extras.d/40-dib.sh
new file mode 100644
index 0000000..fdae011
--- /dev/null
+++ b/extras.d/40-dib.sh
@@ -0,0 +1,27 @@
+# dib.sh - Devstack extras script to install diskimage-builder
+
+if is_service_enabled dib; then
+ if [[ "$1" == "source" ]]; then
+ # Initial source
+ source $TOP_DIR/lib/dib
+ elif [[ "$1" == "stack" && "$2" == "install" ]]; then
+ echo_summary "Installing diskimage-builder"
+ install_dib
+ elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
+ # no-op
+ :
+ elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
+ # no-op
+ :
+ fi
+
+ if [[ "$1" == "unstack" ]]; then
+ # no-op
+ :
+ fi
+
+ if [[ "$1" == "clean" ]]; then
+ # no-op
+ :
+ fi
+fi
diff --git a/extras.d/70-marconi.sh b/extras.d/70-marconi.sh
deleted file mode 100644
index a96a4c5..0000000
--- a/extras.d/70-marconi.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-# marconi.sh - Devstack extras script to install Marconi
-
-if is_service_enabled marconi-server; then
- if [[ "$1" == "source" ]]; then
- # Initial source
- source $TOP_DIR/lib/marconi
- elif [[ "$1" == "stack" && "$2" == "install" ]]; then
- echo_summary "Installing Marconi"
- install_marconiclient
- install_marconi
- elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
- echo_summary "Configuring Marconi"
- configure_marconi
- configure_marconiclient
-
- if is_service_enabled key; then
- create_marconi_accounts
- fi
-
- elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
- echo_summary "Initializing Marconi"
- init_marconi
- start_marconi
- fi
-
- if [[ "$1" == "unstack" ]]; then
- stop_marconi
- fi
-fi
diff --git a/extras.d/70-zaqar.sh b/extras.d/70-zaqar.sh
new file mode 100644
index 0000000..63c4fd5
--- /dev/null
+++ b/extras.d/70-zaqar.sh
@@ -0,0 +1,29 @@
+# zaqar.sh - Devstack extras script to install Zaqar
+
+if is_service_enabled zaqar-server; then
+ if [[ "$1" == "source" ]]; then
+ # Initial source
+ source $TOP_DIR/lib/zaqar
+ elif [[ "$1" == "stack" && "$2" == "install" ]]; then
+ echo_summary "Installing Zaqar"
+ install_zaqarclient
+ install_zaqar
+ elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
+ echo_summary "Configuring Zaqar"
+ configure_zaqar
+ configure_zaqarclient
+
+ if is_service_enabled key; then
+ create_zaqar_accounts
+ fi
+
+ elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
+ echo_summary "Initializing Zaqar"
+ init_zaqar
+ start_zaqar
+ fi
+
+ if [[ "$1" == "unstack" ]]; then
+ stop_zaqar
+ fi
+fi
diff --git a/files/apache-dib-pip-repo.template b/files/apache-dib-pip-repo.template
new file mode 100644
index 0000000..5d2379b
--- /dev/null
+++ b/files/apache-dib-pip-repo.template
@@ -0,0 +1,15 @@
+Listen %DIB_PIP_REPO_PORT%
+
+<VirtualHost *:%DIB_PIP_REPO_PORT%>
+ DocumentRoot %DIB_PIP_REPO%
+ <Directory %DIB_PIP_REPO%>
+ DirectoryIndex index.html
+ Require all granted
+ Order allow,deny
+ allow from all
+ </Directory>
+
+ ErrorLog /var/log/%APACHE_NAME%/dib_pip_repo_error.log
+ LogLevel warn
+ CustomLog /var/log/%APACHE_NAME%/dib_pip_repo_access.log combined
+</VirtualHost>
diff --git a/files/apache-horizon.template b/files/apache-horizon.template
index af880c4..c1dd693 100644
--- a/files/apache-horizon.template
+++ b/files/apache-horizon.template
@@ -1,6 +1,6 @@
<VirtualHost *:80>
WSGIScriptAlias / %HORIZON_DIR%/openstack_dashboard/wsgi/django.wsgi
- WSGIDaemonProcess horizon user=%USER% group=%GROUP% processes=3 threads=10 home=%HORIZON_DIR%
+ WSGIDaemonProcess horizon user=%USER% group=%GROUP% processes=3 threads=10 home=%HORIZON_DIR% display-name=%{GROUP}
WSGIApplicationGroup %{GLOBAL}
SetEnv APACHE_RUN_USER %USER%
diff --git a/files/apache-ironic.template b/files/apache-ironic.template
new file mode 100644
index 0000000..8864194
--- /dev/null
+++ b/files/apache-ironic.template
@@ -0,0 +1,12 @@
+Listen %PUBLICPORT%
+
+<VirtualHost *:%PUBLICPORT%>
+ DocumentRoot "%HTTPROOT%"
+ <Directory "%HTTPROOT%">
+ Options Indexes FollowSymLinks
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+ Require all granted
+ </Directory>
+</VirtualHost>
diff --git a/files/apache-keystone.template b/files/apache-keystone.template
index 805e7b8..fc8731c 100644
--- a/files/apache-keystone.template
+++ b/files/apache-keystone.template
@@ -2,7 +2,7 @@
Listen %ADMINPORT%
<VirtualHost *:%PUBLICPORT%>
- WSGIDaemonProcess keystone-public processes=5 threads=1 user=%USER%
+ WSGIDaemonProcess keystone-public processes=5 threads=1 user=%USER% display-name=%{GROUP}
WSGIProcessGroup keystone-public
WSGIScriptAlias / %PUBLICWSGI%
WSGIApplicationGroup %{GLOBAL}
@@ -12,7 +12,7 @@
</VirtualHost>
<VirtualHost *:%ADMINPORT%>
- WSGIDaemonProcess keystone-admin processes=5 threads=1 user=%USER%
+ WSGIDaemonProcess keystone-admin processes=5 threads=1 user=%USER% display-name=%{GROUP}
WSGIProcessGroup keystone-admin
WSGIScriptAlias / %ADMINWSGI%
WSGIApplicationGroup %{GLOBAL}
diff --git a/files/apts/ironic b/files/apts/ironic
index fe9c07f..8674d9f 100644
--- a/files/apts/ironic
+++ b/files/apts/ironic
@@ -1,5 +1,6 @@
ipmitool
iptables
+ipxe
libguestfs0
libvirt-bin
openssh-client
diff --git a/files/apts/keystone b/files/apts/keystone
index b7218b7..d316a42 100644
--- a/files/apts/keystone
+++ b/files/apts/keystone
@@ -6,6 +6,7 @@
python-pysqlite2
python-sqlalchemy
python-mysqldb
+python-mysql.connector
python-webob
python-greenlet
python-routes
diff --git a/files/apts/neutron b/files/apts/neutron
index 92e0a06..d3a08c7 100644
--- a/files/apts/neutron
+++ b/files/apts/neutron
@@ -15,6 +15,7 @@
python-eventlet
python-sqlalchemy
python-mysqldb
+python-mysql.connector
python-pyudev
python-qpid # dist:precise
dnsmasq-base
diff --git a/files/apts/nova b/files/apts/nova
index e779849..090ca4d 100644
--- a/files/apts/nova
+++ b/files/apts/nova
@@ -6,6 +6,7 @@
iputils-arping
mysql-server # NOPRIME
python-mysqldb
+python-mysql.connector
python-xattr # needed for glance which is needed for nova --- this shouldn't be here
python-lxml # needed for glance which is needed for nova --- this shouldn't be here
gawk
diff --git a/files/apts/marconi-server b/files/apts/zaqar-server
similarity index 100%
rename from files/apts/marconi-server
rename to files/apts/zaqar-server
diff --git a/files/rpms-suse/keystone b/files/rpms-suse/keystone
index 403d82f..a734cb9 100644
--- a/files/rpms-suse/keystone
+++ b/files/rpms-suse/keystone
@@ -10,5 +10,6 @@
python-greenlet
python-lxml
python-mysql
+python-mysql.connector
python-pysqlite
sqlite3
diff --git a/files/rpms-suse/neutron b/files/rpms-suse/neutron
index f3bafc7..79f5bff 100644
--- a/files/rpms-suse/neutron
+++ b/files/rpms-suse/neutron
@@ -10,6 +10,7 @@
python-iso8601
python-kombu
python-mysql
+python-mysql.connector
python-Paste
python-PasteDeploy
python-pyudev
diff --git a/files/rpms-suse/nova b/files/rpms-suse/nova
index 7a1160e..2a210e5 100644
--- a/files/rpms-suse/nova
+++ b/files/rpms-suse/nova
@@ -35,6 +35,7 @@
python-lxml # needed for glance which is needed for nova --- this shouldn't be here
python-mox
python-mysql
+python-mysql.connector
python-numpy # needed by websockify for spice console
python-paramiko
python-sqlalchemy-migrate
diff --git a/files/rpms/ironic b/files/rpms/ironic
index 0c81081..959ac3c 100644
--- a/files/rpms/ironic
+++ b/files/rpms/ironic
@@ -1,5 +1,6 @@
ipmitool
iptables
+ipxe-bootimgs
libguestfs
libvirt
libvirt-python
diff --git a/files/rpms/neutron b/files/rpms/neutron
index f02c02b..aaff48a 100644
--- a/files/rpms/neutron
+++ b/files/rpms/neutron
@@ -4,6 +4,7 @@
ebtables
iptables
iputils
+mysql-connector-python
mysql-server # NOPRIME
openvswitch # NOPRIME
python-boto
diff --git a/files/rpms/nova b/files/rpms/nova
index 6097991..ccee8a7 100644
--- a/files/rpms/nova
+++ b/files/rpms/nova
@@ -15,6 +15,7 @@
libxml2-python
numpy # needed by websockify for spice console
m2crypto
+mysql-connector-python
mysql-server # NOPRIME
parted
polkit
diff --git a/files/rpms/marconi-server b/files/rpms/zaqar-server
similarity index 100%
rename from files/rpms/marconi-server
rename to files/rpms/zaqar-server
diff --git a/lib/baremetal b/lib/baremetal
index 4ef161a..af90c06 100644
--- a/lib/baremetal
+++ b/lib/baremetal
@@ -127,10 +127,6 @@
BM_FLAVOR_ARCH=${BM_FLAVOR_ARCH:-$BM_CPU_ARCH}
-# Below this, we set some path and filenames.
-# Defaults are probably sufficient.
-DIB_DIR=${DIB_DIR:-$DEST/diskimage-builder}
-
# Use DIB to create deploy ramdisk and kernel.
BM_BUILD_DEPLOY_RAMDISK=`trueorfalse True $BM_BUILD_DEPLOY_RAMDISK`
# If not use DIB, these files are used as deploy ramdisk/kernel.
@@ -165,8 +161,9 @@
# Install diskimage-builder and shell-in-a-box
# so that we can build the deployment kernel & ramdisk
function prepare_baremetal_toolchain {
- git_clone $DIB_REPO $DIB_DIR $DIB_BUILD_BRANCH
-
+ if [[ $(type -P ramdisk-image-create) == "" ]]; then
+ pip_install diskimage_builder
+ fi
local shellinabox_basename=$(basename $BM_SHELL_IN_A_BOX)
if [[ ! -e $DEST/$shellinabox_basename ]]; then
cd $DEST
@@ -223,7 +220,7 @@
BM_DEPLOY_KERNEL=bm-deploy.kernel
BM_DEPLOY_RAMDISK=bm-deploy.initramfs
if [ ! -e "$TOP_DIR/files/$BM_DEPLOY_KERNEL" -o ! -e "$TOP_DIR/files/$BM_DEPLOY_RAMDISK" ]; then
- $DIB_DIR/bin/ramdisk-image-create $BM_DEPLOY_FLAVOR \
+ ramdisk-image-create $BM_DEPLOY_FLAVOR \
-o $TOP_DIR/files/bm-deploy
fi
fi
@@ -273,7 +270,7 @@
image_name=$(basename "$file" ".qcow2")
# this call returns the file names as "$kernel,$ramdisk"
- out=$($DIB_DIR/bin/disk-image-get-kernel \
+ out=$(disk-image-get-kernel \
-x -d $TOP_DIR/files -o bm-deploy -i $file)
if [ $? -ne 0 ]; then
die $LINENO "Failed to get kernel and ramdisk from $file"
diff --git a/lib/ceilometer b/lib/ceilometer
index 7bd1421..340acb9 100644
--- a/lib/ceilometer
+++ b/lib/ceilometer
@@ -79,19 +79,19 @@
create_ceilometer_accounts() {
- SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+ local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+ local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
# Ceilometer
if [[ "$ENABLED_SERVICES" =~ "ceilometer-api" ]]; then
- CEILOMETER_USER=$(get_or_create_user "ceilometer" \
- "$SERVICE_PASSWORD" $SERVICE_TENANT)
- get_or_add_user_role $ADMIN_ROLE $CEILOMETER_USER $SERVICE_TENANT
+ local ceilometer_user=$(get_or_create_user "ceilometer" \
+ "$SERVICE_PASSWORD" $service_tenant)
+ get_or_add_user_role $admin_role $ceilometer_user $service_tenant
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
- CEILOMETER_SERVICE=$(get_or_create_service "ceilometer" \
+ local ceilometer_service=$(get_or_create_service "ceilometer" \
"metering" "OpenStack Telemetry Service")
- get_or_create_endpoint $CEILOMETER_SERVICE \
+ get_or_create_endpoint $ceilometer_service \
"$REGION_NAME" \
"$CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/" \
"$CEILOMETER_SERVICE_PROTOCOL://$CEILOMETER_SERVICE_HOST:$CEILOMETER_SERVICE_PORT/" \
@@ -154,6 +154,7 @@
if [ "$CEILOMETER_BACKEND" = 'mysql' ] || [ "$CEILOMETER_BACKEND" = 'postgresql' ] ; then
iniset $CEILOMETER_CONF database connection `database_connection_url ceilometer`
+ iniset $CEILOMETER_CONF DEFAULT collector_workers $(( ($(nproc) + 1) / 2 ))
else
iniset $CEILOMETER_CONF database connection mongodb://localhost:27017/ceilometer
configure_mongodb
diff --git a/lib/cinder b/lib/cinder
index 38ce4d6..ce13b86 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -96,10 +96,10 @@
# Source the enabled backends
if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
- BE_TYPE=${be%%:*}
- BE_NAME=${be##*:}
- if [[ -r $CINDER_BACKENDS/${BE_TYPE} ]]; then
- source $CINDER_BACKENDS/${BE_TYPE}
+ be_type=${be%%:*}
+ be_name=${be##*:}
+ if [[ -r $CINDER_BACKENDS/${be_type} ]]; then
+ source $CINDER_BACKENDS/${be_type}
fi
done
fi
@@ -120,7 +120,7 @@
function cleanup_cinder {
# ensure the volume group is cleared up because fails might
# leave dead volumes in the group
- TARGETS=$(sudo tgtadm --op show --mode target)
+ local targets=$(sudo tgtadm --op show --mode target)
if [ $? -ne 0 ]; then
# If tgt driver isn't running this won't work obviously
# So check the response and restart if need be
@@ -130,11 +130,11 @@
else
restart_service tgtd
fi
- TARGETS=$(sudo tgtadm --op show --mode target)
+ targets=$(sudo tgtadm --op show --mode target)
fi
- if [[ -n "$TARGETS" ]]; then
- iqn_list=( $(grep --no-filename -r iqn $SCSI_PERSIST_DIR | sed 's/<target //' | sed 's/>//') )
+ if [[ -n "$targets" ]]; then
+ local iqn_list=( $(grep --no-filename -r iqn $SCSI_PERSIST_DIR | sed 's/<target //' | sed 's/>//') )
for i in "${iqn_list[@]}"; do
echo removing iSCSI target: $i
sudo tgt-admin --delete $i
@@ -148,11 +148,12 @@
fi
if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
+ local be be_name be_type
for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
- BE_TYPE=${be%%:*}
- BE_NAME=${be##*:}
- if type cleanup_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
- cleanup_cinder_backend_${BE_TYPE} ${BE_NAME}
+ be_type=${be%%:*}
+ be_name=${be##*:}
+ if type cleanup_cinder_backend_${be_type} >/dev/null 2>&1; then
+ cleanup_cinder_backend_${be_type} ${be_name}
fi
done
fi
@@ -161,7 +162,7 @@
# configure_cinder_rootwrap() - configure Cinder's rootwrap
function configure_cinder_rootwrap {
# Set the paths of certain binaries
- CINDER_ROOTWRAP=$(get_rootwrap_location cinder)
+ local cinder_rootwrap=$(get_rootwrap_location cinder)
# Deploy new rootwrap filters files (owned by root).
# Wipe any existing rootwrap.d files first
@@ -179,14 +180,14 @@
sudo chown root:root $CINDER_CONF_DIR/rootwrap.conf
sudo chmod 0644 $CINDER_CONF_DIR/rootwrap.conf
# Specify rootwrap.conf as first parameter to rootwrap
- ROOTWRAP_CSUDOER_CMD="$CINDER_ROOTWRAP $CINDER_CONF_DIR/rootwrap.conf *"
+ ROOTWRAP_CSUDOER_CMD="$cinder_rootwrap $CINDER_CONF_DIR/rootwrap.conf *"
# Set up the rootwrap sudoers for cinder
- TEMPFILE=`mktemp`
- echo "$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_CSUDOER_CMD" >$TEMPFILE
- chmod 0440 $TEMPFILE
- sudo chown root:root $TEMPFILE
- sudo mv $TEMPFILE /etc/sudoers.d/cinder-rootwrap
+ local tempfile=`mktemp`
+ echo "$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_CSUDOER_CMD" >$tempfile
+ chmod 0440 $tempfile
+ sudo chown root:root $tempfile
+ sudo mv $tempfile /etc/sudoers.d/cinder-rootwrap
}
# configure_cinder() - Set config files, create data dirs, etc
@@ -237,18 +238,19 @@
iniset $CINDER_CONF DEFAULT enable_v1_api true
if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
- enabled_backends=""
- default_name=""
+ local enabled_backends=""
+ local default_name=""
+ local be be_name be_type
for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
- BE_TYPE=${be%%:*}
- BE_NAME=${be##*:}
- if type configure_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
- configure_cinder_backend_${BE_TYPE} ${BE_NAME}
+ be_type=${be%%:*}
+ be_name=${be##*:}
+ if type configure_cinder_backend_${be_type} >/dev/null 2>&1; then
+ configure_cinder_backend_${be_type} ${be_name}
fi
- if [[ -z "$default_name" ]]; then
- default_name=$BE_NAME
+ if [[ -z "$default_type" ]]; then
+ default_name=$be_type
fi
- enabled_backends+=$BE_NAME,
+ enabled_backends+=$be_name,
done
iniset $CINDER_CONF DEFAULT enabled_backends ${enabled_backends%,*}
if [[ -n "$default_name" ]]; then
@@ -316,28 +318,28 @@
# Migrated from keystone_data.sh
function create_cinder_accounts {
- SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+ local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+ local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
# Cinder
if [[ "$ENABLED_SERVICES" =~ "c-api" ]]; then
- CINDER_USER=$(get_or_create_user "cinder" \
- "$SERVICE_PASSWORD" $SERVICE_TENANT)
- get_or_add_user_role $ADMIN_ROLE $CINDER_USER $SERVICE_TENANT
+ local cinder_user=$(get_or_create_user "cinder" \
+ "$SERVICE_PASSWORD" $service_tenant)
+ get_or_add_user_role $admin_role $cinder_user $service_tenant
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
- CINDER_SERVICE=$(get_or_create_service "cinder" \
+ local cinder_service=$(get_or_create_service "cinder" \
"volume" "Cinder Volume Service")
- get_or_create_endpoint $CINDER_SERVICE "$REGION_NAME" \
+ get_or_create_endpoint $cinder_service "$REGION_NAME" \
"$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/\$(tenant_id)s" \
"$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/\$(tenant_id)s" \
"$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v1/\$(tenant_id)s"
- CINDER_V2_SERVICE=$(get_or_create_service "cinderv2" \
+ local cinder_v2_service=$(get_or_create_service "cinderv2" \
"volumev2" "Cinder Volume Service V2")
- get_or_create_endpoint $CINDER_V2_SERVICE "$REGION_NAME" \
+ get_or_create_endpoint $cinder_v2_service "$REGION_NAME" \
"$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v2/\$(tenant_id)s" \
"$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v2/\$(tenant_id)s" \
"$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v2/\$(tenant_id)s"
@@ -354,6 +356,7 @@
}
# init_cinder() - Initialize database and volume group
+# Uses global ``NOVA_ENABLED_APIS``
function init_cinder {
# Force nova volumes off
NOVA_ENABLED_APIS=$(echo $NOVA_ENABLED_APIS | sed "s/osapi_volume,//")
@@ -367,11 +370,12 @@
fi
if is_service_enabled c-vol && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
+ local be be_name be_type
for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
- BE_TYPE=${be%%:*}
- BE_NAME=${be##*:}
- if type init_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
- init_cinder_backend_${BE_TYPE} ${BE_NAME}
+ be_type=${be%%:*}
+ be_name=${be##*:}
+ if type init_cinder_backend_${be_type} >/dev/null 2>&1; then
+ init_cinder_backend_${be_type} ${be_name}
fi
done
fi
@@ -450,6 +454,7 @@
# stop_cinder() - Stop running processes
function stop_cinder {
# Kill the cinder screen windows
+ local serv
for serv in c-api c-bak c-sch c-vol; do
screen_stop $serv
done
@@ -467,14 +472,13 @@
function create_volume_types {
# Create volume types
if is_service_enabled c-api && [[ -n "$CINDER_ENABLED_BACKENDS" ]]; then
+ local be be_name be_type
for be in ${CINDER_ENABLED_BACKENDS//,/ }; do
- BE_TYPE=${be%%:*}
- BE_NAME=${be##*:}
- if type configure_cinder_backend_${BE_TYPE} >/dev/null 2>&1; then
- # openstack volume type create --property volume_backend_name="${BE_TYPE}" ${BE_NAME}
- cinder type-create ${BE_NAME} && \
- cinder type-key ${BE_NAME} set volume_backend_name="${BE_NAME}"
- fi
+ be_type=${be%%:*}
+ be_name=${be##*:}
+ # openstack volume type create --property volume_backend_name="${be_type}" ${be_name}
+ cinder type-create ${be_name} && \
+ cinder type-key ${be_name} set volume_backend_name="${be_name}"
done
fi
}
diff --git a/lib/cinder_backends/lvm b/lib/cinder_backends/lvm
index 324c323..8f8ab79 100644
--- a/lib/cinder_backends/lvm
+++ b/lib/cinder_backends/lvm
@@ -112,6 +112,7 @@
local lv_prefix=$2
# Clean out existing volumes
+ local lv
for lv in $(sudo lvs --noheadings -o lv_name $vg 2>/dev/null); do
# lv_prefix prefixes the LVs we want
if [[ "${lv#$lv_prefix}" != "$lv" ]]; then
@@ -132,9 +133,9 @@
# of the backing file
if [[ -z "$(sudo lvs --noheadings -o lv_name $vg 2>/dev/null)" ]]; then
# if the backing physical device is a loop device, it was probably setup by devstack
- VG_DEV=$(sudo losetup -j $backing_file | awk -F':' '/backing-file/ { print $1}')
- if [[ -n "$VG_DEV" ]] && [[ -e "$VG_DEV" ]]; then
- sudo losetup -d $VG_DEV
+ local vg_dev=$(sudo losetup -j $backing_file | awk -F':' '/backing-file/ { print $1}')
+ if [[ -n "$vg_dev" ]] && [[ -e "$vg_dev" ]]; then
+ sudo losetup -d $vg_dev
rm -f $backing_file
fi
fi
@@ -159,11 +160,11 @@
if [ -z "$VOLUME_BACKING_DEVICE" ]; then
# Only create if the file doesn't already exists
[[ -f $backing_file ]] || truncate -s $VOLUME_BACKING_FILE_SIZE $backing_file
- DEV=`sudo losetup -f --show $backing_file`
+ local vg_dev=`sudo losetup -f --show $backing_file`
# Only create if the loopback device doesn't contain $VOLUME_GROUP
if ! sudo vgs $vg_name; then
- sudo vgcreate $vg_name $DEV
+ sudo vgcreate $vg_name $vg_dev
fi
else
sudo vgcreate $vg_name $VOLUME_BACKING_DEVICE
diff --git a/lib/config b/lib/config
index 67d788c..0baa4cc 100644
--- a/lib/config
+++ b/lib/config
@@ -110,6 +110,7 @@
[[ -r $localfile ]] || return 0
+ local configfile group
for group in $matchgroups; do
for configfile in $(get_meta_section_files $localfile $group); do
if [[ -d $(dirname $(eval "echo $configfile")) ]]; then
diff --git a/lib/database b/lib/database
index 0661049..e226515 100644
--- a/lib/database
+++ b/lib/database
@@ -89,7 +89,7 @@
# a multi-node DevStack installation.
# NOTE: Don't specify ``/db`` in this string so we can use it for multiple services
- BASE_SQL_CONN=${BASE_SQL_CONN:-${DATABASE_TYPE}://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST}
+ BASE_SQL_CONN=${BASE_SQL_CONN:-$(get_database_type)://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOST}
return 0
}
@@ -120,6 +120,14 @@
database_connection_url_$DATABASE_TYPE $db
}
+function get_database_type {
+ if [[ -n "${SQLALCHEMY_DATABASE_DRIVER}" ]]; then
+ echo "${DATABASE_TYPE}+${SQLALCHEMY_DATABASE_DRIVER}"
+ else
+ echo "${DATABASE_TYPE}"
+ fi
+}
+
# Restore xtrace
$XTRACE
diff --git a/lib/dib b/lib/dib
new file mode 100644
index 0000000..3a1167f
--- /dev/null
+++ b/lib/dib
@@ -0,0 +1,133 @@
+# lib/dib
+# Install and build images with **diskimage-builder**
+
+# Dependencies:
+#
+# - functions
+# - DEST, DATA_DIR must be defined
+
+# stack.sh
+# ---------
+# - install_dib
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+# Defaults
+# --------
+
+# set up default directories
+DIB_DIR=$DEST/diskimage-builder
+TIE_DIR=$DEST/tripleo-image-elements
+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
+
+# Functions
+# ---------
+
+# install_dib() - Collect source and prepare
+function install_dib {
+ git_clone $DIB_REPO $DIB_DIR $DIB_BRANCH
+ pushd $DIB_DIR
+ pip_install ./
+ popd
+
+ git_clone $TIE_REPO $TIE_DIR $TIE_BRANCH
+ git_clone $OCC_REPO $OCC_DIR $OCC_BRANCH
+ git_clone $ORC_REPO $ORC_DIR $ORC_BRANCH
+ git_clone $OAC_REPO $OAC_DIR $OAC_BRANCH
+ mkdir -p $DIB_IMAGE_CACHE
+}
+
+# build_dib_pip_repo() - Builds a local pip repo from local projects
+function build_dib_pip_repo {
+ local project_dirs=$1
+ local projpath proj package
+
+ rm -rf $DIB_PIP_REPO
+ mkdir -p $DIB_PIP_REPO
+
+ echo "<html><body>" > $DIB_PIP_REPO/index.html
+ for projpath in $project_dirs; do
+ proj=$(basename $projpath)
+ mkdir -p $DIB_PIP_REPO/$proj
+ pushd $projpath
+ rm -rf dist
+ python setup.py sdist
+ pushd dist
+ package=$(ls *)
+ mv $package $DIB_PIP_REPO/$proj/$package
+ popd
+
+ echo "<html><body><a href=\"$package\">$package</a></body></html>" > $DIB_PIP_REPO/$proj/index.html
+ echo "<a href=\"$proj\">$proj</a><br/>" >> $DIB_PIP_REPO/index.html
+
+ popd
+ done
+
+ echo "</body></html>" >> $DIB_PIP_REPO/index.html
+
+ local dib_pip_repo_apache_conf=$(apache_site_config_for dib_pip_repo)
+
+ sudo cp $FILES/apache-dib-pip-repo.template $dib_pip_repo_apache_conf
+ sudo sed -e "
+ s|%DIB_PIP_REPO%|$DIB_PIP_REPO|g;
+ s|%DIB_PIP_REPO_PORT%|$DIB_PIP_REPO_PORT|g;
+ s|%APACHE_NAME%|$APACHE_NAME|g;
+ " -i $dib_pip_repo_apache_conf
+ enable_apache_site dib_pip_repo
+}
+
+# disk_image_create_upload() - Creates and uploads a diskimage-builder built image
+function disk_image_create_upload {
+
+ local image_name=$1
+ local image_elements=$2
+ local elements_path=$3
+
+ local image_path=$TOP_DIR/files/$image_name.qcow2
+
+ # Set the local pip repo as the primary index mirror so the
+ # image is built with local packages
+ local pypi_mirror_url=http://$SERVICE_HOST:$DIB_PIP_REPO_PORT/
+ local pypi_mirror_url_1
+
+ if [ -a $HOME/.pip/pip.conf ]; then
+ # Add the current pip.conf index-url as an extra-index-url
+ # in the image build
+ pypi_mirror_url_1=$(iniget $HOME/.pip/pip.conf global index-url)
+ else
+ # If no pip.conf, set upstream pypi as an extra mirror
+ # (this also sets the .pydistutils.cfg index-url)
+ pypi_mirror_url_1=http://pypi.python.org/simple
+ fi
+
+ # The disk-image-create command to run
+ ELEMENTS_PATH=$elements_path \
+ PYPI_MIRROR_URL=$pypi_mirror_url \
+ PYPI_MIRROR_URL_1=$pypi_mirror_url_1 \
+ disk-image-create -a amd64 $image_elements \
+ --image-cache $DIB_IMAGE_CACHE \
+ -o $image_path
+
+ local token=$(keystone token-get | grep ' id ' | get_field 2)
+ die_if_not_set $LINENO token "Keystone fail to get token"
+
+ glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT \
+ image-create --name $image_name --is-public True \
+ --container-format=bare --disk-format qcow2 \
+ < $image_path
+}
+
+# Restore xtrace
+$XTRACE
+
+# Tell emacs to use shell-script-mode
+## Local variables:
+## mode: shell-script
+## End:
diff --git a/lib/glance b/lib/glance
index 78e5e88..1dea6cf 100644
--- a/lib/glance
+++ b/lib/glance
@@ -169,23 +169,23 @@
function create_glance_accounts {
if is_service_enabled g-api; then
- GLANCE_USER=$(get_or_create_user "glance" \
+ local glance_user=$(get_or_create_user "glance" \
"$SERVICE_PASSWORD" $SERVICE_TENANT_NAME)
- get_or_add_user_role service $GLANCE_USER $SERVICE_TENANT_NAME
+ get_or_add_user_role service $glance_user $SERVICE_TENANT_NAME
# required for swift access
if is_service_enabled s-proxy; then
- GLANCE_SWIFT_USER=$(get_or_create_user "glance-swift" \
+ local glance_swift_user=$(get_or_create_user "glance-swift" \
"$SERVICE_PASSWORD" $SERVICE_TENANT_NAME "glance-swift@example.com")
- get_or_add_user_role "ResellerAdmin" $GLANCE_SWIFT_USER $SERVICE_TENANT_NAME
+ get_or_add_user_role "ResellerAdmin" $glance_swift_user $SERVICE_TENANT_NAME
fi
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
- GLANCE_SERVICE=$(get_or_create_service "glance" \
+ local glance_service=$(get_or_create_service "glance" \
"image" "Glance Image Service")
- get_or_create_endpoint $GLANCE_SERVICE \
+ get_or_create_endpoint $glance_service \
"$REGION_NAME" \
"http://$GLANCE_HOSTPORT" \
"http://$GLANCE_HOSTPORT" \
diff --git a/lib/heat b/lib/heat
index b6124c0..bd99d6b 100644
--- a/lib/heat
+++ b/lib/heat
@@ -31,6 +31,8 @@
# set up default directories
HEAT_DIR=$DEST/heat
HEATCLIENT_DIR=$DEST/python-heatclient
+HEAT_CFNTOOLS_DIR=$DEST/heat-cfntools
+HEAT_TEMPLATES_REPO_DIR=$DEST/heat-templates
HEAT_AUTH_CACHE_DIR=${HEAT_AUTH_CACHE_DIR:-/var/cache/heat}
HEAT_STANDALONE=`trueorfalse False $HEAT_STANDALONE`
HEAT_CONF_DIR=/etc/heat
@@ -179,6 +181,12 @@
git_clone $HEAT_REPO $HEAT_DIR $HEAT_BRANCH
}
+# install_heat_other() - Collect source and prepare
+function install_heat_other {
+ git_clone $HEAT_CFNTOOLS_REPO $HEAT_CFNTOOLS_DIR $HEAT_CFNTOOLS_BRANCH
+ git_clone $HEAT_TEMPLATES_REPO $HEAT_TEMPLATES_REPO_DIR $HEAT_TEMPLATES_BRANCH
+}
+
# start_heat() - Start running processes, including screen
function start_heat {
screen_it h-eng "cd $HEAT_DIR; bin/heat-engine --config-file=$HEAT_CONF"
@@ -196,21 +204,6 @@
done
}
-function disk_image_create {
- local elements_path=$1
- local elements=$2
- local arch=$3
- local output=$TOP_DIR/files/$4
- if [[ -f "$output.qcow2" ]]; then
- echo "Image file already exists: $output_file"
- else
- ELEMENTS_PATH=$elements_path disk-image-create \
- $elements -a $arch -o $output
- fi
- # upload with fake URL so that image in $TOP_DIR/files is used
- upload_image "http://localhost/$output.qcow2" $TOKEN
-}
-
# create_heat_accounts() - Set up common required heat accounts
function create_heat_accounts {
# migrated from files/keystone_data.sh
@@ -286,6 +279,23 @@
fi
}
+# build_heat_functional_test_image() - Build and upload functional test image
+function build_heat_functional_test_image {
+ build_dib_pip_repo "$OCC_DIR $OAC_DIR $ORC_DIR $HEAT_CFNTOOLS_DIR"
+ local image_name=heat-functional-tests-image
+
+ # The elements to invoke disk-image-create with
+ local image_elements="vm fedora selinux-permissive pypi \
+ os-collect-config os-refresh-config os-apply-config heat-cfntools \
+ heat-config heat-config-cfn-init heat-config-puppet heat-config-script"
+
+ # Elements path for tripleo-image-elements and heat-templates software-config
+ local elements_path=$TIE_DIR/elements:$HEAT_TEMPLATES_REPO_DIR/hot/software-config/elements
+
+ disk_image_create_upload "$image_name" "$image_elements" "$elements_path"
+ iniset $TEMPEST_CONFIG orchestration image_ref $image_name
+}
+
# Restore xtrace
$XTRACE
diff --git a/lib/ironic b/lib/ironic
index b05edcf..469f3a3 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -66,8 +66,6 @@
IRONIC_VM_LOG_CONSOLE=${IRONIC_VM_LOG_CONSOLE:-True}
IRONIC_VM_LOG_DIR=${IRONIC_VM_LOG_DIR:-$IRONIC_DATA_DIR/logs/}
-DIB_DIR=${DIB_DIR:-$DEST/diskimage-builder}
-
# Use DIB to create deploy ramdisk and kernel.
IRONIC_BUILD_DEPLOY_RAMDISK=`trueorfalse True $IRONIC_BUILD_DEPLOY_RAMDISK`
# If not use DIB, these files are used as deploy ramdisk/kernel.
@@ -76,6 +74,12 @@
IRONIC_DEPLOY_KERNEL=${IRONIC_DEPLOY_KERNEL:-}
IRONIC_DEPLOY_ELEMENT=${IRONIC_DEPLOY_ELEMENT:-deploy-ironic}
+IRONIC_AGENT_TARBALL=${IRONIC_AGENT_TARBALL:-http://tarballs.openstack.org/ironic-python-agent/coreos/ipa-coreos.tar.gz}
+
+# Which deploy driver to use - valid choices right now
+# are 'pxe_ssh' and 'agent_ssh'.
+IRONIC_DEPLOY_DRIVER=${IRONIC_DEPLOY_DRIVER:-pxe_ssh}
+
#TODO(agordeev): replace 'ubuntu' with host distro name getting
IRONIC_DEPLOY_FLAVOR=${IRONIC_DEPLOY_FLAVOR:-ubuntu $IRONIC_DEPLOY_ELEMENT}
@@ -89,6 +93,32 @@
# Tell Tempest this project is present
TEMPEST_SERVICES+=,ironic
+# Enable iPXE
+IRONIC_IPXE_ENABLED=$(trueorfalse False $IRONIC_IPXE_ENABLED)
+IRONIC_HTTP_DIR=${IRONIC_HTTP_DIR:-$IRONIC_DATA_DIR/httpboot}
+IRONIC_HTTP_SERVER=${IRONIC_HTTP_SERVER:-$HOST_IP}
+IRONIC_HTTP_PORT=${IRONIC_HTTP_PORT:-8088}
+
+# get_pxe_boot_file() - Get the PXE/iPXE boot file path
+function get_pxe_boot_file {
+ local relpath=syslinux/pxelinux.0
+ if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+ relpath=ipxe/undionly.kpxe
+ fi
+
+ local pxe_boot_file
+ if is_ubuntu; then
+ pxe_boot_file=/usr/lib/$relpath
+ elif is_fedora || is_suse; then
+ pxe_boot_file=/usr/share/$relpath
+ fi
+
+ echo $pxe_boot_file
+}
+
+# PXE boot image
+IRONIC_PXE_BOOT_IMAGE=${IRONIC_PXE_BOOT_IMAGE:-$(get_pxe_boot_file)}
+
# Functions
# ---------
@@ -110,6 +140,10 @@
done
git_clone $IRONIC_REPO $IRONIC_DIR $IRONIC_BRANCH
setup_develop $IRONIC_DIR
+
+ if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+ install_apache_wsgi
+ fi
}
# install_ironicclient() - Collect sources and prepare
@@ -119,6 +153,25 @@
sudo install -D -m 0644 -o $STACK_USER {$IRONICCLIENT_DIR/tools/,/etc/bash_completion.d/}ironic.bash_completion
}
+# _cleanup_ironic_apache_wsgi() - Remove wsgi files, disable and remove apache vhost file
+function _cleanup_ironic_apache_wsgi {
+ sudo rm -rf $IRONIC_HTTP_DIR
+ disable_apache_site ironic
+ sudo rm -f $(apache_site_config_for ironic)
+ restart_apache_server
+}
+
+# _config_ironic_apache_wsgi() - Set WSGI config files of Ironic
+function _config_ironic_apache_wsgi {
+ local ironic_apache_conf=$(apache_site_config_for ironic)
+ sudo cp $FILES/apache-ironic.template $ironic_apache_conf
+ sudo sed -e "
+ s|%PUBLICPORT%|$IRONIC_HTTP_PORT|g;
+ s|%HTTPROOT%|$IRONIC_HTTP_DIR|g;
+ " -i $ironic_apache_conf
+ enable_apache_site ironic
+}
+
# cleanup_ironic() - Remove residual data files, anything left over from previous
# runs that would need to clean up.
function cleanup_ironic {
@@ -131,22 +184,24 @@
if [[ ! -d $IRONIC_CONF_DIR ]]; then
sudo mkdir -p $IRONIC_CONF_DIR
fi
+
+ if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+ sudo mkdir -p $IRONIC_HTTP_DIR
+ sudo chown -R $STACK_USER:$LIBVIRT_GROUP $IRONIC_HTTP_DIR
+ fi
+
sudo mkdir -p $IRONIC_DATA_DIR
sudo mkdir -p $IRONIC_STATE_PATH
sudo mkdir -p $IRONIC_TFTPBOOT_DIR
sudo chown -R $STACK_USER $IRONIC_DATA_DIR $IRONIC_STATE_PATH
sudo chown -R $STACK_USER:$LIBVIRT_GROUP $IRONIC_TFTPBOOT_DIR
- if is_ubuntu; then
- local pxebin=/usr/lib/syslinux/pxelinux.0
- elif is_fedora; then
- local pxebin=/usr/share/syslinux/pxelinux.0
- fi
- if [ ! -f $pxebin ]; then
- die $LINENO "pxelinux.0 (from SYSLINUX) not found."
+ mkdir -p $IRONIC_TFTPBOOT_DIR/pxelinux.cfg
+
+ if [ ! -f $IRONIC_PXE_BOOT_IMAGE ]; then
+ die $LINENO "PXE boot file $IRONIC_PXE_BOOT_IMAGE not found."
fi
- cp $pxebin $IRONIC_TFTPBOOT_DIR
- mkdir -p $IRONIC_TFTPBOOT_DIR/pxelinux.cfg
+ cp $IRONIC_PXE_BOOT_IMAGE $IRONIC_TFTPBOOT_DIR
}
# configure_ironic() - Set config files, create data dirs, etc
@@ -175,6 +230,10 @@
if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
setup_colorized_logging $IRONIC_CONF_FILE DEFAULT
fi
+
+ if [[ "$IRONIC_IPXE_ENABLED" == "True" ]]; then
+ _config_ironic_apache_wsgi
+ fi
}
# configure_ironic_api() - Is used by configure_ironic(). Performs
@@ -218,6 +277,31 @@
if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
iniset $IRONIC_CONF_FILE pxe pxe_append_params "nofb nomodeset vga=normal console=ttyS0"
fi
+ if [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]] ; then
+ if [[ "$SWIFT_ENABLE_TEMPURLS" == "True" ]] ; then
+ iniset $IRONIC_CONF_FILE glance swift_temp_url_key $SWIFT_TEMPURL_KEY
+ else
+ die $LINENO "SWIFT_ENABLE_TEMPURLS must be True to use agent_ssh driver in Ironic."
+ fi
+ iniset $IRONIC_CONF_FILE glance swift_endpoint_url http://${HOST_IP}:8080
+ iniset $IRONIC_CONF_FILE glance swift_api_version v1
+ iniset $IRONIC_CONF_FILE glance swift_account AUTH_${SERVICE_TENANT}
+ iniset $IRONIC_CONF_FILE glance swift_container glance
+ iniset $IRONIC_CONF_FILE glance swift_temp_url_duration 3600
+ iniset $IRONIC_CONF_FILE agent heartbeat_timeout 30
+ if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
+ iniset $IRONIC_CONF_FILE agent agent_pxe_append_params "nofb nomodeset vga=normal console=ttyS0"
+ fi
+ fi
+
+ if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+ local pxebin=`basename $IRONIC_PXE_BOOT_IMAGE`
+ iniset $IRONIC_CONF_FILE pxe ipxe_enabled True
+ iniset $IRONIC_CONF_FILE pxe pxe_config_template '\$pybasedir/drivers/modules/ipxe_config.template'
+ iniset $IRONIC_CONF_FILE pxe pxe_bootfile_name $pxebin
+ iniset $IRONIC_CONF_FILE pxe http_root $IRONIC_HTTP_DIR
+ iniset $IRONIC_CONF_FILE pxe http_url "http://$IRONIC_HTTP_SERVER:$IRONIC_HTTP_PORT"
+ fi
}
# create_ironic_cache_dir() - Part of the init_ironic() process
@@ -285,6 +369,11 @@
if is_service_enabled ir-cond; then
start_ironic_conductor
fi
+
+ # Start Apache if iPXE is enabled
+ if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+ restart_apache_server
+ fi
}
# start_ironic_api() - Used by start_ironic().
@@ -309,6 +398,11 @@
# Kill the Ironic screen windows
screen -S $SCREEN_NAME -p ir-api -X kill
screen -S $SCREEN_NAME -p ir-cond -X kill
+
+ # Cleanup the WSGI files
+ if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
+ _cleanup_ironic_apache_wsgi
+ fi
}
function is_ironic {
@@ -345,6 +439,10 @@
# Remove the port needed only for workaround.
neutron port-delete $port_id
+
+ # Finally, share the fixed tenant network across all tenants. This allows the host
+ # to serve TFTP to a single network namespace via the tap device created above.
+ neutron net-update $ironic_net_id --shared true
}
function create_bridge_and_vms {
@@ -365,10 +463,21 @@
function enroll_vms {
local chassis_id=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
local idx=0
+
+ if [[ "$IRONIC_DEPLOY_DRIVER" == "pxe_ssh" ]] ; then
+ local _IRONIC_DEPLOY_KERNEL_KEY=pxe_deploy_kernel
+ local _IRONIC_DEPLOY_RAMDISK_KEY=pxe_deploy_ramdisk
+ elif [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]] ; then
+ local _IRONIC_DEPLOY_KERNEL_KEY=deploy_kernel
+ local _IRONIC_DEPLOY_RAMDISK_KEY=deploy_ramdisk
+ fi
+
while read MAC; do
- local node_id=$(ironic node-create --chassis_uuid $chassis_id --driver pxe_ssh \
- -i pxe_deploy_kernel=$IRONIC_DEPLOY_KERNEL_ID \
- -i pxe_deploy_ramdisk=$IRONIC_DEPLOY_RAMDISK_ID \
+
+ local node_id=$(ironic node-create --chassis_uuid $chassis_id \
+ --driver $IRONIC_DEPLOY_DRIVER \
+ -i $_IRONIC_DEPLOY_KERNEL_KEY=$IRONIC_DEPLOY_KERNEL_ID \
+ -i $_IRONIC_DEPLOY_RAMDISK_KEY=$IRONIC_DEPLOY_RAMDISK_ID \
-i ssh_virt_type=$IRONIC_SSH_VIRT_TYPE \
-i ssh_address=$IRONIC_VM_SSH_ADDRESS \
-i ssh_port=$IRONIC_VM_SSH_PORT \
@@ -405,15 +514,6 @@
}
function configure_tftpd {
- if is_ubuntu; then
- local pxebin=/usr/lib/syslinux/pxelinux.0
- elif is_fedora; then
- local pxebin=/usr/share/syslinux/pxelinux.0
- fi
- if [ ! -f $pxebin ]; then
- die $LINENO "pxelinux.0 (from SYSLINUX) not found."
- fi
-
# stop tftpd and setup serving via xinetd
stop_service tftpd-hpa || true
[ -f /etc/init/tftpd-hpa.conf ] && echo "manual" | sudo tee /etc/init/tftpd-hpa.override
@@ -465,7 +565,9 @@
echo_summary "Creating and uploading baremetal images for ironic"
# install diskimage-builder
- git_clone $DIB_REPO $DIB_DIR $DIB_BRANCH
+ if [[ $(type -P ramdisk-image-create) == "" ]]; then
+ pip_install diskimage_builder
+ fi
if [ -z "$IRONIC_DEPLOY_KERNEL" -o -z "$IRONIC_DEPLOY_RAMDISK" ]; then
local IRONIC_DEPLOY_KERNEL_PATH=$TOP_DIR/files/ir-deploy.kernel
@@ -480,13 +582,27 @@
if [ "$IRONIC_BUILD_DEPLOY_RAMDISK" = "True" ]; then
# we can build them only if we're not offline
if [ "$OFFLINE" != "True" ]; then
- $DIB_DIR/bin/ramdisk-image-create $IRONIC_DEPLOY_FLAVOR \
- -o $TOP_DIR/files/ir-deploy
+ if [ "$IRONIC_DEPLOY_RAMDISK" == "agent_ssh" ]; then
+ die $LINENO "Ironic-python-agent build is not yet supported"
+ else
+ ramdisk-image-create $IRONIC_DEPLOY_FLAVOR \
+ -o $TOP_DIR/files/ir-deploy
+ fi
else
die $LINENO "Deploy kernel+ramdisk files don't exist and cannot be build in OFFLINE mode"
fi
else
- die $LINENO "Deploy kernel+ramdisk files don't exist and their building was disabled explicitly by IRONIC_BUILD_DEPLOY_RAMDISK"
+ if [ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]; then
+ # download the agent image tarball
+ wget "$IRONIC_AGENT_TARBALL" -O ironic_agent_tarball.tar.gz
+ tar zxfv ironic_agent_tarball.tar.gz
+ mv UPLOAD/coreos_production_pxe.vmlinuz $IRONIC_DEPLOY_KERNEL_PATH
+ mv UPLOAD/coreos_production_pxe_image-oem.cpio.gz $IRONIC_DEPLOY_RAMDISK_PATH
+ rm -rf UPLOAD
+ rm ironic_agent_tarball.tar.gz
+ else
+ die $LINENO "Deploy kernel+ramdisk files don't exist and their building was disabled explicitly by IRONIC_BUILD_DEPLOY_RAMDISK"
+ fi
fi
fi
@@ -500,6 +616,7 @@
image create \
$(basename $IRONIC_DEPLOY_KERNEL_PATH) \
--public --disk-format=aki \
+ --container-format=aki \
< $IRONIC_DEPLOY_KERNEL_PATH | grep ' id ' | get_field 2)
IRONIC_DEPLOY_RAMDISK_ID=$(openstack \
--os-token $token \
@@ -507,6 +624,7 @@
image create \
$(basename $IRONIC_DEPLOY_RAMDISK_PATH) \
--public --disk-format=ari \
+ --container-format=ari \
< $IRONIC_DEPLOY_RAMDISK_PATH | grep ' id ' | get_field 2)
}
diff --git a/lib/marconi b/lib/marconi
deleted file mode 100644
index e05518c..0000000
--- a/lib/marconi
+++ /dev/null
@@ -1,211 +0,0 @@
-# lib/marconi
-# Install and start **Marconi** service
-
-# To enable a minimal set of Marconi services, add the following to localrc:
-#
-# enable_service marconi-server
-#
-# Dependencies:
-# - functions
-# - OS_AUTH_URL for auth in api
-# - DEST set to the destination directory
-# - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api
-# - STACK_USER service user
-
-# stack.sh
-# ---------
-# install_marconi
-# configure_marconi
-# init_marconi
-# start_marconi
-# stop_marconi
-# cleanup_marconi
-
-# Save trace setting
-XTRACE=$(set +o | grep xtrace)
-set +o xtrace
-
-
-# Defaults
-# --------
-
-# Set up default directories
-MARCONI_DIR=$DEST/marconi
-MARCONICLIENT_DIR=$DEST/python-marconiclient
-MARCONI_CONF_DIR=/etc/marconi
-MARCONI_CONF=$MARCONI_CONF_DIR/marconi.conf
-MARCONI_API_LOG_DIR=/var/log/marconi
-MARCONI_API_LOG_FILE=$MARCONI_API_LOG_DIR/queues.log
-MARCONI_AUTH_CACHE_DIR=${MARCONI_AUTH_CACHE_DIR:-/var/cache/marconi}
-
-# Support potential entry-points console scripts
-MARCONI_BIN_DIR=$(get_python_exec_prefix)
-
-# Set up database backend
-MARCONI_BACKEND=${MARCONI_BACKEND:-mongodb}
-
-
-# Set Marconi repository
-MARCONI_REPO=${MARCONI_REPO:-${GIT_BASE}/openstack/marconi.git}
-MARCONI_BRANCH=${MARCONI_BRANCH:-master}
-
-# Set client library repository
-MARCONICLIENT_REPO=${MARCONICLIENT_REPO:-${GIT_BASE}/openstack/python-marconiclient.git}
-MARCONICLIENT_BRANCH=${MARCONICLIENT_BRANCH:-master}
-
-# Set Marconi Connection Info
-MARCONI_SERVICE_HOST=${MARCONI_SERVICE_HOST:-$SERVICE_HOST}
-MARCONI_SERVICE_PORT=${MARCONI_SERVICE_PORT:-8888}
-MARCONI_SERVICE_PROTOCOL=${MARCONI_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
-
-# Tell Tempest this project is present
-TEMPEST_SERVICES+=,marconi
-
-
-# Functions
-# ---------
-
-# Test if any Marconi services are enabled
-# is_marconi_enabled
-function is_marconi_enabled {
- [[ ,${ENABLED_SERVICES} =~ ,"marconi-" ]] && return 0
- return 1
-}
-
-# cleanup_marconi() - Remove residual data files, anything left over from previous
-# runs that a clean run would need to clean up
-function cleanup_marconi {
- if ! timeout $SERVICE_TIMEOUT sh -c "while ! mongo marconi --eval 'db.dropDatabase();'; do sleep 1; done"; then
- die $LINENO "Mongo DB did not start"
- else
- full_version=$(mongo marconi --eval 'db.dropDatabase();')
- mongo_version=`echo $full_version | cut -d' ' -f4`
- required_mongo_version='2.2'
- if [[ $mongo_version < $required_mongo_version ]]; then
- die $LINENO "Marconi needs Mongo DB version >= 2.2 to run."
- fi
- fi
-}
-
-# configure_marconiclient() - Set config files, create data dirs, etc
-function configure_marconiclient {
- setup_develop $MARCONICLIENT_DIR
-}
-
-# configure_marconi() - Set config files, create data dirs, etc
-function configure_marconi {
- setup_develop $MARCONI_DIR
-
- [ ! -d $MARCONI_CONF_DIR ] && sudo mkdir -m 755 -p $MARCONI_CONF_DIR
- sudo chown $USER $MARCONI_CONF_DIR
-
- [ ! -d $MARCONI_API_LOG_DIR ] && sudo mkdir -m 755 -p $MARCONI_API_LOG_DIR
- sudo chown $USER $MARCONI_API_LOG_DIR
-
- iniset $MARCONI_CONF DEFAULT verbose True
- iniset $MARCONI_CONF DEFAULT use_syslog $SYSLOG
- iniset $MARCONI_CONF DEFAULT log_file $MARCONI_API_LOG_FILE
- iniset $MARCONI_CONF 'drivers:transport:wsgi' bind $MARCONI_SERVICE_HOST
-
- iniset $MARCONI_CONF keystone_authtoken auth_protocol http
- iniset $MARCONI_CONF keystone_authtoken admin_user marconi
- iniset $MARCONI_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
- iniset $MARCONI_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
- iniset $MARCONI_CONF keystone_authtoken signing_dir $MARCONI_AUTH_CACHE_DIR
-
- if [ "$MARCONI_BACKEND" = 'mysql' ] || [ "$MARCONI_BACKEND" = 'postgresql' ] ; then
- iniset $MARCONI_CONF drivers storage sqlalchemy
- iniset $MARCONI_CONF 'drivers:storage:sqlalchemy' uri `database_connection_url marconi`
- elif [ "$MARCONI_BACKEND" = 'mongodb' ] ; then
- iniset $MARCONI_CONF drivers storage mongodb
- iniset $MARCONI_CONF 'drivers:storage:mongodb' uri mongodb://localhost:27017/marconi
- configure_mongodb
- cleanup_marconi
- fi
-}
-
-function configure_mongodb {
- # Set nssize to 2GB. This increases the number of namespaces supported
- # # per database.
- if is_ubuntu; then
- sudo sed -i -e "
- s|[^ \t]*#[ \t]*\(nssize[ \t]*=.*\$\)|\1|
- s|^\(nssize[ \t]*=[ \t]*\).*\$|\1 2047|
- " /etc/mongodb.conf
- restart_service mongodb
- elif is_fedora; then
- sudo sed -i '/--nssize/!s/OPTIONS=\"/OPTIONS=\"--nssize 2047 /' /etc/sysconfig/mongod
- restart_service mongod
- fi
-}
-
-# init_marconi() - Initialize etc.
-function init_marconi {
- # Create cache dir
- sudo mkdir -p $MARCONI_AUTH_CACHE_DIR
- sudo chown $STACK_USER $MARCONI_AUTH_CACHE_DIR
- rm -f $MARCONI_AUTH_CACHE_DIR/*
-}
-
-# install_marconi() - Collect source and prepare
-function install_marconi {
- git_clone $MARCONI_REPO $MARCONI_DIR $MARCONI_BRANCH
- setup_develop $MARCONI_DIR
-}
-
-# install_marconiclient() - Collect source and prepare
-function install_marconiclient {
- git_clone $MARCONICLIENT_REPO $MARCONICLIENT_DIR $MARCONICLIENT_BRANCH
- setup_develop $MARCONICLIENT_DIR
-}
-
-# start_marconi() - Start running processes, including screen
-function start_marconi {
- if [[ "$USE_SCREEN" = "False" ]]; then
- screen_it marconi-server "marconi-server --config-file $MARCONI_CONF --daemon"
- else
- screen_it marconi-server "marconi-server --config-file $MARCONI_CONF"
- fi
-
- echo "Waiting for Marconi to start..."
- if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT/v1/health; do sleep 1; done"; then
- die $LINENO "Marconi did not start"
- fi
-}
-
-# stop_marconi() - Stop running processes
-function stop_marconi {
- # Kill the marconi screen windows
- for serv in marconi-server; do
- screen -S $SCREEN_NAME -p $serv -X kill
- done
-}
-
-function create_marconi_accounts {
- SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
-
- MARCONI_USER=$(get_or_create_user "marconi" \
- "$SERVICE_PASSWORD" $SERVICE_TENANT)
- get_or_add_user_role $ADMIN_ROLE $MARCONI_USER $SERVICE_TENANT
-
- if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
-
- MARCONI_SERVICE=$(get_or_create_service "marconi" \
- "queuing" "Marconi Service")
- get_or_create_endpoint $MARCONI_SERVICE \
- "$REGION_NAME" \
- "$MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT" \
- "$MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT" \
- "$MARCONI_SERVICE_PROTOCOL://$MARCONI_SERVICE_HOST:$MARCONI_SERVICE_PORT"
- fi
-
-}
-
-
-# Restore xtrace
-$XTRACE
-
-# Local variables:
-# mode: shell-script
-# End:
diff --git a/lib/neutron_plugins/linuxbridge_agent b/lib/neutron_plugins/linuxbridge_agent
index 82b5fc9..2638dd3 100644
--- a/lib/neutron_plugins/linuxbridge_agent
+++ b/lib/neutron_plugins/linuxbridge_agent
@@ -47,6 +47,7 @@
iniset /$Q_PLUGIN_CONF_FILE securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
fi
AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-linuxbridge-agent"
+ iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
# Define extra "AGENT" configuration options when q-agt is configured by defining
# the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
# For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
diff --git a/lib/neutron_plugins/ml2 b/lib/neutron_plugins/ml2
index 42dd57f..44b947f 100644
--- a/lib/neutron_plugins/ml2
+++ b/lib/neutron_plugins/ml2
@@ -6,13 +6,13 @@
set +o xtrace
# Enable this to simply and quickly enable tunneling with ML2.
-# Select either 'gre', 'vxlan', or '(gre vxlan)'
+# Select either 'gre', 'vxlan', or 'gre,vxlan'
Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-"vxlan"}
# This has to be set here since the agent will set this in the config file
if [[ "$Q_ML2_TENANT_NETWORK_TYPE" == "gre" || "$Q_ML2_TENANT_NETWORK_TYPE" == "vxlan" ]]; then
- Q_AGENT_EXTRA_AGENT_OPTS+=(tunnel_types=$Q_ML2_TENANT_NETWORK_TYPE)
+ Q_TUNNEL_TYPES=$Q_ML2_TENANT_NETWORK_TYPE
elif [[ "$ENABLE_TENANT_TUNNELS" == "True" ]]; then
- Q_AGENT_EXTRA_AGENT_OPTS+=(tunnel_types=gre)
+ Q_TUNNEL_TYPES=gre
fi
# Default openvswitch L2 agent
diff --git a/lib/neutron_plugins/ofagent_agent b/lib/neutron_plugins/ofagent_agent
index 66283ad..b4c2ada 100644
--- a/lib/neutron_plugins/ofagent_agent
+++ b/lib/neutron_plugins/ofagent_agent
@@ -71,6 +71,7 @@
fi
AGENT_BINARY="$NEUTRON_BIN_DIR/neutron-ofagent-agent"
+ iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
# Define extra "AGENT" configuration options when q-agt is configured by defining
# defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
# For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
diff --git a/lib/neutron_plugins/openvswitch_agent b/lib/neutron_plugins/openvswitch_agent
index 5adb0c5..3fc37de 100644
--- a/lib/neutron_plugins/openvswitch_agent
+++ b/lib/neutron_plugins/openvswitch_agent
@@ -102,6 +102,7 @@
# Set root wrap
iniset "/$Q_PLUGIN_CONF_FILE.domU" agent root_helper "$Q_RR_COMMAND"
fi
+ iniset /$Q_PLUGIN_CONF_FILE agent tunnel_types $Q_TUNNEL_TYPES
# Define extra "AGENT" configuration options when q-agt is configured by defining
# defining the array ``Q_AGENT_EXTRA_AGENT_OPTS``.
# For Example: ``Q_AGENT_EXTRA_AGENT_OPTS=(foo=true bar=2)``
diff --git a/lib/nova b/lib/nova
index 6b1afd9..892aace 100644
--- a/lib/nova
+++ b/lib/nova
@@ -59,10 +59,6 @@
# Set the paths of certain binaries
NOVA_ROOTWRAP=$(get_rootwrap_location nova)
-# Allow rate limiting to be turned off for testing, like for Tempest
-# NOTE: Set API_RATE_LIMIT="False" to turn OFF rate limiting
-API_RATE_LIMIT=${API_RATE_LIMIT:-"True"}
-
# Option to enable/disable config drive
# NOTE: Set FORCE_CONFIG_DRIVE="False" to turn OFF config drive
FORCE_CONFIG_DRIVE=${FORCE_CONFIG_DRIVE:-"always"}
@@ -461,9 +457,6 @@
if [ "$SYSLOG" != "False" ]; then
iniset $NOVA_CONF DEFAULT use_syslog "True"
fi
- if [ "$API_RATE_LIMIT" != "True" ]; then
- iniset $NOVA_CONF DEFAULT api_rate_limit "False"
- fi
if [ "$FORCE_CONFIG_DRIVE" != "False" ]; then
iniset $NOVA_CONF DEFAULT force_config_drive "$FORCE_CONFIG_DRIVE"
fi
diff --git a/lib/sahara b/lib/sahara
index 70feacd..05ef4a8 100644
--- a/lib/sahara
+++ b/lib/sahara
@@ -57,18 +57,18 @@
# service sahara admin
function create_sahara_accounts {
- SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+ local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+ local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
- SAHARA_USER=$(get_or_create_user "sahara" \
- "$SERVICE_PASSWORD" $SERVICE_TENANT)
- get_or_add_user_role $ADMIN_ROLE $SAHARA_USER $SERVICE_TENANT
+ local sahara_user=$(get_or_create_user "sahara" \
+ "$SERVICE_PASSWORD" $service_tenant)
+ get_or_add_user_role $admin_role $sahara_user $service_tenant
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
- SAHARA_SERVICE=$(get_or_create_service "sahara" \
+ local sahara_service=$(get_or_create_service "sahara" \
"data_processing" "Sahara Data Processing")
- get_or_create_endpoint $SAHARA_SERVICE \
+ get_or_create_endpoint $sahara_service \
"$REGION_NAME" \
"$SAHARA_SERVICE_PROTOCOL://$SAHARA_SERVICE_HOST:$SAHARA_SERVICE_PORT/v1.1/\$(tenant_id)s" \
"$SAHARA_SERVICE_PROTOCOL://$SAHARA_SERVICE_HOST:$SAHARA_SERVICE_PORT/v1.1/\$(tenant_id)s" \
diff --git a/lib/stackforge b/lib/stackforge
index e6528af..2d80dad 100644
--- a/lib/stackforge
+++ b/lib/stackforge
@@ -29,35 +29,21 @@
# --------
WSME_DIR=$DEST/wsme
PECAN_DIR=$DEST/pecan
+SQLALCHEMY_MIGRATE_DIR=$DEST/sqlalchemy-migrate
# Entry Points
# ------------
# install_stackforge() - Collect source and prepare
function install_stackforge {
- # TODO(sdague): remove this once we get to Icehouse, this just makes
- # for a smoother transition of existing users.
- cleanup_stackforge
-
git_clone $WSME_REPO $WSME_DIR $WSME_BRANCH
setup_package $WSME_DIR
git_clone $PECAN_REPO $PECAN_DIR $PECAN_BRANCH
setup_package $PECAN_DIR
-}
-# cleanup_stackforge() - purge possibly old versions of stackforge libraries
-function cleanup_stackforge {
- # this means we've got an old version installed, lets get rid of it
- # otherwise python hates itself
- for lib in wsme pecan; do
- if ! python -c "import $lib" 2>/dev/null; then
- echo "Found old $lib... removing to ensure consistency"
- local PIP_CMD=$(get_pip_command)
- pip_install $lib
- sudo $PIP_CMD uninstall -y $lib
- fi
- done
+ git_clone $SQLALCHEMY_MIGRATE_REPO $SQLALCHEMY_MIGRATE_DIR $SQLALCHEMY_MIGRATE_BRANCH
+ setup_package $SQLALCHEMY_MIGRATE_DIR
}
# Restore xtrace
diff --git a/lib/tempest b/lib/tempest
index d6d6020..681da1e 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -419,7 +419,7 @@
# install_tempest() - Collect source and prepare
function install_tempest {
git_clone $TEMPEST_REPO $TEMPEST_DIR $TEMPEST_BRANCH
- pip_install "tox<1.7"
+ pip_install tox
}
# init_tempest() - Initialize ec2 images
diff --git a/lib/tls b/lib/tls
index e58e513..62a4ae3 100644
--- a/lib/tls
+++ b/lib/tls
@@ -84,6 +84,7 @@
return 0
fi
+ local i
for i in certs crl newcerts private; do
mkdir -p $ca_dir/$i
done
diff --git a/lib/trove b/lib/trove
index 6877d0f..db9af1d 100644
--- a/lib/trove
+++ b/lib/trove
@@ -76,21 +76,20 @@
# service trove admin # if enabled
function create_trove_accounts {
- # Trove
- SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
- SERVICE_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+ local service_tenant=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+ local service_role=$(openstack role list | awk "/ admin / { print \$2 }")
if [[ "$ENABLED_SERVICES" =~ "trove" ]]; then
- TROVE_USER=$(get_or_create_user "trove" \
- "$SERVICE_PASSWORD" $SERVICE_TENANT)
- get_or_add_user_role $SERVICE_ROLE $TROVE_USER $SERVICE_TENANT
+ local trove_user=$(get_or_create_user "trove" \
+ "$SERVICE_PASSWORD" $service_tenant)
+ get_or_add_user_role $service_role $trove_user $service_tenant
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
- TROVE_SERVICE=$(get_or_create_service "trove" \
+ local trove_service=$(get_or_create_service "trove" \
"database" "Trove Service")
- get_or_create_endpoint $TROVE_SERVICE \
+ get_or_create_endpoint $trove_service \
"$REGION_NAME" \
"http://$SERVICE_HOST:8779/v1.0/\$(tenant_id)s" \
"http://$SERVICE_HOST:8779/v1.0/\$(tenant_id)s" \
@@ -237,6 +236,7 @@
# stop_trove() - Stop running processes
function stop_trove {
# Kill the trove screen windows
+ local serv
for serv in tr-api tr-tmgr tr-cond; do
screen_stop $serv
done
diff --git a/lib/zaqar b/lib/zaqar
new file mode 100644
index 0000000..0d33df2
--- /dev/null
+++ b/lib/zaqar
@@ -0,0 +1,211 @@
+# lib/zaqar
+# Install and start **Zaqar** service
+
+# To enable a minimal set of Zaqar services, add the following to localrc:
+#
+# enable_service zaqar-server
+#
+# Dependencies:
+# - functions
+# - OS_AUTH_URL for auth in api
+# - DEST set to the destination directory
+# - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api
+# - STACK_USER service user
+
+# stack.sh
+# ---------
+# install_zaqar
+# configure_zaqar
+# init_zaqar
+# start_zaqar
+# stop_zaqar
+# cleanup_zaqar
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Defaults
+# --------
+
+# Set up default directories
+ZAQAR_DIR=$DEST/zaqar
+ZAQARCLIENT_DIR=$DEST/python-zaqarclient
+ZAQAR_CONF_DIR=/etc/zaqar
+ZAQAR_CONF=$ZAQAR_CONF_DIR/zaqar.conf
+ZAQAR_API_LOG_DIR=/var/log/zaqar
+ZAQAR_API_LOG_FILE=$ZAQAR_API_LOG_DIR/queues.log
+ZAQAR_AUTH_CACHE_DIR=${ZAQAR_AUTH_CACHE_DIR:-/var/cache/zaqar}
+
+# Support potential entry-points console scripts
+ZAQAR_BIN_DIR=$(get_python_exec_prefix)
+
+# Set up database backend
+ZAQAR_BACKEND=${ZAQAR_BACKEND:-mongodb}
+
+
+# Set Zaqar repository
+ZAQAR_REPO=${ZAQAR_REPO:-${GIT_BASE}/openstack/zaqar.git}
+ZAQAR_BRANCH=${ZAQAR_BRANCH:-master}
+
+# Set client library repository
+ZAQARCLIENT_REPO=${ZAQARCLIENT_REPO:-${GIT_BASE}/openstack/python-zaqarclient.git}
+ZAQARCLIENT_BRANCH=${ZAQARCLIENT_BRANCH:-master}
+
+# Set Zaqar Connection Info
+ZAQAR_SERVICE_HOST=${ZAQAR_SERVICE_HOST:-$SERVICE_HOST}
+ZAQAR_SERVICE_PORT=${ZAQAR_SERVICE_PORT:-8888}
+ZAQAR_SERVICE_PROTOCOL=${ZAQAR_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
+
+# Tell Tempest this project is present
+TEMPEST_SERVICES+=,zaqar
+
+
+# Functions
+# ---------
+
+# Test if any Zaqar services are enabled
+# is_zaqar_enabled
+function is_zaqar_enabled {
+ [[ ,${ENABLED_SERVICES} =~ ,"zaqar-" ]] && return 0
+ return 1
+}
+
+# cleanup_zaqar() - Remove residual data files, anything left over from previous
+# runs that a clean run would need to clean up
+function cleanup_zaqar {
+ if ! timeout $SERVICE_TIMEOUT sh -c "while ! mongo zaqar --eval 'db.dropDatabase();'; do sleep 1; done"; then
+ die $LINENO "Mongo DB did not start"
+ else
+ full_version=$(mongo zaqar --eval 'db.dropDatabase();')
+ mongo_version=`echo $full_version | cut -d' ' -f4`
+ required_mongo_version='2.2'
+ if [[ $mongo_version < $required_mongo_version ]]; then
+ die $LINENO "Zaqar needs Mongo DB version >= 2.2 to run."
+ fi
+ fi
+}
+
+# configure_zaqarclient() - Set config files, create data dirs, etc
+function configure_zaqarclient {
+ setup_develop $ZAQARCLIENT_DIR
+}
+
+# configure_zaqar() - Set config files, create data dirs, etc
+function configure_zaqar {
+ setup_develop $ZAQAR_DIR
+
+ [ ! -d $ZAQAR_CONF_DIR ] && sudo mkdir -m 755 -p $ZAQAR_CONF_DIR
+ sudo chown $USER $ZAQAR_CONF_DIR
+
+ [ ! -d $ZAQAR_API_LOG_DIR ] && sudo mkdir -m 755 -p $ZAQAR_API_LOG_DIR
+ sudo chown $USER $ZAQAR_API_LOG_DIR
+
+ iniset $ZAQAR_CONF DEFAULT verbose True
+ iniset $ZAQAR_CONF DEFAULT use_syslog $SYSLOG
+ iniset $ZAQAR_CONF DEFAULT log_file $ZAQAR_API_LOG_FILE
+ iniset $ZAQAR_CONF 'drivers:transport:wsgi' bind $ZAQAR_SERVICE_HOST
+
+ iniset $ZAQAR_CONF keystone_authtoken auth_protocol http
+ iniset $ZAQAR_CONF keystone_authtoken admin_user zaqar
+ iniset $ZAQAR_CONF keystone_authtoken admin_password $SERVICE_PASSWORD
+ iniset $ZAQAR_CONF keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
+ iniset $ZAQAR_CONF keystone_authtoken signing_dir $ZAQAR_AUTH_CACHE_DIR
+
+ if [ "$ZAQAR_BACKEND" = 'mysql' ] || [ "$ZAQAR_BACKEND" = 'postgresql' ] ; then
+ iniset $ZAQAR_CONF drivers storage sqlalchemy
+ iniset $ZAQAR_CONF 'drivers:storage:sqlalchemy' uri `database_connection_url zaqar`
+ elif [ "$ZAQAR_BACKEND" = 'mongodb' ] ; then
+ iniset $ZAQAR_CONF drivers storage mongodb
+ iniset $ZAQAR_CONF 'drivers:storage:mongodb' uri mongodb://localhost:27017/zaqar
+ configure_mongodb
+ cleanup_zaqar
+ fi
+}
+
+function configure_mongodb {
+ # Set nssize to 2GB. This increases the number of namespaces supported
+ # # per database.
+ if is_ubuntu; then
+ sudo sed -i -e "
+ s|[^ \t]*#[ \t]*\(nssize[ \t]*=.*\$\)|\1|
+ s|^\(nssize[ \t]*=[ \t]*\).*\$|\1 2047|
+ " /etc/mongodb.conf
+ restart_service mongodb
+ elif is_fedora; then
+ sudo sed -i '/--nssize/!s/OPTIONS=\"/OPTIONS=\"--nssize 2047 /' /etc/sysconfig/mongod
+ restart_service mongod
+ fi
+}
+
+# init_zaqar() - Initialize etc.
+function init_zaqar {
+ # Create cache dir
+ sudo mkdir -p $ZAQAR_AUTH_CACHE_DIR
+ sudo chown $STACK_USER $ZAQAR_AUTH_CACHE_DIR
+ rm -f $ZAQAR_AUTH_CACHE_DIR/*
+}
+
+# install_zaqar() - Collect source and prepare
+function install_zaqar {
+ git_clone $ZAQAR_REPO $ZAQAR_DIR $ZAQAR_BRANCH
+ setup_develop $ZAQAR_DIR
+}
+
+# install_zaqarclient() - Collect source and prepare
+function install_zaqarclient {
+ git_clone $ZAQARCLIENT_REPO $ZAQARCLIENT_DIR $ZAQARCLIENT_BRANCH
+ setup_develop $ZAQARCLIENT_DIR
+}
+
+# start_zaqar() - Start running processes, including screen
+function start_zaqar {
+ if [[ "$USE_SCREEN" = "False" ]]; then
+ screen_it zaqar-server "zaqar-server --config-file $ZAQAR_CONF --daemon"
+ else
+ screen_it zaqar-server "zaqar-server --config-file $ZAQAR_CONF"
+ fi
+
+ echo "Waiting for Zaqar to start..."
+ if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT/v1/health; do sleep 1; done"; then
+ die $LINENO "Zaqar did not start"
+ fi
+}
+
+# stop_zaqar() - Stop running processes
+function stop_zaqar {
+ # Kill the zaqar screen windows
+ for serv in zaqar-server; do
+ screen -S $SCREEN_NAME -p $serv -X kill
+ done
+}
+
+function create_zaqar_accounts {
+ SERVICE_TENANT=$(openstack project list | awk "/ $SERVICE_TENANT_NAME / { print \$2 }")
+ ADMIN_ROLE=$(openstack role list | awk "/ admin / { print \$2 }")
+
+ ZAQAR_USER=$(get_or_create_user "zaqar" \
+ "$SERVICE_PASSWORD" $SERVICE_TENANT)
+ get_or_add_user_role $ADMIN_ROLE $ZAQAR_USER $SERVICE_TENANT
+
+ if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
+
+ ZAQAR_SERVICE=$(get_or_create_service "zaqar" \
+ "queuing" "Zaqar Service")
+ get_or_create_endpoint $ZAQAR_SERVICE \
+ "$REGION_NAME" \
+ "$ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT" \
+ "$ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT" \
+ "$ZAQAR_SERVICE_PROTOCOL://$ZAQAR_SERVICE_HOST:$ZAQAR_SERVICE_PORT"
+ fi
+
+}
+
+
+# Restore xtrace
+$XTRACE
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/stack.sh b/stack.sh
index 7a7655c..f267490 100755
--- a/stack.sh
+++ b/stack.sh
@@ -631,7 +631,11 @@
if [[ $r -ne 0 ]]; then
echo "Error on exit"
- ./tools/worlddump.py -d $LOGDIR
+ if [[ -z $LOGDIR ]]; then
+ ./tools/worlddump.py
+ else
+ ./tools/worlddump.py -d $LOGDIR
+ fi
fi
exit $r
@@ -674,7 +678,7 @@
fi
# Do the ugly hacks for broken packages and distros
-$TOP_DIR/tools/fixup_stuff.sh
+source $TOP_DIR/tools/fixup_stuff.sh
# Extras Pre-install
@@ -808,6 +812,7 @@
if is_service_enabled heat; then
install_heat
+ install_heat_other
cleanup_heat
configure_heat
fi
@@ -1267,6 +1272,10 @@
init_heat
echo_summary "Starting Heat"
start_heat
+ if [ "$HEAT_CREATE_TEST_IMAGE" = "True" ]; then
+ echo_summary "Building Heat functional test image"
+ build_heat_functional_test_image
+ fi
fi
diff --git a/stackrc b/stackrc
index fb84366..ad7da6c 100644
--- a/stackrc
+++ b/stackrc
@@ -52,6 +52,18 @@
ENABLED_SERVICES+=,rabbit,tempest,mysql
fi
+# SQLAlchemy supports multiple database drivers for each database server
+# type. For example, deployer may use MySQLdb, MySQLConnector, or oursql
+# to access MySQL database.
+#
+# When defined, the variable controls which database driver is used to
+# connect to database server. Otherwise using default driver defined for
+# each database type.
+#
+# You can find the list of currently supported drivers for each database
+# type at: http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html
+# SQLALCHEMY_DATABASE_DRIVER="mysqldb"
+
# Global toggle for enabling services under mod_wsgi. If this is set to
# ``True`` all services that use HTTPD + mod_wsgi as the preferred method of
# deployment, will be deployed under Apache. If this is set to ``False`` all
@@ -124,6 +136,10 @@
CINDERCLIENT_REPO=${CINDERCLIENT_REPO:-${GIT_BASE}/openstack/python-cinderclient.git}
CINDERCLIENT_BRANCH=${CINDERCLIENT_BRANCH:-master}
+# diskimage-builder
+DIB_REPO=${DIB_REPO:-${GIT_BASE}/openstack/diskimage-builder.git}
+DIB_BRANCH=${DIB_BRANCH:-master}
+
# image catalog service
GLANCE_REPO=${GLANCE_REPO:-${GIT_BASE}/openstack/glance.git}
GLANCE_BRANCH=${GLANCE_BRANCH:-master}
@@ -140,6 +156,14 @@
HEATCLIENT_REPO=${HEATCLIENT_REPO:-${GIT_BASE}/openstack/python-heatclient.git}
HEATCLIENT_BRANCH=${HEATCLIENT_BRANCH:-master}
+# heat-cfntools server agent
+HEAT_CFNTOOLS_REPO=${HEAT_CFNTOOLS_REPO:-${GIT_BASE}/openstack/heat-cfntools.git}
+HEAT_CFNTOOLS_BRANCH=${HEAT_CFNTOOLS_BRANCH:-master}
+
+# heat example templates and elements
+HEAT_TEMPLATES_REPO=${HEAT_TEMPLATES_REPO:-${GIT_BASE}/openstack/heat-templates.git}
+HEAT_TEMPLATES_BRANCH=${HEAT_TEMPLATES_BRANCH:-master}
+
# django powered web control panel for openstack
HORIZON_REPO=${HORIZON_REPO:-${GIT_BASE}/openstack/horizon.git}
HORIZON_BRANCH=${HORIZON_BRANCH:-master}
@@ -176,10 +200,22 @@
NOVACLIENT_REPO=${NOVACLIENT_REPO:-${GIT_BASE}/openstack/python-novaclient.git}
NOVACLIENT_BRANCH=${NOVACLIENT_BRANCH:-master}
+# os-apply-config configuration template tool
+OAC_REPO=${OAC_REPO:-${GIT_BASE}/openstack/os-apply-config.git}
+OAC_BRANCH=${OAC_BRANCH:-master}
+
+# os-collect-config configuration agent
+OCC_REPO=${OCC_REPO:-${GIT_BASE}/openstack/os-collect-config.git}
+OCC_BRANCH=${OCC_BRANCH:-master}
+
# consolidated openstack python client
OPENSTACKCLIENT_REPO=${OPENSTACKCLIENT_REPO:-${GIT_BASE}/openstack/python-openstackclient.git}
OPENSTACKCLIENT_BRANCH=${OPENSTACKCLIENT_BRANCH:-master}
+# os-refresh-config configuration run-parts tool
+ORC_REPO=${ORC_REPO:-${GIT_BASE}/openstack/os-refresh-config.git}
+ORC_BRANCH=${ORC_BRANCH:-master}
+
# cliff command line framework
CLIFF_REPO=${CLIFF_REPO:-${GIT_BASE}/openstack/cliff.git}
CLIFF_BRANCH=${CLIFF_BRANCH:-master}
@@ -250,10 +286,9 @@
TEMPEST_REPO=${TEMPEST_REPO:-${GIT_BASE}/openstack/tempest.git}
TEMPEST_BRANCH=${TEMPEST_BRANCH:-master}
-
-# diskimage-builder
-DIB_REPO=${DIB_REPO:-${GIT_BASE}/openstack/diskimage-builder.git}
-DIB_BRANCH=${DIB_BRANCH:-master}
+# Tripleo elements for diskimage-builder images
+TIE_REPO=${TIE_REPO:-${GIT_BASE}/openstack/tripleo-image-elements.git}
+TIE_BRANCH=${TIE_BRANCH:-master}
# a websockets/html5 or flash powered VNC console for vm instances
NOVNC_REPO=${NOVNC_REPO:-https://github.com/kanaka/noVNC.git}
@@ -284,6 +319,10 @@
PECAN_REPO=${PECAN_REPO:-${GIT_BASE}/stackforge/pecan.git}
PECAN_BRANCH=${PECAN_BRANCH:-master}
+# sqlalchemy-migrate
+SQLALCHEMY_MIGRATE_REPO=${SQLALCHEMY_MIGRATE_REPO:-${GIT_BASE}/stackforge/sqlalchemy-migrate.git}
+SQLALCHEMY_MIGRATE_BRANCH=${SQLALCHEMY_MIGRATE_BRANCH:-master}
+
# Nova hypervisor configuration. We default to libvirt with **kvm** but will
# drop back to **qemu** if we are unable to load the kvm module. ``stack.sh`` can
@@ -366,6 +405,15 @@
DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-0.3.0-x86_64-disk}
IMAGE_URLS=${IMAGE_URLS:-"https://github.com/downloads/citrix-openstack/warehouse/cirros-0.3.0-x86_64-disk.vhd.tgz"}
IMAGE_URLS+=",http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-uec.tar.gz";;
+ ironic)
+ # Ironic can do both partition and full disk images, depending on the driver
+ if [[ "$IRONIC_DEPLOY_DRIVER" == "agent_ssh" ]]; then
+ DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-x86_64-disk}
+ else
+ DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-x86_64-uec}
+ fi
+ IMAGE_URLS=${IMAGE_URLS:-"http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-uec.tar.gz"}
+ IMAGE_URLS+=",http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-x86_64-disk.img";;
*) # Default to Cirros with kernel, ramdisk and disk image
DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec}
IMAGE_URLS=${IMAGE_URLS:-"http://download.cirros-cloud.net/${CIRROS_VERSION}/cirros-${CIRROS_VERSION}-${CIRROS_ARCH}-uec.tar.gz"};;
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 50fb31c..d849302 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -20,20 +20,24 @@
# - pre-install hgtools to work around a bug in RHEL6 distribute
# - install nose 1.1 from EPEL
-set -o errexit
-set -o xtrace
+# If TOP_DIR is set we're being sourced rather than running stand-alone
+# or in a sub-shell
+if [[ -z "$TOP_DIR" ]]; then
+ set -o errexit
+ set -o xtrace
-# Keep track of the current directory
-TOOLS_DIR=$(cd $(dirname "$0") && pwd)
-TOP_DIR=$(cd $TOOLS_DIR/..; pwd)
+ # Keep track of the current directory
+ TOOLS_DIR=$(cd $(dirname "$0") && pwd)
+ TOP_DIR=$(cd $TOOLS_DIR/..; pwd)
-# Change dir to top of devstack
-cd $TOP_DIR
+ # Change dir to top of devstack
+ cd $TOP_DIR
-# Import common functions
-source $TOP_DIR/functions
+ # Import common functions
+ source $TOP_DIR/functions
-FILES=$TOP_DIR/files
+ FILES=$TOP_DIR/files
+fi
# Keystone Port Reservation
# -------------------------
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..c8a603b
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,13 @@
+[tox]
+minversion = 1.6
+skipsdist = True
+envlist = bashate
+
+[testenv]
+usedevelop = False
+install_command = pip install {opts} {packages}
+
+[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"