#!/usr/bin/env python

"""
Utility to display Lustre cfs_hash tables
Copyright (c) 2019 Cray Inc. All Rights Reserved.
"""

from pykdump.API import *
import argparse

import lustrelib as ll

description_short = 'Displays summary of cfs_hash tables'

CFS_HASH_THETA_BITS = 10

def cfs_hash_cur_theta(hs):
    hs_cnt = readSU('atomic_t', hs.hs_count).counter
    return ((hs_cnt << CFS_HASH_THETA_BITS) >> hs.hs_cur_bits)

def cfs_hash_theta_int(theta):
    return (theta >> CFS_HASH_THETA_BITS)

def cfs_hash_theta_frac(theta):
    frac = ((theta * 1000) >> CFS_HASH_THETA_BITS) - \
           (cfs_hash_theta_int(theta) * 1000)
    return frac

def cfs_hash_format_theta(theta):
    val = str(cfs_hash_theta_int(theta)) + \
          "." + \
          str(cfs_hash_theta_frac(theta))
    return val

def print_theta(hs):
    theta = cfs_hash_cur_theta(hs)
    print("Theta: %d %s" % (theta, cfs_hash_format_theta(theta)))

def print_thetas(name, hashtable):
    hs = readSU('struct cfs_hash', hashtable)
    if hs:
        print_theta(hs)

def print_separator(count):
    s = ""
    for idx in xrange(count):
        s += "="
    print(s)

def print_hash_labels():
    print("%-15s %-17s\t %-5s %-5s %-5s %-5s %-5s %-5s %-5s " \
          "%-5s %-5s %-5s %-5s %-11s %-11s %-11s %-5s" % \
          ("name", "cfs_hash", "cnt", "rhcnt", "xtr", "cur", "min", "max", "rhash", \
           "bkt", "nbkt", "nhlst", "flags", "theta", "minT", "maxT", "bktsz"))

def print_hash_summary(name, hashtable):
    hs = readSU('struct cfs_hash', hashtable)
    if hs:
        hs_cnt = readSU('atomic_t', hs.hs_count).counter
        hs_ref = readSU('atomic_t', hs.hs_refcount).counter
        print("%-15s %-17x\t %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5d %-5x %-11s %-11s %-11s %-5d" % \
              (name, (Addr(hs)), \
               readSU('atomic_t', hs.hs_count).counter, \
               hs.hs_rehash_count, \
               hs.hs_extra_bytes, \
               hs.hs_cur_bits, \
               hs.hs_min_bits, \
               hs.hs_max_bits, \
               hs.hs_rehash_bits, \
               hs.hs_bkt_bits, \
               ll.CFS_HASH_NBKT(hs), \
               ll.CFS_HASH_BKT_NHLIST(hs), \
               hs.hs_flags, \
               cfs_hash_format_theta(cfs_hash_cur_theta(hs)), \
               cfs_hash_format_theta(hs.hs_min_theta), \
               cfs_hash_format_theta(hs.hs_max_theta), \
               ll.cfs_hash_bucket_size(hs)))
    else:
        print("%-15s %-17x" % \
              (name, (Addr(hs))))

def obd_print_export_hashes(obd, exp_list, fld):
    print("\nExport list head %x %s" % (exp_list, fld))
    for exp in readSUListFromHead(exp_list, fld, 'struct obd_export'):
        print_hash_summary('exp_lock', exp.exp_lock_hash)
        print_hash_summary('exp_flock', exp.exp_flock_hash)

def obd_print_one_device_hashes(obd):
    try:
        nm = ll.obd2str(obd)
    except Exception as e:
        return 1

    print("obd_device %-17x %-22s" % (Addr(obd), ll.obd2str(obd)))
    print_hash_labels()

    print_hash_summary("uuid", obd.obd_uuid_hash)
    print_hash_summary("nid", obd.obd_nid_hash)
    print_hash_summary("nid_stats", obd.obd_nid_stats_hash)

    if "clilov" in nm:
        print_hash_summary("lov_pools", obd.u.lov.lov_pools_hash_body)
    elif "clilmv" in nm:
        pass
    else:
        print_hash_summary("cl_quota0", obd.u.cli.cl_quota_hash[0])
        print_hash_summary("cl_quota1", obd.u.cli.cl_quota_hash[1])

#    obd_print_export_hashes(obd, obd.obd_exports, 'exp_obd_chain')
#    obd_print_export_hashes(obd, obd.obd_exports_timed, 'exp_obd_chain_timed')
    print("")
    return 0

def obd_devs_hash():
    devices = readSymbol('obd_devs')

    for obd in devices:
       if not obd_print_one_device_hashes(obd) == 0:
           break
    print_separator(150)

def ldlm_print_ns_hashes(ns, type):
    ns_list = readSymbol(ns)
    print("\n%s namespaces-resources" % type)
    print_hash_labels()
    for ns in readSUListFromHead(ns_list, 'ns_list_chain', 'struct ldlm_namespace'):
        nm = ll.obd2str(ns.ns_obd)[0:20]
        print_hash_summary(nm, ns.ns_rs_hash)

def ldlm_namespaces_hash():
    ldlm_print_ns_hashes('ldlm_cli_active_namespace_list', "Client")
    ldlm_print_ns_hashes('ldlm_cli_inactive_namespace_list', "Inactive")
    ldlm_print_ns_hashes('ldlm_srv_namespace_list', "Server")

def lu_sites_hashes():
    lu_sites = readSymbol('lu_sites')
    print_hash_labels()
    for site in readSUListFromHead(lu_sites, 'ls_linkage', 'struct lu_site'):
        print_hash_summary("lu_site_vvp", site.ls_obj_hash)
    print("")


def global_hashes():
    print_hash_labels()
    print_hash_summary("conn_hash", readSymbol('conn_hash'))
    if symbol_exists('jobid_hash'):
        print_hash_summary("jobid_hash", readSymbol('jobid_hash'))
    if symbol_exists('cl_env_hash'):
        print_hash_summary("cl_env_hash", readSymbol('cl_env_hash'))
    print("")

if __name__ == "__main__":
    description = "Displays summary of hash tables in 'obd_devs'"
    parser = argparse.ArgumentParser(description=description)
    args = parser.parse_args()

    global_hashes()
    lu_sites_hashes()
    obd_devs_hash()
    ldlm_namespaces_hash()
