Skip to content

Commit fb4d87d

Browse files
committed
Use WMI to implement Volume API for better performance
1 parent ab1d3ab commit fb4d87d

File tree

8 files changed

+1185
-165
lines changed

8 files changed

+1185
-165
lines changed

Diff for: go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ toolchain go1.22.3
99

1010
require (
1111
github.com/Microsoft/go-winio v0.6.2
12+
github.com/go-ole/go-ole v1.3.0
1213
github.com/google/go-cmp v0.6.0
1314
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
1415
github.com/iancoleman/strcase v0.3.0
1516
github.com/kubernetes-csi/csi-proxy/client v1.1.3
17+
github.com/microsoft/wmi v0.25.1
1618
github.com/pkg/errors v0.9.1
1719
github.com/prometheus/client_golang v1.20.5
1820
github.com/sergi/go-diff v1.3.1

Diff for: go.sum

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
2525
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
2626
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
2727
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
28+
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
29+
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
2830
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
2931
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
3032
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -69,6 +71,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
6971
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
7072
github.com/mauriciopoppe/gengo v0.0.0-20210525224835-9c78f58f3486 h1:+l047vEi0SyAzdVToIaAcfoY5DwwGW+OyqTdH/P3TTg=
7173
github.com/mauriciopoppe/gengo v0.0.0-20210525224835-9c78f58f3486/go.mod h1:xXv3T4UXTLta31wMhVezwVkc26OLei4hMbLeBJbPmxc=
74+
github.com/microsoft/wmi v0.25.1 h1:sQv9hCEHtW5K6yEVL78T6XGRMGxk4aTpcJwCiB5rLN0=
75+
github.com/microsoft/wmi v0.25.1/go.mod h1:1zbdSF0A+5OwTUII5p3hN7/K6KF2m3o27pSG6Y51VU8=
7276
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
7377
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
7478
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -134,6 +138,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
134138
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
135139
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
136140
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
141+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
137142
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
138143
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
139144
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

Diff for: pkg/cim/disk.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package cim
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
7+
"github.com/microsoft/wmi/pkg/base/query"
8+
"github.com/microsoft/wmi/server2019/root/microsoft/windows/storage"
9+
)
10+
11+
const (
12+
// PartitionStyleUnknown indicates an unknown partition table format
13+
PartitionStyleUnknown = 0
14+
// PartitionStyleGPT indicates the disk uses GUID Partition Table (GPT) format
15+
PartitionStyleGPT = 2
16+
17+
// GPTPartitionTypeBasicData is the GUID for basic data partitions in GPT
18+
// Used for general purpose storage partitions
19+
GPTPartitionTypeBasicData = "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
20+
// GPTPartitionTypeMicrosoftReserved is the GUID for Microsoft Reserved Partition (MSR)
21+
// Reserved by Windows for system use
22+
GPTPartitionTypeMicrosoftReserved = "{e3c9e316-0b5c-4db8-817d-f92df00215ae}"
23+
)
24+
25+
// QueryDiskByNumber retrieves disk information for a specific disk identified by its number.
26+
func QueryDiskByNumber(diskNumber uint32, selectorList []string) (*storage.MSFT_Disk, error) {
27+
diskQuery := query.NewWmiQueryWithSelectList("MSFT_Disk", selectorList, "Number", strconv.Itoa(int(diskNumber)))
28+
instances, err := QueryInstances(WMINamespaceStorage, diskQuery)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
disk, err := storage.NewMSFT_DiskEx1(instances[0])
34+
if err != nil {
35+
return nil, fmt.Errorf("failed to query disk %d. error: %v", diskNumber, err)
36+
}
37+
38+
return disk, nil
39+
}
40+
41+
// ListDisks retrieves information about all available disks.
42+
func ListDisks(selectorList []string) ([]*storage.MSFT_Disk, error) {
43+
diskQuery := query.NewWmiQueryWithSelectList("MSFT_Disk", selectorList)
44+
instances, err := QueryInstances(WMINamespaceStorage, diskQuery)
45+
if IgnoreNotFound(err) != nil {
46+
return nil, err
47+
}
48+
49+
var disks []*storage.MSFT_Disk
50+
for _, instance := range instances {
51+
disk, err := storage.NewMSFT_DiskEx1(instance)
52+
if err != nil {
53+
return nil, fmt.Errorf("failed to query disk %v. error: %v", instance, err)
54+
}
55+
56+
disks = append(disks, disk)
57+
}
58+
59+
return disks, nil
60+
}

Diff for: pkg/cim/iscsi.go

+271
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package cim
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
7+
"github.com/microsoft/wmi/pkg/base/query"
8+
cim "github.com/microsoft/wmi/pkg/wmiinstance"
9+
"github.com/microsoft/wmi/server2019/root/microsoft/windows/storage"
10+
)
11+
12+
// ListISCSITargetPortals retrieves a list of iSCSI target portals.
13+
func ListISCSITargetPortals(selectorList []string) ([]*storage.MSFT_iSCSITargetPortal, error) {
14+
q := query.NewWmiQueryWithSelectList("MSFT_IscsiTargetPortal", selectorList)
15+
instances, err := QueryInstances(WMINamespaceStorage, q)
16+
if IgnoreNotFound(err) != nil {
17+
return nil, err
18+
}
19+
20+
var targetPortals []*storage.MSFT_iSCSITargetPortal
21+
for _, instance := range instances {
22+
portal, err := storage.NewMSFT_iSCSITargetPortalEx1(instance)
23+
if err != nil {
24+
return nil, fmt.Errorf("failed to query iSCSI target portal %v. error: %v", instance, err)
25+
}
26+
27+
targetPortals = append(targetPortals, portal)
28+
}
29+
30+
return targetPortals, nil
31+
}
32+
33+
// QueryISCSITargetPortal retrieves information about a specific iSCSI target portal
34+
// identified by its network address and port number.
35+
func QueryISCSITargetPortal(address string, port uint32, selectorList []string) (*storage.MSFT_iSCSITargetPortal, error) {
36+
portalQuery := query.NewWmiQueryWithSelectList(
37+
"MSFT_iSCSITargetPortal", selectorList,
38+
"TargetPortalAddress", address,
39+
"TargetPortalPortNumber", strconv.Itoa(int(port)))
40+
instances, err := QueryInstances(WMINamespaceStorage, portalQuery)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
targetPortal, err := storage.NewMSFT_iSCSITargetPortalEx1(instances[0])
46+
if err != nil {
47+
return nil, fmt.Errorf("failed to query iSCSI target portal at (%s:%d). error: %v", address, port, err)
48+
}
49+
50+
return targetPortal, nil
51+
}
52+
53+
// NewISCSITargetPortal creates a new iSCSI target portal.
54+
func NewISCSITargetPortal(targetPortalAddress string,
55+
targetPortalPortNumber uint32,
56+
initiatorInstanceName *string,
57+
initiatorPortalAddress *string,
58+
isHeaderDigest *bool,
59+
isDataDigest *bool) (*storage.MSFT_iSCSITargetPortal, error) {
60+
params := map[string]interface{}{
61+
"TargetPortalAddress": targetPortalAddress,
62+
"TargetPortalPortNumber": targetPortalPortNumber,
63+
}
64+
if initiatorInstanceName != nil {
65+
params["InitiatorInstanceName"] = *initiatorInstanceName
66+
}
67+
if initiatorPortalAddress != nil {
68+
params["InitiatorPortalAddress"] = *initiatorPortalAddress
69+
}
70+
if isHeaderDigest != nil {
71+
params["IsHeaderDigest"] = *isHeaderDigest
72+
}
73+
if isDataDigest != nil {
74+
params["IsDataDigest"] = *isDataDigest
75+
}
76+
result, _, err := InvokeCimMethod(WMINamespaceStorage, "MSFT_iSCSITargetPortal", "New", params)
77+
if err != nil {
78+
return nil, fmt.Errorf("failed to create iSCSI target portal with %v. result: %d, error: %v", params, result, err)
79+
}
80+
81+
return QueryISCSITargetPortal(targetPortalAddress, targetPortalPortNumber, nil)
82+
}
83+
84+
var (
85+
// Indexes iSCSI targets by their Object ID specified in node address
86+
mappingISCSITargetIndexer = mappingObjectRefIndexer("iSCSITarget", "MSFT_iSCSITarget", "NodeAddress")
87+
// Indexes iSCSI target portals by their Object ID specified in portal address
88+
mappingISCSITargetPortalIndexer = mappingObjectRefIndexer("iSCSITargetPortal", "MSFT_iSCSITargetPortal", "TargetPortalAddress")
89+
// Indexes iSCSI connections by their Object ID specified in connection identifier
90+
mappingISCSIConnectionIndexer = mappingObjectRefIndexer("iSCSIConnection", "MSFT_iSCSIConnection", "ConnectionIdentifier")
91+
// Indexes iSCSI sessions by their Object ID specified in session identifier
92+
mappingISCSISessionIndexer = mappingObjectRefIndexer("iSCSISession", "MSFT_iSCSISession", "SessionIdentifier")
93+
94+
// Indexes iSCSI targets by their node address
95+
iscsiTargetIndexer = stringPropertyIndexer("NodeAddress")
96+
// Indexes iSCSI targets by their target portal address
97+
iscsiTargetPortalIndexer = stringPropertyIndexer("TargetPortalAddress")
98+
// Indexes iSCSI connections by their connection identifier
99+
iscsiConnectionIndexer = stringPropertyIndexer("ConnectionIdentifier")
100+
// Indexes iSCSI sessions by their session identifier
101+
iscsiSessionIndexer = stringPropertyIndexer("SessionIdentifier")
102+
)
103+
104+
// ListISCSITargetToISCSITargetPortalMapping builds a mapping between iSCSI target and iSCSI target portal with iSCSI target as the key.
105+
func ListISCSITargetToISCSITargetPortalMapping() (map[string]string, error) {
106+
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSITargetToiSCSITargetPortal", nil, mappingISCSITargetIndexer, mappingISCSITargetPortalIndexer)
107+
}
108+
109+
// ListISCSIConnectionToISCSITargetMapping builds a mapping between iSCSI connection and iSCSI target with iSCSI connection as the key.
110+
func ListISCSIConnectionToISCSITargetMapping() (map[string]string, error) {
111+
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSITargetToiSCSIConnection", nil, mappingISCSIConnectionIndexer, mappingISCSITargetIndexer)
112+
}
113+
114+
// ListISCSISessionToISCSITargetMapping builds a mapping between iSCSI session and iSCSI target with iSCSI session as the key.
115+
func ListISCSISessionToISCSITargetMapping() (map[string]string, error) {
116+
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSITargetToiSCSISession", nil, mappingISCSISessionIndexer, mappingISCSITargetIndexer)
117+
}
118+
119+
// ListDiskToISCSIConnectionMapping builds a mapping between disk and iSCSI connection with disk Object ID as the key.
120+
func ListDiskToISCSIConnectionMapping() (map[string]string, error) {
121+
return ListWMIInstanceMappings(WMINamespaceStorage, "MSFT_iSCSIConnectionToDisk", nil, mappingObjectRefIndexer("Disk", "MSFT_Disk", "ObjectId"), mappingISCSIConnectionIndexer)
122+
}
123+
124+
// ListISCSITargetsByTargetPortalWithFilters retrieves all iSCSI targets from the specified iSCSI target portal and conditions by query filters.
125+
func ListISCSITargetsByTargetPortalWithFilters(targetSelectorList []string, portals []*storage.MSFT_iSCSITargetPortal, filters ...*query.WmiQueryFilter) ([]*storage.MSFT_iSCSITarget, error) {
126+
targetQuery := query.NewWmiQueryWithSelectList("MSFT_iSCSITarget", targetSelectorList)
127+
targetQuery.Filters = append(targetQuery.Filters, filters...)
128+
instances, err := QueryInstances(WMINamespaceStorage, targetQuery)
129+
if err != nil {
130+
return nil, err
131+
}
132+
133+
var portalInstances []*cim.WmiInstance
134+
for _, portal := range portals {
135+
portalInstances = append(portalInstances, portal.WmiInstance)
136+
}
137+
138+
targetToTargetPortalMapping, err := ListISCSITargetToISCSITargetPortalMapping()
139+
if err != nil {
140+
return nil, err
141+
}
142+
143+
targetInstances, err := FindInstancesByMapping(instances, iscsiTargetIndexer, portalInstances, iscsiTargetPortalIndexer, targetToTargetPortalMapping)
144+
if err != nil {
145+
return nil, err
146+
}
147+
148+
var targets []*storage.MSFT_iSCSITarget
149+
for _, instance := range targetInstances {
150+
target, err := storage.NewMSFT_iSCSITargetEx1(instance)
151+
if err != nil {
152+
return nil, fmt.Errorf("failed to query iSCSI target %v. %v", instance, err)
153+
}
154+
155+
targets = append(targets, target)
156+
}
157+
158+
return targets, nil
159+
}
160+
161+
// QueryISCSITarget retrieves the iSCSI target from the specified portal address, portal and node address.
162+
func QueryISCSITarget(address string, port uint32, nodeAddress string, selectorList []string) (*storage.MSFT_iSCSITarget, error) {
163+
portal, err := QueryISCSITargetPortal(address, port, nil)
164+
if err != nil {
165+
return nil, err
166+
}
167+
168+
targets, err := ListISCSITargetsByTargetPortalWithFilters(selectorList, []*storage.MSFT_iSCSITargetPortal{portal},
169+
query.NewWmiQueryFilter("NodeAddress", nodeAddress, query.Equals))
170+
if err != nil {
171+
return nil, err
172+
}
173+
174+
return targets[0], nil
175+
}
176+
177+
// QueryISCSISessionByTarget retrieves the iSCSI session from the specified iSCSI target.
178+
func QueryISCSISessionByTarget(target *storage.MSFT_iSCSITarget, selectorList []string) (*storage.MSFT_iSCSISession, error) {
179+
sessionQuery := query.NewWmiQueryWithSelectList("MSFT_iSCSISession", selectorList)
180+
sessionInstances, err := QueryInstances(WMINamespaceStorage, sessionQuery)
181+
if err != nil {
182+
return nil, err
183+
}
184+
185+
targetToTargetSessionMapping, err := ListISCSISessionToISCSITargetMapping()
186+
if err != nil {
187+
return nil, err
188+
}
189+
190+
filtered, err := FindInstancesByMapping(sessionInstances, iscsiSessionIndexer, []*cim.WmiInstance{target.WmiInstance}, iscsiTargetIndexer, targetToTargetSessionMapping)
191+
if err != nil {
192+
return nil, err
193+
}
194+
195+
session, err := storage.NewMSFT_iSCSISessionEx1(filtered[0])
196+
return session, err
197+
}
198+
199+
// ListDisksByTarget lists all the disks on the specified iSCSI target.
200+
func ListDisksByTarget(target *storage.MSFT_iSCSITarget, selectorList []string) ([]*storage.MSFT_Disk, error) {
201+
// list connections to the given iSCSI target
202+
connectionQuery := query.NewWmiQueryWithSelectList("MSFT_iSCSIConnection", selectorList)
203+
connectionInstances, err := QueryInstances(WMINamespaceStorage, connectionQuery)
204+
if err != nil {
205+
return nil, err
206+
}
207+
208+
connectionToTargetMapping, err := ListISCSIConnectionToISCSITargetMapping()
209+
if err != nil {
210+
return nil, err
211+
}
212+
213+
connectionsToTarget, err := FindInstancesByMapping(connectionInstances, iscsiConnectionIndexer, []*cim.WmiInstance{target.WmiInstance}, iscsiTargetIndexer, connectionToTargetMapping)
214+
if err != nil {
215+
return nil, err
216+
}
217+
218+
disks, err := ListDisks(selectorList)
219+
if err != nil {
220+
return nil, err
221+
}
222+
223+
var diskInstances []*cim.WmiInstance
224+
for _, disk := range disks {
225+
diskInstances = append(diskInstances, disk.WmiInstance)
226+
}
227+
228+
diskToConnectionMapping, err := ListDiskToISCSIConnectionMapping()
229+
if err != nil {
230+
return nil, err
231+
}
232+
233+
filtered, err := FindInstancesByMapping(diskInstances, objectIDPropertyIndexer, connectionsToTarget, iscsiConnectionIndexer, diskToConnectionMapping)
234+
if err != nil {
235+
return nil, err
236+
}
237+
238+
var filteredDisks []*storage.MSFT_Disk
239+
for _, instance := range filtered {
240+
disk, err := storage.NewMSFT_DiskEx1(instance)
241+
if err != nil {
242+
return nil, fmt.Errorf("failed to query disk %v. error: %v", disk, err)
243+
}
244+
245+
filteredDisks = append(filteredDisks, disk)
246+
}
247+
return filteredDisks, err
248+
}
249+
250+
// ConnectISCSITarget establishes a connection to an iSCSI target with optional CHAP authentication credential.
251+
func ConnectISCSITarget(portalAddress string, portalPortNumber uint32, nodeAddress string, authType string, chapUsername *string, chapSecret *string) (int, map[string]interface{}, error) {
252+
inParams := map[string]interface{}{
253+
"NodeAddress": nodeAddress,
254+
"TargetPortalAddress": portalAddress,
255+
"TargetPortalPortNumber": int(portalPortNumber),
256+
"AuthenticationType": authType,
257+
}
258+
// InitiatorPortalAddress
259+
// IsDataDigest
260+
// IsHeaderDigest
261+
// ReportToPnP
262+
if chapUsername != nil {
263+
inParams["ChapUsername"] = *chapUsername
264+
}
265+
if chapSecret != nil {
266+
inParams["ChapSecret"] = *chapSecret
267+
}
268+
269+
result, outParams, err := InvokeCimMethod(WMINamespaceStorage, "MSFT_iSCSITarget", "Connect", inParams)
270+
return result, outParams, err
271+
}

0 commit comments

Comments
 (0)