mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
feat(host): pod volume metrics (#21291)
* feat(host): pod volume metrics * feat(host): add pod_netio for pod
This commit is contained in:
@@ -264,6 +264,10 @@ func (d disk) doTemplateOverlayAction(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d disk) InjectUsageTags(usage *ContainerVolumeMountUsage, vol *hostapi.ContainerVolumeMount) {
|
||||
usage.Tags["disk_id"] = vol.Disk.Id
|
||||
}
|
||||
|
||||
type diskOverlayDir struct{}
|
||||
|
||||
func newDiskOverlayDir() iDiskOverlay {
|
||||
|
||||
@@ -17,6 +17,7 @@ package volume_mount
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
pdisk "github.com/shirou/gopsutil/v3/disk"
|
||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
@@ -55,6 +56,21 @@ type IVolumeMount interface {
|
||||
Unmount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error
|
||||
}
|
||||
|
||||
type ContainerVolumeMountUsage struct {
|
||||
Id string
|
||||
HostPath string
|
||||
MountPath string
|
||||
VolumeType string
|
||||
Usage *pdisk.UsageStat
|
||||
Tags map[string]string
|
||||
}
|
||||
|
||||
type IUsageVolumeMount interface {
|
||||
IVolumeMount
|
||||
|
||||
InjectUsageTags(usage *ContainerVolumeMountUsage, vol *hostapi.ContainerVolumeMount)
|
||||
}
|
||||
|
||||
func GetRuntimeVolumeMountPropagation(input apis.ContainerMountPropagation) runtimeapi.MountPropagation {
|
||||
switch input {
|
||||
case apis.MOUNTPROPAGATION_PROPAGATION_PRIVATE:
|
||||
|
||||
@@ -108,6 +108,7 @@ func (cr *containerRunner) RunInContainer(pod *desc.SGuestDesc, containerId stri
|
||||
type PodInstance interface {
|
||||
GuestRuntimeInstance
|
||||
|
||||
GetContainerById(ctrId string) *hostapi.ContainerDesc
|
||||
CreateContainer(ctx context.Context, userCred mcclient.TokenCredential, id string, input *hostapi.ContainerCreateInput) (jsonutils.JSONObject, error)
|
||||
StartContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerCreateInput) (jsonutils.JSONObject, error)
|
||||
DeleteContainer(ctx context.Context, cred mcclient.TokenCredential, id string) (jsonutils.JSONObject, error)
|
||||
@@ -121,6 +122,9 @@ type PodInstance interface {
|
||||
CommitContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerCommitInput) (jsonutils.JSONObject, error)
|
||||
|
||||
ReadLogs(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *computeapi.PodLogOptions, stdout, stderr io.Writer) error
|
||||
|
||||
// for monitoring
|
||||
GetVolumeMountUsages() (map[ContainerVolumeKey]*volume_mount.ContainerVolumeMountUsage, error)
|
||||
}
|
||||
|
||||
type sContainer struct {
|
||||
|
||||
@@ -17,10 +17,14 @@ package guestman
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/hostman/container/volume_mount"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/image"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/nerdctl"
|
||||
@@ -94,3 +98,52 @@ func PushContainerdImage(input *hostapi.ContainerPushImageInput) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ContainerVolumeKey struct {
|
||||
Id string
|
||||
HostPath string
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) GetVolumeMountUsages() (map[ContainerVolumeKey]*volume_mount.ContainerVolumeMountUsage, error) {
|
||||
errs := []error{}
|
||||
result := make(map[ContainerVolumeKey]*volume_mount.ContainerVolumeMountUsage)
|
||||
for ctrId, vols := range s.getContainerVolumeMounts() {
|
||||
for i := range vols {
|
||||
vol := vols[i]
|
||||
drv, ok := volume_mount.GetDriver(vol.Type).(volume_mount.IUsageVolumeMount)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
vu, err := s.getVolumeMountUsage(drv, ctrId, vol)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "get container %s %s volume usage: %s", ctrId, drv.GetType(), jsonutils.Marshal(vol)))
|
||||
}
|
||||
result[ContainerVolumeKey{
|
||||
Id: ctrId,
|
||||
HostPath: vu.HostPath,
|
||||
}] = vu
|
||||
}
|
||||
}
|
||||
return result, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) getVolumeMountUsage(drv volume_mount.IUsageVolumeMount, ctrId string, vol *hostapi.ContainerVolumeMount) (*volume_mount.ContainerVolumeMountUsage, error) {
|
||||
hp, err := drv.GetRuntimeMountHostPath(s, ctrId, vol)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetRuntimeMountHostPath")
|
||||
}
|
||||
us, err := disk.Usage(hp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "disk.Usage of %s", hp)
|
||||
}
|
||||
usage := &volume_mount.ContainerVolumeMountUsage{
|
||||
Id: ctrId,
|
||||
MountPath: vol.MountPath,
|
||||
HostPath: hp,
|
||||
VolumeType: string(drv.GetType()),
|
||||
Usage: us,
|
||||
Tags: make(map[string]string),
|
||||
}
|
||||
drv.InjectUsageTags(usage, vol)
|
||||
return usage, nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestman"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/stats"
|
||||
)
|
||||
|
||||
@@ -17,11 +20,21 @@ const (
|
||||
MEMORY_WORKING_SET_BYTES = "working_set_bytes"
|
||||
// memory usage rate
|
||||
MEMORY_USAGE_RATE = "usage_rate"
|
||||
|
||||
VOLUME_TOTAL = "total"
|
||||
VOLUME_FREE = "free"
|
||||
VOLUME_USED = "used"
|
||||
VOLUME_USED_PERCENT = "used_percent"
|
||||
VOLUME_INODES_TOTAL = "inodes_total"
|
||||
VOLUME_INODES_FREE = "inodes_free"
|
||||
VOLUME_INODES_USED = "inodes_used"
|
||||
VOLUME_INODES_USED_PERCENT = "inodes_used_percent"
|
||||
)
|
||||
|
||||
type PodMetrics struct {
|
||||
PodCpu *PodCpuMetric `json:"pod_cpu"`
|
||||
PodMemory *PodMemoryMetric `json:"pod_memory"`
|
||||
PodVolumes []*PodVolumeMetric `json:"pod_volume"`
|
||||
Containers []*ContainerMetrics `json:"containers"`
|
||||
}
|
||||
|
||||
@@ -74,6 +87,59 @@ func (m PodMemoryMetric) ToMap() map[string]interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
type PodVolumeMetric struct {
|
||||
ContainerMetricMeta
|
||||
// 容器内挂载路径
|
||||
MountPath string `json:"mount_path"`
|
||||
// 宿主机路径
|
||||
HostPath string `json:"host_path"`
|
||||
Type string `json:"type"`
|
||||
Fstype string `json:"fstype"`
|
||||
Total uint64 `json:"total"`
|
||||
Free uint64 `json:"free"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"used_percent"`
|
||||
InodesTotal uint64 `json:"inodes_total"`
|
||||
InodesUsed uint64 `json:"inodes_used"`
|
||||
InodesFree uint64 `json:"inodes_free"`
|
||||
InodesUsedPercent float64 `json:"inodes_used_percent"`
|
||||
Tags map[string]string `json:"tags"`
|
||||
}
|
||||
|
||||
func (m PodVolumeMetric) GetName() string {
|
||||
return "pod_volume"
|
||||
}
|
||||
|
||||
func (m PodVolumeMetric) ToMap() map[string]interface{} {
|
||||
r := map[string]interface{}{
|
||||
VOLUME_TOTAL: m.Total,
|
||||
VOLUME_FREE: m.Free,
|
||||
VOLUME_USED: m.Used,
|
||||
VOLUME_USED_PERCENT: m.UsedPercent,
|
||||
VOLUME_INODES_TOTAL: m.InodesTotal,
|
||||
VOLUME_INODES_FREE: m.InodesFree,
|
||||
VOLUME_INODES_USED: m.InodesUsed,
|
||||
VOLUME_INODES_USED_PERCENT: m.InodesUsedPercent,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (m PodVolumeMetric) GetTag() map[string]string {
|
||||
baseTags := m.ContainerMetricMeta.GetTag()
|
||||
curTags := map[string]string{
|
||||
"mount_path": m.MountPath,
|
||||
"host_path": m.HostPath,
|
||||
"type": m.Type,
|
||||
}
|
||||
for k, v := range curTags {
|
||||
baseTags[k] = v
|
||||
}
|
||||
for k, v := range m.Tags {
|
||||
baseTags[k] = v
|
||||
}
|
||||
return baseTags
|
||||
}
|
||||
|
||||
type ContainerMetrics struct {
|
||||
ContainerCpu *ContainerCpuMetric `json:"container_cpu"`
|
||||
ContainerMemory *ContainerMemoryMetric `json:"container_memory"`
|
||||
@@ -147,6 +213,13 @@ func GetPodStatsById(stats []stats.PodStats, podId string) *stats.PodStats {
|
||||
func (s *SGuestMonitorCollector) collectPodMetrics(gm *SGuestMonitor, prevUsage *GuestMetrics) *GuestMetrics {
|
||||
gmData := new(GuestMetrics)
|
||||
gmData.PodMetrics = gm.PodMetrics(prevUsage)
|
||||
|
||||
// netio
|
||||
gmData.VmNetio = gm.Netio()
|
||||
netio1 := gmData.VmNetio
|
||||
netio2 := prevUsage.VmNetio
|
||||
s.addNetio(netio1, netio2)
|
||||
|
||||
return gmData
|
||||
}
|
||||
|
||||
@@ -163,6 +236,44 @@ func (m *SGuestMonitor) HasPodMetrics() bool {
|
||||
return m.podStat != nil
|
||||
}
|
||||
|
||||
func (m *SGuestMonitor) getVolumeMetrics() []*PodVolumeMetric {
|
||||
pi := m.instance.(guestman.PodInstance)
|
||||
if !pi.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
vus, err := pi.GetVolumeMountUsages()
|
||||
if err != nil {
|
||||
log.Warningf("get volume mount usages: %v", err)
|
||||
}
|
||||
result := make([]*PodVolumeMetric, 0)
|
||||
for i := range vus {
|
||||
vu := vus[i]
|
||||
ctr := pi.GetContainerById(vu.Id)
|
||||
if ctr == nil {
|
||||
log.Warningf("not found container by %s", vu.Id)
|
||||
continue
|
||||
}
|
||||
meta := NewContainerMetricMeta(pi.GetId(), vu.Id, ctr.Name, time.Now())
|
||||
result = append(result, &PodVolumeMetric{
|
||||
ContainerMetricMeta: meta,
|
||||
MountPath: vu.MountPath,
|
||||
HostPath: vu.HostPath,
|
||||
Type: vu.VolumeType,
|
||||
Fstype: vu.Usage.Fstype,
|
||||
Total: vu.Usage.Total,
|
||||
Free: vu.Usage.Free,
|
||||
Used: vu.Usage.Used,
|
||||
UsedPercent: vu.Usage.UsedPercent,
|
||||
InodesTotal: vu.Usage.InodesTotal,
|
||||
InodesUsed: vu.Usage.InodesUsed,
|
||||
InodesFree: vu.Usage.InodesFree,
|
||||
InodesUsedPercent: vu.Usage.InodesUsedPercent,
|
||||
Tags: vu.Tags,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *SGuestMonitor) PodMetrics(prevUsage *GuestMetrics) *PodMetrics {
|
||||
stat := m.podStat
|
||||
podCpu := &PodCpuMetric{
|
||||
@@ -208,6 +319,7 @@ func (m *SGuestMonitor) PodMetrics(prevUsage *GuestMetrics) *PodMetrics {
|
||||
return &PodMetrics{
|
||||
PodCpu: podCpu,
|
||||
PodMemory: podMemory,
|
||||
PodVolumes: m.getVolumeMetrics(),
|
||||
Containers: containers,
|
||||
}
|
||||
}
|
||||
@@ -221,6 +333,9 @@ type iPodMetric interface {
|
||||
func (d *GuestMetrics) toPodTelegrafData(tagStr string) []string {
|
||||
m := d.PodMetrics
|
||||
ims := []iPodMetric{m.PodCpu, m.PodMemory}
|
||||
for i := range m.PodVolumes {
|
||||
ims = append(ims, m.PodVolumes[i])
|
||||
}
|
||||
for _, c := range m.Containers {
|
||||
ims = append(ims, c.ContainerCpu)
|
||||
ims = append(ims, c.ContainerMemory)
|
||||
@@ -228,14 +343,16 @@ func (d *GuestMetrics) toPodTelegrafData(tagStr string) []string {
|
||||
res := []string{}
|
||||
for _, im := range ims {
|
||||
tagMap := im.GetTag()
|
||||
newTagStr := tagStr
|
||||
if len(tagMap) != 0 {
|
||||
var newTagArr []string
|
||||
for k, v := range tagMap {
|
||||
newTagArr = append(newTagArr, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
tagStr = strings.Join([]string{tagStr, strings.Join(newTagArr, ",")}, ",")
|
||||
newTagStr = strings.Join([]string{tagStr, strings.Join(newTagArr, ",")}, ",")
|
||||
}
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", im.GetName(), tagStr, d.mapToStatStr(im.ToMap())))
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", im.GetName(), newTagStr, d.mapToStatStr(im.ToMap())))
|
||||
}
|
||||
res = append(res, d.netioToTelegrafData("pod_netio", tagStr)...)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package hostmetrics
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -111,7 +112,8 @@ func (m *SHostMetricsCollector) reportUsageToTelegraf(data string) {
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 204 {
|
||||
log.Errorf("upload guest metric failed %d", res.StatusCode)
|
||||
resBody, _ := io.ReadAll(res.Body)
|
||||
log.Errorf("upload guest metric failed %d: %s", res.StatusCode, string(resBody))
|
||||
timestamp := time.Now().UnixNano()
|
||||
for _, line := range strings.Split(data, "\n") {
|
||||
m.waitingReportData = append(m.waitingReportData,
|
||||
@@ -199,7 +201,7 @@ func (s *SGuestMonitorCollector) GetGuests() map[string]*SGuestMonitor {
|
||||
gm.MemMB = instance.GetDesc().Mem
|
||||
} else {
|
||||
delete(s.monitors, guestId)
|
||||
gm, err = NewGuestMonitor(guestName, guestId, pid, nicsDesc, int(vcpuCount))
|
||||
gm, err = NewGuestMonitor(instance, guestName, guestId, pid, nicsDesc, int(vcpuCount))
|
||||
if err != nil {
|
||||
log.Errorf("NewGuestMonitor for %s(%s), pid: %d, nics: %#v", guestName, guestId, pid, nicsDesc)
|
||||
return true
|
||||
@@ -225,7 +227,7 @@ func (s *SGuestMonitorCollector) GetGuests() map[string]*SGuestMonitor {
|
||||
}
|
||||
podStat := GetPodStatsById(podStats, guestId)
|
||||
if podStat != nil {
|
||||
gm, err := NewGuestPodMonitor(guestName, guestId, podStat, nicsDesc, int(vcpuCount))
|
||||
gm, err := NewGuestPodMonitor(instance, guestName, guestId, podStat, nicsDesc, int(vcpuCount))
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
@@ -409,21 +411,27 @@ func (d *GuestMetrics) mapToStatStr(m map[string]interface{}) string {
|
||||
return strings.Join(statArr, ",")
|
||||
}
|
||||
|
||||
func (d *GuestMetrics) toVmTelegrafData(tagStr string) []string {
|
||||
var res = []string{}
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_cpu", tagStr, d.mapToStatStr(d.VmCpu.ToMap())))
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_mem", tagStr, d.mapToStatStr(d.VmMem.ToMap())))
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_diskio", tagStr, d.mapToStatStr(d.VmDiskio.ToMap())))
|
||||
func (d *GuestMetrics) netioToTelegrafData(measurement string, tagStr string) []string {
|
||||
res := []string{}
|
||||
for i := range d.VmNetio {
|
||||
netTagMap := d.VmNetio[i].ToTag()
|
||||
for k, v := range netTagMap {
|
||||
tagStr = fmt.Sprintf("%s,%s=%s", tagStr, k, v)
|
||||
}
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_netio", tagStr, d.mapToStatStr(d.VmNetio[i].ToMap())))
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", measurement, tagStr, d.mapToStatStr(d.VmNetio[i].ToMap())))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *GuestMetrics) toVmTelegrafData(tagStr string) []string {
|
||||
var res = []string{}
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_cpu", tagStr, d.mapToStatStr(d.VmCpu.ToMap())))
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_mem", tagStr, d.mapToStatStr(d.VmMem.ToMap())))
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_diskio", tagStr, d.mapToStatStr(d.VmDiskio.ToMap())))
|
||||
res = append(res, d.netioToTelegrafData("vm_netio", tagStr)...)
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *GuestMetrics) toTelegrafData(tags map[string]string) []string {
|
||||
var tagArr = []string{}
|
||||
for k, v := range tags {
|
||||
@@ -544,18 +552,19 @@ type SGuestMonitor struct {
|
||||
DomainId string
|
||||
ProjectDomain string
|
||||
podStat *stats.PodStats
|
||||
instance guestman.GuestRuntimeInstance
|
||||
}
|
||||
|
||||
func NewGuestMonitor(name, id string, pid int, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
|
||||
func NewGuestMonitor(instance guestman.GuestRuntimeInstance, name, id string, pid int, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
|
||||
proc, err := process.NewProcess(int32(pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newGuestMonitor(name, id, proc, nics, cpuCount)
|
||||
return newGuestMonitor(instance, name, id, proc, nics, cpuCount)
|
||||
}
|
||||
|
||||
func NewGuestPodMonitor(name, id string, stat *stats.PodStats, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
|
||||
m, err := newGuestMonitor(name, id, nil, nics, cpuCount)
|
||||
func NewGuestPodMonitor(instance guestman.GuestRuntimeInstance, name, id string, stat *stats.PodStats, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
|
||||
m, err := newGuestMonitor(instance, name, id, nil, nics, cpuCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "new pod GuestMonitor")
|
||||
}
|
||||
@@ -563,7 +572,7 @@ func NewGuestPodMonitor(name, id string, stat *stats.PodStats, nics []*desc.SGue
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func newGuestMonitor(name, id string, proc *process.Process, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
|
||||
func newGuestMonitor(instance guestman.GuestRuntimeInstance, name, id string, proc *process.Process, nics []*desc.SGuestNetwork, cpuCount int) (*SGuestMonitor, error) {
|
||||
var ip string
|
||||
if len(nics) >= 1 {
|
||||
ip = nics[0].Ip
|
||||
@@ -573,13 +582,14 @@ func newGuestMonitor(name, id string, proc *process.Process, nics []*desc.SGuest
|
||||
pid = int(proc.Pid)
|
||||
}
|
||||
return &SGuestMonitor{
|
||||
Name: name,
|
||||
Id: id,
|
||||
Pid: pid,
|
||||
Nics: nics,
|
||||
CpuCnt: cpuCount,
|
||||
Ip: ip,
|
||||
Process: proc,
|
||||
Name: name,
|
||||
Id: id,
|
||||
Pid: pid,
|
||||
Nics: nics,
|
||||
CpuCnt: cpuCount,
|
||||
Ip: ip,
|
||||
Process: proc,
|
||||
instance: instance,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,16 @@ func newMetricFieldCreateInput(name, displayName, unit string, score int) monito
|
||||
}
|
||||
}
|
||||
|
||||
func registryNetio(measurement string, displayName string, resType string, score int) {
|
||||
RegistryMetricCreateInput(measurement, displayName, resType,
|
||||
monitor.METRIC_DATABASE_TELE, score, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("bps_recv", "Received traffic per second", monitor.METRIC_UNIT_BPS, 1),
|
||||
newMetricFieldCreateInput("bps_sent", "Send traffic per second", monitor.METRIC_UNIT_BPS, 2),
|
||||
newMetricFieldCreateInput("pps_recv", "Received packets per second", monitor.METRIC_UNIT_PPS, 3),
|
||||
newMetricFieldCreateInput("pps_sent", "Send packets per second", monitor.METRIC_UNIT_PPS, 4),
|
||||
})
|
||||
}
|
||||
|
||||
// order by score asc
|
||||
// score default:99
|
||||
func init() {
|
||||
@@ -180,13 +190,7 @@ func init() {
|
||||
})
|
||||
|
||||
// vm_netio
|
||||
RegistryMetricCreateInput("vm_netio", "Guest network traffic", monitor.METRIC_RES_TYPE_GUEST,
|
||||
monitor.METRIC_DATABASE_TELE, 4, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("bps_recv", "Received traffic per second", monitor.METRIC_UNIT_BPS, 1),
|
||||
newMetricFieldCreateInput("bps_sent", "Send traffic per second", monitor.METRIC_UNIT_BPS, 2),
|
||||
newMetricFieldCreateInput("pps_recv", "Received packets per second", monitor.METRIC_UNIT_PPS, 3),
|
||||
newMetricFieldCreateInput("pps_sent", "Send packets per second", monitor.METRIC_UNIT_PPS, 4),
|
||||
})
|
||||
registryNetio("vm_netio", "Guest network traffic", monitor.METRIC_RES_TYPE_GUEST, 4)
|
||||
|
||||
// oss_latency
|
||||
RegistryMetricCreateInput("oss_latency", "Object storage latency",
|
||||
@@ -502,19 +506,31 @@ func init() {
|
||||
|
||||
// metrics of pod and container
|
||||
RegistryMetricCreateInput("pod_cpu", "Pod cpu", monitor.METRIC_RES_TYPE_CONTAINER,
|
||||
monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
|
||||
monitor.METRIC_DATABASE_TELE, 4, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("usage_rate", "Pod cpu usage rate", monitor.METRIC_UNIT_PERCENT, 1),
|
||||
})
|
||||
RegistryMetricCreateInput("pod_mem", "Pod memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
|
||||
RegistryMetricCreateInput("pod_mem", "Pod memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 5, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("usage_rate", "Pod memory usage rate", monitor.METRIC_UNIT_PERCENT, 1),
|
||||
newMetricFieldCreateInput("working_set_bytes", "Pod memory working set bytes", monitor.METRIC_UNIT_BYTE, 2),
|
||||
})
|
||||
RegistryMetricCreateInput("pod_volume", "Pod volume",
|
||||
monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 6, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("total", "Pod volume total size", monitor.METRIC_UNIT_BYTE, 1),
|
||||
newMetricFieldCreateInput("free", "Pod volume free size", monitor.METRIC_UNIT_BYTE, 2),
|
||||
newMetricFieldCreateInput("used", "Pod volume used size", monitor.METRIC_UNIT_BYTE, 3),
|
||||
newMetricFieldCreateInput("used_percent", "Pod volume used percent", monitor.METRIC_UNIT_PERCENT, 4),
|
||||
newMetricFieldCreateInput("inodes_total", "Pod volume inodes total count", monitor.METRIC_UNIT_COUNT, 5),
|
||||
newMetricFieldCreateInput("inodes_free", "Pod volume inodes free count", monitor.METRIC_UNIT_COUNT, 6),
|
||||
newMetricFieldCreateInput("inodes_used", "Pod volume inodes used count", monitor.METRIC_UNIT_COUNT, 7),
|
||||
newMetricFieldCreateInput("inodes_used_percent", "Pod volume inodes used percent", monitor.METRIC_UNIT_PERCENT, 8),
|
||||
})
|
||||
registryNetio("pod_netio", "Pod network traffic", monitor.METRIC_RES_TYPE_CONTAINER, 7)
|
||||
|
||||
RegistryMetricCreateInput("container_cpu", "Container cpu", monitor.METRIC_RES_TYPE_CONTAINER,
|
||||
monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
|
||||
monitor.METRIC_DATABASE_TELE, 8, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("usage_rate", "Container cpu usage rate", monitor.METRIC_UNIT_PERCENT, 1),
|
||||
})
|
||||
RegistryMetricCreateInput("container_mem", "Container memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 3, []monitor.MetricFieldCreateInput{
|
||||
RegistryMetricCreateInput("container_mem", "Container memory", monitor.METRIC_RES_TYPE_CONTAINER, monitor.METRIC_DATABASE_TELE, 9, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("usage_rate", "Container memory usage rate", monitor.METRIC_UNIT_PERCENT, 1),
|
||||
newMetricFieldCreateInput("working_set_bytes", "Container memory working set bytes", monitor.METRIC_UNIT_BYTE, 2),
|
||||
})
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
var invoke common.Invoker = common.Invoke{}
|
||||
|
||||
type UsageStat struct {
|
||||
Path string `json:"path"`
|
||||
Fstype string `json:"fstype"`
|
||||
Total uint64 `json:"total"`
|
||||
Free uint64 `json:"free"`
|
||||
Used uint64 `json:"used"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
InodesTotal uint64 `json:"inodesTotal"`
|
||||
InodesUsed uint64 `json:"inodesUsed"`
|
||||
InodesFree uint64 `json:"inodesFree"`
|
||||
InodesUsedPercent float64 `json:"inodesUsedPercent"`
|
||||
}
|
||||
|
||||
type PartitionStat struct {
|
||||
Device string `json:"device"`
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
Fstype string `json:"fstype"`
|
||||
Opts []string `json:"opts"`
|
||||
}
|
||||
|
||||
type IOCountersStat struct {
|
||||
ReadCount uint64 `json:"readCount"`
|
||||
MergedReadCount uint64 `json:"mergedReadCount"`
|
||||
WriteCount uint64 `json:"writeCount"`
|
||||
MergedWriteCount uint64 `json:"mergedWriteCount"`
|
||||
ReadBytes uint64 `json:"readBytes"`
|
||||
WriteBytes uint64 `json:"writeBytes"`
|
||||
ReadTime uint64 `json:"readTime"`
|
||||
WriteTime uint64 `json:"writeTime"`
|
||||
IopsInProgress uint64 `json:"iopsInProgress"`
|
||||
IoTime uint64 `json:"ioTime"`
|
||||
WeightedIO uint64 `json:"weightedIO"`
|
||||
Name string `json:"name"`
|
||||
SerialNumber string `json:"serialNumber"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
func (d UsageStat) String() string {
|
||||
s, _ := json.Marshal(d)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (d PartitionStat) String() string {
|
||||
s, _ := json.Marshal(d)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (d IOCountersStat) String() string {
|
||||
s, _ := json.Marshal(d)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Usage returns a file system usage. path is a filesystem path such
|
||||
// as "/", not device file path like "/dev/vda1". If you want to use
|
||||
// a return value of disk.Partitions, use "Mountpoint" not "Device".
|
||||
func Usage(path string) (*UsageStat, error) {
|
||||
return UsageWithContext(context.Background(), path)
|
||||
}
|
||||
|
||||
// Partitions returns disk partitions. If all is false, returns
|
||||
// physical devices only (e.g. hard disks, cd-rom drives, USB keys)
|
||||
// and ignore all others (e.g. memory partitions such as /dev/shm)
|
||||
//
|
||||
// 'all' argument is ignored for BSD, see: https://github.com/giampaolo/psutil/issues/906
|
||||
func Partitions(all bool) ([]PartitionStat, error) {
|
||||
return PartitionsWithContext(context.Background(), all)
|
||||
}
|
||||
|
||||
func IOCounters(names ...string) (map[string]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), names...)
|
||||
}
|
||||
|
||||
// SerialNumber returns Serial Number of given device or empty string
|
||||
// on error. Name of device is expected, eg. /dev/sda
|
||||
func SerialNumber(name string) (string, error) {
|
||||
return SerialNumberWithContext(context.Background(), name)
|
||||
}
|
||||
|
||||
// Label returns label of given device or empty string on error.
|
||||
// Name of device is expected, eg. /dev/sda
|
||||
// Supports label based on devicemapper name
|
||||
// See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm
|
||||
func Label(name string) (string, error) {
|
||||
return LabelWithContext(context.Background(), name)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
//go:build aix
|
||||
// +build aix
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
//go:build aix && cgo
|
||||
// +build aix,cgo
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/power-devops/perfstat"
|
||||
)
|
||||
|
||||
var FSType map[int]string
|
||||
|
||||
func init() {
|
||||
FSType = map[int]string{
|
||||
0: "jfs2", 1: "namefs", 2: "nfs", 3: "jfs", 5: "cdrom", 6: "proc",
|
||||
16: "special-fs", 17: "cache-fs", 18: "nfs3", 19: "automount-fs", 20: "pool-fs", 32: "vxfs",
|
||||
33: "veritas-fs", 34: "udfs", 35: "nfs4", 36: "nfs4-pseudo", 37: "smbfs", 38: "mcr-pseudofs",
|
||||
39: "ahafs", 40: "sterm-nfs", 41: "asmfs",
|
||||
}
|
||||
}
|
||||
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
f, err := perfstat.FileSystemStat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]PartitionStat, len(f))
|
||||
|
||||
for _, fs := range f {
|
||||
fstyp, exists := FSType[fs.FSType]
|
||||
if !exists {
|
||||
fstyp = "unknown"
|
||||
}
|
||||
info := PartitionStat{
|
||||
Device: fs.Device,
|
||||
Mountpoint: fs.MountPoint,
|
||||
Fstype: fstyp,
|
||||
}
|
||||
ret = append(ret, info)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
|
||||
f, err := perfstat.FileSystemStat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocksize := uint64(512)
|
||||
for _, fs := range f {
|
||||
if path == fs.MountPoint {
|
||||
fstyp, exists := FSType[fs.FSType]
|
||||
if !exists {
|
||||
fstyp = "unknown"
|
||||
}
|
||||
info := UsageStat{
|
||||
Path: path,
|
||||
Fstype: fstyp,
|
||||
Total: uint64(fs.TotalBlocks) * blocksize,
|
||||
Free: uint64(fs.FreeBlocks) * blocksize,
|
||||
Used: uint64(fs.TotalBlocks-fs.FreeBlocks) * blocksize,
|
||||
InodesTotal: uint64(fs.TotalInodes),
|
||||
InodesFree: uint64(fs.FreeInodes),
|
||||
InodesUsed: uint64(fs.TotalInodes - fs.FreeInodes),
|
||||
}
|
||||
info.UsedPercent = (float64(info.Used) / float64(info.Total)) * 100.0
|
||||
info.InodesUsedPercent = (float64(info.InodesUsed) / float64(info.InodesTotal)) * 100.0
|
||||
return &info, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("mountpoint %s not found", path)
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
//go:build aix && !cgo
|
||||
// +build aix,!cgo
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
var whiteSpaces = regexp.MustCompile(`\s+`)
|
||||
var startBlank = regexp.MustCompile(`^\s+`)
|
||||
|
||||
var ignoreFSType = map[string]bool{"procfs": true}
|
||||
var FSType = map[int]string{
|
||||
0: "jfs2", 1: "namefs", 2: "nfs", 3: "jfs", 5: "cdrom", 6: "proc",
|
||||
16: "special-fs", 17: "cache-fs", 18: "nfs3", 19: "automount-fs", 20: "pool-fs", 32: "vxfs",
|
||||
33: "veritas-fs", 34: "udfs", 35: "nfs4", 36: "nfs4-pseudo", 37: "smbfs", 38: "mcr-pseudofs",
|
||||
39: "ahafs", 40: "sterm-nfs", 41: "asmfs",
|
||||
}
|
||||
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
var ret []PartitionStat
|
||||
|
||||
out, err := invoke.CommandWithContext(ctx, "mount")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parse head lines for column names
|
||||
colidx := make(map[string]int)
|
||||
lines := strings.Split(string(out), "\n")
|
||||
if len(lines) < 3 {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
idx := 0
|
||||
start := 0
|
||||
finished := false
|
||||
for pos, ch := range lines[1] {
|
||||
if ch == ' ' && ! finished {
|
||||
name := strings.TrimSpace(lines[0][start:pos])
|
||||
colidx[name] = idx
|
||||
finished = true
|
||||
} else if ch == '-' && finished {
|
||||
idx++
|
||||
start = pos
|
||||
finished = false
|
||||
}
|
||||
}
|
||||
name := strings.TrimSpace(lines[0][start:len(lines[1])])
|
||||
colidx[name] = idx
|
||||
|
||||
for idx := 2; idx < len(lines); idx++ {
|
||||
line := lines[idx]
|
||||
if startBlank.MatchString(line) {
|
||||
line = "localhost" + line
|
||||
}
|
||||
p := whiteSpaces.Split(lines[idx], 6)
|
||||
if len(p) < 5 || ignoreFSType[p[colidx["vfs"]]] {
|
||||
continue
|
||||
}
|
||||
d := PartitionStat{
|
||||
Device: p[colidx["mounted"]],
|
||||
Mountpoint: p[colidx["mounted over"]],
|
||||
Fstype: p[colidx["vfs"]],
|
||||
Opts: strings.Split(p[colidx["options"]], ","),
|
||||
}
|
||||
|
||||
ret = append(ret, d)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getFsType(stat unix.Statfs_t) string {
|
||||
return FSType[int(stat.Vfstype)]
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// PartitionsWithContext returns disk partition.
|
||||
// 'all' argument is ignored, see: https://github.com/giampaolo/psutil/issues/906
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
var ret []PartitionStat
|
||||
|
||||
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
fs := make([]unix.Statfs_t, count)
|
||||
if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
for _, stat := range fs {
|
||||
opts := []string{"rw"}
|
||||
if stat.Flags&unix.MNT_RDONLY != 0 {
|
||||
opts = []string{"ro"}
|
||||
}
|
||||
if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
|
||||
opts = append(opts, "sync")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOEXEC != 0 {
|
||||
opts = append(opts, "noexec")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOSUID != 0 {
|
||||
opts = append(opts, "nosuid")
|
||||
}
|
||||
if stat.Flags&unix.MNT_UNION != 0 {
|
||||
opts = append(opts, "union")
|
||||
}
|
||||
if stat.Flags&unix.MNT_ASYNC != 0 {
|
||||
opts = append(opts, "async")
|
||||
}
|
||||
if stat.Flags&unix.MNT_DONTBROWSE != 0 {
|
||||
opts = append(opts, "nobrowse")
|
||||
}
|
||||
if stat.Flags&unix.MNT_AUTOMOUNTED != 0 {
|
||||
opts = append(opts, "automounted")
|
||||
}
|
||||
if stat.Flags&unix.MNT_JOURNALED != 0 {
|
||||
opts = append(opts, "journaled")
|
||||
}
|
||||
if stat.Flags&unix.MNT_MULTILABEL != 0 {
|
||||
opts = append(opts, "multilabel")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOATIME != 0 {
|
||||
opts = append(opts, "noatime")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NODEV != 0 {
|
||||
opts = append(opts, "nodev")
|
||||
}
|
||||
d := PartitionStat{
|
||||
Device: common.ByteToString(stat.Mntfromname[:]),
|
||||
Mountpoint: common.ByteToString(stat.Mntonname[:]),
|
||||
Fstype: common.ByteToString(stat.Fstypename[:]),
|
||||
Opts: opts,
|
||||
}
|
||||
|
||||
ret = append(ret, d)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getFsType(stat unix.Statfs_t) string {
|
||||
return common.ByteToString(stat.Fstypename[:])
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
//go:build darwin && cgo
|
||||
// +build darwin,cgo
|
||||
|
||||
package disk
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -framework CoreFoundation -framework IOKit
|
||||
#include <stdint.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "iostat_darwin.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
var buf [C.NDRIVE]C.DriveStats
|
||||
n, err := C.gopsutil_v3_readdrivestat(&buf[0], C.int(len(buf)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make(map[string]IOCountersStat, 0)
|
||||
for i := 0; i < int(n); i++ {
|
||||
d := IOCountersStat{
|
||||
ReadBytes: uint64(buf[i].read),
|
||||
WriteBytes: uint64(buf[i].written),
|
||||
ReadCount: uint64(buf[i].nread),
|
||||
WriteCount: uint64(buf[i].nwrite),
|
||||
ReadTime: uint64(buf[i].readtime / 1000 / 1000), // note: read/write time are in ns, but we want ms.
|
||||
WriteTime: uint64(buf[i].writetime / 1000 / 1000),
|
||||
IoTime: uint64((buf[i].readtime + buf[i].writetime) / 1000 / 1000),
|
||||
Name: C.GoString(&buf[i].name[0]),
|
||||
}
|
||||
if len(names) > 0 && !common.StringsHas(names, d.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
ret[d.Name] = d
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
//go:build darwin && !cgo
|
||||
// +build darwin,!cgo
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//go:build !darwin && !linux && !freebsd && !openbsd && !windows && !solaris && !aix
|
||||
// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris,!aix
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
return []PartitionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
)
|
||||
|
||||
// PartitionsWithContext returns disk partition.
|
||||
// 'all' argument is ignored, see: https://github.com/giampaolo/psutil/issues/906
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
var ret []PartitionStat
|
||||
|
||||
// get length
|
||||
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
fs := make([]unix.Statfs_t, count)
|
||||
if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
for _, stat := range fs {
|
||||
opts := []string{"rw"}
|
||||
if stat.Flags&unix.MNT_RDONLY != 0 {
|
||||
opts = []string{"ro"}
|
||||
}
|
||||
if stat.Flags&unix.MNT_SYNCHRONOUS != 0 {
|
||||
opts = append(opts, "sync")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOEXEC != 0 {
|
||||
opts = append(opts, "noexec")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOSUID != 0 {
|
||||
opts = append(opts, "nosuid")
|
||||
}
|
||||
if stat.Flags&unix.MNT_UNION != 0 {
|
||||
opts = append(opts, "union")
|
||||
}
|
||||
if stat.Flags&unix.MNT_ASYNC != 0 {
|
||||
opts = append(opts, "async")
|
||||
}
|
||||
if stat.Flags&unix.MNT_SUIDDIR != 0 {
|
||||
opts = append(opts, "suiddir")
|
||||
}
|
||||
if stat.Flags&unix.MNT_SOFTDEP != 0 {
|
||||
opts = append(opts, "softdep")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOSYMFOLLOW != 0 {
|
||||
opts = append(opts, "nosymfollow")
|
||||
}
|
||||
if stat.Flags&unix.MNT_GJOURNAL != 0 {
|
||||
opts = append(opts, "gjournal")
|
||||
}
|
||||
if stat.Flags&unix.MNT_MULTILABEL != 0 {
|
||||
opts = append(opts, "multilabel")
|
||||
}
|
||||
if stat.Flags&unix.MNT_ACLS != 0 {
|
||||
opts = append(opts, "acls")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOATIME != 0 {
|
||||
opts = append(opts, "noatime")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOCLUSTERR != 0 {
|
||||
opts = append(opts, "noclusterr")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NOCLUSTERW != 0 {
|
||||
opts = append(opts, "noclusterw")
|
||||
}
|
||||
if stat.Flags&unix.MNT_NFS4ACLS != 0 {
|
||||
opts = append(opts, "nfsv4acls")
|
||||
}
|
||||
|
||||
d := PartitionStat{
|
||||
Device: common.ByteToString(stat.Mntfromname[:]),
|
||||
Mountpoint: common.ByteToString(stat.Mntonname[:]),
|
||||
Fstype: common.ByteToString(stat.Fstypename[:]),
|
||||
Opts: opts,
|
||||
}
|
||||
|
||||
ret = append(ret, d)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
// statinfo->devinfo->devstat
|
||||
// /usr/include/devinfo.h
|
||||
ret := make(map[string]IOCountersStat)
|
||||
|
||||
r, err := unix.Sysctl("kern.devstat.all")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := []byte(r)
|
||||
length := len(buf)
|
||||
|
||||
count := int(uint64(length) / uint64(sizeOfdevstat))
|
||||
|
||||
buf = buf[8:] // devstat.all has version in the head.
|
||||
// parse buf to devstat
|
||||
for i := 0; i < count; i++ {
|
||||
b := buf[i*sizeOfdevstat : i*sizeOfdevstat+sizeOfdevstat]
|
||||
d, err := parsedevstat(b)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
un := strconv.Itoa(int(d.Unit_number))
|
||||
name := common.IntToString(d.Device_name[:]) + un
|
||||
|
||||
if len(names) > 0 && !common.StringsHas(names, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
ds := IOCountersStat{
|
||||
ReadCount: d.Operations[devstat_READ],
|
||||
WriteCount: d.Operations[devstat_WRITE],
|
||||
ReadBytes: d.Bytes[devstat_READ],
|
||||
WriteBytes: d.Bytes[devstat_WRITE],
|
||||
ReadTime: uint64(d.Duration[devstat_READ].Compute() * 1000),
|
||||
WriteTime: uint64(d.Duration[devstat_WRITE].Compute() * 1000),
|
||||
IoTime: uint64(d.Busy_time.Compute() * 1000),
|
||||
Name: name,
|
||||
}
|
||||
ds.SerialNumber, _ = SerialNumberWithContext(ctx, name)
|
||||
ret[name] = ds
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (b bintime) Compute() float64 {
|
||||
BINTIME_SCALE := 5.42101086242752217003726400434970855712890625e-20
|
||||
return float64(b.Sec) + float64(b.Frac)*BINTIME_SCALE
|
||||
}
|
||||
|
||||
// BT2LD(time) ((long double)(time).sec + (time).frac * BINTIME_SCALE)
|
||||
|
||||
func parsedevstat(buf []byte) (devstat, error) {
|
||||
var ds devstat
|
||||
br := bytes.NewReader(buf)
|
||||
// err := binary.Read(br, binary.LittleEndian, &ds)
|
||||
err := common.Read(br, binary.LittleEndian, &ds)
|
||||
if err != nil {
|
||||
return ds, err
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func getFsType(stat unix.Statfs_t) string {
|
||||
return common.ByteToString(stat.Fstypename[:])
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
geomOut, err := invoke.CommandWithContext(ctx, "geom", "disk", "list", name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("exec geom: %w", err)
|
||||
}
|
||||
s := bufio.NewScanner(bytes.NewReader(geomOut))
|
||||
serial := ""
|
||||
for s.Scan() {
|
||||
flds := strings.Fields(s.Text())
|
||||
if len(flds) == 2 && flds[0] == "ident:" {
|
||||
if flds[1] != "(null)" {
|
||||
serial = flds[1]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if err = s.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return serial, nil
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x4
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x4
|
||||
sizeofLongLong = 0x8
|
||||
sizeofLongDouble = 0x8
|
||||
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfdevstat = 0xf0
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int32
|
||||
_C_long_long int64
|
||||
_C_long_double int64
|
||||
)
|
||||
|
||||
type devstat struct {
|
||||
Sequence0 uint32
|
||||
Allocated int32
|
||||
Start_count uint32
|
||||
End_count uint32
|
||||
Busy_from bintime
|
||||
Dev_links _Ctype_struct___0
|
||||
Device_number uint32
|
||||
Device_name [16]int8
|
||||
Unit_number int32
|
||||
Bytes [4]uint64
|
||||
Operations [4]uint64
|
||||
Duration [4]bintime
|
||||
Busy_time bintime
|
||||
Creation_time bintime
|
||||
Block_size uint32
|
||||
Tag_types [3]uint64
|
||||
Flags uint32
|
||||
Device_type uint32
|
||||
Priority uint32
|
||||
Id *byte
|
||||
Sequence1 uint32
|
||||
}
|
||||
|
||||
type bintime struct {
|
||||
Sec int32
|
||||
Frac uint64
|
||||
}
|
||||
|
||||
type _Ctype_struct___0 struct {
|
||||
Empty uint32
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
sizeofLongDouble = 0x8
|
||||
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfdevstat = 0x120
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
_C_long_double int64
|
||||
)
|
||||
|
||||
type devstat struct {
|
||||
Sequence0 uint32
|
||||
Allocated int32
|
||||
Start_count uint32
|
||||
End_count uint32
|
||||
Busy_from bintime
|
||||
Dev_links _Ctype_struct___0
|
||||
Device_number uint32
|
||||
Device_name [16]int8
|
||||
Unit_number int32
|
||||
Bytes [4]uint64
|
||||
Operations [4]uint64
|
||||
Duration [4]bintime
|
||||
Busy_time bintime
|
||||
Creation_time bintime
|
||||
Block_size uint32
|
||||
Pad_cgo_0 [4]byte
|
||||
Tag_types [3]uint64
|
||||
Flags uint32
|
||||
Device_type uint32
|
||||
Priority uint32
|
||||
Pad_cgo_1 [4]byte
|
||||
ID *byte
|
||||
Sequence1 uint32
|
||||
Pad_cgo_2 [4]byte
|
||||
}
|
||||
|
||||
type bintime struct {
|
||||
Sec int64
|
||||
Frac uint64
|
||||
}
|
||||
|
||||
type _Ctype_struct___0 struct {
|
||||
Empty uint64
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x4
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x4
|
||||
sizeofLongLong = 0x8
|
||||
sizeofLongDouble = 0x8
|
||||
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfdevstat = 0xf0
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int32
|
||||
_C_long_long int64
|
||||
_C_long_double int64
|
||||
)
|
||||
|
||||
type devstat struct {
|
||||
Sequence0 uint32
|
||||
Allocated int32
|
||||
Start_count uint32
|
||||
End_count uint32
|
||||
Busy_from bintime
|
||||
Dev_links _Ctype_struct___0
|
||||
Device_number uint32
|
||||
Device_name [16]int8
|
||||
Unit_number int32
|
||||
Bytes [4]uint64
|
||||
Operations [4]uint64
|
||||
Duration [4]bintime
|
||||
Busy_time bintime
|
||||
Creation_time bintime
|
||||
Block_size uint32
|
||||
Tag_types [3]uint64
|
||||
Flags uint32
|
||||
Device_type uint32
|
||||
Priority uint32
|
||||
Id *byte
|
||||
Sequence1 uint32
|
||||
}
|
||||
|
||||
type bintime struct {
|
||||
Sec int32
|
||||
Frac uint64
|
||||
}
|
||||
|
||||
type _Ctype_struct___0 struct {
|
||||
Empty uint32
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
//go:build freebsd && arm64
|
||||
// +build freebsd,arm64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs disk/types_freebsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
sizeofLongDouble = 0x8
|
||||
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfdevstat = 0x120
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
_C_long_double int64
|
||||
)
|
||||
|
||||
type devstat struct {
|
||||
Sequence0 uint32
|
||||
Allocated int32
|
||||
Start_count uint32
|
||||
End_count uint32
|
||||
Busy_from bintime
|
||||
Dev_links _Ctype_struct___0
|
||||
Device_number uint32
|
||||
Device_name [16]int8
|
||||
Unit_number int32
|
||||
Bytes [4]uint64
|
||||
Operations [4]uint64
|
||||
Duration [4]bintime
|
||||
Busy_time bintime
|
||||
Creation_time bintime
|
||||
Block_size uint32
|
||||
Tag_types [3]uint64
|
||||
Flags uint32
|
||||
Device_type uint32
|
||||
Priority uint32
|
||||
Id *byte
|
||||
Sequence1 uint32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
type bintime struct {
|
||||
Sec int64
|
||||
Frac uint64
|
||||
}
|
||||
|
||||
type _Ctype_struct___0 struct {
|
||||
Empty uint64
|
||||
}
|
||||
+538
@@ -0,0 +1,538 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
sectorSize = 512
|
||||
)
|
||||
|
||||
const (
|
||||
// man statfs
|
||||
ADFS_SUPER_MAGIC = 0xadf5
|
||||
AFFS_SUPER_MAGIC = 0xADFF
|
||||
BDEVFS_MAGIC = 0x62646576
|
||||
BEFS_SUPER_MAGIC = 0x42465331
|
||||
BFS_MAGIC = 0x1BADFACE
|
||||
BINFMTFS_MAGIC = 0x42494e4d
|
||||
BTRFS_SUPER_MAGIC = 0x9123683E
|
||||
CGROUP_SUPER_MAGIC = 0x27e0eb
|
||||
CIFS_MAGIC_NUMBER = 0xFF534D42
|
||||
CODA_SUPER_MAGIC = 0x73757245
|
||||
COH_SUPER_MAGIC = 0x012FF7B7
|
||||
CRAMFS_MAGIC = 0x28cd3d45
|
||||
DEBUGFS_MAGIC = 0x64626720
|
||||
DEVFS_SUPER_MAGIC = 0x1373
|
||||
DEVPTS_SUPER_MAGIC = 0x1cd1
|
||||
EFIVARFS_MAGIC = 0xde5e81e4
|
||||
EFS_SUPER_MAGIC = 0x00414A53
|
||||
EXT_SUPER_MAGIC = 0x137D
|
||||
EXT2_OLD_SUPER_MAGIC = 0xEF51
|
||||
EXT2_SUPER_MAGIC = 0xEF53
|
||||
EXT3_SUPER_MAGIC = 0xEF53
|
||||
EXT4_SUPER_MAGIC = 0xEF53
|
||||
FUSE_SUPER_MAGIC = 0x65735546
|
||||
FUTEXFS_SUPER_MAGIC = 0xBAD1DEA
|
||||
HFS_SUPER_MAGIC = 0x4244
|
||||
HFSPLUS_SUPER_MAGIC = 0x482b
|
||||
HOSTFS_SUPER_MAGIC = 0x00c0ffee
|
||||
HPFS_SUPER_MAGIC = 0xF995E849
|
||||
HUGETLBFS_MAGIC = 0x958458f6
|
||||
ISOFS_SUPER_MAGIC = 0x9660
|
||||
JFFS2_SUPER_MAGIC = 0x72b6
|
||||
JFS_SUPER_MAGIC = 0x3153464a
|
||||
MINIX_SUPER_MAGIC = 0x137F /* orig. minix */
|
||||
MINIX_SUPER_MAGIC2 = 0x138F /* 30 char minix */
|
||||
MINIX2_SUPER_MAGIC = 0x2468 /* minix V2 */
|
||||
MINIX2_SUPER_MAGIC2 = 0x2478 /* minix V2, 30 char names */
|
||||
MINIX3_SUPER_MAGIC = 0x4d5a /* minix V3 fs, 60 char names */
|
||||
MQUEUE_MAGIC = 0x19800202
|
||||
MSDOS_SUPER_MAGIC = 0x4d44
|
||||
NCP_SUPER_MAGIC = 0x564c
|
||||
NFS_SUPER_MAGIC = 0x6969
|
||||
NILFS_SUPER_MAGIC = 0x3434
|
||||
NTFS_SB_MAGIC = 0x5346544e
|
||||
OCFS2_SUPER_MAGIC = 0x7461636f
|
||||
OPENPROM_SUPER_MAGIC = 0x9fa1
|
||||
PIPEFS_MAGIC = 0x50495045
|
||||
PROC_SUPER_MAGIC = 0x9fa0
|
||||
PSTOREFS_MAGIC = 0x6165676C
|
||||
QNX4_SUPER_MAGIC = 0x002f
|
||||
QNX6_SUPER_MAGIC = 0x68191122
|
||||
RAMFS_MAGIC = 0x858458f6
|
||||
REISERFS_SUPER_MAGIC = 0x52654973
|
||||
ROMFS_MAGIC = 0x7275
|
||||
SELINUX_MAGIC = 0xf97cff8c
|
||||
SMACK_MAGIC = 0x43415d53
|
||||
SMB_SUPER_MAGIC = 0x517B
|
||||
SOCKFS_MAGIC = 0x534F434B
|
||||
SQUASHFS_MAGIC = 0x73717368
|
||||
SYSFS_MAGIC = 0x62656572
|
||||
SYSV2_SUPER_MAGIC = 0x012FF7B6
|
||||
SYSV4_SUPER_MAGIC = 0x012FF7B5
|
||||
TMPFS_MAGIC = 0x01021994
|
||||
UDF_SUPER_MAGIC = 0x15013346
|
||||
UFS_MAGIC = 0x00011954
|
||||
USBDEVICE_SUPER_MAGIC = 0x9fa2
|
||||
V9FS_MAGIC = 0x01021997
|
||||
VXFS_SUPER_MAGIC = 0xa501FCF5
|
||||
XENFS_SUPER_MAGIC = 0xabba1974
|
||||
XENIX_SUPER_MAGIC = 0x012FF7B4
|
||||
XFS_SUPER_MAGIC = 0x58465342
|
||||
_XIAFS_SUPER_MAGIC = 0x012FD16D
|
||||
|
||||
AFS_SUPER_MAGIC = 0x5346414F
|
||||
AUFS_SUPER_MAGIC = 0x61756673
|
||||
ANON_INODE_FS_SUPER_MAGIC = 0x09041934
|
||||
BPF_FS_MAGIC = 0xCAFE4A11
|
||||
CEPH_SUPER_MAGIC = 0x00C36400
|
||||
CGROUP2_SUPER_MAGIC = 0x63677270
|
||||
CONFIGFS_MAGIC = 0x62656570
|
||||
ECRYPTFS_SUPER_MAGIC = 0xF15F
|
||||
F2FS_SUPER_MAGIC = 0xF2F52010
|
||||
FAT_SUPER_MAGIC = 0x4006
|
||||
FHGFS_SUPER_MAGIC = 0x19830326
|
||||
FUSEBLK_SUPER_MAGIC = 0x65735546
|
||||
FUSECTL_SUPER_MAGIC = 0x65735543
|
||||
GFS_SUPER_MAGIC = 0x1161970
|
||||
GPFS_SUPER_MAGIC = 0x47504653
|
||||
MTD_INODE_FS_SUPER_MAGIC = 0x11307854
|
||||
INOTIFYFS_SUPER_MAGIC = 0x2BAD1DEA
|
||||
ISOFS_R_WIN_SUPER_MAGIC = 0x4004
|
||||
ISOFS_WIN_SUPER_MAGIC = 0x4000
|
||||
JFFS_SUPER_MAGIC = 0x07C0
|
||||
KAFS_SUPER_MAGIC = 0x6B414653
|
||||
LUSTRE_SUPER_MAGIC = 0x0BD00BD0
|
||||
NFSD_SUPER_MAGIC = 0x6E667364
|
||||
NSFS_MAGIC = 0x6E736673
|
||||
PANFS_SUPER_MAGIC = 0xAAD7AAEA
|
||||
RPC_PIPEFS_SUPER_MAGIC = 0x67596969
|
||||
SECURITYFS_SUPER_MAGIC = 0x73636673
|
||||
TRACEFS_MAGIC = 0x74726163
|
||||
UFS_BYTESWAPPED_SUPER_MAGIC = 0x54190100
|
||||
VMHGFS_SUPER_MAGIC = 0xBACBACBC
|
||||
VZFS_SUPER_MAGIC = 0x565A4653
|
||||
ZFS_SUPER_MAGIC = 0x2FC12FC1
|
||||
)
|
||||
|
||||
// coreutils/src/stat.c
|
||||
var fsTypeMap = map[int64]string{
|
||||
ADFS_SUPER_MAGIC: "adfs", /* 0xADF5 local */
|
||||
AFFS_SUPER_MAGIC: "affs", /* 0xADFF local */
|
||||
AFS_SUPER_MAGIC: "afs", /* 0x5346414F remote */
|
||||
ANON_INODE_FS_SUPER_MAGIC: "anon-inode FS", /* 0x09041934 local */
|
||||
AUFS_SUPER_MAGIC: "aufs", /* 0x61756673 remote */
|
||||
// AUTOFS_SUPER_MAGIC: "autofs", /* 0x0187 local */
|
||||
BEFS_SUPER_MAGIC: "befs", /* 0x42465331 local */
|
||||
BDEVFS_MAGIC: "bdevfs", /* 0x62646576 local */
|
||||
BFS_MAGIC: "bfs", /* 0x1BADFACE local */
|
||||
BINFMTFS_MAGIC: "binfmt_misc", /* 0x42494E4D local */
|
||||
BPF_FS_MAGIC: "bpf", /* 0xCAFE4A11 local */
|
||||
BTRFS_SUPER_MAGIC: "btrfs", /* 0x9123683E local */
|
||||
CEPH_SUPER_MAGIC: "ceph", /* 0x00C36400 remote */
|
||||
CGROUP_SUPER_MAGIC: "cgroupfs", /* 0x0027E0EB local */
|
||||
CGROUP2_SUPER_MAGIC: "cgroup2fs", /* 0x63677270 local */
|
||||
CIFS_MAGIC_NUMBER: "cifs", /* 0xFF534D42 remote */
|
||||
CODA_SUPER_MAGIC: "coda", /* 0x73757245 remote */
|
||||
COH_SUPER_MAGIC: "coh", /* 0x012FF7B7 local */
|
||||
CONFIGFS_MAGIC: "configfs", /* 0x62656570 local */
|
||||
CRAMFS_MAGIC: "cramfs", /* 0x28CD3D45 local */
|
||||
DEBUGFS_MAGIC: "debugfs", /* 0x64626720 local */
|
||||
DEVFS_SUPER_MAGIC: "devfs", /* 0x1373 local */
|
||||
DEVPTS_SUPER_MAGIC: "devpts", /* 0x1CD1 local */
|
||||
ECRYPTFS_SUPER_MAGIC: "ecryptfs", /* 0xF15F local */
|
||||
EFIVARFS_MAGIC: "efivarfs", /* 0xDE5E81E4 local */
|
||||
EFS_SUPER_MAGIC: "efs", /* 0x00414A53 local */
|
||||
EXT_SUPER_MAGIC: "ext", /* 0x137D local */
|
||||
EXT2_SUPER_MAGIC: "ext2/ext3", /* 0xEF53 local */
|
||||
EXT2_OLD_SUPER_MAGIC: "ext2", /* 0xEF51 local */
|
||||
F2FS_SUPER_MAGIC: "f2fs", /* 0xF2F52010 local */
|
||||
FAT_SUPER_MAGIC: "fat", /* 0x4006 local */
|
||||
FHGFS_SUPER_MAGIC: "fhgfs", /* 0x19830326 remote */
|
||||
FUSEBLK_SUPER_MAGIC: "fuseblk", /* 0x65735546 remote */
|
||||
FUSECTL_SUPER_MAGIC: "fusectl", /* 0x65735543 remote */
|
||||
FUTEXFS_SUPER_MAGIC: "futexfs", /* 0x0BAD1DEA local */
|
||||
GFS_SUPER_MAGIC: "gfs/gfs2", /* 0x1161970 remote */
|
||||
GPFS_SUPER_MAGIC: "gpfs", /* 0x47504653 remote */
|
||||
HFS_SUPER_MAGIC: "hfs", /* 0x4244 local */
|
||||
HFSPLUS_SUPER_MAGIC: "hfsplus", /* 0x482b local */
|
||||
HPFS_SUPER_MAGIC: "hpfs", /* 0xF995E849 local */
|
||||
HUGETLBFS_MAGIC: "hugetlbfs", /* 0x958458F6 local */
|
||||
MTD_INODE_FS_SUPER_MAGIC: "inodefs", /* 0x11307854 local */
|
||||
INOTIFYFS_SUPER_MAGIC: "inotifyfs", /* 0x2BAD1DEA local */
|
||||
ISOFS_SUPER_MAGIC: "isofs", /* 0x9660 local */
|
||||
ISOFS_R_WIN_SUPER_MAGIC: "isofs", /* 0x4004 local */
|
||||
ISOFS_WIN_SUPER_MAGIC: "isofs", /* 0x4000 local */
|
||||
JFFS_SUPER_MAGIC: "jffs", /* 0x07C0 local */
|
||||
JFFS2_SUPER_MAGIC: "jffs2", /* 0x72B6 local */
|
||||
JFS_SUPER_MAGIC: "jfs", /* 0x3153464A local */
|
||||
KAFS_SUPER_MAGIC: "k-afs", /* 0x6B414653 remote */
|
||||
LUSTRE_SUPER_MAGIC: "lustre", /* 0x0BD00BD0 remote */
|
||||
MINIX_SUPER_MAGIC: "minix", /* 0x137F local */
|
||||
MINIX_SUPER_MAGIC2: "minix (30 char.)", /* 0x138F local */
|
||||
MINIX2_SUPER_MAGIC: "minix v2", /* 0x2468 local */
|
||||
MINIX2_SUPER_MAGIC2: "minix v2 (30 char.)", /* 0x2478 local */
|
||||
MINIX3_SUPER_MAGIC: "minix3", /* 0x4D5A local */
|
||||
MQUEUE_MAGIC: "mqueue", /* 0x19800202 local */
|
||||
MSDOS_SUPER_MAGIC: "msdos", /* 0x4D44 local */
|
||||
NCP_SUPER_MAGIC: "novell", /* 0x564C remote */
|
||||
NFS_SUPER_MAGIC: "nfs", /* 0x6969 remote */
|
||||
NFSD_SUPER_MAGIC: "nfsd", /* 0x6E667364 remote */
|
||||
NILFS_SUPER_MAGIC: "nilfs", /* 0x3434 local */
|
||||
NSFS_MAGIC: "nsfs", /* 0x6E736673 local */
|
||||
NTFS_SB_MAGIC: "ntfs", /* 0x5346544E local */
|
||||
OPENPROM_SUPER_MAGIC: "openprom", /* 0x9FA1 local */
|
||||
OCFS2_SUPER_MAGIC: "ocfs2", /* 0x7461636f remote */
|
||||
PANFS_SUPER_MAGIC: "panfs", /* 0xAAD7AAEA remote */
|
||||
PIPEFS_MAGIC: "pipefs", /* 0x50495045 remote */
|
||||
PROC_SUPER_MAGIC: "proc", /* 0x9FA0 local */
|
||||
PSTOREFS_MAGIC: "pstorefs", /* 0x6165676C local */
|
||||
QNX4_SUPER_MAGIC: "qnx4", /* 0x002F local */
|
||||
QNX6_SUPER_MAGIC: "qnx6", /* 0x68191122 local */
|
||||
RAMFS_MAGIC: "ramfs", /* 0x858458F6 local */
|
||||
REISERFS_SUPER_MAGIC: "reiserfs", /* 0x52654973 local */
|
||||
ROMFS_MAGIC: "romfs", /* 0x7275 local */
|
||||
RPC_PIPEFS_SUPER_MAGIC: "rpc_pipefs", /* 0x67596969 local */
|
||||
SECURITYFS_SUPER_MAGIC: "securityfs", /* 0x73636673 local */
|
||||
SELINUX_MAGIC: "selinux", /* 0xF97CFF8C local */
|
||||
SMB_SUPER_MAGIC: "smb", /* 0x517B remote */
|
||||
SOCKFS_MAGIC: "sockfs", /* 0x534F434B local */
|
||||
SQUASHFS_MAGIC: "squashfs", /* 0x73717368 local */
|
||||
SYSFS_MAGIC: "sysfs", /* 0x62656572 local */
|
||||
SYSV2_SUPER_MAGIC: "sysv2", /* 0x012FF7B6 local */
|
||||
SYSV4_SUPER_MAGIC: "sysv4", /* 0x012FF7B5 local */
|
||||
TMPFS_MAGIC: "tmpfs", /* 0x01021994 local */
|
||||
TRACEFS_MAGIC: "tracefs", /* 0x74726163 local */
|
||||
UDF_SUPER_MAGIC: "udf", /* 0x15013346 local */
|
||||
UFS_MAGIC: "ufs", /* 0x00011954 local */
|
||||
UFS_BYTESWAPPED_SUPER_MAGIC: "ufs", /* 0x54190100 local */
|
||||
USBDEVICE_SUPER_MAGIC: "usbdevfs", /* 0x9FA2 local */
|
||||
V9FS_MAGIC: "v9fs", /* 0x01021997 local */
|
||||
VMHGFS_SUPER_MAGIC: "vmhgfs", /* 0xBACBACBC remote */
|
||||
VXFS_SUPER_MAGIC: "vxfs", /* 0xA501FCF5 local */
|
||||
VZFS_SUPER_MAGIC: "vzfs", /* 0x565A4653 local */
|
||||
XENFS_SUPER_MAGIC: "xenfs", /* 0xABBA1974 local */
|
||||
XENIX_SUPER_MAGIC: "xenix", /* 0x012FF7B4 local */
|
||||
XFS_SUPER_MAGIC: "xfs", /* 0x58465342 local */
|
||||
_XIAFS_SUPER_MAGIC: "xia", /* 0x012FD16D local */
|
||||
ZFS_SUPER_MAGIC: "zfs", /* 0x2FC12FC1 local */
|
||||
}
|
||||
|
||||
// readMountFile reads mountinfo or mounts file under the specified root path
|
||||
// (eg, /proc/1, /proc/self, etc)
|
||||
func readMountFile(root string) (lines []string, useMounts bool, filename string, err error) {
|
||||
filename = path.Join(root, "mountinfo")
|
||||
lines, err = common.ReadLines(filename)
|
||||
if err != nil {
|
||||
var pathErr *os.PathError
|
||||
if !errors.As(err, &pathErr) {
|
||||
return
|
||||
}
|
||||
// if kernel does not support 1/mountinfo, fallback to 1/mounts (<2.6.26)
|
||||
useMounts = true
|
||||
filename = path.Join(root, "mounts")
|
||||
lines, err = common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
// by default, try "/proc/1/..." first
|
||||
root := common.HostProc(path.Join("1"))
|
||||
|
||||
// force preference for dirname of HOST_PROC_MOUNTINFO, if set #1271
|
||||
hpmPath := os.Getenv("HOST_PROC_MOUNTINFO")
|
||||
if hpmPath != "" {
|
||||
root = filepath.Dir(hpmPath)
|
||||
}
|
||||
|
||||
lines, useMounts, filename, err := readMountFile(root)
|
||||
if err != nil {
|
||||
if hpmPath != "" { // don't fallback with HOST_PROC_MOUNTINFO
|
||||
return nil, err
|
||||
}
|
||||
// fallback to "/proc/self/..." #1159
|
||||
lines, useMounts, filename, err = readMountFile(common.HostProc(path.Join("self")))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
fs, err := getFileSystems()
|
||||
if err != nil && !all {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]PartitionStat, 0, len(lines))
|
||||
|
||||
for _, line := range lines {
|
||||
var d PartitionStat
|
||||
if useMounts {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
d = PartitionStat{
|
||||
Device: fields[0],
|
||||
Mountpoint: unescapeFstab(fields[1]),
|
||||
Fstype: fields[2],
|
||||
Opts: strings.Fields(fields[3]),
|
||||
}
|
||||
|
||||
if !all {
|
||||
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// a line of 1/mountinfo has the following structure:
|
||||
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||
// (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11)
|
||||
|
||||
// split the mountinfo line by the separator hyphen
|
||||
parts := strings.Split(line, " - ")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("found invalid mountinfo line in file %s: %s ", filename, line)
|
||||
}
|
||||
|
||||
fields := strings.Fields(parts[0])
|
||||
blockDeviceID := fields[2]
|
||||
mountPoint := fields[4]
|
||||
mountOpts := strings.Split(fields[5], ",")
|
||||
|
||||
if rootDir := fields[3]; rootDir != "" && rootDir != "/" {
|
||||
mountOpts = append(mountOpts, "bind")
|
||||
}
|
||||
|
||||
fields = strings.Fields(parts[1])
|
||||
fstype := fields[0]
|
||||
device := fields[1]
|
||||
|
||||
d = PartitionStat{
|
||||
Device: device,
|
||||
Mountpoint: unescapeFstab(mountPoint),
|
||||
Fstype: fstype,
|
||||
Opts: mountOpts,
|
||||
}
|
||||
|
||||
if !all {
|
||||
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(d.Device, "/dev/mapper/") {
|
||||
devpath, err := filepath.EvalSymlinks(common.HostDev(strings.Replace(d.Device, "/dev", "", -1)))
|
||||
if err == nil {
|
||||
d.Device = devpath
|
||||
}
|
||||
}
|
||||
|
||||
// /dev/root is not the real device name
|
||||
// so we get the real device name from its major/minor number
|
||||
if d.Device == "/dev/root" {
|
||||
devpath, err := os.Readlink(common.HostSys("/dev/block/" + blockDeviceID))
|
||||
if err == nil {
|
||||
d.Device = strings.Replace(d.Device, "root", filepath.Base(devpath), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = append(ret, d)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// getFileSystems returns supported filesystems from /proc/filesystems
|
||||
func getFileSystems() ([]string, error) {
|
||||
filename := common.HostProc("filesystems")
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ret []string
|
||||
for _, line := range lines {
|
||||
if !strings.HasPrefix(line, "nodev") {
|
||||
ret = append(ret, strings.TrimSpace(line))
|
||||
continue
|
||||
}
|
||||
t := strings.Split(line, "\t")
|
||||
if len(t) != 2 || t[1] != "zfs" {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, strings.TrimSpace(t[1]))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
filename := common.HostProc("diskstats")
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make(map[string]IOCountersStat)
|
||||
empty := IOCountersStat{}
|
||||
|
||||
// use only basename such as "/dev/sda1" to "sda1"
|
||||
for i, name := range names {
|
||||
names[i] = filepath.Base(name)
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 14 {
|
||||
// malformed line in /proc/diskstats, avoid panic by ignoring.
|
||||
continue
|
||||
}
|
||||
name := fields[2]
|
||||
|
||||
if len(names) > 0 && !common.StringsHas(names, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
reads, err := strconv.ParseUint((fields[3]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
mergedReads, err := strconv.ParseUint((fields[4]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
rbytes, err := strconv.ParseUint((fields[5]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
rtime, err := strconv.ParseUint((fields[6]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
writes, err := strconv.ParseUint((fields[7]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
mergedWrites, err := strconv.ParseUint((fields[8]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
wbytes, err := strconv.ParseUint((fields[9]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
wtime, err := strconv.ParseUint((fields[10]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
iopsInProgress, err := strconv.ParseUint((fields[11]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
iotime, err := strconv.ParseUint((fields[12]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
weightedIO, err := strconv.ParseUint((fields[13]), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
d := IOCountersStat{
|
||||
ReadBytes: rbytes * sectorSize,
|
||||
WriteBytes: wbytes * sectorSize,
|
||||
ReadCount: reads,
|
||||
WriteCount: writes,
|
||||
MergedReadCount: mergedReads,
|
||||
MergedWriteCount: mergedWrites,
|
||||
ReadTime: rtime,
|
||||
WriteTime: wtime,
|
||||
IopsInProgress: iopsInProgress,
|
||||
IoTime: iotime,
|
||||
WeightedIO: weightedIO,
|
||||
}
|
||||
if d == empty {
|
||||
continue
|
||||
}
|
||||
d.Name = name
|
||||
|
||||
d.SerialNumber, _ = SerialNumberWithContext(ctx, name)
|
||||
d.Label, _ = LabelWithContext(ctx, name)
|
||||
|
||||
ret[name] = d
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
var stat unix.Stat_t
|
||||
err := unix.Stat(name, &stat)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
major := unix.Major(uint64(stat.Rdev))
|
||||
minor := unix.Minor(uint64(stat.Rdev))
|
||||
|
||||
// Try to get the serial from udev data
|
||||
udevDataPath := common.HostRun(fmt.Sprintf("udev/data/b%d:%d", major, minor))
|
||||
if udevdata, err := ioutil.ReadFile(udevDataPath); err == nil {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(udevdata))
|
||||
for scanner.Scan() {
|
||||
values := strings.Split(scanner.Text(), "=")
|
||||
if len(values) == 2 && values[0] == "E:ID_SERIAL" {
|
||||
return values[1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get the serial from sysfs, look at the disk device (minor 0) directly
|
||||
// because if it is a partition it is not going to contain any device information
|
||||
devicePath := common.HostSys(fmt.Sprintf("dev/block/%d:0/device", major))
|
||||
model, _ := ioutil.ReadFile(filepath.Join(devicePath, "model"))
|
||||
serial, _ := ioutil.ReadFile(filepath.Join(devicePath, "serial"))
|
||||
if len(model) > 0 && len(serial) > 0 {
|
||||
return fmt.Sprintf("%s_%s", string(model), string(serial)), nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
// Try label based on devicemapper name
|
||||
dmname_filename := common.HostSys(fmt.Sprintf("block/%s/dm/name", name))
|
||||
|
||||
if !common.PathExists(dmname_filename) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
dmname, err := ioutil.ReadFile(dmname_filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(dmname)), nil
|
||||
}
|
||||
|
||||
func getFsType(stat unix.Statfs_t) string {
|
||||
t := int64(stat.Type)
|
||||
ret, ok := fsTypeMap[t]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return ret
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
var ret []PartitionStat
|
||||
|
||||
// get length
|
||||
count, err := unix.Getfsstat(nil, unix.MNT_WAIT)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
fs := make([]unix.Statfs_t, count)
|
||||
if _, err = unix.Getfsstat(fs, unix.MNT_WAIT); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
for _, stat := range fs {
|
||||
opts := []string{"rw"}
|
||||
if stat.F_flags&unix.MNT_RDONLY != 0 {
|
||||
opts = []string{"rw"}
|
||||
}
|
||||
if stat.F_flags&unix.MNT_SYNCHRONOUS != 0 {
|
||||
opts = append(opts, "sync")
|
||||
}
|
||||
if stat.F_flags&unix.MNT_NOEXEC != 0 {
|
||||
opts = append(opts, "noexec")
|
||||
}
|
||||
if stat.F_flags&unix.MNT_NOSUID != 0 {
|
||||
opts = append(opts, "nosuid")
|
||||
}
|
||||
if stat.F_flags&unix.MNT_NODEV != 0 {
|
||||
opts = append(opts, "nodev")
|
||||
}
|
||||
if stat.F_flags&unix.MNT_ASYNC != 0 {
|
||||
opts = append(opts, "async")
|
||||
}
|
||||
if stat.F_flags&unix.MNT_SOFTDEP != 0 {
|
||||
opts = append(opts, "softdep")
|
||||
}
|
||||
if stat.F_flags&unix.MNT_NOATIME != 0 {
|
||||
opts = append(opts, "noatime")
|
||||
}
|
||||
if stat.F_flags&unix.MNT_WXALLOWED != 0 {
|
||||
opts = append(opts, "wxallowed")
|
||||
}
|
||||
|
||||
d := PartitionStat{
|
||||
Device: common.ByteToString(stat.F_mntfromname[:]),
|
||||
Mountpoint: common.ByteToString(stat.F_mntonname[:]),
|
||||
Fstype: common.ByteToString(stat.F_fstypename[:]),
|
||||
Opts: opts,
|
||||
}
|
||||
|
||||
ret = append(ret, d)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
ret := make(map[string]IOCountersStat)
|
||||
|
||||
r, err := unix.SysctlRaw("hw.diskstats")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := []byte(r)
|
||||
length := len(buf)
|
||||
|
||||
count := int(uint64(length) / uint64(sizeOfDiskstats))
|
||||
|
||||
// parse buf to Diskstats
|
||||
for i := 0; i < count; i++ {
|
||||
b := buf[i*sizeOfDiskstats : i*sizeOfDiskstats+sizeOfDiskstats]
|
||||
d, err := parseDiskstats(b)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
name := common.IntToString(d.Name[:])
|
||||
|
||||
if len(names) > 0 && !common.StringsHas(names, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
ds := IOCountersStat{
|
||||
ReadCount: d.Rxfer,
|
||||
WriteCount: d.Wxfer,
|
||||
ReadBytes: d.Rbytes,
|
||||
WriteBytes: d.Wbytes,
|
||||
Name: name,
|
||||
}
|
||||
ret[name] = ds
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// BT2LD(time) ((long double)(time).sec + (time).frac * BINTIME_SCALE)
|
||||
|
||||
func parseDiskstats(buf []byte) (Diskstats, error) {
|
||||
var ds Diskstats
|
||||
br := bytes.NewReader(buf)
|
||||
// err := binary.Read(br, binary.LittleEndian, &ds)
|
||||
err := common.Read(br, binary.LittleEndian, &ds)
|
||||
if err != nil {
|
||||
return ds, err
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
|
||||
stat := unix.Statfs_t{}
|
||||
err := unix.Statfs(path, &stat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bsize := stat.F_bsize
|
||||
|
||||
ret := &UsageStat{
|
||||
Path: path,
|
||||
Fstype: getFsType(stat),
|
||||
Total: (uint64(stat.F_blocks) * uint64(bsize)),
|
||||
Free: (uint64(stat.F_bavail) * uint64(bsize)),
|
||||
InodesTotal: (uint64(stat.F_files)),
|
||||
InodesFree: (uint64(stat.F_ffree)),
|
||||
}
|
||||
|
||||
ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
|
||||
ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
|
||||
ret.Used = (uint64(stat.F_blocks) - uint64(stat.F_bfree)) * uint64(bsize)
|
||||
ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getFsType(stat unix.Statfs_t) string {
|
||||
return common.ByteToString(stat.F_fstypename[:])
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//go:build openbsd && 386
|
||||
// +build openbsd,386
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs disk/types_openbsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfDiskstats = 0x60
|
||||
)
|
||||
|
||||
type Diskstats struct {
|
||||
Name [16]int8
|
||||
Busy int32
|
||||
Rxfer uint64
|
||||
Wxfer uint64
|
||||
Seek uint64
|
||||
Rbytes uint64
|
||||
Wbytes uint64
|
||||
Attachtime Timeval
|
||||
Timestamp Timeval
|
||||
Time Timeval
|
||||
}
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
}
|
||||
|
||||
type Diskstat struct{}
|
||||
type bintime struct{}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs types_openbsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfDiskstats = 0x70
|
||||
)
|
||||
|
||||
type Diskstats struct {
|
||||
Name [16]int8
|
||||
Busy int32
|
||||
Pad_cgo_0 [4]byte
|
||||
Rxfer uint64
|
||||
Wxfer uint64
|
||||
Seek uint64
|
||||
Rbytes uint64
|
||||
Wbytes uint64
|
||||
Attachtime Timeval
|
||||
Timestamp Timeval
|
||||
Time Timeval
|
||||
}
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
type Diskstat struct{}
|
||||
type bintime struct{}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//go:build openbsd && arm
|
||||
// +build openbsd,arm
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs disk/types_openbsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfDiskstats = 0x60
|
||||
)
|
||||
|
||||
type Diskstats struct {
|
||||
Name [16]int8
|
||||
Busy int32
|
||||
Rxfer uint64
|
||||
Wxfer uint64
|
||||
Seek uint64
|
||||
Rbytes uint64
|
||||
Wbytes uint64
|
||||
Attachtime Timeval
|
||||
Timestamp Timeval
|
||||
Time Timeval
|
||||
}
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
}
|
||||
|
||||
type Diskstat struct{}
|
||||
type bintime struct{}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//go:build openbsd && arm64
|
||||
// +build openbsd,arm64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs disk/types_openbsd.go
|
||||
|
||||
package disk
|
||||
|
||||
const (
|
||||
devstat_NO_DATA = 0x00
|
||||
devstat_READ = 0x01
|
||||
devstat_WRITE = 0x02
|
||||
devstat_FREE = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfDiskstats = 0x70
|
||||
)
|
||||
|
||||
type Diskstats struct {
|
||||
Name [16]int8
|
||||
Busy int32
|
||||
Rxfer uint64
|
||||
Wxfer uint64
|
||||
Seek uint64
|
||||
Rbytes uint64
|
||||
Wbytes uint64
|
||||
Attachtime Timeval
|
||||
Timestamp Timeval
|
||||
Time Timeval
|
||||
}
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
type Diskstat struct{}
|
||||
type bintime struct{}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
//go:build solaris
|
||||
// +build solaris
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// _DEFAULT_NUM_MOUNTS is set to `cat /etc/mnttab | wc -l` rounded up to the
|
||||
// nearest power of two.
|
||||
_DEFAULT_NUM_MOUNTS = 32
|
||||
|
||||
// _MNTTAB default place to read mount information
|
||||
_MNTTAB = "/etc/mnttab"
|
||||
)
|
||||
|
||||
// A blacklist of read-only virtual filesystems. Writable filesystems are of
|
||||
// operational concern and must not be included in this list.
|
||||
var fsTypeBlacklist = map[string]struct{}{
|
||||
"ctfs": {},
|
||||
"dev": {},
|
||||
"fd": {},
|
||||
"lofs": {},
|
||||
"lxproc": {},
|
||||
"mntfs": {},
|
||||
"objfs": {},
|
||||
"proc": {},
|
||||
}
|
||||
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
ret := make([]PartitionStat, 0, _DEFAULT_NUM_MOUNTS)
|
||||
|
||||
// Scan mnttab(4)
|
||||
f, err := os.Open(_MNTTAB)
|
||||
if err != nil {
|
||||
}
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = f.Close()
|
||||
} else {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
fields := strings.Split(scanner.Text(), "\t")
|
||||
|
||||
if _, found := fsTypeBlacklist[fields[2]]; found {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, PartitionStat{
|
||||
// NOTE(seanc@): Device isn't exactly accurate: from mnttab(4): "The name
|
||||
// of the resource that has been mounted." Ideally this value would come
|
||||
// from Statvfs_t.Fsid but I'm leaving it to the caller to traverse
|
||||
// unix.Statvfs().
|
||||
Device: fields[0],
|
||||
Mountpoint: fields[1],
|
||||
Fstype: fields[2],
|
||||
Opts: strings.Split(fields[3], ","),
|
||||
})
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("unable to scan %q: %v", _MNTTAB, err)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
|
||||
statvfs := unix.Statvfs_t{}
|
||||
if err := unix.Statvfs(path, &statvfs); err != nil {
|
||||
return nil, fmt.Errorf("unable to call statvfs(2) on %q: %v", path, err)
|
||||
}
|
||||
|
||||
usageStat := &UsageStat{
|
||||
Path: path,
|
||||
Fstype: common.IntToString(statvfs.Basetype[:]),
|
||||
Total: statvfs.Blocks * statvfs.Frsize,
|
||||
Free: statvfs.Bfree * statvfs.Frsize,
|
||||
Used: (statvfs.Blocks - statvfs.Bfree) * statvfs.Frsize,
|
||||
|
||||
// NOTE: ZFS (and FreeBZSD's UFS2) use dynamic inode/dnode allocation.
|
||||
// Explicitly return a near-zero value for InodesUsedPercent so that nothing
|
||||
// attempts to garbage collect based on a lack of available inodes/dnodes.
|
||||
// Similarly, don't use the zero value to prevent divide-by-zero situations
|
||||
// and inject a faux near-zero value. Filesystems evolve. Has your
|
||||
// filesystem evolved? Probably not if you care about the number of
|
||||
// available inodes.
|
||||
InodesTotal: 1024.0 * 1024.0,
|
||||
InodesUsed: 1024.0,
|
||||
InodesFree: math.MaxUint64,
|
||||
InodesUsedPercent: (1024.0 / (1024.0 * 1024.0)) * 100.0,
|
||||
}
|
||||
|
||||
usageStat.UsedPercent = (float64(usageStat.Used) / float64(usageStat.Total)) * 100.0
|
||||
|
||||
return usageStat, nil
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "cfgadm", "-ls", "select=type(disk),cols=ap_id:info,cols2=,noheadings")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("exec cfgadm: %w", err)
|
||||
}
|
||||
|
||||
suf := "::" + strings.TrimPrefix(name, "/dev/")
|
||||
s := bufio.NewScanner(bytes.NewReader(out))
|
||||
for s.Scan() {
|
||||
flds := strings.Fields(s.Text())
|
||||
if strings.HasSuffix(flds[0], suf) {
|
||||
flen := len(flds)
|
||||
if flen >= 3 {
|
||||
for i, f := range flds {
|
||||
if i > 0 && i < flen-1 && f == "SN:" {
|
||||
return flds[i+1], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
//go:build freebsd || linux || darwin || (aix && !cgo)
|
||||
// +build freebsd linux darwin aix,!cgo
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
|
||||
stat := unix.Statfs_t{}
|
||||
err := unix.Statfs(path, &stat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bsize := stat.Bsize
|
||||
|
||||
ret := &UsageStat{
|
||||
Path: unescapeFstab(path),
|
||||
Fstype: getFsType(stat),
|
||||
Total: (uint64(stat.Blocks) * uint64(bsize)),
|
||||
Free: (uint64(stat.Bavail) * uint64(bsize)),
|
||||
InodesTotal: (uint64(stat.Files)),
|
||||
InodesFree: (uint64(stat.Ffree)),
|
||||
}
|
||||
|
||||
ret.Used = (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(bsize)
|
||||
|
||||
if (ret.Used + ret.Free) == 0 {
|
||||
ret.UsedPercent = 0
|
||||
} else {
|
||||
// We don't use ret.Total to calculate percent.
|
||||
// see https://github.com/shirou/gopsutil/issues/562
|
||||
ret.UsedPercent = (float64(ret.Used) / float64(ret.Used+ret.Free)) * 100.0
|
||||
}
|
||||
|
||||
// if could not get InodesTotal, return empty
|
||||
if ret.InodesTotal < ret.InodesFree {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
|
||||
|
||||
if ret.InodesTotal == 0 {
|
||||
ret.InodesUsedPercent = 0
|
||||
} else {
|
||||
ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Unescape escaped octal chars (like space 040, ampersand 046 and backslash 134) to their real value in fstab fields issue#555
|
||||
func unescapeFstab(path string) string {
|
||||
escaped, err := strconv.Unquote(`"` + path + `"`)
|
||||
if err != nil {
|
||||
return path
|
||||
}
|
||||
return escaped
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/internal/common"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
var (
|
||||
procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW")
|
||||
procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW")
|
||||
procGetDriveType = common.Modkernel32.NewProc("GetDriveTypeW")
|
||||
procGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW")
|
||||
)
|
||||
|
||||
var (
|
||||
fileFileCompression = int64(16) // 0x00000010
|
||||
fileReadOnlyVolume = int64(524288) // 0x00080000
|
||||
)
|
||||
|
||||
// diskPerformance is an equivalent representation of DISK_PERFORMANCE in the Windows API.
|
||||
// https://docs.microsoft.com/fr-fr/windows/win32/api/winioctl/ns-winioctl-disk_performance
|
||||
type diskPerformance struct {
|
||||
BytesRead int64
|
||||
BytesWritten int64
|
||||
ReadTime int64
|
||||
WriteTime int64
|
||||
IdleTime int64
|
||||
ReadCount uint32
|
||||
WriteCount uint32
|
||||
QueueDepth uint32
|
||||
SplitCount uint32
|
||||
QueryTime int64
|
||||
StorageDeviceNumber uint32
|
||||
StorageManagerName [8]uint16
|
||||
alignmentPadding uint32 // necessary for 32bit support, see https://github.com/elastic/beats/pull/16553
|
||||
}
|
||||
|
||||
func init() {
|
||||
// enable disk performance counters on Windows Server editions (needs to run as admin)
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Services\PartMgr`, registry.SET_VALUE)
|
||||
if err == nil {
|
||||
key.SetDWordValue("EnableCounterForIoctl", 1)
|
||||
}
|
||||
}
|
||||
|
||||
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
|
||||
lpFreeBytesAvailable := int64(0)
|
||||
lpTotalNumberOfBytes := int64(0)
|
||||
lpTotalNumberOfFreeBytes := int64(0)
|
||||
diskret, _, err := procGetDiskFreeSpaceExW.Call(
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))),
|
||||
uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
|
||||
uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
|
||||
uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))
|
||||
if diskret == 0 {
|
||||
return nil, err
|
||||
}
|
||||
ret := &UsageStat{
|
||||
Path: path,
|
||||
Total: uint64(lpTotalNumberOfBytes),
|
||||
Free: uint64(lpTotalNumberOfFreeBytes),
|
||||
Used: uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes),
|
||||
UsedPercent: (float64(lpTotalNumberOfBytes) - float64(lpTotalNumberOfFreeBytes)) / float64(lpTotalNumberOfBytes) * 100,
|
||||
// InodesTotal: 0,
|
||||
// InodesFree: 0,
|
||||
// InodesUsed: 0,
|
||||
// InodesUsedPercent: 0,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
|
||||
warnings := common.Warnings{
|
||||
Verbose: true,
|
||||
}
|
||||
var ret []PartitionStat
|
||||
lpBuffer := make([]byte, 254)
|
||||
diskret, _, err := procGetLogicalDriveStringsW.Call(
|
||||
uintptr(len(lpBuffer)),
|
||||
uintptr(unsafe.Pointer(&lpBuffer[0])))
|
||||
if diskret == 0 {
|
||||
return ret, err
|
||||
}
|
||||
for _, v := range lpBuffer {
|
||||
if v >= 65 && v <= 90 {
|
||||
path := string(v) + ":"
|
||||
typepath, _ := windows.UTF16PtrFromString(path)
|
||||
typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath)))
|
||||
if typeret == 0 {
|
||||
err := windows.GetLastError()
|
||||
warnings.Add(err)
|
||||
continue
|
||||
}
|
||||
// 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM
|
||||
|
||||
if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 {
|
||||
lpVolumeNameBuffer := make([]byte, 256)
|
||||
lpVolumeSerialNumber := int64(0)
|
||||
lpMaximumComponentLength := int64(0)
|
||||
lpFileSystemFlags := int64(0)
|
||||
lpFileSystemNameBuffer := make([]byte, 256)
|
||||
volpath, _ := windows.UTF16PtrFromString(string(v) + ":/")
|
||||
driveret, _, err := procGetVolumeInformation.Call(
|
||||
uintptr(unsafe.Pointer(volpath)),
|
||||
uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])),
|
||||
uintptr(len(lpVolumeNameBuffer)),
|
||||
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
|
||||
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
|
||||
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
|
||||
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])),
|
||||
uintptr(len(lpFileSystemNameBuffer)))
|
||||
if driveret == 0 {
|
||||
if typeret == 5 || typeret == 2 {
|
||||
continue // device is not ready will happen if there is no disk in the drive
|
||||
}
|
||||
warnings.Add(err)
|
||||
continue
|
||||
}
|
||||
opts := []string{"rw"}
|
||||
if lpFileSystemFlags&fileReadOnlyVolume != 0 {
|
||||
opts = []string{"ro"}
|
||||
}
|
||||
if lpFileSystemFlags&fileFileCompression != 0 {
|
||||
opts = append(opts, "compress")
|
||||
}
|
||||
|
||||
d := PartitionStat{
|
||||
Mountpoint: path,
|
||||
Device: path,
|
||||
Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)),
|
||||
Opts: opts,
|
||||
}
|
||||
ret = append(ret, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, warnings.Reference()
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
|
||||
// https://github.com/giampaolo/psutil/blob/544e9daa4f66a9f80d7bf6c7886d693ee42f0a13/psutil/arch/windows/disk.c#L83
|
||||
drivemap := make(map[string]IOCountersStat, 0)
|
||||
var diskPerformance diskPerformance
|
||||
|
||||
lpBuffer := make([]uint16, 254)
|
||||
lpBufferLen, err := windows.GetLogicalDriveStrings(uint32(len(lpBuffer)), &lpBuffer[0])
|
||||
if err != nil {
|
||||
return drivemap, err
|
||||
}
|
||||
for _, v := range lpBuffer[:lpBufferLen] {
|
||||
if 'A' <= v && v <= 'Z' {
|
||||
path := string(rune(v)) + ":"
|
||||
typepath, _ := windows.UTF16PtrFromString(path)
|
||||
typeret := windows.GetDriveType(typepath)
|
||||
if typeret == 0 {
|
||||
return drivemap, windows.GetLastError()
|
||||
}
|
||||
if typeret != windows.DRIVE_FIXED {
|
||||
continue
|
||||
}
|
||||
szDevice := fmt.Sprintf(`\\.\%s`, path)
|
||||
const IOCTL_DISK_PERFORMANCE = 0x70020
|
||||
h, err := windows.CreateFile(syscall.StringToUTF16Ptr(szDevice), 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, 0, 0)
|
||||
if err != nil {
|
||||
if err == windows.ERROR_FILE_NOT_FOUND {
|
||||
continue
|
||||
}
|
||||
return drivemap, err
|
||||
}
|
||||
defer windows.CloseHandle(h)
|
||||
|
||||
var diskPerformanceSize uint32
|
||||
err = windows.DeviceIoControl(h, IOCTL_DISK_PERFORMANCE, nil, 0, (*byte)(unsafe.Pointer(&diskPerformance)), uint32(unsafe.Sizeof(diskPerformance)), &diskPerformanceSize, nil)
|
||||
if err != nil {
|
||||
return drivemap, err
|
||||
}
|
||||
drivemap[path] = IOCountersStat{
|
||||
ReadBytes: uint64(diskPerformance.BytesRead),
|
||||
WriteBytes: uint64(diskPerformance.BytesWritten),
|
||||
ReadCount: uint64(diskPerformance.ReadCount),
|
||||
WriteCount: uint64(diskPerformance.WriteCount),
|
||||
ReadTime: uint64(diskPerformance.ReadTime / 10000 / 1000), // convert to ms: https://github.com/giampaolo/psutil/issues/1012
|
||||
WriteTime: uint64(diskPerformance.WriteTime / 10000 / 1000),
|
||||
Name: path,
|
||||
}
|
||||
}
|
||||
}
|
||||
return drivemap, nil
|
||||
}
|
||||
|
||||
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.c
|
||||
#include <stdint.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "iostat_darwin.h"
|
||||
|
||||
#define IOKIT 1 /* to get io_name_t in device_types.h */
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/storage/IOBlockStorageDriver.h>
|
||||
#include <IOKit/storage/IOMedia.h>
|
||||
#include <IOKit/IOBSD.h>
|
||||
|
||||
#include <mach/mach_host.h>
|
||||
|
||||
static int getdrivestat(io_registry_entry_t d, DriveStats *stat);
|
||||
static int fillstat(io_registry_entry_t d, DriveStats *stat);
|
||||
|
||||
int
|
||||
gopsutil_v3_readdrivestat(DriveStats a[], int n)
|
||||
{
|
||||
CFMutableDictionaryRef match;
|
||||
io_iterator_t drives;
|
||||
io_registry_entry_t d;
|
||||
kern_return_t status;
|
||||
int na, rv;
|
||||
|
||||
match = IOServiceMatching("IOMedia");
|
||||
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
|
||||
status = IOServiceGetMatchingServices(0, match, &drives);
|
||||
if(status != KERN_SUCCESS)
|
||||
return -1;
|
||||
|
||||
na = 0;
|
||||
while(na < n && (d=IOIteratorNext(drives)) > 0){
|
||||
rv = getdrivestat(d, &a[na]);
|
||||
if(rv < 0)
|
||||
return -1;
|
||||
if(rv > 0)
|
||||
na++;
|
||||
IOObjectRelease(d);
|
||||
}
|
||||
IOObjectRelease(drives);
|
||||
return na;
|
||||
}
|
||||
|
||||
static int
|
||||
getdrivestat(io_registry_entry_t d, DriveStats *stat)
|
||||
{
|
||||
io_registry_entry_t parent;
|
||||
kern_return_t status;
|
||||
CFDictionaryRef props;
|
||||
CFStringRef name;
|
||||
CFNumberRef num;
|
||||
int rv;
|
||||
|
||||
memset(stat, 0, sizeof *stat);
|
||||
status = IORegistryEntryGetParentEntry(d, kIOServicePlane, &parent);
|
||||
if(status != KERN_SUCCESS)
|
||||
return -1;
|
||||
if(!IOObjectConformsTo(parent, "IOBlockStorageDriver")){
|
||||
IOObjectRelease(parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
|
||||
if(status != KERN_SUCCESS){
|
||||
IOObjectRelease(parent);
|
||||
return -1;
|
||||
}
|
||||
name = (CFStringRef)CFDictionaryGetValue(props, CFSTR(kIOBSDNameKey));
|
||||
CFStringGetCString(name, stat->name, NAMELEN, CFStringGetSystemEncoding());
|
||||
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaSizeKey));
|
||||
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->size);
|
||||
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaPreferredBlockSizeKey));
|
||||
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->blocksize);
|
||||
CFRelease(props);
|
||||
|
||||
rv = fillstat(parent, stat);
|
||||
IOObjectRelease(parent);
|
||||
if(rv < 0)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct {
|
||||
char *key;
|
||||
size_t off;
|
||||
} statstab[] = {
|
||||
{kIOBlockStorageDriverStatisticsBytesReadKey, offsetof(DriveStats, read)},
|
||||
{kIOBlockStorageDriverStatisticsBytesWrittenKey, offsetof(DriveStats, written)},
|
||||
{kIOBlockStorageDriverStatisticsReadsKey, offsetof(DriveStats, nread)},
|
||||
{kIOBlockStorageDriverStatisticsWritesKey, offsetof(DriveStats, nwrite)},
|
||||
{kIOBlockStorageDriverStatisticsTotalReadTimeKey, offsetof(DriveStats, readtime)},
|
||||
{kIOBlockStorageDriverStatisticsTotalWriteTimeKey, offsetof(DriveStats, writetime)},
|
||||
{kIOBlockStorageDriverStatisticsLatentReadTimeKey, offsetof(DriveStats, readlat)},
|
||||
{kIOBlockStorageDriverStatisticsLatentWriteTimeKey, offsetof(DriveStats, writelat)},
|
||||
};
|
||||
|
||||
static int
|
||||
fillstat(io_registry_entry_t d, DriveStats *stat)
|
||||
{
|
||||
CFDictionaryRef props, v;
|
||||
CFNumberRef num;
|
||||
kern_return_t status;
|
||||
typeof(statstab[0]) *bp, *ep;
|
||||
|
||||
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
|
||||
if(status != KERN_SUCCESS)
|
||||
return -1;
|
||||
v = (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOBlockStorageDriverStatisticsKey));
|
||||
if(v == NULL){
|
||||
CFRelease(props);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ep = &statstab[sizeof(statstab)/sizeof(statstab[0])];
|
||||
for(bp = &statstab[0]; bp < ep; bp++){
|
||||
CFStringRef s;
|
||||
|
||||
s = CFStringCreateWithCString(kCFAllocatorDefault, bp->key, CFStringGetSystemEncoding());
|
||||
num = (CFNumberRef)CFDictionaryGetValue(v, s);
|
||||
if(num)
|
||||
CFNumberGetValue(num, kCFNumberSInt64Type, ((char*)stat)+bp->off);
|
||||
CFRelease(s);
|
||||
}
|
||||
|
||||
CFRelease(props);
|
||||
return 0;
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.h
|
||||
typedef struct DriveStats DriveStats;
|
||||
typedef struct CPUStats CPUStats;
|
||||
|
||||
enum {
|
||||
NDRIVE = 16,
|
||||
NAMELEN = 31
|
||||
};
|
||||
|
||||
struct DriveStats {
|
||||
char name[NAMELEN+1];
|
||||
int64_t size;
|
||||
int64_t blocksize;
|
||||
|
||||
int64_t read;
|
||||
int64_t written;
|
||||
int64_t nread;
|
||||
int64_t nwrite;
|
||||
int64_t readtime;
|
||||
int64_t writetime;
|
||||
int64_t readlat;
|
||||
int64_t writelat;
|
||||
};
|
||||
|
||||
struct CPUStats {
|
||||
natural_t user;
|
||||
natural_t nice;
|
||||
natural_t sys;
|
||||
natural_t idle;
|
||||
};
|
||||
|
||||
extern int gopsutil_v3_readdrivestat(DriveStats a[], int n);
|
||||
Vendored
+1
@@ -1080,6 +1080,7 @@ github.com/shirou/gopsutil/process
|
||||
# github.com/shirou/gopsutil/v3 v3.22.10
|
||||
## explicit; go 1.15
|
||||
github.com/shirou/gopsutil/v3/cpu
|
||||
github.com/shirou/gopsutil/v3/disk
|
||||
github.com/shirou/gopsutil/v3/internal/common
|
||||
github.com/shirou/gopsutil/v3/mem
|
||||
github.com/shirou/gopsutil/v3/net
|
||||
|
||||
Reference in New Issue
Block a user