github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/bin/pfs_stat (about)

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