k8s.io/kubernetes@v1.29.3/pkg/volume/util/device_util_linux_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2016 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package util 21 22 import ( 23 "errors" 24 "os" 25 "reflect" 26 "regexp" 27 "testing" 28 "time" 29 ) 30 31 type mockOsIOHandler struct{} 32 33 func (handler *mockOsIOHandler) ReadFile(filename string) ([]byte, error) { 34 portPattern := regexp.MustCompile("^/sys/class/iscsi_host/(host\\d)/device/session\\d/connection\\d:0/iscsi_connection/connection\\d:0/(?:persistent_)?port$") 35 if portPattern.MatchString(filename) { 36 return []byte("3260"), nil 37 } 38 addressPattern := regexp.MustCompile("^/sys/class/iscsi_host/(host\\d)/device/session\\d/connection\\d:0/iscsi_connection/connection\\d:0/(?:persistent_)?address$") 39 matches := addressPattern.FindStringSubmatch(filename) 40 if nil != matches { 41 switch matches[1] { 42 case "host2": 43 return []byte("10.0.0.1"), nil 44 case "host3": 45 return []byte("10.0.0.2"), nil 46 } 47 } 48 targetNamePattern := regexp.MustCompile("^/sys/class/iscsi_host/(host\\d)/device/session\\d/iscsi_session/session\\d/targetname$") 49 matches = targetNamePattern.FindStringSubmatch(filename) 50 if nil != matches { 51 switch matches[1] { 52 case "host2": 53 return []byte("target1"), nil 54 case "host3": 55 return []byte("target2"), nil 56 } 57 } 58 return nil, errors.New("not Implemented for Mock") 59 } 60 61 func (handler *mockOsIOHandler) ReadDir(dirname string) ([]os.FileInfo, error) { 62 switch dirname { 63 case "/sys/block/dm-1/slaves": 64 f1 := &fakeFileInfo{ 65 name: "sda", 66 } 67 f2 := &fakeFileInfo{ 68 name: "sdb", 69 } 70 return []os.FileInfo{f1, f2}, nil 71 case "/sys/block/": 72 f1 := &fakeFileInfo{ 73 name: "sda", 74 } 75 f2 := &fakeFileInfo{ 76 name: "dm-1", 77 } 78 return []os.FileInfo{f1, f2}, nil 79 case "/sys/class/iscsi_host": 80 f1 := &fakeFileInfo{ 81 name: "host2", 82 } 83 f2 := &fakeFileInfo{ 84 name: "host3", 85 } 86 f3 := &fakeFileInfo{ 87 name: "ignore", 88 } 89 return []os.FileInfo{f1, f2, f3}, nil 90 case "/sys/class/iscsi_host/host2/device": 91 f1 := &fakeFileInfo{ 92 name: "session1", 93 } 94 f2 := &fakeFileInfo{ 95 name: "ignore", 96 } 97 return []os.FileInfo{f1, f2}, nil 98 case "/sys/class/iscsi_host/host3/device": 99 f1 := &fakeFileInfo{ 100 name: "session2", 101 } 102 f2 := &fakeFileInfo{ 103 name: "ignore", 104 } 105 return []os.FileInfo{f1, f2}, nil 106 case "/sys/class/iscsi_host/host2/device/session1": 107 f1 := &fakeFileInfo{ 108 name: "connection1:0", 109 } 110 f2 := &fakeFileInfo{ 111 name: "ignore", 112 } 113 return []os.FileInfo{f1, f2}, nil 114 case "/sys/class/iscsi_host/host3/device/session2": 115 f1 := &fakeFileInfo{ 116 name: "connection2:0", 117 } 118 f2 := &fakeFileInfo{ 119 name: "ignore", 120 } 121 return []os.FileInfo{f1, f2}, nil 122 case "/sys/class/iscsi_host/host2/device/session1/target2:0:0/2:0:0:1/block": 123 f1 := &fakeFileInfo{ 124 name: "sda", 125 } 126 return []os.FileInfo{f1}, nil 127 case "/sys/class/iscsi_host/host2/device/session1/target2:0:0/2:0:0:2/block": 128 f1 := &fakeFileInfo{ 129 name: "sdc", 130 } 131 return []os.FileInfo{f1}, nil 132 case "/sys/class/iscsi_host/host3/device/session2/target3:0:0/3:0:0:1/block": 133 f1 := &fakeFileInfo{ 134 name: "sdb", 135 } 136 return []os.FileInfo{f1}, nil 137 case "/sys/class/iscsi_host/host3/device/session2/target3:0:0/3:0:0:2/block": 138 f1 := &fakeFileInfo{ 139 name: "sdd", 140 } 141 return []os.FileInfo{f1}, nil 142 } 143 return nil, errors.New("not Implemented for Mock") 144 } 145 146 func (handler *mockOsIOHandler) Lstat(name string) (os.FileInfo, error) { 147 links := map[string]string{ 148 "/sys/block/dm-1/slaves/sda": "sda", 149 "/dev/sda": "sda", 150 "/sys/class/iscsi_host/host2/device/session1/target2:0:0/2:0:0:1": "2:0:0:1", 151 "/sys/class/iscsi_host/host2/device/session1/target2:0:0/2:0:0:2": "2:0:0:2", 152 "/sys/class/iscsi_host/host3/device/session2/target3:0:0/3:0:0:1": "3:0:0:1", 153 "/sys/class/iscsi_host/host3/device/session2/target3:0:0/3:0:0:2": "3:0:0:2", 154 } 155 if dev, ok := links[name]; ok { 156 return &fakeFileInfo{name: dev}, nil 157 } 158 return nil, errors.New("not Implemented for Mock") 159 } 160 161 func (handler *mockOsIOHandler) EvalSymlinks(path string) (string, error) { 162 links := map[string]string{ 163 "/returns/a/dev": "/dev/sde", 164 "/returns/non/dev": "/sys/block", 165 "/dev/disk/by-path/127.0.0.1:3260-eui.02004567A425678D-lun-0": "/dev/sda", 166 "/dev/disk/by-path/127.0.0.3:3260-eui.03004567A425678D-lun-0": "/dev/sdb", 167 "/dev/dm-2": "/dev/dm-2", 168 "/dev/dm-3": "/dev/dm-3", 169 "/dev/sdc": "/dev/sdc", 170 "/dev/sde": "/dev/sde", 171 } 172 return links[path], nil 173 } 174 175 func (handler *mockOsIOHandler) WriteFile(filename string, data []byte, perm os.FileMode) error { 176 return errors.New("not Implemented for Mock") 177 } 178 179 type fakeFileInfo struct { 180 name string 181 } 182 183 func (fi *fakeFileInfo) Name() string { 184 return fi.name 185 } 186 187 func (fi *fakeFileInfo) Size() int64 { 188 return 0 189 } 190 191 func (fi *fakeFileInfo) Mode() os.FileMode { 192 return 777 193 } 194 195 func (fi *fakeFileInfo) ModTime() time.Time { 196 return time.Now() 197 } 198 func (fi *fakeFileInfo) IsDir() bool { 199 return false 200 } 201 202 func (fi *fakeFileInfo) Sys() interface{} { 203 return nil 204 } 205 206 func TestFindMultipathDeviceForDevice(t *testing.T) { 207 mockDeviceUtil := NewDeviceHandler(&mockOsIOHandler{}) 208 dev := mockDeviceUtil.FindMultipathDeviceForDevice("/dev/disk/by-path/127.0.0.1:3260-eui.02004567A425678D-lun-0") 209 if dev != "/dev/dm-1" { 210 t.Fatalf("mpio device not found dm-1 expected got [%s]", dev) 211 } 212 dev = mockDeviceUtil.FindMultipathDeviceForDevice("/dev/disk/by-path/empty") 213 if dev != "" { 214 t.Fatalf("mpio device not found '' expected got [%s]", dev) 215 } 216 } 217 218 func TestFindDeviceForPath(t *testing.T) { 219 io := &mockOsIOHandler{} 220 221 disk, err := findDeviceForPath("/dev/sde", io) 222 if err != nil { 223 t.Fatalf("error finding device for path /dev/sde:%v", err) 224 } 225 if disk != "sde" { 226 t.Fatalf("disk [%s] didn't match expected sde", disk) 227 } 228 disk, err = findDeviceForPath("/returns/a/dev", io) 229 if err != nil { 230 t.Fatalf("error finding device for path /returns/a/dev:%v", err) 231 } 232 if disk != "sde" { 233 t.Fatalf("disk [%s] didn't match expected sde", disk) 234 } 235 _, err = findDeviceForPath("/returns/non/dev", io) 236 if err == nil { 237 t.Fatalf("link is to incorrect dev") 238 } 239 240 _, err = findDeviceForPath("/path/doesnt/exist", &osIOHandler{}) 241 if err == nil { 242 t.Fatalf("path shouldn't exist but still doesn't give an error") 243 } 244 245 } 246 247 func TestFindSlaveDevicesOnMultipath(t *testing.T) { 248 mockDeviceUtil := NewDeviceHandler(&mockOsIOHandler{}) 249 devices := mockDeviceUtil.FindSlaveDevicesOnMultipath("/dev/dm-1") 250 if !reflect.DeepEqual(devices, []string{"/dev/sda", "/dev/sdb"}) { 251 t.Fatalf("failed to find devices managed by mpio device. /dev/sda, /dev/sdb expected got [%s]", devices) 252 } 253 dev := mockDeviceUtil.FindSlaveDevicesOnMultipath("/dev/sdc") 254 if len(dev) != 0 { 255 t.Fatalf("mpio device not found '' expected got [%s]", dev) 256 } 257 } 258 259 func TestGetISCSIPortalHostMapForTarget(t *testing.T) { 260 mockDeviceUtil := NewDeviceHandler(&mockOsIOHandler{}) 261 portalHostMap, err := mockDeviceUtil.GetISCSIPortalHostMapForTarget("target1") 262 if err != nil { 263 t.Fatalf("error getting scsi hosts for target: %v", err) 264 } 265 if portalHostMap == nil { 266 t.Fatal("no portal host map returned") 267 } 268 if len(portalHostMap) != 1 { 269 t.Fatalf("wrong number of map entries in portal host map: %d", len(portalHostMap)) 270 } 271 if portalHostMap["10.0.0.1:3260"] != 2 { 272 t.Fatalf("incorrect entry in portal host map: %v", portalHostMap) 273 } 274 } 275 276 func TestFindDevicesForISCSILun(t *testing.T) { 277 mockDeviceUtil := NewDeviceHandler(&mockOsIOHandler{}) 278 devices, err := mockDeviceUtil.FindDevicesForISCSILun("target1", 1) 279 if err != nil { 280 t.Fatalf("error getting devices for lun: %v", err) 281 } 282 if devices == nil { 283 t.Fatal("no devices returned") 284 } 285 if len(devices) != 1 { 286 t.Fatalf("wrong number of devices: %d", len(devices)) 287 } 288 if devices[0] != "sda" { 289 t.Fatalf("incorrect device %v", devices) 290 } 291 }