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 }