iscsi.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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. "fmt"
  16. "os"
  17. "path/filepath"
  18. "strconv"
  19. "strings"
  20. "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/types"
  23. "k8s.io/klog"
  24. "k8s.io/kubernetes/pkg/util/mount"
  25. "k8s.io/kubernetes/pkg/volume"
  26. ioutil "k8s.io/kubernetes/pkg/volume/util"
  27. "k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
  28. "k8s.io/utils/keymutex"
  29. utilstrings "k8s.io/utils/strings"
  30. )
  31. // This is the primary entrypoint for volume plugins.
  32. func ProbeVolumePlugins() []volume.VolumePlugin {
  33. return []volume.VolumePlugin{&iscsiPlugin{}}
  34. }
  35. type iscsiPlugin struct {
  36. host volume.VolumeHost
  37. targetLocks keymutex.KeyMutex
  38. }
  39. var _ volume.VolumePlugin = &iscsiPlugin{}
  40. var _ volume.PersistentVolumePlugin = &iscsiPlugin{}
  41. var _ volume.BlockVolumePlugin = &iscsiPlugin{}
  42. const (
  43. iscsiPluginName = "kubernetes.io/iscsi"
  44. )
  45. func (plugin *iscsiPlugin) Init(host volume.VolumeHost) error {
  46. plugin.host = host
  47. plugin.targetLocks = keymutex.NewHashed(0)
  48. return nil
  49. }
  50. func (plugin *iscsiPlugin) GetPluginName() string {
  51. return iscsiPluginName
  52. }
  53. func (plugin *iscsiPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  54. tp, _, iqn, lun, err := getISCSITargetInfo(spec)
  55. if err != nil {
  56. return "", err
  57. }
  58. return fmt.Sprintf("%v:%v:%v", tp, iqn, lun), nil
  59. }
  60. func (plugin *iscsiPlugin) CanSupport(spec *volume.Spec) bool {
  61. return (spec.Volume != nil && spec.Volume.ISCSI != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.ISCSI != nil)
  62. }
  63. func (plugin *iscsiPlugin) IsMigratedToCSI() bool {
  64. return false
  65. }
  66. func (plugin *iscsiPlugin) RequiresRemount() bool {
  67. return false
  68. }
  69. func (plugin *iscsiPlugin) SupportsMountOption() bool {
  70. return true
  71. }
  72. func (plugin *iscsiPlugin) SupportsBulkVolumeVerification() bool {
  73. return false
  74. }
  75. func (plugin *iscsiPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
  76. return []v1.PersistentVolumeAccessMode{
  77. v1.ReadWriteOnce,
  78. v1.ReadOnlyMany,
  79. }
  80. }
  81. func (plugin *iscsiPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
  82. if pod == nil {
  83. return nil, fmt.Errorf("nil pod")
  84. }
  85. secret, err := createSecretMap(spec, plugin, pod.Namespace)
  86. if err != nil {
  87. return nil, err
  88. }
  89. return plugin.newMounterInternal(spec, pod.UID, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret)
  90. }
  91. func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec, secret map[string]string) (volume.Mounter, error) {
  92. readOnly, fsType, err := getISCSIVolumeInfo(spec)
  93. if err != nil {
  94. return nil, err
  95. }
  96. iscsiDisk, err := createISCSIDisk(spec, podUID, plugin, manager, secret)
  97. if err != nil {
  98. return nil, err
  99. }
  100. if iscsiDisk != nil {
  101. //Add volume metrics
  102. iscsiDisk.MetricsProvider = volume.NewMetricsStatFS(iscsiDisk.GetPath())
  103. }
  104. return &iscsiDiskMounter{
  105. iscsiDisk: iscsiDisk,
  106. fsType: fsType,
  107. readOnly: readOnly,
  108. mounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec},
  109. exec: exec,
  110. deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()),
  111. mountOptions: ioutil.MountOptionFromSpec(spec),
  112. }, nil
  113. }
  114. // NewBlockVolumeMapper creates a new volume.BlockVolumeMapper from an API specification.
  115. func (plugin *iscsiPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) {
  116. // If this is called via GenerateUnmapDeviceFunc(), pod is nil.
  117. // Pass empty string as dummy uid since uid isn't used in the case.
  118. var uid types.UID
  119. var secret map[string]string
  120. var err error
  121. if pod != nil {
  122. uid = pod.UID
  123. secret, err = createSecretMap(spec, plugin, pod.Namespace)
  124. if err != nil {
  125. return nil, err
  126. }
  127. }
  128. return plugin.newBlockVolumeMapperInternal(spec, uid, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret)
  129. }
  130. func (plugin *iscsiPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec, secret map[string]string) (volume.BlockVolumeMapper, error) {
  131. readOnly, _, err := getISCSIVolumeInfo(spec)
  132. if err != nil {
  133. return nil, err
  134. }
  135. iscsiDisk, err := createISCSIDisk(spec, podUID, plugin, manager, secret)
  136. if err != nil {
  137. return nil, err
  138. }
  139. return &iscsiDiskMapper{
  140. iscsiDisk: iscsiDisk,
  141. readOnly: readOnly,
  142. exec: exec,
  143. deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()),
  144. }, nil
  145. }
  146. func (plugin *iscsiPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  147. return plugin.newUnmounterInternal(volName, podUID, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
  148. }
  149. func (plugin *iscsiPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec) (volume.Unmounter, error) {
  150. return &iscsiDiskUnmounter{
  151. iscsiDisk: &iscsiDisk{
  152. podUID: podUID,
  153. VolName: volName,
  154. manager: manager,
  155. plugin: plugin,
  156. MetricsProvider: volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(podUID, utilstrings.EscapeQualifiedName(iscsiPluginName), volName)),
  157. },
  158. mounter: mounter,
  159. exec: exec,
  160. deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()),
  161. }, nil
  162. }
  163. // NewBlockVolumeUnmapper creates a new volume.BlockVolumeUnmapper from recoverable state.
  164. func (plugin *iscsiPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) {
  165. return plugin.newUnmapperInternal(volName, podUID, &ISCSIUtil{}, plugin.host.GetExec(plugin.GetPluginName()))
  166. }
  167. func (plugin *iscsiPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager, exec mount.Exec) (volume.BlockVolumeUnmapper, error) {
  168. return &iscsiDiskUnmapper{
  169. iscsiDisk: &iscsiDisk{
  170. podUID: podUID,
  171. VolName: volName,
  172. manager: manager,
  173. plugin: plugin,
  174. },
  175. exec: exec,
  176. deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()),
  177. }, nil
  178. }
  179. func (plugin *iscsiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
  180. // Find globalPDPath from pod volume directory(mountPath)
  181. var globalPDPath string
  182. mounter := plugin.host.GetMounter(plugin.GetPluginName())
  183. paths, err := mounter.GetMountRefs(mountPath)
  184. if err != nil {
  185. return nil, err
  186. }
  187. for _, path := range paths {
  188. if strings.Contains(path, plugin.host.GetPluginDir(iscsiPluginName)) {
  189. globalPDPath = path
  190. break
  191. }
  192. }
  193. // Couldn't fetch globalPDPath
  194. if len(globalPDPath) == 0 {
  195. return nil, fmt.Errorf("couldn't fetch globalPDPath. failed to obtain volume spec")
  196. }
  197. // Obtain iscsi disk configurations from globalPDPath
  198. device, _, err := extractDeviceAndPrefix(globalPDPath)
  199. if err != nil {
  200. return nil, err
  201. }
  202. bkpPortal, iqn, err := extractPortalAndIqn(device)
  203. if err != nil {
  204. return nil, err
  205. }
  206. arr := strings.Split(device, "-lun-")
  207. if len(arr) < 2 {
  208. return nil, fmt.Errorf("failed to retrieve lun from globalPDPath: %v", globalPDPath)
  209. }
  210. lun, err := strconv.Atoi(arr[1])
  211. if err != nil {
  212. return nil, err
  213. }
  214. iface, _ := extractIface(globalPDPath)
  215. iscsiVolume := &v1.Volume{
  216. Name: volumeName,
  217. VolumeSource: v1.VolumeSource{
  218. ISCSI: &v1.ISCSIVolumeSource{
  219. TargetPortal: bkpPortal,
  220. IQN: iqn,
  221. Lun: int32(lun),
  222. ISCSIInterface: iface,
  223. },
  224. },
  225. }
  226. return volume.NewSpecFromVolume(iscsiVolume), nil
  227. }
  228. func (plugin *iscsiPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, mapPath string) (*volume.Spec, error) {
  229. pluginDir := plugin.host.GetVolumeDevicePluginDir(iscsiPluginName)
  230. blkutil := volumepathhandler.NewBlockVolumePathHandler()
  231. globalMapPathUUID, err := blkutil.FindGlobalMapPathUUIDFromPod(pluginDir, mapPath, podUID)
  232. if err != nil {
  233. return nil, err
  234. }
  235. klog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err)
  236. // Retrieve volume information from globalMapPathUUID
  237. // globalMapPathUUID example:
  238. // plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}/{pod uuid}
  239. // plugins/kubernetes.io/iscsi/volumeDevices/iface-default/192.168.0.10:3260-iqn.2017-05.com.example:test-lun-0/{pod uuid}
  240. globalMapPath := filepath.Dir(globalMapPathUUID)
  241. return getVolumeSpecFromGlobalMapPath(volumeName, globalMapPath)
  242. }
  243. type iscsiDisk struct {
  244. VolName string
  245. podUID types.UID
  246. Portals []string
  247. Iqn string
  248. Lun string
  249. Iface string
  250. chapDiscovery bool
  251. chapSession bool
  252. secret map[string]string
  253. InitiatorName string
  254. plugin *iscsiPlugin
  255. // Utility interface that provides API calls to the provider to attach/detach disks.
  256. manager diskManager
  257. volume.MetricsProvider
  258. }
  259. func (iscsi *iscsiDisk) GetPath() string {
  260. name := iscsiPluginName
  261. // safe to use PodVolumeDir now: volume teardown occurs before pod is cleaned up
  262. return iscsi.plugin.host.GetPodVolumeDir(iscsi.podUID, utilstrings.EscapeQualifiedName(name), iscsi.VolName)
  263. }
  264. func (iscsi *iscsiDisk) iscsiGlobalMapPath(spec *volume.Spec) (string, error) {
  265. mounter, err := volumeSpecToMounter(spec, iscsi.plugin.host, iscsi.plugin.targetLocks, nil /* pod */)
  266. if err != nil {
  267. klog.Warningf("failed to get iscsi mounter: %v", err)
  268. return "", err
  269. }
  270. return iscsi.manager.MakeGlobalVDPDName(*mounter.iscsiDisk), nil
  271. }
  272. func (iscsi *iscsiDisk) iscsiPodDeviceMapPath() (string, string) {
  273. name := iscsiPluginName
  274. return iscsi.plugin.host.GetPodVolumeDeviceDir(iscsi.podUID, utilstrings.EscapeQualifiedName(name)), iscsi.VolName
  275. }
  276. type iscsiDiskMounter struct {
  277. *iscsiDisk
  278. readOnly bool
  279. fsType string
  280. volumeMode v1.PersistentVolumeMode
  281. mounter *mount.SafeFormatAndMount
  282. exec mount.Exec
  283. deviceUtil ioutil.DeviceUtil
  284. mountOptions []string
  285. }
  286. var _ volume.Mounter = &iscsiDiskMounter{}
  287. func (b *iscsiDiskMounter) GetAttributes() volume.Attributes {
  288. return volume.Attributes{
  289. ReadOnly: b.readOnly,
  290. Managed: !b.readOnly,
  291. SupportsSELinux: true,
  292. }
  293. }
  294. // Checks prior to mount operations to verify that the required components (binaries, etc.)
  295. // to mount the volume are available on the underlying node.
  296. // If not, it returns an error
  297. func (b *iscsiDiskMounter) CanMount() error {
  298. return nil
  299. }
  300. func (b *iscsiDiskMounter) SetUp(mounterArgs volume.MounterArgs) error {
  301. return b.SetUpAt(b.GetPath(), mounterArgs)
  302. }
  303. func (b *iscsiDiskMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  304. // diskSetUp checks mountpoints and prevent repeated calls
  305. err := diskSetUp(b.manager, *b, dir, b.mounter, mounterArgs.FsGroup)
  306. if err != nil {
  307. klog.Errorf("iscsi: failed to setup")
  308. }
  309. return err
  310. }
  311. type iscsiDiskUnmounter struct {
  312. *iscsiDisk
  313. mounter mount.Interface
  314. exec mount.Exec
  315. deviceUtil ioutil.DeviceUtil
  316. }
  317. var _ volume.Unmounter = &iscsiDiskUnmounter{}
  318. // Unmounts the bind mount, and detaches the disk only if the disk
  319. // resource was the last reference to that disk on the kubelet.
  320. func (c *iscsiDiskUnmounter) TearDown() error {
  321. return c.TearDownAt(c.GetPath())
  322. }
  323. func (c *iscsiDiskUnmounter) TearDownAt(dir string) error {
  324. return mount.CleanupMountPoint(dir, c.mounter, false)
  325. }
  326. // Block Volumes Support
  327. type iscsiDiskMapper struct {
  328. *iscsiDisk
  329. readOnly bool
  330. exec mount.Exec
  331. deviceUtil ioutil.DeviceUtil
  332. }
  333. var _ volume.BlockVolumeMapper = &iscsiDiskMapper{}
  334. func (b *iscsiDiskMapper) SetUpDevice() (string, error) {
  335. return "", nil
  336. }
  337. func (b *iscsiDiskMapper) MapDevice(devicePath, globalMapPath, volumeMapPath, volumeMapName string, podUID types.UID) error {
  338. return ioutil.MapBlockVolume(devicePath, globalMapPath, volumeMapPath, volumeMapName, podUID)
  339. }
  340. type iscsiDiskUnmapper struct {
  341. *iscsiDisk
  342. exec mount.Exec
  343. deviceUtil ioutil.DeviceUtil
  344. }
  345. var _ volume.BlockVolumeUnmapper = &iscsiDiskUnmapper{}
  346. // Even though iSCSI plugin has attacher/detacher implementation, iSCSI plugin
  347. // needs volume detach operation during TearDownDevice(). This method is only
  348. // chance that operations are done on kubelet node during volume teardown sequences.
  349. func (c *iscsiDiskUnmapper) TearDownDevice(mapPath, _ string) error {
  350. err := c.manager.DetachBlockISCSIDisk(*c, mapPath)
  351. if err != nil {
  352. return fmt.Errorf("iscsi: failed to detach disk: %s\nError: %v", mapPath, err)
  353. }
  354. klog.V(4).Infof("iscsi: %q is unmounted, deleting the directory", mapPath)
  355. err = os.RemoveAll(mapPath)
  356. if err != nil {
  357. return fmt.Errorf("iscsi: failed to delete the directory: %s\nError: %v", mapPath, err)
  358. }
  359. klog.V(4).Infof("iscsi: successfully detached disk: %s", mapPath)
  360. return nil
  361. }
  362. // GetGlobalMapPath returns global map path and error
  363. // path: plugins/kubernetes.io/{PluginName}/volumeDevices/{ifaceName}/{portal-some_iqn-lun-lun_id}
  364. func (iscsi *iscsiDisk) GetGlobalMapPath(spec *volume.Spec) (string, error) {
  365. return iscsi.iscsiGlobalMapPath(spec)
  366. }
  367. // GetPodDeviceMapPath returns pod device map path and volume name
  368. // path: pods/{podUid}/volumeDevices/kubernetes.io~iscsi
  369. // volumeName: pv0001
  370. func (iscsi *iscsiDisk) GetPodDeviceMapPath() (string, string) {
  371. return iscsi.iscsiPodDeviceMapPath()
  372. }
  373. func portalMounter(portal string) string {
  374. if !strings.Contains(portal, ":") {
  375. portal = portal + ":3260"
  376. }
  377. return portal
  378. }
  379. // get iSCSI volume info: readOnly and fstype
  380. func getISCSIVolumeInfo(spec *volume.Spec) (bool, string, error) {
  381. // for volume source, readonly is in volume spec
  382. // for PV, readonly is in PV spec. PV gets the ReadOnly flag indirectly through the PVC source
  383. if spec.Volume != nil && spec.Volume.ISCSI != nil {
  384. return spec.Volume.ISCSI.ReadOnly, spec.Volume.ISCSI.FSType, nil
  385. } else if spec.PersistentVolume != nil &&
  386. spec.PersistentVolume.Spec.ISCSI != nil {
  387. return spec.ReadOnly, spec.PersistentVolume.Spec.ISCSI.FSType, nil
  388. }
  389. return false, "", fmt.Errorf("Spec does not reference an ISCSI volume type")
  390. }
  391. // get iSCSI target info: target portal, portals, iqn, and lun
  392. func getISCSITargetInfo(spec *volume.Spec) (string, []string, string, int32, error) {
  393. if spec.Volume != nil && spec.Volume.ISCSI != nil {
  394. return spec.Volume.ISCSI.TargetPortal, spec.Volume.ISCSI.Portals, spec.Volume.ISCSI.IQN, spec.Volume.ISCSI.Lun, nil
  395. } else if spec.PersistentVolume != nil &&
  396. spec.PersistentVolume.Spec.ISCSI != nil {
  397. return spec.PersistentVolume.Spec.ISCSI.TargetPortal, spec.PersistentVolume.Spec.ISCSI.Portals, spec.PersistentVolume.Spec.ISCSI.IQN, spec.PersistentVolume.Spec.ISCSI.Lun, nil
  398. }
  399. return "", nil, "", 0, fmt.Errorf("Spec does not reference an ISCSI volume type")
  400. }
  401. // get iSCSI initiator info: iface and initiator name
  402. func getISCSIInitiatorInfo(spec *volume.Spec) (string, *string, error) {
  403. if spec.Volume != nil && spec.Volume.ISCSI != nil {
  404. return spec.Volume.ISCSI.ISCSIInterface, spec.Volume.ISCSI.InitiatorName, nil
  405. } else if spec.PersistentVolume != nil &&
  406. spec.PersistentVolume.Spec.ISCSI != nil {
  407. return spec.PersistentVolume.Spec.ISCSI.ISCSIInterface, spec.PersistentVolume.Spec.ISCSI.InitiatorName, nil
  408. }
  409. return "", nil, fmt.Errorf("Spec does not reference an ISCSI volume type")
  410. }
  411. // get iSCSI Discovery CHAP boolean
  412. func getISCSIDiscoveryCHAPInfo(spec *volume.Spec) (bool, error) {
  413. if spec.Volume != nil && spec.Volume.ISCSI != nil {
  414. return spec.Volume.ISCSI.DiscoveryCHAPAuth, nil
  415. } else if spec.PersistentVolume != nil &&
  416. spec.PersistentVolume.Spec.ISCSI != nil {
  417. return spec.PersistentVolume.Spec.ISCSI.DiscoveryCHAPAuth, nil
  418. }
  419. return false, fmt.Errorf("Spec does not reference an ISCSI volume type")
  420. }
  421. // get iSCSI Session CHAP boolean
  422. func getISCSISessionCHAPInfo(spec *volume.Spec) (bool, error) {
  423. if spec.Volume != nil && spec.Volume.ISCSI != nil {
  424. return spec.Volume.ISCSI.SessionCHAPAuth, nil
  425. } else if spec.PersistentVolume != nil &&
  426. spec.PersistentVolume.Spec.ISCSI != nil {
  427. return spec.PersistentVolume.Spec.ISCSI.SessionCHAPAuth, nil
  428. }
  429. return false, fmt.Errorf("Spec does not reference an ISCSI volume type")
  430. }
  431. // get iSCSI CHAP Secret info: secret name and namespace
  432. func getISCSISecretNameAndNamespace(spec *volume.Spec, defaultSecretNamespace string) (string, string, error) {
  433. if spec.Volume != nil && spec.Volume.ISCSI != nil {
  434. if spec.Volume.ISCSI.SecretRef != nil {
  435. return spec.Volume.ISCSI.SecretRef.Name, defaultSecretNamespace, nil
  436. }
  437. return "", "", nil
  438. } else if spec.PersistentVolume != nil &&
  439. spec.PersistentVolume.Spec.ISCSI != nil {
  440. secretRef := spec.PersistentVolume.Spec.ISCSI.SecretRef
  441. secretNs := defaultSecretNamespace
  442. if secretRef != nil {
  443. if len(secretRef.Namespace) != 0 {
  444. secretNs = secretRef.Namespace
  445. }
  446. return secretRef.Name, secretNs, nil
  447. }
  448. return "", "", nil
  449. }
  450. return "", "", fmt.Errorf("Spec does not reference an ISCSI volume type")
  451. }
  452. func createISCSIDisk(spec *volume.Spec, podUID types.UID, plugin *iscsiPlugin, manager diskManager, secret map[string]string) (*iscsiDisk, error) {
  453. tp, portals, iqn, lunStr, err := getISCSITargetInfo(spec)
  454. if err != nil {
  455. return nil, err
  456. }
  457. lun := strconv.Itoa(int(lunStr))
  458. portal := portalMounter(tp)
  459. var bkportal []string
  460. bkportal = append(bkportal, portal)
  461. for _, p := range portals {
  462. bkportal = append(bkportal, portalMounter(string(p)))
  463. }
  464. iface, initiatorNamePtr, err := getISCSIInitiatorInfo(spec)
  465. if err != nil {
  466. return nil, err
  467. }
  468. var initiatorName string
  469. if initiatorNamePtr != nil {
  470. initiatorName = *initiatorNamePtr
  471. }
  472. chapDiscovery, err := getISCSIDiscoveryCHAPInfo(spec)
  473. if err != nil {
  474. return nil, err
  475. }
  476. chapSession, err := getISCSISessionCHAPInfo(spec)
  477. if err != nil {
  478. return nil, err
  479. }
  480. return &iscsiDisk{
  481. podUID: podUID,
  482. VolName: spec.Name(),
  483. Portals: bkportal,
  484. Iqn: iqn,
  485. Lun: lun,
  486. Iface: iface,
  487. chapDiscovery: chapDiscovery,
  488. chapSession: chapSession,
  489. secret: secret,
  490. InitiatorName: initiatorName,
  491. manager: manager,
  492. plugin: plugin}, nil
  493. }
  494. func createSecretMap(spec *volume.Spec, plugin *iscsiPlugin, namespace string) (map[string]string, error) {
  495. var secret map[string]string
  496. chapDiscover, err := getISCSIDiscoveryCHAPInfo(spec)
  497. if err != nil {
  498. return nil, err
  499. }
  500. chapSession, err := getISCSISessionCHAPInfo(spec)
  501. if err != nil {
  502. return nil, err
  503. }
  504. if chapDiscover || chapSession {
  505. secretName, secretNamespace, err := getISCSISecretNameAndNamespace(spec, namespace)
  506. if err != nil {
  507. return nil, err
  508. }
  509. if len(secretName) > 0 && len(secretNamespace) > 0 {
  510. // if secret is provideded, retrieve it
  511. kubeClient := plugin.host.GetKubeClient()
  512. if kubeClient == nil {
  513. return nil, fmt.Errorf("Cannot get kube client")
  514. }
  515. secretObj, err := kubeClient.CoreV1().Secrets(secretNamespace).Get(secretName, metav1.GetOptions{})
  516. if err != nil {
  517. err = fmt.Errorf("Couldn't get secret %v/%v error: %v", secretNamespace, secretName, err)
  518. return nil, err
  519. }
  520. secret = make(map[string]string)
  521. for name, data := range secretObj.Data {
  522. klog.V(4).Infof("retrieving CHAP secret name: %s", name)
  523. secret[name] = string(data)
  524. }
  525. }
  526. }
  527. return secret, err
  528. }
  529. func createPersistentVolumeFromISCSIPVSource(volumeName string, iscsi v1.ISCSIPersistentVolumeSource) *v1.PersistentVolume {
  530. block := v1.PersistentVolumeBlock
  531. return &v1.PersistentVolume{
  532. ObjectMeta: metav1.ObjectMeta{
  533. Name: volumeName,
  534. },
  535. Spec: v1.PersistentVolumeSpec{
  536. PersistentVolumeSource: v1.PersistentVolumeSource{
  537. ISCSI: &iscsi,
  538. },
  539. VolumeMode: &block,
  540. },
  541. }
  542. }
  543. func getVolumeSpecFromGlobalMapPath(volumeName, globalMapPath string) (*volume.Spec, error) {
  544. // Retrieve volume spec information from globalMapPath
  545. // globalMapPath example:
  546. // plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}
  547. // plugins/kubernetes.io/iscsi/volumeDevices/iface-default/192.168.0.10:3260-iqn.2017-05.com.example:test-lun-0
  548. // device: 192.168.0.10:3260-iqn.2017-05.com.example:test-lun-0
  549. device, _, err := extractDeviceAndPrefix(globalMapPath)
  550. if err != nil {
  551. return nil, err
  552. }
  553. bkpPortal, iqn, err := extractPortalAndIqn(device)
  554. if err != nil {
  555. return nil, err
  556. }
  557. arr := strings.Split(device, "-lun-")
  558. if len(arr) < 2 {
  559. return nil, fmt.Errorf("failed to retrieve lun from globalMapPath: %v", globalMapPath)
  560. }
  561. lun, err := strconv.Atoi(arr[1])
  562. if err != nil {
  563. return nil, err
  564. }
  565. iface, found := extractIface(globalMapPath)
  566. if !found {
  567. return nil, fmt.Errorf("failed to retrieve iface from globalMapPath: %v", globalMapPath)
  568. }
  569. iscsiPV := createPersistentVolumeFromISCSIPVSource(volumeName,
  570. v1.ISCSIPersistentVolumeSource{
  571. TargetPortal: bkpPortal,
  572. IQN: iqn,
  573. Lun: int32(lun),
  574. ISCSIInterface: iface,
  575. },
  576. )
  577. klog.V(5).Infof("ConstructBlockVolumeSpec: TargetPortal: %v, IQN: %v, Lun: %v, ISCSIInterface: %v",
  578. iscsiPV.Spec.PersistentVolumeSource.ISCSI.TargetPortal,
  579. iscsiPV.Spec.PersistentVolumeSource.ISCSI.IQN,
  580. iscsiPV.Spec.PersistentVolumeSource.ISCSI.Lun,
  581. iscsiPV.Spec.PersistentVolumeSource.ISCSI.ISCSIInterface,
  582. )
  583. return volume.NewSpecFromPersistentVolume(iscsiPV, false), nil
  584. }