#!/bin/bash
# Copyright 2018 Quobyte Inc.

LANG=en_US.utf-8

export LC_TYPE=en_US.utf-8

DEVICE_ID_FILE="QUOBYTE_DEV_SETUP"

function print_usage {
  if [[ "$(basename $0)" == qbootstrap* ]]; then
    printf "Usage: %s [-y] [-f] [-s serial_number] mount_point\n" "$(basename $0)"
    echo "Creates a new Quobyte bootstrap device on the disk/file system mounted at <mount_point>."
    echo "-f overwrites any existing device information (dangerous!)."
    echo "-s serial_number serial number for a device."
    echo "-l <label> apply file system label to ext4 device. Recommended:
    'quobyte-dev'. Xfs does not support relabeling of mounted devices."
    echo "-d skip device detection and create a directory based Quobyte device"
    echo "   Note: Quobyte services check '/var/lib/quobyte/devices/' for devices"
    echo "-y do not ask for confirmation."
  else
    printf "Usage: %s [-f] [-d] [-t type] [-s serial_number] [-l label] [-T tags] mount_point\n" "$(basename $0)"
    echo "Creates a new Quobyte device on the disk/file system mounted at <mount_point>."
    echo "-f overwrites any existing device information (dangerous!)."
    echo "-t DATA|METADATA|REGISTRY optional device type"
    echo "   this device should be used as."
    echo "-l <label> apply file system label to ext4 device. Recommended:
    'quobyte-dev'. Xfs does not support relabeling of mounted devices."
    echo "-d skip device detection and create a directory based Quobyte device"
    echo "   Note: Quobyte services check '/var/lib/quobyte/devices/' for devices"
    echo "-s <serial_number> serial number for the device."
    echo "-T <tags> comma separated list of Quobyte device tags"
  fi
  echo
  exit 2
}

force_flag=
type_flag=
serial_number=
yes_mode=
bootstrap_mode=
fs_label=
device_directory=
quobyte_tags=
while getopts "fyBhs:t:l:dT:" name
do
    case $name in
    f)    force_flag=1;;
    t)    type_flag="$OPTARG";;
    s)    serial_number="$OPTARG";;
    y)    yes_mode=1;;
    d)    device_directory=1;;
    l)    fs_label="$OPTARG";;
    T)    quobyte_tags="$OPTARG";;
    ?)    print_usage;;
    esac
done
shift $(($OPTIND - 1))

if [[ "$(basename $0)" == qbootstrap* ]]
then
  bootstrap_mode=1
fi

if [ $# -ne 1 ]; then
  print_usage
fi

if [ -n "$EUID" -a $EUID -ne 0 ]; then
  echo "Error: This program must be executed as root (requires access to smartctl/lshw to read device identity)."
  exit 4
fi

MOUNT_POINT="$(readlink -f "$1")"

if [[ ${bootstrap_mode} == 1 ]]; then
  if [[ "$type_flag" != "" ]];then
    echo "Cannot specify type when creating bootstrap registry device."
    exit 11
  fi
  type_flag="R"

  if [ -z "$yes_mode" ]; then
    echo "Attention: a bootstrap device must only be created once per Quobyte installation."
    echo "If you have already created a bootstrap device on any machine, please use qmkdev -t to create further devices."
    read -p "Are you sure you that you want to make ${MOUNT_POINT} your installation's bootstrap device (y/n)? " -n 1 -r
    echo    # (optional) move to a new line
    if [[ ! $REPLY =~ ^[Yy]$ ]]
    then
      echo "Cancelled."
      exit 8
    fi
  else
     echo "Skipping confirmation."
  fi
fi

echo "Device mount point: ${MOUNT_POINT}"
type_flag=$(echo "$type_flag"|awk '{print toupper($0)}')
if [[ "$type_flag" != "" && "$type_flag" != "REGISTRY" && \
    "$type_flag" != "METADATA" && "$type_flag" != "DATA" &&\
    "$type_flag" != "D" && "$type_flag" != "M" && "$type_flag" != "R" ]]; then
  echo "Error: Invalid device type."
  exit 10
fi

DEVICE_FILE="${MOUNT_POINT}/${DEVICE_ID_FILE}"
if [ -e "${DEVICE_FILE}" ]; then
  if [ -z "$force_flag" ]; then
    echo "Error: Selected mount point is already a Quobyte device."
    echo "       Use -f to overwrite the current device information."
    echo "Exiting."
    exit 3
  else
    echo "WARNING: Device information will be overwritten. All data on this device is lost."
    mv ${DEVICE_FILE} ${DEVICE_FILE}.bak
  fi
fi

if [ -z "${device_directory}" ]; then
  echo "Creating device based Quobyte device"

  escaped="${MOUNT_POINT// /\\040}"
  # Ignore any rootfs mounts.
  lines="$(fgrep " ${escaped} " /proc/mounts | fgrep -v " rootfs ")"
  if [[ "$(wc -l <<<"${lines}")" -gt 1 ]]; then
    echo "Error: Ambiguous mount point. Multiple matches for ' ${escaped} ' in /proc/mounts."
    exit 11
  fi
  if [[ "$(cut -d " " -f 2 <<<"${lines}")" != "${escaped}" ]]; then
    echo "Error: Invalid mount point. ' ${escaped} ' not found in second column of /proc/mounts."
    exit 11
  fi
  DEVICE="$(cut -d " " -f 1 <<<"${lines}")"
  FSTYPE="$(cut -d " " -f 3 <<<"${lines}")"

  if [ -z "${DEVICE}" ]; then
    echo "Error: Invalid mount point. ' ${escaped} ' not found in /proc/mounts."
    exit 11
  fi
  if [ ! -b "${DEVICE}" ]; then
    echo "Error: Invalid mount point. ${DEVICE} is no block device."
    exit 12
  fi
  # NOTE(quobyte): Keep this list in sync with the one in the data service.
  SUPPORTED_FS_TYPES="ext4 xfs"
  supported_fs_found=0
  for expected_fs in ${SUPPORTED_FS_TYPES}
  do
    if [[ ${expected_fs} == "$FSTYPE" ]]; then
      supported_fs_found=1
      break
    fi
  done
  if [ ${supported_fs_found} -eq 0 ]; then
    echo "Error: Device file system not supported (${FSTYPE}). Supported file system types are: ${SUPPORTED_FS_TYPES}"
    exit 13
  fi

  DEVICE_VENDOR=$(udevadm info --query=all --name=${DEVICE} |
                      awk '/^E: ID_VENDOR=/ {gsub(/E: ID_VENDOR=/, "", $0); gsub(/[[:cntrl:]]/, "_", $0); print $0}')
  DEVICE_MODEL=$(udevadm info --query=all --name=${DEVICE} |
                     awk '/^E: ID_MODEL=/ {gsub(/E: ID_MODEL=/, "", $0); gsub(/[[:cntrl:]]/, "_", $0); print $0}')
  DEVICE_SERIAL=$(udevadm info --query=all --name=${DEVICE} |
                      awk '/^E: ID_SERIAL_SHORT=/ {gsub(/E: ID_SERIAL_SHORT=/, "", $0); gsub(/[[:cntrl:]]/, "_", $0); print $0}')
  FS_UUID=$(udevadm info --query=all --name=${DEVICE} |
                awk '/^E: ID_FS_UUID=/ {gsub(/E: ID_FS_UUID=/, "", $0); gsub(/[^[:xdigit:]-]+/, "_", $0); print $0}')

  echo "Detected information for device: ${DEVICE}"
  echo "DEVICE_VENDOR: ${DEVICE_VENDOR}"
  echo "DEVICE_MODEL: ${DEVICE_MODEL}"
  echo "DEVICE_SERIAL: ${DEVICE_SERIAL}"
  echo "FS_UUID: ${FS_UUID}"

else
  echo "Creating directory based Quobyte device"
  mkdir -p ${MOUNT_POINT}
  DEVICE_MODEL="Directory"
fi

if [ -z "$DEVICE_MODEL" ]; then
  DEVICE_MODEL="unknown"
  echo "WARNING: Cannot determine device model."
fi

if [ -z "$DEVICE_VENDOR" ]; then
  DEVICE_VENDOR="unknown"
  echo "WARNING: Cannot determine device vendor."
fi

if [ -z "$FS_UUID" ]; then
  FS_UUID=`uuidgen 2> /dev/null`
  echo "WARNING: Cannot determine FS_UUID. Using random ID".
fi

generate_random_serial=0
if [ -n "$serial_number" ]; then
  echo "Override Serial Number: ${serial_number}"
  DEVICE_SERIAL="${serial_number}"
else
  if [ ${generate_random_serial} -eq 0 ]; then
    if [[ "$DEVICE_MODEL" =~ ^QEMU.* ]]; then
      echo "Ignoring pseudo serial number from QEMU device. Generating uuid."
      generate_random_serial=1
    elif [[ "$DEVICE_MODEL" =~ ^VBOX.* ]]; then
      echo "Ignoring pseudo serial number from VBOX device. Generating uuid."
      generate_random_serial=1
    elif [[ "$DEVICE_VENDOR" =~ ^Google.* ]]; then
      echo "Ignoring pseudo serial number from Google device. Generating uuid."
      generate_random_serial=1
    else
      if [ -z "$DEVICE_SERIAL" ]; then
        echo "Cannot determine device serial number. Generating uuid."
        generate_random_serial=1
      fi
    fi
  fi
fi
if [ ${generate_random_serial} -eq 0 -a ${#DEVICE_SERIAL} -lt 5 ]; then
  echo "Device serial number too short. Generating uuid."
  generate_random_serial=1
fi
if [ ${generate_random_serial} -eq 1 ]; then
  DEVICE_SERIAL=`uuidgen 2> /dev/null`
  if [ -z "$DEVICE_SERIAL" ]; then
    echo "Cannot determine device serial number and uuidgen failed."
    echo "Please provide a serial number using the -s switch."
    exit 7
  fi
fi

if [[ "$type_flag" == "DATA" || "$type_flag" == "D" ]]; then
  AVAIL_SPACE=`df -B1 ${MOUNT_POINT} | grep -v Avail | awk '{ print $4 "\t" }'`
  AVAIL_SPACE_HR=`df -h ${MOUNT_POINT} | grep -v Avail | awk '{ print $4 "\t" }' | tr -d '[[:space:]]'`
  if [ ${AVAIL_SPACE} -lt 21474836480 ]; then
    echo "WARNING: $MOUNT_POINT has less than 20G of available space ($AVAIL_SPACE_HR)."
    echo "Quobyte may refuse to create new files if such devices are used as data devices."
    echo "We highly recommend to use a device with more capacity!"
  fi
fi

if [ -n "$quobyte_tags" ]; then
  if ! [[ "${quobyte_tags}" =~ ^[A-Za-z0-9_,-]+$ ]]; then
    echo "Error: invalid characters in device tags '${quobyte_tags}'. Only alphanumeric, and '-' and '_' expected."
    exit 14
  fi
fi

CREATION_DATE=`date`
umask 0022
echo "# Quobyte device identifier file" > "${DEVICE_FILE}"
echo "# Created ${CREATION_DATE}" >> "${DEVICE_FILE}"
echo "# Hostname: ${HOSTNAME}" >> "${DEVICE_FILE}"
echo "device.serial=${DEVICE_SERIAL}" >> "${DEVICE_FILE}"
echo "device.fs_uuid=${FS_UUID}" >> "${DEVICE_FILE}"
echo "device.model=${DEVICE_MODEL}" >> "${DEVICE_FILE}"
if [ -n "$type_flag" ]; then
  if [[ "$type_flag" == "METADATA" || "$type_flag" == "M" ]]; then
    echo "device.type=METADATA_DEVICE" >> "${DEVICE_FILE}"
  elif [[ "$type_flag" == "DATA" || "$type_flag" == "D" ]]; then
    echo "device.type=DATA_DEVICE" >> "${DEVICE_FILE}"
  elif [[ "$type_flag" == "REGISTRY" || "$type_flag" == "R" ]]; then
    if [[ ${bootstrap_mode} == 1 ]]; then
      echo "device.bootstrap=true" >> "${DEVICE_FILE}"
    fi
    echo "device.type=DIR_DEVICE" >> "${DEVICE_FILE}"
  fi
else
  echo "No type specified. To use the device, please assign one or more content"
  echo "types using the qmgmt tool."
fi

if [ -n "$quobyte_tags" ]; then
  echo "device.tags=${quobyte_tags}" >> "${DEVICE_FILE}"
fi


device_owner=${QMKDEV_DEVICE_OWNER:-quobyte}
chown ${device_owner} "${DEVICE_FILE}"
chown ${device_owner} "${MOUNT_POINT}"

if [[ $? != 0 ]]; then
  echo "WARNING: Cannot change ownership of mount point, please make sure Quobyte services"
  echo "         can write to the mount point!"
fi

if [ "$FSTYPE" = "ext4" ]; then
  tune2fs >/dev/null -m 0 "${DEVICE}"

  if [ -n "$fs_label" ]; then
    tune2fs -L ${fs_label} ${DEVICE}
  fi
fi

if [ "$FSTYPE" = "xfs" ]; then
  if [ -n "$fs_label" ]; then
    echo  "======================================================================================"
    echo "Xfs does not support labeling of mounted device."
    echo "Please manually unmount the device and execute: xfs_admin -L ${fs_label} ${DEVICE}"
    echo  "======================================================================================"
  fi
fi

if [[ ${bootstrap_mode} == 1 ]]; then
  echo "Bootstrap device successfully initialized."
else
  echo "Device successfully initialized."
fi
echo
echo "If this device is mounted on a host with a running Quobyte service, "
echo "the device will be registered automatically."
