2015-03-13 21:31:13 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2015 The Kubernetes Authors All rights reserved .
2015-03-13 21:31:13 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package iscsi
import (
"errors"
"os"
"path"
"strings"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount"
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
"github.com/golang/glog"
)
// stat a path, if not exists, retry maxRetries times
func waitForPathToExist ( devicePath string , maxRetries int ) bool {
for i := 0 ; i < maxRetries ; i ++ {
_ , err := os . Stat ( devicePath )
if err == nil {
return true
}
if err != nil && ! os . IsNotExist ( err ) {
return false
}
time . Sleep ( time . Second )
}
return false
}
// getDevicePrefixRefCount: given a prefix of device path, find its reference count from /proc/mounts
// returns the reference count to the device and error code
// for services like iscsi construct multiple device paths with the same prefix pattern.
// this function aggregates all references to a service based on the prefix pattern
// More specifically, this prefix semantics is to aggregate disk paths that belong to the same iSCSI target/iqn pair.
// an iSCSI target could expose multiple LUNs through the same IQN, and Linux iSCSI initiator creates disk paths that start the same prefix but end with different LUN number
// When we decide whether it is time to logout a target, we have to see if none of the LUNs are used any more.
// That's where the prefix based ref count kicks in. If we only count the disks using exact match, we could log other disks out.
func getDevicePrefixRefCount ( mounter mount . Interface , deviceNamePrefix string ) ( int , error ) {
mps , err := mounter . List ( )
if err != nil {
return - 1 , err
}
// Find the number of references to the device.
refCount := 0
for i := range mps {
if strings . HasPrefix ( mps [ i ] . Device , deviceNamePrefix ) {
refCount ++
}
}
return refCount , nil
}
// make a directory like /var/lib/kubelet/plugins/kubernetes.io/pod/iscsi/portal-iqn-some_iqn-lun-0
func makePDNameInternal ( host volume . VolumeHost , portal string , iqn string , lun string ) string {
return path . Join ( host . GetPluginDir ( ISCSIPluginName ) , "iscsi" , portal + "-iqn-" + iqn + "-lun-" + lun )
}
type ISCSIUtil struct { }
func ( util * ISCSIUtil ) MakeGlobalPDName ( iscsi iscsiDisk ) string {
return makePDNameInternal ( iscsi . plugin . host , iscsi . portal , iscsi . iqn , iscsi . lun )
}
func ( util * ISCSIUtil ) AttachDisk ( iscsi iscsiDisk ) error {
devicePath := strings . Join ( [ ] string { "/dev/disk/by-path/ip" , iscsi . portal , "iscsi" , iscsi . iqn , "lun" , iscsi . lun } , "-" )
exist := waitForPathToExist ( devicePath , 1 )
if exist == false {
// discover iscsi target
2015-06-26 18:46:41 +00:00
out , err := iscsi . plugin . execCommand ( "iscsiadm" , [ ] string { "-m" , "discovery" , "-t" , "sendtargets" , "-p" , iscsi . portal } )
2015-03-13 21:31:13 +00:00
if err != nil {
2015-06-26 18:46:41 +00:00
glog . Errorf ( "iscsi: failed to sendtargets to portal %s error: %s" , iscsi . portal , string ( out ) )
2015-03-13 21:31:13 +00:00
return err
}
// login to iscsi target
2015-06-26 18:46:41 +00:00
out , err = iscsi . plugin . execCommand ( "iscsiadm" , [ ] string { "-m" , "node" , "-p" , iscsi . portal , "-T" , iscsi . iqn , "--login" } )
2015-03-13 21:31:13 +00:00
if err != nil {
2015-06-26 18:46:41 +00:00
glog . Errorf ( "iscsi: failed to attach disk:Error: %s (%v)" , string ( out ) , err )
2015-03-13 21:31:13 +00:00
return err
}
exist = waitForPathToExist ( devicePath , 10 )
if ! exist {
return errors . New ( "Could not attach disk: Timeout after 10s" )
}
}
// mount it
globalPDPath := iscsi . manager . MakeGlobalPDName ( iscsi )
mountpoint , err := iscsi . mounter . IsMountPoint ( globalPDPath )
if mountpoint {
glog . Infof ( "iscsi: %s already mounted" , globalPDPath )
return nil
}
if err := os . MkdirAll ( globalPDPath , 0750 ) ; err != nil {
glog . Errorf ( "iscsi: failed to mkdir %s, error" , globalPDPath )
return err
}
2015-04-03 01:08:04 +00:00
err = iscsi . mounter . Mount ( devicePath , globalPDPath , iscsi . fsType , nil )
2015-03-13 21:31:13 +00:00
if err != nil {
glog . Errorf ( "iscsi: failed to mount iscsi volume %s [%s] to %s, error %v" , devicePath , iscsi . fsType , globalPDPath , err )
}
return err
}
func ( util * ISCSIUtil ) DetachDisk ( iscsi iscsiDisk , mntPath string ) error {
device , cnt , err := mount . GetDeviceNameFromMount ( iscsi . mounter , mntPath )
if err != nil {
glog . Errorf ( "iscsi detach disk: failed to get device from mnt: %s\nError: %v" , mntPath , err )
return err
}
2015-04-03 01:08:04 +00:00
if err = iscsi . mounter . Unmount ( mntPath ) ; err != nil {
glog . Errorf ( "iscsi detach disk: failed to unmount: %s\nError: %v" , mntPath , err )
2015-03-13 21:31:13 +00:00
return err
}
cnt --
// if device is no longer used, see if need to logout the target
if cnt == 0 {
// strip -lun- from device path
ind := strings . LastIndex ( device , "-lun-" )
prefix := device [ : ( ind - 1 ) ]
refCount , err := getDevicePrefixRefCount ( iscsi . mounter , prefix )
if err == nil && refCount == 0 {
// this portal/iqn are no longer referenced, log out
// extract portal and iqn from device path
ind1 := strings . LastIndex ( device , "-iscsi-" )
portal := device [ ( len ( "/dev/disk/by-path/ip-" ) ) : ind1 ]
iqn := device [ ind1 + len ( "-iscsi-" ) : ind ]
glog . Infof ( "iscsi: log out target %s iqn %s" , portal , iqn )
2015-06-26 18:46:41 +00:00
out , err := iscsi . plugin . execCommand ( "iscsiadm" , [ ] string { "-m" , "node" , "-p" , portal , "-T" , iqn , "--logout" } )
2015-03-13 21:31:13 +00:00
if err != nil {
2015-06-26 18:46:41 +00:00
glog . Errorf ( "iscsi: failed to detach disk Error: %s" , string ( out ) )
2015-03-13 21:31:13 +00:00
}
}
}
return nil
}