mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
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:
parent
fa30b796a8
commit
6e46fa1fd4
@ -12901,6 +12901,10 @@
|
||||
"format": "int32",
|
||||
"description": "iSCSI target lun number."
|
||||
},
|
||||
"iscsiInterface": {
|
||||
"type": "string",
|
||||
"description": "Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "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"
|
||||
|
@ -3319,6 +3319,10 @@
|
||||
"format": "int32",
|
||||
"description": "iSCSI target lun number."
|
||||
},
|
||||
"iscsiInterface": {
|
||||
"type": "string",
|
||||
"description": "Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport."
|
||||
},
|
||||
"fsType": {
|
||||
"type": "string",
|
||||
"description": "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"
|
||||
|
@ -2163,6 +2163,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">iscsiInterface</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Optional: Defaults to <em>default</em> (tcp). iSCSI interface name that uses an iSCSI transport.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">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: <a href="http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi">http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
@ -4268,7 +4275,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2015-12-01 22:09:32 UTC
|
||||
Last updated 2015-12-07 03:51:23 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -1814,6 +1814,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">iscsiInterface</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Optional: Defaults to <em>default</em> (tcp). iSCSI interface name that uses an iSCSI transport.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">fsType</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">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: <a href="http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi">http://releases.k8s.io/HEAD/docs/user-guide/volumes.md#iscsi</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
@ -6901,7 +6908,7 @@ The resulting set of endpoints can be viewed as:<br>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2015-11-29 16:46:14 UTC
|
||||
Last updated 2015-12-01 22:56:16 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -51,6 +51,8 @@ GCE does not provide preconfigured Fedora 21 image, so I set up the iSCSI target
|
||||
|
||||
Once you have installed iSCSI initiator and new Kubernetes, you can create a pod based on the example *iscsi.json*. In the pod JSON, you need to provide *targetPortal* (the iSCSI target's **IP** address and *port* if not the default port 3260), target's *iqn*, *lun*, and the type of the filesystem that has been created on the lun, and *readOnly* boolean. No initiator information is required.
|
||||
|
||||
If you want to use an iSCSI offload card or other open-iscsi transports besides tcp, setup an iSCSI interface and provide *iscsiInterface* in the pod JSON. The default name for an iscsi iface (open-iscsi parameter iface.iscsi\_ifacename) is in the format transport\_name.hwaddress when generated by iscsiadm. See [open-iscsi](http://www.open-iscsi.org/docs/README) or [openstack](http://docs.openstack.org/kilo/config-reference/content/iscsi-iface-config.html) for detailed configuration information.
|
||||
|
||||
**Note:** If you have followed the instructions in the links above you
|
||||
may have partitioned the device, the iSCSI volume plugin does not
|
||||
currently support partitions so format the device as one partition.
|
||||
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user