From 45d3eabab9312581e89eb812a276e1459903fc91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sun, 3 Nov 2024 11:20:46 +0100 Subject: [PATCH] mi: Revert "replace all WMI calls with MI calls" (#1713) --- .golangci.yaml | 2 +- go.mod | 17 +- go.sum | 36 ++- internal/collector/ad/ad.go | 4 +- internal/collector/adcs/adcs.go | 4 +- internal/collector/adfs/adfs.go | 4 +- internal/collector/cache/cache.go | 4 +- internal/collector/container/container.go | 4 +- internal/collector/cpu/cpu.go | 4 +- internal/collector/cpu_info/cpu_info.go | 63 ++-- internal/collector/cs/cs.go | 4 +- internal/collector/dfsr/dfsr.go | 4 +- internal/collector/dhcp/dhcp.go | 4 +- internal/collector/diskdrive/diskdrive.go | 47 ++- internal/collector/dns/dns.go | 4 +- internal/collector/exchange/exchange.go | 4 +- internal/collector/filetime/filetime.go | 4 +- internal/collector/fsrmquota/fsrmquota.go | 51 ++- internal/collector/hyperv/hyperv.go | 267 ++++++++-------- internal/collector/iis/iis.go | 4 +- internal/collector/license/license.go | 4 +- .../collector/logical_disk/logical_disk.go | 4 +- internal/collector/logon/logon.go | 4 +- internal/collector/memory/memory.go | 4 +- internal/collector/mscluster/mscluster.go | 12 +- .../collector/mscluster/mscluster_cluster.go | 166 +++++----- .../collector/mscluster/mscluster_network.go | 20 +- .../collector/mscluster/mscluster_node.go | 40 ++- .../collector/mscluster/mscluster_resource.go | 48 ++- .../mscluster/mscluster_resourcegroup.go | 40 ++- internal/collector/msmq/msmq.go | 32 +- internal/collector/mssql/mssql.go | 4 +- internal/collector/net/net.go | 4 +- .../collector/netframework/netframework.go | 12 +- .../netframework_clrexceptions.go | 20 +- .../netframework/netframework_clrinterop.go | 20 +- .../netframework/netframework_clrjit.go | 24 +- .../netframework/netframework_clrloading.go | 44 ++- .../netframework_clrlocksandthreads.go | 32 +- .../netframework/netframework_clrmemory.go | 60 ++-- .../netframework/netframework_clrremoting.go | 24 +- .../netframework/netframework_clrsecurity.go | 22 +- internal/collector/nps/nps.go | 95 +++--- internal/collector/os/os.go | 10 +- internal/collector/perfdata/perfdata.go | 4 +- .../collector/physical_disk/physical_disk.go | 4 +- internal/collector/printer/printer.go | 68 ++-- internal/collector/process/process.go | 36 +-- internal/collector/remote_fx/remote_fx.go | 4 +- .../scheduled_task/scheduled_task.go | 6 +- internal/collector/service/service.go | 4 +- internal/collector/smb/smb.go | 4 +- internal/collector/smbclient/smbclient.go | 4 +- internal/collector/smtp/smtp.go | 10 +- internal/collector/system/system.go | 4 +- internal/collector/tcp/tcp.go | 4 +- .../terminal_services/terminal_services.go | 15 +- internal/collector/textfile/textfile.go | 4 +- internal/collector/thermalzone/thermalzone.go | 37 +-- internal/collector/time/time.go | 4 +- internal/collector/update/update.go | 6 +- internal/collector/vmware/vmware.go | 77 ++--- internal/httphandler/httphandler.go | 2 +- internal/mi/application.go | 283 ----------------- internal/mi/callbacks.go | 143 --------- internal/mi/doc.go | 7 - internal/mi/errors.go | 10 - internal/mi/instance.go | 179 ----------- internal/mi/mi_bench_test.go | 43 --- internal/mi/mi_test.go | 294 ------------------ internal/mi/operation.go | 270 ---------------- internal/mi/result.go | 102 ------ internal/mi/session.go | 244 --------------- internal/mi/types.go | 137 -------- internal/mi/value.go | 112 ------- internal/testutils/handle.go | 28 -- internal/testutils/testutils.go | 14 +- internal/utils/collector.go | 8 - internal/utils/utils.go | 11 - main.go | 1 - pkg/collector/collector.go | 52 +--- pkg/collector/types.go | 6 +- 82 files changed, 746 insertions(+), 2775 deletions(-) delete mode 100644 internal/mi/application.go delete mode 100644 internal/mi/callbacks.go delete mode 100644 internal/mi/doc.go delete mode 100644 internal/mi/errors.go delete mode 100644 internal/mi/instance.go delete mode 100644 internal/mi/mi_bench_test.go delete mode 100644 internal/mi/mi_test.go delete mode 100644 internal/mi/operation.go delete mode 100644 internal/mi/result.go delete mode 100644 internal/mi/session.go delete mode 100644 internal/mi/types.go delete mode 100644 internal/mi/value.go delete mode 100644 internal/testutils/handle.go delete mode 100644 main.go diff --git a/.golangci.yaml b/.golangci.yaml index 054340773..49c1839b3 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -51,7 +51,7 @@ linters-settings: forbidigo: forbid: - "^(fmt\\.Print(|f|ln)|print|println)$" - - p: "^syscall\\.(.{1,7}|.{7}[^N]|.{9,})$" + - p: "^syscall\\..*$" msg: use golang.org/x/sys/windows instead of syscall - p: "^windows\\.NewLazyDLL$" msg: use NewLazySystemDLL instead NewLazyDLL diff --git a/go.mod b/go.mod index 9a0c33cb4..ebd3ec587 100644 --- a/go.mod +++ b/go.mod @@ -14,13 +14,14 @@ require ( github.com/prometheus/common v0.60.1 github.com/prometheus/exporter-toolkit v0.13.0 github.com/stretchr/testify v1.9.0 + github.com/yusufpapurcu/wmi v1.2.4 golang.org/x/sys v0.26.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect + github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/cgroups/v3 v3.0.3 // indirect @@ -30,7 +31,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/jpillora/backoff v1.0.0 // indirect - github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/compress v1.17.10 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mdlayher/vsock v1.2.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -41,13 +42,13 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.19.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect - google.golang.org/grpc v1.67.1 // indirect - google.golang.org/protobuf v1.35.1 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect + google.golang.org/grpc v1.67.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index dc625f8be..6aa52dfa6 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/Microsoft/hcsshim v0.12.8 h1:BtDWYlFMcWhorrvSSo2M7z0csPdw6t7no/C3FsSv github.com/Microsoft/hcsshim v0.12.8/go.mod h1:cibQ4BqhJ32FXDwPdQhKhwrwophnh3FuT4nwQZF907w= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= -github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= @@ -34,6 +34,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -64,8 +65,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= +github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -112,12 +113,14 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -128,8 +131,8 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -141,6 +144,7 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -148,8 +152,8 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -161,15 +165,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -179,8 +183,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/collector/ad/ad.go b/internal/collector/ad/ad.go index 12643f78c..09b720582 100644 --- a/internal/collector/ad/ad.go +++ b/internal/collector/ad/ad.go @@ -8,10 +8,10 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "ad" @@ -118,7 +118,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { counters := []string{ abANRPerSec, abBrowsesPerSec, diff --git a/internal/collector/adcs/adcs.go b/internal/collector/adcs/adcs.go index de465f0b4..d484793a5 100644 --- a/internal/collector/adcs/adcs.go +++ b/internal/collector/adcs/adcs.go @@ -8,12 +8,12 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "adcs" @@ -74,7 +74,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ requestsPerSecond, diff --git a/internal/collector/adfs/adfs.go b/internal/collector/adfs/adfs.go index 99e70dc7d..a481ead24 100644 --- a/internal/collector/adfs/adfs.go +++ b/internal/collector/adfs/adfs.go @@ -11,12 +11,12 @@ import ( "slices" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "adfs" @@ -107,7 +107,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ adLoginConnectionFailures, diff --git a/internal/collector/cache/cache.go b/internal/collector/cache/cache.go index ecd863c7a..051c69462 100644 --- a/internal/collector/cache/cache.go +++ b/internal/collector/cache/cache.go @@ -8,13 +8,13 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "cache" @@ -92,7 +92,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ asyncCopyReadsTotal, diff --git a/internal/collector/container/container.go b/internal/collector/container/container.go index d32cc8259..b6b536509 100644 --- a/internal/collector/container/container.go +++ b/internal/collector/container/container.go @@ -10,10 +10,10 @@ import ( "github.com/Microsoft/hcsshim" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "container" @@ -86,7 +86,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { c.containerAvailable = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "available"), "Available", diff --git a/internal/collector/cpu/cpu.go b/internal/collector/cpu/cpu.go index e881516e8..e48ae8fd2 100644 --- a/internal/collector/cpu/cpu.go +++ b/internal/collector/cpu/cpu.go @@ -8,12 +8,12 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "cpu" @@ -83,7 +83,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ c1TimeSeconds, diff --git a/internal/collector/cpu_info/cpu_info.go b/internal/collector/cpu_info/cpu_info.go index 6c576b52a..f5335ac78 100644 --- a/internal/collector/cpu_info/cpu_info.go +++ b/internal/collector/cpu_info/cpu_info.go @@ -4,15 +4,14 @@ package cpu_info import ( "errors" - "fmt" "log/slog" "strconv" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "cpu_info" @@ -23,9 +22,9 @@ var ConfigDefaults = Config{} // A Collector is a Prometheus Collector for a few WMI metrics in Win32_Processor. type Collector struct { - config Config - miSession *mi.Session - miQuery mi.Query + config Config + + wmiClient *wmi.Client cpuInfo *prometheus.Desc cpuCoreCount *prometheus.Desc @@ -64,19 +63,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") - } - - miQuery, err := mi.NewQuery("SELECT Architecture, DeviceId, Description, Family, L2CacheSize, L3CacheSize, Name, ThreadCount, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors FROM Win32_Processor") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.miQuery = miQuery - c.miSession = miSession - + c.wmiClient = wmiClient c.cpuInfo = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, "", Name), "Labelled CPU information as provided by Win32_Processor", @@ -141,20 +133,18 @@ func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { return nil } -type miProcessor struct { - Architecture uint32 `mi:"Architecture"` - DeviceID string `mi:"DeviceID"` - Description string `mi:"Description"` - Family uint16 `mi:"Family"` - L2CacheSize uint32 `mi:"L2CacheSize"` - L3CacheSize uint32 `mi:"L3CacheSize"` - Name string `mi:"Name"` - ThreadCount uint32 `mi:"ThreadCount"` - NumberOfCores uint32 `mi:"NumberOfCores"` - NumberOfEnabledCore uint32 `mi:"NumberOfEnabledCore"` - NumberOfLogicalProcessors uint32 `mi:"NumberOfLogicalProcessors"` - - Total int +type win32Processor struct { + Architecture uint32 + DeviceID string + Description string + Family uint16 + L2CacheSize uint32 + L3CacheSize uint32 + Name string + ThreadCount uint32 + NumberOfCores uint32 + NumberOfEnabledCore uint32 + NumberOfLogicalProcessors uint32 } // Collect sends the metric values for each metric @@ -173,9 +163,16 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan } func (c *Collector) collect(ch chan<- prometheus.Metric) error { - var dst []miProcessor - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQuery); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + var dst []win32Processor + // We use a static query here because the provided methods in wmi.go all issue a SELECT *; + // This results in the time-consuming LoadPercentage field being read which seems to measure each CPU + // serially over a 1 second interval, so the scrape time is at least 1s * num_sockets + if err := c.wmiClient.Query("SELECT Architecture, DeviceId, Description, Family, L2CacheSize, L3CacheSize, Name, ThreadCount, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors FROM Win32_Processor", &dst); err != nil { + return err + } + + if len(dst) == 0 { + return errors.New("WMI query returned empty result set") } // Some CPUs end up exposing trailing spaces for certain strings, so clean them up diff --git a/internal/collector/cs/cs.go b/internal/collector/cs/cs.go index b6bc1ae5d..bee7ab345 100644 --- a/internal/collector/cs/cs.go +++ b/internal/collector/cs/cs.go @@ -7,9 +7,9 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "cs" @@ -61,7 +61,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger.Warn("The cs collector is deprecated and will be removed in a future release. " + "Logical processors has been moved to cpu_info collector. " + "Physical memory has been moved to memory collector. " + diff --git a/internal/collector/dfsr/dfsr.go b/internal/collector/dfsr/dfsr.go index 96522f90e..b9392c56d 100644 --- a/internal/collector/dfsr/dfsr.go +++ b/internal/collector/dfsr/dfsr.go @@ -10,12 +10,12 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "dfsr" @@ -167,7 +167,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger = logger.With(slog.String("collector", Name)) logger.Info("dfsr collector is in an experimental state! Metrics for this collector have not been tested.") diff --git a/internal/collector/dhcp/dhcp.go b/internal/collector/dhcp/dhcp.go index 8af64bc21..7a330873c 100644 --- a/internal/collector/dhcp/dhcp.go +++ b/internal/collector/dhcp/dhcp.go @@ -8,13 +8,13 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "dhcp" @@ -88,7 +88,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ acksTotal, diff --git a/internal/collector/diskdrive/diskdrive.go b/internal/collector/diskdrive/diskdrive.go index 0eec94381..06857e5fb 100644 --- a/internal/collector/diskdrive/diskdrive.go +++ b/internal/collector/diskdrive/diskdrive.go @@ -4,17 +4,19 @@ package diskdrive import ( "errors" - "fmt" "log/slog" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) -const Name = "diskdrive" +const ( + Name = "diskdrive" + win32DiskQuery = "SELECT DeviceID, Model, Caption, Name, Partitions, Size, Status, Availability FROM WIN32_DiskDrive" +) type Config struct{} @@ -23,8 +25,7 @@ var ConfigDefaults = Config{} // A Collector is a Prometheus Collector for a few WMI metrics in Win32_DiskDrive. type Collector struct { config Config - miSession *mi.Session - miQuery mi.Query + wmiClient *wmi.Client availability *prometheus.Desc diskInfo *prometheus.Desc @@ -61,19 +62,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") - } - - miQuery, err := mi.NewQuery("SELECT DeviceID, Model, Caption, Name, Partitions, Size, Status, Availability FROM WIN32_DiskDrive") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.miQuery = miQuery - c.miSession = miSession - + c.wmiClient = wmiClient c.diskInfo = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "info"), "General drive information", @@ -114,14 +108,14 @@ func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { } type win32_DiskDrive struct { - DeviceID string `mi:"DeviceID"` - Model string `mi:"Model"` - Size uint64 `mi:"Size"` - Name string `mi:"Name"` - Caption string `mi:"Caption"` - Partitions uint32 `mi:"Partitions"` - Status string `mi:"Status"` - Availability uint16 `mi:"Availability"` + DeviceID string + Model string + Size uint64 + Name string + Caption string + Partitions uint32 + Status string + Availability uint16 } var ( @@ -181,8 +175,9 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan func (c *Collector) collect(ch chan<- prometheus.Metric) error { var dst []win32_DiskDrive - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQuery); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + + if err := c.wmiClient.Query(win32DiskQuery, &dst); err != nil { + return err } if len(dst) == 0 { diff --git a/internal/collector/dns/dns.go b/internal/collector/dns/dns.go index 9853c8fe5..d83472f96 100644 --- a/internal/collector/dns/dns.go +++ b/internal/collector/dns/dns.go @@ -8,11 +8,11 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "dns" @@ -79,7 +79,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { counters := []string{ axfrRequestReceived, axfrRequestSent, diff --git a/internal/collector/exchange/exchange.go b/internal/collector/exchange/exchange.go index 31582582b..c90769851 100644 --- a/internal/collector/exchange/exchange.go +++ b/internal/collector/exchange/exchange.go @@ -10,11 +10,11 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "exchange" @@ -207,7 +207,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { collectorFuncs := map[string]func() error{ adAccessProcesses: c.buildADAccessProcesses, diff --git a/internal/collector/filetime/filetime.go b/internal/collector/filetime/filetime.go index bb2d00f03..0dd8ccb76 100644 --- a/internal/collector/filetime/filetime.go +++ b/internal/collector/filetime/filetime.go @@ -12,9 +12,9 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/bmatcuk/doublestar/v4" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "filetime" @@ -85,7 +85,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger.Info("filetime collector is in an experimental state! It may subject to change.", slog.String("collector", Name), ) diff --git a/internal/collector/fsrmquota/fsrmquota.go b/internal/collector/fsrmquota/fsrmquota.go index d45fe1b5f..6d2ed3774 100644 --- a/internal/collector/fsrmquota/fsrmquota.go +++ b/internal/collector/fsrmquota/fsrmquota.go @@ -4,14 +4,13 @@ package fsrmquota import ( "errors" - "fmt" "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "fsrmquota" @@ -22,8 +21,7 @@ var ConfigDefaults = Config{} type Collector struct { config Config - miSession *mi.Session - miQuery mi.Query + wmiClient *wmi.Client quotasCount *prometheus.Desc peakUsage *prometheus.Desc @@ -65,18 +63,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - miQuery, err := mi.NewQuery("SELECT * FROM MSFT_FSRMQuota") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - c.miQuery = miQuery - c.miSession = miSession + c.wmiClient = wmiClient c.quotasCount = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "count"), @@ -154,28 +146,29 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan // MSFT_FSRMQuota docs: // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/fsrm/msft-fsrmquota type MSFT_FSRMQuota struct { - Name string `mi:"Name"` - - Path string `mi:"Path"` - PeakUsage uint64 `mi:"PeakUsage"` - Size uint64 `mi:"Size"` - Usage uint64 `mi:"Usage"` - Description string `mi:"Description"` - Template string `mi:"Template"` - // Threshold string `mi:"Threshold"` - Disabled bool `mi:"Disabled"` - MatchesTemplate bool `mi:"MatchesTemplate"` - SoftLimit bool `mi:"SoftLimit"` + Name string + + Path string + PeakUsage uint64 + Size uint64 + Usage uint64 + Description string + Template string + // Threshold string + Disabled bool + MatchesTemplate bool + SoftLimit bool } func (c *Collector) collect(ch chan<- prometheus.Metric) error { var dst []MSFT_FSRMQuota - if err := c.miSession.Query(&dst, mi.NamespaceRootWindowsFSRM, c.miQuery); err != nil { - return fmt.Errorf("WMI query failed: %w", err) - } var count int + if err := c.wmiClient.Query("SELECT * FROM MSFT_FSRMQuota", &dst, nil, "root/microsoft/windows/fsrm"); err != nil { + return err + } + for _, quota := range dst { count++ path := quota.Path diff --git a/internal/collector/hyperv/hyperv.go b/internal/collector/hyperv/hyperv.go index fead371e5..c64f5e3bc 100644 --- a/internal/collector/hyperv/hyperv.go +++ b/internal/collector/hyperv/hyperv.go @@ -9,10 +9,9 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "hyperv" @@ -24,7 +23,7 @@ var ConfigDefaults = Config{} // Collector is a Prometheus Collector for hyper-v. type Collector struct { config Config - miSession *mi.Session + wmiClient *wmi.Client // Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary healthCritical *prometheus.Desc @@ -169,12 +168,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.miSession = miSession + c.wmiClient = wmiClient buildSubsystemName := func(component string) string { return "hyperv_" + component } @@ -859,14 +858,14 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan // Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary vm health status. type Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary struct { - HealthCritical uint32 `mi:"HealthCritical"` - HealthOk uint32 `mi:"HealthOK"` + HealthCritical uint32 + HealthOk uint32 } func (c *Collector) collectVmHealth(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary", &dst); err != nil { + return err } for _, health := range dst { @@ -888,16 +887,16 @@ func (c *Collector) collectVmHealth(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition ..,. type Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition struct { - Name string `mi:"Name"` - PhysicalPagesAllocated uint64 `mi:"PhysicalPagesAllocated"` - PreferredNUMANodeIndex uint64 `mi:"PreferredNUMANodeIndex"` - RemotePhysicalPages uint64 `mi:"RemotePhysicalPages"` + Name string + PhysicalPagesAllocated uint64 + PreferredNUMANodeIndex uint64 + RemotePhysicalPages uint64 } func (c *Collector) collectVmVid(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition", &dst); err != nil { + return err } for _, page := range dst { @@ -932,34 +931,34 @@ func (c *Collector) collectVmVid(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition ... type Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition struct { - Name string `mi:"Name"` - AddressSpaces uint64 `mi:"AddressSpaces"` - AttachedDevices uint64 `mi:"AttachedDevices"` - DepositedPages uint64 `mi:"DepositedPages"` - DeviceDMAErrors uint64 `mi:"DeviceDMAErrors"` - DeviceInterruptErrors uint64 `mi:"DeviceInterruptErrors"` - DeviceInterruptMappings uint64 `mi:"DeviceInterruptMappings"` - DeviceInterruptThrottleEvents uint64 `mi:"DeviceInterruptThrottleEvents"` - GPAPages uint64 `mi:"GPAPages"` - GPASpaceModificationsPersec uint64 `mi:"GPASpaceModificationsPersec"` - IOTLBFlushCost uint64 `mi:"IOTLBFlushCost"` - IOTLBFlushesPersec uint64 `mi:"IOTLBFlushesPersec"` - RecommendedVirtualTLBSize uint64 `mi:"RecommendedVirtualTLBSize"` - SkippedTimerTicks uint64 `mi:"SkippedTimerTicks"` - Value1Gdevicepages uint64 `mi:"Value1Gdevicepages"` - Value1GGPApages uint64 `mi:"Value1GGPApages"` - Value2Mdevicepages uint64 `mi:"Value2Mdevicepages"` - Value2MGPApages uint64 `mi:"Value2MGPApages"` - Value4Kdevicepages uint64 `mi:"Value4Kdevicepages"` - Value4KGPApages uint64 `mi:"Value4KGPApages"` - VirtualTLBFlushEntiresPersec uint64 `mi:"VirtualTLBFlushEntiresPersec"` - VirtualTLBPages uint64 `mi:"VirtualTLBPages"` + Name string + AddressSpaces uint64 + AttachedDevices uint64 + DepositedPages uint64 + DeviceDMAErrors uint64 + DeviceInterruptErrors uint64 + DeviceInterruptMappings uint64 + DeviceInterruptThrottleEvents uint64 + GPAPages uint64 + GPASpaceModificationsPersec uint64 + IOTLBFlushCost uint64 + IOTLBFlushesPersec uint64 + RecommendedVirtualTLBSize uint64 + SkippedTimerTicks uint64 + Value1Gdevicepages uint64 + Value1GGPApages uint64 + Value2Mdevicepages uint64 + Value2MGPApages uint64 + Value4Kdevicepages uint64 + Value4KGPApages uint64 + VirtualTLBFlushEntiresPersec uint64 + VirtualTLBPages uint64 } func (c *Collector) collectVmHv(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition", &dst); err != nil { + return err } for _, obj := range dst { @@ -1088,14 +1087,14 @@ func (c *Collector) collectVmHv(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_HvStats_HyperVHypervisor ... type Win32_PerfRawData_HvStats_HyperVHypervisor struct { - LogicalProcessors uint64 `mi:"LogicalProcessors"` - VirtualProcessors uint64 `mi:"VirtualProcessors"` + LogicalProcessors uint64 + VirtualProcessors uint64 } func (c *Collector) collectVmProcessor(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_HvStats_HyperVHypervisor - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisor"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisor", &dst); err != nil { + return err } for _, obj := range dst { @@ -1117,16 +1116,16 @@ func (c *Collector) collectVmProcessor(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor ... type Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor struct { - Name string `mi:"Name"` - PercentGuestRunTime uint64 `mi:"PercentGuestRunTime"` - PercentHypervisorRunTime uint64 `mi:"PercentHypervisorRunTime"` - PercentTotalRunTime uint64 `mi:"PercentTotalRunTime"` + Name string + PercentGuestRunTime uint64 + PercentHypervisorRunTime uint64 + PercentTotalRunTime uint } func (c *Collector) collectHostLPUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor", &dst); err != nil { + return err } for _, obj := range dst { @@ -1171,18 +1170,18 @@ func (c *Collector) collectHostLPUsage(logger *slog.Logger, ch chan<- prometheus // Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor ... type Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor struct { - Name string `mi:"Name"` - PercentGuestRunTime uint64 `mi:"PercentGuestRunTime"` - PercentHypervisorRunTime uint64 `mi:"PercentHypervisorRunTime"` - PercentRemoteRunTime uint64 `mi:"PercentRemoteRunTime"` - PercentTotalRunTime uint64 `mi:"PercentTotalRunTime"` - CPUWaitTimePerDispatch uint64 `mi:"CPUWaitTimePerDispatch"` + Name string + PercentGuestRunTime uint64 + PercentHypervisorRunTime uint64 + PercentRemoteRunTime uint64 + PercentTotalRunTime uint64 + CPUWaitTimePerDispatch uint64 } func (c *Collector) collectHostCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor", &dst); err != nil { + return err } for _, obj := range dst { @@ -1241,18 +1240,18 @@ func (c *Collector) collectHostCpuUsage(logger *slog.Logger, ch chan<- prometheu // Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor ... type Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor struct { - Name string `mi:"Name"` - PercentGuestRunTime uint64 `mi:"PercentGuestRunTime"` - PercentHypervisorRunTime uint64 `mi:"PercentHypervisorRunTime"` - PercentRemoteRunTime uint64 `mi:"PercentRemoteRunTime"` - PercentTotalRunTime uint64 `mi:"PercentTotalRunTime"` - CPUWaitTimePerDispatch uint64 `mi:"CPUWaitTimePerDispatch"` + Name string + PercentGuestRunTime uint64 + PercentHypervisorRunTime uint64 + PercentRemoteRunTime uint64 + PercentTotalRunTime uint64 + CPUWaitTimePerDispatch uint64 } func (c *Collector) collectVmCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor", &dst); err != nil { + return err } for _, obj := range dst { @@ -1319,37 +1318,37 @@ func (c *Collector) collectVmCpuUsage(logger *slog.Logger, ch chan<- prometheus. // Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch ... type Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch struct { - Name string `mi:"Name"` - BroadcastPacketsReceivedPersec uint64 `mi:"BroadcastPacketsReceivedPersec"` - BroadcastPacketsSentPersec uint64 `mi:"BroadcastPacketsSentPersec"` - BytesPersec uint64 `mi:"BytesPersec"` - BytesReceivedPersec uint64 `mi:"BytesReceivedPersec"` - BytesSentPersec uint64 `mi:"BytesSentPersec"` - DirectedPacketsReceivedPersec uint64 `mi:"DirectedPacketsReceivedPersec"` - DirectedPacketsSentPersec uint64 `mi:"DirectedPacketsSentPersec"` - DroppedPacketsIncomingPersec uint64 `mi:"DroppedPacketsIncomingPersec"` - DroppedPacketsOutgoingPersec uint64 `mi:"DroppedPacketsOutgoingPersec"` - ExtensionsDroppedPacketsIncomingPersec uint64 `mi:"ExtensionsDroppedPacketsIncomingPersec"` - ExtensionsDroppedPacketsOutgoingPersec uint64 `mi:"ExtensionsDroppedPacketsOutgoingPersec"` - LearnedMacAddresses uint64 `mi:"LearnedMacAddresses"` - LearnedMacAddressesPersec uint64 `mi:"LearnedMacAddressesPersec"` - MulticastPacketsReceivedPersec uint64 `mi:"MulticastPacketsReceivedPersec"` - MulticastPacketsSentPersec uint64 `mi:"MulticastPacketsSentPersec"` - NumberofSendChannelMovesPersec uint64 `mi:"NumberofSendChannelMovesPersec"` - NumberofVMQMovesPersec uint64 `mi:"NumberofVMQMovesPersec"` - PacketsFlooded uint64 `mi:"PacketsFlooded"` - PacketsFloodedPersec uint64 `mi:"PacketsFloodedPersec"` - PacketsPersec uint64 `mi:"PacketsPersec"` - PacketsReceivedPersec uint64 `mi:"PacketsReceivedPersec"` - PacketsSentPersec uint64 `mi:"PacketsSentPersec"` - PurgedMacAddresses uint64 `mi:"PurgedMacAddresses"` - PurgedMacAddressesPersec uint64 `mi:"PurgedMacAddressesPersec"` + Name string + BroadcastPacketsReceivedPersec uint64 + BroadcastPacketsSentPersec uint64 + BytesPersec uint64 + BytesReceivedPersec uint64 + BytesSentPersec uint64 + DirectedPacketsReceivedPersec uint64 + DirectedPacketsSentPersec uint64 + DroppedPacketsIncomingPersec uint64 + DroppedPacketsOutgoingPersec uint64 + ExtensionsDroppedPacketsIncomingPersec uint64 + ExtensionsDroppedPacketsOutgoingPersec uint64 + LearnedMacAddresses uint64 + LearnedMacAddressesPersec uint64 + MulticastPacketsReceivedPersec uint64 + MulticastPacketsSentPersec uint64 + NumberofSendChannelMovesPersec uint64 + NumberofVMQMovesPersec uint64 + PacketsFlooded uint64 + PacketsFloodedPersec uint64 + PacketsPersec uint64 + PacketsReceivedPersec uint64 + PacketsSentPersec uint64 + PurgedMacAddresses uint64 + PurgedMacAddressesPersec uint64 } func (c *Collector) collectVmSwitch(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch", &dst); err != nil { + return err } for _, obj := range dst { @@ -1501,19 +1500,19 @@ func (c *Collector) collectVmSwitch(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter ... type Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter struct { - Name string `mi:"Name"` - BytesDropped uint64 `mi:"BytesDropped"` - BytesReceivedPersec uint64 `mi:"BytesReceivedPersec"` - BytesSentPersec uint64 `mi:"BytesSentPersec"` - FramesDropped uint64 `mi:"FramesDropped"` - FramesReceivedPersec uint64 `mi:"FramesReceivedPersec"` - FramesSentPersec uint64 `mi:"FramesSentPersec"` + Name string + BytesDropped uint64 + BytesReceivedPersec uint64 + BytesSentPersec uint64 + FramesDropped uint64 + FramesReceivedPersec uint64 + FramesSentPersec uint64 } func (c *Collector) collectVmEthernet(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter", &dst); err != nil { + return err } for _, obj := range dst { @@ -1569,19 +1568,19 @@ func (c *Collector) collectVmEthernet(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_Counters_HyperVVirtualStorageDevice ... type Win32_PerfRawData_Counters_HyperVVirtualStorageDevice struct { - Name string `mi:"Name"` - ErrorCount uint64 `mi:"ErrorCount"` - QueueLength uint32 `mi:"QueueLength"` - ReadBytesPersec uint64 `mi:"ReadBytesPersec"` - ReadOperationsPerSec uint64 `mi:"ReadOperationsPerSec"` - WriteBytesPersec uint64 `mi:"WriteBytesPersec"` - WriteOperationsPerSec uint64 `mi:"WriteOperationsPerSec"` + Name string + ErrorCount uint64 + QueueLength uint32 + ReadBytesPersec uint64 + ReadOperationsPerSec uint64 + WriteBytesPersec uint64 + WriteOperationsPerSec uint64 } func (c *Collector) collectVmStorage(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_Counters_HyperVVirtualStorageDevice - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_Counters_HyperVVirtualStorageDevice"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_Counters_HyperVVirtualStorageDevice", &dst); err != nil { + return err } for _, obj := range dst { @@ -1637,19 +1636,19 @@ func (c *Collector) collectVmStorage(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter ... type Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter struct { - Name string `mi:"Name"` - BytesReceivedPersec uint64 `mi:"BytesReceivedPersec"` - BytesSentPersec uint64 `mi:"BytesSentPersec"` - DroppedPacketsIncomingPersec uint64 `mi:"DroppedPacketsIncomingPersec"` - DroppedPacketsOutgoingPersec uint64 `mi:"DroppedPacketsOutgoingPersec"` - PacketsReceivedPersec uint64 `mi:"PacketsReceivedPersec"` - PacketsSentPersec uint64 `mi:"PacketsSentPersec"` + Name string + BytesReceivedPersec uint64 + BytesSentPersec uint64 + DroppedPacketsIncomingPersec uint64 + DroppedPacketsOutgoingPersec uint64 + PacketsReceivedPersec uint64 + PacketsSentPersec uint64 } func (c *Collector) collectVmNetwork(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter", &dst); err != nil { + return err } for _, obj := range dst { @@ -1705,23 +1704,23 @@ func (c *Collector) collectVmNetwork(ch chan<- prometheus.Metric) error { // Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM ... type Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM struct { - Name string `mi:"Name"` - AddedMemory uint64 `mi:"AddedMemory"` - AveragePressure uint64 `mi:"AveragePressure"` - CurrentPressure uint64 `mi:"CurrentPressure"` - GuestVisiblePhysicalMemory uint64 `mi:"GuestVisiblePhysicalMemory"` - MaximumPressure uint64 `mi:"MaximumPressure"` - MemoryAddOperations uint64 `mi:"MemoryAddOperations"` - MemoryRemoveOperations uint64 `mi:"MemoryRemoveOperations"` - MinimumPressure uint64 `mi:"MinimumPressure"` - PhysicalMemory uint64 `mi:"PhysicalMemory"` - RemovedMemory uint64 `mi:"RemovedMemory"` + Name string + AddedMemory uint64 + AveragePressure uint64 + CurrentPressure uint64 + GuestVisiblePhysicalMemory uint64 + MaximumPressure uint64 + MemoryAddOperations uint64 + MemoryRemoveOperations uint64 + MinimumPressure uint64 + PhysicalMemory uint64 + RemovedMemory uint64 } func (c *Collector) collectVmMemory(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM", &dst); err != nil { + return err } for _, obj := range dst { diff --git a/internal/collector/iis/iis.go b/internal/collector/iis/iis.go index dbd93319e..31e126023 100644 --- a/internal/collector/iis/iis.go +++ b/internal/collector/iis/iis.go @@ -10,10 +10,10 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows/registry" ) @@ -262,7 +262,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger = logger.With(slog.String("collector", Name)) c.iisVersion = getIISVersion(logger) diff --git a/internal/collector/license/license.go b/internal/collector/license/license.go index 74d221098..9d0aa04a9 100644 --- a/internal/collector/license/license.go +++ b/internal/collector/license/license.go @@ -7,9 +7,9 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/headers/slc" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "license" @@ -61,7 +61,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { c.licenseStatus = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "status"), "Status of windows license", diff --git a/internal/collector/logical_disk/logical_disk.go b/internal/collector/logical_disk/logical_disk.go index 145c10954..dd2374c48 100644 --- a/internal/collector/logical_disk/logical_disk.go +++ b/internal/collector/logical_disk/logical_disk.go @@ -13,13 +13,13 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" ) @@ -141,7 +141,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ currentDiskQueueLength, diff --git a/internal/collector/logon/logon.go b/internal/collector/logon/logon.go index 63a45238f..3c40410d1 100644 --- a/internal/collector/logon/logon.go +++ b/internal/collector/logon/logon.go @@ -8,9 +8,9 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/headers/secur32" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "logon" @@ -54,7 +54,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { c.sessionInfo = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "session_logon_timestamp_seconds"), "timestamp of the logon session in seconds.", diff --git a/internal/collector/memory/memory.go b/internal/collector/memory/memory.go index 977e3a544..573a1470b 100644 --- a/internal/collector/memory/memory.go +++ b/internal/collector/memory/memory.go @@ -12,13 +12,13 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "memory" @@ -105,7 +105,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ availableBytes, diff --git a/internal/collector/mscluster/mscluster.go b/internal/collector/mscluster/mscluster.go index 40f03c012..6a5d09e8b 100644 --- a/internal/collector/mscluster/mscluster.go +++ b/internal/collector/mscluster/mscluster.go @@ -8,9 +8,9 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "mscluster" @@ -32,7 +32,7 @@ var ConfigDefaults = Config{ // A Collector is a Prometheus Collector for WMI MSCluster_Cluster metrics. type Collector struct { config Config - miSession *mi.Session + wmiClient *wmi.Client // cluster clusterAddEvictDelay *prometheus.Desc @@ -221,16 +221,16 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { if len(c.config.CollectorsEnabled) == 0 { return nil } - if miSession == nil { - return errors.New("miSession is nil") + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.miSession = miSession + c.wmiClient = wmiClient if slices.Contains(c.config.CollectorsEnabled, "cluster") { c.buildCluster() diff --git a/internal/collector/mscluster/mscluster_cluster.go b/internal/collector/mscluster/mscluster_cluster.go index 082910886..fba00da5b 100644 --- a/internal/collector/mscluster/mscluster_cluster.go +++ b/internal/collector/mscluster/mscluster_cluster.go @@ -1,11 +1,7 @@ package mscluster import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -14,85 +10,85 @@ const nameCluster = Name + "_cluster" // msClusterCluster represents the MSCluster_Cluster WMI class // - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-cluster type msClusterCluster struct { - Name string `mi:"Name"` - - AddEvictDelay uint `mi:"AddEvictDelay"` - AdminAccessPoint uint `mi:"AdminAccessPoint"` - AutoAssignNodeSite uint `mi:"AutoAssignNodeSite"` - AutoBalancerLevel uint `mi:"AutoBalancerLevel"` - AutoBalancerMode uint `mi:"AutoBalancerMode"` - BackupInProgress uint `mi:"BackupInProgress"` - BlockCacheSize uint `mi:"BlockCacheSize"` - ClusSvcHangTimeout uint `mi:"ClusSvcHangTimeout"` - ClusSvcRegroupOpeningTimeout uint `mi:"ClusSvcRegroupOpeningTimeout"` - ClusSvcRegroupPruningTimeout uint `mi:"ClusSvcRegroupPruningTimeout"` - ClusSvcRegroupStageTimeout uint `mi:"ClusSvcRegroupStageTimeout"` - ClusSvcRegroupTickInMilliseconds uint `mi:"ClusSvcRegroupTickInMilliseconds"` - ClusterEnforcedAntiAffinity uint `mi:"ClusterEnforcedAntiAffinity"` - ClusterFunctionalLevel uint `mi:"ClusterFunctionalLevel"` - ClusterGroupWaitDelay uint `mi:"ClusterGroupWaitDelay"` - ClusterLogLevel uint `mi:"ClusterLogLevel"` - ClusterLogSize uint `mi:"ClusterLogSize"` - ClusterUpgradeVersion uint `mi:"ClusterUpgradeVersion"` - CrossSiteDelay uint `mi:"CrossSiteDelay"` - CrossSiteThreshold uint `mi:"CrossSiteThreshold"` - CrossSubnetDelay uint `mi:"CrossSubnetDelay"` - CrossSubnetThreshold uint `mi:"CrossSubnetThreshold"` - CsvBalancer uint `mi:"CsvBalancer"` - DatabaseReadWriteMode uint `mi:"DatabaseReadWriteMode"` - DefaultNetworkRole uint `mi:"DefaultNetworkRole"` - DetectedCloudPlatform uint `mi:"DetectedCloudPlatform"` - DetectManagedEvents uint `mi:"DetectManagedEvents"` - DetectManagedEventsThreshold uint `mi:"DetectManagedEventsThreshold"` - DisableGroupPreferredOwnerRandomization uint `mi:"DisableGroupPreferredOwnerRandomization"` - DrainOnShutdown uint `mi:"DrainOnShutdown"` - DynamicQuorumEnabled uint `mi:"DynamicQuorumEnabled"` - EnableSharedVolumes uint `mi:"EnableSharedVolumes"` - FixQuorum uint `mi:"FixQuorum"` - GracePeriodEnabled uint `mi:"GracePeriodEnabled"` - GracePeriodTimeout uint `mi:"GracePeriodTimeout"` - GroupDependencyTimeout uint `mi:"GroupDependencyTimeout"` - HangRecoveryAction uint `mi:"HangRecoveryAction"` - IgnorePersistentStateOnStartup uint `mi:"IgnorePersistentStateOnStartup"` - LogResourceControls uint `mi:"LogResourceControls"` - LowerQuorumPriorityNodeId uint `mi:"LowerQuorumPriorityNodeId"` - MaxNumberOfNodes uint `mi:"MaxNumberOfNodes"` - MessageBufferLength uint `mi:"MessageBufferLength"` - MinimumNeverPreemptPriority uint `mi:"MinimumNeverPreemptPriority"` - MinimumPreemptorPriority uint `mi:"MinimumPreemptorPriority"` - NetftIPSecEnabled uint `mi:"NetftIPSecEnabled"` - PlacementOptions uint `mi:"PlacementOptions"` - PlumbAllCrossSubnetRoutes uint `mi:"PlumbAllCrossSubnetRoutes"` - PreventQuorum uint `mi:"PreventQuorum"` - QuarantineDuration uint `mi:"QuarantineDuration"` - QuarantineThreshold uint `mi:"QuarantineThreshold"` - QuorumArbitrationTimeMax uint `mi:"QuorumArbitrationTimeMax"` - QuorumArbitrationTimeMin uint `mi:"QuorumArbitrationTimeMin"` - QuorumLogFileSize uint `mi:"QuorumLogFileSize"` - QuorumTypeValue uint `mi:"QuorumTypeValue"` - RequestReplyTimeout uint `mi:"RequestReplyTimeout"` - ResiliencyDefaultPeriod uint `mi:"ResiliencyDefaultPeriod"` - ResiliencyLevel uint `mi:"ResiliencyLevel"` - ResourceDllDeadlockPeriod uint `mi:"ResourceDllDeadlockPeriod"` - RootMemoryReserved uint `mi:"RootMemoryReserved"` - RouteHistoryLength uint `mi:"RouteHistoryLength"` - S2DBusTypes uint `mi:"S2DBusTypes"` - S2DCacheDesiredState uint `mi:"S2DCacheDesiredState"` - S2DCacheFlashReservePercent uint `mi:"S2DCacheFlashReservePercent"` - S2DCachePageSizeKBytes uint `mi:"S2DCachePageSizeKBytes"` - S2DEnabled uint `mi:"S2DEnabled"` - S2DIOLatencyThreshold uint `mi:"S2DIOLatencyThreshold"` - S2DOptimizations uint `mi:"S2DOptimizations"` - SameSubnetDelay uint `mi:"SameSubnetDelay"` - SameSubnetThreshold uint `mi:"SameSubnetThreshold"` - SecurityLevel uint `mi:"SecurityLevel"` - SecurityLevelForStorage uint `mi:"SecurityLevelForStorage"` - SharedVolumeVssWriterOperationTimeout uint `mi:"SharedVolumeVssWriterOperationTimeout"` - ShutdownTimeoutInMinutes uint `mi:"ShutdownTimeoutInMinutes"` - UseClientAccessNetworksForSharedVolumes uint `mi:"UseClientAccessNetworksForSharedVolumes"` - WitnessDatabaseWriteTimeout uint `mi:"WitnessDatabaseWriteTimeout"` - WitnessDynamicWeight uint `mi:"WitnessDynamicWeight"` - WitnessRestartInterval uint `mi:"WitnessRestartInterval"` + Name string + + AddEvictDelay uint + AdminAccessPoint uint + AutoAssignNodeSite uint + AutoBalancerLevel uint + AutoBalancerMode uint + BackupInProgress uint + BlockCacheSize uint + ClusSvcHangTimeout uint + ClusSvcRegroupOpeningTimeout uint + ClusSvcRegroupPruningTimeout uint + ClusSvcRegroupStageTimeout uint + ClusSvcRegroupTickInMilliseconds uint + ClusterEnforcedAntiAffinity uint + ClusterFunctionalLevel uint + ClusterGroupWaitDelay uint + ClusterLogLevel uint + ClusterLogSize uint + ClusterUpgradeVersion uint + CrossSiteDelay uint + CrossSiteThreshold uint + CrossSubnetDelay uint + CrossSubnetThreshold uint + CsvBalancer uint + DatabaseReadWriteMode uint + DefaultNetworkRole uint + DetectedCloudPlatform uint + DetectManagedEvents uint + DetectManagedEventsThreshold uint + DisableGroupPreferredOwnerRandomization uint + DrainOnShutdown uint + DynamicQuorumEnabled uint + EnableSharedVolumes uint + FixQuorum uint + GracePeriodEnabled uint + GracePeriodTimeout uint + GroupDependencyTimeout uint + HangRecoveryAction uint + IgnorePersistentStateOnStartup uint + LogResourceControls uint + LowerQuorumPriorityNodeId uint + MaxNumberOfNodes uint + MessageBufferLength uint + MinimumNeverPreemptPriority uint + MinimumPreemptorPriority uint + NetftIPSecEnabled uint + PlacementOptions uint + PlumbAllCrossSubnetRoutes uint + PreventQuorum uint + QuarantineDuration uint + QuarantineThreshold uint + QuorumArbitrationTimeMax uint + QuorumArbitrationTimeMin uint + QuorumLogFileSize uint + QuorumTypeValue uint + RequestReplyTimeout uint + ResiliencyDefaultPeriod uint + ResiliencyLevel uint + ResourceDllDeadlockPeriod uint + RootMemoryReserved uint + RouteHistoryLength uint + S2DBusTypes uint + S2DCacheDesiredState uint + S2DCacheFlashReservePercent uint + S2DCachePageSizeKBytes uint + S2DEnabled uint + S2DIOLatencyThreshold uint + S2DOptimizations uint + SameSubnetDelay uint + SameSubnetThreshold uint + SecurityLevel uint + SecurityLevelForStorage uint + SharedVolumeVssWriterOperationTimeout uint + ShutdownTimeoutInMinutes uint + UseClientAccessNetworksForSharedVolumes uint + WitnessDatabaseWriteTimeout uint + WitnessDynamicWeight uint + WitnessRestartInterval uint } func (c *Collector) buildCluster() { @@ -562,8 +558,8 @@ func (c *Collector) buildCluster() { func (c *Collector) collectCluster(ch chan<- prometheus.Metric) error { var dst []msClusterCluster - if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * MSCluster_Cluster"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM MSCluster_Cluster", &dst, nil, "root/MSCluster"); err != nil { + return err } for _, v := range dst { diff --git a/internal/collector/mscluster/mscluster_network.go b/internal/collector/mscluster/mscluster_network.go index b0ed2874c..c013fff9a 100644 --- a/internal/collector/mscluster/mscluster_network.go +++ b/internal/collector/mscluster/mscluster_network.go @@ -1,11 +1,7 @@ package mscluster import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -14,13 +10,13 @@ const nameNetwork = Name + "_network" // msClusterNetwork represents the MSCluster_Network WMI class // - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-network type msClusterNetwork struct { - Name string `mi:"Name"` + Name string - Characteristics uint `mi:"Characteristics"` - Flags uint `mi:"Flags"` - Metric uint `mi:"Metric"` - Role uint `mi:"Role"` - State uint `mi:"State"` + Characteristics uint + Flags uint + Metric uint + Role uint + State uint } func (c *Collector) buildNetwork() { @@ -61,8 +57,8 @@ func (c *Collector) buildNetwork() { func (c *Collector) collectNetwork(ch chan<- prometheus.Metric) error { var dst []msClusterNetwork - if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * MSCluster_Node"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM MSCluster_Network", &dst, nil, "root/MSCluster"); err != nil { + return err } for _, v := range dst { diff --git a/internal/collector/mscluster/mscluster_node.go b/internal/collector/mscluster/mscluster_node.go index f89a8bc82..523d26869 100644 --- a/internal/collector/mscluster/mscluster_node.go +++ b/internal/collector/mscluster/mscluster_node.go @@ -1,11 +1,7 @@ package mscluster import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -14,22 +10,22 @@ const nameNode = Name + "_node" // msClusterNode represents the MSCluster_Node WMI class // - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-node type msClusterNode struct { - Name string `mi:"Name"` - - BuildNumber uint `mi:"BuildNumber"` - Characteristics uint `mi:"Characteristics"` - DetectedCloudPlatform uint `mi:"DetectedCloudPlatform"` - DynamicWeight uint `mi:"DynamicWeight"` - Flags uint `mi:"Flags"` - MajorVersion uint `mi:"MajorVersion"` - MinorVersion uint `mi:"MinorVersion"` - NeedsPreventQuorum uint `mi:"NeedsPreventQuorum"` - NodeDrainStatus uint `mi:"NodeDrainStatus"` - NodeHighestVersion uint `mi:"NodeHighestVersion"` - NodeLowestVersion uint `mi:"NodeLowestVersion"` - NodeWeight uint `mi:"NodeWeight"` - State uint `mi:"State"` - StatusInformation uint `mi:"StatusInformation"` + Name string + + BuildNumber uint + Characteristics uint + DetectedCloudPlatform uint + DynamicWeight uint + Flags uint + MajorVersion uint + MinorVersion uint + NeedsPreventQuorum uint + NodeDrainStatus uint + NodeHighestVersion uint + NodeLowestVersion uint + NodeWeight uint + State uint + StatusInformation uint } func (c *Collector) buildNode() { @@ -124,8 +120,8 @@ func (c *Collector) buildNode() { func (c *Collector) collectNode(ch chan<- prometheus.Metric) ([]string, error) { var dst []msClusterNode - if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * FROM MSCluster_Node"))); err != nil { - return nil, fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM MSCluster_Node", &dst, nil, "root/MSCluster"); err != nil { + return nil, err } nodeNames := make([]string, 0, len(dst)) diff --git a/internal/collector/mscluster/mscluster_resource.go b/internal/collector/mscluster/mscluster_resource.go index 64a231a72..990775c44 100644 --- a/internal/collector/mscluster/mscluster_resource.go +++ b/internal/collector/mscluster/mscluster_resource.go @@ -1,11 +1,7 @@ package mscluster import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -14,27 +10,27 @@ const nameResource = Name + "_resource" // msClusterResource represents the MSCluster_Resource WMI class // - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-resource type msClusterResource struct { - Name string `mi:"Name"` - Type string `mi:"Type"` - OwnerGroup string `mi:"OwnerGroup"` - OwnerNode string `mi:"OwnerNode"` + Name string + Type string + OwnerGroup string + OwnerNode string - Characteristics uint `mi:"Characteristics"` - DeadlockTimeout uint `mi:"DeadlockTimeout"` - EmbeddedFailureAction uint `mi:"EmbeddedFailureAction"` - Flags uint `mi:"Flags"` - IsAlivePollInterval uint `mi:"IsAlivePollInterval"` - LooksAlivePollInterval uint `mi:"LooksAlivePollInterval"` - MonitorProcessId uint `mi:"MonitorProcessId"` - PendingTimeout uint `mi:"PendingTimeout"` - ResourceClass uint `mi:"ResourceClass"` - RestartAction uint `mi:"RestartAction"` - RestartDelay uint `mi:"RestartDelay"` - RestartPeriod uint `mi:"RestartPeriod"` - RestartThreshold uint `mi:"RestartThreshold"` - RetryPeriodOnFailure uint `mi:"RetryPeriodOnFailure"` - State uint `mi:"State"` - Subclass uint `mi:"Subclass"` + Characteristics uint + DeadlockTimeout uint + EmbeddedFailureAction uint + Flags uint + IsAlivePollInterval uint + LooksAlivePollInterval uint + MonitorProcessId uint + PendingTimeout uint + ResourceClass uint + RestartAction uint + RestartDelay uint + RestartPeriod uint + RestartThreshold uint + RetryPeriodOnFailure uint + State uint + Subclass uint } func (c *Collector) buildResource() { @@ -153,8 +149,8 @@ func (c *Collector) buildResource() { func (c *Collector) collectResource(ch chan<- prometheus.Metric, nodeNames []string) error { var dst []msClusterResource - if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * FROM MSCluster_Resource"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM MSCluster_Resource", &dst, nil, "root/MSCluster"); err != nil { + return err } for _, v := range dst { diff --git a/internal/collector/mscluster/mscluster_resourcegroup.go b/internal/collector/mscluster/mscluster_resourcegroup.go index 94a60481c..fb5df81bb 100644 --- a/internal/collector/mscluster/mscluster_resourcegroup.go +++ b/internal/collector/mscluster/mscluster_resourcegroup.go @@ -1,11 +1,7 @@ package mscluster import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -14,22 +10,22 @@ const nameResourceGroup = Name + "_resourcegroup" // msClusterResourceGroup represents the MSCluster_ResourceGroup WMI class // - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-resourcegroup type msClusterResourceGroup struct { - Name string `mi:"Name"` - - AutoFailbackType uint `mi:"AutoFailbackType"` - Characteristics uint `mi:"Characteristics"` - ColdStartSetting uint `mi:"ColdStartSetting"` - DefaultOwner uint `mi:"DefaultOwner"` - FailbackWindowEnd int `mi:"FailbackWindowEnd"` - FailbackWindowStart int `mi:"FailbackWindowStart"` - FailoverPeriod uint `mi:"FailoverPeriod"` - FailoverThreshold uint `mi:"FailoverThreshold"` - Flags uint `mi:"Flags"` - GroupType uint `mi:"GroupType"` - OwnerNode string `mi:"OwnerNode"` - Priority uint `mi:"Priority"` - ResiliencyPeriod uint `mi:"ResiliencyPeriod"` - State uint `mi:"State"` + Name string + + AutoFailbackType uint + Characteristics uint + ColdStartSetting uint + DefaultOwner uint + FailbackWindowEnd int + FailbackWindowStart int + FailoverPeriod uint + FailoverThreshold uint + Flags uint + GroupType uint + OwnerNode string + Priority uint + ResiliencyPeriod uint + State uint } func (c *Collector) buildResourceGroup() { @@ -130,8 +126,8 @@ func (c *Collector) buildResourceGroup() { func (c *Collector) collectResourceGroup(ch chan<- prometheus.Metric, nodeNames []string) error { var dst []msClusterResourceGroup - if err := c.miSession.Query(&dst, mi.NamespaceRootMSCluster, utils.Must(mi.NewQuery("SELECT * FROM MSCluster_ResourceGroup"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM MSCluster_ResourceGroup", &dst, nil, "root/MSCluster"); err != nil { + return err } for _, v := range dst { diff --git a/internal/collector/msmq/msmq.go b/internal/collector/msmq/msmq.go index b128c4ac2..b0b97ed0e 100644 --- a/internal/collector/msmq/msmq.go +++ b/internal/collector/msmq/msmq.go @@ -4,15 +4,14 @@ package msmq import ( "errors" - "fmt" "log/slog" "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "msmq" @@ -28,7 +27,7 @@ var ConfigDefaults = Config{ // A Collector is a Prometheus Collector for WMI Win32_PerfRawData_MSMQ_MSMQQueue metrics. type Collector struct { config Config - miSession *mi.Session + wmiClient *wmi.Client bytesInJournalQueue *prometheus.Desc bytesInQueue *prometheus.Desc @@ -76,14 +75,14 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error { logger = logger.With(slog.String("collector", Name)) - if miSession == nil { - return errors.New("miSession is nil") + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.miSession = miSession + c.wmiClient = wmiClient if *c.config.QueryWhereClause == "" { logger.Warn("No where-clause specified for msmq collector. This will generate a very large number of metrics!") @@ -133,12 +132,12 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan } type msmqQueue struct { - Name string `mi:"Name"` + Name string - BytesInJournalQueue uint64 `mi:"BytesInJournalQueue"` - BytesInQueue uint64 `mi:"BytesInQueue"` - MessagesInJournalQueue uint64 `mi:"MessagesInJournalQueue"` - MessagesInQueue uint64 `mi:"MessagesInQueue"` + BytesInJournalQueue uint64 + BytesInQueue uint64 + MessagesInJournalQueue uint64 + MessagesInQueue uint64 } func (c *Collector) collect(ch chan<- prometheus.Metric) error { @@ -149,13 +148,8 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error { query += " WHERE " + *c.config.QueryWhereClause } - queryExpression, err := mi.NewQuery(query) - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, queryExpression); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query(query, &dst); err != nil { + return err } for _, msmq := range dst { diff --git a/internal/collector/mssql/mssql.go b/internal/collector/mssql/mssql.go index d040210b3..271173f38 100644 --- a/internal/collector/mssql/mssql.go +++ b/internal/collector/mssql/mssql.go @@ -13,10 +13,10 @@ import ( "time" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows/registry" ) @@ -508,7 +508,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { // Result must order, to prevent test failures. sort.Strings(c.config.CollectorsEnabled) diff --git a/internal/collector/net/net.go b/internal/collector/net/net.go index 80f73f771..a3870187c 100644 --- a/internal/collector/net/net.go +++ b/internal/collector/net/net.go @@ -13,12 +13,12 @@ import ( "unsafe" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" ) @@ -149,7 +149,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { if utils.PDHEnabled() { counters := []string{ BytesReceivedPerSec, diff --git a/internal/collector/netframework/netframework.go b/internal/collector/netframework/netframework.go index cae840341..530933e6c 100644 --- a/internal/collector/netframework/netframework.go +++ b/internal/collector/netframework/netframework.go @@ -9,9 +9,9 @@ import ( "slices" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "netframework" @@ -47,7 +47,7 @@ const ( // A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRExceptions metrics. type Collector struct { config Config - miSession *mi.Session + wmiClient *wmi.Client // clrexceptions numberOfExceptionsThrown *prometheus.Desc @@ -143,12 +143,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.miSession = miSession + c.wmiClient = wmiClient if slices.Contains(c.config.CollectorsEnabled, collectorClrExceptions) { c.buildClrExceptions() diff --git a/internal/collector/netframework/netframework_clrexceptions.go b/internal/collector/netframework/netframework_clrexceptions.go index 059befca9..4f98b3e4c 100644 --- a/internal/collector/netframework/netframework_clrexceptions.go +++ b/internal/collector/netframework/netframework_clrexceptions.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -39,19 +35,19 @@ func (c *Collector) buildClrExceptions() { } type Win32_PerfRawData_NETFramework_NETCLRExceptions struct { - Name string `mi:"Name"` + Name string - NumberofExcepsThrown uint32 `mi:"NumberofExcepsThrown"` - NumberofExcepsThrownPersec uint32 `mi:"NumberofExcepsThrownPersec"` - NumberofFiltersPersec uint32 `mi:"NumberofFiltersPersec"` - NumberofFinallysPersec uint32 `mi:"NumberofFinallysPersec"` - ThrowToCatchDepthPersec uint32 `mi:"ThrowToCatchDepthPersec"` + NumberofExcepsThrown uint32 + NumberofExcepsThrownPersec uint32 + NumberofFiltersPersec uint32 + NumberofFinallysPersec uint32 + ThrowToCatchDepthPersec uint32 } func (c *Collector) collectClrExceptions(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRExceptions - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRExceptions"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRExceptions", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/netframework/netframework_clrinterop.go b/internal/collector/netframework/netframework_clrinterop.go index 7fca0e261..0d4992684 100644 --- a/internal/collector/netframework/netframework_clrinterop.go +++ b/internal/collector/netframework/netframework_clrinterop.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -33,19 +29,19 @@ func (c *Collector) buildClrInterop() { } type Win32_PerfRawData_NETFramework_NETCLRInterop struct { - Name string `mi:"Name"` + Name string - NumberofCCWs uint32 `mi:"NumberofCCWs"` - Numberofmarshalling uint32 `mi:"Numberofmarshalling"` - NumberofStubs uint32 `mi:"NumberofStubs"` - NumberofTLBexportsPersec uint32 `mi:"NumberofTLBexportsPersec"` - NumberofTLBimportsPersec uint32 `mi:"NumberofTLBimportsPersec"` + NumberofCCWs uint32 + Numberofmarshalling uint32 + NumberofStubs uint32 + NumberofTLBexportsPersec uint32 + NumberofTLBimportsPersec uint32 } func (c *Collector) collectClrInterop(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRInterop - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRInterop"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRInterop", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/netframework/netframework_clrjit.go b/internal/collector/netframework/netframework_clrjit.go index db12fa4af..d84389138 100644 --- a/internal/collector/netframework/netframework_clrjit.go +++ b/internal/collector/netframework/netframework_clrjit.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -39,21 +35,21 @@ func (c *Collector) buildClrJIT() { } type Win32_PerfRawData_NETFramework_NETCLRJit struct { - Name string `mi:"Name"` + Name string - Frequency_PerfTime uint32 `mi:"Frequency_PerfTime"` - ILBytesJittedPersec uint32 `mi:"ILBytesJittedPersec"` - NumberofILBytesJitted uint32 `mi:"NumberofILBytesJitted"` - NumberofMethodsJitted uint32 `mi:"NumberofMethodsJitted"` - PercentTimeinJit uint32 `mi:"PercentTimeinJit"` - StandardJitFailures uint32 `mi:"StandardJitFailures"` - TotalNumberofILBytesJitted uint32 `mi:"TotalNumberofILBytesJitted"` + Frequency_PerfTime uint32 + ILBytesJittedPersec uint32 + NumberofILBytesJitted uint32 + NumberofMethodsJitted uint32 + PercentTimeinJit uint32 + StandardJitFailures uint32 + TotalNumberofILBytesJitted uint32 } func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRJit - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRJit"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRJit", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/netframework/netframework_clrloading.go b/internal/collector/netframework/netframework_clrloading.go index 5f95fbe8c..1dfa5c704 100644 --- a/internal/collector/netframework/netframework_clrloading.go +++ b/internal/collector/netframework/netframework_clrloading.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -69,30 +65,30 @@ func (c *Collector) buildClrLoading() { } type Win32_PerfRawData_NETFramework_NETCLRLoading struct { - Name string `mi:"Name"` - - AssemblySearchLength uint32 `mi:"AssemblySearchLength"` - BytesinLoaderHeap uint64 `mi:"BytesinLoaderHeap"` - Currentappdomains uint32 `mi:"Currentappdomains"` - CurrentAssemblies uint32 `mi:"CurrentAssemblies"` - CurrentClassesLoaded uint32 `mi:"CurrentClassesLoaded"` - PercentTimeLoading uint64 `mi:"PercentTimeLoading"` - Rateofappdomains uint32 `mi:"Rateofappdomains"` - Rateofappdomainsunloaded uint32 `mi:"Rateofappdomainsunloaded"` - RateofAssemblies uint32 `mi:"RateofAssemblies"` - RateofClassesLoaded uint32 `mi:"RateofClassesLoaded"` - RateofLoadFailures uint32 `mi:"RateofLoadFailures"` - TotalAppdomains uint32 `mi:"TotalAppdomains"` - Totalappdomainsunloaded uint32 `mi:"Totalappdomainsunloaded"` - TotalAssemblies uint32 `mi:"TotalAssemblies"` - TotalClassesLoaded uint32 `mi:"TotalClassesLoaded"` - TotalNumberofLoadFailures uint32 `mi:"TotalNumberofLoadFailures"` + Name string + + AssemblySearchLength uint32 + BytesinLoaderHeap uint64 + Currentappdomains uint32 + CurrentAssemblies uint32 + CurrentClassesLoaded uint32 + PercentTimeLoading uint64 + Rateofappdomains uint32 + Rateofappdomainsunloaded uint32 + RateofAssemblies uint32 + RateofClassesLoaded uint32 + RateofLoadFailures uint32 + TotalAppdomains uint32 + Totalappdomainsunloaded uint32 + TotalAssemblies uint32 + TotalClassesLoaded uint32 + TotalNumberofLoadFailures uint32 } func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRLoading - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRLoading"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLoading", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/netframework/netframework_clrlocksandthreads.go b/internal/collector/netframework/netframework_clrlocksandthreads.go index 6bcd5239e..812e67b7c 100644 --- a/internal/collector/netframework/netframework_clrlocksandthreads.go +++ b/internal/collector/netframework/netframework_clrlocksandthreads.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -57,24 +53,24 @@ func (c *Collector) buildClrLocksAndThreads() { } type Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads struct { - Name string `mi:"Name"` - - ContentionRatePersec uint32 `mi:"ContentionRatePersec"` - CurrentQueueLength uint32 `mi:"CurrentQueueLength"` - NumberofcurrentlogicalThreads uint32 `mi:"NumberofcurrentlogicalThreads"` - NumberofcurrentphysicalThreads uint32 `mi:"NumberofcurrentphysicalThreads"` - Numberofcurrentrecognizedthreads uint32 `mi:"Numberofcurrentrecognizedthreads"` - Numberoftotalrecognizedthreads uint32 `mi:"Numberoftotalrecognizedthreads"` - QueueLengthPeak uint32 `mi:"QueueLengthPeak"` - QueueLengthPersec uint32 `mi:"QueueLengthPersec"` - RateOfRecognizedThreadsPersec uint32 `mi:"RateOfRecognizedThreadsPersec"` - TotalNumberofContentions uint32 `mi:"TotalNumberofContentions"` + Name string + + ContentionRatePersec uint32 + CurrentQueueLength uint32 + NumberofcurrentlogicalThreads uint32 + NumberofcurrentphysicalThreads uint32 + Numberofcurrentrecognizedthreads uint32 + Numberoftotalrecognizedthreads uint32 + QueueLengthPeak uint32 + QueueLengthPersec uint32 + RateOfRecognizedThreadsPersec uint32 + TotalNumberofContentions uint32 } func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/netframework/netframework_clrmemory.go b/internal/collector/netframework/netframework_clrmemory.go index afc394b8f..f324f0af0 100644 --- a/internal/collector/netframework/netframework_clrmemory.go +++ b/internal/collector/netframework/netframework_clrmemory.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -87,43 +83,43 @@ func (c *Collector) buildClrMemory() { } type Win32_PerfRawData_NETFramework_NETCLRMemory struct { - Name string `mi:"Name"` + Name string - AllocatedBytesPersec uint64 `mi:"AllocatedBytesPersec"` - FinalizationSurvivors uint64 `mi:"FinalizationSurvivors"` - Frequency_PerfTime uint64 `mi:"Frequency_PerfTime"` - Gen0heapsize uint64 `mi:"Gen0heapsize"` - Gen0PromotedBytesPerSec uint64 `mi:"Gen0PromotedBytesPersec"` - Gen1heapsize uint64 `mi:"Gen1heapsize"` - Gen1PromotedBytesPerSec uint64 `mi:"Gen1PromotedBytesPersec"` - Gen2heapsize uint64 `mi:"Gen2heapsize"` - LargeObjectHeapsize uint64 `mi:"LargeObjectHeapsize"` - NumberBytesinallHeaps uint64 `mi:"NumberBytesinallHeaps"` - NumberGCHandles uint64 `mi:"NumberGCHandles"` - NumberGen0Collections uint64 `mi:"NumberGen0Collections"` - NumberGen1Collections uint64 `mi:"NumberGen1Collections"` - NumberGen2Collections uint64 `mi:"NumberGen2Collections"` - NumberInducedGC uint64 `mi:"NumberInducedGC"` - NumberofPinnedObjects uint64 `mi:"NumberofPinnedObjects"` - NumberofSinkBlocksinuse uint64 `mi:"NumberofSinkBlocksinuse"` - NumberTotalcommittedBytes uint64 `mi:"NumberTotalcommittedBytes"` - NumberTotalreservedBytes uint64 `mi:"NumberTotalreservedBytes"` + AllocatedBytesPersec uint64 + FinalizationSurvivors uint64 + Frequency_PerfTime uint64 + Gen0heapsize uint64 + Gen0PromotedBytesPerSec uint64 + Gen1heapsize uint64 + Gen1PromotedBytesPerSec uint64 + Gen2heapsize uint64 + LargeObjectHeapsize uint64 + NumberBytesinallHeaps uint64 + NumberGCHandles uint64 + NumberGen0Collections uint64 + NumberGen1Collections uint64 + NumberGen2Collections uint64 + NumberInducedGC uint64 + NumberofPinnedObjects uint64 + NumberofSinkBlocksinuse uint64 + NumberTotalcommittedBytes uint64 + NumberTotalreservedBytes uint64 // PercentTimeinGC has countertype=PERF_RAW_FRACTION. // Formula: (100 * CounterValue) / BaseValue // By docs https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/ms974615(v=msdn.10)#perf_raw_fraction - PercentTimeinGC uint32 `mi:"PercentTimeinGC"` + PercentTimeinGC uint32 // BaseValue is just a "magic" number used to make the calculation come out right. - PercentTimeinGC_base uint32 `mi:"PercentTimeinGC_base"` - ProcessID uint64 `mi:"ProcessID"` - PromotedFinalizationMemoryfromGen0 uint64 `mi:"PromotedFinalizationMemoryfromGen0"` - PromotedMemoryfromGen0 uint64 `mi:"PromotedMemoryfromGen0"` - PromotedMemoryfromGen1 uint64 `mi:"PromotedMemoryfromGen1"` + PercentTimeinGC_base uint32 + ProcessID uint64 + PromotedFinalizationMemoryfromGen0 uint64 + PromotedMemoryfromGen0 uint64 + PromotedMemoryfromGen1 uint64 } func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRMemory - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRMemory"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRMemory", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/netframework/netframework_clrremoting.go b/internal/collector/netframework/netframework_clrremoting.go index a83b7388c..a2fa7d897 100644 --- a/internal/collector/netframework/netframework_clrremoting.go +++ b/internal/collector/netframework/netframework_clrremoting.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -51,21 +47,21 @@ func (c *Collector) buildClrRemoting() { } type Win32_PerfRawData_NETFramework_NETCLRRemoting struct { - Name string `mi:"Name"` + Name string - Channels uint32 `mi:"Channels"` - ContextBoundClassesLoaded uint32 `mi:"ContextBoundClassesLoaded"` - ContextBoundObjectsAllocPersec uint32 `mi:"ContextBoundObjectsAllocPersec"` - ContextProxies uint32 `mi:"ContextProxies"` - Contexts uint32 `mi:"Contexts"` - RemoteCallsPersec uint32 `mi:"RemoteCallsPersec"` - TotalRemoteCalls uint32 `mi:"TotalRemoteCalls"` + Channels uint32 + ContextBoundClassesLoaded uint32 + ContextBoundObjectsAllocPersec uint32 + ContextProxies uint32 + Contexts uint32 + RemoteCallsPersec uint32 + TotalRemoteCalls uint32 } func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRRemoting - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRRemoting"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRRemoting", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/netframework/netframework_clrsecurity.go b/internal/collector/netframework/netframework_clrsecurity.go index ef30b2ab9..7e3dfdc36 100644 --- a/internal/collector/netframework/netframework_clrsecurity.go +++ b/internal/collector/netframework/netframework_clrsecurity.go @@ -3,11 +3,7 @@ package netframework import ( - "fmt" - - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" ) @@ -39,20 +35,20 @@ func (c *Collector) buildClrSecurity() { } type Win32_PerfRawData_NETFramework_NETCLRSecurity struct { - Name string `mi:"Name"` + Name string - Frequency_PerfTime uint32 `mi:"Frequency_PerfTime"` - NumberLinkTimeChecks uint32 `mi:"NumberLinkTimeChecks"` - PercentTimeinRTchecks uint32 `mi:"PercentTimeinRTchecks"` - PercentTimeSigAuthenticating uint64 `mi:"PercentTimeSigAuthenticating"` - StackWalkDepth uint32 `mi:"StackWalkDepth"` - TotalRuntimeChecks uint32 `mi:"TotalRuntimeChecks"` + Frequency_PerfTime uint32 + NumberLinkTimeChecks uint32 + PercentTimeinRTchecks uint32 + PercentTimeSigAuthenticating uint64 + StackWalkDepth uint32 + TotalRuntimeChecks uint32 } func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_NETFramework_NETCLRSecurity - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRSecurity"))); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRSecurity", &dst); err != nil { + return err } for _, process := range dst { diff --git a/internal/collector/nps/nps.go b/internal/collector/nps/nps.go index 86a5bece0..06cd9abb9 100644 --- a/internal/collector/nps/nps.go +++ b/internal/collector/nps/nps.go @@ -6,9 +6,9 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "nps" @@ -20,10 +20,7 @@ var ConfigDefaults = Config{} // Collector is a Prometheus Collector for WMI Win32_PerfRawData_IAS_NPSAuthenticationServer and Win32_PerfRawData_IAS_NPSAccountingServer metrics. type Collector struct { config Config - miSession *mi.Session - - miQueryAuthenticationServer mi.Query - miQueryAccountingServer mi.Query + wmiClient *wmi.Client accessAccepts *prometheus.Desc accessChallenges *prometheus.Desc @@ -81,26 +78,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") - } - - miQuery, err := mi.NewQuery("SELECT Name, AccessAccepts, AccessChallenges, AccessRejects, AccessRequests, AccessBadAuthenticators, AccessDroppedPackets, AccessInvalidRequests, AccessMalformedPackets, AccessPacketsReceived, AccessPacketsSent, AccessServerResetTime, AccessServerUpTime, AccessUnknownType FROM Win32_PerfRawData_IAS_NPSAuthenticationServer") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.miQueryAuthenticationServer = miQuery - - miQuery, err = mi.NewQuery("SELECT Name, AccountingRequests, AccountingResponses, AccountingBadAuthenticators, AccountingDroppedPackets, AccountingInvalidRequests, AccountingMalformedPackets, AccountingNoRecord, AccountingPacketsReceived, AccountingPacketsSent, AccountingServerResetTime, AccountingServerUpTime, AccountingUnknownType FROM Win32_PerfRawData_IAS_NPSAccountingServer") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - c.miQueryAccountingServer = miQuery - c.miSession = miSession - + c.wmiClient = wmiClient c.accessAccepts = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "access_accepts"), "(AccessAccepts)", @@ -278,46 +261,46 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan // Win32_PerfRawData_IAS_NPSAuthenticationServer docs: // at the moment there is no Microsoft documentation. type Win32_PerfRawData_IAS_NPSAuthenticationServer struct { - Name string `mi:"Name"` - - AccessAccepts uint32 `mi:"AccessAccepts"` - AccessChallenges uint32 `mi:"AccessChallenges"` - AccessRejects uint32 `mi:"AccessRejects"` - AccessRequests uint32 `mi:"AccessRequests"` - AccessBadAuthenticators uint32 `mi:"AccessBadAuthenticators"` - AccessDroppedPackets uint32 `mi:"AccessDroppedPackets"` - AccessInvalidRequests uint32 `mi:"AccessInvalidRequests"` - AccessMalformedPackets uint32 `mi:"AccessMalformedPackets"` - AccessPacketsReceived uint32 `mi:"AccessPacketsReceived"` - AccessPacketsSent uint32 `mi:"AccessPacketsSent"` - AccessServerResetTime uint32 `mi:"AccessServerResetTime"` - AccessServerUpTime uint32 `mi:"AccessServerUpTime"` - AccessUnknownType uint32 `mi:"AccessUnknownType"` + Name string + + AccessAccepts uint32 + AccessChallenges uint32 + AccessRejects uint32 + AccessRequests uint32 + AccessBadAuthenticators uint32 + AccessDroppedPackets uint32 + AccessInvalidRequests uint32 + AccessMalformedPackets uint32 + AccessPacketsReceived uint32 + AccessPacketsSent uint32 + AccessServerResetTime uint32 + AccessServerUpTime uint32 + AccessUnknownType uint32 } type Win32_PerfRawData_IAS_NPSAccountingServer struct { - Name string `mi:"Name"` - - AccountingRequests uint32 `mi:"AccountingRequests"` - AccountingResponses uint32 `mi:"AccountingResponses"` - AccountingBadAuthenticators uint32 `mi:"AccountingBadAuthenticators"` - AccountingDroppedPackets uint32 `mi:"AccountingDroppedPackets"` - AccountingInvalidRequests uint32 `mi:"AccountingInvalidRequests"` - AccountingMalformedPackets uint32 `mi:"AccountingMalformedPackets"` - AccountingNoRecord uint32 `mi:"AccountingNoRecord"` - AccountingPacketsReceived uint32 `mi:"AccountingPacketsReceived"` - AccountingPacketsSent uint32 `mi:"AccountingPacketsSent"` - AccountingServerResetTime uint32 `mi:"AccountingServerResetTime"` - AccountingServerUpTime uint32 `mi:"AccountingServerUpTime"` - AccountingUnknownType uint32 `mi:"AccountingUnknownType"` + Name string + + AccountingRequests uint32 + AccountingResponses uint32 + AccountingBadAuthenticators uint32 + AccountingDroppedPackets uint32 + AccountingInvalidRequests uint32 + AccountingMalformedPackets uint32 + AccountingNoRecord uint32 + AccountingPacketsReceived uint32 + AccountingPacketsSent uint32 + AccountingServerResetTime uint32 + AccountingServerUpTime uint32 + AccountingUnknownType uint32 } // CollectAccept sends the metric values for each metric // to the provided prometheus Metric channel. func (c *Collector) CollectAccept(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_IAS_NPSAuthenticationServer - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryAuthenticationServer); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_IAS_NPSAuthenticationServer", &dst); err != nil { + return err } ch <- prometheus.MustNewConstMetric( @@ -403,8 +386,8 @@ func (c *Collector) CollectAccept(ch chan<- prometheus.Metric) error { func (c *Collector) CollectAccounting(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_IAS_NPSAccountingServer - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryAccountingServer); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_IAS_NPSAccountingServer", &dst); err != nil { + return err } ch <- prometheus.MustNewConstMetric( diff --git a/internal/collector/os/os.go b/internal/collector/os/os.go index 4920cecf2..e0acaf0d1 100644 --- a/internal/collector/os/os.go +++ b/internal/collector/os/os.go @@ -16,10 +16,10 @@ import ( "github.com/prometheus-community/windows_exporter/internal/headers/netapi32" "github.com/prometheus-community/windows_exporter/internal/headers/psapi" "github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" ) @@ -109,11 +109,9 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { - logger.Warn("The os collect holds a number of deprecated metrics and will be removed mid 2025. "+ - "See https://github.com/prometheus-community/windows_exporter/pull/1596 for more information.", - slog.String("collector", Name), - ) +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { + logger.Warn("The os collect holds a number of deprecated metrics and will be removed mid 2025. " + + "See https://github.com/prometheus-community/windows_exporter/pull/1596 for more information.") workstationInfo, err := netapi32.GetWorkstationInfo() if err != nil { diff --git a/internal/collector/perfdata/perfdata.go b/internal/collector/perfdata/perfdata.go index 82023e813..b0e6b2639 100644 --- a/internal/collector/perfdata/perfdata.go +++ b/internal/collector/perfdata/perfdata.go @@ -11,11 +11,11 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "perfdata" @@ -92,7 +92,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger.Warn("The perfdata collector is in an experimental state! The configuration may change in future. Please report any issues.") for i, object := range c.config.Objects { diff --git a/internal/collector/physical_disk/physical_disk.go b/internal/collector/physical_disk/physical_disk.go index 2f833007b..7ff8b6d47 100644 --- a/internal/collector/physical_disk/physical_disk.go +++ b/internal/collector/physical_disk/physical_disk.go @@ -9,11 +9,11 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "physical_disk" @@ -114,7 +114,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { c.requestsQueued = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "requests_queued"), "The number of requests queued to the disk (PhysicalDisk.CurrentDiskQueueLength)", diff --git a/internal/collector/printer/printer.go b/internal/collector/printer/printer.go index 7ca9dd72d..1617c1430 100644 --- a/internal/collector/printer/printer.go +++ b/internal/collector/printer/printer.go @@ -10,9 +10,9 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "printer" @@ -39,10 +39,8 @@ var ConfigDefaults = Config{ } type Collector struct { - config Config - miSession *mi.Session - miQueryPrinterJobs mi.Query - miQueryPrinter mi.Query + config Config + wmiClient *wmi.Client printerStatus *prometheus.Desc printerJobStatus *prometheus.Desc @@ -109,25 +107,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - miQuery, err := mi.NewQuery("SELECT Name, Default, PrinterStatus, JobCountSinceLastReset FROM win32_Printer") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - c.miQueryPrinter = miQuery - - miQuery, err = mi.NewQuery("SELECT Name, Status FROM win32_PrintJob") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - c.miQueryPrinterJobs = miQuery - c.miSession = miSession + c.wmiClient = wmiClient c.printerJobStatus = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "job_status"), @@ -158,35 +143,42 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { } type wmiPrinter struct { - Name string `mi:"Name"` - Default bool `mi:"Default"` - PrinterStatus uint16 `mi:"PrinterStatus"` - JobCountSinceLastReset uint32 `mi:"JobCountSinceLastReset"` + Name string + Default bool + PrinterStatus uint16 + JobCountSinceLastReset uint32 } type wmiPrintJob struct { - Name string `mi:"Name"` - Status string `mi:"Status"` + Name string + Status string } -func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error { - var errs []error - +func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { + logger = logger.With(slog.String("collector", Name)) if err := c.collectPrinterStatus(ch); err != nil { - errs = append(errs, fmt.Errorf("failed to collect printer status metrics: %w", err)) + logger.Error("failed to collect printer status metrics", + slog.Any("err", err), + ) + + return err } if err := c.collectPrinterJobStatus(ch); err != nil { - errs = append(errs, fmt.Errorf("failed to collect printer job status metrics: %w", err)) + logger.Error("failed to collect printer job status metrics", + slog.Any("err", err), + ) + + return err } - return errors.Join(errs...) + return nil } func (c *Collector) collectPrinterStatus(ch chan<- prometheus.Metric) error { var printers []wmiPrinter - if err := c.miSession.Query(&printers, mi.NamespaceRootCIMv2, c.miQueryPrinter); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM win32_Printer", &printers); err != nil { + return err } for _, printer := range printers { @@ -223,8 +215,8 @@ func (c *Collector) collectPrinterStatus(ch chan<- prometheus.Metric) error { func (c *Collector) collectPrinterJobStatus(ch chan<- prometheus.Metric) error { var printJobs []wmiPrintJob - if err := c.miSession.Query(&printJobs, mi.NamespaceRootCIMv2, c.miQueryPrinterJobs); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM win32_PrintJob", &printJobs); err != nil { + return err } groupedPrintJobs := c.groupPrintJobs(printJobs) diff --git a/internal/collector/process/process.go b/internal/collector/process/process.go index 6bb660691..3d1089399 100644 --- a/internal/collector/process/process.go +++ b/internal/collector/process/process.go @@ -12,13 +12,13 @@ import ( "unsafe" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" v2 "github.com/prometheus-community/windows_exporter/internal/perfdata/v2" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" ) @@ -38,9 +38,7 @@ var ConfigDefaults = Config{ type Collector struct { config Config - miSession *mi.Session - - workerProcessMIQueryQuery mi.Query + wmiClient *wmi.Client perfDataCollector perfdata.Collector @@ -141,20 +139,14 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error { logger = logger.With(slog.String("collector", Name)) - if miSession == nil { - return errors.New("miSession is nil") - } - - miQuery, err := mi.NewQuery("SELECT AppPoolName, ProcessId FROM WorkerProcess") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - c.workerProcessMIQueryQuery = miQuery - c.miSession = miSession + c.wmiClient = wmiClient if utils.PDHEnabled() { counters := []string{ @@ -310,8 +302,8 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error { } type WorkerProcess struct { - AppPoolName string `mi:"AppPoolName"` - ProcessId uint64 `mi:"ProcessId"` + AppPoolName string + ProcessId uint64 } func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { @@ -341,8 +333,10 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch var workerProcesses []WorkerProcess if c.config.EnableWorkerProcess { - if err := c.miSession.Query(&workerProcesses, mi.NamespaceRootWebAdministration, c.workerProcessMIQueryQuery); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM WorkerProcess", &workerProcesses, nil, "root\\WebAdministration"); err != nil { + logger.Debug("Could not query WebAdministration namespace for IIS worker processes", + slog.Any("err", err), + ) } } @@ -546,8 +540,10 @@ func (c *Collector) collectPDH(logger *slog.Logger, ch chan<- prometheus.Metric) var workerProcesses []WorkerProcess if c.config.EnableWorkerProcess { - if err := c.miSession.Query(&workerProcesses, mi.NamespaceRootWebAdministration, c.workerProcessMIQueryQuery); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM WorkerProcess", &workerProcesses, nil, "root\\WebAdministration"); err != nil { + logger.Debug("Could not query WebAdministration namespace for IIS worker processes", + slog.Any("err", err), + ) } } diff --git a/internal/collector/remote_fx/remote_fx.go b/internal/collector/remote_fx/remote_fx.go index d4f445455..92921bca3 100644 --- a/internal/collector/remote_fx/remote_fx.go +++ b/internal/collector/remote_fx/remote_fx.go @@ -7,11 +7,11 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "remote_fx" @@ -81,7 +81,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(*slog.Logger, *mi.Session) error { +func (c *Collector) Build(*slog.Logger, *wmi.Client) error { // net c.baseTCPRTT = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "net_base_tcp_rtt_seconds"), diff --git a/internal/collector/scheduled_task/scheduled_task.go b/internal/collector/scheduled_task/scheduled_task.go index 5a8bae0fc..810ebcfa0 100644 --- a/internal/collector/scheduled_task/scheduled_task.go +++ b/internal/collector/scheduled_task/scheduled_task.go @@ -13,9 +13,9 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "scheduled_task" @@ -148,7 +148,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { initErrCh := make(chan error) c.scheduledTasksReqCh = make(chan struct{}) c.scheduledTasksCh = make(chan *scheduledTaskResults) @@ -281,7 +281,7 @@ func (c *Collector) initializeScheduleService(initErrCh chan<- error) { if err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED); err != nil { var oleCode *ole.OleError - if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != 0x00000001 { + if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != wmi.S_FALSE { initErrCh <- err return diff --git a/internal/collector/service/service.go b/internal/collector/service/service.go index 714b9fcd3..c15cb6114 100644 --- a/internal/collector/service/service.go +++ b/internal/collector/service/service.go @@ -11,9 +11,9 @@ import ( "unsafe" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc/mgr" ) @@ -106,7 +106,7 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { return []string{}, nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger = logger.With(slog.String("collector", Name)) if c.config.ServiceInclude.String() == "^(?:.*)$" && c.config.ServiceExclude.String() == "^(?:)$" { diff --git a/internal/collector/smb/smb.go b/internal/collector/smb/smb.go index 8a1d212c8..dd97598a6 100644 --- a/internal/collector/smb/smb.go +++ b/internal/collector/smb/smb.go @@ -7,10 +7,10 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "smb" @@ -56,7 +56,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { // desc creates a new prometheus description desc := func(metricName string, description string, labels ...string) *prometheus.Desc { return prometheus.NewDesc( diff --git a/internal/collector/smbclient/smbclient.go b/internal/collector/smbclient/smbclient.go index ad5bde414..2b579e408 100644 --- a/internal/collector/smbclient/smbclient.go +++ b/internal/collector/smbclient/smbclient.go @@ -7,11 +7,11 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const ( @@ -79,7 +79,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { // desc creates a new prometheus description desc := func(metricName string, description string, labels []string) *prometheus.Desc { return prometheus.NewDesc( diff --git a/internal/collector/smtp/smtp.go b/internal/collector/smtp/smtp.go index 0ddf3fa16..35977e09e 100644 --- a/internal/collector/smtp/smtp.go +++ b/internal/collector/smtp/smtp.go @@ -8,10 +8,10 @@ import ( "regexp" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "smtp" @@ -141,10 +141,10 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { - logger.Info("smtp collector is in an experimental state! Metrics for this collector have not been tested.", - slog.String("collector", Name), - ) +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { + logger = logger.With(slog.String("collector", Name)) + + logger.Info("smtp collector is in an experimental state! Metrics for this collector have not been tested.") c.badMailedMessagesBadPickupFileTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "badmailed_messages_bad_pickup_file_total"), diff --git a/internal/collector/system/system.go b/internal/collector/system/system.go index 4dd70ccaf..6465debb5 100644 --- a/internal/collector/system/system.go +++ b/internal/collector/system/system.go @@ -7,10 +7,10 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "system" @@ -61,7 +61,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { c.contextSwitchesTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "context_switches_total"), "Total number of context switches (WMI source: PerfOS_System.ContextSwitchesPersec)", diff --git a/internal/collector/tcp/tcp.go b/internal/collector/tcp/tcp.go index ef96d3d1f..24b8d4354 100644 --- a/internal/collector/tcp/tcp.go +++ b/internal/collector/tcp/tcp.go @@ -10,11 +10,11 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/headers/iphlpapi" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" ) @@ -100,7 +100,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { counters := []string{ connectionFailures, connectionsActive, diff --git a/internal/collector/terminal_services/terminal_services.go b/internal/collector/terminal_services/terminal_services.go index 93a36bfdb..31b8d5737 100644 --- a/internal/collector/terminal_services/terminal_services.go +++ b/internal/collector/terminal_services/terminal_services.go @@ -11,11 +11,10 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/headers/wtsapi32" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" - "github.com/prometheus-community/windows_exporter/internal/utils" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" ) @@ -32,9 +31,9 @@ type Win32_ServerFeature struct { ID uint32 } -func isConnectionBrokerServer(logger *slog.Logger, miSession *mi.Session) bool { +func isConnectionBrokerServer(logger *slog.Logger, wmiClient *wmi.Client) bool { var dst []Win32_ServerFeature - if err := miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_ServerFeature"))); err != nil { + if err := wmiClient.Query("SELECT * FROM Win32_ServerFeature", &dst); err != nil { return false } @@ -113,14 +112,10 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") - } - +func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error { logger = logger.With(slog.String("collector", Name)) - c.connectionBrokerEnabled = isConnectionBrokerServer(logger, miSession) + c.connectionBrokerEnabled = isConnectionBrokerServer(logger, wmiClient) c.sessionInfo = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "session_info"), diff --git a/internal/collector/textfile/textfile.go b/internal/collector/textfile/textfile.go index fce5ac4ed..da5005d8e 100644 --- a/internal/collector/textfile/textfile.go +++ b/internal/collector/textfile/textfile.go @@ -29,11 +29,11 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/dimchansky/utfbom" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" + "github.com/yusufpapurcu/wmi" ) const Name = "textfile" @@ -104,7 +104,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger.Info("textfile Collector directories: "+strings.Join(c.config.TextFileDirectories, ","), slog.String("collector", Name), ) diff --git a/internal/collector/thermalzone/thermalzone.go b/internal/collector/thermalzone/thermalzone.go index fa5a7f5ae..6828b163a 100644 --- a/internal/collector/thermalzone/thermalzone.go +++ b/internal/collector/thermalzone/thermalzone.go @@ -4,13 +4,12 @@ package thermalzone import ( "errors" - "fmt" "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "thermalzone" @@ -22,8 +21,7 @@ var ConfigDefaults = Config{} // A Collector is a Prometheus Collector for WMI Win32_PerfRawData_Counters_ThermalZoneInformation metrics. type Collector struct { config Config - miSession *mi.Session - miQuery mi.Query + wmiClient *wmi.Client percentPassiveLimit *prometheus.Desc temperature *prometheus.Desc @@ -58,19 +56,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - miQuery, err := mi.NewQuery("SELECT Name, HighPrecisionTemperature, PercentPassiveLimit, ThrottleReasons FROM Win32_PerfRawData_Counters_ThermalZoneInformation") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - c.miQuery = miQuery - c.miSession = miSession - + c.wmiClient = wmiClient c.temperature = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "temperature_celsius"), "(Temperature)", @@ -117,20 +108,22 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan // Win32_PerfRawData_Counters_ThermalZoneInformation docs: // https://wutils.com/wmi/root/cimv2/win32_perfrawdata_counters_thermalzoneinformation/ type Win32_PerfRawData_Counters_ThermalZoneInformation struct { - Name string `mi:"Name"` - HighPrecisionTemperature uint32 `mi:"HighPrecisionTemperature"` - PercentPassiveLimit uint32 `mi:"PercentPassiveLimit"` - ThrottleReasons uint32 `mi:"ThrottleReasons"` + Name string + + HighPrecisionTemperature uint32 + PercentPassiveLimit uint32 + ThrottleReasons uint32 } func (c *Collector) collect(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_Counters_ThermalZoneInformation - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQuery); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_Counters_ThermalZoneInformation", &dst); err != nil { + return err } + // ThermalZone collector has been known to 'successfully' return an empty result. if len(dst) == 0 { - return errors.New("WMI query returned empty result set") + return errors.New("empty results set for collector") } for _, info := range dst { diff --git a/internal/collector/time/time.go b/internal/collector/time/time.go index c955bbc85..7b0b84874 100644 --- a/internal/collector/time/time.go +++ b/internal/collector/time/time.go @@ -9,10 +9,10 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/prometheus-community/windows_exporter/internal/headers/kernel32" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sys/windows" ) @@ -64,7 +64,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { c.currentTime = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "current_timestamp_seconds"), "OperatingSystem.LocalDateTime", diff --git a/internal/collector/update/update.go b/internal/collector/update/update.go index 969c050c3..f32591c0d 100644 --- a/internal/collector/update/update.go +++ b/internal/collector/update/update.go @@ -15,9 +15,9 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "update" @@ -80,7 +80,7 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error { +func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error { logger = logger.With(slog.String("collector", Name)) logger.Info("update collector is in an experimental state! The configuration and metrics may change in future. Please report any issues.") @@ -147,7 +147,7 @@ func (c *Collector) scheduleUpdateStatus(logger *slog.Logger, initErrCh chan<- e if err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED); err != nil { var oleCode *ole.OleError - if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != 0x00000001 { + if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != wmi.S_FALSE { initErrCh <- fmt.Errorf("CoInitializeEx: %w", err) return diff --git a/internal/collector/vmware/vmware.go b/internal/collector/vmware/vmware.go index 812fe8ec2..ba5d0ccf9 100644 --- a/internal/collector/vmware/vmware.go +++ b/internal/collector/vmware/vmware.go @@ -4,14 +4,13 @@ package vmware import ( "errors" - "fmt" "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) const Name = "vmware" @@ -22,10 +21,8 @@ var ConfigDefaults = Config{} // A Collector is a Prometheus Collector for WMI Win32_PerfRawData_vmGuestLib_VMem/Win32_PerfRawData_vmGuestLib_VCPU metrics. type Collector struct { - config Config - miSession *mi.Session - miQueryCPU mi.Query - miQueryMem mi.Query + config Config + wmiClient *wmi.Client memActive *prometheus.Desc memBallooned *prometheus.Desc @@ -77,25 +74,12 @@ func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error { - if miSession == nil { - return errors.New("miSession is nil") +func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error { + if wmiClient == nil || wmiClient.SWbemServicesClient == nil { + return errors.New("wmiClient or SWbemServicesClient is nil") } - miQuery, err := mi.NewQuery("SELECT * FROM Win32_PerfRawData_vmGuestLib_VCPU") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - c.miQueryCPU = miQuery - - miQuery, err = mi.NewQuery("SELECT * FROM Win32_PerfRawData_vmGuestLib_VMem") - if err != nil { - return fmt.Errorf("failed to create WMI query: %w", err) - } - - c.miQueryMem = miQuery - c.miSession = miSession + c.wmiClient = wmiClient c.memActive = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "mem_active_bytes"), @@ -240,34 +224,34 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan } type Win32_PerfRawData_vmGuestLib_VMem struct { - MemActiveMB uint64 `mi:"MemActiveMB"` - MemBalloonedMB uint64 `mi:"MemBalloonedMB"` - MemLimitMB uint64 `mi:"MemLimitMB"` - MemMappedMB uint64 `mi:"MemMappedMB"` - MemOverheadMB uint64 `mi:"MemOverheadMB"` - MemReservationMB uint64 `mi:"MemReservationMB"` - MemSharedMB uint64 `mi:"MemSharedMB"` - MemSharedSavedMB uint64 `mi:"MemSharedSavedMB"` - MemShares uint64 `mi:"MemShares"` - MemSwappedMB uint64 `mi:"MemSwappedMB"` - MemTargetSizeMB uint64 `mi:"MemTargetSizeMB"` - MemUsedMB uint64 `mi:"MemUsedMB"` + MemActiveMB uint64 + MemBalloonedMB uint64 + MemLimitMB uint64 + MemMappedMB uint64 + MemOverheadMB uint64 + MemReservationMB uint64 + MemSharedMB uint64 + MemSharedSavedMB uint64 + MemShares uint64 + MemSwappedMB uint64 + MemTargetSizeMB uint64 + MemUsedMB uint64 } type Win32_PerfRawData_vmGuestLib_VCPU struct { - CpuLimitMHz uint64 `mi:"CpuLimitMHz"` - CpuReservationMHz uint64 `mi:"CpuReservationMHz"` - CpuShares uint64 `mi:"CpuShares"` - CpuStolenMs uint64 `mi:"CpuStolenMs"` - CpuTimePercents uint64 `mi:"CpuTimePercents"` - EffectiveVMSpeedMHz uint64 `mi:"EffectiveVMSpeedMHz"` - HostProcessorSpeedMHz uint64 `mi:"HostProcessorSpeedMHz"` + CpuLimitMHz uint64 + CpuReservationMHz uint64 + CpuShares uint64 + CpuStolenMs uint64 + CpuTimePercents uint64 + EffectiveVMSpeedMHz uint64 + HostProcessorSpeedMHz uint64 } func (c *Collector) collectMem(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_vmGuestLib_VMem - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryMem); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_vmGuestLib_VMem", &dst); err != nil { + return err } if len(dst) == 0 { @@ -349,15 +333,14 @@ func (c *Collector) collectMem(ch chan<- prometheus.Metric) error { return nil } -// mbToBytes moved to utils package func mbToBytes(mb uint64) float64 { return float64(mb * 1024 * 1024) } func (c *Collector) collectCpu(ch chan<- prometheus.Metric) error { var dst []Win32_PerfRawData_vmGuestLib_VCPU - if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryCPU); err != nil { - return fmt.Errorf("WMI query failed: %w", err) + if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_vmGuestLib_VCPU", &dst); err != nil { + return err } if len(dst) == 0 { diff --git a/internal/httphandler/httphandler.go b/internal/httphandler/httphandler.go index d587d05c7..8efa33203 100644 --- a/internal/httphandler/httphandler.go +++ b/internal/httphandler/httphandler.go @@ -129,7 +129,7 @@ func (c *MetricsHTTPHandler) handlerFactory(logger *slog.Logger, scrapeTimeout t metricCollectors = &collector.MetricCollectors{ Collectors: filteredCollectors, - MISession: c.metricCollectors.MISession, + WMIClient: c.metricCollectors.WMIClient, PerfCounterQuery: c.metricCollectors.PerfCounterQuery, } } diff --git a/internal/mi/application.go b/internal/mi/application.go deleted file mode 100644 index 851241abd..000000000 --- a/internal/mi/application.go +++ /dev/null @@ -1,283 +0,0 @@ -//go:build windows - -package mi - -import ( - "errors" - "fmt" - "syscall" - "time" - "unsafe" - - "golang.org/x/sys/windows" -) - -const ( - applicationID = "windows_exporter" - - LocaleEnglish = "en-us" -) - -var ( - // DestinationOptionsTimeout is the key for the timeout option. - // - // https://github.com/microsoft/win32metadata/blob/527806d20d83d3abd43d16cd3fa8795d8deba343/generation/WinSDK/RecompiledIdlHeaders/um/mi.h#L7830 - DestinationOptionsTimeout = UTF16PtrFromString[*uint16]("__MI_DESTINATIONOPTIONS_TIMEOUT") - - // DestinationOptionsUILocale is the key for the UI locale option. - // - // https://github.com/microsoft/win32metadata/blob/527806d20d83d3abd43d16cd3fa8795d8deba343/generation/WinSDK/RecompiledIdlHeaders/um/mi.h#L8248 - DestinationOptionsUILocale = UTF16PtrFromString[*uint16]("__MI_DESTINATIONOPTIONS_UI_LOCALE") -) - -var ( - modMi = windows.NewLazySystemDLL("mi.dll") - - procMIApplicationInitialize = modMi.NewProc("MI_Application_InitializeV1") -) - -// Application represents the MI application. -// https://learn.microsoft.com/de-de/windows/win32/api/mi/ns-mi-mi_application -type Application struct { - reserved1 uint64 - reserved2 uintptr - ft *ApplicationFT -} - -// ApplicationFT represents the function table of the MI application. -// https://learn.microsoft.com/de-de/windows/win32/api/mi/ns-mi-mi_applicationft -type ApplicationFT struct { - Close uintptr - NewSession uintptr - NewHostedProvider uintptr - NewInstance uintptr - NewDestinationOptions uintptr - NewOperationOptions uintptr - NewSubscriptionDeliveryOptions uintptr - NewSerializer uintptr - NewDeserializer uintptr - NewInstanceFromClass uintptr - NewClass uintptr -} - -type DestinationOptions struct { - reserved1 uint64 - reserved2 uintptr - ft *DestinationOptionsFT -} - -type DestinationOptionsFT struct { - Delete uintptr - SetString uintptr - SetNumber uintptr - AddCredentials uintptr - GetString uintptr - GetNumber uintptr - GetOptionCount uintptr - GetOptionAt uintptr - GetOption uintptr - GetCredentialsCount uintptr - GetCredentialsAt uintptr - GetCredentialsPasswordAt uintptr - Clone uintptr - SetInterval uintptr - GetInterval uintptr -} - -// Application_Initialize initializes the MI [Application]. -// It is recommended to have only one Application per process. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_initializev1 -func Application_Initialize() (*Application, error) { - application := &Application{} - - applicationId, err := windows.UTF16PtrFromString(applicationID) - if err != nil { - return nil, err - } - - r0, _, err := procMIApplicationInitialize.Call( - 0, - uintptr(unsafe.Pointer(applicationId)), - 0, - uintptr(unsafe.Pointer(application)), - ) - - if !errors.Is(err, windows.NOERROR) { - return nil, fmt.Errorf("syscall returned: %w", err) - } - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, result - } - - return application, nil -} - -// Close deinitializes the management infrastructure client API that was initialized through a call to Application_Initialize. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_close -func (application *Application) Close() error { - if application == nil || application.ft == nil { - return ErrNotInitialized - } - - r0, _, _ := syscall.SyscallN(application.ft.Close, uintptr(unsafe.Pointer(application))) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -// NewSession creates a session used to share connections for a set of operations to a single destination. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_newsession -func (application *Application) NewSession(options *DestinationOptions) (*Session, error) { - if application == nil || application.ft == nil { - return nil, ErrNotInitialized - } - - session := &Session{} - - r0, _, _ := syscall.SyscallN( - application.ft.NewSession, - uintptr(unsafe.Pointer(application)), - 0, - 0, - uintptr(unsafe.Pointer(options)), - 0, - 0, - uintptr(unsafe.Pointer(session)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, result - } - - defaultOperationOptions, err := application.NewOperationOptions() - if err != nil { - return nil, fmt.Errorf("failed to create default operation options: %w", err) - } - - if err = defaultOperationOptions.SetTimeout(5 * time.Second); err != nil { - return nil, fmt.Errorf("failed to set timeout: %w", err) - } - - session.defaultOperationOptions = defaultOperationOptions - - return session, nil -} - -// NewOperationOptions creates an OperationOptions object that can be used with the operation functions on the Session object. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_newoperationoptions -func (application *Application) NewOperationOptions() (*OperationOptions, error) { - if application == nil || application.ft == nil { - return nil, ErrNotInitialized - } - - operationOptions := &OperationOptions{} - mustUnderstand := True - - r0, _, _ := syscall.SyscallN( - application.ft.NewOperationOptions, - uintptr(unsafe.Pointer(application)), - uintptr(mustUnderstand), - uintptr(unsafe.Pointer(operationOptions)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, result - } - - return operationOptions, nil -} - -// NewDestinationOptions creates an DestinationOptions object that can be used with the Application.NewSession function. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_application_newdestinationoptions -func (application *Application) NewDestinationOptions() (*DestinationOptions, error) { - if application == nil || application.ft == nil { - return nil, ErrNotInitialized - } - - operationOptions := &DestinationOptions{} - - r0, _, _ := syscall.SyscallN( - application.ft.NewDestinationOptions, - uintptr(unsafe.Pointer(application)), - uintptr(unsafe.Pointer(operationOptions)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, result - } - - return operationOptions, nil -} - -// SetTimeout sets the timeout for the destination options. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_destinationoptions_settimeout -func (do *DestinationOptions) SetTimeout(timeout time.Duration) error { - if do == nil || do.ft == nil { - return ErrNotInitialized - } - - r0, _, _ := syscall.SyscallN( - do.ft.SetInterval, - uintptr(unsafe.Pointer(do)), - uintptr(unsafe.Pointer(DestinationOptionsTimeout)), - uintptr(unsafe.Pointer(NewInterval(timeout))), - 0, - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -// SetLocale sets the locale for the destination options. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_destinationoptions_setuilocale -func (do *DestinationOptions) SetLocale(locale string) error { - if do == nil || do.ft == nil { - return ErrNotInitialized - } - - localeUTF16, err := windows.UTF16PtrFromString(locale) - if err != nil { - return fmt.Errorf("failed to convert locale: %w", err) - } - - r0, _, _ := syscall.SyscallN( - do.ft.SetString, - uintptr(unsafe.Pointer(do)), - uintptr(unsafe.Pointer(DestinationOptionsUILocale)), - uintptr(unsafe.Pointer(localeUTF16)), - 0, - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -func (do *DestinationOptions) Delete() error { - r0, _, _ := syscall.SyscallN( - do.ft.Delete, - uintptr(unsafe.Pointer(do)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} diff --git a/internal/mi/callbacks.go b/internal/mi/callbacks.go deleted file mode 100644 index a07bc4bf8..000000000 --- a/internal/mi/callbacks.go +++ /dev/null @@ -1,143 +0,0 @@ -//go:build windows - -package mi - -import ( - "errors" - "fmt" - "reflect" - "unsafe" - - "golang.org/x/sys/windows" -) - -var operationUnmarshalCallbacksInstanceResult = windows.NewCallback(func( - operation *Operation, - callbacks *OperationUnmarshalCallbacks, - instance *Instance, - moreResults Boolean, - instanceResult ResultError, - errorMessageUTF16 *uint16, - errorDetails *Instance, - _ uintptr, -) uintptr { - return callbacks.InstanceResult(operation, instance, moreResults, instanceResult, errorMessageUTF16, errorDetails) -}) - -type OperationUnmarshalCallbacks struct { - dst any - dv reflect.Value - errCh chan<- error - - elemType reflect.Type - elemValue reflect.Value -} - -func NewOperationUnmarshalCallbacks(dst any, errCh chan<- error) (*OperationUnmarshalCallbacks, error) { - dv := reflect.ValueOf(dst) - if dv.Kind() != reflect.Ptr || dv.IsNil() { - return nil, ErrInvalidEntityType - } - - dv = dv.Elem() - - elemType := dv.Type().Elem() - elemValue := reflect.ValueOf(reflect.New(elemType).Interface()).Elem() - - if dv.Kind() != reflect.Slice || elemType.Kind() != reflect.Struct { - return nil, ErrInvalidEntityType - } - - dv.Set(reflect.MakeSlice(dv.Type(), 0, 0)) - - return &OperationUnmarshalCallbacks{ - errCh: errCh, - dst: dst, - dv: dv, - elemType: elemType, - elemValue: elemValue, - }, nil -} - -func (o *OperationUnmarshalCallbacks) InstanceResult( - _ *Operation, - instance *Instance, - moreResults Boolean, - instanceResult ResultError, - errorMessageUTF16 *uint16, - _ *Instance, -) uintptr { - defer func() { - if moreResults == False { - close(o.errCh) - } - }() - - if !errors.Is(instanceResult, MI_RESULT_OK) { - o.errCh <- fmt.Errorf("%w: %s", instanceResult, windows.UTF16PtrToString(errorMessageUTF16)) - - return 0 - } - - if instance == nil { - return 0 - } - - counter, err := instance.GetElementCount() - if err != nil { - o.errCh <- fmt.Errorf("failed to get element count: %w", err) - - return 0 - } - - if counter == 0 { - return 0 - } - - for i := range o.elemType.NumField() { - field := o.elemValue.Field(i) - - // Check if the field has an `mi` tag - miTag := o.elemType.Field(i).Tag.Get("mi") - if miTag == "" { - continue - } - - element, err := instance.GetElement(miTag) - if err != nil { - o.errCh <- fmt.Errorf("failed to get element: %w", err) - - return 0 - } - - switch element.valueType { - case ValueTypeBOOLEAN: - field.SetBool(element.value == 1) - case ValueTypeUINT8, ValueTypeUINT16, ValueTypeUINT32, ValueTypeUINT64: - field.SetUint(uint64(element.value)) - case ValueTypeSINT8, ValueTypeSINT16, ValueTypeSINT32, ValueTypeSINT64: - field.SetInt(int64(element.value)) - case ValueTypeSTRING: - if element.value == 0 { - o.errCh <- fmt.Errorf("%s: invalid pointer: value is nil", miTag) - - return 0 - } - - // Convert the UTF-16 string to a Go string - stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value))) - - field.SetString(stringValue) - case ValueTypeREAL32, ValueTypeREAL64: - field.SetFloat(float64(element.value)) - default: - o.errCh <- fmt.Errorf("unsupported value type: %d", element.valueType) - - return 0 - } - } - - o.dv.Set(reflect.Append(o.dv, o.elemValue)) - - return 0 -} diff --git a/internal/mi/doc.go b/internal/mi/doc.go deleted file mode 100644 index 49ac0ca72..000000000 --- a/internal/mi/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build windows - -// mi is a package that provides a Go API for Windows Management Infrastructure (MI) functions. -// It requires Windows Management Framework 3.0 or later. -// -// https://learn.microsoft.com/de-de/previous-versions/windows/desktop/wmi_v2/why-use-mi- -package mi diff --git a/internal/mi/errors.go b/internal/mi/errors.go deleted file mode 100644 index c6abbcd7d..000000000 --- a/internal/mi/errors.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build windows - -package mi - -import "errors" - -var ( - ErrNotInitialized = errors.New("not initialized") - ErrInvalidEntityType = errors.New("invalid entity type") -) diff --git a/internal/mi/instance.go b/internal/mi/instance.go deleted file mode 100644 index 90eebe493..000000000 --- a/internal/mi/instance.go +++ /dev/null @@ -1,179 +0,0 @@ -//go:build windows - -package mi - -import ( - "errors" - "fmt" - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -type Instance struct { - ft *InstanceFT - classDecl *ClassDecl - serverName *uint16 - nameSpace *uint16 - _ [4]uintptr -} - -type InstanceFT struct { - Clone uintptr - Destruct uintptr - Delete uintptr - IsA uintptr - GetClassName uintptr - SetNameSpace uintptr - GetNameSpace uintptr - GetElementCount uintptr - AddElement uintptr - SetElement uintptr - SetElementAt uintptr - GetElement uintptr - GetElementAt uintptr - ClearElement uintptr - ClearElementAt uintptr - GetServerName uintptr - SetServerName uintptr - GetClass uintptr -} - -type ClassDecl struct { - Flags uint32 - Code uint32 - Name *uint16 - Mqualifiers uintptr - NumQualifiers uint32 - Mproperties uintptr - NumProperties uint32 - Size uint32 - SuperClass *uint16 - SuperClassDecl uintptr - Methods uintptr - NumMethods uint32 - - Schema uintptr - ProviderFT uintptr - OwningClass uintptr -} - -func (instance *Instance) Delete() error { - if instance == nil || instance.ft == nil { - return ErrNotInitialized - } - - r0, _, _ := syscall.SyscallN(instance.ft.Delete, uintptr(unsafe.Pointer(instance))) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -func (instance *Instance) GetElement(elementName string) (*Element, error) { - if instance == nil || instance.ft == nil { - return nil, ErrNotInitialized - } - - elementNameUTF16, err := windows.UTF16PtrFromString(elementName) - if err != nil { - return nil, fmt.Errorf("failed to convert element name %s to UTF-16: %w", elementName, err) - } - - var ( - value uintptr - valueType ValueType - ) - - r0, _, _ := syscall.SyscallN( - instance.ft.GetElement, - uintptr(unsafe.Pointer(instance)), - uintptr(unsafe.Pointer(elementNameUTF16)), - uintptr(unsafe.Pointer(&value)), - uintptr(unsafe.Pointer(&valueType)), - 0, - 0, - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, result - } - - return &Element{ - value: value, - valueType: valueType, - }, nil -} - -func (instance *Instance) GetElementCount() (uint32, error) { - if instance == nil || instance.ft == nil { - return 0, ErrNotInitialized - } - - var count uint32 - - r0, _, _ := syscall.SyscallN( - instance.ft.GetElementCount, - uintptr(unsafe.Pointer(instance)), - uintptr(unsafe.Pointer(&count)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return 0, result - } - - return count, nil -} - -func (instance *Instance) GetClassName() (string, error) { - if instance == nil || instance.ft == nil { - return "", ErrNotInitialized - } - - var classNameUTF16 *uint16 - - r0, _, _ := syscall.SyscallN( - instance.ft.GetClassName, - uintptr(unsafe.Pointer(instance)), - uintptr(unsafe.Pointer(&classNameUTF16)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return "", result - } - - if classNameUTF16 == nil { - return "", errors.New("class name is nil") - } - - return windows.UTF16PtrToString(classNameUTF16), nil -} - -func Instance_Print(instance *Instance) (string, error) { - elementMap := map[string]any{} - - properties := instance.classDecl.Properties() - - count, err := instance.GetElementCount() - if err != nil { - return "", err - } - - if count == 0 { - return "", nil - } - - for _, property := range properties { - name := windows.UTF16PtrToString(property.Name) - - element, _ := instance.GetElement(name) - value, _ := element.GetValue() - - elementMap[windows.UTF16PtrToString(property.Name)] = value - } - - return fmt.Sprintf("%v", elementMap), nil -} diff --git a/internal/mi/mi_bench_test.go b/internal/mi/mi_bench_test.go deleted file mode 100644 index c6f1ea589..000000000 --- a/internal/mi/mi_bench_test.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build windows - -package mi_test - -import ( - "testing" - - "github.com/prometheus-community/windows_exporter/internal/mi" - "github.com/stretchr/testify/require" -) - -func Benchmark_MI_Query_Unmarshal(b *testing.B) { - application, err := mi.Application_Initialize() - require.NoError(b, err) - require.NotEmpty(b, application) - - session, err := application.NewSession(nil) - require.NoError(b, err) - require.NotEmpty(b, session) - - b.ResetTimer() - - var processes []win32Process - - query, err := mi.NewQuery("SELECT Name FROM Win32_Process WHERE Handle = 0 OR Handle = 4") - require.NoError(b, err) - - for i := 0; i < b.N; i++ { - err := session.QueryUnmarshal(&processes, mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, query) - require.NoError(b, err) - require.Equal(b, []win32Process{{Name: "System Idle Process"}, {Name: "System"}}, processes) - } - - b.StopTimer() - - err = session.Close() - require.NoError(b, err) - - err = application.Close() - require.NoError(b, err) - - b.ReportAllocs() -} diff --git a/internal/mi/mi_test.go b/internal/mi/mi_test.go deleted file mode 100644 index d457753bb..000000000 --- a/internal/mi/mi_test.go +++ /dev/null @@ -1,294 +0,0 @@ -//go:build windows - -package mi_test - -import ( - "testing" - "time" - - "github.com/prometheus-community/windows_exporter/internal/mi" - "github.com/prometheus-community/windows_exporter/internal/testutils" - "github.com/stretchr/testify/require" - "golang.org/x/sys/windows" -) - -type win32Process struct { - Name string `mi:"Name"` -} - -type wmiPrinter struct { - Name string `mi:"Name"` - Default bool `mi:"Default"` - PrinterStatus uint16 `mi:"PrinterStatus"` - JobCountSinceLastReset uint32 `mi:"JobCountSinceLastReset"` -} - -type wmiPrintJob struct { - Name string `mi:"Name"` - Status string `mi:"Status"` -} - -func Test_MI_Application_Initialize(t *testing.T) { - application, err := mi.Application_Initialize() - require.NoError(t, err) - require.NotEmpty(t, application) - - err = application.Close() - require.NoError(t, err) -} - -func Test_MI_Application_TestConnection(t *testing.T) { - application, err := mi.Application_Initialize() - require.NoError(t, err) - require.NotEmpty(t, application) - - destinationOptions, err := application.NewDestinationOptions() - require.NoError(t, err) - require.NotEmpty(t, destinationOptions) - - err = destinationOptions.SetTimeout(1 * time.Second) - require.NoError(t, err) - - err = destinationOptions.SetLocale(mi.LocaleEnglish) - require.NoError(t, err) - - session, err := application.NewSession(destinationOptions) - require.NoError(t, err) - require.NotEmpty(t, session) - - err = session.TestConnection() - require.NoError(t, err) - require.NotEmpty(t, session) - - err = session.Close() - require.NoError(t, err) - - err = application.Close() - require.NoError(t, err) -} - -func Test_MI_Query(t *testing.T) { - application, err := mi.Application_Initialize() - require.NoError(t, err) - require.NotEmpty(t, application) - - destinationOptions, err := application.NewDestinationOptions() - require.NoError(t, err) - require.NotEmpty(t, destinationOptions) - - err = destinationOptions.SetTimeout(1 * time.Second) - require.NoError(t, err) - - err = destinationOptions.SetLocale(mi.LocaleEnglish) - require.NoError(t, err) - - session, err := application.NewSession(destinationOptions) - require.NoError(t, err) - require.NotEmpty(t, session) - - operation, err := session.QueryInstances(mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, "select Name from win32_process where handle = 0") - - require.NoError(t, err) - require.NotEmpty(t, operation) - - instance, moreResults, err := operation.GetInstance() - require.NoError(t, err) - require.NotEmpty(t, instance) - - count, err := instance.GetElementCount() - require.NoError(t, err) - require.NotZero(t, count) - - element, err := instance.GetElement("Name") - require.NoError(t, err) - require.NotEmpty(t, element) - - value, err := element.GetValue() - require.NoError(t, err) - require.Equal(t, "System Idle Process", value) - require.NotEmpty(t, value) - - require.False(t, moreResults) - - err = operation.Close() - require.NoError(t, err) - - err = session.Close() - require.NoError(t, err) - - err = application.Close() - require.NoError(t, err) -} - -func Test_MI_QueryUnmarshal(t *testing.T) { - application, err := mi.Application_Initialize() - require.NoError(t, err) - require.NotEmpty(t, application) - - destinationOptions, err := application.NewDestinationOptions() - require.NoError(t, err) - require.NotEmpty(t, destinationOptions) - - err = destinationOptions.SetTimeout(1 * time.Second) - require.NoError(t, err) - - err = destinationOptions.SetLocale(mi.LocaleEnglish) - require.NoError(t, err) - - session, err := application.NewSession(destinationOptions) - require.NoError(t, err) - require.NotEmpty(t, session) - - var processes []win32Process - - queryProcess, err := mi.NewQuery("select Name from win32_process where handle = 0") - require.NoError(t, err) - - err = session.QueryUnmarshal(&processes, mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, queryProcess) - require.NoError(t, err) - require.Equal(t, []win32Process{{Name: "System Idle Process"}}, processes) - - err = session.Close() - require.NoError(t, err) - - err = application.Close() - require.NoError(t, err) -} - -func Test_MI_EmptyQuery(t *testing.T) { - application, err := mi.Application_Initialize() - require.NoError(t, err) - require.NotEmpty(t, application) - - destinationOptions, err := application.NewDestinationOptions() - require.NoError(t, err) - require.NotEmpty(t, destinationOptions) - - err = destinationOptions.SetTimeout(1 * time.Second) - require.NoError(t, err) - - err = destinationOptions.SetLocale(mi.LocaleEnglish) - require.NoError(t, err) - - session, err := application.NewSession(destinationOptions) - require.NoError(t, err) - require.NotEmpty(t, session) - - operation, err := session.QueryInstances(mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, "SELECT Name, Status FROM win32_PrintJob") - - require.NoError(t, err) - require.NotEmpty(t, operation) - - instance, moreResults, err := operation.GetInstance() - require.NoError(t, err) - require.Empty(t, instance) - require.False(t, moreResults) - - err = operation.Close() - require.NoError(t, err) - - err = session.Close() - require.NoError(t, err) - - err = application.Close() - require.NoError(t, err) -} - -func Test_MI_Query_Unmarshal(t *testing.T) { - application, err := mi.Application_Initialize() - require.NoError(t, err) - require.NotEmpty(t, application) - - destinationOptions, err := application.NewDestinationOptions() - require.NoError(t, err) - require.NotEmpty(t, destinationOptions) - - err = destinationOptions.SetTimeout(1 * time.Second) - require.NoError(t, err) - - err = destinationOptions.SetLocale(mi.LocaleEnglish) - require.NoError(t, err) - - session, err := application.NewSession(destinationOptions) - require.NoError(t, err) - require.NotEmpty(t, session) - - operation, err := session.QueryInstances(mi.OperationFlagsStandardRTTI, nil, mi.NamespaceRootCIMv2, mi.QueryDialectWQL, "SELECT Name FROM Win32_Process WHERE Handle = 0 OR Handle = 4") - - require.NoError(t, err) - require.NotEmpty(t, operation) - - var processes []win32Process - - err = operation.Unmarshal(&processes) - require.NoError(t, err) - require.Equal(t, []win32Process{{Name: "System Idle Process"}, {Name: "System"}}, processes) - - err = operation.Close() - require.NoError(t, err) - - err = session.Close() - require.NoError(t, err) - - err = application.Close() - require.NoError(t, err) -} - -func Test_MI_FD_Leak(t *testing.T) { - t.Skip("This test is disabled because it is not deterministic and may fail on some systems.") - - application, err := mi.Application_Initialize() - require.NoError(t, err) - require.NotEmpty(t, application) - - session, err := application.NewSession(nil) - require.NoError(t, err) - require.NotEmpty(t, session) - - currentFileHandle, err := testutils.GetProcessHandleCount(windows.CurrentProcess()) - require.NoError(t, err) - - t.Log("Current File Handle Count: ", currentFileHandle) - - queryPrinter, err := mi.NewQuery("SELECT Name, Default, PrinterStatus, JobCountSinceLastReset FROM win32_Printer") - require.NoError(t, err) - - queryPrinterJob, err := mi.NewQuery("SELECT Name, Status FROM win32_PrintJob") - require.NoError(t, err) - - for range 1000 { - var wmiPrinters []wmiPrinter - err := session.Query(&wmiPrinters, mi.NamespaceRootCIMv2, queryPrinter) - require.NoError(t, err) - - var wmiPrintJobs []wmiPrintJob - err = session.Query(&wmiPrintJobs, mi.NamespaceRootCIMv2, queryPrinterJob) - require.NoError(t, err) - - currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess()) - require.NoError(t, err) - - t.Log("Current File Handle Count: ", currentFileHandle) - } - - currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess()) - require.NoError(t, err) - - t.Log("Current File Handle Count: ", currentFileHandle) - - err = session.Close() - require.NoError(t, err) - - currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess()) - require.NoError(t, err) - - t.Log("Current File Handle Count: ", currentFileHandle) - - err = application.Close() - require.NoError(t, err) - - currentFileHandle, err = testutils.GetProcessHandleCount(windows.CurrentProcess()) - require.NoError(t, err) - - t.Log("Current File Handle Count: ", currentFileHandle) -} diff --git a/internal/mi/operation.go b/internal/mi/operation.go deleted file mode 100644 index 470b997a7..000000000 --- a/internal/mi/operation.go +++ /dev/null @@ -1,270 +0,0 @@ -//go:build windows - -package mi - -import ( - "errors" - "fmt" - "reflect" - "syscall" - "time" - "unsafe" - - "golang.org/x/sys/windows" -) - -// OperationOptionsTimeout is the key for the timeout option. -// -// https://github.com/microsoft/win32metadata/blob/527806d20d83d3abd43d16cd3fa8795d8deba343/generation/WinSDK/RecompiledIdlHeaders/um/mi.h#L9240 -var OperationOptionsTimeout = UTF16PtrFromString[*uint16]("__MI_OPERATIONOPTIONS_TIMEOUT") - -// OperationFlags represents the flags for an operation. -// -// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/wmi_v2/mi-flags -type OperationFlags uint32 - -const ( - OperationFlagsDefaultRTTI OperationFlags = 0x0000 - OperationFlagsBasicRTTI OperationFlags = 0x0002 - OperationFlagsNoRTTI OperationFlags = 0x0400 - OperationFlagsStandardRTTI OperationFlags = 0x0800 - OperationFlagsFullRTTI OperationFlags = 0x0004 -) - -// Operation represents an operation. -// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_operation -type Operation struct { - reserved1 uint64 - reserved2 uintptr - ft *OperationFT -} - -// OperationFT represents the function table for Operation. -// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_operationft -type OperationFT struct { - Close uintptr - Cancel uintptr - GetSession uintptr - GetInstance uintptr - GetIndication uintptr - GetClass uintptr -} - -type OperationOptions struct { - reserved1 uint64 - reserved2 uintptr - ft *OperationOptionsFT -} - -type OperationOptionsFT struct { - Delete uintptr - SetString uintptr - SetNumber uintptr - SetCustomOption uintptr - GetString uintptr - GetNumber uintptr - GetOptionCount uintptr - GetOptionAt uintptr - GetOption uintptr - GetEnabledChannels uintptr - Clone uintptr - SetInterval uintptr - GetInterval uintptr -} - -type OperationCallbacks struct { - CallbackContext uintptr - PromptUser uintptr - WriteError uintptr - WriteMessage uintptr - WriteProgress uintptr - InstanceResult uintptr - IndicationResult uintptr - ClassResult uintptr - StreamedParameterResult uintptr -} - -// Close closes an operation handle. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_operation_close -func (o *Operation) Close() error { - if o == nil || o.ft == nil { - return ErrNotInitialized - } - - r0, _, _ := syscall.SyscallN(o.ft.Close, uintptr(unsafe.Pointer(o))) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -func (o *Operation) Cancel() error { - if o == nil || o.ft == nil { - return ErrNotInitialized - } - - r0, _, _ := syscall.SyscallN(o.ft.Close, uintptr(unsafe.Pointer(o)), 0) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -func (o *Operation) GetInstance() (*Instance, bool, error) { - if o == nil || o.ft == nil { - return nil, false, ErrNotInitialized - } - - var ( - instance *Instance - errorDetails *Instance - moreResults Boolean - instanceResult ResultError - errorMessageUTF16 *uint16 - ) - - r0, _, _ := syscall.SyscallN( - o.ft.GetInstance, - uintptr(unsafe.Pointer(o)), - uintptr(unsafe.Pointer(&instance)), - uintptr(unsafe.Pointer(&moreResults)), - uintptr(unsafe.Pointer(&instanceResult)), - uintptr(unsafe.Pointer(&errorMessageUTF16)), - uintptr(unsafe.Pointer(&errorDetails)), - ) - - if !errors.Is(instanceResult, MI_RESULT_OK) { - return nil, false, fmt.Errorf("instance result: %w (%s)", instanceResult, windows.UTF16PtrToString(errorMessageUTF16)) - } - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, false, result - } - - return instance, moreResults == True, nil -} - -func (o *Operation) Unmarshal(dst any) error { - if o == nil || o.ft == nil { - return ErrNotInitialized - } - - dv := reflect.ValueOf(dst) - if dv.Kind() != reflect.Ptr || dv.IsNil() { - return ErrInvalidEntityType - } - - dv = dv.Elem() - - elemType := dv.Type().Elem() - elemValue := reflect.ValueOf(reflect.New(elemType).Interface()).Elem() - - if dv.Kind() != reflect.Slice || elemType.Kind() != reflect.Struct { - return ErrInvalidEntityType - } - - dv.Set(reflect.MakeSlice(dv.Type(), 0, 0)) - - for { - instance, moreResults, err := o.GetInstance() - if err != nil { - return fmt.Errorf("failed to get instance: %w", err) - } - - // If WMI returns nil, it means there are no more results. - if instance == nil { - break - } - - counter, err := instance.GetElementCount() - if err != nil { - return fmt.Errorf("failed to get element count: %w", err) - } - - if counter == 0 { - break - } - - for i := range elemType.NumField() { - field := elemValue.Field(i) - - // Check if the field has an `mi` tag - miTag := elemType.Field(i).Tag.Get("mi") - if miTag == "" { - continue - } - - element, err := instance.GetElement(miTag) - if err != nil { - return fmt.Errorf("failed to get element: %w", err) - } - - switch element.valueType { - case ValueTypeBOOLEAN: - field.SetBool(element.value == 1) - case ValueTypeUINT8, ValueTypeUINT16, ValueTypeUINT32, ValueTypeUINT64: - field.SetUint(uint64(element.value)) - case ValueTypeSINT8, ValueTypeSINT16, ValueTypeSINT32, ValueTypeSINT64: - field.SetInt(int64(element.value)) - case ValueTypeSTRING: - if element.value == 0 { - return fmt.Errorf("%s: invalid pointer: value is nil", miTag) - } - - // Convert the UTF-16 string to a Go string - stringValue := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(element.value))) - - field.SetString(stringValue) - case ValueTypeREAL32, ValueTypeREAL64: - field.SetFloat(float64(element.value)) - } - } - - dv.Set(reflect.Append(dv, elemValue)) - - if !moreResults { - break - } - } - - return nil -} - -func (o *OperationOptions) SetTimeout(timeout time.Duration) error { - if o == nil || o.ft == nil { - return ErrNotInitialized - } - - r0, _, _ := syscall.SyscallN( - o.ft.SetInterval, - uintptr(unsafe.Pointer(o)), - uintptr(unsafe.Pointer(OperationOptionsTimeout)), - uintptr(unsafe.Pointer(NewInterval(timeout))), - 0, - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -func (o *OperationOptions) Delete() error { - if o == nil || o.ft == nil { - return ErrNotInitialized - } - - r0, _, _ := syscall.SyscallN(o.ft.Delete, uintptr(unsafe.Pointer(o))) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} diff --git a/internal/mi/result.go b/internal/mi/result.go deleted file mode 100644 index 209ea172b..000000000 --- a/internal/mi/result.go +++ /dev/null @@ -1,102 +0,0 @@ -//go:build windows - -package mi - -import "errors" - -type ResultError uint32 - -const ( - MI_RESULT_OK ResultError = iota - MI_RESULT_FAILED - MI_RESULT_ACCESS_DENIED - MI_RESULT_INVALID_NAMESPACE - MI_RESULT_INVALID_PARAMETER - MI_RESULT_INVALID_CLASS - MI_RESULT_NOT_FOUND - MI_RESULT_NOT_SUPPORTED - MI_RESULT_CLASS_HAS_CHILDREN - MI_RESULT_CLASS_HAS_INSTANCES - MI_RESULT_INVALID_SUPERCLASS - MI_RESULT_ALREADY_EXISTS - MI_RESULT_NO_SUCH_PROPERTY - MI_RESULT_TYPE_MISMATCH - MI_RESULT_QUERY_LANGUAGE_NOT_SUPPORTED - MI_RESULT_INVALID_QUERY - MI_RESULT_METHOD_NOT_AVAILABLE - MI_RESULT_METHOD_NOT_FOUND - MI_RESULT_NAMESPACE_NOT_EMPTY - MI_RESULT_INVALID_ENUMERATION_CONTEXT - MI_RESULT_INVALID_OPERATION_TIMEOUT - MI_RESULT_PULL_HAS_BEEN_ABANDONED - MI_RESULT_PULL_CANNOT_BE_ABANDONED - MI_RESULT_FILTERED_ENUMERATION_NOT_SUPPORTED - MI_RESULT_CONTINUATION_ON_ERROR_NOT_SUPPORTED - MI_RESULT_SERVER_LIMITS_EXCEEDED - MI_RESULT_SERVER_IS_SHUTTING_DOWN -) - -func (r ResultError) Error() string { - return r.String() -} - -func (r ResultError) String() string { - switch { - case errors.Is(r, MI_RESULT_OK): - return "MI_RESULT_OK" - case errors.Is(r, MI_RESULT_FAILED): - return "MI_RESULT_FAILED" - case errors.Is(r, MI_RESULT_ACCESS_DENIED): - return "MI_RESULT_ACCESS_DENIED" - case errors.Is(r, MI_RESULT_INVALID_NAMESPACE): - return "MI_RESULT_INVALID_NAMESPACE" - case errors.Is(r, MI_RESULT_INVALID_PARAMETER): - return "MI_RESULT_INVALID_PARAMETER" - case errors.Is(r, MI_RESULT_INVALID_CLASS): - return "MI_RESULT_INVALID_CLASS" - case errors.Is(r, MI_RESULT_NOT_FOUND): - return "MI_RESULT_NOT_FOUND" - case errors.Is(r, MI_RESULT_NOT_SUPPORTED): - return "MI_RESULT_NOT_SUPPORTED" - case errors.Is(r, MI_RESULT_CLASS_HAS_CHILDREN): - return "MI_RESULT_CLASS_HAS_CHILDREN" - case errors.Is(r, MI_RESULT_CLASS_HAS_INSTANCES): - return "MI_RESULT_CLASS_HAS_INSTANCES" - case errors.Is(r, MI_RESULT_INVALID_SUPERCLASS): - return "MI_RESULT_INVALID_SUPERCLASS" - case errors.Is(r, MI_RESULT_ALREADY_EXISTS): - return "MI_RESULT_ALREADY_EXISTS" - case errors.Is(r, MI_RESULT_NO_SUCH_PROPERTY): - return "MI_RESULT_NO_SUCH_PROPERTY" - case errors.Is(r, MI_RESULT_TYPE_MISMATCH): - return "MI_RESULT_TYPE_MISMATCH" - case errors.Is(r, MI_RESULT_QUERY_LANGUAGE_NOT_SUPPORTED): - return "MI_RESULT_QUERY_LANGUAGE_NOT_SUPPORTED" - case errors.Is(r, MI_RESULT_INVALID_QUERY): - return "MI_RESULT_INVALID_QUERY" - case errors.Is(r, MI_RESULT_METHOD_NOT_AVAILABLE): - return "MI_RESULT_METHOD_NOT_AVAILABLE" - case errors.Is(r, MI_RESULT_METHOD_NOT_FOUND): - return "MI_RESULT_METHOD_NOT_FOUND" - case errors.Is(r, MI_RESULT_NAMESPACE_NOT_EMPTY): - return "MI_RESULT_NAMESPACE_NOT_EMPTY" - case errors.Is(r, MI_RESULT_INVALID_ENUMERATION_CONTEXT): - return "MI_RESULT_INVALID_ENUMERATION_CONTEXT" - case errors.Is(r, MI_RESULT_INVALID_OPERATION_TIMEOUT): - return "MI_RESULT_INVALID_OPERATION_TIMEOUT" - case errors.Is(r, MI_RESULT_PULL_HAS_BEEN_ABANDONED): - return "MI_RESULT_PULL_HAS_BEEN_ABANDONED" - case errors.Is(r, MI_RESULT_PULL_CANNOT_BE_ABANDONED): - return "MI_RESULT_PULL_CANNOT_BE_ABANDONED" - case errors.Is(r, MI_RESULT_FILTERED_ENUMERATION_NOT_SUPPORTED): - return "MI_RESULT_FILTERED_ENUMERATION_NOT_SUPPORTED" - case errors.Is(r, MI_RESULT_CONTINUATION_ON_ERROR_NOT_SUPPORTED): - return "MI_RESULT_CONTINUATION_ON_ERROR_NOT_SUPPORTED" - case errors.Is(r, MI_RESULT_SERVER_LIMITS_EXCEEDED): - return "MI_RESULT_SERVER_LIMITS_EXCEEDED" - case errors.Is(r, MI_RESULT_SERVER_IS_SHUTTING_DOWN): - return "MI_RESULT_SERVER_IS_SHUTTING_DOWN" - default: - return "MI_RESULT_UNKNOWN" - } -} diff --git a/internal/mi/session.go b/internal/mi/session.go deleted file mode 100644 index e046619ed..000000000 --- a/internal/mi/session.go +++ /dev/null @@ -1,244 +0,0 @@ -//go:build windows - -package mi - -import ( - "errors" - "fmt" - "syscall" - "time" - "unsafe" - - "golang.org/x/sys/windows" -) - -// Session represents a session. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_session -type Session struct { - reserved1 uint64 - reserved2 uintptr - ft *SessionFT - - defaultOperationOptions *OperationOptions -} - -// SessionFT represents the function table for Session. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/ns-mi-mi_session -type SessionFT struct { - Close uintptr - GetApplication uintptr - GetInstance uintptr - ModifyInstance uintptr - CreateInstance uintptr - DeleteInstance uintptr - Invoke uintptr - EnumerateInstances uintptr - QueryInstances uintptr - AssociatorInstances uintptr - ReferenceInstances uintptr - Subscribe uintptr - GetClass uintptr - EnumerateClasses uintptr - TestConnection uintptr -} - -// Close closes a session and releases all associated memory. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_close -func (s *Session) Close() error { - if s == nil || s.ft == nil { - return ErrNotInitialized - } - - if s.defaultOperationOptions != nil { - _ = s.defaultOperationOptions.Delete() - } - - r0, _, _ := syscall.SyscallN(s.ft.Close, - uintptr(unsafe.Pointer(s)), - 0, - 0, - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - return nil -} - -// TestConnection queries instances. It is used to test the connection. -// The function returns an operation that can be used to retrieve the result with [Operation.GetInstance]. The operation must be closed with [Operation.Close]. -// The instance returned by [Operation.GetInstance] is always nil. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_testconnection -func (s *Session) TestConnection() error { - if s == nil || s.ft == nil { - return ErrNotInitialized - } - - operation := &Operation{} - - // ref: https://github.com/KurtDeGreeff/omi/blob/9caa55032a1070a665e14fd282a091f6247d13c3/Unix/scriptext/py/PMI_Session.c#L92-L105 - r0, _, _ := syscall.SyscallN( - s.ft.TestConnection, - uintptr(unsafe.Pointer(s)), - 0, - 0, - uintptr(unsafe.Pointer(operation)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - var err error - - if _, _, err = operation.GetInstance(); err != nil { - return fmt.Errorf("failed to get instance: %w", err) - } - - if err = operation.Close(); err != nil { - return fmt.Errorf("failed to close operation: %w", err) - } - - return nil -} - -// GetApplication gets the Application handle that was used to create the specified session. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_getapplication -func (s *Session) GetApplication() (*Application, error) { - if s == nil || s.ft == nil { - return nil, ErrNotInitialized - } - - application := &Application{} - - r0, _, _ := syscall.SyscallN( - s.ft.GetApplication, - uintptr(unsafe.Pointer(s)), - uintptr(unsafe.Pointer(application)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, result - } - - return application, nil -} - -// QueryInstances queries for a set of instances based on a query expression. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_queryinstances -func (s *Session) QueryInstances(flags OperationFlags, operationOptions *OperationOptions, namespaceName Namespace, - queryDialect QueryDialect, queryExpression string, -) (*Operation, error) { - if s == nil || s.ft == nil { - return nil, ErrNotInitialized - } - - queryExpressionUTF16, err := windows.UTF16PtrFromString(queryExpression) - if err != nil { - return nil, err - } - - operation := &Operation{} - - if operationOptions == nil { - operationOptions = s.defaultOperationOptions - } - - r0, _, _ := syscall.SyscallN( - s.ft.QueryInstances, - uintptr(unsafe.Pointer(s)), - uintptr(flags), - uintptr(unsafe.Pointer(operationOptions)), - uintptr(unsafe.Pointer(namespaceName)), - uintptr(unsafe.Pointer(queryDialect)), - uintptr(unsafe.Pointer(queryExpressionUTF16)), - 0, - uintptr(unsafe.Pointer(operation)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return nil, result - } - - return operation, nil -} - -// QueryUnmarshal queries for a set of instances based on a query expression. -// -// https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_session_queryinstances -func (s *Session) QueryUnmarshal(dst any, - flags OperationFlags, operationOptions *OperationOptions, - namespaceName Namespace, queryDialect QueryDialect, queryExpression Query, -) error { - if s == nil || s.ft == nil { - return ErrNotInitialized - } - - operation := &Operation{} - - if operationOptions == nil { - operationOptions = s.defaultOperationOptions - } - - errCh := make(chan error, 1) - - callbacks, err := NewOperationUnmarshalCallbacks(dst, errCh) - if err != nil { - return err - } - - operationCallbacks := &OperationCallbacks{ - CallbackContext: uintptr(unsafe.Pointer(callbacks)), - InstanceResult: operationUnmarshalCallbacksInstanceResult, - } - - r0, _, _ := syscall.SyscallN( - s.ft.QueryInstances, - uintptr(unsafe.Pointer(s)), - uintptr(flags), - uintptr(unsafe.Pointer(operationOptions)), - uintptr(unsafe.Pointer(namespaceName)), - uintptr(unsafe.Pointer(queryDialect)), - uintptr(unsafe.Pointer(queryExpression)), - uintptr(unsafe.Pointer(operationCallbacks)), - uintptr(unsafe.Pointer(operation)), - ) - - if result := ResultError(r0); !errors.Is(result, MI_RESULT_OK) { - return result - } - - defer operation.Close() - - var errs []error - - for { - select { - case err := <-errCh: - if err != nil { - errs = append(errs, err) - } else { - return errors.Join(errs...) - } - case <-time.After(8500 * time.Millisecond): - return errors.New("timeout") - } - } -} - -// Query queries for a set of instances based on a query expression. -func (s *Session) Query(dst any, namespaceName Namespace, queryExpression Query) error { - err := s.QueryUnmarshal(dst, OperationFlagsStandardRTTI, nil, namespaceName, QueryDialectWQL, queryExpression) - if err != nil { - return fmt.Errorf("WMI query failed: %w", err) - } - - return nil -} diff --git a/internal/mi/types.go b/internal/mi/types.go deleted file mode 100644 index 46db7c41f..000000000 --- a/internal/mi/types.go +++ /dev/null @@ -1,137 +0,0 @@ -//go:build windows - -package mi - -import ( - "time" - "unsafe" - - "github.com/prometheus-community/windows_exporter/internal/utils" - "golang.org/x/sys/windows" -) - -type Boolean uint8 - -const ( - False Boolean = 0 - True Boolean = 1 -) - -type QueryDialect *uint16 - -func NewQueryDialect(queryDialect string) (QueryDialect, error) { - return windows.UTF16PtrFromString(queryDialect) -} - -var ( - QueryDialectWQL = utils.Must(NewQueryDialect("WQL")) - QueryDialectCQL = utils.Must(NewQueryDialect("CQL")) -) - -type Namespace *uint16 - -func NewNamespace(namespace string) (Namespace, error) { - return windows.UTF16PtrFromString(namespace) -} - -var ( - NamespaceRootCIMv2 = utils.Must(NewNamespace("root/CIMv2")) - NamespaceRootWindowsFSRM = utils.Must(NewNamespace("root/microsoft/windows/fsrm")) - NamespaceRootWebAdministration = utils.Must(NewNamespace("root/WebAdministration")) - NamespaceRootMSCluster = utils.Must(NewNamespace("root/MSCluster")) -) - -type Query *uint16 - -func NewQuery(query string) (Query, error) { - return windows.UTF16PtrFromString(query) -} - -// UTF16PtrFromString converts a string to a UTF-16 pointer at initialization time. -// -//nolint:ireturn -func UTF16PtrFromString[T *uint16](s string) T { - val, err := windows.UTF16PtrFromString(s) - if err != nil { - panic(err) - } - - return val -} - -type Timestamp struct { - Year uint32 - Month uint32 - Day uint32 - Hour uint32 - Minute uint32 - Second uint32 - Microseconds uint32 - UTC int32 -} - -type Interval struct { - Days uint32 - Hours uint32 - Minutes uint32 - Seconds uint32 - Microseconds uint32 - Padding1 uint32 - Padding2 uint32 - Padding3 uint32 -} - -func NewInterval(interval time.Duration) *Interval { - // Convert the duration to a number of microseconds - microseconds := interval.Microseconds() - - // Create a new interval with the microseconds - return &Interval{ - Days: uint32(microseconds / (24 * 60 * 60 * 1000000)), - Hours: uint32(microseconds / (60 * 60 * 1000000)), - Minutes: uint32(microseconds / (60 * 1000000)), - Seconds: uint32(microseconds / 1000000), - Microseconds: uint32(microseconds % 1000000), - } -} - -type Datetime struct { - IsTimestamp bool - Timestamp *Timestamp // Used when IsTimestamp is true - Interval *Interval // Used when IsTimestamp is false -} - -type PropertyDecl struct { - Flags uint32 - Code uint32 - Name *uint16 - Mqualifiers uintptr - NumQualifiers uint32 - PropertyType ValueType - ClassName *uint16 - Subscript uint32 - Offset uint32 - Origin *uint16 - Propagator *uint16 - Value uintptr -} - -func (c *ClassDecl) Properties() []*PropertyDecl { - // Create a slice to hold the properties - properties := make([]*PropertyDecl, c.NumProperties) - - // Mproperties is a pointer to an array of pointers to PropertyDecl - propertiesArray := (**PropertyDecl)(unsafe.Pointer(c.Mproperties)) - - // Iterate over the number of properties and fetch each property - for i := range c.NumProperties { - // Get the property pointer at index i - propertyPtr := *(**PropertyDecl)(unsafe.Pointer(uintptr(unsafe.Pointer(propertiesArray)) + uintptr(i)*unsafe.Sizeof(uintptr(0)))) - - // Append the property to the slice - properties[i] = propertyPtr - } - - // Return the slice of properties - return properties -} diff --git a/internal/mi/value.go b/internal/mi/value.go deleted file mode 100644 index 4c4954ab2..000000000 --- a/internal/mi/value.go +++ /dev/null @@ -1,112 +0,0 @@ -//go:build windows - -package mi - -import ( - "errors" - "fmt" - "unsafe" - - "golang.org/x/sys/windows" -) - -type ValueType int - -const ( - ValueTypeBOOLEAN ValueType = iota - ValueTypeUINT8 - ValueTypeSINT8 - ValueTypeUINT16 - ValueTypeSINT16 - ValueTypeUINT32 - ValueTypeSINT32 - ValueTypeUINT64 - ValueTypeSINT64 - ValueTypeREAL32 - ValueTypeREAL64 - ValueTypeCHAR16 - ValueTypeDATETIME - ValueTypeSTRING - ValueTypeREFERENCE - ValueTypeINSTANCE - ValueTypeBOOLEANA - ValueTypeUINT8A - ValueTypeSINT8A - ValueTypeUINT16A - ValueTypeSINT16A - ValueTypeUINT32A - ValueTypeSINT32A - ValueTypeUINT64A - ValueTypeSINT64A - ValueTypeREAL32A - ValueTypeREAL64A - ValueTypeCHAR16A - ValueTypeDATETIMEA - ValueTypeSTRINGA - ValueTypeREFERENCEA - ValueTypeINSTANCEA - ValueTypeARRAY ValueType = 16 -) - -type Element struct { - value uintptr - valueType ValueType -} - -func (e *Element) GetValue() (any, error) { - switch e.valueType { - case ValueTypeBOOLEAN: - return e.value == 1, nil - case ValueTypeUINT8: - return uint8(e.value), nil - case ValueTypeSINT8: - return int8(e.value), nil - case ValueTypeUINT16: - return uint16(e.value), nil - case ValueTypeSINT16: - return int16(e.value), nil - case ValueTypeUINT32: - return uint32(e.value), nil - case ValueTypeSINT32: - return int32(e.value), nil - case ValueTypeUINT64: - return uint64(e.value), nil - case ValueTypeSINT64: - return int64(e.value), nil - case ValueTypeREAL32: - return float32(e.value), nil - case ValueTypeREAL64: - return float64(e.value), nil - case ValueTypeCHAR16: - return uint16(e.value), nil - case ValueTypeDATETIME: - if e.value == 0 { - return nil, errors.New("invalid pointer: value is nil") - } - - return *(*Datetime)(unsafe.Pointer(e.value)), nil - case ValueTypeSTRING: - if e.value == 0 { - return nil, errors.New("invalid pointer: value is nil") - } - - // Convert the UTF-16 string to a Go string - return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(e.value))), nil - case ValueTypeSTRINGA: - if e.value == 0 { - return nil, errors.New("invalid pointer: value is nil") - } - - // Assuming array of pointers to UTF-16 strings - ptrArray := *(*[]*uint16)(unsafe.Pointer(e.value)) - strArray := make([]string, len(ptrArray)) - - for i, ptr := range ptrArray { - strArray[i] = windows.UTF16PtrToString(ptr) - } - - return strArray, nil - default: - return nil, fmt.Errorf("unsupported value type: %d", e.valueType) - } -} diff --git a/internal/testutils/handle.go b/internal/testutils/handle.go deleted file mode 100644 index d177d263b..000000000 --- a/internal/testutils/handle.go +++ /dev/null @@ -1,28 +0,0 @@ -package testutils - -import ( - "unsafe" - - "golang.org/x/sys/windows" -) - -var ( - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - - procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount") -) - -func GetProcessHandleCount(handle windows.Handle) (uint32, error) { - var count uint32 - - r1, _, err := procGetProcessHandleCount.Call( - uintptr(handle), - uintptr(unsafe.Pointer(&count)), - ) - - if r1 != 1 { - return 0, err - } - - return count, nil -} diff --git a/internal/testutils/testutils.go b/internal/testutils/testutils.go index 5be1c5081..a202d4636 100644 --- a/internal/testutils/testutils.go +++ b/internal/testutils/testutils.go @@ -10,10 +10,10 @@ import ( "time" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/pkg/collector" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" + "github.com/yusufpapurcu/wmi" ) func FuncBenchmarkCollector[C collector.Collector](b *testing.B, name string, collectFunc collector.BuilderWithFlags[C]) { @@ -57,16 +57,14 @@ func TestCollector[C collector.Collector, V interface{}](t *testing.T, fn func(* c := fn(conf) ch := make(chan prometheus.Metric, 10000) - miApp, err := mi.Application_Initialize() - require.NoError(t, err) - - miSession, err := miApp.NewSession(nil) + wmiClient := &wmi.Client{ + AllowMissingFields: true, + } + wmiClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(wmiClient) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, c.Close(logger)) - require.NoError(t, miSession.Close()) - require.NoError(t, miApp.Close()) }) wg := sync.WaitGroup{} @@ -80,7 +78,7 @@ func TestCollector[C collector.Collector, V interface{}](t *testing.T, fn func(* } }() - require.NoError(t, c.Build(logger, miSession)) + require.NoError(t, c.Build(logger, wmiClient)) time.Sleep(1 * time.Second) diff --git a/internal/utils/collector.go b/internal/utils/collector.go index 5c44e49bd..76d1ab1ac 100644 --- a/internal/utils/collector.go +++ b/internal/utils/collector.go @@ -35,11 +35,3 @@ func PDHEnabled() bool { return false } - -func MIEnabled() bool { - if v, ok := os.LookupEnv("WINDOWS_EXPORTER_WMI_ENGINE"); ok && v == "mi" { - return true - } - - return false -} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 5daf902ef..1823ad535 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -17,14 +17,3 @@ func BoolToFloat(b bool) float64 { func ToPTR[t any](v t) *t { return &v } - -// Must panics if the error is not nil. -// -//nolint:ireturn -func Must[T any](v T, err error) T { - if err != nil { - panic(err) - } - - return v -} diff --git a/main.go b/main.go deleted file mode 100644 index 06ab7d0f9..000000000 --- a/main.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 006b5bb2b..e5424abe8 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -57,9 +57,9 @@ import ( "github.com/prometheus-community/windows_exporter/internal/collector/time" "github.com/prometheus-community/windows_exporter/internal/collector/update" "github.com/prometheus-community/windows_exporter/internal/collector/vmware" - "github.com/prometheus-community/windows_exporter/internal/mi" v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/types" + "github.com/yusufpapurcu/wmi" ) // NewWithFlags To be called by the exporter for collector initialization before running kingpin.Parse. @@ -132,6 +132,9 @@ func NewWithConfig(config Config) *MetricCollectors { func New(collectors Map) *MetricCollectors { return &MetricCollectors{ Collectors: collectors, + WMIClient: &wmi.Client{ + AllowMissingFields: true, + }, } } @@ -183,9 +186,11 @@ func (c *MetricCollectors) Enable(enabledCollectors []string) error { // Build To be called by the exporter for collector initialization. func (c *MetricCollectors) Build(logger *slog.Logger) error { - err := c.initMI() + var err error + + c.WMIClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(c.WMIClient) if err != nil { - return fmt.Errorf("error from initialize MI: %w", err) + return fmt.Errorf("initialize SWbemServices: %w", err) } wg := sync.WaitGroup{} @@ -198,7 +203,7 @@ func (c *MetricCollectors) Build(logger *slog.Logger) error { go func() { defer wg.Done() - if err = collector.Build(logger, c.MISession); err != nil { + if err = collector.Build(logger, c.WMIClient); err != nil { errCh <- fmt.Errorf("error build collector %s: %w", collector.GetName(), err) } }() @@ -240,42 +245,11 @@ func (c *MetricCollectors) Close(logger *slog.Logger) error { } } - app, err := c.MISession.GetApplication() - if err != nil && !errors.Is(err, mi.ErrNotInitialized) { - errs = append(errs, err) - } - - if err := c.MISession.Close(); err != nil && !errors.Is(err, mi.ErrNotInitialized) { - errs = append(errs, err) - } - - if err := app.Close(); err != nil && !errors.Is(err, mi.ErrNotInitialized) { - errs = append(errs, err) + if c.WMIClient != nil && c.WMIClient.SWbemServicesClient != nil { + if err := c.WMIClient.SWbemServicesClient.Close(); err != nil { + errs = append(errs, err) + } } return errors.Join(errs...) } - -// Close To be called by the exporter for collector cleanup. -func (c *MetricCollectors) initMI() error { - app, err := mi.Application_Initialize() - if err != nil { - return fmt.Errorf("error from initialize MI application: %w", err) - } - - destinationOptions, err := app.NewDestinationOptions() - if err != nil { - return fmt.Errorf("error from create NewDestinationOptions: %w", err) - } - - if err = destinationOptions.SetLocale(mi.LocaleEnglish); err != nil { - return fmt.Errorf("error from set locale: %w", err) - } - - c.MISession, err = app.NewSession(destinationOptions) - if err != nil { - return fmt.Errorf("error from create NewSession: %w", err) - } - - return nil -} diff --git a/pkg/collector/types.go b/pkg/collector/types.go index 8de5d2b06..98385889a 100644 --- a/pkg/collector/types.go +++ b/pkg/collector/types.go @@ -4,14 +4,14 @@ import ( "log/slog" "github.com/alecthomas/kingpin/v2" - "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" + "github.com/yusufpapurcu/wmi" ) type MetricCollectors struct { Collectors Map - MISession *mi.Session + WMIClient *wmi.Client PerfCounterQuery string } @@ -22,7 +22,7 @@ type ( // Collector interface that a collector has to implement. type Collector interface { - Build(logger *slog.Logger, miSession *mi.Session) error + Build(logger *slog.Logger, wmiClient *wmi.Client) error // Close closes the collector Close(logger *slog.Logger) error // GetName get the name of the collector