diff --git a/extras.d/50-ironic.sh b/extras.d/50-ironic.sh
index 9e61dc5d78..3b8e3d5045 100644
--- a/extras.d/50-ironic.sh
+++ b/extras.d/50-ironic.sh
@@ -24,10 +24,17 @@ if is_service_enabled ir-api ir-cond; then
# Start the ironic API and ironic taskmgr components
echo_summary "Starting Ironic"
start_ironic
+
+ if [[ "$IRONIC_BAREMETAL_BASIC_OPS" = "True" ]]; then
+ prepare_baremetal_basic_ops
+ fi
fi
if [[ "$1" == "unstack" ]]; then
stop_ironic
+ if [[ "$IRONIC_BAREMETAL_BASIC_OPS" = "True" ]]; then
+ cleanup_baremetal_basic_ops
+ fi
fi
if [[ "$1" == "clean" ]]; then
diff --git a/files/apts/ironic b/files/apts/ironic
new file mode 100644
index 0000000000..a749ad762e
--- /dev/null
+++ b/files/apts/ironic
@@ -0,0 +1,10 @@
+libguestfs0
+libvirt-bin
+openssh-client
+openvswitch-switch
+openvswitch-datapath-dkms
+python-libguestfs
+python-libvirt
+syslinux
+tftpd-hpa
+xinetd
diff --git a/files/rpms/ironic b/files/rpms/ironic
new file mode 100644
index 0000000000..54b98299ee
--- /dev/null
+++ b/files/rpms/ironic
@@ -0,0 +1,9 @@
+libguestfs
+libvirt
+libvirt-python
+openssh-clients
+openvswitch
+python-libguestfs
+syslinux
+tftp-server
+xinetd
diff --git a/lib/baremetal b/lib/baremetal
index 1d02e1e417..eda92f97cb 100644
--- a/lib/baremetal
+++ b/lib/baremetal
@@ -140,7 +140,10 @@ BM_DEPLOY_KERNEL=${BM_DEPLOY_KERNEL:-}
# If you need to add any extra flavors to the deploy ramdisk image
# eg, specific network drivers, specify them here
-BM_DEPLOY_FLAVOR=${BM_DEPLOY_FLAVOR:-}
+#
+# NOTE(deva): this will be moved to lib/ironic in a future patch
+# for now, set the default to a suitable value for Ironic's needs
+BM_DEPLOY_FLAVOR=${BM_DEPLOY_FLAVOR:--a amd64 ubuntu deploy-ironic}
# set URL and version for google shell-in-a-box
BM_SHELL_IN_A_BOX=${BM_SHELL_IN_A_BOX:-http://shellinabox.googlecode.com/files/shellinabox-2.14.tar.gz}
@@ -220,7 +223,7 @@ function upload_baremetal_deploy {
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
- $BM_IMAGE_BUILD_DIR/bin/ramdisk-image-create $BM_DEPLOY_FLAVOR deploy \
+ $BM_IMAGE_BUILD_DIR/bin/ramdisk-image-create $BM_DEPLOY_FLAVOR \
-o $TOP_DIR/files/bm-deploy
fi
fi
diff --git a/lib/ironic b/lib/ironic
index b346de1e69..c6fa563e6a 100644
--- a/lib/ironic
+++ b/lib/ironic
@@ -18,16 +18,19 @@
# - stop_ironic
# - cleanup_ironic
-# Save trace setting
+# Save trace and pipefail settings
XTRACE=$(set +o | grep xtrace)
+PIPEFAIL=$(set +o | grep pipefail)
set +o xtrace
-
+set +o pipefail
# Defaults
# --------
# Set up default directories
IRONIC_DIR=$DEST/ironic
+IRONIC_DATA_DIR=$DATA_DIR/ironic
+IRONIC_STATE_PATH=/var/lib/ironic
IRONICCLIENT_DIR=$DEST/python-ironicclient
IRONIC_AUTH_CACHE_DIR=${IRONIC_AUTH_CACHE_DIR:-/var/cache/ironic}
IRONIC_CONF_DIR=${IRONIC_CONF_DIR:-/etc/ironic}
@@ -35,6 +38,28 @@ IRONIC_CONF_FILE=$IRONIC_CONF_DIR/ironic.conf
IRONIC_ROOTWRAP_CONF=$IRONIC_CONF_DIR/rootwrap.conf
IRONIC_POLICY_JSON=$IRONIC_CONF_DIR/policy.json
+# Set up defaults for functional / integration testing
+IRONIC_SCRIPTS_DIR=${IRONIC_SCRIPTS_DIR:-$TOP_DIR/tools/ironic/scripts}
+IRONIC_TEMPLATES_DIR=${IRONIC_TEMPLATES_DIR:-$TOP_DIR/tools/ironic/templates}
+IRONIC_BAREMETAL_BASIC_OPS=$(trueorfalse False $IRONIC_BAREMETAL_BASIC_OPS)
+IRONIC_SSH_USERNAME=${IRONIC_SSH_USERNAME:-`whoami`}
+IRONIC_SSH_KEY_DIR=${IRONIC_SSH_KEY_DIR:-$IRONIC_DATA_DIR/ssh_keys}
+IRONIC_SSH_KEY_FILENAME=${IRONIC_SSH_KEY_FILENAME:-ironic_key}
+IRONIC_KEY_FILE=$IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME
+IRONIC_SSH_VIRT_TYPE=${IRONIC_SSH_VIRT_TYPE:-virsh}
+IRONIC_TFTPBOOT_DIR=${IRONIC_TFTPBOOT_DIR:-$IRONIC_DATA_DIR/tftpboot}
+IRONIC_VM_SSH_PORT=${IRONIC_VM_SSH_PORT:-2222}
+IRONIC_VM_SSH_ADDRESS=${IRONIC_VM_SSH_ADDRESS:-$HOST_IP}
+IRONIC_VM_COUNT=${IRONIC_VM_COUNT:-1}
+IRONIC_VM_SPECS_CPU=${IRONIC_VM_SPECS_CPU:-1}
+IRONIC_VM_SPECS_RAM=${IRONIC_VM_SPECS_RAM:-256}
+IRONIC_VM_SPECS_DISK=${IRONIC_VM_SPECS_DISK:-10}
+IRONIC_VM_EMULATOR=${IRONIC_VM_EMULATOR:-/usr/bin/qemu-system-x86_64}
+IRONIC_VM_NETWORK_BRIDGE=${IRONIC_VM_NETWORK_BRIDGE:-brbm}
+IRONIC_VM_NETWORK_RANGE=${IRONIC_VM_NETWORK_RANGE:-192.0.2.0/24}
+IRONIC_VM_MACS_CSV_FILE=${IRONIC_VM_MACS_CSV_FILE:-$IRONIC_DATA_DIR/ironic_macs.csv}
+IRONIC_AUTHORIZED_KEYS_FILE=${IRONIC_AUTHORIZED_KEYS_FILE:-$HOME/.ssh/authorized_keys}
+
# Support entry points installation of console scripts
IRONIC_BIN_DIR=$(get_python_exec_prefix)
@@ -86,8 +111,8 @@ function configure_ironic {
iniset $IRONIC_CONF_FILE DEFAULT debug True
inicomment $IRONIC_CONF_FILE DEFAULT log_file
iniset $IRONIC_CONF_FILE DEFAULT sql_connection `database_connection_url ironic`
+ iniset $IRONIC_CONF_FILE DEFAULT state_path $IRONIC_STATE_PATH
iniset $IRONIC_CONF_FILE DEFAULT use_syslog $SYSLOG
-
# Configure Ironic conductor, if it was enabled.
if is_service_enabled ir-cond; then
configure_ironic_conductor
@@ -97,6 +122,10 @@ function configure_ironic {
if is_service_enabled ir-api; then
configure_ironic_api
fi
+
+ if [[ "$IRONIC_BAREMETAL_BASIC_OPS" == "True" ]]; then
+ configure_ironic_auxiliary
+ fi
}
# configure_ironic_api() - Is used by configure_ironic(). Performs
@@ -125,6 +154,10 @@ function configure_ironic_conductor {
cp -r $IRONIC_DIR/etc/ironic/rootwrap.d $IRONIC_CONF_DIR
iniset $IRONIC_CONF_FILE DEFAULT rootwrap_config $IRONIC_ROOTWRAP_CONF
+ iniset $IRONIC_CONF_FILE conductor api_url http://$SERVICE_HOST:6385
+ iniset $IRONIC_CONF_FILE pxe tftp_server $SERVICE_HOST
+ iniset $IRONIC_CONF_FILE pxe tftp_root $IRONIC_TFTPBOOT_DIR
+ iniset $IRONIC_CONF_FILE pxe tftp_master_path $IRONIC_TFTPBOOT_DIR/master_images
}
# create_ironic_cache_dir() - Part of the init_ironic() process
@@ -225,9 +258,233 @@ function stop_ironic {
screen -S $SCREEN_NAME -p ir-cond -X kill
}
+function is_ironic {
+ if ( is_service_enabled ir-cond && is_service_enabled ir-api ); then
+ return 0
+ fi
+ return 1
+}
-# Restore xtrace
+function configure_ironic_dirs {
+ 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
+ PXEBIN=/usr/lib/syslinux/pxelinux.0
+ elif is_fedora; then
+ PXEBIN=/usr/share/syslinux/pxelinux.0
+ fi
+ if [ ! -f $PXEBIN ]; then
+ die $LINENO "pxelinux.0 (from SYSLINUX) not found."
+ fi
+
+ cp $PXEBIN $IRONIC_TFTPBOOT_DIR
+ mkdir -p $IRONIC_TFTPBOOT_DIR/pxelinux.cfg
+}
+
+function ironic_ensure_libvirt_group {
+ groups $STACK_USER | grep -q $LIBVIRT_GROUP || adduser $STACK_USER $LIBVIRT_GROUP
+}
+
+function create_bridge_and_vms {
+ ironic_ensure_libvirt_group
+
+ # Call libvirt setup scripts in a new shell to ensure any new group membership
+ sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/setup-network"
+
+ sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/create-nodes \
+ $IRONIC_VM_SPECS_CPU $IRONIC_VM_SPECS_RAM $IRONIC_VM_SPECS_DISK \
+ amd64 $IRONIC_VM_COUNT $IRONIC_VM_NETWORK_BRIDGE $IRONIC_VM_EMULATOR" >> $IRONIC_VM_MACS_CSV_FILE
+
+}
+
+function enroll_vms {
+
+ CHASSIS_ID=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
+ IRONIC_NET_ID=$(neutron net-list | grep private | get_field 1)
+ local idx=0
+
+ # work around; need to know what netns neutron uses for private network
+ neutron port-create private
+
+ while read MAC; do
+
+ NODE_ID=$(ironic node-create --chassis_uuid $CHASSIS_ID --driver pxe_ssh \
+ -i ssh_virt_type=$IRONIC_SSH_VIRT_TYPE \
+ -i ssh_address=$IRONIC_VM_SSH_ADDRESS \
+ -i ssh_port=$IRONIC_VM_SSH_PORT \
+ -i ssh_username=$IRONIC_SSH_USERNAME \
+ -i ssh_key_filename=$IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME \
+ -p cpus=$IRONIC_VM_SPECS_CPU \
+ -p memory_mb=$IRONIC_VM_SPECS_RAM \
+ -p local_gb=$IRONIC_VM_SPECS_DISK \
+ -p cpu_arch=x86_64 \
+ | grep " uuid " | get_field 2)
+
+ ironic port-create --address $MAC --node_uuid $NODE_ID
+
+ idx=$((idx+1))
+
+ done < $IRONIC_VM_MACS_CSV_FILE
+
+ # create the nova flavor
+ nova flavor-create baremetal auto $IRONIC_VM_SPECS_RAM $IRONIC_VM_SPECS_DISK $IRONIC_VM_SPECS_CPU
+ nova flavor-key baremetal set "cpu_arch"="x86_64" "baremetal:deploy_kernel_id"="$BM_DEPLOY_KERNEL_ID" "baremetal:deploy_ramdisk_id"="$BM_DEPLOY_RAMDISK_ID"
+
+ # intentional sleep to make sure the tag has been set to port
+ sleep 10
+ TAPDEV=$(sudo ip netns exec qdhcp-${IRONIC_NET_ID} ip link list | grep tap | cut -d':' -f2 | cut -b2-)
+ TAG_ID=$(sudo ovs-vsctl show |grep ${TAPDEV} -A1 -m1 | grep tag | cut -d':' -f2 | cut -b2-)
+
+ # make sure veth pair is not existing, otherwise delete its links
+ sudo ip link show ovs-tap1 && sudo ip link delete ovs-tap1
+ sudo ip link show brbm-tap1 && sudo ip link delete brbm-tap1
+ # create veth pair for future interconnection between br-int and brbm
+ sudo ip link add brbm-tap1 type veth peer name ovs-tap1
+ sudo ip link set dev brbm-tap1 up
+ sudo ip link set dev ovs-tap1 up
+
+ sudo ovs-vsctl -- --if-exists del-port ovs-tap1 -- add-port br-int ovs-tap1 tag=$TAG_ID
+ sudo ovs-vsctl -- --if-exists del-port brbm-tap1 -- add-port $IRONIC_VM_NETWORK_BRIDGE brbm-tap1
+}
+
+function configure_tftpd {
+ # enable tftp natting for allowing connections to SERVICE_HOST's tftp server
+ sudo modprobe nf_conntrack_tftp
+ sudo modprobe nf_nat_tftp
+
+ if is_ubuntu; then
+ PXEBIN=/usr/lib/syslinux/pxelinux.0
+ elif is_fedora; then
+ 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
+ sudo cp $IRONIC_TEMPLATES_DIR/tftpd-xinetd.template /etc/xinetd.d/tftp
+ sudo sed -e "s|%TFTPBOOT_DIR%|$IRONIC_TFTPBOOT_DIR|g" -i /etc/xinetd.d/tftp
+
+ # setup tftp file mapping to satisfy requests at the root (booting) and
+ # /tftpboot/ sub-dir (as per deploy-ironic elements)
+ echo "r ^([^/]) $IRONIC_TFTPBOOT_DIR/\1" >$IRONIC_TFTPBOOT_DIR/map-file
+ echo "r ^(/tftpboot/) $IRONIC_TFTPBOOT_DIR/\2" >>$IRONIC_TFTPBOOT_DIR/map-file
+
+ chmod -R 0755 $IRONIC_TFTPBOOT_DIR
+ restart_service xinetd
+}
+
+function configure_ironic_ssh_keypair {
+ # Generating ssh key pair for stack user
+ if [[ ! -d $IRONIC_SSH_KEY_DIR ]]; then
+ mkdir -p $IRONIC_SSH_KEY_DIR
+ fi
+ if [[ ! -d $HOME/.ssh ]]; then
+ mkdir -p $HOME/.ssh
+ chmod 700 $HOME/.ssh
+ fi
+ echo -e 'n\n' | ssh-keygen -q -t rsa -P '' -f $IRONIC_KEY_FILE
+ cat $IRONIC_KEY_FILE.pub | tee -a $IRONIC_AUTHORIZED_KEYS_FILE
+}
+
+function ironic_ssh_check {
+ local KEY_FILE=$1
+ local FLOATING_IP=$2
+ local PORT=$3
+ local DEFAULT_INSTANCE_USER=$4
+ local ACTIVE_TIMEOUT=$5
+ if ! timeout $ACTIVE_TIMEOUT sh -c "while ! ssh -p $PORT -o StrictHostKeyChecking=no -i $KEY_FILE ${DEFAULT_INSTANCE_USER}@$FLOATING_IP echo success; do sleep 1; done"; then
+ die $LINENO "server didn't become ssh-able!"
+ fi
+}
+
+function configure_ironic_sshd {
+ # Ensure sshd server accepts connections from localhost only
+
+ SSH_CONFIG=/etc/ssh/sshd_config
+ HOST_PORT=$IRONIC_VM_SSH_ADDRESS:$IRONIC_VM_SSH_PORT
+ if ! sudo grep ListenAddress $SSH_CONFIG | grep $HOST_PORT; then
+ echo "ListenAddress $HOST_PORT" | sudo tee -a $SSH_CONFIG
+ fi
+
+ SSH_SERVICE_NAME=sshd
+ if is_ubuntu; then
+ SSH_SERVICE_NAME=ssh
+ fi
+
+ restart_service $SSH_SERVICE_NAME
+ # to ensure ssh service is up and running
+ sleep 3
+ ironic_ssh_check $IRONIC_SSH_KEY_DIR/$IRONIC_SSH_KEY_FILENAME $IRONIC_VM_SSH_ADDRESS $IRONIC_VM_SSH_PORT $IRONIC_SSH_USERNAME 10
+
+}
+
+function configure_ironic_auxiliary {
+ configure_ironic_dirs
+ configure_ironic_ssh_keypair
+ configure_ironic_sshd
+}
+
+function prepare_baremetal_basic_ops {
+
+ # install diskimage-builder
+ git_clone $BM_IMAGE_BUILD_REPO $BM_IMAGE_BUILD_DIR $BM_IMAGE_BUILD_BRANCH
+
+ # make sure all needed service were enabled
+ for srv in nova glance key neutron; do
+ if ! is_service_enabled "$srv"; then
+ die $LINENO "$srv should be enabled for ironic tests"
+ fi
+ done
+
+ SCREEN_NAME=${SCREEN_NAME:-stack}
+ SERVICE_DIR=${SERVICE_DIR:-${DEST}/status}
+
+ # stop all nova services
+ stop_nova || true
+
+ # remove any nova services failure status
+ find $SERVICE_DIR/$SCREEN_NAME -name 'n-*.failure' -exec rm -f '{}' \;
+
+ # start them again
+ start_nova_api
+ start_nova
+
+ TOKEN=$(keystone token-get | grep ' id ' | get_field 2)
+ die_if_not_set $LINENO TOKEN "Keystone fail to get token"
+
+ echo_summary "Creating and uploading baremetal images for ironic"
+
+ # build and upload separate deploy kernel & ramdisk
+ upload_baremetal_deploy $TOKEN
+
+ create_bridge_and_vms
+ enroll_vms
+ configure_tftpd
+}
+
+function cleanup_baremetal_basic_ops {
+ rm -f $IRONIC_VM_MACS_CSV_FILE
+ if [ -f $IRONIC_KEY_FILE ]; then
+ KEY=`cat $IRONIC_KEY_FILE.pub`
+ # remove public key from authorized_keys
+ grep -v "$KEY" $IRONIC_AUTHORIZED_KEYS_FILE > temp && mv temp $IRONIC_AUTHORIZED_KEYS_FILE
+ chmod 0600 $IRONIC_AUTHORIZED_KEYS_FILE
+ fi
+ sudo rm -rf $IRONIC_DATA_DIR $IRONIC_STATE_PATH
+ sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/cleanup-nodes $IRONIC_VM_COUNT $IRONIC_VM_NETWORK_BRIDGE"
+ sudo rm -rf /etc/xinetd.d/tftp /etc/init/tftpd-hpa.override
+ restart_service xinetd
+}
+
+# Restore xtrace + pipefail
$XTRACE
+$PIPEFAIL
# Tell emacs to use shell-script-mode
## Local variables:
diff --git a/lib/nova_plugins/hypervisor-ironic b/lib/nova_plugins/hypervisor-ironic
new file mode 100644
index 0000000000..5af7c0b292
--- /dev/null
+++ b/lib/nova_plugins/hypervisor-ironic
@@ -0,0 +1,75 @@
+# lib/nova_plugins/hypervisor-ironic
+# Configure the ironic hypervisor
+
+# Enable with:
+# VIRT_DRIVER=ironic
+
+# Dependencies:
+# ``functions`` file
+# ``nova`` configuration
+
+# 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
+# --------
+
+# Entry Points
+# ------------
+
+# clean_nova_hypervisor - Clean up an installation
+function cleanup_nova_hypervisor {
+ # This function intentionally left blank
+ :
+}
+
+# configure_nova_hypervisor - Set config files, create data dirs, etc
+function configure_nova_hypervisor {
+ iniset $NOVA_CONF ironic sql_connection `database_connection_url nova_bm`
+ LIBVIRT_FIREWALL_DRIVER=${LIBVIRT_FIREWALL_DRIVER:-"nova.virt.firewall.NoopFirewallDriver"}
+ iniset $NOVA_CONF DEFAULT compute_driver ironic.nova.virt.ironic.IronicDriver
+ iniset $NOVA_CONF DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
+ iniset $NOVA_CONF DEFAULT scheduler_host_manager ironic.nova.scheduler.ironic_host_manager.IronicHostManager
+ iniset $NOVA_CONF DEFAULT ram_allocation_ratio 1.0
+ iniset $NOVA_CONF DEFAULT reserved_host_memory_mb 0
+ # ironic section
+ iniset $NOVA_CONF ironic admin_username admin
+ iniset $NOVA_CONF ironic admin_password $ADMIN_PASSWORD
+ iniset $NOVA_CONF ironic admin_url $KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT/v2.0
+ iniset $NOVA_CONF ironic admin_tenant_name demo
+ iniset $NOVA_CONF ironic api_endpoint http://$SERVICE_HOST:6358/v1
+}
+
+# install_nova_hypervisor() - Install external components
+function install_nova_hypervisor {
+ # This function intentionally left blank
+ :
+}
+
+# start_nova_hypervisor - Start any required external services
+function start_nova_hypervisor {
+ # This function intentionally left blank
+ :
+}
+
+# stop_nova_hypervisor - Stop any external services
+function stop_nova_hypervisor {
+ # This function intentionally left blank
+ :
+}
+
+
+# Restore xtrace
+$MY_XTRACE
+
+# Local variables:
+# mode: shell-script
+# End:
diff --git a/stackrc b/stackrc
index 456637854b..4a997bf77c 100644
--- a/stackrc
+++ b/stackrc
@@ -267,7 +267,7 @@ DEFAULT_VIRT_DRIVER=libvirt
is_package_installed xenserver-core && DEFAULT_VIRT_DRIVER=xenserver
VIRT_DRIVER=${VIRT_DRIVER:-$DEFAULT_VIRT_DRIVER}
case "$VIRT_DRIVER" in
- libvirt)
+ ironic|libvirt)
LIBVIRT_TYPE=${LIBVIRT_TYPE:-kvm}
if [[ "$os_VENDOR" =~ (Debian) ]]; then
LIBVIRT_GROUP=libvirt
diff --git a/tools/install_prereqs.sh b/tools/install_prereqs.sh
index 0c65fd9b00..9651083cb3 100755
--- a/tools/install_prereqs.sh
+++ b/tools/install_prereqs.sh
@@ -55,7 +55,13 @@ export_proxy_variables
# ================
# Install package requirements
-install_package $(get_packages general $ENABLED_SERVICES)
+PACKAGES=$(get_packages general $ENABLED_SERVICES)
+if is_ubuntu && echo $PACKAGES | grep -q dkms ; then
+ # ensure headers for the running kernel are installed for any DKMS builds
+ PACKAGES="$PACKAGES linux-headers-$(uname -r)"
+fi
+
+install_package $PACKAGES
if [[ -n "$SYSLOG" && "$SYSLOG" != "False" ]]; then
if is_ubuntu || is_fedora; then
diff --git a/tools/ironic/scripts/cleanup-nodes b/tools/ironic/scripts/cleanup-nodes
new file mode 100755
index 0000000000..dc5a19d1cd
--- /dev/null
+++ b/tools/ironic/scripts/cleanup-nodes
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+# **cleanup-nodes**
+
+# Cleans up baremetal poseur nodes and volumes created during ironic setup
+# Assumes calling user has proper libvirt group membership and access.
+
+set -exu
+
+LIBVIRT_STORAGE_POOL=${LIBVIRT_STORAGE_POOL:-"default"}
+
+VM_COUNT=$1
+NETWORK_BRIDGE=$2
+
+for (( idx=0; idx<$VM_COUNT; idx++ )); do
+ NAME="baremetal${NETWORK_BRIDGE}_${idx}"
+ VOL_NAME="baremetal${NETWORK_BRIDGE}-${idx}.qcow2"
+ virsh list | grep -q $NAME && virsh destroy $NAME
+ virsh list --inactive | grep -q $NAME && virsh undefine $NAME
+
+ if virsh pool-list | grep -q $LIBVIRT_STORAGE_POOL ; then
+ virsh vol-list $LIBVIRT_STORAGE_POOL | grep -q $VOL_NAME &&
+ virsh vol-delete $VOL_NAME --pool $LIBVIRT_STORAGE_POOL
+ fi
+done
diff --git a/tools/ironic/scripts/configure-vm b/tools/ironic/scripts/configure-vm
new file mode 100755
index 0000000000..9936b76c4f
--- /dev/null
+++ b/tools/ironic/scripts/configure-vm
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+import argparse
+import os.path
+
+import libvirt
+
+templatedir = os.path.join(os.path.dirname(os.path.dirname(__file__)),
+ 'templates')
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Configure a kvm virtual machine for the seed image.")
+ parser.add_argument('--name', default='seed',
+ help='the name to give the machine in libvirt.')
+ parser.add_argument('--image',
+ help='Use a custom image file (must be qcow2).')
+ parser.add_argument('--engine', default='qemu',
+ help='The virtualization engine to use')
+ parser.add_argument('--arch', default='i686',
+ help='The architecture to use')
+ parser.add_argument('--memory', default='2097152',
+ help="Maximum memory for the VM in KB.")
+ parser.add_argument('--cpus', default='1',
+ help="CPU count for the VM.")
+ parser.add_argument('--bootdev', default='hd',
+ help="What boot device to use (hd/network).")
+ parser.add_argument('--network', default="brbm",
+ help='The libvirt network name to use')
+ parser.add_argument('--libvirt-nic-driver', default='e1000',
+ help='The libvirt network driver to use')
+ parser.add_argument('--emulator', default=None,
+ help='Path to emulator bin for vm template')
+ args = parser.parse_args()
+ with file(templatedir + '/vm.xml', 'rb') as f:
+ source_template = f.read()
+ params = {
+ 'name': args.name,
+ 'imagefile': args.image,
+ 'engine': args.engine,
+ 'arch': args.arch,
+ 'memory': args.memory,
+ 'cpus': args.cpus,
+ 'bootdev': args.bootdev,
+ 'network': args.network,
+ 'emulator': args.emulator,
+ }
+
+ if args.emulator:
+ params['emulator'] = args.emulator
+ else:
+ if os.path.exists("/usr/bin/kvm"): # Debian
+ params['emulator'] = "/usr/bin/kvm"
+ elif os.path.exists("/usr/bin/qemu-kvm"): # Redhat
+ params['emulator'] = "/usr/bin/qemu-kvm"
+
+ nicparams = {
+ 'nicdriver': args.libvirt_nic_driver,
+ 'network': args.network,
+ }
+
+ params['bm_network'] = """
+
+
+
+
+
+
+""" % nicparams
+
+ libvirt_template = source_template % params
+ conn = libvirt.open("qemu:///system")
+ a = conn.defineXML(libvirt_template)
+ print ("Created machine %s with UUID %s" % (args.name, a.UUIDString()))
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/ironic/scripts/create-nodes b/tools/ironic/scripts/create-nodes
new file mode 100755
index 0000000000..3232b50776
--- /dev/null
+++ b/tools/ironic/scripts/create-nodes
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+
+# **create-nodes**
+
+# Creates baremetal poseur nodes for ironic testing purposes
+
+set -exu
+
+# Keep track of the devstack directory
+TOP_DIR=$(cd $(dirname "$0")/.. && pwd)
+
+CPU=$1
+MEM=$(( 1024 * $2 ))
+# extra G to allow fuzz for partition table : flavor size and registered size
+# need to be different to actual size.
+DISK=$(( $3 + 1))
+
+case $4 in
+ i386) ARCH='i686' ;;
+ amd64) ARCH='x86_64' ;;
+ *) echo "Unsupported arch $4!" ; exit 1 ;;
+esac
+
+TOTAL=$(($5 - 1))
+BRIDGE=$6
+EMULATOR=$7
+
+LIBVIRT_NIC_DRIVER=${LIBVIRT_NIC_DRIVER:-"e1000"}
+LIBVIRT_STORAGE_POOL=${LIBVIRT_STORAGE_POOL:-"default"}
+
+if ! virsh pool-list --all | grep -q $LIBVIRT_STORAGE_POOL; then
+ virsh pool-define-as --name $LIBVIRT_STORAGE_POOL dir --target /var/lib/libvirt/images >&2
+ virsh pool-autostart $LIBVIRT_STORAGE_POOL >&2
+ virsh pool-start $LIBVIRT_STORAGE_POOL >&2
+fi
+
+pool_state=$(virsh pool-info $LIBVIRT_STORAGE_POOL | grep State | awk '{ print $2 }')
+if [ "$pool_state" != "running" ] ; then
+ [ ! -d /var/lib/libvirt/images ] && sudo mkdir /var/lib/libvirt/images
+ virsh pool-start $LIBVIRT_STORAGE_POOL >&2
+fi
+
+PREALLOC=
+if [ -f /etc/debian_version ]; then
+ PREALLOC="--prealloc-metadata"
+fi
+
+DOMS=""
+for idx in $(seq 0 $TOTAL) ; do
+ NAME="baremetal${BRIDGE}_${idx}"
+ DOMS="$DOMS $NAME"
+ VOL_NAME="baremetal${BRIDGE}-${idx}.qcow2"
+ (virsh list --all | grep -q $NAME) && continue
+
+ virsh vol-list --pool $LIBVIRT_STORAGE_POOL | grep -q $VOL_NAME &&
+ virsh vol-delete $VOL_NAME --pool $LIBVIRT_STORAGE_POOL >&2
+ virsh vol-create-as $LIBVIRT_STORAGE_POOL ${VOL_NAME} ${DISK}G --format qcow2 $PREALLOC >&2
+ volume_path=$(virsh vol-path --pool $LIBVIRT_STORAGE_POOL $VOL_NAME)
+ # Pre-touch the VM to set +C, as it can only be set on empty files.
+ sudo touch "$volume_path"
+ sudo chattr +C "$volume_path" || true
+ $TOP_DIR/scripts/configure-vm --bootdev network --name $NAME --image "$volume_path" --arch $ARCH --cpus $CPU --memory $MEM --libvirt-nic-driver $LIBVIRT_NIC_DRIVER --emulator $EMULATOR --network $BRIDGE >&2
+done
+
+for dom in $DOMS ; do
+ # echo mac
+ virsh dumpxml $dom | grep "mac address" | head -1 | cut -d\' -f2
+done
diff --git a/tools/ironic/scripts/setup-network b/tools/ironic/scripts/setup-network
new file mode 100755
index 0000000000..8c3ea901b4
--- /dev/null
+++ b/tools/ironic/scripts/setup-network
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# **setup-network**
+
+# Setups openvswitch libvirt network suitable for
+# running baremetal poseur nodes for ironic testing purposes
+
+set -exu
+
+# Keep track of the devstack directory
+TOP_DIR=$(cd $(dirname "$0")/.. && pwd)
+BRIDGE_SUFFIX=${1:-''}
+BRIDGE_NAME=brbm$BRIDGE_SUFFIX
+
+# Only add bridge if missing
+(sudo ovs-vsctl list-br | grep ${BRIDGE_NAME}$) || sudo ovs-vsctl add-br ${BRIDGE_NAME}
+
+# remove bridge before replacing it.
+(virsh net-list | grep "${BRIDGE_NAME} ") && virsh net-destroy ${BRIDGE_NAME}
+(virsh net-list --inactive | grep "${BRIDGE_NAME} ") && virsh net-undefine ${BRIDGE_NAME}
+
+virsh net-define <(sed s/brbm/$BRIDGE_NAME/ $TOP_DIR/templates/brbm.xml)
+virsh net-autostart ${BRIDGE_NAME}
+virsh net-start ${BRIDGE_NAME}
diff --git a/tools/ironic/templates/brbm.xml b/tools/ironic/templates/brbm.xml
new file mode 100644
index 0000000000..0769d3f1d0
--- /dev/null
+++ b/tools/ironic/templates/brbm.xml
@@ -0,0 +1,6 @@
+
+ brbm
+
+
+
+
diff --git a/tools/ironic/templates/tftpd-xinetd.template b/tools/ironic/templates/tftpd-xinetd.template
new file mode 100644
index 0000000000..7b9b0f8a78
--- /dev/null
+++ b/tools/ironic/templates/tftpd-xinetd.template
@@ -0,0 +1,11 @@
+service tftp
+{
+ protocol = udp
+ port = 69
+ socket_type = dgram
+ wait = yes
+ user = root
+ server = /usr/sbin/in.tftpd
+ server_args = -v -v -v -v -v --map-file %TFTPBOOT_DIR%/map-file %TFTPBOOT_DIR%
+ disable = no
+}
diff --git a/tools/ironic/templates/vm.xml b/tools/ironic/templates/vm.xml
new file mode 100644
index 0000000000..b18dec055f
--- /dev/null
+++ b/tools/ironic/templates/vm.xml
@@ -0,0 +1,43 @@
+
+ %(name)s
+ %(memory)s
+ %(cpus)s
+
+ hvm
+
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ restart
+
+ %(emulator)s
+
+
+
+
+
+
+
+
+
+ %(network)s
+ %(bm_network)s
+
+
+
+
+
+
+
+
+