github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/cmd/lhsm/hsm_import.go (about)

     1  // Copyright (c) 2018 DDN. All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"os/user"
    11  	"path"
    12  	"strconv"
    13  	"syscall"
    14  	"time"
    15  
    16  	"github.com/intel-hpdd/go-lustre/hsm"
    17  	"github.com/intel-hpdd/go-lustre/llapi"
    18  	"github.com/intel-hpdd/lemur/cmd/lhsmd/agent/fileid"
    19  	"github.com/intel-hpdd/logging/debug"
    20  	"github.com/pkg/errors"
    21  	cli "gopkg.in/urfave/cli.v1"
    22  )
    23  
    24  func strtou32(s string) (uint32, error) {
    25  	v, err := strconv.ParseUint(s, 0, 32)
    26  	if err != nil {
    27  		return 0, err
    28  	}
    29  	return uint32(v), nil
    30  }
    31  
    32  func lookupUser(s string) (uint32, error) {
    33  	if s == "" {
    34  		return 0, errors.New("no user or uid specified")
    35  	}
    36  
    37  	if val, err := strtou32(s); err == nil {
    38  		return val, nil
    39  	}
    40  
    41  	u, err := user.Lookup(s)
    42  	if err != nil {
    43  		return 0, err
    44  	}
    45  
    46  	return strtou32(u.Uid)
    47  }
    48  
    49  func lookupGroup(s string) (uint32, error) {
    50  	if s == "" {
    51  		return 0, errors.New("no group or groupd id specified")
    52  	}
    53  
    54  	if val, err := strtou32(s); err == nil {
    55  		return val, nil
    56  	}
    57  
    58  	g, err := user.LookupGroup(s)
    59  	if err != nil {
    60  		return 0, err
    61  	}
    62  
    63  	return strtou32(g.Gid)
    64  
    65  }
    66  
    67  func getTimeOpt(c *cli.Context, name string, dflt time.Time) (time.Time, error) {
    68  	value := dflt
    69  	if c.String(name) != "" {
    70  		t, err := parseTimestamp(c.String("timefmt"), c.String(name))
    71  		if err != nil {
    72  			return value, errors.Wrap(err, "Unable to parse mtime")
    73  		}
    74  		value = t
    75  	}
    76  	return value, nil
    77  }
    78  
    79  func parseTimestamp(timefmt, s string) (time.Time, error) {
    80  	t, err := time.Parse(timefmt, s)
    81  	if err != nil {
    82  		return t, err
    83  	}
    84  	return t, err
    85  }
    86  
    87  // Construct a FileInfo compatible struct.
    88  type myFileInfo struct {
    89  	name string         // base name of the file
    90  	stat syscall.Stat_t // underlying data source (can return nil)
    91  }
    92  
    93  func (fi *myFileInfo) Name() string {
    94  	return fi.name
    95  }
    96  
    97  func (fi *myFileInfo) Size() int64 {
    98  	return fi.stat.Size
    99  }
   100  
   101  func (fi *myFileInfo) Mode() os.FileMode {
   102  	return os.FileMode(fi.stat.Mode)
   103  }
   104  
   105  func (fi *myFileInfo) ModTime() time.Time {
   106  	return time.Unix(fi.stat.Mtim.Sec, fi.stat.Mtim.Nsec)
   107  }
   108  
   109  func (fi *myFileInfo) IsDir() bool {
   110  	return fi.Mode().IsDir()
   111  }
   112  func (fi *myFileInfo) Sys() interface{} {
   113  	return &fi.stat
   114  }
   115  
   116  func hsmImportAction(c *cli.Context) error {
   117  	logContext(c)
   118  	archive := c.Uint("id")
   119  	uuid := c.String("uuid")
   120  	hash := c.String("hash")
   121  	args := c.Args()
   122  	if len(args) != 1 {
   123  		return errors.New("HSM import only supports one file")
   124  	}
   125  
   126  	uid, err := lookupUser(c.String("uid"))
   127  	if err != nil {
   128  		return errors.Wrap(err, "Valid user required.")
   129  
   130  	}
   131  
   132  	gid, err := lookupGroup(c.String("gid"))
   133  	if err != nil {
   134  		return errors.Wrap(err, "Valid group required.")
   135  	}
   136  
   137  	mtime, err := getTimeOpt(c, "mtime", time.Now())
   138  	if err != nil {
   139  		return errors.Wrap(err, "Unable to parse mtime")
   140  	}
   141  
   142  	atime, err := getTimeOpt(c, "atime", mtime)
   143  	if err != nil {
   144  		return errors.Wrap(err, "Unable to parse atime")
   145  	}
   146  
   147  	fi := &myFileInfo{}
   148  	fi.name = args[0]
   149  
   150  	stat := &fi.stat
   151  	stat.Uid = uid
   152  	stat.Gid = gid
   153  	stat.Mode = uint32(c.Uint("mode"))
   154  	stat.Size = c.Int64("size")
   155  	stat.Atim.Sec = int64(atime.Unix())
   156  	stat.Atim.Nsec = int64(atime.Nanosecond())
   157  	stat.Mtim.Sec = int64(mtime.Unix())
   158  	stat.Mtim.Nsec = int64(atime.Nanosecond())
   159  
   160  	layout := llapi.DefaultDataLayout()
   161  	layout.StripeCount = c.Int("stripe_count")
   162  	layout.StripeSize = c.Int("stripe_size")
   163  	layout.PoolName = c.String("pool")
   164  
   165  	debug.Printf("%v, %v, %v, %v", archive, uuid, hash, args[0])
   166  	_, err = hsm.Import(args[0], archive, fi, layout)
   167  	if err != nil {
   168  		return errors.Wrap(err, "Import failed")
   169  	}
   170  
   171  	if uuid != "" {
   172  		fileid.UUID.Set(args[0], []byte(uuid))
   173  	}
   174  
   175  	if hash != "" {
   176  		fileid.Hash.Set(args[0], []byte(hash))
   177  	}
   178  
   179  	return nil
   180  }
   181  
   182  func clone(srcPath, targetPath string, stripeCount, stripeSize int, poolName string, requiredState llapi.HsmStateFlag) error {
   183  	srcStat, err := os.Stat(srcPath)
   184  	if err != nil {
   185  		return errors.Wrap(err, srcPath)
   186  	}
   187  
   188  	if srcStat.IsDir() {
   189  		return errors.Errorf("can't clone a directory: %s", srcPath)
   190  	}
   191  
   192  	tgtStat, err := os.Stat(targetPath)
   193  	if err == nil {
   194  		if tgtStat.IsDir() {
   195  			targetPath = path.Join(targetPath, srcStat.Name())
   196  			_, err = os.Stat(targetPath)
   197  			if err == nil {
   198  				return errors.Errorf("%s: already exists, can't overwrite", targetPath)
   199  			}
   200  		}
   201  	}
   202  
   203  	state, archive, err := llapi.GetHsmFileStatus(srcPath)
   204  	if err != nil {
   205  		return errors.Wrap(err, "unable to get HSM status")
   206  	}
   207  
   208  	if !state.HasFlag(requiredState) {
   209  		return errors.Errorf("%s: file not in in %s state. ", srcPath, requiredState)
   210  	}
   211  
   212  	layout, err := llapi.FileDataLayout(srcPath)
   213  	if err != nil {
   214  		return errors.Wrap(err, "failed to get layout")
   215  	}
   216  
   217  	if stripeCount != 0 {
   218  		layout.StripeCount = stripeCount
   219  	}
   220  
   221  	if stripeSize != 0 {
   222  		layout.StripeSize = stripeSize
   223  
   224  	}
   225  
   226  	if poolName != "" {
   227  		layout.PoolName = poolName
   228  	}
   229  
   230  	//debug.Printf("%v, %v, %v, %v", archive, uuid, hash, srcPath)
   231  	_, err = hsm.Import(targetPath, uint(archive), srcStat, layout)
   232  	if err != nil {
   233  		return errors.Wrap(err, "Import failed")
   234  	}
   235  
   236  	uuid, err := fileid.UUID.Get(srcPath)
   237  	if err == nil && len(uuid) > 0 {
   238  		fileid.UUID.Set(targetPath, uuid)
   239  	}
   240  
   241  	hash, err := fileid.Hash.Get(srcPath)
   242  	if err == nil && len(hash) > 0 {
   243  		fileid.Hash.Set(targetPath, hash)
   244  	}
   245  	return nil
   246  }
   247  
   248  func hsmCloneAction(c *cli.Context) error {
   249  	logContext(c)
   250  	args := c.Args()
   251  	if len(args) != 2 {
   252  		return errors.New("HSM clone requires source and destination argument")
   253  	}
   254  
   255  	return clone(args[0], args[1], c.Int("stripe_count"), c.Int("stripe_size"), c.String("pool"), llapi.HsmFileArchived)
   256  }
   257  
   258  // tempName returns a tempname based on path provided
   259  func tempName(p string) string {
   260  	return fmt.Sprintf("%s#%x", p, os.Getpid())
   261  }
   262  func hsmRestripeAction(c *cli.Context) error {
   263  	logContext(c)
   264  	args := c.Args()
   265  	if len(args) != 1 {
   266  		return errors.New("Can only restripe one file at a time.")
   267  	}
   268  	tempFile := tempName(args[0])
   269  	err := clone(args[0], tempFile, c.Int("stripe_count"), c.Int("stripe_size"), c.String("pool"), llapi.HsmFileReleased)
   270  	if err != nil {
   271  		os.Remove(tempFile)
   272  		return errors.Wrap(err, "Unable to restripe")
   273  	}
   274  	err = os.Rename(tempFile, args[0])
   275  	if err != nil {
   276  		return errors.Wrap(err, "Unable to rename")
   277  	}
   278  	return nil
   279  }