devstack/lib/ironic
Jamie Lennox b17ad7560d Use identity V3 API for endpoint creation
Always use the keystone V3 API when creating services and endpoints. The syntax
here is slightly different but we maintain the function interface.

Change-Id: Ib3a375918a45fd6e37d873a1a5c0c4b26bdbb5d8
Implements: bp keystonev3
2015-07-08 05:17:00 +10:00

839 lines
33 KiB
Bash

#!/bin/bash
#
# lib/ironic
# Functions to control the configuration and operation of the **Ironic** service
# Dependencies:
#
# - ``functions`` file
# - ``DEST``, ``DATA_DIR``, ``STACK_USER`` must be defined
# - ``SERVICE_{TENANT_NAME|PASSWORD}`` must be defined
# - ``SERVICE_HOST``
# - ``KEYSTONE_TOKEN_FORMAT`` must be defined
# ``stack.sh`` calls the entry points in this order:
#
# - install_ironic
# - install_ironicclient
# - init_ironic
# - start_ironic
# - stop_ironic
# - cleanup_ironic
# 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
GITDIR["python-ironicclient"]=$DEST/python-ironicclient
IRONIC_DIR=$DEST/ironic
IRONIC_PYTHON_AGENT_DIR=$DEST/ironic-python-agent
IRONIC_DATA_DIR=$DATA_DIR/ironic
IRONIC_STATE_PATH=/var/lib/ironic
IRONIC_AUTH_CACHE_DIR=${IRONIC_AUTH_CACHE_DIR:-/var/cache/ironic}
IRONIC_CONF_DIR=${IRONIC_CONF_DIR:-/etc/ironic}
IRONIC_CONF_FILE=$IRONIC_CONF_DIR/ironic.conf
IRONIC_ROOTWRAP_CONF=$IRONIC_CONF_DIR/rootwrap.conf
IRONIC_POLICY_JSON=$IRONIC_CONF_DIR/policy.json
# Deploy callback timeout can be changed from its default (1800), if required.
IRONIC_CALLBACK_TIMEOUT=${IRONIC_CALLBACK_TIMEOUT:-}
# Deploy to hardware platform
IRONIC_HW_NODE_CPU=${IRONIC_HW_NODE_CPU:-1}
IRONIC_HW_NODE_RAM=${IRONIC_HW_NODE_RAM:-512}
IRONIC_HW_NODE_DISK=${IRONIC_HW_NODE_DISK:-10}
IRONIC_HW_EPHEMERAL_DISK=${IRONIC_HW_EPHEMERAL_DISK:-0}
# The file is composed of multiple lines, each line includes four field
# separated by white space: IPMI address, MAC address, IPMI username
# and IPMI password.
#
# 192.168.110.107 00:1e:67:57:50:4c root otc123
IRONIC_IPMIINFO_FILE=${IRONIC_IPMIINFO_FILE:-$IRONIC_DATA_DIR/hardware_info}
# Set up defaults for functional / integration testing
IRONIC_NODE_UUID=${IRONIC_NODE_UUID:-`uuidgen`}
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_ENABLED_DRIVERS=${IRONIC_ENABLED_DRIVERS:-fake,pxe_ssh,pxe_ipmitool}
IRONIC_SSH_USERNAME=${IRONIC_SSH_USERNAME:-`whoami`}
IRONIC_SSH_TIMEOUT=${IRONIC_SSH_TIMEOUT:-15}
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_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_TFTPSERVER_IP=${IRONIC_TFTPSERVER_IP:-$HOST_IP}
IRONIC_VM_SSH_PORT=${IRONIC_VM_SSH_PORT:-22}
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:-512}
IRONIC_VM_SPECS_DISK=${IRONIC_VM_SPECS_DISK:-10}
IRONIC_VM_EPHEMERAL_DISK=${IRONIC_VM_EPHEMERAL_DISK:-0}
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}
# By default, baremetal VMs will console output to file.
IRONIC_VM_LOG_CONSOLE=${IRONIC_VM_LOG_CONSOLE:-True}
IRONIC_VM_LOG_DIR=${IRONIC_VM_LOG_DIR:-$IRONIC_DATA_DIR/logs/}
# 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.
# (The value must be a absolute path)
IRONIC_DEPLOY_RAMDISK=${IRONIC_DEPLOY_RAMDISK:-}
IRONIC_DEPLOY_KERNEL=${IRONIC_DEPLOY_KERNEL:-}
IRONIC_DEPLOY_ELEMENT=${IRONIC_DEPLOY_ELEMENT:-deploy-ironic}
IRONIC_AGENT_KERNEL_URL=${IRONIC_AGENT_KERNEL_URL:-http://tarballs.openstack.org/ironic-python-agent/coreos/files/coreos_production_pxe.vmlinuz}
IRONIC_AGENT_RAMDISK_URL=${IRONIC_AGENT_RAMDISK_URL:-http://tarballs.openstack.org/ironic-python-agent/coreos/files/coreos_production_pxe_image-oem.cpio.gz}
# Which deploy driver to use - valid choices right now
# are ``pxe_ssh``, ``pxe_ipmitool``, ``agent_ssh`` and ``agent_ipmitool``.
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}
# Support entry points installation of console scripts
IRONIC_BIN_DIR=$(get_python_exec_prefix)
# Ironic connection info. Note the port must be specified.
IRONIC_SERVICE_PROTOCOL=http
IRONIC_SERVICE_PORT=${IRONIC_SERVICE_PORT:-6385}
IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST:$IRONIC_SERVICE_PORT}
# 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}
# NOTE(lucasagomes): This flag is used to differentiate the nodes that
# uses IPA as their deploy ramdisk from nodes that uses the agent_* drivers
# (which also uses IPA but depends on Swift Temp URLs to work). At present,
# all drivers that uses the iSCSI approach for their deployment supports
# using both, IPA or bash ramdisks for the deployment. In the future we
# want to remove the support for the bash ramdisk in favor of IPA, once
# we get there this flag can be removed, and all conditionals that uses
# it should just run by default.
IRONIC_DEPLOY_DRIVER_ISCSI_WITH_IPA=$(trueorfalse False IRONIC_DEPLOY_DRIVER_ISCSI_WITH_IPA)
# 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
# ---------
# Test if any Ironic services are enabled
# is_ironic_enabled
function is_ironic_enabled {
[[ ,${ENABLED_SERVICES} =~ ,"ir-" ]] && return 0
return 1
}
function is_ironic_hardware {
is_ironic_enabled && [[ -n "${IRONIC_DEPLOY_DRIVER##*_ssh}" ]] && return 0
return 1
}
function is_deployed_by_agent {
[[ -z "${IRONIC_DEPLOY_DRIVER%%agent*}" ]] && return 0
return 1
}
function is_deployed_with_ipa_ramdisk {
is_deployed_by_agent || [[ "$IRONIC_DEPLOY_DRIVER_ISCSI_WITH_IPA" == "True" ]] && return 0
return 1
}
# install_ironic() - Collect source and prepare
function install_ironic {
# make sure all needed service were enabled
local req_services="key"
if [[ "$VIRT_DRIVER" == "ironic" ]]; then
req_services+=" nova glance neutron"
fi
for srv in $req_services; do
if ! is_service_enabled "$srv"; then
die $LINENO "$srv should be enabled for Ironic."
fi
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
function install_ironicclient {
if use_library_from_git "python-ironicclient"; then
git_clone_by_name "python-ironicclient"
setup_dev_lib "python-ironicclient"
sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-ironicclient"]}/tools/,/etc/bash_completion.d/}ironic.bash_completion
else
# nothing actually "requires" ironicclient, so force instally from pypi
pip_install_gr python-ironicclient
fi
}
# _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 {
sudo rm -rf $IRONIC_AUTH_CACHE_DIR $IRONIC_CONF_DIR
}
# configure_ironic_dirs() - Create all directories required by Ironic and
# associated services.
function configure_ironic_dirs {
sudo install -d -o $STACK_USER $IRONIC_CONF_DIR $STACK_USER $IRONIC_DATA_DIR \
$IRONIC_STATE_PATH $IRONIC_TFTPBOOT_DIR $IRONIC_TFTPBOOT_DIR/pxelinux.cfg
sudo chown -R $STACK_USER:$LIBVIRT_GROUP $IRONIC_TFTPBOOT_DIR
if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
sudo install -d -o $STACK_USER -g $LIBVIRT_GROUP $IRONIC_HTTP_DIR
fi
if [ ! -f $IRONIC_PXE_BOOT_IMAGE ]; then
die $LINENO "PXE boot file $IRONIC_PXE_BOOT_IMAGE not found."
fi
# Copy PXE binary
if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
cp $IRONIC_PXE_BOOT_IMAGE $IRONIC_TFTPBOOT_DIR
else
# Syslinux >= 5.00 pxelinux.0 binary is not "stand-alone" anymore,
# it depends on some c32 modules to work correctly.
# More info: http://www.syslinux.org/wiki/index.php/Library_modules
cp -aR $(dirname $IRONIC_PXE_BOOT_IMAGE)/*.{c32,0} $IRONIC_TFTPBOOT_DIR
fi
}
# configure_ironic() - Set config files, create data dirs, etc
function configure_ironic {
configure_ironic_dirs
# Copy over ironic configuration file and configure common parameters.
cp $IRONIC_DIR/etc/ironic/ironic.conf.sample $IRONIC_CONF_FILE
iniset $IRONIC_CONF_FILE DEFAULT debug True
inicomment $IRONIC_CONF_FILE DEFAULT log_file
iniset $IRONIC_CONF_FILE database 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
fi
# Configure Ironic API, if it was enabled.
if is_service_enabled ir-api; then
configure_ironic_api
fi
# Format logging
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
# API specific configuration.
function configure_ironic_api {
iniset $IRONIC_CONF_FILE DEFAULT auth_strategy keystone
iniset $IRONIC_CONF_FILE oslo_policy policy_file $IRONIC_POLICY_JSON
# TODO(Yuki Nishiwaki): This is a temporary work-around until Ironic is fixed(bug#1422632).
# These codes need to be changed to use the function of configure_auth_token_middleware
# after Ironic conforms to the new auth plugin.
iniset $IRONIC_CONF_FILE keystone_authtoken identity_uri $KEYSTONE_AUTH_URI
iniset $IRONIC_CONF_FILE keystone_authtoken auth_uri $KEYSTONE_SERVICE_URI/v2.0
iniset $IRONIC_CONF_FILE keystone_authtoken admin_user ironic
iniset $IRONIC_CONF_FILE keystone_authtoken admin_password $SERVICE_PASSWORD
iniset $IRONIC_CONF_FILE keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
iniset $IRONIC_CONF_FILE keystone_authtoken cafile $SSL_BUNDLE_FILE
iniset $IRONIC_CONF_FILE keystone_authtoken signing_dir $IRONIC_AUTH_CACHE_DIR/api
iniset_rpc_backend ironic $IRONIC_CONF_FILE
iniset $IRONIC_CONF_FILE api port $IRONIC_SERVICE_PORT
cp -p $IRONIC_DIR/etc/ironic/policy.json $IRONIC_POLICY_JSON
}
# configure_ironic_conductor() - Is used by configure_ironic().
# Sets conductor specific settings.
function configure_ironic_conductor {
cp $IRONIC_DIR/etc/ironic/rootwrap.conf $IRONIC_ROOTWRAP_CONF
cp -r $IRONIC_DIR/etc/ironic/rootwrap.d $IRONIC_CONF_DIR
local ironic_rootwrap=$(get_rootwrap_location ironic)
local rootwrap_isudoer_cmd="$ironic_rootwrap $IRONIC_CONF_DIR/rootwrap.conf *"
# Set up the rootwrap sudoers for ironic
local tempfile=`mktemp`
echo "$STACK_USER ALL=(root) NOPASSWD: $rootwrap_isudoer_cmd" >$tempfile
chmod 0440 $tempfile
sudo chown root:root $tempfile
sudo mv $tempfile /etc/sudoers.d/ironic-rootwrap
iniset $IRONIC_CONF_FILE DEFAULT rootwrap_config $IRONIC_ROOTWRAP_CONF
iniset $IRONIC_CONF_FILE DEFAULT enabled_drivers $IRONIC_ENABLED_DRIVERS
iniset $IRONIC_CONF_FILE conductor api_url $IRONIC_SERVICE_PROTOCOL://$HOST_IP:$IRONIC_SERVICE_PORT
if [[ -n "$IRONIC_CALLBACK_TIMEOUT" ]]; then
iniset $IRONIC_CONF_FILE conductor deploy_callback_timeout $IRONIC_CALLBACK_TIMEOUT
fi
iniset $IRONIC_CONF_FILE pxe tftp_server $IRONIC_TFTPSERVER_IP
iniset $IRONIC_CONF_FILE pxe tftp_root $IRONIC_TFTPBOOT_DIR
iniset $IRONIC_CONF_FILE pxe tftp_master_path $IRONIC_TFTPBOOT_DIR/master_images
local pxe_params=""
if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
pxe_params+="nofb nomodeset vga=normal console=ttyS0"
if is_deployed_with_ipa_ramdisk; then
pxe_params+=" systemd.journald.forward_to_console=yes"
fi
fi
# When booting with less than 1GB, we need to switch from default tmpfs
# to ramfs for ramdisks to decompress successfully.
if (is_ironic_hardware && [[ "$IRONIC_HW_NODE_RAM" -lt 1024 ]]) ||
(! is_ironic_hardware && [[ "$IRONIC_VM_SPECS_RAM" -lt 1024 ]]); then
pxe_params+=" rootfstype=ramfs"
fi
if [[ -n "$pxe_params" ]]; then
iniset $IRONIC_CONF_FILE pxe pxe_append_params "$pxe_params"
fi
if is_deployed_by_agent; 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}:${SWIFT_DEFAULT_BIND_PORT:-8080}
iniset $IRONIC_CONF_FILE glance swift_api_version v1
local tenant_id=$(get_or_create_project $SERVICE_TENANT_NAME default)
iniset $IRONIC_CONF_FILE glance swift_account AUTH_${tenant_id}
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
iniset $IRONIC_CONF_FILE agent agent_erase_devices_priority 0
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
function create_ironic_cache_dir {
# Create cache dir
sudo mkdir -p $IRONIC_AUTH_CACHE_DIR/api
sudo chown $STACK_USER $IRONIC_AUTH_CACHE_DIR/api
rm -f $IRONIC_AUTH_CACHE_DIR/api/*
sudo mkdir -p $IRONIC_AUTH_CACHE_DIR/registry
sudo chown $STACK_USER $IRONIC_AUTH_CACHE_DIR/registry
rm -f $IRONIC_AUTH_CACHE_DIR/registry/*
}
# create_ironic_accounts() - Set up common required ironic accounts
# Tenant User Roles
# ------------------------------------------------------------------
# service ironic admin # if enabled
function create_ironic_accounts {
# Ironic
if [[ "$ENABLED_SERVICES" =~ "ir-api" ]]; then
# Get ironic user if exists
# NOTE(Shrews): This user MUST have admin level privileges!
create_service_user "ironic" "admin"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
get_or_create_service "ironic" "baremetal" "Ironic baremetal provisioning service"
get_or_create_endpoint "baremetal" \
"$REGION_NAME" \
"$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT" \
"$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT" \
"$IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT"
fi
fi
}
# init_ironic() - Initialize databases, etc.
function init_ironic {
# Save private network as cleaning network
local cleaning_network_uuid
cleaning_network_uuid=$(neutron net-list | grep private | get_field 1)
iniset $IRONIC_CONF_FILE neutron cleaning_network_uuid ${cleaning_network_uuid}
# (Re)create ironic database
recreate_database ironic
# Migrate ironic database
$IRONIC_BIN_DIR/ironic-dbsync --config-file=$IRONIC_CONF_FILE
create_ironic_cache_dir
}
# _ironic_bm_vm_names() - Generates list of names for baremetal VMs.
function _ironic_bm_vm_names {
local idx
local num_vms=$(($IRONIC_VM_COUNT - 1))
for idx in $(seq 0 $num_vms); do
echo "baremetal${IRONIC_VM_NETWORK_BRIDGE}_${idx}"
done
}
# start_ironic() - Start running processes, including screen
function start_ironic {
# Start Ironic API server, if enabled.
if is_service_enabled ir-api; then
start_ironic_api
fi
# Start Ironic conductor, if enabled.
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().
# Starts Ironic API server.
function start_ironic_api {
run_process ir-api "$IRONIC_BIN_DIR/ironic-api --config-file=$IRONIC_CONF_FILE"
echo "Waiting for ir-api ($IRONIC_HOSTPORT) to start..."
if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $IRONIC_SERVICE_PROTOCOL://$IRONIC_HOSTPORT; do sleep 1; done"; then
die $LINENO "ir-api did not start"
fi
}
# start_ironic_conductor() - Used by start_ironic().
# Starts Ironic conductor.
function start_ironic_conductor {
run_process ir-cond "$IRONIC_BIN_DIR/ironic-conductor --config-file=$IRONIC_CONF_FILE"
# TODO(romcheg): Find a way to check whether the conductor has started.
}
# stop_ironic() - Stop running processes
function stop_ironic {
stop_process ir-api
stop_process ir-cond
# Cleanup the WSGI files
if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
_cleanup_ironic_apache_wsgi
fi
}
function create_ovs_taps {
local ironic_net_id=$(neutron net-list | grep private | get_field 1)
# Work around: No netns exists on host until a Neutron port is created. We
# need to create one in Neutron to know what netns to tap into prior to the
# first node booting.
local port_id=$(neutron port-create private | grep " id " | get_field 2)
# intentional sleep to make sure the tag has been set to port
sleep 10
if [[ "$Q_USE_NAMESPACE" = "True" ]]; then
local tapdev=$(sudo ip netns exec qdhcp-${ironic_net_id} ip link list | grep " tap" | cut -d':' -f2 | cut -b2-)
else
local tapdev=$(sudo ip link list | grep " tap" | cut -d':' -f2 | cut -b2-)
fi
local 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
# 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 {
# Call libvirt setup scripts in a new shell to ensure any new group membership
sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/setup-network"
if [[ "$IRONIC_VM_LOG_CONSOLE" == "True" ]] ; then
local log_arg="$IRONIC_VM_LOG_DIR"
else
local log_arg=""
fi
local vm_name
for vm_name in $(_ironic_bm_vm_names); do
sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/create-node $vm_name \
$IRONIC_VM_SPECS_CPU $IRONIC_VM_SPECS_RAM $IRONIC_VM_SPECS_DISK \
amd64 $IRONIC_VM_NETWORK_BRIDGE $IRONIC_VM_EMULATOR \
$log_arg" >> $IRONIC_VM_MACS_CSV_FILE
done
create_ovs_taps
}
function wait_for_nova_resources {
# After nodes have been enrolled, we need to wait for both ironic and
# nova's periodic tasks to populate the resource tracker with available
# nodes and resources. Wait up to 2 minutes for a given resource before
# timing out.
local resource=$1
local expected_count=$2
echo_summary "Waiting 2 minutes for Nova resource tracker to pick up $resource >= $expected_count"
for i in $(seq 1 120); do
if [ $(nova hypervisor-stats | grep " $resource " | get_field 2) -ge $expected_count ]; then
return 0
fi
sleep 1
done
die $LINENO "Timed out waiting for Nova hypervisor-stats $resource >= $expected_count"
}
function enroll_nodes {
local chassis_id=$(ironic chassis-create -d "ironic test chassis" | grep " uuid " | get_field 2)
if ! is_ironic_hardware; then
local ironic_node_cpu=$IRONIC_VM_SPECS_CPU
local ironic_node_ram=$IRONIC_VM_SPECS_RAM
local ironic_node_disk=$IRONIC_VM_SPECS_DISK
local ironic_ephemeral_disk=$IRONIC_VM_EPHEMERAL_DISK
local ironic_hwinfo_file=$IRONIC_VM_MACS_CSV_FILE
local node_options="\
-i deploy_kernel=$IRONIC_DEPLOY_KERNEL_ID \
-i deploy_ramdisk=$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 \
-i ssh_username=$IRONIC_SSH_USERNAME \
-i ssh_key_filename=$IRONIC_KEY_FILE"
else
local ironic_node_cpu=$IRONIC_HW_NODE_CPU
local ironic_node_ram=$IRONIC_HW_NODE_RAM
local ironic_node_disk=$IRONIC_HW_NODE_DISK
local ironic_ephemeral_disk=$IRONIC_HW_EPHEMERAL_DISK
if [[ -z "${IRONIC_DEPLOY_DRIVER##*_ipmitool}" ]]; then
local ironic_hwinfo_file=$IRONIC_IPMIINFO_FILE
fi
fi
local total_nodes=0
local total_cpus=0
while read hardware_info; do
if ! is_ironic_hardware; then
local mac_address=$hardware_info
elif [[ -z "${IRONIC_DEPLOY_DRIVER##*_ipmitool}" ]]; then
local ipmi_address=$(echo $hardware_info |awk '{print $1}')
local mac_address=$(echo $hardware_info |awk '{print $2}')
local ironic_ipmi_username=$(echo $hardware_info |awk '{print $3}')
local ironic_ipmi_passwd=$(echo $hardware_info |awk '{print $4}')
# Currently we require all hardware platform have same CPU/RAM/DISK info
# in future, this can be enhanced to support different type, and then
# we create the bare metal flavor with minimum value
local node_options="-i ipmi_address=$ipmi_address -i ipmi_password=$ironic_ipmi_passwd\
-i ipmi_username=$ironic_ipmi_username"
node_options+=" -i deploy_kernel=$IRONIC_DEPLOY_KERNEL_ID"
node_options+=" -i deploy_ramdisk=$IRONIC_DEPLOY_RAMDISK_ID"
fi
# First node created will be used for testing in ironic w/o glance
# scenario, so we need to know its UUID.
local standalone_node_uuid=$([ $total_nodes -eq 0 ] && echo "--uuid $IRONIC_NODE_UUID")
local node_id=$(ironic node-create $standalone_node_uuid\
--chassis_uuid $chassis_id \
--driver $IRONIC_DEPLOY_DRIVER \
-p cpus=$ironic_node_cpu\
-p memory_mb=$ironic_node_ram\
-p local_gb=$ironic_node_disk\
-p cpu_arch=x86_64 \
$node_options \
| grep " uuid " | get_field 2)
ironic port-create --address $mac_address --node $node_id
total_nodes=$((total_nodes+1))
total_cpus=$((total_cpus+$ironic_node_cpu))
done < $ironic_hwinfo_file
# create the nova flavor
# NOTE(adam_g): Attempting to use an autogenerated UUID for flavor id here uncovered
# bug (LP: #1333852) in Trove. This can be changed to use an auto flavor id when the
# bug is fixed in Juno.
local adjusted_disk=$(($ironic_node_disk - $ironic_ephemeral_disk))
nova flavor-create --ephemeral $ironic_ephemeral_disk baremetal 551 $ironic_node_ram $adjusted_disk $ironic_node_cpu
nova flavor-key baremetal set "cpu_arch"="x86_64"
if [ "$VIRT_DRIVER" == "ironic" ]; then
wait_for_nova_resources "count" $total_nodes
wait_for_nova_resources "vcpus" $total_cpus
fi
}
function configure_iptables {
# enable tftp natting for allowing connections to HOST_IP's tftp server
sudo modprobe nf_conntrack_tftp
sudo modprobe nf_nat_tftp
# nodes boot from TFTP and callback to the API server listening on $HOST_IP
sudo iptables -I INPUT -d $HOST_IP -p udp --dport 69 -j ACCEPT || true
sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true
if is_deployed_by_agent; then
# agent ramdisk gets instance image from swift
sudo iptables -I INPUT -d $HOST_IP -p tcp --dport ${SWIFT_DEFAULT_BIND_PORT:-8080} -j ACCEPT || true
fi
if [[ "$IRONIC_IPXE_ENABLED" == "True" ]] ; then
sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $IRONIC_HTTP_PORT -j ACCEPT || true
fi
}
function configure_tftpd {
# 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 {
if [[ ! -d $HOME/.ssh ]]; then
mkdir -p $HOME/.ssh
chmod 700 $HOME/.ssh
fi
if [[ ! -e $IRONIC_KEY_FILE ]]; then
if [[ ! -d $(dirname $IRONIC_KEY_FILE) ]]; then
mkdir -p $(dirname $IRONIC_KEY_FILE)
fi
echo -e 'n\n' | ssh-keygen -q -t rsa -P '' -f $IRONIC_KEY_FILE
fi
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_auxiliary {
configure_ironic_ssh_keypair
ironic_ssh_check $IRONIC_KEY_FILE $IRONIC_VM_SSH_ADDRESS $IRONIC_VM_SSH_PORT $IRONIC_SSH_USERNAME $IRONIC_SSH_TIMEOUT
}
function build_ipa_coreos_ramdisk {
echo "Building ironic-python-agent deploy ramdisk"
local kernel_path=$1
local ramdisk_path=$2
git_clone $IRONIC_PYTHON_AGENT_REPO $IRONIC_PYTHON_AGENT_DIR $IRONIC_PYTHON_AGENT_BRANCH
cd $IRONIC_PYTHON_AGENT_DIR
imagebuild/coreos/build_coreos_image.sh
cp imagebuild/coreos/UPLOAD/coreos_production_pxe_image-oem.cpio.gz $ramdisk_path
cp imagebuild/coreos/UPLOAD/coreos_production_pxe.vmlinuz $kernel_path
sudo rm -rf UPLOAD
cd -
}
# build deploy kernel+ramdisk, then upload them to glance
# this function sets ``IRONIC_DEPLOY_KERNEL_ID``, ``IRONIC_DEPLOY_RAMDISK_ID``
function upload_baremetal_ironic_deploy {
declare -g IRONIC_DEPLOY_KERNEL_ID IRONIC_DEPLOY_RAMDISK_ID
echo_summary "Creating and uploading baremetal images for ironic"
# install diskimage-builder
if [[ $(type -P ramdisk-image-create) == "" ]]; then
pip_install_gr "diskimage-builder"
fi
if [ -z "$IRONIC_DEPLOY_KERNEL" -o -z "$IRONIC_DEPLOY_RAMDISK" ]; then
local IRONIC_DEPLOY_KERNEL_PATH=$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.kernel
local IRONIC_DEPLOY_RAMDISK_PATH=$TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER.initramfs
else
local IRONIC_DEPLOY_KERNEL_PATH=$IRONIC_DEPLOY_KERNEL
local IRONIC_DEPLOY_RAMDISK_PATH=$IRONIC_DEPLOY_RAMDISK
fi
if [ ! -e "$IRONIC_DEPLOY_RAMDISK_PATH" -o ! -e "$IRONIC_DEPLOY_KERNEL_PATH" ]; then
# files don't exist, need to build them
if [ "$IRONIC_BUILD_DEPLOY_RAMDISK" = "True" ]; then
# we can build them only if we're not offline
if [ "$OFFLINE" != "True" ]; then
if is_deployed_with_ipa_ramdisk; then
build_ipa_coreos_ramdisk $IRONIC_DEPLOY_KERNEL_PATH $IRONIC_DEPLOY_RAMDISK_PATH
else
ramdisk-image-create $IRONIC_DEPLOY_FLAVOR \
-o $TOP_DIR/files/ir-deploy-$IRONIC_DEPLOY_DRIVER
fi
else
die $LINENO "Deploy kernel+ramdisk files don't exist and cannot be build in OFFLINE mode"
fi
else
if is_deployed_with_ipa_ramdisk; then
# download the agent image tarball
wget "$IRONIC_AGENT_KERNEL_URL" -O $IRONIC_DEPLOY_KERNEL_PATH
wget "$IRONIC_AGENT_RAMDISK_URL" -O $IRONIC_DEPLOY_RAMDISK_PATH
else
die $LINENO "Deploy kernel+ramdisk files don't exist and their building was disabled explicitly by IRONIC_BUILD_DEPLOY_RAMDISK"
fi
fi
fi
local token=$(openstack token issue -c id -f value)
die_if_not_set $LINENO token "Keystone fail to get token"
# load them into glance
IRONIC_DEPLOY_KERNEL_ID=$(openstack \
--os-token $token \
--os-url http://$GLANCE_HOSTPORT \
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 \
--os-url http://$GLANCE_HOSTPORT \
image create \
$(basename $IRONIC_DEPLOY_RAMDISK_PATH) \
--public --disk-format=ari \
--container-format=ari \
< $IRONIC_DEPLOY_RAMDISK_PATH | grep ' id ' | get_field 2)
}
function prepare_baremetal_basic_ops {
if ! is_ironic_hardware; then
configure_ironic_auxiliary
fi
upload_baremetal_ironic_deploy
if ! is_ironic_hardware; then
create_bridge_and_vms
fi
enroll_nodes
configure_tftpd
configure_iptables
}
function cleanup_baremetal_basic_ops {
rm -f $IRONIC_VM_MACS_CSV_FILE
if [ -f $IRONIC_KEY_FILE ]; then
local 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
local vm_name
for vm_name in $(_ironic_bm_vm_names); do
sudo su $STACK_USER -c "$IRONIC_SCRIPTS_DIR/cleanup-node $vm_name $IRONIC_VM_NETWORK_BRIDGE"
done
sudo rm -rf /etc/xinetd.d/tftp /etc/init/tftpd-hpa.override
restart_service xinetd
sudo iptables -D INPUT -d $HOST_IP -p udp --dport 69 -j ACCEPT || true
sudo iptables -D INPUT -d $HOST_IP -p tcp --dport $IRONIC_SERVICE_PORT -j ACCEPT || true
if is_deployed_by_agent; then
# agent ramdisk gets instance image from swift
sudo iptables -D INPUT -d $HOST_IP -p tcp --dport ${SWIFT_DEFAULT_BIND_PORT:-8080} -j ACCEPT || true
fi
sudo rmmod nf_conntrack_tftp || true
sudo rmmod nf_nat_tftp || true
}
# Restore xtrace + pipefail
$XTRACE
$PIPEFAIL
# Tell emacs to use shell-script-mode
## Local variables:
## mode: shell-script
## End: