github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/cmd/lhsmd/agent/mountpoints.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 agent 6 7 import ( 8 "fmt" 9 "os" 10 "time" 11 12 "github.com/pkg/errors" 13 14 "golang.org/x/net/context" 15 "golang.org/x/sys/unix" 16 17 "github.com/intel-hpdd/logging/audit" 18 "github.com/intel-hpdd/logging/debug" 19 "github.com/intel-hpdd/go-lustre/pkg/mntent" 20 ) 21 22 // UnmountTimeout is the time, in seconds, that an unmount will be retried 23 // before failing with an error. 24 const UnmountTimeout = 10 25 26 type ( 27 mountConfig struct { 28 Device string 29 Directory string 30 Type string 31 Options clientMountOptions 32 Flags uintptr 33 } 34 ) 35 36 func (mc *mountConfig) String() string { 37 return fmt.Sprintf("%s %s %s %s (%d)", mc.Device, mc.Directory, mc.Type, mc.Options, mc.Flags) 38 } 39 40 func mountClient(cfg *mountConfig) error { 41 if err := os.MkdirAll(cfg.Directory, 0700); err != nil { 42 return errors.Wrap(err, "mkdir failed") 43 } 44 45 return unix.Mount(cfg.Device, cfg.Directory, cfg.Type, cfg.Flags, cfg.Options.String()) 46 } 47 48 func createMountConfigs(cfg *Config) []*mountConfig { 49 device := cfg.ClientDevice.String() 50 // this is what mount_lustre.c does... 51 opts := append(cfg.ClientMountOptions, "device="+device) 52 53 var flags uintptr 54 // LU-1783 -- force strictatime until a kernel vfs bug is fixed 55 flags |= unix.MS_STRICTATIME 56 57 // Create the agent mountpoint first, then add per-plugin mountpoints 58 configs := []*mountConfig{ 59 &mountConfig{ 60 Device: device, 61 Directory: cfg.AgentMountpoint(), 62 Type: "lustre", 63 Options: opts, 64 Flags: flags, 65 }, 66 } 67 68 for _, plugin := range cfg.Plugins() { 69 configs = append(configs, &mountConfig{ 70 Device: device, 71 Directory: plugin.ClientMount, 72 Type: "lustre", 73 Options: opts, 74 Flags: flags, 75 }) 76 } 77 78 return configs 79 } 80 81 // ConfigureMounts configures a set of Lustre client mounts; one for the agent 82 // and one for each configure data mover. 83 func ConfigureMounts(cfg *Config) error { 84 entries, err := mntent.GetMounted() 85 if err != nil { 86 return errors.Wrap(err, "failed to get list of mounted filesystems") 87 } 88 89 for _, mc := range createMountConfigs(cfg) { 90 if _, err := entries.ByDir(mc.Directory); err == nil { 91 continue 92 } 93 94 debug.Printf("Mounting client at %s", mc.Directory) 95 if err := mountClient(mc); err != nil { 96 return errors.Wrap(err, "mount client failed") 97 } 98 } 99 100 return nil 101 } 102 103 func doTimedUnmount(dir string) error { 104 done := make(chan struct{}) 105 lastError := make(chan error) 106 107 // This feels a little baroque, but it accomplishes two goals: 108 // 1) Don't leak this goroutine if we time out 109 // 2) Make sure that we safely get an error from unix.Unmount 110 // back out to the caller 111 ctx, cancel := context.WithCancel(context.Background()) 112 go func(ctx context.Context) { 113 var err error 114 for { 115 select { 116 case <-ctx.Done(): 117 lastError <- err 118 return 119 default: 120 err = unix.Unmount(dir, 0) 121 if err == nil { 122 close(done) 123 lastError <- err 124 return 125 } 126 audit.Logf("Waiting for %s to be unmounted", dir) 127 time.Sleep(1 * time.Second) 128 } 129 } 130 }(ctx) 131 132 for { 133 select { 134 case <-done: 135 return <-lastError 136 case <-time.After(time.Duration(UnmountTimeout) * time.Second): 137 cancel() 138 return errors.Wrapf(<-lastError, "Unmount of %s timed out after %d seconds", dir, UnmountTimeout) 139 } 140 } 141 } 142 143 // CleanupMounts unmounts the Lustre client mounts configured by 144 // ConfigureMounts(). 145 func CleanupMounts(cfg *Config) error { 146 entries, err := mntent.GetMounted() 147 if err != nil { 148 return errors.Wrap(err, "failed to get list of mounted filesystems") 149 } 150 151 // Reverse the generated slice to perform mover unmounts first, 152 // finishing with the agent unmount. 153 mcList := createMountConfigs(cfg) 154 revList := make([]*mountConfig, len(mcList)) 155 for i := range mcList { 156 revList[i] = mcList[len(mcList)-1-i] 157 } 158 159 for _, mc := range revList { 160 if _, err := entries.ByDir(mc.Directory); err != nil { 161 continue 162 } 163 164 debug.Printf("Cleaning up %s", mc.Directory) 165 if err := doTimedUnmount(mc.Directory); err != nil { 166 return errors.Wrapf(err, "Failed to unmount %s", mc.Directory) 167 } 168 } 169 170 return nil 171 }