lotus/cmd/lotus-storage-miner/storage.go

384 lines
8.4 KiB
Go
Raw Normal View History

2020-03-09 06:13:22 +00:00
package main
import (
"encoding/json"
2020-03-16 17:50:07 +00:00
"fmt"
2020-03-09 06:13:22 +00:00
"io/ioutil"
"os"
"path/filepath"
2020-03-20 22:39:07 +00:00
"sort"
2020-03-19 19:51:33 +00:00
"strconv"
2020-05-01 12:06:19 +00:00
"strings"
"time"
2020-03-09 06:13:22 +00:00
2020-05-01 12:06:19 +00:00
"github.com/fatih/color"
2020-03-09 06:13:22 +00:00
"github.com/google/uuid"
"github.com/mitchellh/go-homedir"
"github.com/urfave/cli/v2"
2020-06-05 22:59:01 +00:00
"golang.org/x/xerrors"
2020-03-09 06:13:22 +00:00
2020-03-19 19:51:33 +00:00
"github.com/filecoin-project/go-address"
2020-07-08 15:23:27 +00:00
"github.com/filecoin-project/sector-storage/fsutil"
"github.com/filecoin-project/sector-storage/stores"
"github.com/filecoin-project/specs-actors/actors/abi"
2020-03-16 17:50:07 +00:00
"github.com/filecoin-project/lotus/chain/types"
2020-03-09 06:13:22 +00:00
lcli "github.com/filecoin-project/lotus/cli"
)
const metaFile = "sectorstore.json"
var storageCmd = &cli.Command{
Name: "storage",
Usage: "manage sector storage",
Subcommands: []*cli.Command{
storageAttachCmd,
2020-03-16 17:50:07 +00:00
storageListCmd,
2020-03-19 19:51:33 +00:00
storageFindCmd,
2020-03-09 06:13:22 +00:00
},
}
var storageAttachCmd = &cli.Command{
Name: "attach",
Usage: "attach local storage path",
Flags: []cli.Flag{
&cli.BoolFlag{
2020-03-09 06:13:45 +00:00
Name: "init",
2020-03-09 06:13:22 +00:00
Usage: "initialize the path first",
},
&cli.Uint64Flag{
2020-03-09 06:13:45 +00:00
Name: "weight",
2020-03-09 06:13:22 +00:00
Usage: "(for init) path weight",
Value: 10,
},
&cli.BoolFlag{
2020-03-09 06:13:45 +00:00
Name: "seal",
2020-03-09 06:13:22 +00:00
Usage: "(for init) use path for sealing",
},
&cli.BoolFlag{
2020-03-09 06:13:45 +00:00
Name: "store",
2020-03-09 06:13:22 +00:00
Usage: "(for init) use path for long-term storage",
},
},
Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
if !cctx.Args().Present() {
return xerrors.Errorf("must specify storage path to attach")
}
p, err := homedir.Expand(cctx.Args().First())
if err != nil {
return xerrors.Errorf("expanding path: %w", err)
}
if cctx.Bool("init") {
2020-03-09 22:00:29 +00:00
if err := os.MkdirAll(p, 0755); err != nil {
if !os.IsExist(err) {
return err
}
}
2020-03-09 06:13:22 +00:00
_, err := os.Stat(filepath.Join(p, metaFile))
if !os.IsNotExist(err) {
if err == nil {
return xerrors.Errorf("path is already initialized")
}
return err
}
2020-03-19 15:10:19 +00:00
cfg := &stores.LocalStorageMeta{
2020-03-13 11:59:19 +00:00
ID: stores.ID(uuid.New().String()),
2020-03-09 06:13:22 +00:00
Weight: cctx.Uint64("weight"),
CanSeal: cctx.Bool("seal"),
CanStore: cctx.Bool("store"),
}
if !(cfg.CanStore || cfg.CanSeal) {
return xerrors.Errorf("must specify at least one of --store of --seal")
}
b, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return xerrors.Errorf("marshaling storage config: %w", err)
}
if err := ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644); err != nil {
return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(p, metaFile), err)
}
}
return nodeApi.StorageAddLocal(ctx, p)
},
}
2020-03-16 17:50:07 +00:00
var storageListCmd = &cli.Command{
2020-03-19 19:51:33 +00:00
Name: "list",
Usage: "list local storage paths",
2020-05-01 12:06:19 +00:00
Flags: []cli.Flag{
&cli.BoolFlag{Name: "color"},
},
2020-03-16 17:50:07 +00:00
Action: func(cctx *cli.Context) error {
2020-05-01 12:06:19 +00:00
color.NoColor = !cctx.Bool("color")
2020-03-16 17:50:07 +00:00
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
st, err := nodeApi.StorageList(ctx)
if err != nil {
return err
}
2020-03-19 19:51:33 +00:00
local, err := nodeApi.StorageLocal(ctx)
if err != nil {
return err
}
type fsInfo struct {
stores.ID
sectors []stores.Decl
2020-07-08 15:23:27 +00:00
stat fsutil.FsStat
}
sorted := make([]fsInfo, 0, len(st))
2020-03-23 14:56:22 +00:00
for id, decls := range st {
2020-05-01 12:06:19 +00:00
st, err := nodeApi.StorageStat(ctx, id)
if err != nil {
sorted = append(sorted, fsInfo{ID: id, sectors: decls})
continue
2020-05-01 12:06:19 +00:00
}
sorted = append(sorted, fsInfo{id, decls, st})
2020-03-23 14:56:22 +00:00
}
sort.Slice(sorted, func(i, j int) bool {
2020-05-01 12:06:19 +00:00
if sorted[i].stat.Capacity != sorted[j].stat.Capacity {
return sorted[i].stat.Capacity > sorted[j].stat.Capacity
}
2020-03-23 14:56:22 +00:00
return sorted[i].ID < sorted[j].ID
})
for _, s := range sorted {
2020-03-22 21:39:06 +00:00
var cnt [3]int
2020-03-23 14:56:22 +00:00
for _, decl := range s.sectors {
2020-03-22 21:39:06 +00:00
for i := range cnt {
if decl.SectorFileType&(1<<i) != 0 {
cnt[i]++
}
2020-03-16 17:50:07 +00:00
}
}
fmt.Printf("%s:\n", s.ID)
pingStart := time.Now()
st, err := nodeApi.StorageStat(ctx, s.ID)
if err != nil {
fmt.Printf("\t%s: %s:\n", color.RedString("Error"), err)
continue
}
ping := time.Now().Sub(pingStart)
2020-05-01 19:51:31 +00:00
usedPercent := (st.Capacity - st.Available) * 100 / st.Capacity
2020-05-01 12:06:19 +00:00
percCol := color.FgGreen
switch {
case usedPercent > 98:
percCol = color.FgRed
case usedPercent > 90:
percCol = color.FgYellow
}
2020-07-08 15:23:27 +00:00
var barCols = int64(50)
2020-05-01 19:51:31 +00:00
set := (st.Capacity - st.Available) * barCols / st.Capacity
2020-05-01 12:06:19 +00:00
bar := strings.Repeat("|", int(set)) + strings.Repeat(" ", int(barCols-set))
fmt.Printf("\t[%s] %s/%s %s\n", color.New(percCol).Sprint(bar),
2020-07-08 15:23:27 +00:00
types.SizeStr(types.NewInt(uint64(st.Capacity-st.Available))),
types.SizeStr(types.NewInt(uint64(st.Capacity))),
2020-05-01 12:06:19 +00:00
color.New(percCol).Sprintf("%d%%", usedPercent))
fmt.Printf("\t%s; %s; %s\n",
color.YellowString("Unsealed: %d", cnt[0]),
color.GreenString("Sealed: %d", cnt[1]),
color.BlueString("Caches: %d", cnt[2]))
2020-03-16 17:50:07 +00:00
2020-03-23 14:56:22 +00:00
si, err := nodeApi.StorageInfo(ctx, s.ID)
2020-03-16 17:50:07 +00:00
if err != nil {
return err
}
2020-03-22 21:39:06 +00:00
fmt.Print("\t")
if si.CanSeal || si.CanStore {
fmt.Printf("Weight: %d; Use: ", si.Weight)
if si.CanSeal {
2020-05-01 12:06:19 +00:00
fmt.Print(color.MagentaString("Seal "))
2020-03-22 21:39:06 +00:00
}
if si.CanStore {
2020-05-01 12:06:19 +00:00
fmt.Print(color.CyanString("Store"))
2020-03-22 21:39:06 +00:00
}
fmt.Println("")
} else {
2020-05-01 12:06:19 +00:00
fmt.Print(color.HiYellowString("Use: ReadOnly"))
2020-03-22 21:39:06 +00:00
}
2020-03-23 14:56:22 +00:00
if localPath, ok := local[s.ID]; ok {
2020-05-01 12:06:19 +00:00
fmt.Printf("\tLocal: %s\n", color.GreenString(localPath))
2020-03-19 19:51:33 +00:00
}
for i, l := range si.URLs {
var rtt string
if _, ok := local[s.ID]; !ok && i == 0 {
rtt = " (latency: " + ping.Truncate(time.Microsecond*100).String() + ")"
}
fmt.Printf("\tURL: %s%s\n", l, rtt) // TODO; try pinging maybe?? print latency?
2020-03-19 19:51:33 +00:00
}
2020-05-01 12:06:19 +00:00
fmt.Println()
2020-03-19 19:51:33 +00:00
}
return nil
},
}
type storedSector struct {
2020-03-20 22:39:07 +00:00
id stores.ID
store stores.SectorStorageInfo
2020-03-20 22:39:07 +00:00
2020-03-19 19:51:33 +00:00
unsealed, sealed, cache bool
}
var storageFindCmd = &cli.Command{
Name: "find",
Usage: "find sector in the storage system",
ArgsUsage: "[sector number]",
Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
ma, err := nodeApi.ActorAddress(ctx)
if err != nil {
return err
}
mid, err := address.IDFromAddress(ma)
if err != nil {
return err
}
if !cctx.Args().Present() {
2020-07-08 10:38:59 +00:00
return xerrors.New("Usage: lotus-miner storage find [sector number]")
2020-03-19 19:51:33 +00:00
}
snum, err := strconv.ParseUint(cctx.Args().First(), 10, 64)
if err != nil {
return err
}
sid := abi.SectorID{
Miner: abi.ActorID(mid),
Number: abi.SectorNumber(snum),
}
2020-03-26 02:50:56 +00:00
u, err := nodeApi.StorageFindSector(ctx, sid, stores.FTUnsealed, false)
2020-03-19 19:51:33 +00:00
if err != nil {
return xerrors.Errorf("finding unsealed: %w", err)
}
2020-03-26 02:50:56 +00:00
s, err := nodeApi.StorageFindSector(ctx, sid, stores.FTSealed, false)
2020-03-19 19:51:33 +00:00
if err != nil {
return xerrors.Errorf("finding sealed: %w", err)
}
2020-03-26 02:50:56 +00:00
c, err := nodeApi.StorageFindSector(ctx, sid, stores.FTCache, false)
2020-03-19 19:51:33 +00:00
if err != nil {
return xerrors.Errorf("finding cache: %w", err)
}
byId := map[stores.ID]*storedSector{}
for _, info := range u {
sts, ok := byId[info.ID]
if !ok {
sts = &storedSector{
2020-03-20 22:39:07 +00:00
id: info.ID,
2020-03-19 19:51:33 +00:00
store: info,
}
byId[info.ID] = sts
}
sts.unsealed = true
}
for _, info := range s {
sts, ok := byId[info.ID]
if !ok {
sts = &storedSector{
2020-03-20 22:39:07 +00:00
id: info.ID,
2020-03-19 19:51:33 +00:00
store: info,
}
byId[info.ID] = sts
}
sts.sealed = true
}
for _, info := range c {
sts, ok := byId[info.ID]
if !ok {
sts = &storedSector{
2020-03-20 22:39:07 +00:00
id: info.ID,
2020-03-19 19:51:33 +00:00
store: info,
}
byId[info.ID] = sts
}
sts.cache = true
}
local, err := nodeApi.StorageLocal(ctx)
if err != nil {
return err
}
2020-03-20 22:39:07 +00:00
var out []*storedSector
for _, sector := range byId {
out = append(out, sector)
}
sort.Slice(out, func(i, j int) bool {
return out[i].id < out[j].id
})
for _, info := range out {
var types string
if info.unsealed {
types += "Unsealed, "
}
if info.sealed {
types += "Sealed, "
}
if info.cache {
types += "Cache, "
}
fmt.Printf("In %s (%s)\n", info.id, types[:len(types)-2])
2020-03-19 19:51:33 +00:00
fmt.Printf("\tSealing: %t; Storage: %t\n", info.store.CanSeal, info.store.CanSeal)
2020-03-20 22:39:07 +00:00
if localPath, ok := local[info.id]; ok {
fmt.Printf("\tLocal (%s)\n", localPath)
} else {
fmt.Printf("\tRemote\n")
2020-03-19 19:51:33 +00:00
}
for _, l := range info.store.URLs {
fmt.Printf("\tURL: %s\n", l)
2020-03-16 17:50:07 +00:00
}
}
return nil
},
}