Skip to content

Commit

Permalink
Merge pull request #46912 from mtanino/automated-cherry-pick-of-#4623…
Browse files Browse the repository at this point in the history
…9-upstream-release-1.6

Automatic merge from submit-queue

Automated cherry pick of #46239

Cherry pick of #46239 on release-1.6.

#46239: Log out from multiple portals with iscsi storage plugin

This Cherry pick also includes fix #46968(follow-up fix for #46239)

#46968: iscsi storage plugin: bkpPortal should be initialized beforehand
  • Loading branch information
Kubernetes Submit Queue committed Jun 9, 2017
2 parents 3be4ea3 + cdb5e47 commit cca1964
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 32 deletions.
12 changes: 6 additions & 6 deletions pkg/volume/iscsi/iscsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UI
iscsiDisk: &iscsiDisk{
podUID: podUID,
volName: spec.Name(),
portals: bkportal,
iqn: iscsi.IQN,
Portals: bkportal,
Iqn: iscsi.IQN,
lun: lun,
iface: iface,
Iface: iface,
manager: manager,
plugin: plugin},
fsType: iscsi.FSType,
Expand Down Expand Up @@ -175,10 +175,10 @@ func (plugin *iscsiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*v
type iscsiDisk struct {
volName string
podUID types.UID
portals []string
iqn string
Portals []string
Iqn string
lun string
iface string
Iface string
plugin *iscsiPlugin
// Utility interface that provides API calls to the provider to attach/detach disks.
manager diskManager
Expand Down
115 changes: 89 additions & 26 deletions pkg/volume/iscsi/iscsi_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package iscsi

import (
"encoding/json"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -98,49 +99,78 @@ func makePDNameInternal(host volume.VolumeHost, portal string, iqn string, lun s
type ISCSIUtil struct{}

func (util *ISCSIUtil) MakeGlobalPDName(iscsi iscsiDisk) string {
return makePDNameInternal(iscsi.plugin.host, iscsi.portals[0], iscsi.iqn, iscsi.lun, iscsi.iface)
return makePDNameInternal(iscsi.plugin.host, iscsi.Portals[0], iscsi.Iqn, iscsi.lun, iscsi.Iface)
}

func (util *ISCSIUtil) persistISCSI(conf iscsiDisk, mnt string) error {
file := path.Join(mnt, "iscsi.json")
fp, err := os.Create(file)
if err != nil {
return fmt.Errorf("iscsi: create %s err %s", file, err)
}
defer fp.Close()
encoder := json.NewEncoder(fp)
if err = encoder.Encode(conf); err != nil {
return fmt.Errorf("iscsi: encode err: %v.", err)
}
return nil
}

func (util *ISCSIUtil) loadISCSI(conf *iscsiDisk, mnt string) error {
// NOTE: The iscsi config json is not deleted after logging out from target portals.
file := path.Join(mnt, "iscsi.json")
fp, err := os.Open(file)
if err != nil {
return fmt.Errorf("iscsi: open %s err %s", file, err)
}
defer fp.Close()
decoder := json.NewDecoder(fp)
if err = decoder.Decode(conf); err != nil {
return fmt.Errorf("iscsi: decode err: %v.", err)
}
return nil
}

func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
var devicePath string
var devicePaths []string
var iscsiTransport string

out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", b.iface, "-o", "show"})
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", b.Iface, "-o", "show"})
if err != nil {
glog.Errorf("iscsi: could not read iface %s error: %s", b.iface, string(out))
glog.Errorf("iscsi: could not read iface %s error: %s", b.Iface, string(out))
return err
}

iscsiTransport = extractTransportname(string(out))

bkpPortal := b.portals
bkpPortal := b.Portals
for _, tp := range bkpPortal {
// Rescan sessions to discover newly mapped LUNs. Do not specify the interface when rescanning
// to avoid establishing additional sessions to the same target.
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-T", b.iqn, "-R"})
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-T", b.Iqn, "-R"})
if err != nil {
glog.Errorf("iscsi: failed to rescan session with error: %s (%v)", string(out), err)
}

if iscsiTransport == "" {
glog.Errorf("iscsi: could not find transport name in iface %s", b.iface)
return errors.New(fmt.Sprintf("Could not parse iface file for %s", b.iface))
glog.Errorf("iscsi: could not find transport name in iface %s", b.Iface)
return errors.New(fmt.Sprintf("Could not parse iface file for %s", b.Iface))
} else if iscsiTransport == "tcp" {
devicePath = strings.Join([]string{"/dev/disk/by-path/ip", tp, "iscsi", b.iqn, "lun", b.lun}, "-")
devicePath = strings.Join([]string{"/dev/disk/by-path/ip", tp, "iscsi", b.Iqn, "lun", b.lun}, "-")
} else {
devicePath = strings.Join([]string{"/dev/disk/by-path/pci", "*", "ip", tp, "iscsi", b.iqn, "lun", b.lun}, "-")
devicePath = strings.Join([]string{"/dev/disk/by-path/pci", "*", "ip", tp, "iscsi", b.Iqn, "lun", b.lun}, "-")
}
exist := waitForPathToExist(devicePath, 1, iscsiTransport)
if exist == false {
// discover iscsi target
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discovery", "-t", "sendtargets", "-p", tp, "-I", b.iface})
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discovery", "-t", "sendtargets", "-p", tp, "-I", b.Iface})
if err != nil {
glog.Errorf("iscsi: failed to sendtargets to portal %s error: %s", tp, string(out))
continue
}
// login to iscsi target
out, err = b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-T", b.iqn, "-I", b.iface, "--login"})
out, err = b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-T", b.Iqn, "-I", b.Iface, "--login"})
if err != nil {
glog.Errorf("iscsi: failed to attach disk:Error: %s (%v)", string(out), err)
continue
Expand Down Expand Up @@ -178,6 +208,9 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
return err
}

// Persist iscsi disk config to json file for DetachDisk path
util.persistISCSI(*(b.iscsiDisk), globalPDPath)

for _, path := range devicePaths {
// There shouldnt be any empty device paths. However adding this check
// for safer side to avoid the possibility of an empty entry.
Expand Down Expand Up @@ -217,27 +250,44 @@ func (util *ISCSIUtil) DetachDisk(c iscsiDiskUnmounter, mntPath string) error {
}
refCount, err := getDevicePrefixRefCount(c.mounter, prefix)
if err == nil && refCount == 0 {
// This portal/iqn/iface is no longer referenced, log out.
// Extract the portal and iqn from device path.
portal, iqn, err := extractPortalAndIqn(device)
if err != nil {
return err
var bkpPortal []string
var iqn, iface string
found := true

// load iscsi disk config from json file
if err := util.loadISCSI(c.iscsiDisk, mntPath); err == nil {
bkpPortal, iqn, iface = c.iscsiDisk.Portals, c.iscsiDisk.Iqn, c.iscsiDisk.Iface
} else {
// If the iscsi disk config is not found, fall back to the original behavior.
// This portal/iqn/iface is no longer referenced, log out.
// Extract the portal and iqn from device path.
bkpPortal = make([]string, 1)
bkpPortal[0], iqn, err = extractPortalAndIqn(device)
if err != nil {
return err
}
// Extract the iface from the mountPath and use it to log out. If the iface
// is not found, maintain the previous behavior to facilitate kubelet upgrade.
// Logout may fail as no session may exist for the portal/IQN on the specified interface.
iface, found = extractIface(mntPath)
}
// Extract the iface from the mountPath and use it to log out. If the iface
// is not found, maintain the previous behavior to facilitate kubelet upgrade.
// Logout may fail as no session may exist for the portal/IQN on the specified interface.
iface, found := extractIface(mntPath)
if found {
for _, portal := range removeDuplicate(bkpPortal) {
logout := []string{"-m", "node", "-p", portal, "-T", iqn, "--logout"}
delete := []string{"-m", "node", "-p", portal, "-T", iqn, "-o", "delete"}
if found {
logout = append(logout, []string{"-I", iface}...)
delete = append(delete, []string{"-I", iface}...)
}
glog.Infof("iscsi: log out target %s iqn %s iface %s", portal, iqn, iface)
out, err := c.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", portal, "-T", iqn, "-I", iface, "--logout"})
out, err := c.plugin.execCommand("iscsiadm", logout)
if err != nil {
glog.Errorf("iscsi: failed to detach disk Error: %s", string(out))
}
} else {
glog.Infof("iscsi: log out target %s iqn %s", portal, iqn)
out, err := c.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", portal, "-T", iqn, "--logout"})
// Delete the node record
glog.Infof("iscsi: delete node record target %s iqn %s", portal, iqn)
out, err = c.plugin.execCommand("iscsiadm", delete)
if err != nil {
glog.Errorf("iscsi: failed to detach disk Error: %s", string(out))
glog.Errorf("iscsi: failed to delete node record Error: %s", string(out))
}
}
}
Expand Down Expand Up @@ -305,3 +355,16 @@ func extractPortalAndIqn(device string) (string, string, error) {
iqn := device[ind2:ind]
return portal, iqn, nil
}

// Remove duplicates or string
func removeDuplicate(s []string) []string {
m := map[string]bool{}
for _, v := range s {
if v != "" && !m[v] {
s[len(m)] = v
m[v] = true
}
}
s = s[:len(m)]
return s
}
10 changes: 10 additions & 0 deletions pkg/volume/iscsi/iscsi_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package iscsi
import (
"os"
"path/filepath"
"reflect"
"testing"

"k8s.io/kubernetes/pkg/util/mount"
Expand Down Expand Up @@ -91,6 +92,15 @@ func TestExtractPortalAndIqn(t *testing.T) {
}
}

func TestRemoveDuplicate(t *testing.T) {
dupPortals := []string{"127.0.0.1:3260", "127.0.0.1:3260", "127.0.0.100:3260"}
portals := removeDuplicate(dupPortals)
want := []string{"127.0.0.1:3260", "127.0.0.100:3260"}
if reflect.DeepEqual(portals, want) == false {
t.Errorf("removeDuplicate: want: %s, got: %s", want, portals)
}
}

func fakeOsStat(devicePath string) (fi os.FileInfo, err error) {
var cmd os.FileInfo
return cmd, nil
Expand Down

0 comments on commit cca1964

Please sign in to comment.