From 706a1e552c96bf75c60844c1dc28fc83778795fc Mon Sep 17 00:00:00 2001 From: Péter Szilágyi Date: Tue, 11 Apr 2017 02:25:53 +0300 Subject: cmd/puppeth: your Ethereum private network manager (#13854) --- cmd/puppeth/wizard_netstats.go | 235 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 cmd/puppeth/wizard_netstats.go (limited to 'cmd/puppeth/wizard_netstats.go') diff --git a/cmd/puppeth/wizard_netstats.go b/cmd/puppeth/wizard_netstats.go new file mode 100644 index 000000000..c2a933a55 --- /dev/null +++ b/cmd/puppeth/wizard_netstats.go @@ -0,0 +1,235 @@ +// 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 . + +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) + + for _, server := range w.conf.Servers { + 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 { + conn, err := dial(server) + 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() + } +} -- cgit