implement NodeStatus API

Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
vyzo 2021-03-10 19:22:35 +02:00 committed by Jakub Sztandera
parent d7cd8bae2c
commit ed61642b3a
No known key found for this signature in database
GPG Key ID: 9A9AF56F8B3879BA
13 changed files with 262 additions and 6 deletions

View File

@ -664,6 +664,11 @@ type FullNode interface {
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error) //perm:write PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error) //perm:write
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) //perm:sign PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) //perm:sign
// MethodGroup: Node
// These methods are general node management and status commands
NodeStatus(ctx context.Context, inclChainStatus bool) (NodeStatus, error) //perm:read
// CreateBackup creates node backup onder the specified file name. The // CreateBackup creates node backup onder the specified file name. The
// method requires that the lotus daemon is running with the // method requires that the lotus daemon is running with the
// LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that // LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that

View File

@ -1692,6 +1692,21 @@ func (mr *MockFullNodeMockRecorder) NetPubsubScores(arg0 interface{}) *gomock.Ca
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetPubsubScores", reflect.TypeOf((*MockFullNode)(nil).NetPubsubScores), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetPubsubScores", reflect.TypeOf((*MockFullNode)(nil).NetPubsubScores), arg0)
} }
// NodeStatus mocks base method
func (m *MockFullNode) NodeStatus(arg0 context.Context, arg1 bool) (api.NodeStatus, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NodeStatus", arg0, arg1)
ret0, _ := ret[0].(api.NodeStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// NodeStatus indicates an expected call of NodeStatus
func (mr *MockFullNodeMockRecorder) NodeStatus(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeStatus", reflect.TypeOf((*MockFullNode)(nil).NodeStatus), arg0, arg1)
}
// PaychAllocateLane mocks base method // PaychAllocateLane mocks base method
func (m *MockFullNode) PaychAllocateLane(arg0 context.Context, arg1 address.Address) (uint64, error) { func (m *MockFullNode) PaychAllocateLane(arg0 context.Context, arg1 address.Address) (uint64, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -287,6 +287,8 @@ type FullNodeStruct struct {
MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) `perm:"sign"` MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) `perm:"sign"`
NodeStatus func(p0 context.Context, p1 bool) (NodeStatus, error) `perm:"read"`
PaychAllocateLane func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"sign"` PaychAllocateLane func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"sign"`
PaychAvailableFunds func(p0 context.Context, p1 address.Address) (*ChannelAvailableFunds, error) `perm:"sign"` PaychAvailableFunds func(p0 context.Context, p1 address.Address) (*ChannelAvailableFunds, error) `perm:"sign"`
@ -1715,6 +1717,14 @@ func (s *FullNodeStub) MsigSwapPropose(p0 context.Context, p1 address.Address, p
return *new(cid.Cid), xerrors.New("method not supported") return *new(cid.Cid), xerrors.New("method not supported")
} }
func (s *FullNodeStruct) NodeStatus(p0 context.Context, p1 bool) (NodeStatus, error) {
return s.Internal.NodeStatus(p0, p1)
}
func (s *FullNodeStub) NodeStatus(p0 context.Context, p1 bool) (NodeStatus, error) {
return *new(NodeStatus), xerrors.New("method not supported")
}
func (s *FullNodeStruct) PaychAllocateLane(p0 context.Context, p1 address.Address) (uint64, error) { func (s *FullNodeStruct) PaychAllocateLane(p0 context.Context, p1 address.Address) (uint64, error) {
return s.Internal.PaychAllocateLane(p0, p1) return s.Internal.PaychAllocateLane(p0, p1)
} }

View File

@ -116,3 +116,24 @@ type ConnMgrInfo struct {
Tags map[string]int Tags map[string]int
Conns map[string]time.Time Conns map[string]time.Time
} }
type NodeStatus struct {
SyncStatus NodeSyncStatus
PeerStatus NodePeerStatus
ChainStatus NodeChainStatus
}
type NodeSyncStatus struct {
Epoch uint64
Behind uint64
}
type NodePeerStatus struct {
PeersToPublishMsgs int
PeersToPublishBlocks int
}
type NodeChainStatus struct {
BlocksPerTipsetLast100 float64
BlocksPerTipsetLastFinality float64
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -81,6 +81,7 @@ var Commands = []*cli.Command{
WithCategory("developer", FetchParamCmd), WithCategory("developer", FetchParamCmd),
WithCategory("network", NetCmd), WithCategory("network", NetCmd),
WithCategory("network", SyncCmd), WithCategory("network", SyncCmd),
WithCategory("status", StatusCmd),
PprofCmd, PprofCmd,
VersionCmd, VersionCmd,
} }

60
cli/status.go Normal file
View File

@ -0,0 +1,60 @@
package cli
import (
"fmt"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/lotus/build"
)
var StatusCmd = &cli.Command{
Name: "status",
Usage: "Check node status",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "chain",
Usage: "include chain health status",
},
},
Action: func(cctx *cli.Context) error {
apic, closer, err := GetFullNodeAPIV1(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
inclChainStatus := cctx.Bool("chain")
status, err := apic.NodeStatus(ctx, inclChainStatus)
if err != nil {
return err
}
fmt.Printf("Sync Epoch: %d\n", status.SyncStatus.Epoch)
fmt.Printf("Epochs Behind: %d\n", status.SyncStatus.Behind)
fmt.Printf("Peers to Publish Messages: %d\n", status.PeerStatus.PeersToPublishMsgs)
fmt.Printf("Peers to Publish Blocks: %d\n", status.PeerStatus.PeersToPublishBlocks)
if inclChainStatus && status.SyncStatus.Epoch > uint64(build.Finality) {
var ok100, okFin string
if status.ChainStatus.BlocksPerTipsetLast100 >= 4.75 {
ok100 = "[OK]"
} else {
ok100 = "[UNHEALTHY]"
}
if status.ChainStatus.BlocksPerTipsetLastFinality >= 4.75 {
okFin = "[OK]"
} else {
okFin = "[UNHEALTHY]"
}
fmt.Printf("Blocks per TipSet in last 100 epochs: %f %s\n", status.ChainStatus.BlocksPerTipsetLast100, ok100)
fmt.Printf("Blocks per TipSet in last finality: %f %s\n", status.ChainStatus.BlocksPerTipsetLastFinality, okFin)
}
return nil
},
}

View File

@ -126,6 +126,8 @@
* [NetPeerInfo](#NetPeerInfo) * [NetPeerInfo](#NetPeerInfo)
* [NetPeers](#NetPeers) * [NetPeers](#NetPeers)
* [NetPubsubScores](#NetPubsubScores) * [NetPubsubScores](#NetPubsubScores)
* [Node](#Node)
* [NodeStatus](#NodeStatus)
* [Paych](#Paych) * [Paych](#Paych)
* [PaychAllocateLane](#PaychAllocateLane) * [PaychAllocateLane](#PaychAllocateLane)
* [PaychAvailableFunds](#PaychAvailableFunds) * [PaychAvailableFunds](#PaychAvailableFunds)
@ -3000,6 +3002,40 @@ Inputs: `null`
Response: `null` Response: `null`
## Node
These methods are general node management and status commands
### NodeStatus
There are not yet any comments for this method.
Perms: read
Inputs:
```json
[
true
]
```
Response:
```json
{
"SyncStatus": {
"Epoch": 42,
"Behind": 42
},
"PeerStatus": {
"PeersToPublishMsgs": 123,
"PeersToPublishBlocks": 123
},
"ChainStatus": {
"BlocksPerTipsetLast100": 12.3,
"BlocksPerTipsetLastFinality": 12.3
}
}
```
## Paych ## Paych
The Paych methods are for interacting with and managing payment channels The Paych methods are for interacting with and managing payment channels

View File

@ -31,6 +31,8 @@ COMMANDS:
NETWORK: NETWORK:
net Manage P2P Network net Manage P2P Network
sync Inspect or interact with the chain syncer sync Inspect or interact with the chain syncer
STATUS:
status Check node status
GLOBAL OPTIONS: GLOBAL OPTIONS:
--help, -h show help (default: false) --help, -h show help (default: false)
@ -2671,3 +2673,20 @@ OPTIONS:
--help, -h show help (default: false) --help, -h show help (default: false)
``` ```
## lotus status
```
NAME:
lotus status - Check node status
USAGE:
lotus status [command options] [arguments...]
CATEGORY:
STATUS
OPTIONS:
--chain include chain health status (default: false)
--help, -h show help (default: false)
```

View File

@ -2,16 +2,21 @@ package impl
import ( import (
"context" "context"
"time"
"github.com/libp2p/go-libp2p-core/peer"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/node/impl/client" "github.com/filecoin-project/lotus/node/impl/client"
"github.com/filecoin-project/lotus/node/impl/common" "github.com/filecoin-project/lotus/node/impl/common"
"github.com/filecoin-project/lotus/node/impl/full" "github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/node/impl/market" "github.com/filecoin-project/lotus/node/impl/market"
"github.com/filecoin-project/lotus/node/impl/paych" "github.com/filecoin-project/lotus/node/impl/paych"
"github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/modules/lp2p"
) )
var log = logging.Logger("node") var log = logging.Logger("node")
@ -30,11 +35,86 @@ type FullNodeAPI struct {
full.SyncAPI full.SyncAPI
full.BeaconAPI full.BeaconAPI
DS dtypes.MetadataDS DS dtypes.MetadataDS
NetworkName dtypes.NetworkName
} }
func (n *FullNodeAPI) CreateBackup(ctx context.Context, fpath string) error { func (n *FullNodeAPI) CreateBackup(ctx context.Context, fpath string) error {
return backup(n.DS, fpath) return backup(n.DS, fpath)
} }
func (n *FullNodeAPI) NodeStatus(ctx context.Context, inclChainStatus bool) (status api.NodeStatus, err error) {
curTs, err := n.ChainHead(ctx)
if err != nil {
return status, err
}
status.SyncStatus.Epoch = uint64(curTs.Height())
timestamp := time.Unix(int64(curTs.MinTimestamp()), 0)
delta := time.Since(timestamp).Seconds()
status.SyncStatus.Behind = uint64(delta / 30)
// get peers in the messages and blocks topics
peersMsgs := make(map[peer.ID]struct{})
peersBlocks := make(map[peer.ID]struct{})
for _, p := range n.PubSub.ListPeers(build.MessagesTopic(n.NetworkName)) {
peersMsgs[p] = struct{}{}
}
for _, p := range n.PubSub.ListPeers(build.BlocksTopic(n.NetworkName)) {
peersBlocks[p] = struct{}{}
}
// get scores for all connected and recent peers
scores, err := n.NetPubsubScores(ctx)
if err != nil {
return status, err
}
for _, score := range scores {
if score.Score.Score > lp2p.PublishScoreThreshold {
_, inMsgs := peersMsgs[score.ID]
if inMsgs {
status.PeerStatus.PeersToPublishMsgs++
}
_, inBlocks := peersBlocks[score.ID]
if inBlocks {
status.PeerStatus.PeersToPublishBlocks++
}
}
}
if inclChainStatus && status.SyncStatus.Epoch > uint64(build.Finality) {
blockCnt := 0
ts := curTs
for i := 0; i < 100; i++ {
blockCnt += len(ts.Blocks())
tsk := ts.Parents()
ts, err = n.ChainGetTipSet(ctx, tsk)
if err != nil {
return status, err
}
}
status.ChainStatus.BlocksPerTipsetLast100 = float64(blockCnt) / 100
for i := 100; i < int(build.Finality); i++ {
blockCnt += len(ts.Blocks())
tsk := ts.Parents()
ts, err = n.ChainGetTipSet(ctx, tsk)
if err != nil {
return status, err
}
}
status.ChainStatus.BlocksPerTipsetLastFinality = float64(blockCnt) / float64(build.Finality)
}
return status, nil
}
var _ api.FullNode = &FullNodeAPI{} var _ api.FullNode = &FullNodeAPI{}

View File

@ -36,6 +36,15 @@ func init() {
pubsub.GossipSubHistoryLength = 10 pubsub.GossipSubHistoryLength = 10
pubsub.GossipSubGossipFactor = 0.1 pubsub.GossipSubGossipFactor = 0.1
} }
const (
GossipScoreThreshold = -500
PublishScoreThreshold = -1000
GraylistScoreThreshold = -2500
AcceptPXScoreThreshold = 1000
OpportunisticGraftScoreThreshold = 3.5
)
func ScoreKeeper() *dtypes.ScoreKeeper { func ScoreKeeper() *dtypes.ScoreKeeper {
return new(dtypes.ScoreKeeper) return new(dtypes.ScoreKeeper)
} }
@ -256,11 +265,11 @@ func GossipSub(in GossipIn) (service *pubsub.PubSub, err error) {
Topics: topicParams, Topics: topicParams,
}, },
&pubsub.PeerScoreThresholds{ &pubsub.PeerScoreThresholds{
GossipThreshold: -500, GossipThreshold: GossipScoreThreshold,
PublishThreshold: -1000, PublishThreshold: PublishScoreThreshold,
GraylistThreshold: -2500, GraylistThreshold: GraylistScoreThreshold,
AcceptPXThreshold: 1000, AcceptPXThreshold: AcceptPXScoreThreshold,
OpportunisticGraftThreshold: 3.5, OpportunisticGraftThreshold: OpportunisticGraftScoreThreshold,
}, },
), ),
pubsub.WithPeerScoreInspect(in.Sk.Update, 10*time.Second), pubsub.WithPeerScoreInspect(in.Sk.Update, 10*time.Second),