2017-04-10 18:25:53 -05:00
|
|
|
// Copyright 2017 The go-ethereum Authors
|
|
|
|
// This file is part of go-ethereum.
|
|
|
|
//
|
|
|
|
// go-ethereum is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// go-ethereum is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/core"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
"github.com/olekukonko/tablewriter"
|
|
|
|
)
|
|
|
|
|
|
|
|
// networkStats verifies the status of network components and generates a protip
|
|
|
|
// configuration set to give users hints on how to do various tasks.
|
|
|
|
func (w *wizard) networkStats(tips bool) {
|
|
|
|
if len(w.servers) == 0 {
|
|
|
|
log.Error("No remote machines to gather stats from")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
protips := new(protips)
|
|
|
|
|
|
|
|
// Iterate over all the specified hosts and check their status
|
|
|
|
stats := tablewriter.NewWriter(os.Stdout)
|
|
|
|
stats.SetHeader([]string{"Server", "IP", "Status", "Service", "Details"})
|
|
|
|
stats.SetColWidth(128)
|
|
|
|
|
2017-05-03 02:09:34 -05:00
|
|
|
for server, pubkey := range w.conf.Servers {
|
2017-04-10 18:25:53 -05:00
|
|
|
client := w.servers[server]
|
|
|
|
logger := log.New("server", server)
|
|
|
|
logger.Info("Starting remote server health-check")
|
|
|
|
|
|
|
|
// If the server is not connected, try to connect again
|
|
|
|
if client == nil {
|
2017-05-03 02:09:34 -05:00
|
|
|
conn, err := dial(server, pubkey)
|
2017-04-10 18:25:53 -05:00
|
|
|
if err != nil {
|
|
|
|
logger.Error("Failed to establish remote connection", "err", err)
|
|
|
|
stats.Append([]string{server, "", err.Error(), "", ""})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client = conn
|
|
|
|
}
|
|
|
|
// Client connected one way or another, run health-checks
|
|
|
|
services := make(map[string]string)
|
|
|
|
logger.Debug("Checking for nginx availability")
|
|
|
|
if infos, err := checkNginx(client, w.network); err != nil {
|
|
|
|
if err != ErrServiceUnknown {
|
|
|
|
services["nginx"] = err.Error()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
services["nginx"] = infos.String()
|
|
|
|
}
|
|
|
|
logger.Debug("Checking for ethstats availability")
|
|
|
|
if infos, err := checkEthstats(client, w.network); err != nil {
|
|
|
|
if err != ErrServiceUnknown {
|
|
|
|
services["ethstats"] = err.Error()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
services["ethstats"] = infos.String()
|
|
|
|
protips.ethstats = infos.config
|
|
|
|
}
|
|
|
|
logger.Debug("Checking for bootnode availability")
|
|
|
|
if infos, err := checkNode(client, w.network, true); err != nil {
|
|
|
|
if err != ErrServiceUnknown {
|
|
|
|
services["bootnode"] = err.Error()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
services["bootnode"] = infos.String()
|
|
|
|
|
|
|
|
protips.genesis = string(infos.genesis)
|
|
|
|
protips.bootFull = append(protips.bootFull, infos.enodeFull)
|
|
|
|
if infos.enodeLight != "" {
|
|
|
|
protips.bootLight = append(protips.bootLight, infos.enodeLight)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logger.Debug("Checking for sealnode availability")
|
|
|
|
if infos, err := checkNode(client, w.network, false); err != nil {
|
|
|
|
if err != ErrServiceUnknown {
|
|
|
|
services["sealnode"] = err.Error()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
services["sealnode"] = infos.String()
|
|
|
|
protips.genesis = string(infos.genesis)
|
|
|
|
}
|
|
|
|
logger.Debug("Checking for faucet availability")
|
|
|
|
if infos, err := checkFaucet(client, w.network); err != nil {
|
|
|
|
if err != ErrServiceUnknown {
|
|
|
|
services["faucet"] = err.Error()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
services["faucet"] = infos.String()
|
|
|
|
}
|
|
|
|
logger.Debug("Checking for dashboard availability")
|
|
|
|
if infos, err := checkDashboard(client, w.network); err != nil {
|
|
|
|
if err != ErrServiceUnknown {
|
|
|
|
services["dashboard"] = err.Error()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
services["dashboard"] = infos.String()
|
|
|
|
}
|
|
|
|
// All status checks complete, report and check next server
|
|
|
|
delete(w.services, server)
|
|
|
|
for service := range services {
|
|
|
|
w.services[server] = append(w.services[server], service)
|
|
|
|
}
|
|
|
|
server, address := client.server, client.address
|
|
|
|
for service, status := range services {
|
|
|
|
stats.Append([]string{server, address, "online", service, status})
|
|
|
|
server, address = "", ""
|
|
|
|
}
|
|
|
|
if len(services) == 0 {
|
|
|
|
stats.Append([]string{server, address, "online", "", ""})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If a genesis block was found, load it into our configs
|
|
|
|
if protips.genesis != "" {
|
|
|
|
genesis := new(core.Genesis)
|
|
|
|
if err := json.Unmarshal([]byte(protips.genesis), genesis); err != nil {
|
|
|
|
log.Error("Failed to parse remote genesis", "err", err)
|
|
|
|
} else {
|
|
|
|
w.conf.genesis = genesis
|
|
|
|
protips.network = genesis.Config.ChainId.Int64()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if protips.ethstats != "" {
|
|
|
|
w.conf.ethstats = protips.ethstats
|
|
|
|
}
|
|
|
|
w.conf.bootFull = protips.bootFull
|
|
|
|
w.conf.bootLight = protips.bootLight
|
|
|
|
|
|
|
|
// Print any collected stats and return
|
|
|
|
if !tips {
|
|
|
|
stats.Render()
|
|
|
|
} else {
|
|
|
|
protips.print(w.network)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// protips contains a collection of network infos to report pro-tips
|
|
|
|
// based on.
|
|
|
|
type protips struct {
|
|
|
|
genesis string
|
|
|
|
network int64
|
|
|
|
bootFull []string
|
|
|
|
bootLight []string
|
|
|
|
ethstats string
|
|
|
|
}
|
|
|
|
|
|
|
|
// print analyzes the network information available and prints a collection of
|
|
|
|
// pro tips for the user's consideration.
|
|
|
|
func (p *protips) print(network string) {
|
|
|
|
// If a known genesis block is available, display it and prepend an init command
|
|
|
|
fullinit, lightinit := "", ""
|
|
|
|
if p.genesis != "" {
|
|
|
|
fullinit = fmt.Sprintf("geth --datadir=$HOME/.%s init %s.json && ", network, network)
|
|
|
|
lightinit = fmt.Sprintf("geth --datadir=$HOME/.%s --light init %s.json && ", network, network)
|
|
|
|
}
|
|
|
|
// If an ethstats server is available, add the ethstats flag
|
|
|
|
statsflag := ""
|
|
|
|
if p.ethstats != "" {
|
|
|
|
if strings.Contains(p.ethstats, " ") {
|
|
|
|
statsflag = fmt.Sprintf(` --ethstats="yournode:%s"`, p.ethstats)
|
|
|
|
} else {
|
|
|
|
statsflag = fmt.Sprintf(` --ethstats=yournode:%s`, p.ethstats)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If bootnodes have been specified, add the bootnode flag
|
|
|
|
bootflagFull := ""
|
|
|
|
if len(p.bootFull) > 0 {
|
|
|
|
bootflagFull = fmt.Sprintf(` --bootnodes %s`, strings.Join(p.bootFull, ","))
|
|
|
|
}
|
|
|
|
bootflagLight := ""
|
|
|
|
if len(p.bootLight) > 0 {
|
|
|
|
bootflagLight = fmt.Sprintf(` --bootnodes %s`, strings.Join(p.bootLight, ","))
|
|
|
|
}
|
|
|
|
// Assemble all the known pro-tips
|
|
|
|
var tasks, tips []string
|
|
|
|
|
|
|
|
tasks = append(tasks, "Run an archive node with historical data")
|
|
|
|
tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=1024%s%s", fullinit, p.network, network, statsflag, bootflagFull))
|
|
|
|
|
|
|
|
tasks = append(tasks, "Run a full node with recent data only")
|
|
|
|
tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=512 --fast%s%s", fullinit, p.network, network, statsflag, bootflagFull))
|
|
|
|
|
|
|
|
tasks = append(tasks, "Run a light node with on demand retrievals")
|
|
|
|
tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --light%s%s", lightinit, p.network, network, statsflag, bootflagLight))
|
|
|
|
|
|
|
|
tasks = append(tasks, "Run an embedded node with constrained memory")
|
|
|
|
tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=32 --light%s%s", lightinit, p.network, network, statsflag, bootflagLight))
|
|
|
|
|
|
|
|
// If the tips are short, display in a table
|
|
|
|
short := true
|
|
|
|
for _, tip := range tips {
|
|
|
|
if len(tip) > 100 {
|
|
|
|
short = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
if short {
|
|
|
|
howto := tablewriter.NewWriter(os.Stdout)
|
|
|
|
howto.SetHeader([]string{"Fun tasks for you", "Tips on how to"})
|
|
|
|
howto.SetColWidth(100)
|
|
|
|
|
|
|
|
for i := 0; i < len(tasks); i++ {
|
|
|
|
howto.Append([]string{tasks[i], tips[i]})
|
|
|
|
}
|
|
|
|
howto.Render()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Meh, tips got ugly, split into many lines
|
|
|
|
for i := 0; i < len(tasks); i++ {
|
|
|
|
fmt.Println(tasks[i])
|
|
|
|
fmt.Println(strings.Repeat("-", len(tasks[i])))
|
|
|
|
fmt.Println(tips[i])
|
|
|
|
fmt.Println()
|
|
|
|
fmt.Println()
|
|
|
|
}
|
|
|
|
}
|