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  }