github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/internal/cli/bor_fingerprint.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"os/exec"
     7  	"strings"
     8  
     9  	"github.com/ethereum/go-ethereum/params"
    10  
    11  	"github.com/mitchellh/cli"
    12  	"github.com/shirou/gopsutil/cpu"
    13  	"github.com/shirou/gopsutil/disk"
    14  	"github.com/shirou/gopsutil/host"
    15  	"github.com/shirou/gopsutil/mem"
    16  )
    17  
    18  // VersionCommand is the command to show the version of the agent
    19  type FingerprintCommand struct {
    20  	UI cli.Ui
    21  }
    22  
    23  // MarkDown implements cli.MarkDown interface
    24  func (c *FingerprintCommand) MarkDown() string {
    25  	items := []string{
    26  		"# Fingerprint",
    27  		"Display the system fingerprint",
    28  	}
    29  
    30  	return strings.Join(items, "\n\n")
    31  }
    32  
    33  // Help implements the cli.Command interface
    34  func (c *FingerprintCommand) Help() string {
    35  	return `Usage: bor fingerprint
    36  
    37    Display the system fingerprint`
    38  }
    39  
    40  // Synopsis implements the cli.Command interface
    41  func (c *FingerprintCommand) Synopsis() string {
    42  	return "Display the system fingerprint"
    43  }
    44  
    45  func getCoresCount(cp []cpu.InfoStat) int {
    46  	cores := 0
    47  	for i := 0; i < len(cp); i++ {
    48  		cores += int(cp[i].Cores)
    49  	}
    50  
    51  	return cores
    52  }
    53  
    54  type MemoryDetails struct {
    55  	TotalMem float64 `json:"totalMem"`
    56  	FreeMem  float64 `json:"freeMem"`
    57  	UsedMem  float64 `json:"usedMem"`
    58  }
    59  
    60  type DiskDetails struct {
    61  	TotalDisk float64 `json:"totalDisk"`
    62  	FreeDisk  float64 `json:"freeDisk"`
    63  	UsedDisk  float64 `json:"usedDisk"`
    64  }
    65  
    66  type BorFingerprint struct {
    67  	CoresCount    int            `json:"coresCount"`
    68  	OsName        string         `json:"osName"`
    69  	OsVer         string         `json:"osVer"`
    70  	DiskDetails   *DiskDetails   `json:"diskDetails"`
    71  	MemoryDetails *MemoryDetails `json:"memoryDetails"`
    72  }
    73  
    74  func formatFingerprint(borFingerprint *BorFingerprint) string {
    75  	base := formatKV([]string{
    76  		fmt.Sprintf("Bor Version : %s", params.VersionWithMeta),
    77  		fmt.Sprintf("CPU : %d cores", borFingerprint.CoresCount),
    78  		fmt.Sprintf("OS : %s %s ", borFingerprint.OsName, borFingerprint.OsVer),
    79  		fmt.Sprintf("RAM :: total : %v GB, free : %v GB, used : %v GB", borFingerprint.MemoryDetails.TotalMem, borFingerprint.MemoryDetails.FreeMem, borFingerprint.MemoryDetails.UsedMem),
    80  		fmt.Sprintf("STORAGE :: total : %v GB, free : %v GB, used : %v GB", borFingerprint.DiskDetails.TotalDisk, borFingerprint.DiskDetails.FreeDisk, borFingerprint.DiskDetails.UsedDisk),
    81  	})
    82  
    83  	return base
    84  }
    85  
    86  func convertBytesToGB(bytesValue uint64) float64 {
    87  	return math.Floor(float64(bytesValue)/(1024*1024*1024)*100) / 100
    88  }
    89  
    90  // Checks if fio exists on the node
    91  func (c *FingerprintCommand) checkFio() error {
    92  	cmd := exec.Command("/bin/sh", "-c", "fio -v")
    93  
    94  	_, err := cmd.CombinedOutput()
    95  	if err != nil {
    96  		message := "\nFio package not installed. Install Fio for IOPS Benchmarking :\n\nDebianOS  :  'sudo apt-get update && sudo apt-get install fio -y'\nAWS AMI/CentOS  :  'sudo yum install fio -y'\nOracle LinuxOS  :  'sudo dnf install fio -y'\n"
    97  		c.UI.Output(message)
    98  
    99  		return err
   100  	}
   101  
   102  	return nil
   103  }
   104  
   105  // Run the IOPS benchmark for the node
   106  func (c *FingerprintCommand) benchmark() error {
   107  	var b []byte
   108  
   109  	err := c.checkFio()
   110  
   111  	if err != nil {
   112  		// Missing Fio is not a fatal error. A message will be logged in console when it is missing in "checkFio()".
   113  		return nil //nolint:nilerr
   114  	}
   115  
   116  	c.UI.Output("\nRunning a 10 second test...\n")
   117  
   118  	cmd := exec.Command("/bin/sh", "-c", "sudo fio --filename=/file --size=2GB --direct=1 --rw=randrw --bs=64k --ioengine=libaio --iodepth=64 --runtime=10 --numjobs=4 --time_based --group_reporting --name=throughput-test-job --eta-newline=1 | grep -e 'read:' -e 'write:' | awk '{print $1,$2}' ")
   119  
   120  	b, err = cmd.CombinedOutput()
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	out := string(b)
   126  	c.UI.Output(out)
   127  
   128  	return nil
   129  }
   130  
   131  // Run implements the cli.Command interface
   132  func (c *FingerprintCommand) Run(args []string) int {
   133  	v, err := mem.VirtualMemory()
   134  	if err != nil {
   135  		c.UI.Error(err.Error())
   136  		return 1
   137  	}
   138  
   139  	h, err := host.Info()
   140  	if err != nil {
   141  		c.UI.Error(err.Error())
   142  		return 1
   143  	}
   144  
   145  	cp, err := cpu.Info()
   146  	if err != nil {
   147  		c.UI.Error(err.Error())
   148  		return 1
   149  	}
   150  
   151  	d, err := disk.Usage("/")
   152  	if err != nil {
   153  		c.UI.Error(err.Error())
   154  		return 1
   155  	}
   156  
   157  	diskDetails := &DiskDetails{
   158  		TotalDisk: convertBytesToGB(d.Total),
   159  		FreeDisk:  convertBytesToGB(d.Free),
   160  		UsedDisk:  convertBytesToGB(d.Used),
   161  	}
   162  
   163  	memoryDetails := &MemoryDetails{
   164  		TotalMem: convertBytesToGB(v.Total),
   165  		FreeMem:  convertBytesToGB(v.Available),
   166  		UsedMem:  convertBytesToGB(v.Used),
   167  	}
   168  
   169  	borFingerprint := &BorFingerprint{
   170  		CoresCount:    getCoresCount(cp),
   171  		OsName:        h.OS,
   172  		OsVer:         h.Platform + " - " + h.PlatformVersion + " - " + h.KernelArch,
   173  		DiskDetails:   diskDetails,
   174  		MemoryDetails: memoryDetails,
   175  	}
   176  
   177  	c.UI.Output(formatFingerprint(borFingerprint))
   178  
   179  	if borFingerprint.OsName == "linux" {
   180  		err = c.benchmark()
   181  		if err != nil {
   182  			c.UI.Error(err.Error())
   183  			return 1
   184  		}
   185  	}
   186  
   187  	return 0
   188  }