github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/bin/pfs_stat (about)

     1  #!/usr/bin/env python
     2  
     3  import json
     4  import os.path
     5  import re
     6  import sys
     7  import time
     8  import argparse
     9  import requests
    10  import subprocess
    11  
    12  # TODO:
    13  # * humanize stats if humanize is installed
    14  # * better print formatting; use tables if appropriate module is installed
    15  
    16  CONFIG_FILE_PATHS = [
    17      "/opt/ss/etc/proxyfs.conf",
    18      "/etc/proxyfsd/saioproxyfsd0.conf"
    19  ]
    20  
    21  PFSCONFJSON_PATHS = [
    22      "/opt/ss/bin/pfsconfjson",
    23      "/home/swift/code/ProxyFS/bin/pfsconfjson"
    24  ]
    25  
    26  DEFAULT_CONFIG = {
    27      "hostname": "localhost",
    28      "port": 15346
    29  }
    30  
    31  
    32  def find_config_file_path():
    33      for file_path in CONFIG_FILE_PATHS:
    34          if os.path.isfile(file_path):
    35              return file_path
    36      return None
    37  
    38  
    39  def get_values_to_return(provided_hostname, provided_port, calculated_hostname,
    40                           calculated_port):
    41      if provided_hostname is not None:
    42          return_hostname = provided_hostname
    43      else:
    44          return_hostname = calculated_hostname
    45  
    46      if provided_port is not None:
    47          return_port = provided_port
    48      else:
    49          return_port = calculated_port
    50  
    51      return return_hostname, return_port
    52  
    53  
    54  def get_pfsconfjson_path():
    55      try:
    56          path = subprocess.check_output("which pfsconfjsons",
    57                                         stderr=subprocess.PIPE, shell=True)
    58          return path.strip()
    59      except:
    60          pass
    61  
    62      for file_path in PFSCONFJSON_PATHS:
    63          if os.path.isfile(file_path):
    64              return file_path
    65  
    66      try:
    67          paths = subprocess.check_output("find / -iname 'pfsconfjson'",
    68                                          shell=True,
    69                                          stderr=subprocess.PIPE).splitlines()
    70      except subprocess.CalledProcessError as ex:
    71          paths = [line for line in ex.output.splitlines() if
    72                   "Permission denied" not in line]
    73      finally:
    74          if len(paths) > 0:
    75              return paths[0].strip()
    76  
    77      return None
    78  
    79  
    80  def get_config_hostname_and_port(config_file_path, pfsconfjson_path):
    81      pfsconfjson_call = "{} {}".format(pfsconfjson_path, config_file_path)
    82      config_json_str = subprocess.check_output(pfsconfjson_call, shell=True)
    83      config = json.loads(config_json_str)
    84      whoami = config["Cluster"]["WhoAmI"][0]
    85      hostname = config[whoami]["PrivateIPAddr"][0]
    86      port = int(config["HTTPServer"]["TCPPort"][0])
    87      return hostname, port
    88  
    89  
    90  def get_hostname_and_port(provided_hostname, provided_port):
    91      if provided_hostname is not None and provided_port is not None:
    92          return provided_hostname, provided_port
    93  
    94      config_file_path = find_config_file_path()
    95      if config_file_path is None:
    96          return get_values_to_return(provided_hostname, provided_port,
    97                                      DEFAULT_CONFIG["hostname"],
    98                                      DEFAULT_CONFIG["port"])
    99  
   100      pfsconfjson_path = get_pfsconfjson_path()
   101      if pfsconfjson_path is None:
   102          return get_values_to_return(provided_hostname, provided_port,
   103                                      DEFAULT_CONFIG["hostname"],
   104                                      DEFAULT_CONFIG["port"])
   105  
   106      try:
   107          config_hostname, config_port = get_config_hostname_and_port(
   108              config_file_path, pfsconfjson_path)
   109          return get_values_to_return(provided_hostname, provided_port,
   110                                      config_hostname, config_port)
   111      except:
   112          return get_values_to_return(provided_hostname, provided_port,
   113                                      DEFAULT_CONFIG["hostname"],
   114                                      DEFAULT_CONFIG["port"])
   115  
   116  
   117  def print_stats(s, exclude=[], show_all=False, diff=True):
   118      exclude_re = None
   119      if exclude:
   120          exclude_re = '|'.join(exclude)
   121  
   122      for k in sorted(s.keys()):
   123          # Do not display the line if it is in an excluded category
   124          # or if we're in diff mode and it is unchanged.
   125          # Unless we specified -v; in that case, show everything.
   126          hide = re.match(exclude_re, k)
   127          hide = hide or (diff and not s[k])
   128          if show_all or not hide:
   129              print("{: <90} {: >20}".format(k, s[k]))
   130  
   131  
   132  def stat_diff(s1, s2):
   133      diff = {}
   134      for k in s2:
   135          if k not in s1:
   136              continue
   137          diff[k] = s2[k] - s1[k]
   138  
   139      return diff
   140  
   141  
   142  def parse_stats(text):
   143      stats = {}
   144      for l in text.split("\n"):
   145          if not l:
   146              continue
   147  
   148          # Are stats guaranteed to be positive integers?
   149          match = re.match("(\S+)\s+(\d+)\s*$", l)
   150          if match:
   151              stats[match.group(1)] = int(match.group(2))
   152          else:
   153              sys.stderr.write("WARNING: unparsed line: [%s]\n" % l)
   154  
   155      return stats
   156  
   157  
   158  def get_stats_from_proxyfsd(hostname, port):
   159      r = requests.get("http://%s:%d/metrics" % (hostname, port))
   160      r.raise_for_status()
   161      stats = r.json()
   162      stats['timestamp'] = int(time.time())
   163      return stats
   164  
   165  
   166  def read_stats_from_file(fn):
   167      with open(fn) as f:
   168          text = f.read()
   169          stats = parse_stats(text)
   170          return stats
   171  
   172  
   173  if __name__ == '__main__':
   174      parser = argparse.ArgumentParser(description='Show Proxyfs stats')
   175      parser.add_argument('--verbose', '-v', action='store_true',
   176                          help='verbose output')
   177      parser.add_argument('--hostname', '-H', type=str, default=None,
   178                          help='Hostname of proxyfs stat server (probably localhost)')
   179      parser.add_argument('--port', '-p', type=int, default=None,
   180                          help='Port used by proxyfs stat server')
   181      parser.add_argument('--file', '-f', type=str,
   182                          help='Read starting stats from a file')
   183      parser.add_argument('cmd', type=str, nargs=argparse.REMAINDER, help='command to run')
   184  
   185      args = parser.parse_args()
   186  
   187      excludes = ['go_runtime', 'logging_level']
   188  
   189      hostname, port = get_hostname_and_port(args.hostname, args.port)
   190  
   191      if args.cmd or args.file:
   192          start_stats = None
   193          if args.file:
   194              start_stats = read_stats_from_file(args.file)
   195          else:
   196              start_stats = get_stats_from_proxyfsd(hostname, port)
   197  
   198          if args.cmd:
   199              subprocess.call(args.cmd)
   200  
   201          end_stats = get_stats_from_proxyfsd(hostname, port)
   202          diff = stat_diff(start_stats, end_stats)
   203  
   204          if args.cmd:
   205              print("\n--")
   206          print_stats(diff, exclude=excludes, show_all=args.verbose)
   207      else:
   208          stats = get_stats_from_proxyfsd(hostname, port)
   209          print_stats(stats, diff=False, exclude=excludes, show_all=args.verbose)