mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-08 05:56:37 +00:00
container: support hostprocess containers and expose kubernetes labels (#1911)
This commit is contained in:
96
internal/headers/guid/guid.go
Normal file
96
internal/headers/guid/guid.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package guid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type GUID windows.GUID
|
||||
|
||||
// FromString parses a string containing a GUID and returns the GUID. The only
|
||||
// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
||||
// format.
|
||||
func FromString(s string) (GUID, error) {
|
||||
if len(s) != 36 {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
|
||||
var g GUID
|
||||
|
||||
data1, err := strconv.ParseUint(s[0:8], 16, 32)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
|
||||
g.Data1 = uint32(data1)
|
||||
|
||||
data2, err := strconv.ParseUint(s[9:13], 16, 16)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
|
||||
g.Data2 = uint16(data2)
|
||||
|
||||
data3, err := strconv.ParseUint(s[14:18], 16, 16)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
|
||||
g.Data3 = uint16(data3)
|
||||
|
||||
for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
|
||||
v, err := strconv.ParseUint(s[x:x+2], 16, 8)
|
||||
if err != nil {
|
||||
return GUID{}, fmt.Errorf("invalid GUID %q", s)
|
||||
}
|
||||
|
||||
g.Data4[i] = uint8(v)
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (g *GUID) UnmarshalJSON(b []byte) error {
|
||||
guid, err := FromString(strings.Trim(strings.Trim(string(b), `"`), `{}`))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*g = guid
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GUID) String() string {
|
||||
return fmt.Sprintf(
|
||||
"%08x-%04x-%04x-%04x-%012x",
|
||||
g.Data1,
|
||||
g.Data2,
|
||||
g.Data3,
|
||||
g.Data4[:2],
|
||||
g.Data4[2:])
|
||||
}
|
||||
47
internal/headers/hcn/hcn.go
Normal file
47
internal/headers/hcn/hcn.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/guid"
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
defaultQuery = utils.Must(windows.UTF16PtrFromString(`{"SchemaVersion":{"Major": 2,"Minor": 0},"Flags":"None"}`))
|
||||
)
|
||||
|
||||
func GetEndpointProperties(endpointID guid.GUID) (EndpointProperties, error) {
|
||||
endpoint, err := OpenEndpoint(endpointID)
|
||||
if err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to open endpoint: %w", err)
|
||||
}
|
||||
|
||||
defer CloseEndpoint(endpoint)
|
||||
|
||||
result, err := QueryEndpointProperties(endpoint, defaultQuery)
|
||||
if err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to query endpoint properties: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
47
internal/headers/hcn/hcn_test.go
Normal file
47
internal/headers/hcn/hcn_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcn"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEnumerateEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, endpoints)
|
||||
}
|
||||
|
||||
func TestQueryEndpointProperties(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
endpoints, err := hcn.EnumerateEndpoints()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(endpoints) == 0 {
|
||||
t.Skip("No endpoints found")
|
||||
}
|
||||
|
||||
_, err = hcn.GetEndpointProperties(endpoints[0])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
134
internal/headers/hcn/syscall.go
Normal file
134
internal/headers/hcn/syscall.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/guid"
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modComputeNetwork = windows.NewLazySystemDLL("computenetwork.dll")
|
||||
|
||||
procHcnEnumerateEndpoints = modComputeNetwork.NewProc("HcnEnumerateEndpoints")
|
||||
procHcnOpenEndpoint = modComputeNetwork.NewProc("HcnOpenEndpoint")
|
||||
procHcnQueryEndpointProperties = modComputeNetwork.NewProc("HcnQueryEndpointProperties")
|
||||
procHcnCloseEndpoint = modComputeNetwork.NewProc("HcnCloseEndpoint")
|
||||
)
|
||||
|
||||
// EnumerateEndpoints enumerates the endpoints.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnenumerateendpoints
|
||||
func EnumerateEndpoints() ([]guid.GUID, error) {
|
||||
var (
|
||||
endpointsJSON *uint16
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnEnumerateEndpoints.Call(
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&endpointsJSON)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
result := windows.UTF16PtrToString(endpointsJSON)
|
||||
|
||||
if r1 != 0 {
|
||||
return nil, fmt.Errorf("HcnEnumerateEndpoints failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
var endpoints []guid.GUID
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &endpoints); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// OpenEndpoint opens an endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnopenendpoint
|
||||
func OpenEndpoint(id guid.GUID) (Endpoint, error) {
|
||||
var (
|
||||
endpoint Endpoint
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnOpenEndpoint.Call(
|
||||
uintptr(unsafe.Pointer(&id)),
|
||||
uintptr(unsafe.Pointer(&endpoint)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
|
||||
if r1 != 0 {
|
||||
return 0, fmt.Errorf("HcnOpenEndpoint failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
// QueryEndpointProperties queries the properties of an endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnqueryendpointproperties
|
||||
func QueryEndpointProperties(endpoint Endpoint, propertyQuery *uint16) (EndpointProperties, error) {
|
||||
var (
|
||||
resultDocument *uint16
|
||||
errorRecord *uint16
|
||||
)
|
||||
|
||||
r1, _, _ := procHcnQueryEndpointProperties.Call(
|
||||
uintptr(endpoint),
|
||||
uintptr(unsafe.Pointer(&propertyQuery)),
|
||||
uintptr(unsafe.Pointer(&resultDocument)),
|
||||
uintptr(unsafe.Pointer(&errorRecord)),
|
||||
)
|
||||
|
||||
windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
|
||||
|
||||
result := windows.UTF16PtrToString(resultDocument)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(resultDocument))
|
||||
|
||||
if r1 != 0 {
|
||||
return EndpointProperties{}, fmt.Errorf("HcsGetComputeSystemProperties failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
var properties EndpointProperties
|
||||
|
||||
if err := json.Unmarshal([]byte(result), &properties); err != nil {
|
||||
return EndpointProperties{}, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
}
|
||||
|
||||
return properties, nil
|
||||
}
|
||||
|
||||
// CloseEndpoint close a handle to an Endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcncloseendpoint
|
||||
func CloseEndpoint(endpoint Endpoint) {
|
||||
_, _, _ = procHcnCloseEndpoint.Call(uintptr(endpoint))
|
||||
}
|
||||
53
internal/headers/hcn/types.go
Normal file
53
internal/headers/hcn/types.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcn
|
||||
|
||||
import (
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/guid"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type Endpoint = windows.Handle
|
||||
|
||||
// EndpointProperties contains the properties of an HCN endpoint.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcn/hns_schema#HostComputeEndpoint
|
||||
type EndpointProperties struct {
|
||||
ID string `json:"ID"`
|
||||
State int `json:"State"`
|
||||
SharedContainers []string `json:"SharedContainers"`
|
||||
Resources EndpointPropertiesResources `json:"Resources"`
|
||||
}
|
||||
|
||||
type EndpointPropertiesResources struct {
|
||||
Allocators []EndpointPropertiesAllocators `json:"Allocators"`
|
||||
}
|
||||
type EndpointPropertiesAllocators struct {
|
||||
AdapterNetCfgInstanceId *guid.GUID `json:"AdapterNetCfgInstanceId"`
|
||||
}
|
||||
|
||||
type EndpointStats struct {
|
||||
BytesReceived uint64 `json:"BytesReceived"`
|
||||
BytesSent uint64 `json:"BytesSent"`
|
||||
DroppedPacketsIncoming uint64 `json:"DroppedPacketsIncoming"`
|
||||
DroppedPacketsOutgoing uint64 `json:"DroppedPacketsOutgoing"`
|
||||
EndpointID string `json:"EndpointId"`
|
||||
InstanceID string `json:"InstanceId"`
|
||||
PacketsReceived uint64 `json:"PacketsReceived"`
|
||||
PacketsSent uint64 `json:"PacketsSent"`
|
||||
}
|
||||
97
internal/headers/hcs/hcs.go
Normal file
97
internal/headers/hcs/hcs.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
ContainerQuery = utils.Must(windows.UTF16PtrFromString(`{"Types":["Container"]}`))
|
||||
StatisticsQuery = utils.Must(windows.UTF16PtrFromString(`{"PropertyTypes":["Statistics"]}`))
|
||||
)
|
||||
|
||||
func GetContainers() ([]Properties, error) {
|
||||
operation, err := CreateOperation()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create operation: %w", err)
|
||||
}
|
||||
|
||||
defer CloseOperation(operation)
|
||||
|
||||
if err := EnumerateComputeSystems(ContainerQuery, operation); err != nil {
|
||||
return nil, fmt.Errorf("failed to enumerate compute systems: %w", err)
|
||||
}
|
||||
|
||||
resultDocument, err := WaitForOperationResult(operation, 1000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to wait and get for operation result: %w - %s", err, resultDocument)
|
||||
} else if resultDocument == "" {
|
||||
return nil, ErrEmptyResultDocument
|
||||
}
|
||||
|
||||
var computeSystems []Properties
|
||||
if err := json.Unmarshal([]byte(resultDocument), &computeSystems); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal compute systems: %w", err)
|
||||
}
|
||||
|
||||
return computeSystems, nil
|
||||
}
|
||||
|
||||
func GetContainerStatistics(containerID string) (Statistics, error) {
|
||||
computeSystem, err := OpenComputeSystem(containerID)
|
||||
if err != nil {
|
||||
return Statistics{}, fmt.Errorf("failed to open compute system: %w", err)
|
||||
}
|
||||
|
||||
defer CloseComputeSystem(computeSystem)
|
||||
|
||||
operation, err := CreateOperation()
|
||||
if err != nil {
|
||||
return Statistics{}, fmt.Errorf("failed to create operation: %w", err)
|
||||
}
|
||||
|
||||
defer CloseOperation(operation)
|
||||
|
||||
if err := GetComputeSystemProperties(computeSystem, operation, StatisticsQuery); err != nil {
|
||||
return Statistics{}, fmt.Errorf("failed to enumerate compute systems: %w", err)
|
||||
}
|
||||
|
||||
resultDocument, err := WaitForOperationResult(operation, 1000)
|
||||
if err != nil {
|
||||
return Statistics{}, fmt.Errorf("failed to get compute system properties: %w", err)
|
||||
} else if resultDocument == "" {
|
||||
return Statistics{}, ErrEmptyResultDocument
|
||||
}
|
||||
|
||||
var properties Properties
|
||||
if err := json.Unmarshal([]byte(resultDocument), &properties); err != nil {
|
||||
return Statistics{}, fmt.Errorf("failed to unmarshal system properties: %w", err)
|
||||
}
|
||||
|
||||
if properties.Statistics == nil {
|
||||
return Statistics{}, fmt.Errorf("no statistics found for container %s", containerID)
|
||||
}
|
||||
|
||||
return *properties.Statistics, nil
|
||||
}
|
||||
55
internal/headers/hcs/hcs_test.go
Normal file
55
internal/headers/hcs/hcs_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcs_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/hcs"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetContainers(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
containers, err := hcs.GetContainers()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, containers)
|
||||
}
|
||||
|
||||
func TestOpenContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
containers, err := hcs.GetContainers()
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(containers) == 0 {
|
||||
t.Skip("No containers found")
|
||||
}
|
||||
|
||||
statistics, err := hcs.GetContainerStatistics(containers[0].ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, statistics)
|
||||
}
|
||||
|
||||
func TestOpenContainerNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := hcs.GetContainerStatistics("f3056b79b36ddfe203376473e2aeb4922a8ca7c5d8100764e5829eb5552fe09b")
|
||||
require.ErrorIs(t, err, hcs.ErrIDNotFound)
|
||||
}
|
||||
130
internal/headers/hcs/syscall.go
Normal file
130
internal/headers/hcs/syscall.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modComputeCore = windows.NewLazySystemDLL("computecore.dll")
|
||||
|
||||
procHcsCreateOperation = modComputeCore.NewProc("HcsCreateOperation")
|
||||
procHcsWaitForOperationResult = modComputeCore.NewProc("HcsWaitForOperationResult")
|
||||
procHcsCloseOperation = modComputeCore.NewProc("HcsCloseOperation")
|
||||
procHcsEnumerateComputeSystems = modComputeCore.NewProc("HcsEnumerateComputeSystems")
|
||||
procHcsOpenComputeSystem = modComputeCore.NewProc("HcsOpenComputeSystem")
|
||||
procHcsGetComputeSystemProperties = modComputeCore.NewProc("HcsGetComputeSystemProperties")
|
||||
procHcsCloseComputeSystem = modComputeCore.NewProc("HcsCloseComputeSystem")
|
||||
)
|
||||
|
||||
// CreateOperation creates a new operation.
|
||||
func CreateOperation() (Operation, error) {
|
||||
r1, r2, _ := procHcsCreateOperation.Call(0, 0)
|
||||
if r2 != 0 {
|
||||
return 0, fmt.Errorf("HcsCreateOperation failed: HRESULT 0x%X: %w", r2, Win32FromHResult(r2))
|
||||
}
|
||||
|
||||
return Operation(r1), nil
|
||||
}
|
||||
|
||||
func WaitForOperationResult(operation Operation, timeout uint32) (string, error) {
|
||||
var resultDocument *uint16
|
||||
|
||||
r1, _, _ := procHcsWaitForOperationResult.Call(uintptr(operation), uintptr(timeout), uintptr(unsafe.Pointer(&resultDocument)))
|
||||
if r1 != 0 {
|
||||
return "", fmt.Errorf("HcsWaitForOperationResult failed: HRESULT 0x%X: %w", r1, Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
result := windows.UTF16PtrToString(resultDocument)
|
||||
windows.CoTaskMemFree(unsafe.Pointer(resultDocument))
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CloseOperation closes an operation.
|
||||
func CloseOperation(operation Operation) {
|
||||
_, _, _ = procHcsCloseOperation.Call(uintptr(operation))
|
||||
}
|
||||
|
||||
// EnumerateComputeSystems enumerates compute systems.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsenumeratecomputesystems
|
||||
func EnumerateComputeSystems(query *uint16, operation Operation) error {
|
||||
r1, _, _ := procHcsEnumerateComputeSystems.Call(uintptr(unsafe.Pointer(query)), uintptr(operation))
|
||||
if r1 != 0 {
|
||||
return fmt.Errorf("HcsEnumerateComputeSystems failed: HRESULT 0x%X: %w", r1, Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenComputeSystem opens a handle to an existing compute system.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsopencomputesystem
|
||||
func OpenComputeSystem(id string) (ComputeSystem, error) {
|
||||
idPtr, err := windows.UTF16PtrFromString(id)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var system ComputeSystem
|
||||
|
||||
r1, _, _ := procHcsOpenComputeSystem.Call(
|
||||
uintptr(unsafe.Pointer(idPtr)),
|
||||
uintptr(windows.GENERIC_ALL),
|
||||
uintptr(unsafe.Pointer(&system)),
|
||||
)
|
||||
if r1 != 0 {
|
||||
return 0, fmt.Errorf("HcsOpenComputeSystem failed: HRESULT 0x%X: %w", r1, Win32FromHResult(r1))
|
||||
}
|
||||
|
||||
return system, nil
|
||||
}
|
||||
|
||||
func GetComputeSystemProperties(system ComputeSystem, operation Operation, propertyQuery *uint16) error {
|
||||
r1, _, err := procHcsGetComputeSystemProperties.Call(
|
||||
uintptr(system),
|
||||
uintptr(operation),
|
||||
uintptr(unsafe.Pointer(propertyQuery)),
|
||||
)
|
||||
if r1 != 0 {
|
||||
return fmt.Errorf("HcsGetComputeSystemProperties failed: HRESULT 0x%X: %w", r1, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseComputeSystem closes a handle to a compute system.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsclosecomputesystem
|
||||
func CloseComputeSystem(system ComputeSystem) {
|
||||
_, _, _ = procHcsCloseComputeSystem.Call(uintptr(system))
|
||||
}
|
||||
|
||||
func Win32FromHResult(hr uintptr) windows.Errno {
|
||||
if hr&0x1fff0000 == 0x00070000 {
|
||||
return windows.Errno(hr & 0xffff)
|
||||
}
|
||||
|
||||
return windows.Errno(hr)
|
||||
}
|
||||
83
internal/headers/hcs/types.go
Normal file
83
internal/headers/hcs/types.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package hcs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEmptyResultDocument = errors.New("empty result document")
|
||||
ErrIDNotFound = windows.Errno(2151088398)
|
||||
)
|
||||
|
||||
type (
|
||||
Operation = windows.Handle
|
||||
ComputeSystem = windows.Handle
|
||||
)
|
||||
|
||||
type Properties struct {
|
||||
ID string `json:"Id,omitempty"`
|
||||
SystemType string `json:"SystemType,omitempty"`
|
||||
Owner string `json:"Owner,omitempty"`
|
||||
State string `json:"State,omitempty"`
|
||||
Statistics *Statistics `json:"Statistics,omitempty"`
|
||||
ProcessList []ProcessDetails `json:"ProcessList,omitempty"`
|
||||
}
|
||||
|
||||
type ProcessDetails struct {
|
||||
ProcessId int32 `json:"ProcessId,omitempty"`
|
||||
ImageName string `json:"ImageName,omitempty"`
|
||||
CreateTimestamp time.Time `json:"CreateTimestamp,omitempty"`
|
||||
UserTime100ns int32 `json:"UserTime100ns,omitempty"`
|
||||
KernelTime100ns int32 `json:"KernelTime100ns,omitempty"`
|
||||
MemoryCommitBytes int32 `json:"MemoryCommitBytes,omitempty"`
|
||||
MemoryWorkingSetPrivateBytes int32 `json:"MemoryWorkingSetPrivateBytes,omitempty"`
|
||||
MemoryWorkingSetSharedBytes int32 `json:"MemoryWorkingSetSharedBytes,omitempty"`
|
||||
}
|
||||
|
||||
type Statistics struct {
|
||||
Timestamp time.Time `json:"Timestamp,omitempty"`
|
||||
ContainerStartTime time.Time `json:"ContainerStartTime,omitempty"`
|
||||
Uptime100ns uint64 `json:"Uptime100ns,omitempty"`
|
||||
Processor *ProcessorStats `json:"Processor,omitempty"`
|
||||
Memory *MemoryStats `json:"Memory,omitempty"`
|
||||
Storage *StorageStats `json:"Storage,omitempty"`
|
||||
}
|
||||
|
||||
type ProcessorStats struct {
|
||||
TotalRuntime100ns uint64 `json:"TotalRuntime100ns,omitempty"`
|
||||
RuntimeUser100ns uint64 `json:"RuntimeUser100ns,omitempty"`
|
||||
RuntimeKernel100ns uint64 `json:"RuntimeKernel100ns,omitempty"`
|
||||
}
|
||||
|
||||
type MemoryStats struct {
|
||||
MemoryUsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"`
|
||||
MemoryUsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"`
|
||||
MemoryUsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"`
|
||||
}
|
||||
|
||||
type StorageStats struct {
|
||||
ReadCountNormalized uint64 `json:"ReadCountNormalized,omitempty"`
|
||||
ReadSizeBytes uint64 `json:"ReadSizeBytes,omitempty"`
|
||||
WriteCountNormalized uint64 `json:"WriteCountNormalized,omitempty"`
|
||||
WriteSizeBytes uint64 `json:"WriteSizeBytes,omitempty"`
|
||||
}
|
||||
@@ -22,13 +22,16 @@ import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/guid"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex")
|
||||
procConvertInterfaceGuidToLuid = modiphlpapi.NewProc("ConvertInterfaceGuidToLuid")
|
||||
)
|
||||
|
||||
func GetTCPConnectionStates(family uint32) (map[MIB_TCP_STATE]uint32, error) {
|
||||
@@ -128,3 +131,37 @@ func getExtendedTcpTable[T any](ulAf uint32, tableClass uint32) ([]T, error) {
|
||||
|
||||
return unsafe.Slice((*T)(unsafe.Pointer(&buf[4])), binary.LittleEndian.Uint32(buf)), nil
|
||||
}
|
||||
|
||||
// GetIfEntry2Ex function retrieves the specified level of information for the specified interface on the local computer.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getifentry2ex
|
||||
func GetIfEntry2Ex(row *MIB_IF_ROW2) error {
|
||||
ret, _, _ := procGetIfEntry2Ex.Call(
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(row)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("GetIfEntry2Ex failed with code %d: %w", ret, windows.Errno(ret))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertInterfaceGUIDToLUID function converts a globally unique identifier (GUID) for a network interface to the
|
||||
// locally unique identifier (LUID) for the interface.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-convertinterfaceguidtoluid
|
||||
func ConvertInterfaceGUIDToLUID(guid guid.GUID) (uint64, error) {
|
||||
var luid uint64
|
||||
ret, _, _ := procConvertInterfaceGuidToLuid.Call(
|
||||
uintptr(unsafe.Pointer(&guid)),
|
||||
uintptr(unsafe.Pointer(&luid)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return 0, fmt.Errorf("ConvertInterfaceGUIDToLUID failed with code %d: %w", ret, windows.Errno(ret))
|
||||
}
|
||||
|
||||
return luid, nil
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ package iphlpapi
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/headers/guid"
|
||||
)
|
||||
|
||||
// MIB_TCPROW_OWNER_PID structure for IPv4.
|
||||
@@ -107,3 +109,54 @@ func (b BigEndianUint32) uint16() uint16 {
|
||||
|
||||
return binary.LittleEndian.Uint16(data)
|
||||
}
|
||||
|
||||
// Constants from Windows headers
|
||||
const (
|
||||
IF_MAX_STRING_SIZE = 256
|
||||
IF_MAX_PHYS_ADDRESS_LENGTH = 32
|
||||
)
|
||||
|
||||
// MIB_IF_ROW2 represents network interface statistics
|
||||
type MIB_IF_ROW2 struct {
|
||||
InterfaceLuid uint64
|
||||
InterfaceIndex uint32
|
||||
InterfaceGuid guid.GUID
|
||||
Alias [IF_MAX_STRING_SIZE + 1]uint16
|
||||
Description [IF_MAX_STRING_SIZE + 1]uint16
|
||||
PhysicalAddressLength uint32
|
||||
PhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]byte
|
||||
PermanentPhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]byte
|
||||
Mtu uint32
|
||||
Type uint32
|
||||
TunnelType uint32
|
||||
MediaType uint32
|
||||
PhysicalMediumType uint32
|
||||
AccessType uint32
|
||||
DirectionType uint32
|
||||
InterfaceAndOperStatusFlags uint8
|
||||
OperStatus uint32
|
||||
AdminStatus uint32
|
||||
MediaConnectState uint32
|
||||
NetworkGuid [16]byte
|
||||
ConnectionType uint32
|
||||
TransmitLinkSpeed uint64
|
||||
ReceiveLinkSpeed uint64
|
||||
InOctets uint64
|
||||
InUcastPkts uint64
|
||||
InNUcastPkts uint64
|
||||
InDiscards uint64
|
||||
InErrors uint64
|
||||
InUnknownProtos uint64
|
||||
InUcastOctets uint64
|
||||
InMulticastOctets uint64
|
||||
InBroadcastOctets uint64
|
||||
OutOctets uint64
|
||||
OutUcastPkts uint64
|
||||
OutNUcastPkts uint64
|
||||
OutDiscards uint64
|
||||
OutErrors uint64
|
||||
OutUcastOctets uint64
|
||||
OutMulticastOctets uint64
|
||||
OutBroadcastOctets uint64
|
||||
OutQLen uint64
|
||||
}
|
||||
|
||||
53
internal/headers/kernel32/job.go
Normal file
53
internal/headers/kernel32/job.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package kernel32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
// JobObjectQuery is required to retrieve certain information about a job object,
|
||||
// such as attributes and accounting information (see QueryInformationJobObject and IsProcessInJob).
|
||||
// https://learn.microsoft.com/en-us/windows/win32/procthread/job-object-security-and-access-rights
|
||||
JobObjectQuery = 0x0004
|
||||
)
|
||||
|
||||
func OpenJobObject(name string) (windows.Handle, error) {
|
||||
handle, _, err := procOpenJobObject.Call(JobObjectQuery, 0, uintptr(unsafe.Pointer(&name)))
|
||||
if handle == 0 {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return windows.Handle(handle), nil
|
||||
}
|
||||
|
||||
func IsProcessInJob(process windows.Handle, job windows.Handle, result *bool) error {
|
||||
ret, _, err := procIsProcessInJob.Call(
|
||||
uintptr(process),
|
||||
uintptr(job),
|
||||
uintptr(unsafe.Pointer(&result)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -30,6 +30,8 @@ var (
|
||||
procGetDynamicTimeZoneInformationSys = modkernel32.NewProc("GetDynamicTimeZoneInformation")
|
||||
procKernelLocalFileTimeToFileTime = modkernel32.NewProc("LocalFileTimeToFileTime")
|
||||
procGetTickCount = modkernel32.NewProc("GetTickCount64")
|
||||
procOpenJobObject = modkernel32.NewProc("OpenJobObjectW")
|
||||
procIsProcessInJob = modkernel32.NewProc("IsProcessInJob")
|
||||
)
|
||||
|
||||
// SYSTEMTIME contains a date and time.
|
||||
|
||||
75
internal/headers/kernel32/types.go
Normal file
75
internal/headers/kernel32/types.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package kernel32
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type JobObjectBasicAccountingInformation struct {
|
||||
TotalUserTime uint64
|
||||
TotalKernelTime uint64
|
||||
ThisPeriodTotalUserTime uint64
|
||||
ThisPeriodTotalKernelTime uint64
|
||||
TotalPageFaultCount uint32
|
||||
TotalProcesses uint32
|
||||
ActiveProcesses uint32
|
||||
TotalTerminatedProcesses uint32
|
||||
}
|
||||
|
||||
type IOCounters struct {
|
||||
ReadOperationCount uint64
|
||||
WriteOperationCount uint64
|
||||
OtherOperationCount uint64
|
||||
ReadTransferCount uint64
|
||||
WriteTransferCount uint64
|
||||
OtherTransferCount uint64
|
||||
}
|
||||
|
||||
type JobObjectExtendedLimitInformation struct {
|
||||
BasicInfo JobObjectBasicAccountingInformation
|
||||
IoInfo IOCounters
|
||||
ProcessMemoryLimit uint64
|
||||
JobMemoryLimit uint64
|
||||
PeakProcessMemoryUsed uint64
|
||||
PeakJobMemoryUsed uint64
|
||||
}
|
||||
|
||||
type JobObjectBasicProcessIDList struct {
|
||||
NumberOfAssignedProcesses uint32
|
||||
NumberOfProcessIdsInList uint32
|
||||
ProcessIdList [1]uintptr
|
||||
}
|
||||
|
||||
// PIDs returns all the process Ids in the job object.
|
||||
func (p *JobObjectBasicProcessIDList) PIDs() []uint32 {
|
||||
return unsafe.Slice((*uint32)(unsafe.Pointer(&p.ProcessIdList[0])), int(p.NumberOfProcessIdsInList))
|
||||
}
|
||||
|
||||
type PROCESS_VM_COUNTERS struct {
|
||||
PeakVirtualSize uintptr
|
||||
VirtualSize uintptr
|
||||
PageFaultCount uint32
|
||||
PeakWorkingSetSize uintptr
|
||||
WorkingSetSize uintptr
|
||||
QuotaPeakPagedPoolUsage uintptr
|
||||
QuotaPagedPoolUsage uintptr
|
||||
QuotaPeakNonPagedPoolUsage uintptr
|
||||
QuotaNonPagedPoolUsage uintptr
|
||||
PagefileUsage uintptr
|
||||
PeakPagefileUsage uintptr
|
||||
PrivateWorkingSetSize uintptr
|
||||
}
|
||||
Reference in New Issue
Block a user