#!/usr/bin/env bash
# -*- tab-width:4;indent-tabs-mode:nil -*-
# ex: ts=4 sw=4 et
#======================================================================
#
# LeoFS
#
# Copyright (c) 2012-2018 Rakuten, Inc.
#
# This file is provided to you 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.
#
#======================================================================

bold=`tput bold`
normal=`tput sgr0`

NONE='\033[00m'
BOLD='\033[1m'
UNDERLINE='\033[4m'

SCRIPT=`basename $0`

: ${LEOFS_ADM_CFG=$HOME/.${SCRIPT}rc}

test -r "${LEOFS_ADM_CFG}" && . "${LEOFS_ADM_CFG}"

: ${LEOFS_ADM_HOST=127.0.0.1}
: ${LEOFS_ADM_PORT=10010}

STORAGE_NODE="<storage-node>"
GATEWAY_NODE="<gateway-node>"
FILE_PATH="<file-path>"
USER_ID="<user-id>"
ACCESS_KEY_ID="<access-key-id>"
SECRET_ACCESS_KEY="<secret-access-key>"
ENDPOINT="<endpoint>"
WHITESPACE="         "
BAR="|"


##
## Set the netcat command's arguments
##
OS=`uname -s`
NC_ARGS=""
case $OS in
    FreeBSD)
        # Use -N flag (shutdown(2) after EOF on the input) if available
        if nc -h 2>&1 | grep -q 'usage: nc \[[^]]*N'; then
            NC_ARGS="-N"
        else
            NC_ARGS=""
        fi
        ;;
    SunOS)
        NC_ARGS=""
        ;;
    Linux)
        DIST_ID=`lsb_release -is`
        RELEASE_NUM=`lsb_release -rs`

        ## For Ubuntu-18.04
        if [ "$DIST_ID" = "Ubuntu" -a "$RELEASE_NUM" = "18.04" ]; then
            NC_ARGS="-N"
        else
            NC_ARGS="-C"
        fi
        ;;
    *)
        NC_ARGS="-C"
esac


##
## Output text
##
output() {
    echo -e "$1"
}

##
## Usage of each command
##
usage_status() {
    USAGE="status [<node>]"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE"
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Retrieve status of every node (default)"
            output "  - Retrieve status of the specified node"
    esac
}

usage_whereis() {
    USAGE="whereis $FILE_PATH"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Retrieve an assigned object by the file-path"
    esac
}

usage_detach() {
    USAGE="detach $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  -  Remove the storage node in the storage cluster"
    esac
}

usage_suspend() {
    USAGE="suspend $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  -  Suspend the storage node"
            output "  -  While suspending, it rejects any requests"
            output "  -  This command does NOT detach the node from the storage cluster"
    esac
}

usage_resume() {
    USAGE="resume $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  -  Resume the storage node"
    esac
}

usage_rollback() {
    USAGE="rollback $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  -  Rollback state of the storage node from detach to running"
    esac
}

usage_start() {
    USAGE="start"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Launch the storage cluster"
    esac
}

usage_rebalance() {
    USAGE="rebalance"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Commit detached and attached nodes to join the cluster AND "
            output "  - Rebalance objects in the cluster based on the updated cluster topology"
    esac
}

usage_commit() {
    output "Description:"
    output "  - "
    output "Usage:"
    output "  $SCRIPT commit"
}

usage_recover_file() {
    USAGE="recover-file $FILE_PATH"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Recover an inconsistent object specified by the file-path"
    esac
}

# == Note: For v2.0 >>
# usage_recover_dir() {
#     USAGE="recover-dir"
#     case "$1" in
#         min)
#             output "$WHITESPACE $USAGE "
#             ;;
#         *)
#             output "Usage:"
#             output "  $SCRIPT $USAGE"
#             output "Since: LeoFS v.1.4.0"
#             output "Description:"
#             output "  - Recover/Rebuild every directory's metadata for the matadata-layer"
#     esac
# }
# <<

usage_recover_disk() {
    USAGE="recover-disk $STORAGE_NODE <disk-id>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Recover all inconsistent objects on the specified disk in the specified node"
    esac
}

usage_recover_node() {
    USAGE="recover-node $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Recover all inconsistent objects in the specified node"
    esac
}

usage_recover_ring() {
    USAGE="recover-ring $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Recover rings of the specified node"
    esac
}

usage_recover_cluster() {
    USAGE="recover-cluster <cluster-id>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Recover all inconsistent objects in the specified cluster"
    esac
}

usage_compact_start() {
    USAGE="compact-start <node> (all | <num-of-targets>) [<number-of-compaction-procs>]"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Remove unnecessary objects from the node"
            output "    - num-of-targets: It controls the number of containers in parallel"
            output "    - number-of-compaction-procs: It controls the number of procs to execute the compaction in parallel"
    esac
}

usage_compact_suspend() {
    USAGE="compact-suspend $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Suspend the compaction"
    esac
}

usage_compact_resume() {
    USAGE="compact-resume $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Resume the compaction"
    esac
}

usage_compact_status() {
    USAGE="compact-status $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - See the current compaction status"
    esac
}

usage_diagnose_start() {
    USAGE="diagnose-start <node>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Diagnose data of the storage-node"
    esac
}

usage_diagnose_suspend() {
    USAGE="diagnose-suspend $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Suspend the data-diagnosis"
    esac
}

usage_diagnose_resume() {
    USAGE="diagnose-resume $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Resume the data-diagnosis"
    esac
}

usage_du() {
    USAGE="du [detail] $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Summarize disk usage of the node"
            output "  - Display disk usage of the every AVS file if detail specified"
    esac
}

usage_purge_cache() {
    USAGE="purge-cache $FILE_PATH"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Remove the cache from each gateway"
    esac
}

usage_remove_gateway() {
    USAGE="remove-gateway $GATEWAY_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Remove the gateway node, which is already stopped"
    esac
}

usage_backup_mnesia() {
    USAGE="backup-mnesia <backup-filepath>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Copy LeoFS's manager data to the filepath"
    esac
}

usage_restore_mnesia() {
    USAGE="restore-mnesia <backup-filepath>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Restore LeoFS's manager data from the backup file"
    esac
}

usage_update_managers() {
    USAGE="update-managers <manager-master> <manager-slave>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Update LeoFS Manager nodes"
            output "  - Destribute the new LeoFS Manager nodes to LeoFS Storage and Gateway"
    esac
}

usage_dump_ring() {
    USAGE="dump-ring (<manager-node>|$STORAGE_NODE|$GATEWAY_NODE)"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Dump the ring data to the local disk"
    esac
}

usage_update_log_level() {
    USAGE="update-log-level ($STORAGE_NODE|$GATEWAY_NODE) (debug|info|warn|error)"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Update a log level of the node"
    esac
}

usage_update_consistency_level() {
    USAGE="update-consistency-level <write-quorum> <read-quorum> <delete-quorum>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Update a consistency level of the node"
    esac
}

usage_update_property() {
    USAGE="update-property <node> <property-name> <property-value>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "  Examples:"
            output "      leofs-adm update-property <node> watchdog.cpu_enabled <boolean>"
            output "      leofs-adm update-property <node> watchdog.cpu_raised_error_times <integer>"
            output "      leofs-adm update-property <node> watchdog.cpu_interval <integer>"
            output "      leofs-adm update-property <node> watchdog.cpu_threshold_load_avg <float>"
            output "      leofs-adm update-property <node> watchdog.cpu_threshold_util <integer>"
            output "      leofs-adm update-property <node> watchdog.disk_enabled <boolean>"
            output "      leofs-adm update-property <node> watchdog.disk_raised_error_times <integer>"
            output "      leofs-adm update-property <node> watchdog.disk_interval <integer>"
            output "      leofs-adm update-property <node> watchdog.disk_threshold_use <integer>"
            output "      leofs-adm update-property <node> watchdog.disk_threshold_util <integer>"
            output "      leofs-adm update-property <node> watchdog.disk_threshold_rkb <integer>"
            output "      leofs-adm update-property <node> watchdog.disk_threshold_wkb <integer>"
            output "      leofs-adm update-property <node> watchdog.cluster_enabled <boolean>"
            output "      leofs-adm update-property <node> watchdog.cluster_interval <integer>"
            output "Description:"
            output "  - Modify a property of the watchdog"
    esac
}

usage_create_user() {
    USAGE="create-user $USER_ID [<password>]"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Register the new user"
            output "  - Generate an S3 key pair (AccessKeyID and SecretAccessKey)"
    esac
}

usage_import_user() {
    USAGE="import-user [-f] $USER_ID $ACCESS_KEY_ID $SECRET_ACCESS_KEY"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Import a user with access key and secret key"
    esac
}

usage_delete_user() {
    USAGE="delete-user $USER_ID"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Remove the user"
    esac
}

usage_get_users() {
    USAGE="get-users"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Retrieve the list of users"
    esac
}

usage_update_user_role() {
    USAGE="update-user-role $USER_ID <role-id>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Update the user's role"
            output "  - Currently, we are supporting two kinds of roles"
            output "    - 1: General user"
            output "    - 9: Administrator"
    esac
}

usage_update_user_password() {
    USAGE="update-user-password $USER_ID <password>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Update the user's password"
    esac
}

usage_add_endpoint() {
    USAGE="add-endpoint $ENDPOINT"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Register the new endpoint"
    esac
}

usage_delete_endpoint() {
    USAGE="delete-endpoint $ENDPOINT"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Remove the endpoint"
    esac
}

usage_get_endpoints() {
    USAGE="get-endpoints"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Retrieve the list of endpoints"
    esac
}

usage_add_bucket() {
    USAGE="add-bucket <bucket> $ACCESS_KEY_ID"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Create the new bucket"
    esac
}

usage_delete_bucket() {
    USAGE="delete-bucket <bucket> $ACCESS_KEY_ID"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Remove the bucket and all files stored in the bucket"
    esac
}

usage_delete_bucket_stats() {
    USAGE="delete-bucket-stats [<bucket>]"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Retrive the deletion-bucket's state"
    esac
}

usage_reset_delete_bucket_stats() {
    USAGE="reset-delete-bucket-stats [<bucket>]"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Reset the deletion-bucket's state"
    esac
}

usage_get_bucket() {
    USAGE="get-bucket $ACCESS_KEY_ID"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Retrieve the list of the buckets owned by the specified user"
    esac
}

usage_get_buckets() {
    USAGE="get-buckets"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Retrieve the list of the buckets registered"
    esac
}

usage_chown_bucket() {
    USAGE="chown-bucket <bucket> $ACCESS_KEY_ID"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Change the owner of the bucket"
    esac
}

# == Note: For v2.0 >>
# usage_set_redundancy_method() {
#     USAGE="set-redundancy-method <bucket> $ACCESS_KEY_ID (copy | erasure-code) [<number-of-data-chunks>] [<number-of-conding-chunks>]"
#     case "$1" in
#         min)
#             output "$WHITESPACE $USAGE "
#             ;;
#         *)
#             output "Usage:"
#             output "  $SCRIPT $USAGE"
#             output "Description:"
#             output "  - Set redundancy method of the bucket"
#             output "  - erasure-code:"
#             output "      - number-of-data-chunks: The number of chunks in which the original object is divided"
#             output "      - number-of-coding-chunks: The number of additional chunks computed by leo_erasure's encoding functions"
#             output "      ex-1. RAID 5: number-of-data-chunks = 2, number-of-coding-chunks: 1"
#             output "      ex-2. RAID 6: number-of-data-chunks = 2, number-of-coding-chunks: 2"
#             output "      ex-3. Custom params.1: number-of-data-chunks = 4, number-of-coding-chunks: 2"
#             output "      ex-4. Custom params.2: number-of-data-chunks = 8, number-of-coding-chunks: 3"
#             output "      ex-5. Custom params.3: number-of-data-chunks = 10, number-of-coding-chunks: 4"
#     esac
# }
# <<

usage_update_acl() {
    USAGE="update-acl <bucket> $ACCESS_KEY_ID (private | public-read | public-read-write)"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Update the ACL (Access Control List) for the bucket"
            output "  - Available ACL list"
            output "    - private (default) : No one except the owner has access rights."
            output "    - public-read       : All users have READ access."
            output "    - public-read-write : All users have READ and WRITE access."
    esac
}

usage_gen_nfs_mnt_key() {
    USAGE="gen-nfs-mnt-key <bucket> <access-key-id> <client-ip-address>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Generate a NFS mount key"
    esac
}

usage_join_cluster() {
    USAGE="join-cluster <remote-manager-master> <remote-manager-slave>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Begin to communicate between the local cluster and the remote cluster"
    esac
}

usage_remove_cluster() {
    USAGE="remove-cluster <remote-manager-master> <remote-manager-slave>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE "
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - Terminate to communicate between the local cluster and the remote cluster"
    esac
}

usage_cluster_status() {
    USAGE="cluster-status"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE"
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - See the state of cluster(s)"
    esac
}

usage_mq_stats() {
    USAGE="mq-stats $STORAGE_NODE"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE"
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  - See the status of message queues used in LeoFS"
    esac
}

usage_mq_suspend() {
    USAGE="mq-suspend $STORAGE_NODE <mq-id>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE"
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  -  Suspend a process consuming a message queue"
            output "  -  Active message queues only can be suspended"
            output "  -  While suspending, no messages are consumed"
            output "  -  Please use this command only if the target node is too busy"
    esac
}

usage_mq_resume() {
    USAGE="mq-resume $STORAGE_NODE <mq-id>"
    case "$1" in
        min)
            output "$WHITESPACE $USAGE"
            ;;
        *)
            output "Usage:"
            output "  $SCRIPT $USAGE"
            output "Description:"
            output "  -  Resume a process consuming a message queue"
    esac
}

##
## Usage of the script
##
usage() {
    output "usage: ${bold}$SCRIPT${normal} [--help]"
    output "       ${bold}$SCRIPT${normal} [-h <host>] [-p <port>] <command> <args>"
    output ""
    output "  LeoFS commands are:"
    output "      ${UNDERLINE}General Commands:${NONE}"
    output "$WHITESPACE version [all]"
    usage_status min
    usage_whereis min
    output ""
    output "      ${UNDERLINE}Storage Operation:${NONE}"
    usage_detach min
    usage_suspend min
    usage_resume min
    usage_rollback min
    usage_start min
    usage_rebalance min
    usage_mq_stats min
    usage_mq_suspend min
    usage_mq_resume min
    output ""
    output "      ${UNDERLINE}Recover Commands:${NONE}"
    usage_recover_file min
    usage_recover_disk min
    usage_recover_node min
    usage_recover_ring min
    usage_recover_cluster min
    output ""
    output "      ${UNDERLINE}Compaction Commands:${NONE}"
    usage_compact_start min
    usage_compact_suspend min
    usage_compact_resume min
    usage_compact_status min
    usage_diagnose_start min
    usage_du min
    output ""
    output "      ${UNDERLINE}Gateway Operation:${NONE}"
    usage_purge_cache min
    usage_remove_gateway min
    output ""
    output "      ${UNDERLINE}Manager Maintenance:${NONE}"
    usage_backup_mnesia min
    usage_restore_mnesia min
    usage_update_managers min
    usage_dump_ring min
    usage_update_log_level min
    usage_update_consistency_level min
    output ""
    output "      ${UNDERLINE}Watchdog Operation:${NONE}"
    usage_update_property min
    output ""
    output "      ${UNDERLINE}S3-related Commands:${NONE}"
    usage_create_user min
    usage_import_user min
    usage_delete_user min
    usage_get_users min
    usage_update_user_role min
    usage_update_user_password min
    output ""
    usage_add_endpoint min
    usage_delete_endpoint min
    usage_get_endpoints min
    output ""
    usage_add_bucket min
    usage_delete_bucket min
    usage_delete_bucket_stats min
    usage_reset_delete_bucket_stats min
    usage_get_bucket min
    usage_get_buckets min
    usage_chown_bucket min
    # usage_set_redundancy_method min
    usage_update_acl min
    usage_gen_nfs_mnt_key min
    output ""
    output "      ${UNDERLINE}Multi Data Center Operation:${NONE}"
    usage_join_cluster min
    usage_remove_cluster min
    usage_cluster_status min
    output ""
}


## Send a command to local manager
send_command() {
    echo "$@" | nc $NC_ARGS $LEOFS_ADM_HOST $LEOFS_ADM_PORT
    if [ $? -ne 0 ]; then
        echo "Error: couldn't connect to LeoFS Manager"
        exit 1
    fi
}

while test -n "$1"; do
    case "$1" in
        --help)
            usage | less -r
            exit 0
            ;;
        -h)
            LEOFS_ADM_HOST="$2"
            shift
            ;;
        -p)
            LEOFS_ADM_PORT="$2"
            shift
            ;;
        *)
            break
    esac
    shift
done

case "$1" in
    version)
        if [ $# -gt 2 ]; then
            echo "Usage: $SCRIPT version"
            exit 1
        fi
        send_command "$@"
        ;;
    status)
        if [ $# -eq 1 ]; then
            send_command "$@"
        elif [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_status
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_status
        fi
        ;;
    whereis)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_whereis
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_whereis
        fi
        ;;

    ##
    ## STORAGE OPERATION
    ##
    detach)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_detach
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_detach
        fi
        ;;
    suspend)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_suspend
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_suspend
        fi
        ;;
    resume)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_resume
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_resume
        fi
        ;;
    rollback)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_rollback
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_rollback
        fi
        ;;
    start)
        if [ $# -ne 1 ]; then
            usage_start
            exit 1
        fi
        send_command "$@"
        ;;
    rebalance)
        if [ $# -ne 1 ]; then
            usage_rebalance
            exit 1
        fi
        send_command "$@"
        ;;
    commit)
        if [ $# -ne 1 ]; then
            usage_commit
            exit 1
        fi
        send_command "$@"
        ;;
    mq-stats)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_mq_stats
                    exit 1
                    ;;
                *)
                    send_command "mq-stats $2"
            esac
        else
            usage_mq_stats
        fi
        ;;
    mq-suspend)
        if [ $# -eq 3 ]; then
            send_command "mq-suspend $2 $3"
        else
            usage_mq_suspend
        fi
        ;;
    mq-resume)
        if [ $# -eq 3 ]; then
            send_command "mq-resume $2 $3"
        else
            usage_mq_resume
        fi
        ;;

    ##
    ## RECOVER
    ##
    recover-file)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_recover_file
                    exit 1
                    ;;
                *)
                    send_command "recover file $2"
            esac
        else
            usage_recover_file
        fi
        ;;
    # == Note: For v2.0 >>
    # recover-dir)
    #     if [ $# -eq 1 ]; then
    #         send_command "recover dir"
    #         exit 1
    #     else
    #         usage_recover_dir
    #     fi
    #     ;;
    # <<
    recover-disk)
        if [ $# -eq 3 ]; then
            send_command "recover disk $2 $3"
        else
            usage_recover_disk
        fi
        ;;
    recover-node)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_recover_node
                    exit 1
                    ;;
                *)
                    send_command "recover node $2"
            esac
        else
            usage_recover_node
        fi
        ;;
    recover-ring)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_recover_ring
                    exit 1
                    ;;
                *)
                    send_command "recover ring $2"
            esac
        else
            usage_recover_ring
        fi
        ;;
    recover-cluster)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_recover_cluster
                    exit 1
                    ;;
                *)
                    send_command "recover cluster $2"
            esac
        else
            usage_recover_cluster
        fi
        ;;

    ##
    ## COMPACTION
    ##
    compact-start)
        if [ $# -eq 3 ]; then
            send_command "compact start $2 $3"
        elif [ $# -eq 4 ]; then
            send_command "compact start $2 $3 $4"
        else
            usage_compact_start
            exit 1
        fi
        ;;
    compact-suspend)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_compact_suspend
                    exit 1
                    ;;
                *)
                    send_command "compact suspend $2"
            esac
        else
            usage_compact_suspend
        fi
        ;;
    compact-resume)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_compact_resume
                    exit 1
                    ;;
                *)
                    send_command "compact resume " $2
            esac
        else
            usage_compact_resume
        fi
        ;;

    diagnose-start)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_diagnose_start
                    exit 1
                    ;;
                *)
                    send_command "diagnose-data $2"
            esac
        else
            usage_diagnose_start
        fi
        ;;
    compact-suspend)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_diagnose_suspend
                    exit 1
                    ;;
                *)
                    send_command "compact suspend $2"
            esac
        else
            usage_diagnose_suspend
        fi
        ;;
    diagnose-resume)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_diagnose_resume
                    exit 1
                    ;;
                *)
                    send_command "compact resume " $2
            esac
        else
            usage_diagnose_resume
        fi
        ;;
    compact-status)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_compact_status
                    exit 1
                    ;;
                *)
                    send_command "compact status " $2
            esac
        else
            usage_compact_status
        fi
        ;;
    du)
        if [ $# -ge 2 ]; then
            case "$2" in
                --help)
                    usage_du
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_du
        fi
        ;;

    ##
    ## GATEWAY
    ##
    purge-cache)
        if [ $# -eq 2 ]; then
            send_command "purge $2"
        else
            usage_purge_cache
        fi
        ;;
    remove-gateway)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_remove_gateway
                    exit 1
                    ;;
                *)
                    send_command "remove $2"
            esac
        else
            usage_remove_gateway
        fi
        ;;

    ##
    ## MNESIA Maintenance
    ##
    backup-mnesia)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_backup_mnesia
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_backup_mnesia
        fi
        ;;
    restore-mnesia)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_restore_mnesia
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_restore_mnesia
        fi
        ;;

    ##
    ## MANAGER
    ##
    update-managers)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_update_managers
        fi
        ;;
    dump-ring)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_dump_ring
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_dump_ring
        fi
        ;;
    update-log-level)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_update_log_level
        fi
        ;;

    update-consistency-level)
        if [ $# -eq 4 ]; then
            send_command "$@"
        else
            usage_update_consistency_level
        fi
        ;;

    ##
    ## Watchdog Operation
    ##
    update-property)
        if [ $# -eq 4 ]; then
            send_command "$@"
        else
            usage_update_property
        fi
        ;;

    ##
    ## S3-related
    ##
    create-user)
        if [ $# -eq 2 -o $# -eq 3 ]; then
            send_command "$@"
        else
            usage_create_user
        fi
        ;;
    import-user)
        if [ $# -eq 4 ]; then
            send_command "$@"
        elif [ $# -eq 5 ]; then
            case "$2" in
                -f)
                    send_command "$@"
                    ;;
                *)
                    usage_import_user
                    exit 1
                    ;;
            esac
        else
            usage_import_user
        fi
        ;;
    delete-user)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_delete_user
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_delete_user
        fi
        ;;
    get-users)
        if [ $# -eq 1 ]; then
            send_command "$@"
        else
            usage_get_users
        fi
        ;;
    update-user-role)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_update_user_role
        fi
        ;;
    update-user-password)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_update_user_password
        fi
        ;;
    add-endpoint)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_add_endpoint
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_add_endpoint
        fi
        ;;
    delete-endpoint)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_delete_endpoint
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_delete_endpoint
        fi
        ;;
    get-endpoints)
        if [ $# -eq 1 ]; then
            send_command "$@"
        else
            usage_get_endpoints
        fi
        ;;
    add-bucket)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_add_bucket
        fi
        ;;
    delete-bucket)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_delete_bucket
        fi
        ;;
    delete-bucket-stats)
        if [ $# -ge 1 ]; then
            case "$2" in
                --help)
                    usage_delete_bucket_stats
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_delete_bucket_stats
        fi
        ;;
    reset-delete-bucket-stats)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_reset_delete_bucket_stats
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_reset_delete_bucket_stats
        fi
        ;;
    get-bucket)
        if [ $# -eq 2 ]; then
            case "$2" in
                --help)
                    usage_get_bucket
                    exit 1
                    ;;
                *)
                    send_command "$@"
            esac
        else
            usage_get_bucket
        fi
        ;;
    get-buckets)
        if [ $# -eq 1 ]; then
            send_command "$@"
        else
            usage_get_buckets
        fi
        ;;
    chown-bucket)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_chown_bucket
        fi
        ;;
    # == Note: For v2.0 >>
    # set-redundancy-method)
    #     if [ $# -eq 4 ]; then
    #         send_command "$@"
    #     elif [ $# -eq 6 ]; then
    #         send_command "$@"
    #     else
    #         usage_set_redundancy_method
    #     fi
    #     ;;
    # <<
    update-acl)
        if [ $# -eq 4 ]; then
            send_command "$@"
        else
            usage_update_acl
        fi
        ;;
    gen-nfs-mnt-key)
        if [ $# -eq 4 ]; then
            send_command "$@"
        else
            usage_gen_nfs_mnt_key
        fi
        ;;
    ##
    ## MDC-Replication
    ##
    join-cluster)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_join_cluster
        fi
        ;;
    remove-cluster)
        if [ $# -eq 3 ]; then
            send_command "$@"
        else
            usage_remove_cluster
        fi
        ;;
    cluster-status)
        if [ $# -eq 1 ]; then
            send_command "$@"
        else
            usage_cluster_status
        fi
        ;;
    *)
        usage | less -r
        exit 1
        ;;
esac