#!/bin/bash set -eu set -o pipefail SCRIPT_NAME=$(basename $0) SCRIPT_HOME=$(dirname $0) BUILD_ONLY= DEBUG_LOGGING= HEAT_ENV= FLAVOR="baremetal" function show_options { echo "Usage: $SCRIPT_NAME [options]" echo echo "Deploys a baremetal cloud via heat." echo echo "Options:" echo " -h -- this help" echo " -c -- re-use existing source/images if they exist." echo " --build-only -- build the needed images but don't deploy them." echo " --debug-logging -- Turn on debug logging in the undercloud. Sets" echo " both OS_DEBUG_LOGGING and the heat Debug parameter." echo " --heat-env -- path to a JSON heat environment file." echo " Defaults to \$TRIPLEO_ROOT/undercloud-env.json." echo " --flavor -- flavor to use for the undercloud. Defaults" echo " to 'baremetal'." echo exit $1 } TEMP=$(getopt -o c,h -l build-only,debug-logging,heat-env:,flavor:,help -n $SCRIPT_NAME -- "$@") 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 -c) USE_CACHE=1; shift 1;; --build-only) BUILD_ONLY="1"; shift 1;; --debug-logging) DEBUG_LOGGING="1" export OS_DEBUG_LOGGING="1" shift 1 ;; --heat-env) HEAT_ENV="$2"; shift 2;; --flavor) FLAVOR="$2"; shift 2;; -h | --help) show_options 0;; --) shift ; break ;; *) echo "Error: unsupported option $1." ; exit 1 ;; esac done set -x USE_CACHE=${USE_CACHE:-0} TE_DATAFILE=${1:?"A test environment description is required as \$1."} UNDERCLOUD_DIB_EXTRA_ARGS=${UNDERCLOUD_DIB_EXTRA_ARGS:-'rabbitmq-server'} if [ "${USE_MARIADB:-}" = 1 ] ; then UNDERCLOUD_DIB_EXTRA_ARGS="$UNDERCLOUD_DIB_EXTRA_ARGS mariadb-rpm" fi ### --include ## devtest_undercloud ## ================== ## #. Specify whether to use the nova-baremetal or nova-ironic drivers ## for provisioning within the undercloud. ## :: if [ "$USE_IRONIC" -eq 0 ] ; then UNDERCLOUD_DIB_EXTRA_ARGS="$UNDERCLOUD_DIB_EXTRA_ARGS nova-baremetal" else UNDERCLOUD_DIB_EXTRA_ARGS="$UNDERCLOUD_DIB_EXTRA_ARGS nova-ironic" fi ## #. Add extra elements for Undercloud UI ## :: if [ "$USE_UNDERCLOUD_UI" -ne 0 ] ; then UNDERCLOUD_DIB_EXTRA_ARGS="$UNDERCLOUD_DIB_EXTRA_ARGS ceilometer-collector \ ceilometer-api ceilometer-agent-central ceilometer-agent-notification \ ceilometer-undercloud-config horizon" fi ## #. Specifiy a client-side timeout in minutes for creating or updating the ## undercloud Heat stack. ## :: UNDERCLOUD_STACK_TIMEOUT=${UNDERCLOUD_STACK_TIMEOUT:-60} ## #. Create your undercloud image. This is the image that the seed nova ## will deploy to become the baremetal undercloud. $UNDERCLOUD_DIB_EXTRA_ARGS is ## meant to be used to pass additional arguments to disk-image-create. ## :: NODE_ARCH=$(os-apply-config -m $TE_DATAFILE --key arch --type raw) if [ ! -e $TRIPLEO_ROOT/undercloud.qcow2 -o "$USE_CACHE" == "0" ] ; then #nodocs $TRIPLEO_ROOT/diskimage-builder/bin/disk-image-create $NODE_DIST \ -a $NODE_ARCH -o $TRIPLEO_ROOT/undercloud \ ntp baremetal boot-stack os-collect-config dhcp-all-interfaces \ neutron-dhcp-agent $DIB_COMMON_ELEMENTS $UNDERCLOUD_DIB_EXTRA_ARGS 2>&1 | \ tee $TRIPLEO_ROOT/dib-undercloud.log ### --end fi if [ -n "$BUILD_ONLY" ]; then exit 0 fi ### --include ## #. If you wanted to build the image and run it elsewhere, you can stop at ## this point and head onto the overcloud image building. ## #. Load the undercloud image into Glance: ## :: UNDERCLOUD_ID=$(load-image -d $TRIPLEO_ROOT/undercloud.qcow2) ## #. Set the public interface of the undercloud network node: ## :: NeutronPublicInterface=${NeutronPublicInterface:-'eth0'} ## #. Set the NTP server for the undercloud:: ## :: UNDERCLOUD_NTP_SERVER=${UNDERCLOUD_NTP_SERVER:-''} ## #. Create secrets for the cloud. The secrets will be written to a file ## ($TRIPLEO_ROOT/tripleo-undercloud-passwords by default) ## that you need to source into your shell environment. ## ## .. note:: ## ## You can also make or change these later and ## update the heat stack definition to inject them - as long as you also ## update the keystone recorded password. ## ## .. note:: ## ## There will be a window between updating keystone and ## instances where they will disagree and service will be down. Instead ## consider adding a new service account and changing everything across ## to it, then deleting the old account after the cluster is updated. ## ## :: ### --end # NOTE(tchaypo): We used to write these passwords in $CWD; so check to see if the # file exists there first. As well as providing backwards compatibility, this # allows for people to run multiple test environments on the same machine - just # make sure to have a different directory for running the scripts for each # different environment you wish to use. # if [ -e tripleo-undercloud-passwords ]; then echo "Re-using existing passwords in $PWD/tripleo-undercloud-passwords" # Add any new passwords since the file was generated setup-undercloud-passwords tripleo-undercloud-passwords source tripleo-undercloud-passwords else ### --include setup-undercloud-passwords $TRIPLEO_ROOT/tripleo-undercloud-passwords source $TRIPLEO_ROOT/tripleo-undercloud-passwords fi #nodocs ## #. Export UNDERCLOUD_CEILOMETER_SNMPD_PASSWORD to your environment ## so it can be applied to the SNMPd of all Overcloud nodes. NEW_JSON=$(jq '.undercloud.ceilometer_snmpd_password="'${UNDERCLOUD_CEILOMETER_SNMPD_PASSWORD}'"' $TE_DATAFILE) echo $NEW_JSON > $TE_DATAFILE ## #. Pull out needed variables from the test environment definition. ## :: POWER_MANAGER=$(os-apply-config -m $TE_DATAFILE --key power_manager --type raw) POWER_KEY=$(os-apply-config -m $TE_DATAFILE --key ssh-key --type raw) POWER_HOST=$(os-apply-config -m $TE_DATAFILE --key host-ip --type raw) POWER_USER=$(os-apply-config -m $TE_DATAFILE --key ssh-user --type raw) ## #. Wait for the BM cloud to register BM nodes with the scheduler:: wait_for -w 60 --delay 1 -- wait_for_hypervisor_stats ## #. We need an environment file to store the parameters we're going to give ## heat.:: HEAT_ENV=${HEAT_ENV:-"${TRIPLEO_ROOT}/undercloud-env.json"} ## #. Read the heat env in for updating.:: if [ -e "${HEAT_ENV}" ]; then ### --end if [ "$(stat -c %a ${HEAT_ENV})" != "600" ]; then echo "Error: Heat environment cache \"${HEAT_ENV}\" not set to permissions of 0600." # We should exit 1 so all the users from before the permissions # requirement dont have their HEAT_ENV files ignored in a nearly silent way exit 1 fi ### --include ENV_JSON=$(cat "${HEAT_ENV}") else ENV_JSON='{"parameters":{}}' fi ## #. Detect if we are deploying with a VLAN for API endpoints / floating IPs. ## This is done by looking for a 'public' network in Neutron, and if found ## we pull out the VLAN id and pass that into Heat, as well as using a VLAN ## enabled Heat template. ## :: if (neutron net-list | grep -q public); then VLAN_ID=$(neutron net-show public | awk '/provider:segmentation_id/ { print $4 }') else VLAN_ID= fi ## #. Nova-baremetal and Ironic require different Heat templates ## and different options. ## :: if [ "$USE_IRONIC" -eq 0 ] ; then if [ -n "$VLAN_ID" ]; then echo "VLANs not supported with Nova-BM" >&2 exit 1 fi HEAT_UNDERCLOUD_TEMPLATE="undercloud-vm.yaml" ENV_JSON=$(jq .parameters.PowerSSHHost=\"${POWER_HOST}\" <<< $ENV_JSON) ENV_JSON=$(jq .parameters.PowerManager=\"${POWER_MANAGER}\" <<< $ENV_JSON) ENV_JSON=$(jq .parameters.PowerUserName=\"${POWER_USER}\" <<< $ENV_JSON) REGISTER_SERVICE_OPTS="" else if [ -n "$VLAN_ID" ]; then HEAT_UNDERCLOUD_TEMPLATE="undercloud-vm-ironic-vlan.yaml" ENV_JSON=$(jq .parameters.NeutronPublicInterfaceTag=\"${VLAN_ID}\" <<< $ENV_JSON) # This should be in the heat template, but see # https://bugs.launchpad.net/heat/+bug/1336656 # note that this will break if there are more than one subnet, as if # more reason to fix the bug is needed :). PUBLIC_SUBNET_ID=$(neutron net-show public | awk '/subnets/ { print $4 }') VLAN_GW=$(neutron subnet-show $PUBLIC_SUBNET_ID | awk '/gateway_ip/ { print $4}') BM_VLAN_CIDR=$(neutron subnet-show $PUBLIC_SUBNET_ID | awk '/cidr/ { print $4}') ENV_JSON=$(jq .parameters.NeutronPublicInterfaceDefaultRoute=\"${VLAN_GW}\" <<< $ENV_JSON) else HEAT_UNDERCLOUD_TEMPLATE="undercloud-vm-ironic.yaml" fi ENV_JSON=$(jq .parameters.IronicPassword=\"${UNDERCLOUD_IRONIC_PASSWORD}\" <<< $ENV_JSON) REGISTER_SERVICE_OPTS="--ironic-password $UNDERCLOUD_IRONIC_PASSWORD" fi STACKNAME_UNDERCLOUD=${STACKNAME_UNDERCLOUD:-'undercloud'} ## #. Choose whether to deploy or update. Use stack-update to update:: ## HEAT_OP=stack-create ## :: if heat stack-show $STACKNAME_UNDERCLOUD > /dev/null; then HEAT_OP=stack-update if (heat stack-show $STACKNAME_UNDERCLOUD | grep -q FAILED); then echo "Updating a failed stack. this is a new ability and may cause problems." >&2 fi else HEAT_OP=stack-create fi ## #. Set parameters we need to deploy a baremetal undercloud:: ENV_JSON=$(jq '.parameters = { "MysqlInnodbBufferPoolSize": 100 } + .parameters + { "AdminPassword": "'"${UNDERCLOUD_ADMIN_PASSWORD}"'", "AdminToken": "'"${UNDERCLOUD_ADMIN_TOKEN}"'", "SnmpdReadonlyUserPassword": "'"${UNDERCLOUD_CEILOMETER_SNMPD_PASSWORD}"'", "GlancePassword": "'"${UNDERCLOUD_GLANCE_PASSWORD}"'", "HeatPassword": "'"${UNDERCLOUD_HEAT_PASSWORD}"'", "NovaPassword": "'"${UNDERCLOUD_NOVA_PASSWORD}"'", "NeutronPassword": "'"${UNDERCLOUD_NEUTRON_PASSWORD}"'", "NeutronPublicInterface": "'"${NeutronPublicInterface}"'", "undercloudImage": "'"${UNDERCLOUD_ID}"'", "BaremetalArch": "'"${NODE_ARCH}"'", "PowerSSHPrivateKey": "'"${POWER_KEY}"'", "NtpServer": "'"${UNDERCLOUD_NTP_SERVER}"'", "Flavor": "'"${FLAVOR}"'" }' <<< $ENV_JSON) ### --end if [ "$DEBUG_LOGGING" = "1" ]; then ENV_JSON=$(jq '.parameters = .parameters + { "Debug": "True", }' <<< $ENV_JSON) fi ### --include #Add Ceilometer to env only if USE_UNDERCLOUD_UI is specified if [ "$USE_UNDERCLOUD_UI" -ne 0 ] ; then ENV_JSON=$(jq '.parameters = .parameters + { "CeilometerPassword": "'"${UNDERCLOUD_CEILOMETER_PASSWORD}"'" }' <<< $ENV_JSON) fi ## #. Save the finished environment file.:: jq . > "${HEAT_ENV}" <<< $ENV_JSON chmod 0600 "${HEAT_ENV}" ## #. Add Keystone certs/key into the environment file.:: generate-keystone-pki --heatenv $HEAT_ENV ## #. Deploy an undercloud. ## :: make -C $TRIPLEO_ROOT/tripleo-heat-templates $HEAT_UNDERCLOUD_TEMPLATE heat $HEAT_OP -e $HEAT_ENV \ -t 360 \ -f $TRIPLEO_ROOT/tripleo-heat-templates/$HEAT_UNDERCLOUD_TEMPLATE \ $STACKNAME_UNDERCLOUD ## You can watch the console via ``virsh``/``virt-manager`` to observe the PXE ## boot/deploy process. After the deploy is complete, it will reboot into the ## image. ## ## #. Get the undercloud IP from ``nova list`` ## :: echo "Waiting for the undercloud stack to be ready" #nodocs # Make time out 60 mins as like the Heat stack-create default timeout. wait_for_stack_ready -w $(($UNDERCLOUD_STACK_TIMEOUT * 60 )) 10 undercloud UNDERCLOUD_CTL_IP=$(nova list | grep ctlplane | sed -e "s/.*=\\([0-9.]*\\).*/\1/") ## #. If we're deploying with a public VLAN we must use it, not the control plane ## network (which we may not even have access to) to ping and configure thing. ## :: if [ -n "$VLAN_ID" ]; then UNDERCLOUD_IP=$(heat output-show undercloud PublicIP|sed 's/^"\(.*\)"$/\1/') else UNDERCLOUD_IP=$UNDERCLOUD_CTL_IP fi ## #. We don't (yet) preserve ssh keys on rebuilds. ## :: ssh-keygen -R $UNDERCLOUD_IP ssh-keygen -R $UNDERCLOUD_CTL_IP ## #. Exclude the undercloud from proxies: ## :: set +u #nodocs export no_proxy=$no_proxy,$UNDERCLOUD_IP set -u #nodocs ## #. Export the undercloud endpoint and credentials to your test environment. ## :: UNDERCLOUD_ENDPOINT="http://$UNDERCLOUD_IP:5000/v2.0" NEW_JSON=$(jq '.undercloud.password="'${UNDERCLOUD_ADMIN_PASSWORD}'" | .undercloud.endpoint="'${UNDERCLOUD_ENDPOINT}'" | .undercloud.endpointhost="'${UNDERCLOUD_IP}'"' $TE_DATAFILE) echo $NEW_JSON > $TE_DATAFILE ## #. Source the undercloud configuration: ## :: source $TRIPLEO_ROOT/tripleo-incubator/undercloudrc ## #. Perform setup of your undercloud. ## :: init-keystone -o $UNDERCLOUD_CTL_IP -t $UNDERCLOUD_ADMIN_TOKEN \ -e admin@example.com -p $UNDERCLOUD_ADMIN_PASSWORD \ --public $UNDERCLOUD_IP --no-pki-setup # Creating these roles to be used by tenants using swift keystone role-create --name=swiftoperator keystone role-create --name=ResellerAdmin # Create service endpoints and optionally include Ceilometer for UI support ENDPOINT_LIST="--glance-password $UNDERCLOUD_GLANCE_PASSWORD --heat-password $UNDERCLOUD_HEAT_PASSWORD --neutron-password $UNDERCLOUD_NEUTRON_PASSWORD --nova-password $UNDERCLOUD_NOVA_PASSWORD --tuskar-password $UNDERCLOUD_TUSKAR_PASSWORD" if [ "$USE_UNDERCLOUD_UI" -ne 0 ] ; then ENDPOINT_LIST="$ENDPOINT_LIST --ceilometer-password $UNDERCLOUD_CEILOMETER_PASSWORD" fi setup-endpoints $UNDERCLOUD_CTL_IP $ENDPOINT_LIST $REGISTER_SERVICE_OPTS \ --public $UNDERCLOUD_IP keystone role-create --name heat_stack_user user-config BM_NETWORK_CIDR=$(os-apply-config -m $TE_DATAFILE --key baremetal-network.cidr --type raw --key-default '192.0.2.0/24') if [ -n "$VLAN_ID" ]; then # No ctl plane gateway - public net gateway is needed. # XXX (lifeless) - Neutron still configures one, first position in the subnet. BM_NETWORK_GATEWAY= else # Use a control plane gateway. BM_NETWORK_GATEWAY=$(os-apply-config -m $TE_DATAFILE --key baremetal-network.gateway-ip --type raw --key-default '192.0.2.1') fi BM_NETWORK_UNDERCLOUD_RANGE_START=$(os-apply-config -m $TE_DATAFILE --key baremetal-network.undercloud.range-start --type raw --key-default '192.0.2.21') BM_NETWORK_UNDERCLOUD_RANGE_END=$(os-apply-config -m $TE_DATAFILE --key baremetal-network.undercloud.range-end --type raw --key-default '192.0.2.40') UNDERCLOUD_NAMESERVER=$(os-apply-config -m $TE_DATAFILE --key undercloud.nameserver --type netaddress --key-default "${UNDERCLOUD_NAMESERVER:-}") NETWORK_JSON=$(mktemp) jq "." < $NETWORK_JSON { "physical": { "gateway": "$BM_NETWORK_GATEWAY", "metadata_server": "$UNDERCLOUD_CTL_IP", "cidr": "$BM_NETWORK_CIDR", "allocation_start": "$BM_NETWORK_UNDERCLOUD_RANGE_START", "allocation_end": "$BM_NETWORK_UNDERCLOUD_RANGE_END", "name": "ctlplane", "nameserver": "$UNDERCLOUD_NAMESERVER" } } EOF setup-neutron -n $NETWORK_JSON rm $NETWORK_JSON if [ -n "$VLAN_ID" ]; then BM_VLAN_START=$(jq -r '.["baremetal-network"].undercloud.public_vlan.start' $TE_DATAFILE) BM_VLAN_END=$(jq -r '.["baremetal-network"].undercloud.public_vlan.finish' $TE_DATAFILE) PUBLIC_NETWORK_JSON=$(mktemp) jq "." < $PUBLIC_NETWORK_JSON { "physical": { "gateway": "$VLAN_GW", "metadata_server": "$UNDERCLOUD_CTL_IP", "cidr": "$BM_VLAN_CIDR", "allocation_start": "$BM_VLAN_START", "allocation_end": "$BM_VLAN_END", "name": "public", "nameserver": "$UNDERCLOUD_NAMESERVER", "segmentation_id": "$VLAN_ID", "physical_network": "ctlplane", "enable_dhcp": false } } EOF setup-neutron -n $PUBLIC_NETWORK_JSON fi ## #. Nova quota runs up with the defaults quota so overide the default to ## allow unlimited cores, instances and ram. ## :: nova quota-update --cores -1 --instances -1 --ram -1 $(keystone tenant-get admin | awk '$2=="id" {print $4}') ## #. Register two baremetal nodes with your undercloud. ## :: setup-baremetal --service-host undercloud --nodes <(jq '.nodes - [.nodes[0]]' $TE_DATAFILE) ### --end