Add support for open-iscsi transports.
This enables use of software or hardware transports viz. be2iscsi, bnx2i, cxgb3i, cxgb4i, qla4xx, iser and ocs. The default transport (tcp) happens to be called "default". Use of non-default transports changes the disk path to the following format: /dev/disk/by-path/pci-<pci_id>-ip-<portal>-iscsi-<iqn>-lun-<lun_id>
This commit is contained in:
@@ -645,6 +645,7 @@ func deepCopy_api_ISCSIVolumeSource(in ISCSIVolumeSource, out *ISCSIVolumeSource
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = in.Lun
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
|
||||
@@ -266,6 +266,12 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
||||
c.Fuzz(t.Interface())
|
||||
}
|
||||
},
|
||||
func(i *api.ISCSIVolumeSource, c fuzz.Continue) {
|
||||
i.ISCSIInterface = c.RandString()
|
||||
if i.ISCSIInterface == "" {
|
||||
i.ISCSIInterface = "default"
|
||||
}
|
||||
},
|
||||
func(d *api.DNSPolicy, c fuzz.Continue) {
|
||||
policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault}
|
||||
*d = policies[c.Rand.Intn(len(policies))]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -449,6 +449,8 @@ type ISCSIVolumeSource struct {
|
||||
IQN string `json:"iqn,omitempty"`
|
||||
// Required: iSCSI target lun number
|
||||
Lun int `json:"lun,omitempty"`
|
||||
// Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport.
|
||||
ISCSIInterface string `json:"iscsiInterface,omitempty"`
|
||||
// Required: Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs"
|
||||
|
||||
@@ -919,6 +919,7 @@ func autoconvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource(in *api.ISCSIVolu
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = int32(in.Lun)
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
@@ -3945,6 +3946,7 @@ func autoconvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource(in *ISCSIVolumeSo
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = int(in.Lun)
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
|
||||
@@ -681,6 +681,7 @@ func deepCopy_v1_ISCSIVolumeSource(in ISCSIVolumeSource, out *ISCSIVolumeSource,
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = in.Lun
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
|
||||
@@ -164,6 +164,11 @@ func addDefaultingFuncs() {
|
||||
obj.Status.Phase = ClaimPending
|
||||
}
|
||||
},
|
||||
func(obj *ISCSIVolumeSource) {
|
||||
if obj.ISCSIInterface == "" {
|
||||
obj.ISCSIInterface = "default"
|
||||
}
|
||||
},
|
||||
func(obj *Endpoints) {
|
||||
for i := range obj.Subsets {
|
||||
ss := &obj.Subsets[i]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -715,6 +715,8 @@ type ISCSIVolumeSource struct {
|
||||
IQN string `json:"iqn"`
|
||||
// iSCSI target lun number.
|
||||
Lun int32 `json:"lun"`
|
||||
// Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport.
|
||||
ISCSIInterface string `json:"iscsiInterface,omitempty"`
|
||||
// Filesystem type of the volume that you want to mount.
|
||||
// Tip: Ensure that the filesystem type is supported by the host operating system.
|
||||
// Examples: "ext4", "xfs", "ntfs".
|
||||
|
||||
@@ -467,12 +467,13 @@ func (HostPathVolumeSource) SwaggerDoc() map[string]string {
|
||||
}
|
||||
|
||||
var map_ISCSIVolumeSource = map[string]string{
|
||||
"": "ISCSIVolumeSource describes an ISCSI Disk can only be mounted as read/write once.",
|
||||
"targetPortal": "iSCSI target portal. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).",
|
||||
"iqn": "Target iSCSI Qualified Name.",
|
||||
"lun": "iSCSI target lun number.",
|
||||
"fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi",
|
||||
"readOnly": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.",
|
||||
"": "ISCSIVolumeSource describes an ISCSI Disk can only be mounted as read/write once.",
|
||||
"targetPortal": "iSCSI target portal. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).",
|
||||
"iqn": "Target iSCSI Qualified Name.",
|
||||
"lun": "iSCSI target lun number.",
|
||||
"iscsiInterface": "Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport.",
|
||||
"fsType": "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". More info: http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi",
|
||||
"readOnly": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.",
|
||||
}
|
||||
|
||||
func (ISCSIVolumeSource) SwaggerDoc() map[string]string {
|
||||
|
||||
@@ -348,6 +348,7 @@ func deepCopy_api_ISCSIVolumeSource(in api.ISCSIVolumeSource, out *api.ISCSIVolu
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = in.Lun
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
|
||||
@@ -490,6 +490,7 @@ func autoconvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource(in *api.ISCSIVolu
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = int32(in.Lun)
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
@@ -1550,6 +1551,7 @@ func autoconvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource(in *v1.ISCSIVolum
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = int(in.Lun)
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
|
||||
@@ -384,6 +384,7 @@ func deepCopy_v1_ISCSIVolumeSource(in v1.ISCSIVolumeSource, out *v1.ISCSIVolumeS
|
||||
out.TargetPortal = in.TargetPortal
|
||||
out.IQN = in.IQN
|
||||
out.Lun = in.Lun
|
||||
out.ISCSIInterface = in.ISCSIInterface
|
||||
out.FSType = in.FSType
|
||||
out.ReadOnly = in.ReadOnly
|
||||
return nil
|
||||
|
||||
@@ -583,9 +583,10 @@ func printISCSIVolumeSource(iscsi *api.ISCSIVolumeSource, out io.Writer) {
|
||||
" TargetPortal:\t%v\n"+
|
||||
" IQN:\t%v\n"+
|
||||
" Lun:\t%v\n"+
|
||||
" ISCSIInterface\t%v\n"+
|
||||
" FSType:\t%v\n"+
|
||||
" ReadOnly:\t%v\n",
|
||||
iscsi.TargetPortal, iscsi.IQN, iscsi.Lun, iscsi.FSType, iscsi.ReadOnly)
|
||||
iscsi.TargetPortal, iscsi.IQN, iscsi.Lun, iscsi.ISCSIInterface, iscsi.FSType, iscsi.ReadOnly)
|
||||
}
|
||||
|
||||
func printGlusterfsVolumeSource(glusterfs *api.GlusterfsVolumeSource, out io.Writer) {
|
||||
|
||||
@@ -97,6 +97,8 @@ func (plugin *iscsiPlugin) newBuilderInternal(spec *volume.Spec, podUID types.UI
|
||||
lun := strconv.Itoa(iscsi.Lun)
|
||||
portal := portalBuilder(iscsi.TargetPortal)
|
||||
|
||||
iface := iscsi.ISCSIInterface
|
||||
|
||||
return &iscsiDiskBuilder{
|
||||
iscsiDisk: &iscsiDisk{
|
||||
podUID: podUID,
|
||||
@@ -104,6 +106,7 @@ func (plugin *iscsiPlugin) newBuilderInternal(spec *volume.Spec, podUID types.UI
|
||||
portal: portal,
|
||||
iqn: iscsi.IQN,
|
||||
lun: lun,
|
||||
iface: iface,
|
||||
manager: manager,
|
||||
plugin: plugin},
|
||||
fsType: iscsi.FSType,
|
||||
@@ -140,6 +143,7 @@ type iscsiDisk struct {
|
||||
portal string
|
||||
iqn string
|
||||
lun string
|
||||
iface string
|
||||
plugin *iscsiPlugin
|
||||
// Utility interface that provides API calls to the provider to attach/detach disks.
|
||||
manager diskManager
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -30,9 +31,26 @@ import (
|
||||
)
|
||||
|
||||
// stat a path, if not exists, retry maxRetries times
|
||||
func waitForPathToExist(devicePath string, maxRetries int) bool {
|
||||
// when iscsi transports other than default are used, use glob instead as pci id of device is unknown
|
||||
type StatFunc func(string) (os.FileInfo, error)
|
||||
type GlobFunc func(string) ([]string, error)
|
||||
|
||||
func waitForPathToExist(devicePath string, maxRetries int, deviceInterface string) bool {
|
||||
// This makes unit testing a lot easier
|
||||
return waitForPathToExistInternal(devicePath, maxRetries, deviceInterface, os.Stat, filepath.Glob)
|
||||
}
|
||||
|
||||
func waitForPathToExistInternal(devicePath string, maxRetries int, deviceInterface string, osStat StatFunc, filepathGlob GlobFunc) bool {
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
_, err := os.Stat(devicePath)
|
||||
var err error
|
||||
if deviceInterface == "default" {
|
||||
_, err = osStat(devicePath)
|
||||
} else {
|
||||
fpath, _ := filepathGlob(devicePath)
|
||||
if fpath == nil {
|
||||
err = os.ErrNotExist
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
@@ -80,22 +98,27 @@ func (util *ISCSIUtil) MakeGlobalPDName(iscsi iscsiDisk) string {
|
||||
}
|
||||
|
||||
func (util *ISCSIUtil) AttachDisk(b iscsiDiskBuilder) error {
|
||||
devicePath := strings.Join([]string{"/dev/disk/by-path/ip", b.portal, "iscsi", b.iqn, "lun", b.lun}, "-")
|
||||
exist := waitForPathToExist(devicePath, 1)
|
||||
var devicePath string
|
||||
if b.iface == "default" {
|
||||
devicePath = strings.Join([]string{"/dev/disk/by-path/ip", b.portal, "iscsi", b.iqn, "lun", b.lun}, "-")
|
||||
} else {
|
||||
devicePath = strings.Join([]string{"/dev/disk/by-path/pci", "*", "ip", b.portal, "iscsi", b.iqn, "lun", b.lun}, "-")
|
||||
}
|
||||
exist := waitForPathToExist(devicePath, 1, b.iface)
|
||||
if exist == false {
|
||||
// discover iscsi target
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discovery", "-t", "sendtargets", "-p", b.portal})
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discovery", "-t", "sendtargets", "-p", b.portal, "-I", b.iface})
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to sendtargets to portal %s error: %s", b.portal, string(out))
|
||||
return err
|
||||
}
|
||||
// login to iscsi target
|
||||
out, err = b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", b.portal, "-T", b.iqn, "--login"})
|
||||
out, err = b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", b.portal, "-T", b.iqn, "-I", b.iface, "--login"})
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to attach disk:Error: %s (%v)", string(out), err)
|
||||
return err
|
||||
}
|
||||
exist = waitForPathToExist(devicePath, 10)
|
||||
exist = waitForPathToExist(devicePath, 10, b.iface)
|
||||
if !exist {
|
||||
return errors.New("Could not attach disk: Timeout after 10s")
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
package iscsi
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
@@ -74,3 +76,35 @@ func TestExtractPortalAndIqn(t *testing.T) {
|
||||
t.Errorf("extractPortalAndIqn: got %v %s %s", err, portal, iqn)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeOsStat(devicePath string) (fi os.FileInfo, err error) {
|
||||
var cmd os.FileInfo
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func fakeFilepathGlob(devicePath string) (globs []string, err error) {
|
||||
return []string{devicePath}, nil
|
||||
}
|
||||
|
||||
func TestWaitForPathToExist(t *testing.T) {
|
||||
devicePath := []string{"/dev/disk/by-path/ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0",
|
||||
"/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"}
|
||||
|
||||
exist := waitForPathToExistInternal(devicePath[0], 1, "default", fakeOsStat, filepath.Glob)
|
||||
if exist == false {
|
||||
t.Errorf("waitForPathToExist: could not find path %s", devicePath[0])
|
||||
}
|
||||
exist = waitForPathToExistInternal(devicePath[0], 1, "fake_iface", fakeOsStat, filepath.Glob)
|
||||
if exist != false {
|
||||
t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[0])
|
||||
}
|
||||
|
||||
exist = waitForPathToExistInternal(devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob)
|
||||
if exist == false {
|
||||
t.Errorf("waitForPathToExist: could not find path %s", devicePath[1])
|
||||
}
|
||||
exist = waitForPathToExistInternal(devicePath[1], 1, "default", os.Stat, fakeFilepathGlob)
|
||||
if exist != false {
|
||||
t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user