2020-08-30 18:28:58 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
2021-03-08 19:57:42 +00:00
|
|
|
"github.com/docker/go-units"
|
2020-08-30 18:28:58 +00:00
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/mitchellh/go-homedir"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
lcli "github.com/filecoin-project/lotus/cli"
|
2022-06-14 18:03:38 +00:00
|
|
|
"github.com/filecoin-project/lotus/storage/sealer/storiface"
|
2020-08-30 18:28:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const metaFile = "sectorstore.json"
|
|
|
|
|
|
|
|
var storageCmd = &cli.Command{
|
|
|
|
Name: "storage",
|
|
|
|
Usage: "manage sector storage",
|
|
|
|
Subcommands: []*cli.Command{
|
|
|
|
storageAttachCmd,
|
2022-07-15 10:44:05 +00:00
|
|
|
storageDetachCmd,
|
|
|
|
storageRedeclareCmd,
|
2020-08-30 18:28:58 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var storageAttachCmd = &cli.Command{
|
|
|
|
Name: "attach",
|
|
|
|
Usage: "attach local storage path",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "init",
|
|
|
|
Usage: "initialize the path first",
|
|
|
|
},
|
|
|
|
&cli.Uint64Flag{
|
|
|
|
Name: "weight",
|
|
|
|
Usage: "(for init) path weight",
|
|
|
|
Value: 10,
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "seal",
|
|
|
|
Usage: "(for init) use path for sealing",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "store",
|
|
|
|
Usage: "(for init) use path for long-term storage",
|
|
|
|
},
|
2021-03-08 19:57:42 +00:00
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "max-storage",
|
|
|
|
Usage: "(for init) limit storage space for sectors (expensive for very large paths!)",
|
|
|
|
},
|
2021-10-06 13:53:32 +00:00
|
|
|
&cli.StringSliceFlag{
|
|
|
|
Name: "groups",
|
|
|
|
Usage: "path group names",
|
|
|
|
},
|
|
|
|
&cli.StringSliceFlag{
|
|
|
|
Name: "allow-to",
|
2021-11-23 15:16:27 +00:00
|
|
|
Usage: "path groups allowed to pull data from this path (allow all if not specified)",
|
2021-10-06 13:53:32 +00:00
|
|
|
},
|
2020-08-30 18:28:58 +00:00
|
|
|
},
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
nodeApi, closer, err := lcli.GetWorkerAPI(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") {
|
|
|
|
if err := os.MkdirAll(p, 0755); err != nil {
|
|
|
|
if !os.IsExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := os.Stat(filepath.Join(p, metaFile))
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
if err == nil {
|
|
|
|
return xerrors.Errorf("path is already initialized")
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-08 19:57:42 +00:00
|
|
|
var maxStor int64
|
|
|
|
if cctx.IsSet("max-storage") {
|
|
|
|
maxStor, err = units.RAMInBytes(cctx.String("max-storage"))
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("parsing max-storage: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 11:01:31 +00:00
|
|
|
cfg := &storiface.LocalStorageMeta{
|
2022-01-18 10:57:04 +00:00
|
|
|
ID: storiface.ID(uuid.New().String()),
|
2021-03-08 19:57:42 +00:00
|
|
|
Weight: cctx.Uint64("weight"),
|
|
|
|
CanSeal: cctx.Bool("seal"),
|
|
|
|
CanStore: cctx.Bool("store"),
|
|
|
|
MaxStorage: uint64(maxStor),
|
2021-10-06 13:53:32 +00:00
|
|
|
Groups: cctx.StringSlice("groups"),
|
|
|
|
AllowTo: cctx.StringSlice("allow-to"),
|
2020-08-30 18:28:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !(cfg.CanStore || cfg.CanSeal) {
|
2021-07-01 19:07:53 +00:00
|
|
|
return xerrors.Errorf("must specify at least one of --store or --seal")
|
2020-08-30 18:28:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
b, err := json.MarshalIndent(cfg, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("marshaling storage config: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-03-29 19:24:07 +00:00
|
|
|
if err := os.WriteFile(filepath.Join(p, metaFile), b, 0644); err != nil {
|
2020-08-30 18:28:58 +00:00
|
|
|
return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(p, metaFile), err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodeApi.StorageAddLocal(ctx, p)
|
|
|
|
},
|
|
|
|
}
|
2022-07-15 10:44:05 +00:00
|
|
|
|
|
|
|
var storageDetachCmd = &cli.Command{
|
|
|
|
Name: "detach",
|
|
|
|
Usage: "detach local storage path",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "really-do-it",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ArgsUsage: "[path]",
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
nodeApi, closer, err := lcli.GetWorkerAPI(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer closer()
|
|
|
|
ctx := lcli.ReqContext(cctx)
|
|
|
|
|
|
|
|
if !cctx.Args().Present() {
|
|
|
|
return xerrors.Errorf("must specify storage path")
|
|
|
|
}
|
|
|
|
|
|
|
|
p, err := homedir.Expand(cctx.Args().First())
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("expanding path: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-08-02 13:18:24 +00:00
|
|
|
if !cctx.Bool("really-do-it") {
|
2022-07-15 10:44:05 +00:00
|
|
|
return xerrors.Errorf("pass --really-do-it to execute the action")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodeApi.StorageDetachLocal(ctx, p)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var storageRedeclareCmd = &cli.Command{
|
|
|
|
Name: "redeclare",
|
|
|
|
Usage: "redeclare sectors in a local storage path",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "id",
|
|
|
|
Usage: "storage path ID",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "all",
|
|
|
|
Usage: "redeclare all storage paths",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "drop-missing",
|
|
|
|
Usage: "Drop index entries with missing files",
|
2023-05-18 16:04:10 +00:00
|
|
|
Value: true,
|
2022-07-15 10:44:05 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
nodeApi, closer, err := lcli.GetWorkerAPI(cctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer closer()
|
|
|
|
ctx := lcli.ReqContext(cctx)
|
|
|
|
|
2023-05-18 16:04:10 +00:00
|
|
|
// check if no argument and no --id or --all flag is provided
|
|
|
|
if cctx.NArg() == 0 && !cctx.IsSet("id") && !cctx.Bool("all") {
|
|
|
|
return xerrors.Errorf("You must specify a storage path, or --id, or --all")
|
|
|
|
}
|
|
|
|
|
2022-07-15 10:44:05 +00:00
|
|
|
if cctx.IsSet("id") && cctx.Bool("all") {
|
|
|
|
return xerrors.Errorf("--id and --all can't be passed at the same time")
|
|
|
|
}
|
|
|
|
|
2023-05-18 16:04:10 +00:00
|
|
|
if cctx.Bool("all") && cctx.NArg() > 0 {
|
|
|
|
return xerrors.Errorf("No additional arguments are expected when --all is set")
|
|
|
|
}
|
|
|
|
|
2022-07-15 10:44:05 +00:00
|
|
|
if cctx.IsSet("id") {
|
|
|
|
id := storiface.ID(cctx.String("id"))
|
|
|
|
return nodeApi.StorageRedeclareLocal(ctx, &id, cctx.Bool("drop-missing"))
|
|
|
|
}
|
|
|
|
|
|
|
|
if cctx.Bool("all") {
|
|
|
|
return nodeApi.StorageRedeclareLocal(ctx, nil, cctx.Bool("drop-missing"))
|
|
|
|
}
|
|
|
|
|
2023-05-18 16:04:10 +00:00
|
|
|
// As no --id or --all flag is set, we can assume the argument is a path.
|
|
|
|
path := cctx.Args().First()
|
|
|
|
metaFilePath := filepath.Join(path, "sectorstore.json")
|
|
|
|
|
|
|
|
var meta storiface.LocalStorageMeta
|
|
|
|
metaFile, err := os.Open(metaFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("Failed to open file: %w", err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if closeErr := metaFile.Close(); closeErr != nil {
|
|
|
|
log.Error("Failed to close the file: %v", closeErr)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
err = json.NewDecoder(metaFile).Decode(&meta)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("Failed to decode file: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
id := meta.ID
|
|
|
|
return nodeApi.StorageRedeclareLocal(ctx, &id, cctx.Bool("drop-missing"))
|
2022-07-15 10:44:05 +00:00
|
|
|
},
|
|
|
|
}
|