mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
349 lines
8.9 KiB
Go
349 lines
8.9 KiB
Go
// Copyright 2020 The Chubao Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
// implied. See the License for the specific language governing
|
|
// permissions and limitations under the License.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/cubefs/cubefs/proto"
|
|
"github.com/cubefs/cubefs/sdk/meta"
|
|
"github.com/cubefs/cubefs/util/log"
|
|
"github.com/cubefs/cubefs/util/ump"
|
|
)
|
|
|
|
// TODO: Support record full path for snapshot tool
|
|
const snapshotFullPath = "(Snapshot Cmd Unsupported)"
|
|
|
|
var gMetaWrapper *meta.MetaWrapper
|
|
|
|
// TODO: unused
|
|
var (
|
|
_ = readSnapshot
|
|
_ = doUnlinkInode
|
|
)
|
|
|
|
func newCleanCmd() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "clean",
|
|
Short: "clean snapshot related inode or dentry according to some rules",
|
|
Args: cobra.MinimumNArgs(0),
|
|
}
|
|
|
|
c.AddCommand(
|
|
newCleanSnapshotCmd(),
|
|
newCleanInodeCmd(),
|
|
newCleanDentryCmd(),
|
|
newEvictInodeCmd(),
|
|
)
|
|
|
|
return c
|
|
}
|
|
|
|
func newCleanSnapshotCmd() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "snapshot",
|
|
Short: "clean snapshot",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := Clean("snapshot", args); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
},
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func newCleanInodeCmd() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "inode",
|
|
Short: "clean dirty inode",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := Clean("inode", args); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
},
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func newCleanDentryCmd() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "dentry",
|
|
Short: "clean dirty dentry",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := Clean("dentry", args); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
},
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func newEvictInodeCmd() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "evict",
|
|
Short: "clean dirty dentry",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := Clean("evict", args); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
},
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func Clean(opt string, args []string) error {
|
|
defer log.LogFlush()
|
|
|
|
if MasterAddr == "" || VolName == "" {
|
|
return fmt.Errorf("Lack of parameters: master(%v) vol(%v)", MasterAddr, VolName)
|
|
}
|
|
|
|
ump.InitUmp("snapshot", "")
|
|
|
|
_, err := log.InitLog("snapshotlog", "snapshot", log.DebugLevel, nil, log.DefaultLogLeftSpaceLimitRatio)
|
|
if err != nil {
|
|
return fmt.Errorf("Init log failed: %v", err)
|
|
}
|
|
|
|
masters := strings.Split(MasterAddr, meta.HostsSeparator)
|
|
metaConfig := &meta.MetaConfig{
|
|
Volume: VolName,
|
|
Masters: masters,
|
|
}
|
|
|
|
gMetaWrapper, err = meta.NewMetaWrapper(metaConfig)
|
|
if err != nil {
|
|
return fmt.Errorf("NewMetaWrapper failed: %v", err)
|
|
}
|
|
|
|
switch opt {
|
|
case "snapshot":
|
|
err = cleanSnapshot()
|
|
if err != nil {
|
|
return fmt.Errorf("Clean snapshot failed: %v", err)
|
|
}
|
|
case "dentry":
|
|
err = cleanDentries()
|
|
if err != nil {
|
|
return fmt.Errorf("Clean dentries failed: %v", err)
|
|
}
|
|
case "evict":
|
|
err = evictInodes()
|
|
if err != nil {
|
|
return fmt.Errorf("Evict inodes failed: %v", err)
|
|
}
|
|
default:
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func evictInodes() error {
|
|
mps, err := getMetaPartitions(MasterAddr, VolName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
for _, mp := range mps {
|
|
cmdline := fmt.Sprintf("http://%s:%s/getAllInodes?pid=%d", strings.Split(mp.LeaderAddr, ":")[0], MetaPort, mp.PartitionID)
|
|
wg.Add(1)
|
|
go evictOnTime(&wg, cmdline)
|
|
}
|
|
|
|
wg.Wait()
|
|
return nil
|
|
}
|
|
|
|
func evictOnTime(wg *sync.WaitGroup, cmdline string) {
|
|
defer wg.Done()
|
|
|
|
client := &http.Client{Timeout: 0}
|
|
resp, err := client.Get(cmdline)
|
|
if err != nil {
|
|
log.LogErrorf("Get request failed: %v %v", cmdline, err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
log.LogErrorf("Invalid status code: %v", resp.StatusCode)
|
|
return
|
|
}
|
|
|
|
log.LogWritef("Dealing with meta partition: %v", cmdline)
|
|
|
|
dec := json.NewDecoder(resp.Body)
|
|
for dec.More() {
|
|
inode := &Inode{}
|
|
err = dec.Decode(inode)
|
|
if err != nil {
|
|
log.LogErrorf("Decode inode failed: %v", err)
|
|
return
|
|
}
|
|
doEvictInode(inode)
|
|
}
|
|
log.LogWritef("Done! Dealing with meta partition: %v", cmdline)
|
|
}
|
|
|
|
func doEvictInode(inode *Inode) error {
|
|
if inode.NLink != 0 || time.Since(time.Unix(inode.ModifyTime, 0)) < 24*time.Hour || !proto.IsRegular(inode.Type) {
|
|
return nil
|
|
}
|
|
err := gMetaWrapper.Evict(inode.Inode, snapshotFullPath)
|
|
if err != nil {
|
|
if err != syscall.ENOENT {
|
|
return err
|
|
}
|
|
}
|
|
log.LogWritef("%v", inode)
|
|
return nil
|
|
}
|
|
|
|
func doUnlinkInode(inode *Inode) error {
|
|
/*
|
|
* Do clean inode with the following exceptions:
|
|
* 1. nlink == 0, might be a temorary inode
|
|
* 2. size == 0 && ctime is close to current time, might be in the process of file creation
|
|
*/
|
|
if inode.NLink == 0 ||
|
|
(inode.Size == 0 &&
|
|
time.Unix(inode.CreateTime, 0).Add(24*time.Hour).After(time.Now())) {
|
|
return nil
|
|
}
|
|
|
|
_, err := gMetaWrapper.InodeUnlink_ll(inode.Inode, snapshotFullPath)
|
|
if err != nil {
|
|
if err != syscall.ENOENT {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = gMetaWrapper.Evict(inode.Inode, snapshotFullPath)
|
|
if err != nil {
|
|
if err != syscall.ENOENT {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func cleanDentries() error {
|
|
//filePath := fmt.Sprintf("_export_%s/%s", VolName, obsoleteDentryDumpFileName)
|
|
// TODO: send request to meta node directly with pino, name and ino.
|
|
//if ino, err := gMetaWrapper.Delete_ll_EX(1, child.Name, true, VerSeq); err != nil {
|
|
//
|
|
//}
|
|
return nil
|
|
}
|
|
|
|
func readSnapshot() (err error) {
|
|
log.LogInfof("action[readSnapshot] vol %v verSeq %v", VolName, VerSeq)
|
|
if VerSeq == 0 {
|
|
VerSeq = math.MaxUint64
|
|
}
|
|
var (
|
|
parents []proto.Dentry
|
|
ino *proto.InodeInfo
|
|
)
|
|
|
|
log.LogDebugf("action[readSnapshot] ReadDirLimit_ll parent root verSeq %v", VerSeq)
|
|
parents, err = gMetaWrapper.ReadDirLimitForSnapShotClean(1, "", math.MaxUint64, VerSeq, false) // one more for nextMarker
|
|
if err != nil && err != syscall.ENOENT {
|
|
log.LogErrorf("action[readSnapshot] parent root verSeq %v err %v", VerSeq, err)
|
|
return err
|
|
}
|
|
for _, child := range parents {
|
|
log.LogDebugf("action[readSnapshot] parent root Delete_ll_EX child name %v verSeq %v ino %v success", child.Name, VerSeq, ino)
|
|
}
|
|
return
|
|
}
|
|
|
|
func cleanSnapshot() (err error) {
|
|
log.LogInfof("action[cleanSnapshot] vol %v verSeq %v", VolName, VerSeq)
|
|
if VerSeq == 0 {
|
|
VerSeq = math.MaxUint64
|
|
}
|
|
var (
|
|
children []proto.Dentry
|
|
parents []proto.Dentry
|
|
ino *proto.InodeInfo
|
|
)
|
|
// return readSnapshot()
|
|
|
|
log.LogDebugf("action[cleanSnapshot] ReadDirLimit_ll parent root verSeq %v", VerSeq)
|
|
parents, err = gMetaWrapper.ReadDirLimitForSnapShotClean(1, "", math.MaxUint64, VerSeq, false) // one more for nextMarker
|
|
if err != nil && err != syscall.ENOENT {
|
|
log.LogErrorf("action[cleanSnapshot] parent root verSeq %v err %v", VerSeq, err)
|
|
return err
|
|
}
|
|
|
|
if err == syscall.ENOENT {
|
|
log.LogWarnf("action[cleanSnapshot] parent root verSeq %v found nothing", VerSeq)
|
|
return nil
|
|
}
|
|
|
|
log.LogDebugf("action[cleanSnapshot] parent root verSeq %v Delete_ll_EX children count %v", VerSeq, len(parents))
|
|
for _, child := range parents {
|
|
fullPath := "/" + child.Name
|
|
if ino, err = gMetaWrapper.Delete_Ver_ll(1, child.Name, proto.IsDir(child.Type), VerSeq, fullPath); err != nil {
|
|
log.LogErrorf("action[cleanSnapshot] parent root Delete_ll_EX child name %v verSeq %v err %v", child.Name, VerSeq, err)
|
|
}
|
|
log.LogDebugf("action[cleanSnapshot] parent root Delete_ll_EX child name %v verSeq %v ino %v success", child.Name, VerSeq, ino)
|
|
}
|
|
|
|
for idx := 0; idx < len(parents); idx++ {
|
|
parent := parents[idx]
|
|
if proto.IsDir(parent.Type) {
|
|
log.LogDebugf("action[cleanSnapshot] try loop delete dir %v %v with verSeq %v", parent.Inode, parent.Name, VerSeq)
|
|
children, err = gMetaWrapper.ReadDirLimitForSnapShotClean(parent.Inode, "", math.MaxUint64, VerSeq, false)
|
|
if err != nil && err != syscall.ENOENT {
|
|
log.LogErrorf("action[cleanSnapshot] parent %v verSeq %v err %v", parent.Name, VerSeq, err)
|
|
return err
|
|
}
|
|
if err == syscall.ENOENT {
|
|
log.LogWarnf("action[cleanSnapshot] parent %v verSeq %v found nothing", parent.Name, VerSeq)
|
|
continue
|
|
}
|
|
for _, child := range children {
|
|
if ino, err = gMetaWrapper.Delete_Ver_ll(parent.Inode, child.Name, proto.IsDir(child.Type), VerSeq, snapshotFullPath); err != nil || ino == nil {
|
|
log.LogErrorf("action[cleanSnapshot] parent %v Delete_ll_EX child name %v verSeq %v err %v", parent.Name, child.Name, VerSeq, err)
|
|
} else {
|
|
log.LogDebugf("action[cleanSnapshot] parent %v Delete_ll_EX child name %v verSeq %v ino %v success", parent.Name, child.Name, VerSeq, ino)
|
|
}
|
|
}
|
|
parents = append(parents, children...)
|
|
} else {
|
|
log.LogDebugf("action[cleanSnapshot] file %v %v is not dir", parent.Inode, parent.Name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|