Skip to content

Commit

Permalink
Use WMI instead of PowerShell for OS operations
Browse files Browse the repository at this point in the history
  • Loading branch information
laozc committed Nov 9, 2024
1 parent 67871ec commit 7aafa45
Show file tree
Hide file tree
Showing 1,711 changed files with 477,638 additions and 385 deletions.
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
module github.com/kubernetes-csi/csi-proxy

go 1.20
go 1.22

toolchain go1.23.2

require (
github.com/Microsoft/go-winio v0.6.1
github.com/go-ole/go-ole v1.3.0
github.com/google/go-cmp v0.6.0
github.com/iancoleman/strcase v0.3.0
github.com/kubernetes-csi/csi-proxy/client v0.0.0-00010101000000-000000000000
github.com/microsoft/wmi v0.23.0
github.com/pkg/errors v0.9.1
github.com/sergi/go-diff v1.3.1
github.com/spf13/pflag v1.0.5
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -50,6 +52,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mauriciopoppe/gengo v0.0.0-20210525224835-9c78f58f3486 h1:+l047vEi0SyAzdVToIaAcfoY5DwwGW+OyqTdH/P3TTg=
github.com/mauriciopoppe/gengo v0.0.0-20210525224835-9c78f58f3486/go.mod h1:xXv3T4UXTLta31wMhVezwVkc26OLei4hMbLeBJbPmxc=
github.com/microsoft/wmi v0.23.0 h1:EbgjakKBOfb4QaTJNiGkfKrb2RWv7wpyicI2g3DHWkw=
github.com/microsoft/wmi v0.23.0/go.mod h1:PNc5VFG7cpB7VOb3ILZNuWMWsqFfYLPyzpoiFkA6fAQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down Expand Up @@ -85,10 +89,12 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
60 changes: 60 additions & 0 deletions pkg/cim/disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package cim

import (
"fmt"
"strconv"

"github.com/microsoft/wmi/pkg/base/query"
"github.com/microsoft/wmi/server2019/root/microsoft/windows/storage"
)

const (
// PartitionStyleUnknown indicates an unknown partition table format
PartitionStyleUnknown = 0
// PartitionStyleGPT indicates the disk uses GUID Partition Table (GPT) format
PartitionStyleGPT = 2

// GPTPartitionTypeBasicData is the GUID for basic data partitions in GPT
// Used for general purpose storage partitions
GPTPartitionTypeBasicData = "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
// GPTPartitionTypeMicrosoftReserved is the GUID for Microsoft Reserved Partition (MSR)
// Reserved by Windows for system use
GPTPartitionTypeMicrosoftReserved = "{e3c9e316-0b5c-4db8-817d-f92df00215ae}"
)

// QueryDiskByNumber retrieves disk information for a specific disk identified by its number.
func QueryDiskByNumber(diskNumber uint32, selectorList []string) (*storage.MSFT_Disk, error) {
diskQuery := query.NewWmiQueryWithSelectList("MSFT_Disk", selectorList, "Number", strconv.Itoa(int(diskNumber)))
instances, err := QueryInstances(WMINamespaceStorage, diskQuery)
if err != nil {
return nil, err
}

disk, err := storage.NewMSFT_DiskEx1(instances[0])
if err != nil {
return nil, fmt.Errorf("failed to query disk %d. error: %v", diskNumber, err)
}

return disk, nil
}

// ListDisks retrieves information about all available disks.
func ListDisks(selectorList []string) ([]*storage.MSFT_Disk, error) {
diskQuery := query.NewWmiQueryWithSelectList("MSFT_Disk", selectorList)
instances, err := QueryInstances(WMINamespaceStorage, diskQuery)
if IgnoreNotFound(err) != nil {
return nil, err
}

var disks []*storage.MSFT_Disk
for _, instance := range instances {
disk, err := storage.NewMSFT_DiskEx1(instance)
if err != nil {
return nil, fmt.Errorf("failed to query disk %v. error: %v", instance, err)
}

disks = append(disks, disk)
}

return disks, nil
}
271 changes: 271 additions & 0 deletions pkg/cim/iscsi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
package cim

import (
"fmt"
"strconv"

"github.com/microsoft/wmi/pkg/base/query"
cim "github.com/microsoft/wmi/pkg/wmiinstance"
"github.com/microsoft/wmi/server2019/root/microsoft/windows/storage"
)

// ListISCSITargetPortals retrieves a list of iSCSI target portals.
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.
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.
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)
}

var (
// Indexes iSCSI targets by their Object ID specified in node address
mappingISCSITargetIndexer = mappingObjectRefIndexer("iSCSITarget", "MSFT_iSCSITarget", "NodeAddress")
// Indexes iSCSI target portals by their Object ID specified in portal address
mappingISCSITargetPortalIndexer = mappingObjectRefIndexer("iSCSITargetPortal", "MSFT_iSCSITargetPortal", "TargetPortalAddress")
// Indexes iSCSI connections by their Object ID specified in connection identifier
mappingISCSIConnectionIndexer = mappingObjectRefIndexer("iSCSIConnection", "MSFT_iSCSIConnection", "ConnectionIdentifier")
// Indexes iSCSI sessions by their Object ID specified in session identifier
mappingISCSISessionIndexer = mappingObjectRefIndexer("iSCSISession", "MSFT_iSCSISession", "SessionIdentifier")

// Indexes iSCSI targets by their node address
iscsiTargetIndexer = stringPropertyIndexer("NodeAddress")
// Indexes iSCSI targets by their target portal address
iscsiTargetPortalIndexer = stringPropertyIndexer("TargetPortalAddress")
// Indexes iSCSI connections by their connection identifier
iscsiConnectionIndexer = stringPropertyIndexer("ConnectionIdentifier")
// Indexes iSCSI sessions by their session identifier
iscsiSessionIndexer = stringPropertyIndexer("SessionIdentifier")
)

// ListISCSITargetToISCSITargetPortalMapping builds a mapping between iSCSI target and iSCSI target portal with iSCSI target as the key.
func ListISCSITargetToISCSITargetPortalMapping() (map[string]string, error) {
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSITargetToiSCSITargetPortal", nil, mappingISCSITargetIndexer, mappingISCSITargetPortalIndexer)
}

// ListISCSIConnectionToISCSITargetMapping builds a mapping between iSCSI connection and iSCSI target with iSCSI connection as the key.
func ListISCSIConnectionToISCSITargetMapping() (map[string]string, error) {
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSITargetToiSCSIConnection", nil, mappingISCSIConnectionIndexer, mappingISCSITargetIndexer)
}

// ListISCSISessionToISCSITargetMapping builds a mapping between iSCSI session and iSCSI target with iSCSI session as the key.
func ListISCSISessionToISCSITargetMapping() (map[string]string, error) {
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSITargetToiSCSISession", nil, mappingISCSISessionIndexer, mappingISCSITargetIndexer)
}

// ListDiskToISCSIConnectionMapping builds a mapping between disk and iSCSI connection with disk Object ID as the key.
func ListDiskToISCSIConnectionMapping() (map[string]string, error) {
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSIConnectionToDisk", nil, mappingObjectRefIndexer("Disk", "MSFT_Disk", "ObjectId"), mappingISCSIConnectionIndexer)
}

// ListISCSITargetsByTargetPortalWithFilters retrieves all iSCSI targets from the specified iSCSI target portal and conditions by query filters.
func ListISCSITargetsByTargetPortalWithFilters(targetSelectorList []string, portals []*storage.MSFT_iSCSITargetPortal, filters ...*query.WmiQueryFilter) ([]*storage.MSFT_iSCSITarget, error) {
targetQuery := query.NewWmiQueryWithSelectList("MSFT_iSCSITarget", targetSelectorList)
targetQuery.Filters = append(targetQuery.Filters, filters...)
instances, err := QueryInstances(WMINamespaceStorage, targetQuery)
if err != nil {
return nil, err
}

var portalInstances []*cim.WmiInstance
for _, portal := range portals {
portalInstances = append(portalInstances, portal.WmiInstance)
}

targetToTargetPortalMapping, err := ListISCSITargetToISCSITargetPortalMapping()
if err != nil {
return nil, err
}

targetInstances, err := FindInstancesByMapping(instances, iscsiTargetIndexer, portalInstances, iscsiTargetPortalIndexer, targetToTargetPortalMapping)
if err != nil {
return nil, err
}

var targets []*storage.MSFT_iSCSITarget
for _, instance := range targetInstances {
target, err := storage.NewMSFT_iSCSITargetEx1(instance)
if err != nil {
return nil, fmt.Errorf("failed to query iSCSI target %v. %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, selectorList []string) (*storage.MSFT_iSCSITarget, error) {
portal, err := QueryISCSITargetPortal(address, port, nil)
if err != nil {
return nil, err
}

targets, err := ListISCSITargetsByTargetPortalWithFilters(selectorList, []*storage.MSFT_iSCSITargetPortal{portal},
query.NewWmiQueryFilter("NodeAddress", nodeAddress, query.Equals))
if err != nil {
return nil, err
}

return targets[0], nil
}

// QueryISCSISessionByTarget retrieves the iSCSI session from the specified iSCSI target.
func QueryISCSISessionByTarget(target *storage.MSFT_iSCSITarget, selectorList []string) (*storage.MSFT_iSCSISession, error) {
sessionQuery := query.NewWmiQueryWithSelectList("MSFT_iSCSISession", selectorList)
sessionInstances, err := QueryInstances(WMINamespaceStorage, sessionQuery)
if err != nil {
return nil, err
}

targetToTargetSessionMapping, err := ListISCSISessionToISCSITargetMapping()
if err != nil {
return nil, err
}

filtered, err := FindInstancesByMapping(sessionInstances, iscsiSessionIndexer, []*cim.WmiInstance{target.WmiInstance}, iscsiTargetIndexer, targetToTargetSessionMapping)
if err != nil {
return nil, err
}

session, err := storage.NewMSFT_iSCSISessionEx1(filtered[0])
return session, err
}

// ListDisksByTarget lists all the disks on the specified iSCSI target.
func ListDisksByTarget(target *storage.MSFT_iSCSITarget, selectorList []string) ([]*storage.MSFT_Disk, error) {
// list connections to the given iSCSI target
connectionQuery := query.NewWmiQueryWithSelectList("MSFT_iSCSIConnection", selectorList)
connectionInstances, err := QueryInstances(WMINamespaceStorage, connectionQuery)
if err != nil {
return nil, err
}

connectionToTargetMapping, err := ListISCSIConnectionToISCSITargetMapping()
if err != nil {
return nil, err
}

connectionsToTarget, err := FindInstancesByMapping(connectionInstances, iscsiConnectionIndexer, []*cim.WmiInstance{target.WmiInstance}, iscsiTargetIndexer, connectionToTargetMapping)
if err != nil {
return nil, err
}

disks, err := ListDisks(selectorList)
if err != nil {
return nil, err
}

var diskInstances []*cim.WmiInstance
for _, disk := range disks {
diskInstances = append(diskInstances, disk.WmiInstance)
}

diskToConnectionMapping, err := ListDiskToISCSIConnectionMapping()
if err != nil {
return nil, err
}

filtered, err := FindInstancesByMapping(diskInstances, objectIDPropertyIndexer, connectionsToTarget, iscsiConnectionIndexer, diskToConnectionMapping)
if err != nil {
return nil, err
}

var filteredDisks []*storage.MSFT_Disk
for _, instance := range filtered {
disk, err := storage.NewMSFT_DiskEx1(instance)
if err != nil {
return nil, fmt.Errorf("failed to query disk %v. error: %v", disk, err)
}

filteredDisks = append(filteredDisks, disk)
}
return filteredDisks, err
}

// ConnectISCSITarget establishes a connection to an iSCSI target with optional CHAP authentication credential.
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
}
Loading

0 comments on commit 7aafa45

Please sign in to comment.