github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // TODO(thockin): This whole pkg is pretty linux-centric. As soon as we have 18 // an alternate platform, we will need to abstract further. 19 package mount 20 21 import ( 22 "strings" 23 24 "github.com/golang/glog" 25 "k8s.io/kubernetes/pkg/util/exec" 26 ) 27 28 type Interface interface { 29 // Mount mounts source to target as fstype with given options. 30 Mount(source string, target string, fstype string, options []string) error 31 // Unmount unmounts given target. 32 Unmount(target string) error 33 // List returns a list of all mounted filesystems. This can be large. 34 // On some platforms, reading mounts is not guaranteed consistent (i.e. 35 // it could change between chunked reads). This is guaranteed to be 36 // consistent. 37 List() ([]MountPoint, error) 38 // IsLikelyNotMountPoint determines if a directory is a mountpoint. 39 IsLikelyNotMountPoint(file string) (bool, error) 40 } 41 42 // This represents a single line in /proc/mounts or /etc/fstab. 43 type MountPoint struct { 44 Device string 45 Path string 46 Type string 47 Opts []string 48 Freq int 49 Pass int 50 } 51 52 // SafeFormatAndMount probes a device to see if it is formatted. If 53 // so it mounts it otherwise it formats it and mounts it 54 type SafeFormatAndMount struct { 55 Interface 56 Runner exec.Interface 57 } 58 59 // Mount mounts the given disk. If the disk is not formatted and the disk is not being mounted as read only 60 // it will format the disk first then mount it. 61 func (mounter *SafeFormatAndMount) Mount(source string, target string, fstype string, options []string) error { 62 // Don't attempt to format if mounting as readonly. Go straight to mounting. 63 for _, option := range options { 64 if option == "ro" { 65 return mounter.Interface.Mount(source, target, fstype, options) 66 } 67 } 68 return mounter.formatAndMount(source, target, fstype, options) 69 } 70 71 // formatAndMount uses unix utils to format and mount the given disk 72 func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error { 73 options = append(options, "defaults") 74 75 // Try to mount the disk 76 err := mounter.Interface.Mount(source, target, fstype, options) 77 if err != nil { 78 // It is possible that this disk is not formatted. Double check using diskLooksUnformatted 79 notFormatted, err := mounter.diskLooksUnformatted(source) 80 if err == nil && notFormatted { 81 args := []string{source} 82 // Disk is unformatted so format it. 83 // Use 'ext4' as the default 84 if len(fstype) == 0 { 85 fstype = "ext4" 86 } 87 if fstype == "ext4" || fstype == "ext3" { 88 args = []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", source} 89 } 90 cmd := mounter.Runner.Command("mkfs."+fstype, args...) 91 _, err := cmd.CombinedOutput() 92 if err == nil { 93 // the disk has been formatted sucessfully try to mount it again. 94 return mounter.Interface.Mount(source, target, fstype, options) 95 } 96 return err 97 } 98 } 99 return err 100 } 101 102 // diskLooksUnformatted uses 'lsblk' to see if the given disk is unformated 103 func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) { 104 args := []string{"-nd", "-o", "FSTYPE", disk} 105 cmd := mounter.Runner.Command("lsblk", args...) 106 dataOut, err := cmd.CombinedOutput() 107 output := strings.TrimSpace(string(dataOut)) 108 109 // TODO (#13212): check if this disk has partitions and return false, and 110 // an error if so. 111 112 if err != nil { 113 return false, err 114 } 115 116 return output == "", nil 117 } 118 119 // New returns a mount.Interface for the current system. 120 func New() Interface { 121 return &Mounter{} 122 } 123 124 // GetMountRefs finds all other references to the device referenced 125 // by mountPath; returns a list of paths. 126 func GetMountRefs(mounter Interface, mountPath string) ([]string, error) { 127 mps, err := mounter.List() 128 if err != nil { 129 return nil, err 130 } 131 132 // Find the device name. 133 deviceName := "" 134 for i := range mps { 135 if mps[i].Path == mountPath { 136 deviceName = mps[i].Device 137 break 138 } 139 } 140 141 // Find all references to the device. 142 var refs []string 143 if deviceName == "" { 144 glog.Warningf("could not determine device for path: %q", mountPath) 145 } else { 146 for i := range mps { 147 if mps[i].Device == deviceName && mps[i].Path != mountPath { 148 refs = append(refs, mps[i].Path) 149 } 150 } 151 } 152 return refs, nil 153 } 154 155 // GetDeviceNameFromMount: given a mnt point, find the device from /proc/mounts 156 // returns the device name, reference count, and error code 157 func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) { 158 mps, err := mounter.List() 159 if err != nil { 160 return "", 0, err 161 } 162 163 // Find the device name. 164 // FIXME if multiple devices mounted on the same mount path, only the first one is returned 165 device := "" 166 for i := range mps { 167 if mps[i].Path == mountPath { 168 device = mps[i].Device 169 break 170 } 171 } 172 173 // Find all references to the device. 174 refCount := 0 175 for i := range mps { 176 if mps[i].Device == device { 177 refCount++ 178 } 179 } 180 return device, refCount, nil 181 }