diff --git a/integrationtests/iscsi_ps_scripts.go b/integrationtests/iscsi_ps_scripts.go
index 89bec253..202e390b 100644
--- a/integrationtests/iscsi_ps_scripts.go
+++ b/integrationtests/iscsi_ps_scripts.go
@@ -42,14 +42,14 @@ $ProgressPreference = "SilentlyContinue"
$targetName = "%s"
# Get local IPv4 (e.g. 10.30.1.15, not 127.0.0.1)
-$address = $(Get-NetIPAddress | Where-Object { $_.InterfaceAlias -eq "Ethernet" -and $_.AddressFamily -eq "IPv4" }).IPAddress
+$address = $(Get-NetIPAddress | Where-Object { $_.InterfaceAlias -eq "%s" -and $_.AddressFamily -eq "IPv4" }).IPAddress
# Create virtual disk in RAM
-New-IscsiVirtualDisk -Path "ramdisk:scratch-${targetName}.vhdx" -Size 100MB | Out-Null
+New-IscsiVirtualDisk -Path "ramdisk:scratch-${targetName}.vhdx" -Size 100MB -ComputerName $env:computername | Out-Null
# Create a target that allows all initiator IQNs and map a disk to the new target
-$target = New-IscsiServerTarget -TargetName $targetName -InitiatorIds @("Iqn:*")
-Add-IscsiVirtualDiskTargetMapping -TargetName $targetName -DevicePath "ramdisk:scratch-${targetName}.vhdx" | Out-Null
+$target = New-IscsiServerTarget -TargetName $targetName -InitiatorIds @("Iqn:*") -ComputerName $env:computername
+Add-IscsiVirtualDiskTargetMapping -TargetName $targetName -DevicePath "ramdisk:scratch-${targetName}.vhdx" -ComputerName $env:computername | Out-Null
$output = @{
"iqn" = "$($target.TargetIqn)"
@@ -68,7 +68,7 @@ $username = "%s"
$password = "%s"
$securestring = ConvertTo-SecureString -String $password -AsPlainText -Force
$chap = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($username, $securestring)
-Set-IscsiServerTarget -TargetName $targetName -EnableChap $true -Chap $chap
+Set-IscsiServerTarget -TargetName $targetName -EnableChap $true -Chap $chap -ComputerName $env:computername
`
func setChap(targetName string, username string, password string) error {
@@ -92,7 +92,7 @@ $securestring = ConvertTo-SecureString -String $password -AsPlainText -Force
# Windows initiator does not uses the username for mutual authentication
$chap = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($username, $securestring)
-Set-IscsiServerTarget -TargetName $targetName -EnableReverseChap $true -ReverseChap $chap
+Set-IscsiServerTarget -TargetName $targetName -EnableReverseChap $true -ReverseChap $chap -ComputerName $env:computername
`
func setReverseChap(targetName string, password string) error {
@@ -131,8 +131,8 @@ Get-IscsiTarget | Disconnect-IscsiTarget -Confirm:$false
Get-IscsiTargetPortal | Remove-IscsiTargetPortal -confirm:$false
# Clean target
-Get-IscsiServerTarget | Remove-IscsiServerTarget
-Get-IscsiVirtualDisk | Remove-IscsiVirtualDisk
+Get-IscsiServerTarget -ComputerName $env:computername | Remove-IscsiServerTarget
+Get-IscsiVirtualDisk -ComputerName $env:computername | Remove-IscsiVirtualDisk
# Stop iSCSI initiator
Get-Service "MsiSCSI" | Stop-Service
@@ -173,7 +173,12 @@ func runPowershellScript(script string) (string, error) {
}
func setupEnv(targetName string) (*IscsiSetupConfig, error) {
- script := fmt.Sprintf(IscsiEnvironmentSetupScript, targetName)
+ ethernetName := "Ethernet"
+ if val, ok := os.LookupEnv("ETHERNET_NAME"); ok {
+ ethernetName = val
+ }
+
+ script := fmt.Sprintf(IscsiEnvironmentSetupScript, targetName, ethernetName)
out, err := runPowershellScript(script)
if err != nil {
return nil, fmt.Errorf("failed setting up environment. err=%v", err)
diff --git a/pkg/cim/iscsi.go b/pkg/cim/iscsi.go
new file mode 100644
index 00000000..9f1ac2a1
--- /dev/null
+++ b/pkg/cim/iscsi.go
@@ -0,0 +1,261 @@
+//go:build windows
+// +build windows
+
+package cim
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/microsoft/wmi/pkg/base/query"
+ "github.com/microsoft/wmi/server2019/root/microsoft/windows/storage"
+)
+
+// ListISCSITargetPortals retrieves a list of iSCSI target portals.
+//
+// The equivalent WMI query is:
+//
+// SELECT [selectors] FROM MSFT_IscsiTargetPortal
+//
+// Refer to https://learn.microsoft.com/en-us/previous-versions/windows/desktop/iscsidisc/msft-iscsitargetportal
+// for the WMI class definition.
+func ListISCSITargetPortals(selectorList []string) ([]*storage.MSFT_iSCSITargetPortal, error) {
+ q := query.NewWmiQueryWithSelectList("MSFT_IscsiTargetPortal", selectorList)
+ instances, err := QueryInstances(WMINamespaceStorage, q)
+ if IgnoreNotFound(err) != nil {
+ return nil, err
+ }
+
+ var targetPortals []*storage.MSFT_iSCSITargetPortal
+ for _, instance := range instances {
+ portal, err := storage.NewMSFT_iSCSITargetPortalEx1(instance)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query iSCSI target portal %v. error: %v", instance, err)
+ }
+
+ targetPortals = append(targetPortals, portal)
+ }
+
+ return targetPortals, nil
+}
+
+// QueryISCSITargetPortal retrieves information about a specific iSCSI target portal
+// identified by its network address and port number.
+//
+// The equivalent WMI query is:
+//
+// SELECT [selectors] FROM MSFT_IscsiTargetPortal
+// WHERE TargetPortalAddress = '
'
+// AND TargetPortalPortNumber = ''
+//
+// Refer to https://learn.microsoft.com/en-us/previous-versions/windows/desktop/iscsidisc/msft-iscsitargetportal
+// for the WMI class definition.
+func QueryISCSITargetPortal(address string, port uint32, selectorList []string) (*storage.MSFT_iSCSITargetPortal, error) {
+ portalQuery := query.NewWmiQueryWithSelectList(
+ "MSFT_iSCSITargetPortal", selectorList,
+ "TargetPortalAddress", address,
+ "TargetPortalPortNumber", strconv.Itoa(int(port)))
+ instances, err := QueryInstances(WMINamespaceStorage, portalQuery)
+ if err != nil {
+ return nil, err
+ }
+
+ targetPortal, err := storage.NewMSFT_iSCSITargetPortalEx1(instances[0])
+ if err != nil {
+ return nil, fmt.Errorf("failed to query iSCSI target portal at (%s:%d). error: %v", address, port, err)
+ }
+
+ return targetPortal, nil
+}
+
+// NewISCSITargetPortal creates a new iSCSI target portal.
+//
+// Refer to https://learn.microsoft.com/en-us/previous-versions/windows/desktop/iscsidisc/msft-iscsitargetportal-new
+// for the WMI method definition.
+func NewISCSITargetPortal(targetPortalAddress string,
+ targetPortalPortNumber uint32,
+ initiatorInstanceName *string,
+ initiatorPortalAddress *string,
+ isHeaderDigest *bool,
+ isDataDigest *bool) (*storage.MSFT_iSCSITargetPortal, error) {
+ params := map[string]interface{}{
+ "TargetPortalAddress": targetPortalAddress,
+ "TargetPortalPortNumber": targetPortalPortNumber,
+ }
+ if initiatorInstanceName != nil {
+ params["InitiatorInstanceName"] = *initiatorInstanceName
+ }
+ if initiatorPortalAddress != nil {
+ params["InitiatorPortalAddress"] = *initiatorPortalAddress
+ }
+ if isHeaderDigest != nil {
+ params["IsHeaderDigest"] = *isHeaderDigest
+ }
+ if isDataDigest != nil {
+ params["IsDataDigest"] = *isDataDigest
+ }
+ result, _, err := InvokeCimMethod(WMINamespaceStorage, "MSFT_iSCSITargetPortal", "New", params)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create iSCSI target portal with %v. result: %d, error: %v", params, result, err)
+ }
+
+ return QueryISCSITargetPortal(targetPortalAddress, targetPortalPortNumber, nil)
+}
+
+// ListISCSITargetsByTargetPortal retrieves all iSCSI targets from the specified iSCSI target portal
+// using MSFT_iSCSITargetToiSCSITargetPortal association.
+//
+// WMI association MSFT_iSCSITargetToiSCSITargetPortal:
+//
+// iSCSITarget | iSCSITargetPortal
+// ----------- | -----------------
+// MSFT_iSCSITarget (NodeAddress = "iqn.1991-05.com.microsoft:win-8e2evaq9q...) | MSFT_iSCSITargetPortal (TargetPortalAdd...
+//
+// Refer to https://learn.microsoft.com/en-us/previous-versions/windows/desktop/iscsidisc/msft-iscsitarget
+// for the WMI class definition.
+func ListISCSITargetsByTargetPortal(portals []*storage.MSFT_iSCSITargetPortal) ([]*storage.MSFT_iSCSITarget, error) {
+ var targets []*storage.MSFT_iSCSITarget
+ for _, portal := range portals {
+ collection, err := portal.GetAssociated("MSFT_iSCSITargetToiSCSITargetPortal", "MSFT_iSCSITarget", "iSCSITarget", "iSCSITargetPortal")
+ if err != nil {
+ return nil, fmt.Errorf("failed to query associated iSCSITarget for %v. error: %v", portal, err)
+ }
+
+ for _, instance := range collection {
+ target, err := storage.NewMSFT_iSCSITargetEx1(instance)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query iSCSI target %v. error: %v", instance, err)
+ }
+
+ targets = append(targets, target)
+ }
+ }
+
+ return targets, nil
+}
+
+// QueryISCSITarget retrieves the iSCSI target from the specified portal address, portal and node address.
+func QueryISCSITarget(address string, port uint32, nodeAddress string) (*storage.MSFT_iSCSITarget, error) {
+ portal, err := QueryISCSITargetPortal(address, port, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ targets, err := ListISCSITargetsByTargetPortal([]*storage.MSFT_iSCSITargetPortal{portal})
+ if err != nil {
+ return nil, err
+ }
+
+ for _, target := range targets {
+ targetNodeAddress, err := target.GetProperty("NodeAddress")
+ if err != nil {
+ return nil, fmt.Errorf("failed to query iSCSI target %v. error: %v", target, err)
+ }
+
+ if targetNodeAddress == nodeAddress {
+ return target, nil
+ }
+ }
+
+ return nil, nil
+}
+
+// QueryISCSISessionByTarget retrieves the iSCSI session from the specified iSCSI target
+// using MSFT_iSCSITargetToiSCSISession association.
+//
+// WMI association MSFT_iSCSITargetToiSCSISession:
+//
+// iSCSISession | iSCSITarget
+// ------------ | -----------
+// MSFT_iSCSISession (SessionIdentifier = "ffffac0cacbff010-4000013700000016") | MSFT_iSCSITarget (NodeAddress = "iqn.199...
+//
+// Refer to https://learn.microsoft.com/en-us/previous-versions/windows/desktop/iscsidisc/msft-iscsisession
+// for the WMI class definition.
+func QueryISCSISessionByTarget(target *storage.MSFT_iSCSITarget) (*storage.MSFT_iSCSISession, error) {
+ collection, err := target.GetAssociated("MSFT_iSCSITargetToiSCSISession", "MSFT_iSCSISession", "iSCSISession", "iSCSITarget")
+ if err != nil {
+ return nil, fmt.Errorf("failed to query associated iSCSISession for %v. error: %v", target, err)
+ }
+
+ if len(collection) == 0 {
+ return nil, nil
+ }
+
+ session, err := storage.NewMSFT_iSCSISessionEx1(collection[0])
+ return session, err
+}
+
+// ListDisksByTarget find all disks associated with an iSCSITarget.
+// It finds out the iSCSIConnections from MSFT_iSCSITargetToiSCSIConnection association,
+// then locate MSFT_Disk objects from MSFT_iSCSIConnectionToDisk association.
+//
+// WMI association MSFT_iSCSITargetToiSCSIConnection:
+//
+// iSCSIConnection | iSCSITarget
+// --------------- | -----------
+// MSFT_iSCSIConnection (ConnectionIdentifier = "ffffac0cacbff010-15") | MSFT_iSCSITarget (NodeAddress = "iqn.1991-05.com...
+//
+// WMI association MSFT_iSCSIConnectionToDisk:
+//
+// Disk | iSCSIConnection
+// ---- | ---------------
+// MSFT_Disk (ObjectId = "{1}\\WIN-8E2EVAQ9QSB\root/Microsoft/Win...) | MSFT_iSCSIConnection (ConnectionIdentifier = "fff...
+//
+// Refer to https://learn.microsoft.com/en-us/previous-versions/windows/desktop/iscsidisc/msft-iscsiconnection
+// for the WMI class definition.
+func ListDisksByTarget(target *storage.MSFT_iSCSITarget, selectorList []string) ([]*storage.MSFT_Disk, error) {
+ // list connections to the given iSCSI target
+ collection, err := target.GetAssociated("MSFT_iSCSITargetToiSCSIConnection", "MSFT_iSCSIConnection", "iSCSIConnection", "iSCSITarget")
+ if err != nil {
+ return nil, fmt.Errorf("failed to query associated iSCSISession for %v. error: %v", target, err)
+ }
+
+ if len(collection) == 0 {
+ return nil, nil
+ }
+
+ var result []*storage.MSFT_Disk
+ for _, conn := range collection {
+ instances, err := conn.GetAssociated("MSFT_iSCSIConnectionToDisk", "MSFT_Disk", "Disk", "iSCSIConnection")
+ if err != nil {
+ return nil, fmt.Errorf("failed to query associated disk for %v. error: %v", target, err)
+ }
+
+ for _, instance := range instances {
+ disk, err := storage.NewMSFT_DiskEx1(instance)
+ if err != nil {
+ return nil, fmt.Errorf("failed to query associated disk %v. error: %v", instance, err)
+ }
+
+ result = append(result, disk)
+ }
+ }
+
+ return result, err
+}
+
+// ConnectISCSITarget establishes a connection to an iSCSI target with optional CHAP authentication credential.
+//
+// Refer https://learn.microsoft.com/en-us/previous-versions/windows/desktop/iscsidisc/msft-iscsitarget-connect
+// for the WMI method definition.
+func ConnectISCSITarget(portalAddress string, portalPortNumber uint32, nodeAddress string, authType string, chapUsername *string, chapSecret *string) (int, map[string]interface{}, error) {
+ inParams := map[string]interface{}{
+ "NodeAddress": nodeAddress,
+ "TargetPortalAddress": portalAddress,
+ "TargetPortalPortNumber": int(portalPortNumber),
+ "AuthenticationType": authType,
+ }
+ // InitiatorPortalAddress
+ // IsDataDigest
+ // IsHeaderDigest
+ // ReportToPnP
+ if chapUsername != nil {
+ inParams["ChapUsername"] = *chapUsername
+ }
+ if chapSecret != nil {
+ inParams["ChapSecret"] = *chapSecret
+ }
+
+ result, outParams, err := InvokeCimMethod(WMINamespaceStorage, "MSFT_iSCSITarget", "Connect", inParams)
+ return result, outParams, err
+}
diff --git a/pkg/os/iscsi/api.go b/pkg/os/iscsi/api.go
index 559ed3b5..05532641 100644
--- a/pkg/os/iscsi/api.go
+++ b/pkg/os/iscsi/api.go
@@ -1,10 +1,13 @@
package iscsi
import (
- "encoding/json"
"fmt"
+ "strconv"
+ "strings"
- "github.com/kubernetes-csi/csi-proxy/pkg/utils"
+ "github.com/kubernetes-csi/csi-proxy/pkg/cim"
+ "github.com/microsoft/wmi/server2019/root/microsoft/windows/storage"
+ "k8s.io/klog/v2"
)
// Implements the iSCSI OS API calls. All code here should be very simple
@@ -18,70 +21,104 @@ func New() APIImplementor {
return APIImplementor{}
}
+func parseTargetPortal(instance *storage.MSFT_iSCSITargetPortal) (string, uint32, error) {
+ portalAddress, err := instance.GetPropertyTargetPortalAddress()
+ if err != nil {
+ return "", 0, fmt.Errorf("failed parsing target portal address %v. err: %w", instance, err)
+ }
+
+ portalPort, err := instance.GetProperty("TargetPortalPortNumber")
+ if err != nil {
+ return "", 0, fmt.Errorf("failed parsing target portal port number %v. err: %w", instance, err)
+ }
+
+ return portalAddress, uint32(portalPort.(int32)), nil
+}
+
func (APIImplementor) AddTargetPortal(portal *TargetPortal) error {
- cmdLine := fmt.Sprintf(
- `New-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} ` +
- `-TargetPortalPortNumber ${Env:iscsi_tp_port}`)
- out, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("iscsi_tp_address=%s", portal.Address),
- fmt.Sprintf("iscsi_tp_port=%d", portal.Port))
+ existing, err := cim.QueryISCSITargetPortal(portal.Address, portal.Port, nil)
+ if cim.IgnoreNotFound(err) != nil {
+ return err
+ }
+
+ if existing != nil {
+ klog.V(2).Infof("target portal at (%s:%d) already exists", portal.Address, portal.Port)
+ return nil
+ }
+
+ _, err = cim.NewISCSITargetPortal(portal.Address, portal.Port, nil, nil, nil, nil)
if err != nil {
- return fmt.Errorf("error adding target portal. cmd %s, output: %s, err: %v", cmdLine, string(out), err)
+ return fmt.Errorf("error adding target portal at (%s:%d). err: %v", portal.Address, portal.Port, err)
}
return nil
}
func (APIImplementor) DiscoverTargetPortal(portal *TargetPortal) ([]string, error) {
- // ConvertTo-Json is not part of the pipeline because powershell converts an
- // array with one element to a single element
- cmdLine := fmt.Sprintf(
- `ConvertTo-Json -InputObject @(Get-IscsiTargetPortal -TargetPortalAddress ` +
- `${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port} | ` +
- `Get-IscsiTarget | Select-Object -ExpandProperty NodeAddress)`)
- out, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("iscsi_tp_address=%s", portal.Address),
- fmt.Sprintf("iscsi_tp_port=%d", portal.Port))
+ instance, err := cim.QueryISCSITargetPortal(portal.Address, portal.Port, nil)
if err != nil {
- return nil, fmt.Errorf("error discovering target portal. cmd: %s, output: %s, err: %w", cmdLine, string(out), err)
+ return nil, err
}
- var iqns []string
- err = json.Unmarshal(out, &iqns)
+ targets, err := cim.ListISCSITargetsByTargetPortal([]*storage.MSFT_iSCSITargetPortal{instance})
if err != nil {
- return nil, fmt.Errorf("failed parsing iqn list. cmd: %s output: %s, err: %w", cmdLine, string(out), err)
+ return nil, err
+ }
+
+ var iqns []string
+ for _, target := range targets {
+ iqn, err := target.GetProperty("NodeAddress")
+ if err != nil {
+ return nil, fmt.Errorf("failed parsing node address of target %v to target portal at (%s:%d). err: %w", target, portal.Address, portal.Port, err)
+ }
+
+ iqns = append(iqns, iqn.(string))
}
return iqns, nil
}
func (APIImplementor) ListTargetPortals() ([]TargetPortal, error) {
- cmdLine := fmt.Sprintf(
- `ConvertTo-Json -InputObject @(Get-IscsiTargetPortal | ` +
- `Select-Object TargetPortalAddress, TargetPortalPortNumber)`)
-
- out, err := utils.RunPowershellCmd(cmdLine)
+ instances, err := cim.ListISCSITargetPortals([]string{"TargetPortalAddress", "TargetPortalPortNumber"})
if err != nil {
- return nil, fmt.Errorf("error listing target portals. cmd %s, output: %s, err: %w", cmdLine, string(out), err)
+ return nil, err
}
var portals []TargetPortal
- err = json.Unmarshal(out, &portals)
- if err != nil {
- return nil, fmt.Errorf("failed parsing target portal list. cmd: %s output: %s, err: %w", cmdLine, string(out), err)
+ for _, instance := range instances {
+ address, port, err := parseTargetPortal(instance)
+ if err != nil {
+ return nil, fmt.Errorf("failed parsing target portal %v. err: %w", instance, err)
+ }
+
+ portals = append(portals, TargetPortal{
+ Address: address,
+ Port: port,
+ })
}
return portals, nil
}
func (APIImplementor) RemoveTargetPortal(portal *TargetPortal) error {
- cmdLine := fmt.Sprintf(
- `Get-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} ` +
- `-TargetPortalPortNumber ${Env:iscsi_tp_port} | Remove-IscsiTargetPortal ` +
- `-Confirm:$false`)
+ instance, err := cim.QueryISCSITargetPortal(portal.Address, portal.Port, nil)
+ if err != nil {
+ return err
+ }
- out, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("iscsi_tp_address=%s", portal.Address),
- fmt.Sprintf("iscsi_tp_port=%d", portal.Port))
+ address, port, err := parseTargetPortal(instance)
if err != nil {
- return fmt.Errorf("error removing target portal. cmd %s, output: %s, err: %w", cmdLine, string(out), err)
+ return fmt.Errorf("failed to parse target portal %v. error: %v", instance, err)
+ }
+
+ result, err := instance.InvokeMethodWithReturn("Remove",
+ nil,
+ nil,
+ int(port),
+ address,
+ )
+ if result != 0 || err != nil {
+ return fmt.Errorf("error removing target portal at (%s:%d). result: %d, err: %w", address, port, result, err)
}
return nil
@@ -89,86 +126,116 @@ func (APIImplementor) RemoveTargetPortal(portal *TargetPortal) error {
func (APIImplementor) ConnectTarget(portal *TargetPortal, iqn string,
authType string, chapUser string, chapSecret string) error {
- // Not using InputObject as Connect-IscsiTarget's InputObject does not work.
- // This is due to being a static WMI method together with a bug in the
- // powershell version of the API.
- cmdLine := fmt.Sprintf(
- `Connect-IscsiTarget -TargetPortalAddress ${Env:iscsi_tp_address}` +
- ` -TargetPortalPortNumber ${Env:iscsi_tp_port} -NodeAddress ${Env:iscsi_target_iqn}` +
- ` -AuthenticationType ${Env:iscsi_auth_type}`)
+ target, err := cim.QueryISCSITarget(portal.Address, portal.Port, iqn)
+ if err != nil {
+ return err
+ }
- if chapUser != "" {
- cmdLine += ` -ChapUsername ${Env:iscsi_chap_user}`
+ connected, err := target.GetPropertyIsConnected()
+ if err != nil {
+ return err
}
- if chapSecret != "" {
- cmdLine += ` -ChapSecret ${Env:iscsi_chap_secret}`
+ if connected {
+ klog.V(2).Infof("target %s from target portal at (%s:%d) is connected.", iqn, portal.Address, portal.Port)
+ return nil
}
- out, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("iscsi_tp_address=%s", portal.Address),
- fmt.Sprintf("iscsi_tp_port=%d", portal.Port),
- fmt.Sprintf("iscsi_target_iqn=%s", iqn),
- fmt.Sprintf("iscsi_auth_type=%s", authType),
- fmt.Sprintf("iscsi_chap_user=%s", chapUser),
- fmt.Sprintf("iscsi_chap_secret=%s", chapSecret))
+ targetAuthType := strings.ToUpper(strings.ReplaceAll(authType, "_", ""))
+
+ result, _, err := cim.ConnectISCSITarget(portal.Address, portal.Port, iqn, targetAuthType, &chapUser, &chapSecret)
if err != nil {
- return fmt.Errorf("error connecting to target portal. cmd %s, output: %s, err: %w", cmdLine, string(out), err)
+ return fmt.Errorf("error connecting to target portal. result: %d, err: %w", result, err)
}
return nil
}
func (APIImplementor) DisconnectTarget(portal *TargetPortal, iqn string) error {
- // Using InputObject instead of pipe to verify input is not empty
- cmdLine := fmt.Sprintf(
- `Disconnect-IscsiTarget -InputObject (Get-IscsiTargetPortal ` +
- `-TargetPortalAddress ${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port} ` +
- ` | Get-IscsiTarget | Where-Object { $_.NodeAddress -eq ${Env:iscsi_target_iqn} }) ` +
- `-Confirm:$false`)
+ target, err := cim.QueryISCSITarget(portal.Address, portal.Port, iqn)
+ if err != nil {
+ return err
+ }
+
+ connected, err := target.GetPropertyIsConnected()
+ if err != nil {
+ return fmt.Errorf("error query connected of target %s from target portal at (%s:%d). err: %w", iqn, portal.Address, portal.Port, err)
+ }
+
+ if !connected {
+ klog.V(2).Infof("target %s from target portal at (%s:%d) is not connected.", iqn, portal.Address, portal.Port)
+ return nil
+ }
- out, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("iscsi_tp_address=%s", portal.Address),
- fmt.Sprintf("iscsi_tp_port=%d", portal.Port),
- fmt.Sprintf("iscsi_target_iqn=%s", iqn))
+ // get session
+ session, err := cim.QueryISCSISessionByTarget(target)
if err != nil {
- return fmt.Errorf("error disconnecting from target portal. cmd %s, output: %s, err: %w", cmdLine, string(out), err)
+ return fmt.Errorf("error query session of target %s from target portal at (%s:%d). err: %w", iqn, portal.Address, portal.Port, err)
+ }
+
+ sessionIdentifier, err := session.GetPropertySessionIdentifier()
+ if err != nil {
+ return fmt.Errorf("error query session identifier of target %s from target portal at (%s:%d). err: %w", iqn, portal.Address, portal.Port, err)
+ }
+
+ persistent, err := session.GetPropertyIsPersistent()
+ if err != nil {
+ return fmt.Errorf("error query session persistency of target %s from target portal at (%s:%d). err: %w", iqn, portal.Address, portal.Port, err)
+ }
+
+ if persistent {
+ result, err := session.InvokeMethodWithReturn("Unregister")
+ if err != nil {
+ return fmt.Errorf("error unregister session on target %s from target portal at (%s:%d). result: %d, err: %w", iqn, portal.Address, portal.Port, result, err)
+ }
+ }
+
+ result, err := target.InvokeMethodWithReturn("Disconnect", sessionIdentifier)
+ if err != nil {
+ return fmt.Errorf("error disconnecting target %s from target portal at (%s:%d). result: %d, err: %w", iqn, portal.Address, portal.Port, result, err)
}
return nil
}
func (APIImplementor) GetTargetDisks(portal *TargetPortal, iqn string) ([]string, error) {
- // Converting DiskNumber to string for compatibility with disk api group
- // Not using pipeline in order to validate that items are non-empty
- cmdLine := fmt.Sprintf(
- `$ErrorActionPreference = "Stop"; ` +
- `$tp = Get-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port}; ` +
- `$t = $tp | Get-IscsiTarget | Where-Object { $_.NodeAddress -eq ${Env:iscsi_target_iqn} }; ` +
- `$c = Get-IscsiConnection -IscsiTarget $t; ` +
- `$ids = $c | Get-Disk | Select -ExpandProperty Number | Out-String -Stream; ` +
- `ConvertTo-Json -InputObject @($ids)`)
+ target, err := cim.QueryISCSITarget(portal.Address, portal.Port, iqn)
+ if err != nil {
+ return nil, err
+ }
- out, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("iscsi_tp_address=%s", portal.Address),
- fmt.Sprintf("iscsi_tp_port=%d", portal.Port),
- fmt.Sprintf("iscsi_target_iqn=%s", iqn))
+ connected, err := target.GetPropertyIsConnected()
if err != nil {
- return nil, fmt.Errorf("error getting target disks. cmd %s, output: %s, err: %w", cmdLine, string(out), err)
+ return nil, fmt.Errorf("error query connected of target %s from target portal at (%s:%d). err: %w", iqn, portal.Address, portal.Port, err)
}
- var ids []string
- err = json.Unmarshal(out, &ids)
+ if !connected {
+ klog.V(2).Infof("target %s from target portal at (%s:%d) is not connected.", iqn, portal.Address, portal.Port)
+ return nil, nil
+ }
+
+ disks, err := cim.ListDisksByTarget(target, []string{})
+
if err != nil {
- return nil, fmt.Errorf("error parsing iqn target disks. cmd: %s output: %s, err: %w", cmdLine, string(out), err)
+ return nil, fmt.Errorf("error getting target disks on target %s from target portal at (%s:%d). err: %w", iqn, portal.Address, portal.Port, err)
}
+ var ids []string
+ for _, disk := range disks {
+ number, err := disk.GetProperty("Number")
+ if err != nil {
+ return nil, fmt.Errorf("error getting number of disk %v on target %s from target portal at (%s:%d). err: %w", disk, iqn, portal.Address, portal.Port, err)
+ }
+
+ ids = append(ids, strconv.Itoa(int(number.(int32))))
+ }
return ids, nil
}
func (APIImplementor) SetMutualChapSecret(mutualChapSecret string) error {
- cmdLine := `Set-IscsiChapSecret -ChapSecret ${Env:iscsi_mutual_chap_secret}`
- out, err := utils.RunPowershellCmd(cmdLine, fmt.Sprintf("iscsi_mutual_chap_secret=%s", mutualChapSecret))
+ result, _, err := cim.InvokeCimMethod(cim.WMINamespaceStorage, "MSFT_iSCSISession", "SetCHAPSecret", map[string]interface{}{"ChapSecret": mutualChapSecret})
if err != nil {
- return fmt.Errorf("error setting mutual chap secret. cmd %s,"+
- " output: %s, err: %v", cmdLine, string(out), err)
+ return fmt.Errorf("error setting mutual chap secret. result: %d, err: %v", result, err)
}
return nil