Bring across disk image code.

This commit is contained in:
Robert Collins 2012-11-10 00:04:13 +13:00
parent 10b9c660d6
commit 5cc195e391
40 changed files with 1112 additions and 0 deletions

100
README.md Normal file
View file

@ -0,0 +1,100 @@
Image building tools for Openstack
==================================
These tools are the components of tripleo (https://github.com/tripleo/demo)
that do the plumbing involved in building disk images. Specific configs live
in the demo repository, while the reusable tools live here.
What tools are there?
---------------------
* disk-image-create -o filename {flavour} [{flavour} ...] : Create an image of
flavour {flavour}, optionally mixing in other flavours.
* ramdisk-image-create -o filename {flavour} [{flavour} ...] : Create a kernel+
ramdisk pair for running maintenance on bare metal machines (deployment,
inventory, burnin etc).
* disk-image-get-kernel filename : Extract the appropriate kernel and ramdisk
to use when doing PXE boot using filename as the image for a machine.
* flavours can be found in the top level flavours directory.
Why?
----
Automation: While users and operators can manually script or put together ram
disks and disk images, mature automation makes customisation and testing easier.
Design
======
Still brief - more details needed.
Images are built using a chroot and bind mounted /proc /sys and /dev. The goal
of the image building process is to produce blank slate machines that have all
the necessary bits to fulfill a specific purpose in the running of an Openstack
cloud: e.g. a nova-compute node.
A flavour is a particular set of code that alters how the image is built, or
runs within the chroot to prepare the image. E.g. the local-config flavour
copies in the http proxy and ssh keys of the user running the image build
process into the image, whereas the vm flavour makes the image build a regular
VM image with partition table and installed grub boot sector.
Existing flavours
-----------------
Flavours are found in the subdirectory flavours. Each flavour is in a directory
named after the flavour itself. Flavours *should* have a README.md in the root
of the flavour directory describing what it is for.
Writing a flavour
-----------------
Make as many of the following subdirectories as you need, depending on what
part of the process you need to customise:
* block-device.d: customise the block device that the image will be made on
(e.g. to make partitions).
* outputs: $IMAGE\_BLOCK\_DEVICE={path}
* inputs: $IMAGE\_BLOCK\_DEVICE={path}
* extra-data.d: pull in extra data from the host environment that hooks may
need during image creation. This should copy any data (such as SSH keys,
http proxy settings and the like) somewhere under $TMP\_HOOKS\_PATH.
* outputs: None
* inputs: $TMP\_HOOKS\_PATH
* pre-install.d: Run code in the chroot before customisation or packages are
installed. A good place to add apt repositories.
* install.d: Runs after pre-install.d in the chroot. This is a good place to
install packages, chain into configuration management tools or do other
image specific operations.
* first-boot.d: Runs inside the image before rc.local. Scripts from here are
good for doing per-instance configuration based on cloud metadata.
Third party flavours
--------------------
Pending implementation. The idea is to have a search path for flavours.
Installation
============
* Clone the repository locally, then add bin to your path.
* Copy sudoers.d/\* into your /etc/sudoers.d/. (Warning, use visudo -c -f
{filename} to check that each one parses successfully on your machine, so you
don't break your machine).
Invocation
==========
The scripts can generally just be run. Options can be set on the command line
or by exporting variables to override those present in lib/img-defaults. -h to
get help.

79
bin/disk-image-create Executable file
View file

@ -0,0 +1,79 @@
#!/bin/bash
set -e
SCRIPTNAME=$(basename $0)
export _LIB=$(dirname $0)/../lib
source $_LIB/die
function show_options () {
echo "Options:"
echo " -a i386|amd64 -- set the architecture of the image"
echo " -o filename -- set the name of the output file"
echo " -x -- turn on tracing"
exit 0
}
TEMP=`getopt -o a:ho:x -n $SCRIPTNAME -- "$@"`
echo "XXX $TEMP"
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
while true ; do
case "$1" in
-a) export ARCH=$2; shift 2 ;;
-o) export IMAGE_NAME=$2; shift 2 ;;
-h) show_options;;
-x) shift; set -x;;
--) shift ; break ;;
*) echo "Internal error!" ; exit 1 ;;
esac
done
for arg do IMAGE_FLAVOUR="$IMAGE_FLAVOUR $arg" ; done
source $_LIB/img-defaults
source $_LIB/img-functions
echo "Building flavours: $IMAGE_FLAVOUR"
echo "If prompted for sudo, install sudoers.d/img-build-sudoers into /etc/sudoers.d and restart the build."
mkdir -p $IMG_PATH
# TODO: make a flavour.
ensure_nbd
mk_build_dir
ensure_base_available
qemu-img create -f qcow2 $TMP_IMAGE_PATH ${IMAGE_SIZE}G
# Should have a grab-next-dev helper ?
NBD_DEV=/dev/nbd0
if [[ $(qemu-nbd --help | grep cache) == *writeback* ]] ; then
CACHE="--cache=writeback"
else
echo "Warning: qemu-nbd without --cache=writeback is /slow/."
CACHE=""
fi
sudo qemu-nbd -c $NBD_DEV $CACHE $TMP_IMAGE_PATH
export EXTRA_UNMOUNT="sudo qemu-nbd -d $NBD_DEV"
export IMAGE_BLOCK_DEVICE=$NBD_DEV
TEMP=`run_d block-device`
echo "$TEMP"
TEMP=`echo "$TEMP" | grep "IMAGE_BLOCK_DEVICE="`
eval "$TEMP"
sudo mkfs -F -t $FS_TYPE -L cloudimg-rootfs ${IMAGE_BLOCK_DEVICE}
mount_tmp_image ${IMAGE_BLOCK_DEVICE}
create_base
run_d extra-data
do_pre_install
do_install
prepare_first_boot
finalise_base
unmount_image
compress_image
save_image $IMAGE_NAME.$IMAGE_TYPE

43
docs/ci.md Normal file
View file

@ -0,0 +1,43 @@
CI needs for image building
===========================
Eventually, if/when TripleO becomes an official Openstack project, all CI for
it should be on Openstack systems. Until then we still need CI.
Jenkins
-------
* Jenkins from jenkins apt repo.
* IRC notification service, notify-only on #triple on freenode, port 7000 ssl.
* Github OAuth plugin, permit all from tripleo organisation, and organisation
members as service admins.
* Grant jenkin builders sudo [may want lxc containers or cloud instances for
security isolation]
* Jobs to build:
* bootstrap VM from-scratch (archive bootstrap.qcow2).
disk-image-create vm devstack -o bootstrap.qcow2 -a i386
* devstack nova-bm execution (archive the resulting image).
Chained off of the bootstrap vm build
ssh into the node, run demo/scripts/demo
* bootstrap VM via image-build chain (archive bm-cloud.qcow2).
disk-image-create vm glance nova-bm swift mysql haproxy-api \
haproxy-mysql cinder quantum rabbitmq -o bootstrap-prod.qcow2
* baremetal SPOF node build (archive the resulting image).
disk-image-create mysql haproxy-mysql haproxy-api local-boot \
rabbitmq -o baremetal-spof.qcow2
* baremetal demo node build (archive the resulting image).
disk-image-create vm glance nova-bm swift cinder quantum \
-o bootstrap-prod.qcow2
* Tempest w/baremetal using libvirt networking as the power API.
take a bootstrap baremetal devstack from above, N VM 'bare metal' nodes,
and run tempest in that environment.

View file

@ -0,0 +1,2 @@
Creates an image prepped to make a devstack baremetal cloud. See
demo/scripts/demo within the built image.

View file

@ -0,0 +1,12 @@
#!/bin/bash
# Add the stack user we recommend folk use.
set -e
set -o xtrace
useradd -G admin -m stack -s /bin/bash
passwd stack <<EOF
stack
stack
EOF

View file

@ -0,0 +1,12 @@
#!/bin/bash
# Configure grub
set -e
set -o xtrace
# XXX: grub-probe on the nbd0 device returns nothing - workaround, manually
# specify modules. https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/1073731
grub-install --modules="biosdisk part_msdos" /dev/nbd0
# XXX: Undiagnosed, but the LABEL=cloudimg-rootfs isn't being picked up: workaround it.
sed -i 's%/dev/nbd0p1%LABEL=cloudimg-rootfs%' /boot/grub/grub.cfg

View file

@ -0,0 +1,10 @@
#!/bin/bash
# Install the image creation toolchain so folk can create their own images
# (also includes the bootstrap-from-devstack facilities needed until we have
# full image mastering of openstack).
set -e
set -o xtrace
apt-get -y install git
sudo -Hiu stack git clone git://github.com/tripleo/demo.git

View file

@ -0,0 +1,7 @@
#!/bin/bash
# Install the haveged daemon so ssh config on startup isn't glacial.
set -e
set -o xtrace
apt-get -y install haveged

View file

@ -0,0 +1,26 @@
#!/bin/bash
# Configure eth1, the baremetal network.
set -e
set -o xtrace
cat << EOF >> /etc/network/interfaces
auto eth1
iface eth1 inet static
# This matches the localrc we have configured for demo environments.
# It is unroutable and not suitable for production: it is a test network.
address 192.0.2.1
netmask 255.255.255.0
# Expose the metadata service needed by the nodes as they boot.
up iptables -t nat -A PREROUTING -d 169.254.169.254 -p tcp -m tcp --dport 80 -j REDIRECT --to-port 8775
# Grant access to the rest of the world by routing via the bootstrap node
# (libvirt rejects traffic from unknown ip addresses, meaning that using
# the default libvirt nat environment requires the MASQUERADE for the bare
# metal nodes unless you reconfigure libvirt as well). Alternatively you
# can create a second bridge on your machine and attached eth0 to that
# (with an appropriate static config (or dhcp on the bridge).
up iptables -t nat -A POSTROUTING -s 192.0.2.0/24 -o eth0 -j MASQUERADE
# This matches the client range defined in localrc.
up ip addr add 192.0.2.33/29 dev eth1
EOF

View file

@ -0,0 +1,8 @@
#!/bin/bash
# Regenerate host keys now. XXX: Really should be a cloud-init task, should get
# that working.
set -e
set -o xtrace
dpkg-reconfigure openssh-server

View file

@ -0,0 +1,2 @@
Copies local user settings such as .ssh/authorized\_keys and $http\_proxy into
the image.

View file

@ -0,0 +1,11 @@
#!/bin/bash
# Save user SSH public key if available.
# (Obviously not suitable for downloadable images).
set -e
source $_LIB/die
[ -n "$TMP_HOOKS_PATH." ] || die "Temp hook path not set"
if [ -e ~/.ssh/authorized_keys ]; then
cat ~/.ssh/authorized_keys > $TMP_HOOKS_PATH/ssh-authorized-keys
fi

View file

@ -0,0 +1,16 @@
#!/bin/bash
# Save the HTTP if one is available.
# XXX: Obviously not suitable for downloadable images.
set -e
set -o xtrace
if [ -n "$http_proxy" ]; then
sudo -Hiu stack dd of=~stack/.profile oflag=append conv=notrunc << EOF
export no_proxy=192.0.2.1
export http_proxy=$http_proxy
EOF
sudo dd of=/etc/apt/apt.conf.d/61-use-http-proxy << _EOF_
Acquire::http::Proxy "$http_proxy";
_EOF_
fi

View file

@ -0,0 +1,10 @@
#!/bin/bash
# Save user SSH public key if available.
# XXX: Obviously not suitable for downloadable images.
set -e
if [ -e "/tmp/in_target.d/ssh-authorized-keys" ]; then
sudo -u stack mkdir ~stack/.ssh
sudo -Hiu stack dd of=~stack/.ssh/authorized_keys oflag=append conv=notrunc if=/tmp/in_target.d/ssh-authorized-keys
fi

View file

@ -0,0 +1 @@
Sets up a nova (kvm) install in the image.

View file

@ -0,0 +1,5 @@
#!/bin/bash
set -e
set -o xtrace
sed -i -r 's/^\s*#(net\.ipv4\.ip_forward=1.*)/\1/' /etc/sysctl.conf
echo 1 > /proc/sys/net/ipv4/ip_forward

View file

@ -0,0 +1,11 @@
#!/bin/bash
ntpfile=`mktemp`
cat << EOF > $ntpfile
server ntp.ubuntu.com iburst
server 127.127.1.0
fudge 127.127.1.0 stratum 10
EOF
mv /etc/ntp.conf /etc/ntp.conf.orig
mv $ntpfile /etc/ntp.conf
service ntp restart

View file

@ -0,0 +1,43 @@
#!/bin/bash
set -e
set -o xtrace
source $(dirname $0)/defaults
#MYSQL_ADMPASS
#MYSQL_NOVAPASS
#MYSQL_GLANCEPASS
#MYSQL_KEYSTONEPASS
#MYSQL_CINDERPASS
service mysql stop || true
MYSQL_BOOTSTRAP="/usr/sbin/mysqld --bootstrap --user=mysql --skip-grant-tables"
sqltfile=`mktemp`
cat <<EOF > $sqltfile
USE mysql;
UPDATE user SET password=PASSWORD("$MYSQL_ADMPASS") WHERE user='root';
EOF
$MYSQL_BOOTSTRAP < $sqltfile
rm -f $sqltfile
sed -i 's/^bind-address/#bind-address/' /etc/mysql/my.cnf
service mysql start
sqltfile=`mktemp`
cat <<EOF > $sqltfile
CREATE DATABASE IF NOT EXISTS nova;
GRANT ALL PRIVILEGES ON nova.* TO 'nova'@'%' IDENTIFIED BY '${MYSQL_NOVAPASS}';
CREATE DATABASE IF NOT EXISTS cinder;
GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%' IDENTIFIED BY '${MYSQL_CINDERPASS}';
CREATE DATABASE IF NOT EXISTS keystone;
GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY '${MYSQL_KEYSTONEPASS}';
CREATE DATABASE IF NOT EXISTS glance;
GRANT ALL PRIVILEGES ON glance.* TO 'glance'@'%' IDENTIFIED BY '${MYSQL_GLANCEPASS}';
FLUSH PRIVILEGES;
EOF
mysql -uroot --password=$MYSQL_ADMPASS < $sqltfile
rm -f sqltfile

View file

@ -0,0 +1,8 @@
#!/bin/bash
set -e
set -o xtrace
source $(dirname $0)/defaults
#RABBIT_PASS
rabbitmqctl change_password guest $̣{RABBIT_PASS}

View file

@ -0,0 +1,19 @@
#!/bin/bash
set -e
set -o xtrace
source $(dirname $0)/defaults
#AUTH_TOKEN
#MYSQL_KEYSTONEPASS
#ADMIN_PASSWORD
#HOST_IP
KEYSTONE_FILE="/etc/keystone/keystone.conf"
SQL_CONNECTION="mysql://keystone:${MYSQL_KEYSTONEPASS}@localhost:3306/keystone"
sed -e "s,^connection\s*=\s*.\+$,connection = $SQL_CONNECTION," -i ${KEYSTONE_FILE}
sed -e 's|^[#]*[ \t]*admin_token[ \t]*=.*|admin_token = '${AUTH_TOKEN}'|' -i ${KEYSTONE_FILE}
service keystone restart
keystone-manage db_sync

View file

@ -0,0 +1,72 @@
#!/bin/sh
#
# Keystone Datas
#
# Description: Fill Keystone with datas.
# Mainly inspired by http://www.hastexo.com/resources/docs/installing-openstack-essex-20121-ubuntu-1204-precise-pangolin
# Written by Martin Gerhard Loschwitz / Hastexo
# Modified by Emilien Macchi / StackOps
#
# Support: openstack@lists.launchpad.net
# License: Apache Software License (ASL) 2.0
#
source $(dirname $0)/defaults
export OS_TENANT_NAME=admin
export OS_USER_NAME=admin
export OS_PASSWORD=${ADMIN_PASSWORD}
export OS_AUTH_URL="http://localhost:5000/v2.0/"
export SERVICE_ENDPOINT="http://localhost:35357/v2.0"
SERVICE_TENANT_NAME=${SERVICE_TENANT_NAME:-service}
get_id () {
echo `$@ | awk '/ id / { print $4 }'`
}
# Tenants
ADMIN_TENANT=$(get_id keystone tenant-create --name=admin)
SERVICE_TENANT=$(get_id keystone tenant-create --name=$SERVICE_TENANT_NAME)
DEMO_TENANT=$(get_id keystone tenant-create --name=demo)
INVIS_TENANT=$(get_id keystone tenant-create --name=invisible_to_admin)
# Users
ADMIN_USER=$(get_id keystone user-create --name=admin --pass="$ADMIN_PASSWORD" --email=admin@domain.com)
#DEMO_USER=$(get_id keystone user-create --name=demo --pass="$ADMIN_PASSWORD" --email=demo@domain.com)
# Roles
ADMIN_ROLE=$(get_id keystone role-create --name=admin)
KEYSTONEADMIN_ROLE=$(get_id keystone role-create --name=KeystoneAdmin)
KEYSTONESERVICE_ROLE=$(get_id keystone role-create --name=KeystoneServiceAdmin)
# Add Roles to Users in Tenants
keystone user-role-add --user-id $ADMIN_USER --role-id $ADMIN_ROLE --tenant-id $ADMIN_TENANT
#keystone user-role-add --user-id $ADMIN_USER --role-id $ADMIN_ROLE --tenant-id $DEMO_TENANT
keystone user-role-add --user-id $ADMIN_USER --role-id $KEYSTONEADMIN_ROLE --tenant-id $ADMIN_TENANT
keystone user-role-add --user-id $ADMIN_USER --role-id $KEYSTONESERVICE_ROLE --tenant-id $ADMIN_TENANT
# The Member role is used by Horizon and Swift
MEMBER_ROLE=$(get_id keystone role-create --name=Member)
#keystone user-role-add --user-id $DEMO_USER --role-id $MEMBER_ROLE --tenant-id $INVIS_TENANT
#keystone user-role-add --user-id $DEMO_USER --role-id $MEMBER_ROLE --tenant-id $DEMO_TENANT
keystone user-role-add --user-id $ADMIN_USER --role-id $MEMBER_ROLE --tenant-id $ADMIN_TENANT
# Configure service users/roles
NOVA_USER=$(get_id keystone user-create --name=nova --pass="$NOVA_PASS" --tenant-id $SERVICE_TENANT --email=nova@domain.com)
keystone user-role-add --tenant-id $SERVICE_TENANT --user-id $NOVA_USER --role-id $ADMIN_ROLE
GLANCE_USER=$(get_id keystone user-create --name=glance --pass="$GLANCE_PASSWORD" --tenant-id $SERVICE_TENANT --email=glance@domain.com)
keystone user-role-add --tenant-id $SERVICE_TENANT --user-id $GLANCE_USER --role-id $ADMIN_ROLE
SWIFT_USER=$(get_id keystone user-create --name=swift --pass="$SWIFT_PASSWORD" --tenant-id $SERVICE_TENANT --email=swift@domain.com)
keystone user-role-add --tenant-id $SERVICE_TENANT --user-id $SWIFT_USER --role-id $ADMIN_ROLE
RESELLER_ROLE=$(get_id keystone role-create --name=ResellerAdmin)
keystone user-role-add --tenant-id $SERVICE_TENANT --user-id $NOVA_USER --role-id $RESELLER_ROLE
QUANTUM_USER=$(get_id keystone user-create --name=quantum --pass="$QUANTUM_PASSWORD" --tenant-id $SERVICE_TENANT --email=quantum@domain.com)
keystone user-role-add --tenant-id $SERVICE_TENANT --user-id $QUANTUM_USER --role-id $ADMIN_ROLE
CINDER_USER=$(get_id keystone user-create --name=cinder --pass="$CINDER_PASSWORD" --tenant-id $SERVICE_TENANT --email=cinder@domain.com)
keystone user-role-add --tenant-id $SERVICE_TENANT --user-id $CINDER_USER --role-id $ADMIN_ROLE

View file

@ -0,0 +1,68 @@
#!/bin/sh
#
# Keystone Endpoints
#
# Description: Create Services Endpoints
# Mainly inspired by http://www.hastexo.com/resources/docs/installing-openstack-essex-20121-ubuntu-1204-precise-pangolin
# Written by Martin Gerhard Loschwitz / Hastexo
# Modified by Emilien Macchi / StackOps
#
# Support: openstack@lists.launchpad.net
# License: Apache Software License (ASL) 2.0
#
source $(dirname $0)/defaults
# MySQL definitions
MYSQL_USER=keystone
MYSQL_DATABASE=keystone
MYSQL_HOST=localhost
# Keystone definitions
KEYSTONE_REGION=RegionOne
SERVICE_TOKEN=password
SERVICE_ENDPOINT="http://localhost:35357/v2.0"
keystone service-create --name nova --type compute --description 'OpenStack Compute Service'
keystone service-create --name cinder --type volume --description 'OpenStack Volume Service'
keystone service-create --name glance --type image --description 'OpenStack Image Service'
keystone service-create --name swift --type object-store --description 'OpenStack Storage Service'
keystone service-create --name keystone --type identity --description 'OpenStack Identity'
keystone service-create --name ec2 --type ec2 --description 'OpenStack EC2 service'
keystone service-create --name quantum --type network --description 'OpenStack Networking service'
create_endpoint () {
case $1 in
compute)
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"${HOST_IP}"':8774/v2/$(tenant_id)s' --adminurl 'http://'"$HOST_IP"':8774/v2/$(tenant_id)s' --internalurl 'http://'"$HOST_IP"':8774/v2/$(tenant_id)s'
;;
volume)
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"$HOST_IP"':8776/v1/$(tenant_id)s' --adminurl 'http://'"$HOST_IP"':8776/v1/$(tenant_id)s' --internalurl 'http://'"$HOST_IP"':8776/v1/$(tenant_id)s'
;;
image)
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"$HOST_IP"':9292/v2' --adminurl 'http://'"$HOST_IP"':9292/v2' --internalurl 'http://'"$HOST_IP"':9292/v2'
;;
object-store)
if [ $SWIFT_HOST_IP ]; then
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"$SWIFT_HOST_IP"':8080/v1/AUTH_$(tenant_id)s' --adminurl 'http://'"$SWIFT_HOST_IP"':8080/v1' --internalurl 'http://'"$SWIFT_HOST_IP"':8080/v1/AUTH_$(tenant_id)s'
else
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"$HOST_IP"':8080/v1/AUTH_$(tenant_id)s' --adminurl 'http://'"$HOST_IP"':8080/v1' --internalurl 'http://'"$HOST_IP"':8080/v1/AUTH_$(tenant_id)s'
fi
;;
identity)
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"$HOST_IP"':5000/v2.0' --adminurl 'http://'"$HOST_IP"':35357/v2.0' --internalurl 'http://'"$HOST_IP"':5000/v2.0'
;;
ec2)
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"$HOST_IP"':8773/services/Cloud' --adminurl 'http://'"$HOST_IP"':8773/services/Admin' --internalurl 'http://'"$HOST_IP"':8773/services/Cloud'
;;
network)
keystone endpoint-create --region $KEYSTONE_REGION --service-id $2 --publicurl 'http://'"$HOST_IP"':9696/' --adminurl 'http://'"$HOST_IP"':9696/' --internalurl 'http://'"$HOST_IP"':9696/'
;;
esac
}
for i in compute volume image object-store identity ec2 network; do
id=`mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_KEYSTONEPASS" "$MYSQL_DATABASE" -ss -e "SELECT id FROM service WHERE type='"$i"';"` || exit 1
create_endpoint $i $id
done

View file

@ -0,0 +1,35 @@
#!/bin/bash
set -e
set -o xtrace
source $(dirname $0)/defaults
#MYSQL_GLANCEPASS
#GLANCE_PASS
#RABBIT_PASS
GLANCE_DIR="/etc/glance/"
SQL_CONNECTION="mysql://glance:${MYSQL_GLANCEPASS}@localhost:3306/glance"
sed -e "s,^sql_connection\s*=\s*.\+$,sql_connection = $SQL_CONNECTION," -i ${GLANCE_DIR}/glance-api.conf
sed -e "s,^sql_connection\s*=\s*.\+$,sql_connection = $SQL_CONNECTION," -i ${GLANCE_DIR}/glance-registry.conf
sed -e "s,^admin_tenant_name\s*=\s*.\+$,admin_tenant_name = service," -i ${GLANCE_DIR}/glance-api.conf
sed -e "s,^admin_tenant_name\s*=\s*.\+$,admin_tenant_name = service," -i ${GLANCE_DIR}/glance-registry.conf
sed -e "s,^admin_user\s*=\s*.\+$,admin_user = glance," -i ${GLANCE_DIR}/glance-api.conf
sed -e "s,^admin_user\s*=\s*.\+$,admin_user = glance," -i ${GLANCE_DIR}/glance-registry.conf
sed -e "s,^admin_password\s*=\s*.\+$,admin_password = ${GLANCE_PASS}," -i ${GLANCE_DIR}/glance-api.conf
sed -e "s,^admin_password\s*=\s*.\+$,admin_password = ${GLANCE_PASS}," -i ${GLANCE_DIR}/glance-registry.conf
sed -e "s,^notifier_strategy\s*=\s*.\+$,notifier_strategy = rabbit," -i ${GLANCE_DIR}/glance-api.conf
sed -e "s,^rabbit_password\s*=\s*.\+$,rabbit_password = ${RABBIT_PASS}," -i ${GLANCE_DIR}/glance-api.conf
service glance-api restart
service glance-registry restart
glance-manage db_sync
#wget http://uec-images.ubuntu.com/releases/precise/release/ubuntu-12.04-server-cloudimg-amd64.tar.gz
#tar xzvf ubuntu-12.04-server-cloudimg-amd64.tar.gz
#glance image-create --name="Ubuntu 12.04 UEC" --public --container-format=ovf --disk-format=qcow2 < precise-server-cloudimg-amd64.img

View file

@ -0,0 +1,85 @@
#!/bin/bash
set -e
set -o xtrace
source $(dirname $0)/defaults
#HOST_IP
NOVA_DIR="/etc/nova/"
SQL_CONNECTION="mysql://nova:${MYSQL_NOVAPASS}d@localhost:3306/nova"
sed -e "s,^admin_tenant_name\s*=\s*.\+$,admin_tenant_name = service," -i ${NOVA_DIR}/api-paste.ini
sed -e "s,^admin_user\s*=\s*.\+$,admin_user = nova," -i ${NOVA_DIR}/api-paste.ini
sed -e "s,^admin_password\s*=\s*.\+$,admin_password = ${NOVA_PASS}," -i ${NOVA_DIR}/api-paste.ini
sed -i '/volume/d' /etc/nova/api-paste.ini
cat << EOF > ${NOVA_DIR}/nova.conf
[DEFAULT]
# MySQL Connection #
sql_connection=mysql://nova:password@${HOST_IP}/nova
# nova-scheduler #
rabbit_password=${RABBIT_PASS}
scheduler_driver=nova.scheduler.simple.SimpleScheduler
# nova-api #
cc_host=${HOST_IP}
auth_strategy=keystone
s3_host=${HOST_IP}
ec2_host=${HOST_IP}
nova_url=http://${HOST_IP}:8774/v1.1/
ec2_url=http://${HOST_IP}:8773/services/Cloud
keystone_ec2_url=http://${HOST_IP}:5000/v2.0/ec2tokens
api_paste_config=/etc/nova/api-paste.ini
allow_admin_api=true
use_deprecated_auth=false
ec2_private_dns_show_ip=True
dmz_cidr=169.254.169.254/32
ec2_dmz_host=${HOST_IP}
metadata_host=${HOST_IP}
metadata_listen=0.0.0.0
enabled_apis=ec2,osapi_compute,metadata
# Networking #
#network_api_class=nova.network.quantumv2.api.API
#quantum_url=http://${HOST_IP}:9696
#quantum_auth_strategy=keystone
#quantum_admin_tenant_name=service
#quantum_admin_username=quantum
#quantum_admin_password=password
#quantum_admin_auth_url=http://${HOST_IP}:35357/v2.0
#libvirt_vif_driver=nova.virt.libvirt.vif.LibvirtHybridOVSBridgeDriver
#linuxnet_interface_driver=nova.network.linux_net.LinuxOVSInterfaceDriver
#firewall_driver=nova.virt.libvirt.firewall.IptablesFirewallDriver
# Cinder #
volume_api_class=nova.volume.cinder.API
# Glance #
glance_api_servers=${HOST_IP}:9292
image_service=nova.image.glance.GlanceImageService
# novnc #
novnc_enable=true
novncproxy_base_url=http://${HOST_IP}:6080/vnc_auto.html
vncserver_proxyclient_address=127.0.0.1
vncserver_listen=0.0.0.0
# Misc #
logdir=/var/log/nova
state_path=/var/lib/nova
lock_path=/var/lock/nova
root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf
verbose=true
EOF
nova-manage db sync
service nova-api restart
service nova-cert restart
service nova-consoleauth restart
service nova-scheduler restart
service novnc restart
service nova-network restart

View file

@ -0,0 +1,27 @@
set -e
DEFAULT_PASSWORD=${DEFAULT_PASSWORD:-password}
# HOST_IP
# Find the interface used for the default route
HOST_IP_IFACE=${HOST_IP_IFACE:-$(ip route | sed -n '/^default/{ s/.*dev \(\w\+\)\s\+.*/\1/; p; }')}
# Search for an IP unless an explicit is set by ``HOST_IP`` environment variable
HOST_IP=`LC_ALL=C ip -f inet addr show ${HOST_IP_IFACE} | awk '/inet/ {split($2,parts,"/"); print parts[1]}'`
# Mysql Passwords
MYSQL_ADMPASS=${MYSQL_ADMPASS:-$ {DEFAULT_PASSWORD}}
MYSQL_NOVAPASS=${MYSQL_NOVAPASS:-$MYSQL_ADMPASS}
MYSQL_GLANCEPASS=${MYSQL_GLANCEPASS:-$MYSQL_ADMPASS}
MYSQL_KEYSTONEPASS=${MYSQL_KEYSTONEPASS:-$MYSQL_ADMPASS}
MYSQL_CINDERPASS=${MYSQL_CINDERPASS:-$MYSQL_ADMPASS}
#Rabbitmq Passwords
RABBIT_PASS=${RABBIT_PASS:-${DEFAULT_PASSWORD}}
#Keystone
AUTH_TOKEN=${AUTH_TOKEN:-${DEFAULT_PASSWORD}}
SERVICE_TOKEN=${AUTH_TOKEN}
ADMIN_PASSWORD=${ADMIN_PASSWORD:-${DEFAULT_PASSWORD}}
#Glance
GLANCE_PASS=${GLANCE_PASS:-${DEFAULT_PASSWORD}}

View file

@ -0,0 +1,8 @@
#!/bin/sh
# Install controller base requiered packages
set -e
set -o xtrace
DEBIAN_FRONTEND=noninteractive apt-get -y install haproxy rabbitmq-server mysql-server ntp dkms

View file

@ -0,0 +1,9 @@
#!/bin/sh
# Install controller openstack packages
set -e
set -o xtrace
apt-get -y install keystone glance nova-api nova-cert nova-common nova-scheduler python-nova python-novaclient nova-consoleauth novnc nova-novncproxy cinder-api cinder-scheduler cinder-volume iscsitarget open-iscsi iscsitarget-dkms python-cinderclient nova-network

View file

@ -0,0 +1,27 @@
#!/bin/sh
# Build iscsi modules with installed kernel
set -e
set -o xtrace
package=iscsitarget-dkms
name=iscsitarget
kernel_version=`ls /boot/vmlinuz-* -r -1| head -n1 | xargs basename |sed 's/vmlinuz-//'`
version=`dpkg-query -W -f='${Version}' "$package" \
|rev|cut -d- -f2-|rev|cut -d':' -f2|tr -d "\n"`
isadded=`dkms status -m "$cd name" -v "$version"`
if [ "x${isadded}" = "x" ] ; then
dkms add -m "$name" -v "$version" -k "$kernel_version" || true
fi
dkms build -m "$name" -v "$version" -k "$kernel_version" && dkms install -m "$name" -v "$version" -k "$kernel_version" || true

View file

@ -0,0 +1,10 @@
#!/bin/sh
# Build iscsi modules with installed kernel
set -e
set -o xtrace
apt-get clean

1
flavours/salt/README.md Normal file
View file

@ -0,0 +1 @@
Adds salt, a config-management tool, to the image.

View file

@ -0,0 +1,7 @@
#!/bin/bash
set -e
set -o xtrace
apt-get -y install salt-minion

View file

@ -0,0 +1,8 @@
#!/bin/bash
# Add the salt PPA
set -e
set -o xtrace
add-apt-repository -y ppa:saltstack/salt

1
flavours/swift/README.md Normal file
View file

@ -0,0 +1 @@
Sets up a swift install in the image.

View file

@ -0,0 +1,9 @@
#!/bin/sh
# Install controller openstack packages
set -e
set -o xtrace
apt-get -y install swift rsync memcached python-netifaces python-xattr python-memcache

2
flavours/vm/README.md Normal file
View file

@ -0,0 +1,2 @@
Sets up a partitioned disk (rather than building just one filesystem with no
partition table).

View file

@ -0,0 +1,16 @@
#!/bin/bash
set -e
source $_LIB/die
[ -n "$IMAGE_BLOCK_DEVICE" ] || die "Image block device not set"
# Create 1 partition far enough up the disk to permit grub to be installed on
# the MBR.
sudo sfdisk $IMAGE_BLOCK_DEVICE << EOF
1 - - *
0 0;
0 0;
0 0;
EOF
echo "IMAGE_BLOCK_DEVICE=${IMAGE_BLOCK_DEVICE}p1"

9
lib/die Normal file
View file

@ -0,0 +1,9 @@
# Prints "message" and exits
# Usage: die "message"
function die() {
local exitcode=$?
set +o xtrace
echo $@
exit $exitcode
}

14
lib/img-defaults Normal file
View file

@ -0,0 +1,14 @@
# options for create-baremetal-image.sh
ARCH=${ARCH:-$(dpkg --print-architecture)}
RELEASE=${RELEASE:-quantal}
BASE_IMAGE_FILE=${BASE_IMAGE_FILE:-$RELEASE-server-cloudimg-$ARCH-root.tar.gz}
CLOUD_IMAGES=${CLOUD_IMAGES:-http://cloud-images.ubuntu.com/}
FS_TYPE=${FS_TYPE:-ext4}
# Used to set the file extension only at this stage.
IMAGE_TYPE=${IMAGE_TYPE:-qcow2}
IMAGE_NAME=${IMAGE_NAME:-image}
IMAGE_SIZE=${IMAGE_SIZE:-1} # N.B. This size is in GB
# Set via the CLI normally.
# IMAGE_FLAVOUR=
IMG_PATH=~/.cache/image-create
FLAVOURS_DIR=$(dirname $0)/../flavours

251
lib/img-functions Normal file
View file

@ -0,0 +1,251 @@
function unmount_image () {
# unmount from the chroot
# Don't use TMP_MOUNT_PATH here, it might not have been set.
sudo umount -f $TMP_BUILD_DIR/mnt/sys || true
sudo umount -f $TMP_BUILD_DIR/mnt/proc || true
sudo umount -f $TMP_BUILD_DIR/mnt/dev || true
sudo umount -f $TMP_BUILD_DIR/mnt/tmp/in_target.d || true
# give it a second (ok really 5) to umount XXX - why? should instead track
# the mount data / lsof etc.
sleep 5
# oh ya don't want to forget to unmount the image
sudo umount -f $TMP_BUILD_DIR/mnt || true
# having disk corruption issues; one possibility is qemu-nbd not flush dirty
# pages on disconnect?
sync
if [ -n "$EXTRA_UNMOUNT" ]; then
$EXTRA_UNMOUNT
fi
}
function cleanup () {
unmount_image
rm -rf $TMP_BUILD_DIR
}
function mk_build_dir () {
export TMP_BUILD_DIR=$(mktemp -t -d image.XXXXXXXX)
[ $? -eq 0 ] || die "Failed to create tmp directory"
trap cleanup EXIT
echo Building in $TMP_BUILD_DIR
export TMP_IMAGE_PATH=$TMP_BUILD_DIR/image
export TMP_HOOKS_PATH=$TMP_BUILD_DIR/hooks
}
function ensure_nbd () {
NBD=`which qemu-nbd`
if [ -z "$NBD" ]; then
echo "Need qemu-nbd to build qcow2 files."
sudo apt-get install qemu-utils
fi
# prep nbd for mounting
(lsmod | grep '^nbd ') || sudo modprobe nbd max_part=16
}
function ensure_base_available () {
# TODO: don't cache -current forever.
if [ ! -f $IMG_PATH/$BASE_IMAGE_FILE ] ; then
echo "Fetching Base Image"
wget $CLOUD_IMAGES/$RELEASE/current/$BASE_IMAGE_FILE -O $IMG_PATH/$BASE_IMAGE_FILE.tmp
mv $IMG_PATH/$BASE_IMAGE_FILE.tmp $IMG_PATH/$BASE_IMAGE_FILE
fi
}
function ensure_sudo () {
sudo echo "Ensuring sudo is available"
}
function mount_tmp_image () {
mkdir $TMP_BUILD_DIR/mnt
sudo mount $@ $TMP_BUILD_DIR/mnt
[ $? -eq 0 ] || die "Failed to mount image"
export TMP_MOUNT_PATH=$TMP_BUILD_DIR/mnt
}
function create_base () {
# Extract the base image
sudo tar -C $TMP_MOUNT_PATH -xzf $IMG_PATH/$BASE_IMAGE_FILE
# Configure Image
# Setup resolv.conf so we can chroot to install some packages
# XXXX: Should store the old state rather than unlink; then restore later.
if [ -L $TMP_MOUNT_PATH/etc/resolv.conf ] ; then
sudo unlink $TMP_MOUNT_PATH/etc/resolv.conf
fi
if [ -f $TMP_MOUNT_PATH/etc/resolv.conf ] ; then
sudo rm -f $TMP_MOUNT_PATH/etc/resolv.conf
fi
# Recreate resolv.conf
sudo touch $TMP_MOUNT_PATH/etc/resolv.conf
sudo chmod 777 $TMP_MOUNT_PATH/etc/resolv.conf
echo nameserver 8.8.8.8 > $TMP_MOUNT_PATH/etc/resolv.conf
# supporting kernel file systems
sudo mount -t proc none $TMP_MOUNT_PATH/proc
sudo mount --bind /dev $TMP_MOUNT_PATH/dev
sudo mount -t sysfs none $TMP_MOUNT_PATH/sys
# If we have a network proxy, use it.
if [ -n "$http_proxy" ] ; then
sudo dd of=$TMP_MOUNT_PATH/etc/apt/apt.conf.d/60img-build-proxy << _EOF_
Acquire::http::Proxy "$http_proxy";
_EOF_
fi
}
# Helper function to run a command inside the chroot
function run_in_target() {
# -E to preserve http_proxy
sudo -E chroot $TMP_MOUNT_PATH "$@"
}
function generate_hooks () {
mkdir -p $TMP_HOOKS_PATH
for _FLAVOUR in $IMAGE_FLAVOUR ; do
[ -d $FLAVOURS_DIR/$_FLAVOUR ] || die "The flavour does not exist." ;
cp -t $TMP_HOOKS_PATH -a $FLAVOURS_DIR/$_FLAVOUR/* ;
done
}
# Check that a real flavour has been chosen (prevents foot-guns)
function check_flavour () {
[ -d $TMP_HOOKS_PATH ] || generate_hooks
}
# Helper function to run a directory of scripts inside the chroot
function run_d_in_target() {
check_flavour
# If we can find a directory of hooks to run in the target filesystem, bind
# mount it into the target and then execute run-parts in a chroot
if [ -d ${TMP_HOOKS_PATH}/$1.d ] ; then
sudo mkdir $TMP_MOUNT_PATH/tmp/in_target.d
sudo mount --bind ${TMP_HOOKS_PATH} $TMP_MOUNT_PATH/tmp/in_target.d
sudo mount -o remount,ro,bind ${TMP_HOOKS_PATH} $TMP_MOUNT_PATH/tmp/in_target.d
run_in_target run-parts -v /tmp/in_target.d/$1.d
sudo umount -f $TMP_MOUNT_PATH/tmp/in_target.d
sudo rmdir $TMP_MOUNT_PATH/tmp/in_target.d
fi
}
# Run a directory of hooks outside the target.
function run_d() {
check_flavour
if [ -d ${TMP_HOOKS_PATH}/$1.d ] ; then
run-parts ${TMP_HOOKS_PATH}/$1.d
fi
}
function prepare_first_boot () {
if [ -d ${TMP_HOOKS_PATH}/first-boot.d ] ; then
sudo cp -t $TMP_MOUNT_PATH/etc/ -a $TMP_HOOKS_PATH/first-boot.d
run_in_target mv /etc/rc.local /etc/rc.local.REAL
sudo dd of=$TMP_MOUNT_PATH/etc/rc.local <<EOF
#!/bin/bash
set -e
set -o xtrace
run-parts -v /etc/first-boot.d
rm -fr /etc/first-boot.d
mv /etc/rc.local.REAL /etc/rc.local
exit 0
EOF
run_in_target chmod 755 /etc/rc.local
fi
}
function finalise_base () {
# Undo our proxy support
sudo rm -f $TMP_MOUNT_PATH/etc/apt/apt.conf.d/60img-build-proxy
# Now remove the resolv.conf we created above
sudo rm -f $TMP_MOUNT_PATH/etc/resolv.conf
# The we need to recreate it as a link
sudo ln -sf ../run/resolvconf/resolv.conf $TMP_MOUNT_PATH/etc/resolv.conf
}
function compress_image () {
# Recreate our image to throw away unnecessary data
qemu-img convert -c $TMP_IMAGE_PATH -O qcow2 $TMP_IMAGE_PATH-new
rm $TMP_IMAGE_PATH
mv $TMP_IMAGE_PATH-new $TMP_IMAGE_PATH
}
function save_image () {
# TODO: this really should rename the old file
if [ -f $1 ] ; then
echo "Old Image file Found REMOVING"
rm -f $1
fi
cp $TMP_IMAGE_PATH $1
rm -r $TMP_BUILD_DIR
# All done!
trap EXIT
echo "Image file $1 created..."
}
function block_apt_translations () {
# Configure APT not to fetch translations files
sudo dd of=$TMP_MOUNT_PATH/etc/apt/apt.conf.d/95no-translations <<EOF
APT::Acquire::Languages "none";
EOF
# And now make sure that we don't fall foul of Debian bug 641967
find $TMP_MOUNT_PATH/var/lib/apt/lists/ -type f -name '*_i18n_Translation-*' -exec sudo rm -f {} \;
}
function block_daemons () {
# Prevent package installs from starting daemons
sudo mv $TMP_MOUNT_PATH/sbin/start-stop-daemon $TMP_MOUNT_PATH/sbin/start-stop-daemon.REAL
sudo dd of=$TMP_MOUNT_PATH/sbin/start-stop-daemon <<EOF
#!/bin/sh
echo
echo "Warning: Fake start-stop-daemon called, doing nothing"
EOF
sudo chmod 755 $TMP_MOUNT_PATH/sbin/start-stop-daemon
sudo mv $TMP_MOUNT_PATH/sbin/initctl $TMP_MOUNT_PATH/sbin/initctl.REAL
sudo dd of=$TMP_MOUNT_PATH/sbin/initctl <<EOF
#!/bin/sh
echo "initctl (tripleo 1.0)"
echo "Warning: Fake initctl called, doing nothing"
EOF
sudo chmod 755 $TMP_MOUNT_PATH/sbin/initctl
sudo mv $TMP_MOUNT_PATH/usr/sbin/invoke-rc.d $TMP_MOUNT_PATH/usr/sbin/invoke-rc.d.REAL
sudo dd of=$TMP_MOUNT_PATH/usr/sbin/invoke-rc.d <<EOF
#!/bin/sh
echo "invoke-rc.d (tripleo 1.0)"
echo "Warning: Fake inovke-rc.d called, doing nothing"
EOF
sudo chmod 755 $TMP_MOUNT_PATH/usr/sbin/invoke-rc.d
}
function unblock_daemons () {
sudo mv $TMP_MOUNT_PATH/sbin/start-stop-daemon.REAL $TMP_MOUNT_PATH/sbin/start-stop-daemon
sudo mv $TMP_MOUNT_PATH/sbin/initctl.REAL $TMP_MOUNT_PATH/sbin/initctl
sudo mv $TMP_MOUNT_PATH/usr/sbin/invoke-rc.d.REAL $TMP_MOUNT_PATH/usr/sbin/invoke-rc.d
}
function do_pre_install () {
block_daemons
block_apt_translations
# Install baseline packages and tools
run_in_target apt-get -y update
run_in_target apt-get -y install python-software-properties
run_in_target add-apt-repository -y ppa:tripleo/demo
# Uncomment to get the bleeding edge - this should be a flavour thing.
# run_in_target add-apt-repository -y ppa:tripleo/demo-staging
# Run pre-install scripts. These do things that prepare the chroot for package installs
run_d_in_target pre-install
run_in_target apt-get -y update
}
function do_install () {
# These are useful, or at worst not harmful, for all imges we build.
run_in_target apt-get -y install linux-image-generic vlan open-iscsi
# Call install scripts to pull in the software users want.
run_d_in_target install
unblock_daemons
}

View file

@ -0,0 +1,28 @@
ALL ALL=(root) NOPASSWD: /bin/chmod * /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/dd of=/tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/ln -sf * /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/mkdir -p /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/mkdir /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/mount --bind /dev /tmp/*/mnt/dev
ALL ALL=(root) NOPASSWD: /bin/mount --bind /tmp/*/hooks /tmp/*/mnt/tmp/in_target.d
ALL ALL=(root) NOPASSWD: /bin/mount -o remount\,ro\,bind /tmp/*/hooks /tmp/*/mnt/tmp/in_target.d
ALL ALL=(root) NOPASSWD: /bin/mount -t proc none /tmp/*/mnt/proc
ALL ALL=(root) NOPASSWD: /bin/mount -t sysfs none /tmp/*/mnt/sys
ALL ALL=(root) NOPASSWD: /bin/mount /dev/nbd0p1 /tmp/*/mnt
ALL ALL=(root) NOPASSWD: /bin/mv /tmp/*/mnt/* /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/rm -* /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/rmdir /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /bin/tar -C /tmp/*/mnt -xzf /*/.cache/image-create/*
ALL ALL=(root) NOPASSWD: /bin/umount -f /tmp/*/mnt
ALL ALL=(root) NOPASSWD: /bin/umount -f /tmp/*/mnt/dev
ALL ALL=(root) NOPASSWD: /bin/umount -f /tmp/*/mnt/proc
ALL ALL=(root) NOPASSWD: /bin/umount -f /tmp/*/mnt/sys
ALL ALL=(root) NOPASSWD: /bin/umount -f /tmp/*/mnt/tmp/in_target.d
ALL ALL=(root) NOPASSWD: /sbin/mkfs -F -t ext4 -L cloudimg-rootfs /dev/nbd0p1
ALL ALL=(root) NOPASSWD: /sbin/modprobe nbd max_part=16
ALL ALL=(root) NOPASSWD: /sbin/sfdisk /dev/nbd0
ALL ALL=(root) NOPASSWD: /usr/bin/qemu-nbd -c /dev/nbd0 --cache=writeback /tmp/*/image
ALL ALL=(root) NOPASSWD: /usr/bin/qemu-nbd -d /dev/nbd0
ALL ALL=(root) NOPASSWD: /usr/bin/touch /tmp/*/mnt/*
ALL ALL=(root) NOPASSWD: /usr/bin/unlink /tmp/*/mnt/*
ALL ALL=(root) SETENV: NOPASSWD: /usr/sbin/chroot /tmp/*/mnt *