#!/usr/bin/env python3

import os
import sys
import json
import logging
import argparse
import lbd3

logging.basicConfig(format='%(asctime)s - %(filename)s[pid:%(process)d]line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.DEBUG,
                    filename='/var/log/lava-block.log',
                    filemode='a')
LAVA_BLOCK_LOG = logging.getLogger(__name__)

def print_dict(d):
    print(json.dumps(d, indent=2))

def print_json(s):
    print(json.dumps(s, indent=2).strip('"'))

def cmd_response(status, add_info):
    ''' Print ctb command result
        Args:
            status: command result, 0 represent successful, 1 represent failed
            add_info: other output, decided by specified command
    '''
    result = {}
    result['status'] = status
    result['result'] = add_info
    print_json(result)

def lava_block_env_probe(args, client_ctx):
    LAVA_BLOCK_LOG.info('env_probe')
    try:
        lbd3.LBD().env_probe(client_ctx)
    except Exception as e:
        LAVA_BLOCK_LOG.error("check lava engine status error")
        raise e
    LAVA_BLOCK_LOG.info("check lava engine status success")

def lava_block_create(args, client_ctx):
    LAVA_BLOCK_LOG.info("create lava block name: %s, size %lu Mbytes, pool: %d", args.name, args.size, args.pool)
    total_size = args.size * 1024 * 1024
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.create_image(total_size, args.pool, args.uuid)
    except Exception as e:
        LAVA_BLOCK_LOG.error("create lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("create lava block %s success", args.name)

def lava_block_delete(args, client_ctx):
    LAVA_BLOCK_LOG.info("delete lava block name: %s", args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.remove_image()
    except Exception as e:
        LAVA_BLOCK_LOG.error("delete lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("delete lava block %s success", args.name)

def lava_block_resize(args, client_ctx):
    LAVA_BLOCK_LOG.info("resize lava block name: %s, new size %lu Mbytes", args.name, args.size)
    total_size = args.size * 1024 * 1024
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.resize_image(total_size)
    except Exception as e:
        LAVA_BLOCK_LOG.error("resize lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("resize lava block %s success", args.name)

def lava_block_rename(args, client_ctx):
    LAVA_BLOCK_LOG.info("rename lava block name: %s, new name: %s", args.src_name, args.name)
    try:
        with lbd3.Image(client_ctx, args.src_name) as image:
            image.rename_image(args.name)
    except Exception as e:
        LAVA_BLOCK_LOG.error("rename lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("rename lava block %s success", args.name)

def lava_block_set_qos(args, client_ctx):
    LAVA_BLOCK_LOG.info('set lava block qos name: %s', args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.set_qos(args.iops, args.bw)
    except Exception as e:
        LAVA_BLOCK_LOG.error('set lava block %s qos failed', args.name)
        raise e
    LAVA_BLOCK_LOG.info('set lava block %s qos success', args.name)

def lava_block_set_priority(args, client_ctx):
    LAVA_BLOCK_LOG.info('set lava block qos name: %s', args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.set_priority(args.write, args.read)
    except Exception as e:
        LAVA_BLOCK_LOG.error('set lava block priority %s failed', args.name)
        raise e
    LAVA_BLOCK_LOG.info('set lava block priority %s success', args.name)

def lava_block_clone(args, client_ctx):
    LAVA_BLOCK_LOG.info("clone lava block name: %s, source block name: %s, snap name: %s", args.name, args.image, args.snap)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.clone_image(args.image, args.snap, args.list, args.pool)
            try:
                if json.loads(image.get_clone_depth())['depth'] > 1:
                    image.flatten_image()
            except Exception as e:
                LAVA_BLOCK_LOG.error("flatten lava block %s error", args.name)
                image.remove_image(force=True)
                raise e
    except Exception as e:
        LAVA_BLOCK_LOG.error("clone lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("clone lava block %s success", args.name)

def lava_block_get_clone_depth(args, client_ctx):
    LAVA_BLOCK_LOG.info('get lava block name: %s clone depth', args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            depth = image.get_clone_depth()
    except Exception as e:
        LAVA_BLOCK_LOG.error('get lava block %s clone depth failed', args.name)
        raise e
    LAVA_BLOCK_LOG.info('get lava block %s clone depth success', args.name)
    return eval(depth)

def lava_block_flatten(args, client_ctx):
    LAVA_BLOCK_LOG.info("flatten lava block name: %s", args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.flatten_image()
    except Exception as e:
        LAVA_BLOCK_LOG.error("flatten lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("flatten lava block %s success", args.name)

def lava_block_import(args, client_ctx):
    def chunkreadable(iter, chunk_size):
        return chunkiter(iter, chunk_size) if hasattr(iter, 'read') else iter
    def chunkiter(fp, chunk_size):
        while True:
            chunk = fp.read(chunk_size)
            if chunk:
                yield chunk
            else:
                break
    LAVA_BLOCK_LOG.info("import lava block name: %s", args.name)
    try:
        fp = open(args.path, 'rb')
        file_size = os.path.getsize(args.path)
        chunks = chunkreadable(fp, args.chunksize)
        with lbd3.Image(client_ctx, args.name, open=True) as image:
            image_info = json.loads(image.get_image_info())
            image_size = int(image_info['image_size'])
            sector_size = int(image_info['sector_size'])

            offset = args.offset
            if file_size + offset > image_size:
                raise lbd3.ImageCapacityError("file size greater than image size.")
            for chunk in chunks:
                if chunk and len(chunk) % sector_size != 0:
                    chunk = chunk.ljust((len(chunk) // sector_size + 1) * sector_size)

                chunk_length = len(chunk)
                image.write(chunk, offset)
                offset += chunk_length
    except Exception as e:
        LAVA_BLOCK_LOG.error("import lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("import lava block %s success", args.name)

def lava_block_export(args, client_ctx):
    LAVA_BLOCK_LOG.info("export lava block name: %s", args.name)
    try:
        fp = open(args.path, 'wb')
        with lbd3.Image(client_ctx, args.name, open=True) as image:
            image_size = int(json.loads(image.get_image_info())['image_size'])
            len = image_size if args.len == 0 else args.len
            offset = args.offset
            while len > 0 and offset < image_size:
                chunksize = min(args.chunksize, len, image_size - offset)
                data = image.read(chunksize, offset)
                fp.write(data)
                offset += chunksize
                len -= chunksize
    except Exception as e:
        LAVA_BLOCK_LOG.error("export lava block %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("export lava block %s success", args.name)

def lava_block_snap_diff(args, client_ctx):
    LAVA_BLOCK_LOG.info('check lava block name: %s snap diff', args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            diff_info = image.get_snap_diff(args.src, args.dst, args.list, args.offset, args.len, args.bitunit)
    except Exception as e:
        LAVA_BLOCK_LOG.error('check lava block %s snap diff failed', args.name)
        raise e
    LAVA_BLOCK_LOG.info('check lava block %s snap diff success', args.name)
    return diff_info

def lava_block_query_info(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava block info name: %s", args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image_info = image.get_image_info()
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava block info %s failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('query lava block %s info success', args.name)
    return eval(image_info)

def lava_block_query_parent_info(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava block name: %s parent info", args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            p_info = image.get_parent_info()
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava block %s parent info failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('query lava block %s parent info success', args.name)
    return eval(p_info)

def lava_block_list(args, client_ctx):
    LAVA_BLOCK_LOG.info('list lava block, start: %s, list number: %d', args.start, args.num)
    try:
        image_list = lbd3.LBD().list_image(client_ctx, args.start, args.num)
    except Exception as e:
        LAVA_BLOCK_LOG.error("list lava block failed")
        raise e
    LAVA_BLOCK_LOG.info('list lava block success')
    return eval(image_list)

def lava_block_list_children(args, client_ctx):
    LAVA_BLOCK_LOG.info('list lava block children, block name: %s, snap name: %s', args.name, args.snap)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            children = image.list_children(args.snap, args.list, args.pool)
    except Exception as e:
        LAVA_BLOCK_LOG.error("list lava block %s children failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('list lava block %s children success', args.name)
    return eval(children)

def lava_block_count(args, client_ctx):
    LAVA_BLOCK_LOG.info('query lava block count')
    try:
        image_count = lbd3.LBD().count_image(client_ctx)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava block count error")
        raise e
    LAVA_BLOCK_LOG.info('query lava block count success')
    return eval(image_count)

def lava_block_reset_status(args, client_ctx):
    LAVA_BLOCK_LOG.info("reset lava block status name: %s", args.name)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
            image.reset_status()
    except Exception as e:
        LAVA_BLOCK_LOG.error("reset lava block %s status failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('reset lava block %s status success', args.name)

def lava_block_discard(args, client_ctx):
    LAVA_BLOCK_LOG.info("discard lava block name: %s", args.name)
    try:
        with lbd3.Image(client_ctx, args.name, open=True) as image:
            image.discard(args.offset, args.len)
    except Exception as e:
        LAVA_BLOCK_LOG.error("discard lava block %s failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('discard lava block %s success', args.name)


def lava_snap_create(args, client_ctx):
    LAVA_BLOCK_LOG.info("create lava snap name: %s from %s, list id: %lu", args.name, args.image, args.list)
    try:
        with lbd3.Image(client_ctx, args.image) as image:
                image.create_snap(args.name, args.list)
    except Exception as e:
        LAVA_BLOCK_LOG.error("create lava snap %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("create lava snap %s success", args.name)

def lava_snap_delete(args, client_ctx):
    LAVA_BLOCK_LOG.info("delete lava snap name: %s from %s, list id: %lu", args.name, args.image, args.list)
    try:
        with lbd3.Image(client_ctx, args.image) as image:
            image.remove_snap(args.name, args.list)
    except Exception as e:
        LAVA_BLOCK_LOG.error("delete lava snap %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("delete lava snap name %s success", args.name)

def lava_snap_rollback(args, client_ctx):
    LAVA_BLOCK_LOG.info("rollback lava snap name: %s from %s, list id: %lu", args.name, args.image, args.list)
    try:
        with lbd3.Image(client_ctx, args.image) as image:
            rollback_info = image.rollback_snap(args.name, args.list, args.action, args.sync)
    except Exception as e:
        LAVA_BLOCK_LOG.error("rollback to lava snap %s error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("rollback to lava snap %s success", args.name)
    return eval(rollback_info)

def lava_snap_rename(args, client_ctx):
    LAVA_BLOCK_LOG.info("rename lava snap name: %s to %s, list id: %lu, block name: %s", args.src_name, args.name, args.list, args.image)
    try:
        with lbd3.Image(client_ctx, args.image) as image:
            image.rename_snap(args.src_name, args.name, args.list)
    except Exception as e:
        LAVA_BLOCK_LOG.error("rename lava snap %s error", args.src_name)
        raise e
    LAVA_BLOCK_LOG.info("rename lava snap %s success", args.src_name)

def lava_get_snap_capacity(args, client_ctx):
    LAVA_BLOCK_LOG.info("get lava block name: %s snap capacity, list id: %lu", args.name, args.list)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
                capacity_info = image.get_snap_capacity(args.list, args.sync)
    except Exception as e:
        LAVA_BLOCK_LOG.error("get lava block %s snap capacity error", args.name)
        raise e
    LAVA_BLOCK_LOG.info("get lava block %s snap capacity success", args.name)
    return eval(capacity_info)

def lava_snap_query_info(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava snap name: %s info, list id: %lu, block name: %s", args.name, args.list, args.image)
    try:
        with lbd3.Image(client_ctx, args.image) as image:
            snap_info = image.get_snap_info(args.name, args.list)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava snap %s info failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('query lava snap %s info success', args.name)
    return eval(snap_info)

def lava_snap_query_count(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava block name: %s snap count, list id: %lu", args.name, args.list)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
                count = image.count_snap(args.list)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava block %s snap count failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('query lava block %s snap count success', args.name)
    return eval(count)

def lava_snap_query_list(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava block name: %s snap list, list id: %lu", args.name, args.list)
    try:
        with lbd3.Image(client_ctx, args.name) as image:
                snap_list = image.list_snap(args.list, args.start, args.num)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava block %s snap list failed", args.name)
        raise e
    LAVA_BLOCK_LOG.info('query lava block %s snap list success', args.name)
    return eval(snap_list)

def lava_get_pool_capacity(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava pool capacity by pool id: %d", args.pool_id)
    try:
        capacity_info = lbd3.LBD().get_pool_capacity(client_ctx, args.pool_id)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava pool %d capacity failed", args.pool_id)
        raise e
    LAVA_BLOCK_LOG.info("query lava pool %d capacity success", args.pool_id)
    return eval(capacity_info)

def lava_get_pool_list(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava pool list")
    try:
        pool_list = lbd3.LBD().list_pool(client_ctx)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava pool list failed")
        raise e
    LAVA_BLOCK_LOG.info("query lava pool list success")
    return eval(pool_list)

def lava_get_cluster_capacity(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava cluster capacity")
    try:
        capacity_info = lbd3.LBD().get_cluster_capacity(client_ctx)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava cluster capacity failed")
        raise e
    LAVA_BLOCK_LOG.info("query lava cluster capacity success")
    return eval(capacity_info)

def lava_get_cluster_id(args, client_ctx):
    LAVA_BLOCK_LOG.info("query lava cluster id")
    try:
        capacity_id = lbd3.LBD().get_cluster_id(client_ctx)
    except Exception as e:
        LAVA_BLOCK_LOG.error("query lava cluster id failed")
        raise e
    LAVA_BLOCK_LOG.info("query lava cluster id success")
    return eval(capacity_id)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Lava Block Client Commands', usage='%(prog)s [options]')
    subparser = parser.add_subparsers(title='Available Subcommand', dest='cmd', metavar='')

    # # env probe: query internal status
    envparser = subparser.add_parser('env', help='lava block internal command')
    envsubparser = envparser.add_subparsers(title='lava block internal command')

    p = envsubparser.add_parser('probe', help='get lava internal status')
    p.set_defaults(func=lava_block_env_probe)

    imageparser = subparser.add_parser('image', help='lava block image command')
    imagesubparser = imageparser.add_subparsers(title='image command')

    # create lava block
    p = imagesubparser.add_parser('create', help='create lava block')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--uuid', help='lava bdev uuid')
    p.add_argument('--size', help='size of create lava block in MB', required=True, type=int)
    p.add_argument('--pool', help='pool id', type=int, default=-1)
    p.set_defaults(func=lava_block_create)

    # remove lava block
    p = imagesubparser.add_parser('delete', help='delete lava block')
    p.add_argument('name', help='delete lava bdev name')
    p.set_defaults(func=lava_block_delete)

    # resize lava block
    p = imagesubparser.add_parser('resize', help='resize lava block')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--size', help='lava bdev new size in MB', required=True, type=int)
    p.set_defaults(func=lava_block_resize)

    # rename lava block
    p = imagesubparser.add_parser('rename', help='rename lava block')
    p.add_argument('src_name', help='lava bdev source name')
    p.add_argument('--name', help='lava block new name', required=True)
    p.set_defaults(func=lava_block_rename)

    # set lava block qos
    p = imagesubparser.add_parser('set_qos', help='set lava block qos')
    p.add_argument('name', help='set qos lava block name')
    p.add_argument('--iops', help='set iops qos', required=False, type=int)
    p.add_argument('--bw', help='set bw qos kbytes', required=False, type=int)
    p.set_defaults(func=lava_block_set_qos)

    # set lava block io priority
    p = imagesubparser.add_parser('set_priority', help='set lava block priority')
    p.add_argument('name', help='lava block name')
    p.add_argument('--write', help='set write priority, 0-9', required=False, type=int)
    p.add_argument('--read', help='set read priority, 0-9', required=False, type=int)
    p.set_defaults(func=lava_block_set_priority)

    # clone lava block
    p = imagesubparser.add_parser('clone', help='clone lava block')
    p.add_argument('name', help='clone name')
    p.add_argument('--image', help='lava bdev name', required=True)
    p.add_argument('--snap', help='lava snap', required=True)
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.add_argument('--pool', help='pool id', type=int, default=-1)
    p.set_defaults(func=lava_block_clone)

    # get lava block clone depth
    p = imagesubparser.add_parser('get_depth', help='get lava block clone depth')
    p.add_argument('name', help='lava cloned bdev name')
    p.set_defaults(func=lava_block_get_clone_depth)

    # flatten lava block
    p = imagesubparser.add_parser('flatten', help='flatten lava block')
    p.add_argument('name', help='flatten lava bdev name')
    p.set_defaults(func=lava_block_flatten)

    # import lava block
    p = imagesubparser.add_parser('import', help='import lava block')
    p.add_argument('name', help='import block name')
    p.add_argument('--path', help='import file path', required=True)
    p.add_argument('--offset', help='offset of bdev', type=int, default=0)
    p.add_argument('--chunksize', help='write data size, size >= 4K, default size is 4M', type=int, default=4194304)
    p.set_defaults(func=lava_block_import)

    # export lava block
    p = imagesubparser.add_parser('export', help='export lava block')
    p.add_argument('name', help='export block name')
    p.add_argument('--path', help='export file path', required=True)
    p.add_argument('--offset', help='offset of bdev', type=int, default=0)
    p.add_argument('--len', help='length of bdev', type=int, default=0)
    p.add_argument('--chunksize', help='write data size, size >= 4K, default size is 4M', type=int, default=4194304)
    p.set_defaults(func=lava_block_export)

    p = imagesubparser.add_parser('snap_diff', help='check lava snap diff')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--src', help='lava src snap')
    p.add_argument('--dst', help='lava dst snap')
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.add_argument('--offset', help='offset of bdev', required=True, type=int)
    p.add_argument('--len', help='check lava snap diff length', required=True, type=int)
    p.add_argument('--bitunit', help='a bit represent data size, default size is 4M', type=int, default=4194304)
    p.set_defaults(func=lava_block_snap_diff)

    # query lava block numbers
    p = imagesubparser.add_parser('query', help='query lava block')
    p.add_argument('name', help='lava bdev name')
    p.set_defaults(func=lava_block_query_info)

    # query lava block's parent block info
    p = imagesubparser.add_parser('parent_info', help='query block\'s parent block info')
    p.add_argument('name', help='lava bdev name')
    p.set_defaults(func=lava_block_query_parent_info)

    # list lava block
    p = imagesubparser.add_parser('list', help='list all lava block')
    p.add_argument('--start', help='start name of image list', required=False)
    p.add_argument('--num', help='number of image name list', type=int, default=30)
    p.set_defaults(func=lava_block_list)

    # query lava block's children blocks
    p = imagesubparser.add_parser('list_children', help='list block\'s children blocks')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--snap', help='lava snap', required=True)
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.add_argument('--pool', help='pool id', type=int, default=-1)
    p.set_defaults(func=lava_block_list_children)

    # query lava block count
    p = imagesubparser.add_parser('count', help='count all lava block')
    p.set_defaults(func=lava_block_count)

    # reset lava block's status
    p = imagesubparser.add_parser('reset_status', help='reset block\'s status')
    p.add_argument('name', help='lava bdev name')
    p.set_defaults(func=lava_block_reset_status)

    p = imagesubparser.add_parser('discard', help='discard image data')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--offset', help='offset of bdev', required=True, type=int)
    p.add_argument('--len', help='discard image length', required=True, type=int)
    p.set_defaults(func=lava_block_discard)

    snapparser = subparser.add_parser('snap', help='lava block snap command')
    snapsubparser = snapparser.add_subparsers(title='snap command')

    # create lava snap
    p = snapsubparser.add_parser('create', help='create lava snap')
    p.add_argument('name', help='lava snap name')
    p.add_argument('--image', help='lava bdev name', required=True)
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.set_defaults(func=lava_snap_create)

    # delete lava snap
    p = snapsubparser.add_parser('delete', help='delete lava snap')
    p.add_argument('name', help='lava snap name')
    p.add_argument('--image', help='lava bdev name', required=True)
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.set_defaults(func=lava_snap_delete)

    # rollback lava snap
    p = snapsubparser.add_parser('rollback', help='rollback lava snap')
    p.add_argument('name', help='lava snap name')
    p.add_argument('--image', help='lava bdev name', required=True)
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.add_argument('--action', help='rollback or get rollback info', required=True, type=int)
    p.add_argument('--sync', help='sync or async', required=True, type=int)
    p.set_defaults(func=lava_snap_rollback)

    # rename lava snap
    p = snapsubparser.add_parser('rename', help='rename lava snap')
    p.add_argument('src_name', help='lava snap source name')
    p.add_argument('--image', help='lava bdev name', required=True)
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.add_argument('--name', help='lava snap new name', required=True)
    p.set_defaults(func=lava_snap_rename)

    # get lava snap capacity
    p = snapsubparser.add_parser('capacity', help='get lava snap capacity')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.add_argument('--sync', help='sync or async', required=True, type=int)
    p.set_defaults(func=lava_get_snap_capacity)

    # query lava snap info
    p = snapsubparser.add_parser('query', help='query lava snap info')
    p.add_argument('name', help='lava snap name')
    p.add_argument('--image', help='lava bdev name', required=True)
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.set_defaults(func=lava_snap_query_info)

    # query lava snap count
    p = snapsubparser.add_parser('count', help='query lava snap count')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.set_defaults(func=lava_snap_query_count)

    # query lava snap list
    p = snapsubparser.add_parser('list', help='list lava snap name')
    p.add_argument('name', help='lava bdev name')
    p.add_argument('--list', help='lava snap list id', required=True, type=int)
    p.add_argument('--start', help='start name of snap list', required=False)
    p.add_argument('--num', help='number of image name list', type=int, default=30)
    p.set_defaults(func=lava_snap_query_list)

    poolparser = subparser.add_parser('pool', help='lava block pool command')
    poolsubparser = poolparser.add_subparsers(title='pool command')

    # get lava pool capacity
    p = poolsubparser.add_parser('capacity', help='get lava pool capacity')
    p.add_argument('--pool_id', help='pool id', type=int, default=1)
    p.set_defaults(func=lava_get_pool_capacity)

    p = poolsubparser.add_parser('list', help='get lava pool list')
    p.set_defaults(func=lava_get_pool_list)

    clusterparser = subparser.add_parser('cluster', help='lava block cluster command')
    clustersubparser = clusterparser.add_subparsers(title='cluster command')

    # get lava pool capacity
    p = clustersubparser.add_parser('capacity', help='get lava cluster capacity')
    p.set_defaults(func=lava_get_cluster_capacity)

    p = clustersubparser.add_parser('id', help='get lava cluster id')
    p.set_defaults(func=lava_get_cluster_id)

    args = parser.parse_args()

    # if user didn't input function name, print help info
    if sys.stdin.isatty() and not hasattr(args, 'func'):
        # No arguments and no data piped through stdin
        parser.print_help()
        exit(1)
    # lock configure file to guarantee command would be executed serially
    # call function
    try:
        lbd3.ClientConfig().init_process()
        client_ctx = lbd3.ClientConfig().connect()
    except Exception as e:
        cmd_response(1, e.strerror)
        lbd3.ClientConfig().clear_process()
        exit(1)

    try:
        output = args.func(args, client_ctx)
    except Exception as e:
        cmd_response(1, e.strerror)
        exit(1)
    finally:
        client_ctx.rel_close()
        lbd3.ClientConfig().clear_process()
    if output:
        cmd_response(0, output)
    else:
        cmd_response(0, 'success')
    exit(0)
