#!/bin/bash # Brett Kelly Jun/21 # 45Drives # Version 1.0 stable usage() { # Help cat << EOF Usage: [-b] Block DB size to expand into. Optional [-d] DB device to expand. Required. Comma separated list of block devices. [-h] Displays this message EOF exit 0 } check_dependancies(){ for i in "${!SCRIPT_DEPENDANCIES[@]}"; do if ! command -v ${SCRIPT_DEPENDANCIES[i]} >/dev/null 2>&1;then echo "cli utility: ${SCRIPT_DEPENDANCIES[i]} is not installed" echo "jq, and bc are required" exit 1 fi done } set -e ECHO_IF_DRYRUN="" SCRIPT_DEPENDANCIES=(bc jq) PHYSICAL_EXTENT_SIZE_BYTES=4194304 AUTO_MODE="true" while getopts 'b:d:Dh' OPTION; do case ${OPTION} in b) NEW_DB_SIZE=${OPTARG} NEW_DB_SIZE_BYTES=$(numfmt --from=iec $NEW_DB_SIZE) AUTO_MODE="false" ;; d) _DB_DEVICE=${OPTARG} IFS=',' read -r -a DB_DEVICE <<< "$_DB_DEVICE" ;; D) ECHO_IF_DRYRUN="echo" ;; h) usage ;; esac done if [ -z $DB_DEVICE ]; then echo "Input required. See ./`basename "$0"` -h for usage details" exit 1 fi # Check cli depandancies check_dependancies for device in ${DB_DEVICE[@]};do if [ ! -b $device ];then echo "Error: DB_DEVICE=$device is not a block device" continue fi ### VERIFY THAT DEVICE IS A DEDICATED DB DEVICE # If there are no lv's present then skip # If there are lvs and the lv name formats are not "osd-db-*" then skip # We are looking for dedicated db devices deployed with either ceph-volume or add-db-to-osd.sh # A dedicated db deployed with either tool will have only 1 volume group with the name ceph-$(uuid) # skip device if no vg present # skip device if more than 1 vg present # skip device if vg does not have name syntax matching ceph-$(uuidgen) DB_VG_COUNT=$(pvs --noheadings --reportformat json $device -o vg_name | jq -r '.report | .[].pv | length') if [ "$DB_VG_COUNT" -eq 0 ];then echo "Warning: $device is has no volume groups present, skipping" continue elif [ "$DB_VG_COUNT" -gt 1 ];then echo "Warning: $device has more than one volume group present, skipping" continue fi DB_VG_NAME=$(pvs --noheadings --reportformat json $device -o vg_name | jq -r '.report | .[].pv | .[0].vg_name') if echo $DB_VG_NAME | grep -vqE "ceph-[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}" ;then echo "Warning: $device has unknown vg present, skipping" continue fi DB_VG_PHYSICAL_EXTENT=$(pvs --noheadings --reportformat json $device -o vg_extent_size --units B --nosuffix | jq -r '.report | .[].pv | .[0].vg_extent_size') if [ "$DB_VG_PHYSICAL_EXTENT" -ne "$PHYSICAL_EXTENT_SIZE_BYTES" ];then echo "Warning: Physical Extent size is not the default 4MiB, skipping" continue fi DB_DEVICE_LV_JSON=$(vgs --noheadings --reportformat json -o lv_name,vg_name $DB_VG_NAME) DB_LV_COUNT=$(echo $DB_DEVICE_LV_JSON | jq '.report | .[].vg | length') if [ "$DB_LV_COUNT" -eq 0 ];then echo "Warning: $device is has no Logical Volumes present, skipping" continue else # If there are lvs and the lv name formats are not "osd-db-$(uuidgen)" then skip i=0 while [ $i -lt $DB_LV_COUNT ]; do name=$(echo $DB_DEVICE_LV_JSON | jq -r --arg index $i '.[] | .[].lv | .[$index |tonumber].lv_name') if echo $name | grep -qE "osd-db-[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}" ;then echo "Warning: $device has non db lvs present and therefore is not a dedicated db device, skipping" continue 2 fi let i=i+1 done fi ### DB_DEVICE_PV_JSON=$(pvs --noheadings --reportformat json $device -o pv_pe_count,pv_pe_alloc_count) TOTAL_PHYSICAL_EXTENTS=$(echo $DB_DEVICE_PV_JSON | jq -r '.report | .[].pv | .[].pv_pe_count') ALLOCATED_PHYSICAL_EXTENTS=$(echo $DB_DEVICE_PV_JSON | jq -r '.report | .[].pv | .[].pv_pe_alloc_count') if $AUTO_MODE ;then echo "MODE: Use Maximum Allowed Space" echo "DB_DEVICE: $device" NEW_DB_SIZE_EXTENTS=$(bc <<< "$TOTAL_PHYSICAL_EXTENTS/$DB_LV_COUNT") NEW_DB_SIZE_BYTES=$(bc <<< "$PHYSICAL_EXTENT_SIZE_BYTES*$NEW_DB_SIZE_EXTENTS") echo "NEW_DB_SIZE_BYTES: $(numfmt --to=iec $NEW_DB_SIZE_BYTES)" echo "NEW_DB_SIZE_EXTENTS: $NEW_DB_SIZE_EXTENTS" else echo "MODE: Use User Specified DB SIZE" echo "DB_DEVICE: $device" NEW_DB_SIZE_EXTENTS=$(bc <<< "$NEW_DB_SIZE_BYTES/$PHYSICAL_EXTENT_SIZE_BYTES") TOTAL_PHYSICAL_BYTES=$(bc <<< "$TOTAL_PHYSICAL_EXTENTS*$PHYSICAL_EXTENT_SIZE_BYTES") NEW_TOTAL_DB_SIZE_EXTENTS=$(bc <<< "$NEW_DB_SIZE_EXTENTS*$DB_LV_COUNT") echo "NEW_DB_SIZE_BYTES: $NEW_DB_SIZE_BYTES" echo "NEW_DB_SIZE_EXTENTS: $NEW_DB_SIZE_EXTENTS" #NEW_TOTAL_DB_SIZE_EXTENTS cant be greater than TOTAL_PHYSICAL_EXTENTS if [ $NEW_TOTAL_DB_SIZE_EXTENTS -gt $TOTAL_PHYSICAL_EXTENTS ];then echo "Warning: New total DB size ($(numfmt --to=iec $NEW_TOTAL_DB_SIZE_BYTES)) exceeds the total available space on the DB device ($(numfmt --to=iec $TOTAL_PHYSICAL_BYTES))" continue fi fi # Make sure ceph admin keyring is present hs correct permission # Remove "set -e" so we can check ceph status error code # Then turn it back on after ceph status > /dev/null 2>&1 ; rc=$? if [[ "$rc" -ne 0 ]];then echo "Warning: permisson denied accessing cluster, admin keyring must be present" exit 1 fi CEPH_MAJOR_VERSION=$(ceph version | awk '{print $3}' | cut -d . -f 1) if [ $CEPH_MAJOR_VERSION -gt "15" ];then echo "Warning: current process is not supported on clusters version 16 and up" exit 1 fi i=0 while [ $i -lt $DB_LV_COUNT ]; do LV_NAME=$(echo $DB_DEVICE_LV_JSON | jq -r --arg index $i '.[] | .[].vg | .[$index |tonumber].lv_name') VG_NAME=$(echo $DB_DEVICE_LV_JSON | jq -r --arg index $i '.[] | .[].vg | .[$index |tonumber].vg_name') OSD_JSON=$(ceph-volume lvm list --format json /dev/$VG_NAME/$LV_NAME) OSD_ID=$( echo $OSD_JSON | jq -r '.[] | .[].tags["ceph.osd_id"]') OSD_TYPE=$( echo $OSD_JSON | jq -r '.[] | .[].type') if [ "$OSD_TYPE" == "block" ];then echo "Warning: /dev/$VG_NAME/$LV_NAME on device $device is a bluestore block device, skipping" continue 2 fi echo "Extending db lv /dev/$VG_NAME/$LV_NAME to $NEW_DB_SIZE_EXTENTS" $ECHO_IF_DRYRUN lvextend -l $NEW_DB_SIZE_EXTENTS /dev/$VG_NAME/$LV_NAME # Call ceph health check function dont continue unless cluster healthy set +e CEPH_STATUS=$(ceph health --format json | jq -r '.status') set -e while [ "$CEPH_STATUS" != "HEALTH_OK" ]; do echo "Warning: Cluster is not in HEALTH_OK state" sleep 2 CEPH_STATUS=$(ceph health --format json | jq -r '.status') done echo "Stopping OSD.$OSD_ID" $ECHO_IF_DRYRUN ceph osd set noout $ECHO_IF_DRYRUN systemctl stop ceph-osd@$OSD_ID echo "Expanding bluestore db device to fill new space" $ECHO_IF_DRYRUN ceph-bluestore-tool bluefs-bdev-expand --path /var/lib/ceph/osd/ceph-$OSD_ID echo "Start OSD again" $ECHO_IF_DRYRUN systemctl start ceph-osd@$OSD_ID echo "Unset noout" $ECHO_IF_DRYRUN ceph osd unset noout echo "Verify osd is back up before continuing" OSD_STATE=$(ceph osd tree --format json | jq --arg id "$OSD_ID" -r '.nodes[] | select(.id == ($id |tonumber)) | .status') echo "OSD_STATE: $OSD_STATE" while [ "$OSD_STATE" != "up" ]; do echo "Warning: OSD.$OSD_ID is not UP yet. Waiting..." sleep 2 OSD_STATE=$(ceph osd tree --format json | jq --arg id "$OSD_ID" -r '.nodes[] | select(.id == ($id |tonumber)) | .status') echo "OSD_STATE: $OSD_STATE" done let i=i+1 done done