iscsi_util_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package iscsi
  14. import (
  15. "errors"
  16. "fmt"
  17. "os"
  18. "path/filepath"
  19. "reflect"
  20. "testing"
  21. "k8s.io/kubernetes/pkg/util/mount"
  22. "k8s.io/kubernetes/pkg/volume"
  23. )
  24. func TestGetDevicePrefixRefCount(t *testing.T) {
  25. fm := &mount.FakeMounter{
  26. MountPoints: []mount.MountPoint{
  27. {Device: "/dev/sdb",
  28. Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"},
  29. {Device: "/dev/sdb",
  30. Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-1"},
  31. {Device: "/dev/sdb",
  32. Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-2"},
  33. {Device: "/dev/sdb",
  34. Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-3"},
  35. },
  36. }
  37. tests := []struct {
  38. devicePrefix string
  39. expectedRefs int
  40. }{
  41. {
  42. "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00",
  43. 4,
  44. },
  45. }
  46. for i, test := range tests {
  47. if refs, err := getDevicePrefixRefCount(fm, test.devicePrefix); err != nil || test.expectedRefs != refs {
  48. t.Errorf("%d. GetDevicePrefixRefCount(%s) = %d, %v; expected %d, nil", i, test.devicePrefix, refs, err, test.expectedRefs)
  49. }
  50. }
  51. }
  52. func TestExtractDeviceAndPrefix(t *testing.T) {
  53. devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00"
  54. mountPrefix := "/var/lib/kubelet/plugins/kubernetes.io/iscsi/iface-default/" + devicePath
  55. lun := "-lun-0"
  56. device, prefix, err := extractDeviceAndPrefix(mountPrefix + lun)
  57. if err != nil || device != (devicePath+lun) || prefix != mountPrefix {
  58. t.Errorf("extractDeviceAndPrefix: expected %s and %s, got %v %s and %s", devicePath+lun, mountPrefix, err, device, prefix)
  59. }
  60. }
  61. func TestExtractIface(t *testing.T) {
  62. ifaceName := "default"
  63. devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"
  64. iface, found := extractIface("/var/lib/kubelet/plugins/kubernetes.io/iscsi/iface-" + ifaceName + "/" + devicePath)
  65. if !found || iface != ifaceName {
  66. t.Errorf("extractIface: expected %s and %t, got %s and %t", ifaceName, true, iface, found)
  67. }
  68. iface, found = extractIface("/var/lib/kubelet/plugins/kubernetes.io/iscsi/" + devicePath)
  69. if found || iface != "" {
  70. t.Errorf("extractIface: expected %s and %t, got %s and %t", "", false, iface, found)
  71. }
  72. }
  73. func TestExtractPortalAndIqn(t *testing.T) {
  74. devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"
  75. portal, iqn, err := extractPortalAndIqn(devicePath)
  76. if err != nil || portal != "127.0.0.1:3260" || iqn != "iqn.2014-12.com.example:test.tgt00" {
  77. t.Errorf("extractPortalAndIqn: got %v %s %s", err, portal, iqn)
  78. }
  79. devicePath = "127.0.0.1:3260-eui.02004567A425678D-lun-0"
  80. portal, iqn, err = extractPortalAndIqn(devicePath)
  81. if err != nil || portal != "127.0.0.1:3260" || iqn != "eui.02004567A425678D" {
  82. t.Errorf("extractPortalAndIqn: got %v %s %s", err, portal, iqn)
  83. }
  84. }
  85. func TestRemoveDuplicate(t *testing.T) {
  86. dupPortals := []string{"127.0.0.1:3260", "127.0.0.1:3260", "127.0.0.100:3260"}
  87. portals := removeDuplicate(dupPortals)
  88. want := []string{"127.0.0.1:3260", "127.0.0.100:3260"}
  89. if reflect.DeepEqual(portals, want) == false {
  90. t.Errorf("removeDuplicate: want: %s, got: %s", want, portals)
  91. }
  92. }
  93. func fakeOsStat(devicePath string) (fi os.FileInfo, err error) {
  94. var cmd os.FileInfo
  95. return cmd, nil
  96. }
  97. func fakeFilepathGlob(devicePath string) (globs []string, err error) {
  98. return []string{devicePath}, nil
  99. }
  100. func fakeFilepathGlob2(devicePath string) (globs []string, err error) {
  101. return []string{
  102. "/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0",
  103. }, nil
  104. }
  105. func TestExtractTransportname(t *testing.T) {
  106. fakeIscsiadmOutput := []string{
  107. "# BEGIN RECORD 2.0-873\n" +
  108. "iface.iscsi_ifacename = default\n" +
  109. "iface.transport_name = tcp\n" +
  110. "iface.initiatorname = <empty>\n" +
  111. "# END RECORD",
  112. "# BEGIN RECORD 2.0-873\n" +
  113. "iface.iscsi_ifacename = default\n" +
  114. "iface.transport_name = cxgb4i\n" +
  115. "iface.initiatorname = <empty>\n" +
  116. "# END RECORD",
  117. "# BEGIN RECORD 2.0-873\n" +
  118. "iface.iscsi_ifacename = default\n" +
  119. "iface.transport_name = <empty>\n" +
  120. "iface.initiatorname = <empty>\n" +
  121. "# END RECORD",
  122. "# BEGIN RECORD 2.0-873\n" +
  123. "iface.iscsi_ifacename = default\n" +
  124. "iface.initiatorname = <empty>\n" +
  125. "# END RECORD"}
  126. transportName := extractTransportname(fakeIscsiadmOutput[0])
  127. if transportName != "tcp" {
  128. t.Errorf("extractTransportname: Could not extract correct iface.transport_name 'tcp', got %s", transportName)
  129. }
  130. transportName = extractTransportname(fakeIscsiadmOutput[1])
  131. if transportName != "cxgb4i" {
  132. t.Errorf("extractTransportname: Could not extract correct iface.transport_name 'cxgb4i', got %s", transportName)
  133. }
  134. transportName = extractTransportname(fakeIscsiadmOutput[2])
  135. if transportName != "tcp" {
  136. t.Errorf("extractTransportname: Could not extract correct iface.transport_name 'tcp', got %s", transportName)
  137. }
  138. transportName = extractTransportname(fakeIscsiadmOutput[3])
  139. if transportName != "" {
  140. t.Errorf("extractTransportname: Could not extract correct iface.transport_name '', got %s", transportName)
  141. }
  142. }
  143. func TestWaitForPathToExist(t *testing.T) {
  144. devicePath := []string{"/dev/disk/by-path/ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0",
  145. "/dev/disk/by-path/pci-*-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"}
  146. fpath := "/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"
  147. exist := waitForPathToExistInternal(&devicePath[0], 1, "tcp", fakeOsStat, filepath.Glob)
  148. if exist == false {
  149. t.Errorf("waitForPathToExist: could not find path %s", devicePath[0])
  150. }
  151. exist = waitForPathToExistInternal(&devicePath[0], 1, "fake_iface", fakeOsStat, filepath.Glob)
  152. if exist != false {
  153. t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[0])
  154. }
  155. exist = waitForPathToExistInternal(&devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob)
  156. if exist == false {
  157. t.Errorf("waitForPathToExist: could not find path %s", devicePath[1])
  158. }
  159. exist = waitForPathToExistInternal(&devicePath[1], 1, "tcp", os.Stat, fakeFilepathGlob)
  160. if exist != false {
  161. t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1])
  162. }
  163. exist = waitForPathToExistInternal(&devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob2)
  164. if devicePath[1] != fpath {
  165. t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1])
  166. }
  167. }
  168. func TestParseIscsiadmShow(t *testing.T) {
  169. fakeIscsiadmOutput1 := "# BEGIN RECORD 2.0-873\n" +
  170. "iface.iscsi_ifacename = default\n" +
  171. "iface.transport_name = tcp\n" +
  172. "iface.initiatorname = <empty>\n" +
  173. "iface.mtu = 0\n" +
  174. "# END RECORD"
  175. fakeIscsiadmOutput2 := "# BEGIN RECORD 2.0-873\n" +
  176. "iface.iscsi_ifacename = default\n" +
  177. "iface.transport_name = cxgb4i\n" +
  178. "iface.initiatorname = <empty>\n" +
  179. "iface.mtu = 0\n" +
  180. "# END RECORD"
  181. fakeIscsiadmOutput3 := "# BEGIN RECORD 2.0-873\n" +
  182. "iface.iscsi_ifacename = custom\n" +
  183. "iface.transport_name = <empty>\n" +
  184. "iface.initiatorname = <empty>\n" +
  185. "iface.mtu = 0\n" +
  186. "# END RECORD"
  187. fakeIscsiadmOutput4 := "iface.iscsi_ifacename=error"
  188. fakeIscsiadmOutput5 := "iface.iscsi_ifacename + error"
  189. expectedIscsiadmOutput1 := map[string]string{
  190. "iface.transport_name": "tcp",
  191. "iface.mtu": "0"}
  192. expectedIscsiadmOutput2 := map[string]string{
  193. "iface.transport_name": "cxgb4i",
  194. "iface.mtu": "0"}
  195. expectedIscsiadmOutput3 := map[string]string{
  196. "iface.mtu": "0"}
  197. params, _ := parseIscsiadmShow(fakeIscsiadmOutput1)
  198. if !reflect.DeepEqual(params, expectedIscsiadmOutput1) {
  199. t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
  200. }
  201. params, _ = parseIscsiadmShow(fakeIscsiadmOutput2)
  202. if !reflect.DeepEqual(params, expectedIscsiadmOutput2) {
  203. t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
  204. }
  205. params, _ = parseIscsiadmShow(fakeIscsiadmOutput3)
  206. if !reflect.DeepEqual(params, expectedIscsiadmOutput3) {
  207. t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
  208. }
  209. _, err := parseIscsiadmShow(fakeIscsiadmOutput4)
  210. if err == nil {
  211. t.Errorf("parseIscsiadmShow: Fail to handle invalid record: iface %s", fakeIscsiadmOutput4)
  212. }
  213. _, err = parseIscsiadmShow(fakeIscsiadmOutput5)
  214. if err == nil {
  215. t.Errorf("parseIscsiadmShow: Fail to handle invalid record: iface %s", fakeIscsiadmOutput5)
  216. }
  217. }
  218. func TestClonedIface(t *testing.T) {
  219. cmdCount := 0
  220. fakeExec := mount.NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
  221. cmdCount++
  222. if cmd != "iscsiadm" {
  223. t.Errorf("iscsiadm command expected, got %q", cmd)
  224. }
  225. switch cmdCount {
  226. case 1:
  227. // iscsiadm -m iface -I <iface> -o show
  228. return []byte("iface.ipaddress = <empty>\niface.transport_name = tcp\niface.initiatorname = <empty>\n"), nil
  229. case 2:
  230. // iscsiadm -m iface -I <newIface> -o new
  231. return []byte("New interface 192.168.1.10:pv0001 added"), nil
  232. case 3:
  233. // iscsiadm -m iface -I <newIface> -o update -n <key> -v <val>
  234. return []byte(""), nil
  235. case 4:
  236. return []byte(""), nil
  237. }
  238. return nil, fmt.Errorf("Unexpected exec call nr %d: %s", cmdCount, cmd)
  239. })
  240. plugins := []volume.VolumePlugin{
  241. &iscsiPlugin{
  242. host: nil,
  243. },
  244. }
  245. plugin := plugins[0]
  246. fakeMounter := iscsiDiskMounter{
  247. iscsiDisk: &iscsiDisk{
  248. plugin: plugin.(*iscsiPlugin)},
  249. exec: fakeExec,
  250. }
  251. newIface := "192.168.1.10:pv0001"
  252. cloneIface(fakeMounter, newIface)
  253. if cmdCount != 4 {
  254. t.Errorf("expected 4 CombinedOutput() calls, got %d", cmdCount)
  255. }
  256. }
  257. func TestClonedIfaceShowError(t *testing.T) {
  258. cmdCount := 0
  259. fakeExec := mount.NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
  260. cmdCount++
  261. if cmd != "iscsiadm" {
  262. t.Errorf("iscsiadm command expected, got %q", cmd)
  263. }
  264. // iscsiadm -m iface -I <iface> -o show, return test error
  265. return []byte(""), errors.New("test error")
  266. })
  267. plugins := []volume.VolumePlugin{
  268. &iscsiPlugin{
  269. host: nil,
  270. },
  271. }
  272. plugin := plugins[0]
  273. fakeMounter := iscsiDiskMounter{
  274. iscsiDisk: &iscsiDisk{
  275. plugin: plugin.(*iscsiPlugin)},
  276. exec: fakeExec,
  277. }
  278. newIface := "192.168.1.10:pv0001"
  279. cloneIface(fakeMounter, newIface)
  280. if cmdCount != 1 {
  281. t.Errorf("expected 1 CombinedOutput() calls, got %d", cmdCount)
  282. }
  283. }
  284. func TestClonedIfaceUpdateError(t *testing.T) {
  285. cmdCount := 0
  286. fakeExec := mount.NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
  287. cmdCount++
  288. if cmd != "iscsiadm" {
  289. t.Errorf("iscsiadm command expected, got %q", cmd)
  290. }
  291. switch cmdCount {
  292. case 1:
  293. // iscsiadm -m iface -I <iface> -o show
  294. return []byte("iface.ipaddress = <empty>\niface.transport_name = tcp\niface.initiatorname = <empty>\n"), nil
  295. case 2:
  296. // iscsiadm -m iface -I <newIface> -o new
  297. return []byte("New interface 192.168.1.10:pv0001 added"), nil
  298. case 3:
  299. // iscsiadm -m iface -I <newIface> -o update -n <key> -v <val>
  300. return []byte(""), nil
  301. case 4:
  302. return []byte(""), errors.New("test error")
  303. case 5:
  304. // iscsiadm -m iface -I <newIface> -o delete
  305. return []byte(""), nil
  306. }
  307. return nil, fmt.Errorf("Unexpected exec call nr %d: %s", cmdCount, cmd)
  308. })
  309. plugins := []volume.VolumePlugin{
  310. &iscsiPlugin{
  311. host: nil,
  312. },
  313. }
  314. plugin := plugins[0]
  315. fakeMounter := iscsiDiskMounter{
  316. iscsiDisk: &iscsiDisk{
  317. plugin: plugin.(*iscsiPlugin)},
  318. exec: fakeExec,
  319. }
  320. newIface := "192.168.1.10:pv0001"
  321. cloneIface(fakeMounter, newIface)
  322. if cmdCount != 5 {
  323. t.Errorf("expected 5 CombinedOutput() calls, got %d", cmdCount)
  324. }
  325. }