2018-08-21 23:08:30 +00:00
#!/bin/bash
2022-12-07 08:08:29 +00:00
######################################################
# Log
######################################################
2022-05-06 12:45:03 +00:00
export RED = '\x1b[0;31m'
export GREEN = '\x1b[38;5;22m'
export CYAN = '\x1b[36m'
export YELLOW = '\x1b[33m'
export NO_COLOR = '\x1b[0m'
if [ -z " ${ LOG_TITLE } " ] ; then
LOG_TITLE = ''
fi
if [ -z " ${ LOG_LEVEL } " ] ; then
LOG_LEVEL = "INFO"
fi
debug( ) {
if [ [ " ${ LOG_LEVEL } " = = "DEBUG" ] ] ; then
local log_title
if [ -n " ${ LOG_TITLE } " ] ; then
log_title = " ( ${ LOG_TITLE } ) "
else
log_title = ''
fi
echo -e " ${ GREEN } [DEBUG] ${ log_title } ${ NO_COLOR } $1 "
fi
}
info( ) {
if [ [ " ${ LOG_LEVEL } " = = "DEBUG" ] ] || \
[ [ " ${ LOG_LEVEL } " = = "INFO" ] ] ; then
local log_title
if [ -n " ${ LOG_TITLE } " ] ; then
log_title = " ( ${ LOG_TITLE } ) "
else
log_title = ''
fi
echo -e " ${ CYAN } [INFO] ${ log_title } ${ NO_COLOR } $1 "
fi
}
warn( ) {
if [ [ " ${ LOG_LEVEL } " = = "DEBUG" ] ] || \
[ [ " ${ LOG_LEVEL } " = = "INFO" ] ] || \
[ [ " ${ LOG_LEVEL } " = = "WARN" ] ] ; then
local log_title
if [ -n " ${ LOG_TITLE } " ] ; then
log_title = " ( ${ LOG_TITLE } ) "
else
log_title = ''
fi
echo -e " ${ YELLOW } [WARN] ${ log_title } ${ NO_COLOR } $1 "
fi
}
error( ) {
if [ [ " ${ LOG_LEVEL } " = = "DEBUG" ] ] || \
[ [ " ${ LOG_LEVEL } " = = "INFO" ] ] || \
[ [ " ${ LOG_LEVEL } " = = "WARN" ] ] || \
[ [ " ${ LOG_LEVEL } " = = "ERROR" ] ] ; then
local log_title
if [ -n " ${ LOG_TITLE } " ] ; then
log_title = " ( ${ LOG_TITLE } ) "
else
log_title = ''
fi
echo -e " ${ RED } [ERROR] ${ log_title } ${ NO_COLOR } $1 "
fi
}
2022-12-07 08:08:29 +00:00
######################################################
# Check logics
######################################################
2022-05-06 12:45:03 +00:00
set_packages_and_check_cmd( )
{
case $OS in
*"debian" * | *"ubuntu" * )
CHECK_CMD = 'dpkg -l | grep -w'
PACKAGES = ( nfs-common open-iscsi)
; ;
*"centos" * | *"fedora" * | *"rocky" * | *"ol" * )
CHECK_CMD = 'rpm -q'
PACKAGES = ( nfs-utils iscsi-initiator-utils)
; ;
*"suse" * )
CHECK_CMD = 'rpm -q'
PACKAGES = ( nfs-client open-iscsi)
; ;
2022-05-07 16:35:19 +00:00
*"arch" * )
CHECK_CMD = 'pacman -Q'
PACKAGES = ( nfs-utils open-iscsi)
; ;
2022-07-29 04:50:59 +00:00
*"gentoo" * )
CHECK_CMD = 'qlist -I'
PACKAGES = ( net-fs/nfs-utils sys-block/open-iscsi)
; ;
2022-05-06 12:45:03 +00:00
*)
CHECK_CMD = ''
PACKAGES = ( )
warn " Stop the environment check because ' $OS ' is not supported in the environment check script. "
exit 1
; ;
esac
}
2023-02-07 06:58:24 +00:00
detect_node_kernel_release( )
{
local pod = " $1 "
KERNEL_RELEASE = $( kubectl exec -i $pod -- nsenter --mount= /proc/1/ns/mnt -- bash -c 'uname -r' )
echo " $KERNEL_RELEASE "
}
2022-12-07 08:08:29 +00:00
detect_node_os( )
{
local pod = " $1 "
2023-01-23 22:40:17 +00:00
OS = $( kubectl exec -i $pod -- nsenter --mount= /proc/1/ns/mnt -- bash -c 'grep -E "^ID_LIKE=" /etc/os-release | cut -d= -f2' )
2022-12-07 08:08:29 +00:00
if [ [ -z " ${ OS } " ] ] ; then
2023-01-23 22:40:17 +00:00
OS = $( kubectl exec -i $pod -- nsenter --mount= /proc/1/ns/mnt -- bash -c 'grep -E "^ID=" /etc/os-release | cut -d= -f2' )
2022-12-07 08:08:29 +00:00
fi
echo " $OS "
}
check_local_dependencies( ) {
2018-08-21 23:08:30 +00:00
local targets = ( $@ )
2022-05-06 12:45:03 +00:00
2022-12-07 08:08:29 +00:00
local all_found = true
2018-08-21 23:08:30 +00:00
for ( ( i = 0; i<${# targets [@] } ; i++) ) ; do
local target = ${ targets [ $i ] }
if [ " $( which $target ) " = = "" ] ; then
2022-12-07 08:08:29 +00:00
all_found = false
2022-05-06 12:45:03 +00:00
error " Not found: $target "
2018-08-21 23:08:30 +00:00
fi
done
2022-12-07 08:08:29 +00:00
if [ " $all_found " = = "false" ] ; then
msg = " Please install missing dependencies: ${ targets [@] } . "
info " $msg "
2018-08-21 23:08:30 +00:00
exit 2
fi
2022-12-07 08:08:29 +00:00
msg = " Required dependencies ' ${ targets [@] } ' are installed. "
info " $msg "
2018-08-21 23:08:30 +00:00
}
create_ds( ) {
cat <<EOF > $TE MP_DIR/environment_check.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: longhorn-environment-check
name: longhorn-environment-check
spec:
selector:
matchLabels:
app: longhorn-environment-check
template:
metadata:
labels:
app: longhorn-environment-check
spec:
2022-05-06 12:45:03 +00:00
hostPID: true
2018-08-21 23:08:30 +00:00
containers:
- name: longhorn-environment-check
2022-05-06 12:45:03 +00:00
image: alpine:3.12
2018-08-21 23:08:30 +00:00
args: [ "/bin/sh" , "-c" , "sleep 1000000000" ]
volumeMounts:
- name: mountpoint
2018-08-22 03:10:20 +00:00
mountPath: /tmp/longhorn-environment-check
2018-08-21 23:08:30 +00:00
mountPropagation: Bidirectional
securityContext:
privileged: true
volumes:
- name: mountpoint
hostPath:
2018-08-22 03:10:20 +00:00
path: /tmp/longhorn-environment-check
2018-08-21 23:08:30 +00:00
EOF
2022-05-06 12:45:03 +00:00
kubectl create -f $TEMP_DIR /environment_check.yaml > /dev/null
2018-08-21 23:08:30 +00:00
}
cleanup( ) {
2022-05-06 12:45:03 +00:00
info "Cleaning up longhorn-environment-check pods..."
kubectl delete -f $TEMP_DIR /environment_check.yaml > /dev/null
2018-08-21 23:08:30 +00:00
rm -rf $TEMP_DIR
2022-05-06 12:45:03 +00:00
info "Cleanup completed."
2018-08-22 03:10:20 +00:00
}
2018-08-21 23:08:30 +00:00
wait_ds_ready( ) {
while true; do
local ds = $( kubectl get ds/longhorn-environment-check -o json)
local numberReady = $( echo $ds | jq .status.numberReady)
local desiredNumberScheduled = $( echo $ds | jq .status.desiredNumberScheduled)
if [ " $desiredNumberScheduled " = = " $numberReady " ] && [ " $desiredNumberScheduled " != "0" ] ; then
2022-05-06 12:45:03 +00:00
info " All longhorn-environment-check pods are ready ( $numberReady / $desiredNumberScheduled ). "
2018-08-21 23:08:30 +00:00
return
fi
2022-05-06 12:45:03 +00:00
info " Waiting for longhorn-environment-check pods to become ready ( $numberReady / $desiredNumberScheduled )... "
2018-08-21 23:08:30 +00:00
sleep 3
done
}
2022-05-06 12:45:03 +00:00
check_mount_propagation( ) {
2018-08-21 23:08:30 +00:00
local allSupported = true
local pods = $( kubectl -l app = longhorn-environment-check get po -o json)
2018-08-23 21:22:08 +00:00
2021-06-03 16:07:26 +00:00
local ds = $( kubectl get ds/longhorn-environment-check -o json)
local desiredNumberScheduled = $( echo $ds | jq .status.desiredNumberScheduled)
for ( ( i = 0; i<desiredNumberScheduled; i++) ) ; do
2018-08-21 23:08:30 +00:00
local pod = $( echo $pods | jq .items[ $i ] )
local nodeName = $( echo $pod | jq -r .spec.nodeName)
local mountPropagation = $( echo $pod | jq -r '.spec.containers[0].volumeMounts[] | select(.name=="mountpoint") | .mountPropagation' )
if [ " $mountPropagation " != "Bidirectional" ] ; then
allSupported = false
2022-05-06 12:45:03 +00:00
error " node $nodeName : MountPropagation is disabled "
2018-08-21 23:08:30 +00:00
fi
done
if [ " $allSupported " != "true" ] ; then
2022-05-06 12:45:03 +00:00
error "MountPropagation is disabled on at least one node. As a result, CSI driver and Base image cannot be supported."
2018-08-21 23:08:30 +00:00
exit 1
else
2022-05-06 12:45:03 +00:00
info "MountPropagation is enabled."
2018-08-21 23:08:30 +00:00
fi
}
2022-05-06 12:45:03 +00:00
check_package_installed( ) {
2022-12-07 08:08:29 +00:00
local pods = $( kubectl get pods -o name -l app = longhorn-environment-check)
2022-05-06 12:45:03 +00:00
2022-12-07 08:08:29 +00:00
local all_found = true
2022-05-06 12:45:03 +00:00
for pod in ${ pods } ; do
2023-01-23 22:40:17 +00:00
OS = $( detect_node_os $pod )
2022-05-06 12:45:03 +00:00
if [ x" $OS " = = x"" ] ; then
error " Unable to detect OS on node $node . "
exit 2
fi
set_packages_and_check_cmd " $OS "
for ( ( i = 0; i<${# PACKAGES [@] } ; i++) ) ; do
local package = ${ PACKAGES [ $i ] }
2022-07-12 11:55:32 +00:00
kubectl exec -i $pod -- nsenter --mount= /proc/1/ns/mnt -- timeout 30 bash -c " $CHECK_CMD $package " > /dev/null 2>& 1
2022-05-06 12:45:03 +00:00
if [ $? != 0 ] ; then
2022-12-07 08:08:29 +00:00
all_found = false
2023-01-23 22:40:17 +00:00
node = $( kubectl get ${ pod } --no-headers -o= custom-columns= :.spec.nodeName)
2022-05-06 12:45:03 +00:00
error " $package is not found in $node . "
fi
done
done
2022-12-07 08:08:29 +00:00
if [ " $all_found " = = "false" ] ; then
2022-05-06 12:45:03 +00:00
error "Please install missing packages."
exit 2
fi
2022-12-07 08:08:29 +00:00
info "Required packages are installed."
}
check_hostname_uniqueness( ) {
hostnames = $( kubectl get nodes -o jsonpath = '{.items[*].status.addresses[?(@.type=="Hostname")].address}' )
declare -A deduplicate_hostnames
num_nodes = 0
for hostname in ${ hostnames } ; do
num_nodes = $(( num_nodes+1))
deduplicate_hostnames[ " ${ hostname } " ] = " ${ hostname } "
done
if [ " ${# deduplicate_hostnames [@] } " != " ${ num_nodes } " ] ; then
error "Nodes do not have unique hostnames."
exit 2
fi
info "Hostname uniqueness check is passed."
2022-05-06 12:45:03 +00:00
}
check_multipathd( ) {
2022-12-07 08:08:29 +00:00
local pods = $( kubectl get pods -o name -l app = longhorn-environment-check)
local all_not_found = true
2022-05-06 12:45:03 +00:00
for pod in ${ pods } ; do
kubectl exec -t $pod -- nsenter --mount= /proc/1/ns/mnt -- bash -c "systemctl status --no-pager multipathd.service" > /dev/null 2>& 1
if [ $? = 0 ] ; then
2022-12-07 08:08:29 +00:00
all_not_found = false
2023-01-23 22:40:17 +00:00
node = $( kubectl get ${ pod } --no-headers -o= custom-columns= :.spec.nodeName)
2022-05-06 12:45:03 +00:00
warn " multipathd is running on $node . "
fi
done
2022-12-07 08:08:29 +00:00
if [ " $all_not_found " = = "false" ] ; then
2022-05-06 12:45:03 +00:00
warn "multipathd would probably result in the Longhorn volume mount failure. Please refer to https://longhorn.io/kb/troubleshooting-volume-with-multipath for more information."
fi
}
2023-10-06 23:59:18 +00:00
verlte( ) {
printf '%s\n' " $1 " " $2 " | sort -C -V
}
verlt( ) {
! verlte " $2 " " $1 "
}
check_kernel_release( ) {
local pods = $( kubectl get pods -o name -l app = longhorn-environment-check)
recommended_kernel_release = "5.8"
for pod in ${ pods } ; do
local kernel = $( detect_node_kernel_release ${ pod } )
if verlt " $kernel " " $recommended_kernel_release " ; then
local node = $( kubectl get ${ pod } --no-headers -o= custom-columns= :.spec.nodeName)
warn " Node $node has outdated kernel release: $kernel . Recommending kernel release >= $recommended_kernel_release "
fi
done
}
2022-05-06 12:45:03 +00:00
check_iscsid( ) {
2022-12-07 08:08:29 +00:00
local pods = $( kubectl get pods -o name -l app = longhorn-environment-check)
local all_found = true
2022-05-06 12:45:03 +00:00
for pod in ${ pods } ; do
kubectl exec -t $pod -- nsenter --mount= /proc/1/ns/mnt -- bash -c "systemctl status --no-pager iscsid.service" > /dev/null 2>& 1
if [ $? != 0 ] ; then
2022-12-07 08:08:29 +00:00
all_found = false
2023-01-23 22:40:17 +00:00
node = $( kubectl get ${ pod } --no-headers -o= custom-columns= :.spec.nodeName)
2022-05-06 12:45:03 +00:00
error " iscsid is not running on $node . "
fi
done
2022-12-07 08:08:29 +00:00
if [ " $all_found " = = "false" ] ; then
2022-05-06 12:45:03 +00:00
exit 2
fi
}
2022-12-26 04:56:14 +00:00
check_nfs_client_kernel_support( ) {
local pods = $( kubectl get pods -o name -l app = longhorn-environment-check)
local all_found = true
local nfs_client_kernel_configs = ( "CONFIG_NFS_V4_1" "CONFIG_NFS_V4_2" )
for config in " ${ nfs_client_kernel_configs [@] } " ; do
2022-12-26 11:29:26 +00:00
declare -A nodes = ( )
2022-12-26 04:56:14 +00:00
for pod in ${ pods } ; do
2023-02-07 06:58:24 +00:00
local kernel_release = $( detect_node_kernel_release $pod )
if [ x" $kernel_release " = = x"" ] ; then
error " Unable to detect kernel release on node $node . "
exit 2
fi
2023-01-23 22:40:17 +00:00
node = $( kubectl get ${ pod } --no-headers -o= custom-columns= :.spec.nodeName)
2023-02-07 06:58:24 +00:00
res = $( kubectl exec -t $pod -- nsenter --mount= /proc/1/ns/mnt -- bash -c " grep -E \"^# ${ config } is not set\" /boot/config- ${ kernel_release } " > /dev/null 2>& 1)
2022-12-26 04:56:14 +00:00
if [ [ $? = = 0 ] ] ; then
all_found = false
nodes[ " ${ node } " ] = " ${ node } "
else
2023-02-07 06:58:24 +00:00
res = $( kubectl exec -t $pod -- nsenter --mount= /proc/1/ns/mnt -- bash -c " grep -E \"^ ${ config } =\" /boot/config- ${ kernel_release } " > /dev/null 2>& 1)
2022-12-26 04:56:14 +00:00
if [ [ $? != 0 ] ] ; then
all_found = false
2022-12-26 11:29:26 +00:00
warn " Unable to check kernel config ${ config } on node ${ node } "
2022-12-26 04:56:14 +00:00
fi
fi
done
if [ ${# nodes [@] } != 0 ] ; then
warn "" ${ config } " kernel config is not enabled on nodes ${ nodes [*] } . "
fi
done
if [ [ ${ all_found } = = false ] ] ; then
warn " NFS client kernel support, ${ nfs_client_kernel_configs [*] } , is not enabled on Longhorn nodes. Please refer to https://longhorn.io/docs/1.4.0/deploy/install/#installing-nfsv4-client for more information. "
fi
}
2022-12-07 08:08:29 +00:00
######################################################
# Main logics
######################################################
2023-10-06 23:59:18 +00:00
DEPENDENCIES = ( "kubectl" "jq" "mktemp" "sort" "printf" )
2022-12-07 08:08:29 +00:00
check_local_dependencies " ${ DEPENDENCIES [@] } "
# Check the each host has a unique hostname (for RWX volume)
check_hostname_uniqueness
2022-05-06 12:45:03 +00:00
2022-12-07 08:08:29 +00:00
# Create a daemonset for checking the requirements in each node
2018-08-21 23:08:30 +00:00
TEMP_DIR = $( mktemp -d)
2022-05-06 12:45:03 +00:00
2018-08-21 23:08:30 +00:00
trap cleanup EXIT
create_ds
wait_ds_ready
2022-12-07 08:08:29 +00:00
2022-12-26 04:56:14 +00:00
check_nfs_client_kernel_support
2022-05-06 12:45:03 +00:00
check_package_installed
check_iscsid
check_multipathd
check_mount_propagation
2023-10-06 23:59:18 +00:00
check_kernel_release
2022-05-06 12:45:03 +00:00
2018-08-21 23:08:30 +00:00
exit 0