Add support for Docker as Nova hypervisor
* Add basic support for hypervisor plugins in lib/nova_plugins
* Add lib/nova_plugins/hypervisor-docker to use Docker as a Nova
hypervisor.
* Add tools/install_docker.sh to install the Docker daemon and
registry container, download base image and import
* Configure Nova to use docker plugin
* Add docker exercise and skip unsupported ones
Nova blueprint: new-hypervisor-docker
Change-Id: I9e7065b562dce2ce853def583ab1165886612227
diff --git a/README.md b/README.md
index 6426e9a..99e9838 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,14 @@
IMPORTANT: Be sure to carefully read `stack.sh` and any other scripts you execute before you run them, as they install software and may alter your networking configuration. We strongly recommend that you run `stack.sh` in a clean and disposable vm when you are first getting started.
-# Devstack on Xenserver
+# DevStack on Xenserver
If you would like to use Xenserver as the hypervisor, please refer to the instructions in `./tools/xen/README.md`.
+# DevStack on Docker
+
+If you would like to use Docker as the hypervisor, please refer to the instructions in `./tools/docker/README.md`.
+
# Versions
The devstack master branch generally points to trunk versions of OpenStack components. For older, stable versions, look for branches named stable/[release] in the DevStack repo. For example, you can do the following to create a diablo OpenStack cloud:
diff --git a/clean.sh b/clean.sh
index f7d15df..a443ac8 100755
--- a/clean.sh
+++ b/clean.sh
@@ -64,6 +64,11 @@
cleanup_neutron
cleanup_swift
+# Do the hypervisor cleanup until this can be moved back into lib/nova
+if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ cleanup_nova_hypervisor
+fi
+
# cinder doesn't always clean up the volume group as it might be used elsewhere...
# clean it up if it is a loop device
VG_DEV=$(sudo losetup -j $DATA_DIR/${VOLUME_GROUP}-backing-file | awk -F':' '/backing-file/ { print $1}')
diff --git a/exercises/boot_from_volume.sh b/exercises/boot_from_volume.sh
index 36524ed..fe27bd0 100755
--- a/exercises/boot_from_volume.sh
+++ b/exercises/boot_from_volume.sh
@@ -44,6 +44,9 @@
# the exercise is skipped
is_service_enabled cinder || exit 55
+# Also skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
# Instance type to create
DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
diff --git a/exercises/docker.sh b/exercises/docker.sh
new file mode 100755
index 0000000..0672bc0
--- /dev/null
+++ b/exercises/docker.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env bash
+
+# **docker**
+
+# Test Docker hypervisor
+
+echo "*********************************************************************"
+echo "Begin DevStack Exercise: $0"
+echo "*********************************************************************"
+
+# This script exits on an error so that errors don't compound and you see
+# only the first error that occurred.
+set -o errexit
+
+# Print the commands being run so that we can see the command that triggers
+# an error. It is also useful for following allowing as the install occurs.
+set -o xtrace
+
+
+# Settings
+# ========
+
+# Keep track of the current directory
+EXERCISE_DIR=$(cd $(dirname "$0") && pwd)
+TOP_DIR=$(cd $EXERCISE_DIR/..; pwd)
+
+# Import common functions
+source $TOP_DIR/functions
+
+# Import configuration
+source $TOP_DIR/openrc
+
+# Import exercise configuration
+source $TOP_DIR/exerciserc
+
+# Skip if the hypervisor is not Docker
+[[ "$VIRT_DRIVER" == "docker" ]] || exit 55
+
+# Import docker functions and declarations
+source $TOP_DIR/lib/nova_plugins/hypervisor-docker
+
+# Image and flavor are ignored but the CLI requires them...
+
+# Instance type to create
+DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
+
+# Boot this image, use first AMI image if unset
+DEFAULT_IMAGE_NAME=${DEFAULT_IMAGE_NAME:-ami}
+
+# Instance name
+VM_NAME=ex-docker
+
+
+# Launching a server
+# ==================
+
+# Grab the id of the image to launch
+IMAGE=$(glance image-list | egrep " $DOCKER_IMAGE_NAME:latest " | get_field 1)
+die_if_not_set $LINENO IMAGE "Failure getting image $DOCKER_IMAGE_NAME"
+
+# Select a flavor
+INSTANCE_TYPE=$(nova flavor-list | grep $DEFAULT_INSTANCE_TYPE | get_field 1)
+if [[ -z "$INSTANCE_TYPE" ]]; then
+ # grab the first flavor in the list to launch if default doesn't exist
+ INSTANCE_TYPE=$(nova flavor-list | head -n 4 | tail -n 1 | get_field 1)
+fi
+
+# Clean-up from previous runs
+nova delete $VM_NAME || true
+if ! timeout $ACTIVE_TIMEOUT sh -c "while nova show $VM_NAME; do sleep 1; done"; then
+ die $LINENO "server didn't terminate!"
+fi
+
+# Boot instance
+# -------------
+
+VM_UUID=$(nova boot --flavor $INSTANCE_TYPE --image $IMAGE $VM_NAME | grep ' id ' | get_field 2)
+die_if_not_set $LINENO VM_UUID "Failure launching $VM_NAME"
+
+# Check that the status is active within ACTIVE_TIMEOUT seconds
+if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova show $VM_UUID | grep status | grep -q ACTIVE; do sleep 1; done"; then
+ die $LINENO "server didn't become active!"
+fi
+
+# Get the instance IP
+IP=$(nova show $VM_UUID | grep "$PRIVATE_NETWORK_NAME" | get_field 2)
+die_if_not_set $LINENO IP "Failure retrieving IP address"
+
+# Private IPs can be pinged in single node deployments
+ping_check "$PRIVATE_NETWORK_NAME" $IP $BOOT_TIMEOUT
+
+# Clean up
+# --------
+
+# Delete instance
+nova delete $VM_UUID || die $LINENO "Failure deleting instance $VM_NAME"
+if ! timeout $TERMINATE_TIMEOUT sh -c "while nova list | grep -q $VM_UUID; do sleep 1; done"; then
+ die $LINENO "Server $VM_NAME not deleted"
+fi
+
+set +o xtrace
+echo "*********************************************************************"
+echo "SUCCESS: End DevStack Exercise: $0"
+echo "*********************************************************************"
+
diff --git a/exercises/euca.sh b/exercises/euca.sh
index b8b283a..64c0014 100755
--- a/exercises/euca.sh
+++ b/exercises/euca.sh
@@ -41,6 +41,9 @@
# Import exercise configuration
source $TOP_DIR/exerciserc
+# Skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
# Instance type to create
DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
diff --git a/exercises/floating_ips.sh b/exercises/floating_ips.sh
index f93a727..2833b65 100755
--- a/exercises/floating_ips.sh
+++ b/exercises/floating_ips.sh
@@ -38,6 +38,9 @@
# Import exercise configuration
source $TOP_DIR/exerciserc
+# Skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
# Instance type to create
DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
diff --git a/exercises/sec_groups.sh b/exercises/sec_groups.sh
index 6b67291..7d80570 100755
--- a/exercises/sec_groups.sh
+++ b/exercises/sec_groups.sh
@@ -33,6 +33,9 @@
# Import exercise configuration
source $TOP_DIR/exerciserc
+# Skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
# Testing Security Groups
# =======================
diff --git a/exercises/volumes.sh b/exercises/volumes.sh
index 028d19b..e536d16 100755
--- a/exercises/volumes.sh
+++ b/exercises/volumes.sh
@@ -42,6 +42,9 @@
# exercise is skipped.
is_service_enabled cinder || exit 55
+# Also skip if the hypervisor is Docker
+[[ "$VIRT_DRIVER" == "docker" ]] && exit 55
+
# Instance type to create
DEFAULT_INSTANCE_TYPE=${DEFAULT_INSTANCE_TYPE:-m1.tiny}
diff --git a/lib/nova b/lib/nova
index 842c098..3486aa8 100644
--- a/lib/nova
+++ b/lib/nova
@@ -169,6 +169,13 @@
fi
sudo rm -rf $NOVA_STATE_PATH $NOVA_AUTH_CACHE_DIR
+
+ # NOTE(dtroyer): This really should be called from here but due to the way
+ # nova abuses the _cleanup() function we're moving it
+ # directly into cleanup.sh until this can be fixed.
+ #if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ # cleanup_nova_hypervisor
+ #fi
}
# configure_nova_rootwrap() - configure Nova's rootwrap
@@ -650,7 +657,9 @@
# install_nova() - Collect source and prepare
function install_nova() {
if is_service_enabled n-cpu; then
- if [[ "$VIRT_DRIVER" = 'libvirt' ]]; then
+ if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ install_nova_hypervisor
+ elif [[ "$VIRT_DRIVER" = 'libvirt' ]]; then
if is_ubuntu; then
install_package kvm
install_package libvirt-bin
@@ -728,6 +737,9 @@
screen_it n-cpu "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-compute --config-file $NOVA_CONF_BOTTOM"
done
else
+ if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ start_nova_hypervisor
+ fi
screen_it n-cpu "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-compute --config-file $NOVA_CONF_BOTTOM"
fi
screen_it n-crt "cd $NOVA_DIR && $NOVA_BIN_DIR/nova-cert"
@@ -754,6 +766,9 @@
for serv in n-api n-cpu n-crt n-net n-sch n-novnc n-xvnc n-cauth n-spice n-cond n-cond n-cell n-cell n-api-meta; do
screen -S $SCREEN_NAME -p $serv -X kill
done
+ if is_service_enabled n-cpu && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ stop_nova_hypervisor
+ fi
}
diff --git a/lib/nova_plugins/hypervisor-docker b/lib/nova_plugins/hypervisor-docker
new file mode 100644
index 0000000..4c8fc27
--- /dev/null
+++ b/lib/nova_plugins/hypervisor-docker
@@ -0,0 +1,132 @@
+# lib/nova_plugins/docker
+# Configure the Docker hypervisor
+
+# Enable with:
+# VIRT_DRIVER=docker
+
+# Dependencies:
+# ``functions`` file
+# ``nova`` and ``glance`` configurations
+
+# install_nova_hypervisor - install any external requirements
+# configure_nova_hypervisor - make configuration changes, including those to other services
+# start_nova_hypervisor - start any external services
+# stop_nova_hypervisor - stop any external services
+# cleanup_nova_hypervisor - remove transient data and cache
+
+# Save trace setting
+MY_XTRACE=$(set +o | grep xtrace)
+set +o xtrace
+
+
+# Defaults
+# --------
+
+# Set up default directories
+DOCKER_DIR=$DEST/docker
+DOCKER_REPO=${DOCKER_REPO:-https://github.com/dotcloud/openstack-docker.git}
+DOCKER_BRANCH=${DOCKER_BRANCH:-master}
+
+DOCKER_UNIX_SOCKET=/var/run/docker.sock
+DOCKER_PID_FILE=/var/run/docker.pid
+DOCKER_REGISTRY_PORT=${DOCKER_REGISTRY_PORT:-5042}
+
+DOCKER_IMAGE=${DOCKER_IMAGE:-http://get.docker.io/images/openstack/docker-ut.tar.gz}
+DOCKER_IMAGE_NAME=docker-busybox
+DOCKER_REGISTRY_IMAGE=${DOCKER_REGISTRY_IMAGE:-http://get.docker.io/images/openstack/docker-registry.tar.gz}
+DOCKER_REGISTRY_IMAGE_NAME=docker-registry
+DOCKER_REPOSITORY_NAME=${SERVICE_HOST}:${DOCKER_REGISTRY_PORT}/${DOCKER_IMAGE_NAME}
+
+DOCKER_PACKAGE_VERSION=${DOCKER_PACKAGE_VERSION:-0.6.1}
+DOCKER_APT_REPO=${DOCKER_APT_REPO:-https://get.docker.io/ubuntu}
+
+
+# Entry Points
+# ------------
+
+# clean_nova_hypervisor - Clean up an installation
+function cleanup_nova_hypervisor() {
+ stop_service docker
+
+ # Clean out work area
+ sudo rm -rf /var/lib/docker
+}
+
+# configure_nova_hypervisor - Set config files, create data dirs, etc
+function configure_nova_hypervisor() {
+ git_clone $DOCKER_REPO $DOCKER_DIR $DOCKER_BRANCH
+
+ ln -snf ${DOCKER_DIR}/nova-driver $NOVA_DIR/nova/virt/docker
+
+ iniset $NOVA_CONF DEFAULT compute_driver docker.DockerDriver
+ iniset $GLANCE_API_CONF DEFAULT container_formats ami,ari,aki,bare,ovf,docker
+
+ sudo cp -p ${DOCKER_DIR}/nova-driver/docker.filters $NOVA_CONF_DIR/rootwrap.d
+}
+
+# install_nova_hypervisor() - Install external components
+function install_nova_hypervisor() {
+ # So far this is Ubuntu only
+ if ! is_ubuntu; then
+ die $LINENO "Docker is only supported on Ubuntu at this time"
+ fi
+
+ # Make sure Docker is installed
+ if ! is_package_installed lxc-docker; then
+ die $LINENO "Docker is not installed. Please run tools/docker/install_docker.sh"
+ fi
+
+ local docker_pid
+ read docker_pid <$DOCKER_PID_FILE
+ if [[ -z $docker_pid ]] || ! ps -p $docker_pid | grep [d]ocker; then
+ die $LINENO "Docker not running"
+ fi
+}
+
+# start_nova_hypervisor - Start any required external services
+function start_nova_hypervisor() {
+ local docker_pid
+ read docker_pid <$DOCKER_PID_FILE
+ if [[ -z $docker_pid ]] || ! ps -p $docker_pid | grep [d]ocker; then
+ die $LINENO "Docker not running, start the daemon"
+ fi
+
+ # Start the Docker registry container
+ docker run -d -p ${DOCKER_REGISTRY_PORT}:5000 \
+ -e SETTINGS_FLAVOR=openstack -e OS_USERNAME=${OS_USERNAME} \
+ -e OS_PASSWORD=${OS_PASSWORD} -e OS_TENANT_NAME=${OS_TENANT_NAME} \
+ -e OS_GLANCE_URL="${SERVICE_PROTOCOL}://${GLANCE_HOSTPORT}" \
+ -e OS_AUTH_URL=${OS_AUTH_URL} \
+ $DOCKER_REGISTRY_IMAGE_NAME ./docker-registry/run.sh
+
+ echo "Waiting for docker registry to start..."
+ DOCKER_REGISTRY=${SERVICE_HOST}:${DOCKER_REGISTRY_PORT}
+ if ! timeout $SERVICE_TIMEOUT sh -c "while ! curl -s $DOCKER_REGISTRY; do sleep 1; done"; then
+ die $LINENO "docker-registry did not start"
+ fi
+
+ # Tag image if not already tagged
+ if ! docker images | grep $DOCKER_REPOSITORY_NAME; then
+ docker tag $DOCKER_IMAGE_NAME $DOCKER_REPOSITORY_NAME
+ fi
+
+ # Make sure we copied the image in Glance
+ DOCKER_IMAGE=$(glance image-list | egrep " $DOCKER_IMAGE_NAME ")
+ if ! is_set DOCKER_IMAGE ; then
+ docker push $DOCKER_REPOSITORY_NAME
+ fi
+}
+
+# stop_nova_hypervisor - Stop any external services
+function stop_nova_hypervisor() {
+ # Stop the docker registry container
+ docker kill $(docker ps | grep docker-registry | cut -d' ' -f1)
+}
+
+
+# Restore xtrace
+$MY_XTRACE
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/stack.sh b/stack.sh
index 8f59328..c3f69ad 100755
--- a/stack.sh
+++ b/stack.sh
@@ -319,6 +319,13 @@
source $TOP_DIR/lib/baremetal
source $TOP_DIR/lib/ldap
+# Look for Nova hypervisor plugin
+NOVA_PLUGINS=$TOP_DIR/lib/nova_plugins
+if is_service_enabled nova && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ # Load plugin
+ source $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER
+fi
+
# Set the destination directories for other OpenStack projects
OPENSTACKCLIENT_DIR=$DEST/python-openstackclient
@@ -1013,6 +1020,10 @@
init_cinder
fi
+
+# Compute Service
+# ---------------
+
if is_service_enabled nova; then
echo_summary "Configuring Nova"
# Rebuild the config file from scratch
@@ -1027,10 +1038,15 @@
fi
+ if [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ # Configure hypervisor plugin
+ configure_nova_hypervisor
+
+
# XenServer
# ---------
- if [ "$VIRT_DRIVER" = 'xenserver' ]; then
+ elif [ "$VIRT_DRIVER" = 'xenserver' ]; then
echo_summary "Using XenServer virtualization driver"
if [ -z "$XENAPI_CONNECTION_URL" ]; then
die $LINENO "XENAPI_CONNECTION_URL is not specified"
diff --git a/tools/docker/README.md b/tools/docker/README.md
new file mode 100644
index 0000000..976111f
--- /dev/null
+++ b/tools/docker/README.md
@@ -0,0 +1,13 @@
+# DevStack on Docker
+
+Using Docker as Nova's hypervisor requries two steps:
+
+* Configure DevStack by adding the following to `localrc`::
+
+ VIRT_DRIVER=docker
+
+* Download and install the Docker service and images::
+
+ tools/docker/install_docker.sh
+
+After this, `stack.sh` should run as normal.
diff --git a/tools/docker/install_docker.sh b/tools/docker/install_docker.sh
new file mode 100755
index 0000000..d659ad1
--- /dev/null
+++ b/tools/docker/install_docker.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+# **install_docker.sh** - Do the initial Docker installation and configuration
+
+# install_docker.sh
+#
+# Install docker package and images
+# * downloads a base busybox image and a glance registry image if necessary
+# * install the images in Docker's image cache
+
+
+# Keep track of the current directory
+SCRIPT_DIR=$(cd $(dirname "$0") && pwd)
+TOP_DIR=$(cd $SCRIPT_DIR/../..; pwd)
+
+# Import common functions
+source $TOP_DIR/functions
+
+# Load local configuration
+source $TOP_DIR/stackrc
+
+FILES=$TOP_DIR/files
+
+# Get our defaults
+source $TOP_DIR/lib/nova_plugins/hypervisor-docker
+
+SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}
+
+
+# Install Docker Service
+# ======================
+
+# Stop the auto-repo updates and do it when required here
+NO_UPDATE_REPOS=True
+
+# Set up home repo
+curl https://get.docker.io/gpg | sudo apt-key add -
+install_package python-software-properties && \
+ sudo sh -c "echo deb $DOCKER_APT_REPO docker main > /etc/apt/sources.list.d/docker.list"
+apt_get update
+install_package --force-yes lxc-docker=${DOCKER_PACKAGE_VERSION}
+
+# Start the daemon - restart just in case the package ever auto-starts...
+restart_service docker
+
+echo "Waiting for docker daemon to start..."
+DOCKER_GROUP=$(groups | cut -d' ' -f1)
+CONFIGURE_CMD="while ! /bin/echo -e 'GET /v1.3/version HTTP/1.0\n\n' | socat - unix-connect:$DOCKER_UNIX_SOCKET | grep -q '200 OK'; do
+ # Set the right group on docker unix socket before retrying
+ sudo chgrp $DOCKER_GROUP $DOCKER_UNIX_SOCKET
+ sudo chmod g+rw $DOCKER_UNIX_SOCKET
+ sleep 1
+done"
+if ! timeout $SERVICE_TIMEOUT sh -c "$CONFIGURE_CMD"; then
+ die $LINENO "docker did not start"
+fi
+
+
+# Get Docker image
+if [[ ! -r $FILES/docker-ut.tar.gz ]]; then
+ (cd $FILES; curl -OR $DOCKER_IMAGE)
+fi
+if [[ ! -r $FILES/docker-ut.tar.gz ]]; then
+ die $LINENO "Docker image unavailable"
+fi
+docker import - $DOCKER_IMAGE_NAME <$FILES/docker-ut.tar.gz
+
+# Get Docker registry image
+if [[ ! -r $FILES/docker-registry.tar.gz ]]; then
+ (cd $FILES; curl -OR $DOCKER_REGISTRY_IMAGE)
+fi
+if [[ ! -r $FILES/docker-registry.tar.gz ]]; then
+ die $LINENO "Docker registry image unavailable"
+fi
+docker import - $DOCKER_REGISTRY_IMAGE_NAME <$FILES/docker-registry.tar.gz
diff --git a/unstack.sh b/unstack.sh
index 2268b90..f053bcd 100755
--- a/unstack.sh
+++ b/unstack.sh
@@ -65,6 +65,14 @@
fi
fi
+# Shut down Nova hypervisor plugins after Nova
+NOVA_PLUGINS=$TOP_DIR/lib/nova_plugins
+if is_service_enabled nova && [[ -r $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER ]]; then
+ # Load plugin
+ source $NOVA_PLUGINS/hypervisor-$VIRT_DRIVER
+ stop_nova_hypervisor
+fi
+
# Swift runs daemons
if is_service_enabled s-proxy; then
stop_swift