github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/cmd/puppeth/wizard_netstats.go (about)

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