diff --git a/cli/client.go b/cli/client.go index 7c3ac9946..255f5a70c 100644 --- a/cli/client.go +++ b/cli/client.go @@ -1423,7 +1423,7 @@ func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTr otherPartyColumn: otherParty, "Root Cid": rootCid, "Initiated?": initiated, - "Transferred": channel.Transferred, + "Transferred": units.BytesSize(float64(channel.Transferred)), "Voucher": voucher, "Message": channel.Message, } diff --git a/cli/util.go b/cli/util.go index 762cdb565..fb555e320 100644 --- a/cli/util.go +++ b/cli/util.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/hako/durafmt" "github.com/ipfs/go-cid" "github.com/filecoin-project/go-state-types/abi" @@ -36,11 +37,11 @@ func parseTipSet(ctx context.Context, api api.FullNode, vals []string) (*types.T func EpochTime(curr, e abi.ChainEpoch) string { switch { case curr > e: - return fmt.Sprintf("%d (%s ago)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))) + return fmt.Sprintf("%d (%s ago)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))).LimitFirstN(2)) case curr == e: return fmt.Sprintf("%d (now)", e) case curr < e: - return fmt.Sprintf("%d (in %s)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))) + return fmt.Sprintf("%d (in %s)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))).LimitFirstN(2)) } panic("math broke") diff --git a/cmd/lotus-storage-miner/sectors.go b/cmd/lotus-storage-miner/sectors.go index 370962bdc..b50f4a86d 100644 --- a/cmd/lotus-storage-miner/sectors.go +++ b/cmd/lotus-storage-miner/sectors.go @@ -5,19 +5,22 @@ import ( "os" "sort" "strconv" - "text/tabwriter" "time" + "github.com/docker/go-units" + "github.com/fatih/color" "github.com/urfave/cli/v2" "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/lib/tablewriter" lcli "github.com/filecoin-project/lotus/cli" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" @@ -144,8 +147,19 @@ var sectorsListCmd = &cli.Command{ Name: "show-removed", Usage: "show removed sectors", }, + &cli.BoolFlag{ + Name: "color", + Aliases: []string{"c"}, + Value: true, + }, + &cli.BoolFlag{ + Name: "fast", + Usage: "don't show on-chain info for better performance", + }, }, Action: func(cctx *cli.Context) error { + color.NoColor = !cctx.Bool("color") + nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) if err != nil { return err @@ -170,7 +184,12 @@ var sectorsListCmd = &cli.Command{ return err } - activeSet, err := fullApi.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK) + head, err := fullApi.ChainHead(ctx) + if err != nil { + return err + } + + activeSet, err := fullApi.StateMinerActiveSectors(ctx, maddr, head.Key()) if err != nil { return err } @@ -179,7 +198,7 @@ var sectorsListCmd = &cli.Command{ activeIDs[info.SectorNumber] = struct{}{} } - sset, err := fullApi.StateMinerSectors(ctx, maddr, nil, types.EmptyTSK) + sset, err := fullApi.StateMinerSectors(ctx, maddr, nil, head.Key()) if err != nil { return err } @@ -192,12 +211,26 @@ var sectorsListCmd = &cli.Command{ return list[i] < list[j] }) - w := tabwriter.NewWriter(os.Stdout, 8, 4, 1, ' ', 0) + tw := tablewriter.New( + tablewriter.Col("ID"), + tablewriter.Col("State"), + tablewriter.Col("OnChain"), + tablewriter.Col("Active"), + tablewriter.Col("Expiration"), + tablewriter.Col("Deals"), + tablewriter.Col("DealWeight"), + tablewriter.NewLineCol("Error"), + tablewriter.NewLineCol("EarlyExpiration")) + + fast := cctx.Bool("fast") for _, s := range list { - st, err := nodeApi.SectorsStatus(ctx, s, false) + st, err := nodeApi.SectorsStatus(ctx, s, !fast) if err != nil { - fmt.Fprintf(w, "%d:\tError: %s\n", s, err) + tw.Write(map[string]interface{}{ + "ID": s, + "Error": err, + }) continue } @@ -205,20 +238,60 @@ var sectorsListCmd = &cli.Command{ _, inSSet := commitedIDs[s] _, inASet := activeIDs[s] - _, _ = fmt.Fprintf(w, "%d: %s\tsSet: %s\tactive: %s\ttktH: %d\tseedH: %d\tdeals: %v\t toUpgrade:%t\n", - s, - st.State, - yesno(inSSet), - yesno(inASet), - st.Ticket.Epoch, - st.Seed.Epoch, - st.Deals, - st.ToUpgrade, - ) + dw := .0 + if st.Expiration-st.Activation > 0 { + dw = float64(big.Div(st.DealWeight, big.NewInt(int64(st.Expiration-st.Activation))).Uint64()) + } + + var deals int + for _, deal := range st.Deals { + if deal != 0 { + deals++ + } + } + + exp := st.Expiration + if st.OnTime > 0 && st.OnTime < exp { + exp = st.OnTime // Can be different when the sector was CC upgraded + } + + m := map[string]interface{}{ + "ID": s, + "State": color.New(stateOrder[sealing.SectorState(st.State)].col).Sprint(st.State), + "OnChain": yesno(inSSet), + "Active": yesno(inASet), + } + + if deals > 0 { + m["Deals"] = color.GreenString("%d", deals) + } else { + m["Deals"] = color.BlueString("CC") + if st.ToUpgrade { + m["Deals"] = color.CyanString("CC(upgrade)") + } + } + + if !fast { + if !inSSet { + m["Expiration"] = "n/a" + } else { + m["Expiration"] = lcli.EpochTime(head.Height(), exp) + + if !fast && deals > 0 { + m["DealWeight"] = units.BytesSize(dw) + } + + if st.Early > 0 { + m["EarlyExpiration"] = color.YellowString(lcli.EpochTime(head.Height(), st.Early)) + } + } + } + + tw.Write(m) } } - return w.Flush() + return tw.Flush(os.Stdout) }, } @@ -447,7 +520,7 @@ var sectorsUpdateCmd = &cli.Command{ func yesno(b bool) string { if b { - return "YES" + return color.GreenString("YES") } - return "NO" + return color.RedString("NO") } diff --git a/go.mod b/go.mod index 06b24732b..f745ccf67 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/google/uuid v1.1.1 github.com/gorilla/mux v1.7.4 github.com/gorilla/websocket v1.4.2 + github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/golang-lru v0.5.4 diff --git a/go.sum b/go.sum index ef1eb7f98..c02c6daea 100644 --- a/go.sum +++ b/go.sum @@ -417,6 +417,8 @@ github.com/gxed/go-shellwords v1.0.3/go.mod h1:N7paucT91ByIjmVJHhvoarjoQnmsi3Jd3 github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/gxed/pubsub v0.0.0-20180201040156-26ebdf44f824/go.mod h1:OiEWyHgK+CWrmOlVquHaIK1vhpUJydC9m0Je6mhaiNE= +github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ= +github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE= github.com/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1 h1:F9k+7wv5OIk1zcq23QpdiL0hfDuXPjuOmMNaC6fgQ0Q= github.com/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1/go.mod h1:jvfsLIxk0fY/2BKSQ1xf2406AKA5dwMmKKv0ADcOfN8= github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY= diff --git a/lib/tablewriter/tablewriter.go b/lib/tablewriter/tablewriter.go index d77611390..cd045710e 100644 --- a/lib/tablewriter/tablewriter.go +++ b/lib/tablewriter/tablewriter.go @@ -12,6 +12,7 @@ import ( type Column struct { Name string SeparateLine bool + Lines int } type TableWriter struct { @@ -50,6 +51,7 @@ cloop: for i, column := range w.cols { if column.Name == col { byColID[i] = fmt.Sprint(val) + w.cols[i].Lines++ continue cloop } } @@ -58,6 +60,7 @@ cloop: w.cols = append(w.cols, Column{ Name: col, SeparateLine: false, + Lines: 1, }) } @@ -77,7 +80,11 @@ func (w *TableWriter) Flush(out io.Writer) error { w.rows = append([]map[int]string{header}, w.rows...) - for col := range w.cols { + for col, c := range w.cols { + if c.Lines == 0 { + continue + } + for _, row := range w.rows { val, found := row[col] if !found { @@ -94,9 +101,13 @@ func (w *TableWriter) Flush(out io.Writer) error { cols := make([]string, len(w.cols)) for ci, col := range w.cols { + if col.Lines == 0 { + continue + } + e, _ := row[ci] pad := colLengths[ci] - cliStringLength(e) + 2 - if !col.SeparateLine { + if !col.SeparateLine && col.Lines > 0 { e = e + strings.Repeat(" ", pad) if _, err := fmt.Fprint(out, e); err != nil { return err