248 lines
8.3 KiB
Bash

#!/usr/bin/env bash
# Copyright (C) 2015 Hewlett-Packard Development Company, L.P.
# Copyright (C) 2015 Pure Storage, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
#
# See the License for the specific language governing permissions and
# limitations under the License.
# Shell commands to get virsh the information it
# needs to successfully pass through a Fibre Channel PCI Card to the virtual
# machine this script is running on. The instance only knows its IP address,
# while its Virsh name is required for pass through. This script uses Nova on
# the provider blade as an intermediary to find the name. Meanwhile, this
# script finds the Fibre Channel PCI card on the provider and generates the
# information Virsh needs to attach it.
#
# Expect four env variables, the provider hostname (optionally user if needed)
# the private key file we should use to connect to the provider, and the file
# that should be sourced for OpenStack credentials.
#
# export FC_PROVIDER=my.provider.hostname
# export FC_PROVIDER_USER=root
# export FC_PROVIDER_KEY=/opt/nodepool-scripts/passthrough
# export FC_PROVIDER_RC=/root/keystonerc_jenkins
#
# The maximum number of FC devices to passthrough, failing if they cannot all be
# aquired
# export FC_NUM=2 (default 1)
#
# For single node setups where the hypervisor is the same as the provider, and dns
# is not configured, export this variable to use the provider ip as the hypervisor
# export FC_SINGLE_NODE=1
FC_NUM=${FC_NUM:-1}
FC_PCI_VAR_NAME=${FC_PCI_VAR_NAME:-"fc_pci_device"}
echo "Planning to passthrough $FC_NUM pci devices"
eth0_ip=$(hostname -I | cut -f1 -d' ')
PROVIDER=${FC_PROVIDER}
if [[ -z $PROVIDER ]]; then
eth0_ip_base=$(echo $eth0_ip | cut -f1,2,3 -d.)
PROVIDER="${eth0_ip_base}.1"
fi
PROVIDER_KEY=${FC_PROVIDER_KEY:-"/opt-nodepool-scripts/passthrough"}
PROVIDER_RC=${FC_PROVIDER_RC:-"keystonerc_jenkins"}
CURRENT_USER=$(whoami)
PROVIDER_USER=${FC_PROVIDER_USER:-$CURRENT_USER}
# Passthrough is a private key that needs to be setup for the provider
# and any compute nodes that might end up hosting the VM we want passthrough on.
# We will assume ownership of the key (probably as the jenkins user..), also
# assuming the group is the same name as the user...
sudo chown $CURRENT_USER:$CURRENT_USER $PROVIDER_KEY
chmod 0400 $PROVIDER_KEY
# Get our NOVA_ID
NOVA_LIST=$(ssh -i $PROVIDER_KEY $PROVIDER_USER@$PROVIDER "source $PROVIDER_RC && nova list")
nova_result=$?
NOVA_ID=$(echo "$NOVA_LIST" | grep ACTIVE | grep -v deleting | grep $eth0_ip | cut -d \| -f 2 | tr -d '[:space:]')
echo "NOVA_ID result: $nova_result"
if [[ $nova_result -ne 0 || -z "$NOVA_ID" ]]; then
echo "Unable to get Nova ID. Aborting. Debug info:"
echo $NOVA_LIST
echo "NOVA_ID: $NOVA_ID"
exit 2
fi
# Get instance details
NOVA_DETAILS=$(ssh -i $PROVIDER_KEY $PROVIDER_USER@$PROVIDER "source $PROVIDER_RC && nova show $NOVA_ID")
nova_results=$?
# Get our Virsh name
VIRSH_NAME=$(echo "$NOVA_DETAILS" | grep instance_name | cut -d \| -f 3 | tr -d '[:space:]')
virsh_result=$?
echo "VIRSH_NAME result: $virsh_result"
if [[ $nova_result -ne 0 || $virsh_result -ne 0 || -z "$VIRSH_NAME" ]]; then
echo "Unable to get Virsh Name. Aborting. Debug info:"
echo "NOVA_LIST:"
echo $NOVA_LIST
echo "NOVA_DETAILS:"
echo $NOVA_DETAILS
echo "VIRSH_NAME: $VIRSH_NAME"
exit 2
fi
# Get the hypervisor_hostname
if [[ -z $FC_SINGLE_NODE ]]; then
HYPERVISOR=$(echo "$NOVA_DETAILS" | grep hypervisor_hostname | cut -d \| -f 3 | tr -d '[:space:]')
hypervisor_result=$?
echo "HYPERVISOR result: $hypervisor_result"
if [[ $hypervisor_result -ne 0 || -z "$HYPERVISOR" ]]; then
echo "Unable to get Hypervisor Host Name. Aborting. Debug info:"
echo "NOVA_LIST:"
echo $NOVA_LIST
echo "NOVA_DETAILS:"
echo $NOVA_DETAILS
echo "HYPERVISOR: $HYPERVISOR"
exit 2
fi
else
HYPERVISOR=$PROVIDER
fi
echo "Found Hypervisor hostname: $HYPERVISOR"
fc_pci_device_cmd="echo \$$FC_PCI_VAR_NAME"
fc_pci_device=$(ssh -i $PROVIDER_KEY $PROVIDER_USER@$HYPERVISOR "source /etc/profile; $fc_pci_device_cmd")
if [[ -z $fc_pci_device ]]; then
echo "No FC device known. Set fc_pci_device in your /etc/profile.d or /etc/environment (depending on distro and ssh configuration) to the desired 'Class Device path', e.g. '0000:21:00.2'"
exit 2
fi
echo "Found pci devices: $fc_pci_device"
function is_device_online() {
fc_device=$1
# If a device is not "Online" we'll get an empty
# string as a result of the following command.
cmd="systool -c fc_host -v"
OUTPUT=$(ssh -i $PROVIDER_KEY $PROVIDER_USER@$HYPERVISOR "systool -c fc_host -v")
ONLINE=`echo "$OUTPUT" | grep -B12 'Online' | grep 'Class Device path' | grep $fc_device`
echo "online result='$ONLINE'"
if [ -z "$ONLINE" ]; then
return 0;
else
return 1;
fi
}
exit_code=1
errexit=$(set +o | grep errexit)
# Ignore errors
set +e
let num_attached=0
for pci in $fc_pci_device; do
echo "Trying passthrough for $pci"
BUS=$(echo $pci | cut -d : -f2)
SLOT=$(echo $pci | cut -d : -f3 | cut -d . -f1)
FUNCTION=$(echo $pci | cut -d : -f3 | cut -d . -f2)
XML="<hostdev mode='subsystem' type='pci' managed='yes'><source><address domain='0x0000' bus='0x$BUS' slot='0x$SLOT' function='0x$FUNCTION'/></source></hostdev>"
echo $XML
fcoe=`mktemp --suffix=_fcoe.xml`
echo $XML > $fcoe
fc_virsh_device="pci_0000_${BUS}_${SLOT}_${FUNCTION}"
scp -i $PROVIDER_KEY $fcoe $PROVIDER_USER@$HYPERVISOR:/tmp/
# Run passthrough and clean up.
# TODO: At the point where we can do more than one node on a provider we
# will need to do this cleanup at the end of the job and not *before* attaching
# since we won't know which ones are still in use
echo $(sudo lspci | grep -i fib)
ssh -i $PROVIDER_KEY $PROVIDER_USER@$HYPERVISOR "virsh nodedev-dettach $fc_virsh_device"
detach_result=$?
echo "Detach result: $detach_result"
if [[ $detach_result -ne 0 ]]; then
echo "Detach failed ($detach_result). Trying next device..."
continue
fi
# Reattach the device to the host.
# This will hopefully reset the device
echo $(sudo lspci | grep -i fib)
ssh -i $PROVIDER_KEY $PROVIDER_USER@$HYPERVISOR "virsh nodedev-reattach $fc_virsh_device"
reattach_result=$?
echo "reattach result: $reattach_result"
if [[ $reattach_result -ne 0 ]]; then
echo "Reattach failed ($reattach_result). Trying next device..."
continue
fi
# Now that the device has been re-attached to it's host device driver
# systool should be able to see it. Make sure it's online.
is_device_online $pci
online=$?
if [ $online -eq 1 ]; then
echo "Device($pci) is Online"
else
echo "Device($pci) is NOT Online"
# It does no good to passthrough an HBA that isn't Online.
# When an HBA goes into 'Linkdown' or 'Offline' mode, the
# host typically needs to get rebooted.
continue
fi
echo $(sudo lspci | grep -i fib)
ssh -i $PROVIDER_KEY $PROVIDER_USER@$HYPERVISOR "virsh attach-device $VIRSH_NAME $fcoe"
attach_result=$?
echo "Attach result: $attach_result"
if [[ $attach_result -eq 0 ]]; then
echo "Attached succeed. Trying next device..."
(( num_attached += 1 ))
exit_code=0
fi
echo $(sudo lspci | grep -i fib)
echo $num_attached
if [[ $num_attached -eq $FC_NUM ]]; then
echo "Attached $num_attached devices. Stopping"
break
fi
done
$errexit
if [[ $exit_code -ne 0 ]]; then
echo "FC Passthrough failed. Aborting."
exit $exit_code
fi
if [[ $num_attached -ne $FC_NUM ]]; then
echo "FC requested $FC_NUM, but only attached $num_attached. Aborting."
exit 1
fi
# Make sure that really it worked...
sudo modprobe lpfc
echo $?
sudo systool -c fc_host -v
echo $?
echo $(sudo lspci | grep -i fib)
device_path=$(sudo systool -c fc_host -v | grep "Device path")
if [[ ${#device_path} -eq 0 ]]; then
echo "Failed to install FC Drivers. Aborting."
exit 1
fi