github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/cmd/puppeth/wizard_netstats.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package main
    13  
    14  import (
    15  	"encoding/json"
    16  	"os"
    17  	"sort"
    18  	"strings"
    19  	"sync"
    20  
    21  	"github.com/Sberex/go-sberex/core"
    22  	"github.com/Sberex/go-sberex/log"
    23  	"github.com/olekukonko/tablewriter"
    24  )
    25  
    26  // networkStats verifies the status of network components and generates a protip
    27  // configuration set to give users hints on how to do various tasks.
    28  func (w *wizard) networkStats() {
    29  	if len(w.servers) == 0 {
    30  		log.Info("No remote machines to gather stats from")
    31  		return
    32  	}
    33  	// Clear out some previous configs to refill from current scan
    34  	w.conf.ethstats = ""
    35  	w.conf.bootnodes = w.conf.bootnodes[:0]
    36  
    37  	// Iterate over all the specified hosts and check their status
    38  	var pend sync.WaitGroup
    39  
    40  	stats := make(serverStats)
    41  	for server, pubkey := range w.conf.Servers {
    42  		pend.Add(1)
    43  
    44  		// Gather the service stats for each server concurrently
    45  		go func(server string, pubkey []byte) {
    46  			defer pend.Done()
    47  
    48  			stat := w.gatherStats(server, pubkey, w.servers[server])
    49  
    50  			// All status checks complete, report and check next server
    51  			w.lock.Lock()
    52  			defer w.lock.Unlock()
    53  
    54  			delete(w.services, server)
    55  			for service := range stat.services {
    56  				w.services[server] = append(w.services[server], service)
    57  			}
    58  			stats[server] = stat
    59  		}(server, pubkey)
    60  	}
    61  	pend.Wait()
    62  
    63  	// Print any collected stats and return
    64  	stats.render()
    65  }
    66  
    67  // gatherStats gathers service statistics for a particular remote server.
    68  func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *serverStat {
    69  	// Gather some global stats to feed into the wizard
    70  	var (
    71  		genesis   string
    72  		ethstats  string
    73  		bootnodes []string
    74  	)
    75  	// Ensure a valid SSH connection to the remote server
    76  	logger := log.New("server", server)
    77  	logger.Info("Starting remote server health-check")
    78  
    79  	stat := &serverStat{
    80  		address:  client.address,
    81  		services: make(map[string]map[string]string),
    82  	}
    83  	if client == nil {
    84  		conn, err := dial(server, pubkey)
    85  		if err != nil {
    86  			logger.Error("Failed to establish remote connection", "err", err)
    87  			stat.failure = err.Error()
    88  			return stat
    89  		}
    90  		client = conn
    91  	}
    92  	// Client connected one way or another, run health-checks
    93  	logger.Debug("Checking for nginx availability")
    94  	if infos, err := checkNginx(client, w.network); err != nil {
    95  		if err != ErrServiceUnknown {
    96  			stat.services["nginx"] = map[string]string{"offline": err.Error()}
    97  		}
    98  	} else {
    99  		stat.services["nginx"] = infos.Report()
   100  	}
   101  	logger.Debug("Checking for ethstats availability")
   102  	if infos, err := checkEthstats(client, w.network); err != nil {
   103  		if err != ErrServiceUnknown {
   104  			stat.services["ethstats"] = map[string]string{"offline": err.Error()}
   105  		}
   106  	} else {
   107  		stat.services["ethstats"] = infos.Report()
   108  		ethstats = infos.config
   109  	}
   110  	logger.Debug("Checking for bootnode availability")
   111  	if infos, err := checkNode(client, w.network, true); err != nil {
   112  		if err != ErrServiceUnknown {
   113  			stat.services["bootnode"] = map[string]string{"offline": err.Error()}
   114  		}
   115  	} else {
   116  		stat.services["bootnode"] = infos.Report()
   117  
   118  		genesis = string(infos.genesis)
   119  		bootnodes = append(bootnodes, infos.enode)
   120  	}
   121  	logger.Debug("Checking for sealnode availability")
   122  	if infos, err := checkNode(client, w.network, false); err != nil {
   123  		if err != ErrServiceUnknown {
   124  			stat.services["sealnode"] = map[string]string{"offline": err.Error()}
   125  		}
   126  	} else {
   127  		stat.services["sealnode"] = infos.Report()
   128  		genesis = string(infos.genesis)
   129  	}
   130  	logger.Debug("Checking for explorer availability")
   131  	if infos, err := checkExplorer(client, w.network); err != nil {
   132  		if err != ErrServiceUnknown {
   133  			stat.services["explorer"] = map[string]string{"offline": err.Error()}
   134  		}
   135  	} else {
   136  		stat.services["explorer"] = infos.Report()
   137  	}
   138  	logger.Debug("Checking for wallet availability")
   139  	if infos, err := checkWallet(client, w.network); err != nil {
   140  		if err != ErrServiceUnknown {
   141  			stat.services["wallet"] = map[string]string{"offline": err.Error()}
   142  		}
   143  	} else {
   144  		stat.services["wallet"] = infos.Report()
   145  	}
   146  	logger.Debug("Checking for faucet availability")
   147  	if infos, err := checkFaucet(client, w.network); err != nil {
   148  		if err != ErrServiceUnknown {
   149  			stat.services["faucet"] = map[string]string{"offline": err.Error()}
   150  		}
   151  	} else {
   152  		stat.services["faucet"] = infos.Report()
   153  	}
   154  	logger.Debug("Checking for dashboard availability")
   155  	if infos, err := checkDashboard(client, w.network); err != nil {
   156  		if err != ErrServiceUnknown {
   157  			stat.services["dashboard"] = map[string]string{"offline": err.Error()}
   158  		}
   159  	} else {
   160  		stat.services["dashboard"] = infos.Report()
   161  	}
   162  	// Feed and newly discovered information into the wizard
   163  	w.lock.Lock()
   164  	defer w.lock.Unlock()
   165  
   166  	if genesis != "" && w.conf.Genesis == nil {
   167  		g := new(core.Genesis)
   168  		if err := json.Unmarshal([]byte(genesis), g); err != nil {
   169  			log.Error("Failed to parse remote genesis", "err", err)
   170  		} else {
   171  			w.conf.Genesis = g
   172  		}
   173  	}
   174  	if ethstats != "" {
   175  		w.conf.ethstats = ethstats
   176  	}
   177  	w.conf.bootnodes = append(w.conf.bootnodes, bootnodes...)
   178  
   179  	return stat
   180  }
   181  
   182  // serverStat is a collection of service configuration parameters and health
   183  // check reports to print to the user.
   184  type serverStat struct {
   185  	address  string
   186  	failure  string
   187  	services map[string]map[string]string
   188  }
   189  
   190  // serverStats is a collection of server stats for multiple hosts.
   191  type serverStats map[string]*serverStat
   192  
   193  // render converts the gathered statistics into a user friendly tabular report
   194  // and prints it to the standard output.
   195  func (stats serverStats) render() {
   196  	// Start gathering service statistics and config parameters
   197  	table := tablewriter.NewWriter(os.Stdout)
   198  
   199  	table.SetHeader([]string{"Server", "Address", "Service", "Config", "Value"})
   200  	table.SetAlignment(tablewriter.ALIGN_LEFT)
   201  	table.SetColWidth(100)
   202  
   203  	// Find the longest lines for all columns for the hacked separator
   204  	separator := make([]string, 5)
   205  	for server, stat := range stats {
   206  		if len(server) > len(separator[0]) {
   207  			separator[0] = strings.Repeat("-", len(server))
   208  		}
   209  		if len(stat.address) > len(separator[1]) {
   210  			separator[1] = strings.Repeat("-", len(stat.address))
   211  		}
   212  		for service, configs := range stat.services {
   213  			if len(service) > len(separator[2]) {
   214  				separator[2] = strings.Repeat("-", len(service))
   215  			}
   216  			for config, value := range configs {
   217  				if len(config) > len(separator[3]) {
   218  					separator[3] = strings.Repeat("-", len(config))
   219  				}
   220  				if len(value) > len(separator[4]) {
   221  					separator[4] = strings.Repeat("-", len(value))
   222  				}
   223  			}
   224  		}
   225  	}
   226  	// Fill up the server report in alphabetical order
   227  	servers := make([]string, 0, len(stats))
   228  	for server := range stats {
   229  		servers = append(servers, server)
   230  	}
   231  	sort.Strings(servers)
   232  
   233  	for i, server := range servers {
   234  		// Add a separator between all servers
   235  		if i > 0 {
   236  			table.Append(separator)
   237  		}
   238  		// Fill up the service report in alphabetical order
   239  		services := make([]string, 0, len(stats[server].services))
   240  		for service := range stats[server].services {
   241  			services = append(services, service)
   242  		}
   243  		sort.Strings(services)
   244  
   245  		if len(services) == 0 {
   246  			table.Append([]string{server, stats[server].address, "", "", ""})
   247  		}
   248  		for j, service := range services {
   249  			// Add an empty line between all services
   250  			if j > 0 {
   251  				table.Append([]string{"", "", "", separator[3], separator[4]})
   252  			}
   253  			// Fill up the config report in alphabetical order
   254  			configs := make([]string, 0, len(stats[server].services[service]))
   255  			for service := range stats[server].services[service] {
   256  				configs = append(configs, service)
   257  			}
   258  			sort.Strings(configs)
   259  
   260  			for k, config := range configs {
   261  				switch {
   262  				case j == 0 && k == 0:
   263  					table.Append([]string{server, stats[server].address, service, config, stats[server].services[service][config]})
   264  				case k == 0:
   265  					table.Append([]string{"", "", service, config, stats[server].services[service][config]})
   266  				default:
   267  					table.Append([]string{"", "", "", config, stats[server].services[service][config]})
   268  				}
   269  			}
   270  		}
   271  	}
   272  	table.Render()
   273  }
   274  
   275  // protips contains a collection of network infos to report pro-tips
   276  // based on.
   277  type protips struct {
   278  	genesis   string
   279  	network   int64
   280  	bootFull  []string
   281  	bootLight []string
   282  	ethstats  string
   283  }