mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-14 06:15:45 +00:00
This is an update that allows the iscsi volume to check if a iscsi device belongs to a mpio device
If it does belong to the device then we make sure we mount the mpio device instead of the raw device. Heuristics Login into /dev/disk/by-path/iqn-example.com.2999 -> /dev/sde Check if sde existsin in /sys/block/[dm-X]/slaves/xx If it does mount /dev/[dm-x] which will look like /dev/mapper/mpiodevicename in mount examples/iscsi has more details
This commit is contained in:
parent
56d7579bfd
commit
1811ded396
@ -32,48 +32,45 @@ Documentation for other releases can be found at
|
|||||||
|
|
||||||
<!-- END MUNGE: UNVERSIONED_WARNING -->
|
<!-- END MUNGE: UNVERSIONED_WARNING -->
|
||||||
|
|
||||||
## Step 1. Setting up iSCSI target and iSCSI initiator
|
## Introduction
|
||||||
|
|
||||||
**Setup A.** On Fedora 21 nodes
|
The Kubernetes iSCSI implementation can connect to iSCSI devices via open-iscsi and multipathd on Linux.
|
||||||
|
Currently supported features are
|
||||||
|
* Connecting to one portal
|
||||||
|
* Mounting a device directly or via multipathd
|
||||||
|
* Formatting and partitioning any new device connected
|
||||||
|
|
||||||
If you use Fedora 21 on Kubernetes node, then first install iSCSI initiator on the node:
|
## Prerequisites
|
||||||
|
|
||||||
# yum -y install iscsi-initiator-utils
|
This example expects there to be a working iSCSI target to connect to.
|
||||||
|
If there isn't one in place then it is possible to setup a software version on Linux by following these guides
|
||||||
|
|
||||||
then edit */etc/iscsi/iscsid.conf* to match your iSCSI target configuration.
|
* [Setup a iSCSI target on Fedora](http://www.server-world.info/en/note?os=Fedora_21&p=iscsi)
|
||||||
|
* [Install the iSCSI initiator on Fedora](http://www.server-world.info/en/note?os=Fedora_21&p=iscsi&f=2)
|
||||||
|
* [Install multipathd for mpio support if required](http://www.linuxstories.eu/2014/07/how-to-setup-dm-multipath-on-rhel.html)
|
||||||
|
|
||||||
I mostly followed these [instructions](http://www.server-world.info/en/note?os=Fedora_21&p=iscsi) to setup iSCSI target. and these [instructions](http://www.server-world.info/en/note?os=Fedora_21&p=iscsi&f=2) to setup iSCSI initiator.
|
|
||||||
|
|
||||||
**Setup B.** On Unbuntu 12.04 and Debian 7 nodes on Google Compute Engine (GCE)
|
## Creating the pod with iSCSI persistent storage
|
||||||
|
|
||||||
GCE does not provide preconfigured Fedora 21 image, so I set up the iSCSI target on a preconfigured Ubuntu 12.04 image, mostly following these [instructions](http://www.server-world.info/en/note?os=Ubuntu_12.04&p=iscsi). My Kubernetes cluster on GCE was running Debian 7 images, so I followed these [instructions](http://www.server-world.info/en/note?os=Debian_7.0&p=iscsi&f=2) to set up the iSCSI initiator.
|
Once you have configured the iSCSI initiator, you can create a pod based on the example *iscsi.yaml*. In the pod YAML, 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.
|
||||||
|
|
||||||
## Step 2. Creating the pod with iSCSI persistent storage
|
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 YAML. 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.
|
||||||
|
|
||||||
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
|
**Note:** If you have followed the instructions in the links above you
|
||||||
may have partitioned the device, the iSCSI volume plugin does not
|
may have partitioned the device, the iSCSI volume plugin does not
|
||||||
currently support partitions so format the device as one partition.
|
currently support partitions so format the device as one partition or leave the device raw and Kubernetes will partition and format it one first mount.
|
||||||
Make sure you have the correct device name then run the following as
|
|
||||||
root to format it:
|
|
||||||
|
Once the pod config is created, run it on the Kubernetes master:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
mkfs.ext4 /dev/<name of device>
|
kubectl create -f ./your_new_pod.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Once your pod is created, run it on the Kubernetes master:
|
Here is the example pod created and expected output:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
kubectl create -f ./your_new_pod.json
|
# kubectl create -f examples/iscsi/iscsi.yaml
|
||||||
```
|
|
||||||
|
|
||||||
Here is my command and output:
|
|
||||||
|
|
||||||
```console
|
|
||||||
# kubectl create -f examples/iscsi/iscsi.json
|
|
||||||
# kubectl get pods
|
# kubectl get pods
|
||||||
NAME READY STATUS RESTARTS AGE
|
NAME READY STATUS RESTARTS AGE
|
||||||
iscsipd 2/2 RUNNING 0 2m
|
iscsipd 2/2 RUNNING 0 2m
|
||||||
@ -81,6 +78,8 @@ iscsipd 2/2 RUNNING 0 2m
|
|||||||
|
|
||||||
On the Kubernetes node, verify the mount output
|
On the Kubernetes node, verify the mount output
|
||||||
|
|
||||||
|
For a non mpio device the output should look like the following
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# mount |grep kub
|
# mount |grep kub
|
||||||
/dev/sdb on /var/lib/kubelet/plugins/kubernetes.io/iscsi/10.0.2.15:3260-iqn.2001-04.com.example:storage.kube.sys1.xyz-lun-0 type ext4 (ro,relatime,data=ordered)
|
/dev/sdb on /var/lib/kubelet/plugins/kubernetes.io/iscsi/10.0.2.15:3260-iqn.2001-04.com.example:storage.kube.sys1.xyz-lun-0 type ext4 (ro,relatime,data=ordered)
|
||||||
@ -89,6 +88,17 @@ On the Kubernetes node, verify the mount output
|
|||||||
/dev/sdc on /var/lib/kubelet/pods/f527ca5b-6d87-11e5-aa7e-080027ff6387/volumes/kubernetes.io~iscsi/iscsipd-rw type ext4 (rw,relatime,data=ordered)
|
/dev/sdc on /var/lib/kubelet/pods/f527ca5b-6d87-11e5-aa7e-080027ff6387/volumes/kubernetes.io~iscsi/iscsipd-rw type ext4 (rw,relatime,data=ordered)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
And for a node with mpio enabled the expected output would be similar to the following
|
||||||
|
|
||||||
|
```console
|
||||||
|
# mount |grep kub
|
||||||
|
/dev/mapper/mpatha on /var/lib/kubelet/plugins/kubernetes.io/iscsi/10.0.2.15:3260-iqn.2001-04.com.example:storage.kube.sys1.xyz-lun-0 type ext4 (ro,relatime,data=ordered)
|
||||||
|
/dev/mapper/mpatha on /var/lib/kubelet/pods/f527ca5b-6d87-11e5-aa7e-080027ff6387/volumes/kubernetes.io~iscsi/iscsipd-ro type ext4 (ro,relatime,data=ordered)
|
||||||
|
/dev/mapper/mpathb on /var/lib/kubelet/plugins/kubernetes.io/iscsi/10.0.2.15:3260-iqn.2001-04.com.example:storage.kube.sys1.xyz-lun-1 type ext4 (rw,relatime,data=ordered)
|
||||||
|
/dev/mapper/mpathb on /var/lib/kubelet/pods/f527ca5b-6d87-11e5-aa7e-080027ff6387/volumes/kubernetes.io~iscsi/iscsipd-rw type ext4 (rw,relatime,data=ordered)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
If you ssh to that machine, you can run `docker ps` to see the actual pod.
|
If you ssh to that machine, you can run `docker ps` to see the actual pod.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Pod",
|
|
||||||
"metadata": {
|
|
||||||
"name": "iscsipd"
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "iscsipd-ro",
|
|
||||||
"image": "kubernetes/pause",
|
|
||||||
"volumeMounts": [
|
|
||||||
{
|
|
||||||
"mountPath": "/mnt/iscsipd",
|
|
||||||
"name": "iscsipd-ro"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "iscsipd-rw",
|
|
||||||
"image": "kubernetes/pause",
|
|
||||||
"volumeMounts": [
|
|
||||||
{
|
|
||||||
"mountPath": "/mnt/iscsipd",
|
|
||||||
"name": "iscsipd-rw"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"volumes": [
|
|
||||||
{
|
|
||||||
"name": "iscsipd-ro",
|
|
||||||
"iscsi": {
|
|
||||||
"targetPortal": "10.0.2.15:3260",
|
|
||||||
"iqn": "iqn.2001-04.com.example:storage.kube.sys1.xyz",
|
|
||||||
"lun": 0,
|
|
||||||
"fsType": "ext4",
|
|
||||||
"readOnly": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "iscsipd-rw",
|
|
||||||
"iscsi": {
|
|
||||||
"targetPortal": "10.0.2.15:3260",
|
|
||||||
"iqn": "iqn.2001-04.com.example:storage.kube.sys1.xyz",
|
|
||||||
"lun": 1,
|
|
||||||
"fsType": "ext4",
|
|
||||||
"readOnly": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
32
examples/iscsi/iscsi.yaml
Normal file
32
examples/iscsi/iscsi.yaml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: iscsipd
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: iscsipd-ro
|
||||||
|
image: kubernetes/pause
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: "/mnt/iscsipd"
|
||||||
|
name: iscsipd-ro
|
||||||
|
- name: iscsipd-rw
|
||||||
|
image: kubernetes/pause
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: "/mnt/iscsipd"
|
||||||
|
name: iscsipd-rw
|
||||||
|
volumes:
|
||||||
|
- name: iscsipd-ro
|
||||||
|
iscsi:
|
||||||
|
targetPortal: 10.0.2.15:3260
|
||||||
|
iqn: iqn.2001-04.com.example:storage.kube.sys1.xyz
|
||||||
|
lun: 0
|
||||||
|
fsType: ext4
|
||||||
|
readOnly: true
|
||||||
|
- name: iscsipd-rw
|
||||||
|
iscsi:
|
||||||
|
targetPortal: 10.0.2.15:3260
|
||||||
|
iqn: iqn.2001-04.com.example:storage.kube.sys1.xyz
|
||||||
|
lun: 1
|
||||||
|
fsType: ext4
|
||||||
|
readOnly: false
|
@ -51,6 +51,8 @@ func (handler *osIOHandler) WriteFile(filename string, data []byte, perm os.File
|
|||||||
}
|
}
|
||||||
|
|
||||||
// given a disk path like /dev/sdx, find the devicemapper parent
|
// given a disk path like /dev/sdx, find the devicemapper parent
|
||||||
|
// TODO #23192 Convert this code to use the generic code in ../util
|
||||||
|
// which is used by the iSCSI implementation
|
||||||
func findMultipathDeviceMapper(disk string, io ioHandler) string {
|
func findMultipathDeviceMapper(disk string, io ioHandler) string {
|
||||||
sys_path := "/sys/block/"
|
sys_path := "/sys/block/"
|
||||||
if dirs, err := io.ReadDir(sys_path); err == nil {
|
if dirs, err := io.ReadDir(sys_path); err == nil {
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
utilstrings "k8s.io/kubernetes/pkg/util/strings"
|
utilstrings "k8s.io/kubernetes/pkg/util/strings"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
|
ioutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is the primary entrypoint for volume plugins.
|
// This is the primary entrypoint for volume plugins.
|
||||||
@ -106,6 +107,7 @@ func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UI
|
|||||||
fsType: iscsi.FSType,
|
fsType: iscsi.FSType,
|
||||||
readOnly: readOnly,
|
readOnly: readOnly,
|
||||||
mounter: &mount.SafeFormatAndMount{Interface: mounter, Runner: exec.New()},
|
mounter: &mount.SafeFormatAndMount{Interface: mounter, Runner: exec.New()},
|
||||||
|
deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +157,7 @@ type iscsiDiskMounter struct {
|
|||||||
readOnly bool
|
readOnly bool
|
||||||
fsType string
|
fsType string
|
||||||
mounter *mount.SafeFormatAndMount
|
mounter *mount.SafeFormatAndMount
|
||||||
|
deviceUtil ioutil.DeviceUtil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ volume.Mounter = &iscsiDiskMounter{}
|
var _ volume.Mounter = &iscsiDiskMounter{}
|
||||||
|
@ -136,6 +136,10 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the dev is using mpio and if so mount it via the dm-XX device
|
||||||
|
if mappedDevicePath := b.deviceUtil.FindMultipathDeviceForDevice(devicePath); mappedDevicePath != "" {
|
||||||
|
devicePath = mappedDevicePath
|
||||||
|
}
|
||||||
err = b.mounter.FormatAndMount(devicePath, globalPDPath, b.fsType, nil)
|
err = b.mounter.FormatAndMount(devicePath, globalPDPath, b.fsType, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("iscsi: failed to mount iscsi volume %s [%s] to %s, error %v", devicePath, b.fsType, globalPDPath, err)
|
glog.Errorf("iscsi: failed to mount iscsi volume %s [%s] to %s, error %v", devicePath, b.fsType, globalPDPath, err)
|
||||||
|
31
pkg/volume/util/device_util.go
Normal file
31
pkg/volume/util/device_util.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 util
|
||||||
|
|
||||||
|
//DeviceUtil is a util for common device methods
|
||||||
|
type DeviceUtil interface {
|
||||||
|
FindMultipathDeviceForDevice(disk string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type deviceHandler struct {
|
||||||
|
get_io IoUtil
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewDeviceHandler Create a new IoHandler implementation
|
||||||
|
func NewDeviceHandler(io IoUtil) DeviceUtil {
|
||||||
|
return &deviceHandler{get_io: io}
|
||||||
|
}
|
61
pkg/volume/util/device_util_linux.go
Normal file
61
pkg/volume/util/device_util_linux.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindMultipathDeviceForDevice given a device name like /dev/sdx, find the devicemapper parent
|
||||||
|
func (handler *deviceHandler) FindMultipathDeviceForDevice(device string) string {
|
||||||
|
io := handler.get_io
|
||||||
|
disk, err := findDeviceForPath(device, io)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
sysPath := "/sys/block/"
|
||||||
|
if dirs, err := io.ReadDir(sysPath); err == nil {
|
||||||
|
for _, f := range dirs {
|
||||||
|
name := f.Name()
|
||||||
|
if strings.HasPrefix(name, "dm-") {
|
||||||
|
if _, err1 := io.Lstat(sysPath + name + "/slaves/" + disk); err1 == nil {
|
||||||
|
return "/dev/" + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// findDeviceForPath Find the underlaying disk for a linked path such as /dev/disk/by-path/XXXX or /dev/mapper/XXXX
|
||||||
|
// will return sdX or hdX etc, if /dev/sdX is passed in then sdX will be returned
|
||||||
|
func findDeviceForPath(path string, io IoUtil) (string, error) {
|
||||||
|
devicePath, err := io.EvalSymlinks(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// if path /dev/hdX split into "", "dev", "hdX" then we will
|
||||||
|
// return just the last part
|
||||||
|
parts := strings.Split(devicePath, "/")
|
||||||
|
if len(parts) == 3 && strings.HasPrefix(parts[1], "dev") {
|
||||||
|
return parts[2], nil
|
||||||
|
}
|
||||||
|
return "", errors.New("Illegal path for device " + devicePath)
|
||||||
|
}
|
136
pkg/volume/util/device_util_linux_test.go
Normal file
136
pkg/volume/util/device_util_linux_test.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockOsIOHandler struct{}
|
||||||
|
|
||||||
|
func (handler *mockOsIOHandler) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||||
|
switch dirname {
|
||||||
|
case "/sys/block/dm-2/slaves/":
|
||||||
|
f := &fakeFileInfo{
|
||||||
|
name: "sda",
|
||||||
|
}
|
||||||
|
return []os.FileInfo{f}, nil
|
||||||
|
case "/sys/block/":
|
||||||
|
f1 := &fakeFileInfo{
|
||||||
|
name: "sda",
|
||||||
|
}
|
||||||
|
f2 := &fakeFileInfo{
|
||||||
|
name: "dm-1",
|
||||||
|
}
|
||||||
|
return []os.FileInfo{f1, f2}, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *mockOsIOHandler) Lstat(name string) (os.FileInfo, error) {
|
||||||
|
links := map[string]string{
|
||||||
|
"/sys/block/dm-1/slaves/sda": "sda",
|
||||||
|
"/dev/sda": "sda",
|
||||||
|
}
|
||||||
|
if dev, ok := links[name]; ok {
|
||||||
|
return &fakeFileInfo{name: dev}, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("Not Implemented for Mock")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *mockOsIOHandler) EvalSymlinks(path string) (string, error) {
|
||||||
|
links := map[string]string{
|
||||||
|
"/returns/a/dev": "/dev/sde",
|
||||||
|
"/returns/non/dev": "/sys/block",
|
||||||
|
"/dev/disk/by-path/127.0.0.1:3260-eui.02004567A425678D-lun-0": "/dev/sda",
|
||||||
|
"/dev/dm-2": "/dev/dm-2",
|
||||||
|
"/dev/dm-3": "/dev/dm-3",
|
||||||
|
"/dev/sde": "/dev/sde",
|
||||||
|
}
|
||||||
|
return links[path], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *mockOsIOHandler) WriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||||
|
return errors.New("Not Implemented for Mock")
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeFileInfo struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi *fakeFileInfo) Name() string {
|
||||||
|
return fi.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi *fakeFileInfo) Size() int64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi *fakeFileInfo) Mode() os.FileMode {
|
||||||
|
return 777
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi *fakeFileInfo) ModTime() time.Time {
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
func (fi *fakeFileInfo) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi *fakeFileInfo) Sys() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindMultipathDeviceForDevice(t *testing.T) {
|
||||||
|
mockDeviceUtil := NewDeviceHandler(&mockOsIOHandler{})
|
||||||
|
dev := mockDeviceUtil.FindMultipathDeviceForDevice("/dev/disk/by-path/127.0.0.1:3260-eui.02004567A425678D-lun-0")
|
||||||
|
if dev != "/dev/dm-1" {
|
||||||
|
t.Fatalf("mpio device not found dm-1 expected got [%s]", dev)
|
||||||
|
}
|
||||||
|
dev = mockDeviceUtil.FindMultipathDeviceForDevice("/dev/disk/by-path/empty")
|
||||||
|
if dev != "" {
|
||||||
|
t.Fatalf("mpio device not found '' expected got [%s]", dev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestfindDeviceForPath(t *testing.T) {
|
||||||
|
io := &mockOsIOHandler{}
|
||||||
|
|
||||||
|
disk, err := findDeviceForPath("/dev/sde", io)
|
||||||
|
if disk != "sde" {
|
||||||
|
t.Fatalf("disk [%s] didn't match expected sde", disk)
|
||||||
|
}
|
||||||
|
disk, err = findDeviceForPath("/returns/a/dev", io)
|
||||||
|
if disk != "sde" {
|
||||||
|
t.Fatalf("disk [%s] didn't match expected sde", disk)
|
||||||
|
}
|
||||||
|
_, err = findDeviceForPath("/returns/non/dev", io)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("link is to incorrect dev")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = findDeviceForPath("/path/doesnt/exist", &osIOHandler{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("path shouldn't exist but still doesn't give an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
pkg/volume/util/device_util_unsupported.go
Normal file
24
pkg/volume/util/device_util_unsupported.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 util
|
||||||
|
|
||||||
|
// FindMultipathDeviceForDevice unsupported returns ""
|
||||||
|
func (handler *deviceHandler) FindMultipathDeviceForDevice(device string) string {
|
||||||
|
return ""
|
||||||
|
}
|
48
pkg/volume/util/io_util.go
Normal file
48
pkg/volume/util/io_util.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
//IoUtil is a util for common IO operations
|
||||||
|
//it also backports certain operations from golang 1.5
|
||||||
|
type IoUtil interface {
|
||||||
|
ReadDir(dirname string) ([]os.FileInfo, error)
|
||||||
|
Lstat(name string) (os.FileInfo, error)
|
||||||
|
EvalSymlinks(path string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type osIOHandler struct{}
|
||||||
|
|
||||||
|
//NewIOHandler Create a new IoHandler implementation
|
||||||
|
func NewIOHandler() IoUtil {
|
||||||
|
return &osIOHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *osIOHandler) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||||
|
return ioutil.ReadDir(dirname)
|
||||||
|
}
|
||||||
|
func (handler *osIOHandler) Lstat(name string) (os.FileInfo, error) {
|
||||||
|
return os.Lstat(name)
|
||||||
|
}
|
||||||
|
func (handler *osIOHandler) EvalSymlinks(path string) (string, error) {
|
||||||
|
return filepath.EvalSymlinks(path)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user