mirror of
https://github.com/datarhei/core.git
synced 2025-10-06 16:37:04 +08:00
Make psutil a submodule of resources, remove default psutil
This commit is contained in:
@@ -36,8 +36,8 @@ import (
|
|||||||
"github.com/datarhei/core/v16/monitor"
|
"github.com/datarhei/core/v16/monitor"
|
||||||
"github.com/datarhei/core/v16/net"
|
"github.com/datarhei/core/v16/net"
|
||||||
"github.com/datarhei/core/v16/prometheus"
|
"github.com/datarhei/core/v16/prometheus"
|
||||||
"github.com/datarhei/core/v16/psutil"
|
|
||||||
"github.com/datarhei/core/v16/resources"
|
"github.com/datarhei/core/v16/resources"
|
||||||
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
"github.com/datarhei/core/v16/restream"
|
"github.com/datarhei/core/v16/restream"
|
||||||
restreamapp "github.com/datarhei/core/v16/restream/app"
|
restreamapp "github.com/datarhei/core/v16/restream/app"
|
||||||
"github.com/datarhei/core/v16/restream/replace"
|
"github.com/datarhei/core/v16/restream/replace"
|
||||||
@@ -127,8 +127,6 @@ type api struct {
|
|||||||
state string
|
state string
|
||||||
|
|
||||||
undoMaxprocs func()
|
undoMaxprocs func()
|
||||||
|
|
||||||
process psutil.Process
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrConfigReload is an error returned to indicate that a reload of
|
// ErrConfigReload is an error returned to indicate that a reload of
|
||||||
@@ -370,12 +368,18 @@ func (a *api) start(ctx context.Context) error {
|
|||||||
debug.SetMemoryLimit(math.MaxInt64)
|
debug.SetMemoryLimit(math.MaxInt64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
psutil, err := psutil.New("", nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize psutils: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
resources, err := resources.New(resources.Config{
|
resources, err := resources.New(resources.Config{
|
||||||
MaxCPU: cfg.Resources.MaxCPUUsage,
|
MaxCPU: cfg.Resources.MaxCPUUsage,
|
||||||
MaxMemory: cfg.Resources.MaxMemoryUsage,
|
MaxMemory: cfg.Resources.MaxMemoryUsage,
|
||||||
MaxGPU: cfg.Resources.MaxGPUUsage,
|
MaxGPU: cfg.Resources.MaxGPUUsage,
|
||||||
MaxGPUMemory: cfg.Resources.MaxGPUMemoryUsage,
|
MaxGPUMemory: cfg.Resources.MaxGPUMemoryUsage,
|
||||||
Logger: a.log.logger.core.WithComponent("Resources"),
|
Logger: a.log.logger.core.WithComponent("Resources"),
|
||||||
|
PSUtil: psutil,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize resource manager: %w", err)
|
return fmt.Errorf("failed to initialize resource manager: %w", err)
|
||||||
@@ -509,6 +513,7 @@ func (a *api) start(ctx context.Context) error {
|
|||||||
ValidatorOutput: validatorOut,
|
ValidatorOutput: validatorOut,
|
||||||
Portrange: portrange,
|
Portrange: portrange,
|
||||||
Collector: a.sessions.Collector("ffmpeg"),
|
Collector: a.sessions.Collector("ffmpeg"),
|
||||||
|
PSUtil: psutil,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create ffmpeg: %w", err)
|
return fmt.Errorf("unable to create ffmpeg: %w", err)
|
||||||
@@ -1230,8 +1235,8 @@ func (a *api) start(ctx context.Context) error {
|
|||||||
metrics.Register(monitor.NewUptimeCollector())
|
metrics.Register(monitor.NewUptimeCollector())
|
||||||
metrics.Register(monitor.NewCPUCollector(a.resources))
|
metrics.Register(monitor.NewCPUCollector(a.resources))
|
||||||
metrics.Register(monitor.NewMemCollector(a.resources))
|
metrics.Register(monitor.NewMemCollector(a.resources))
|
||||||
metrics.Register(monitor.NewNetCollector())
|
metrics.Register(monitor.NewNetCollector(a.resources))
|
||||||
metrics.Register(monitor.NewDiskCollector(a.diskfs.Metadata("base")))
|
metrics.Register(monitor.NewDiskCollector(a.diskfs.Metadata("base"), a.resources))
|
||||||
metrics.Register(monitor.NewFilesystemCollector("diskfs", a.diskfs))
|
metrics.Register(monitor.NewFilesystemCollector("diskfs", a.diskfs))
|
||||||
metrics.Register(monitor.NewFilesystemCollector("memfs", a.memfs))
|
metrics.Register(monitor.NewFilesystemCollector("memfs", a.memfs))
|
||||||
for name, fs := range a.s3fs {
|
for name, fs := range a.s3fs {
|
||||||
@@ -1888,11 +1893,6 @@ func (a *api) stop() {
|
|||||||
a.service = nil
|
a.service = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.process != nil {
|
|
||||||
a.process.Stop()
|
|
||||||
a.process = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unregister all collectors
|
// Unregister all collectors
|
||||||
if a.metrics != nil {
|
if a.metrics != nil {
|
||||||
a.metrics.UnregisterAll()
|
a.metrics.UnregisterAll()
|
||||||
|
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/net"
|
"github.com/datarhei/core/v16/net"
|
||||||
"github.com/datarhei/core/v16/process"
|
"github.com/datarhei/core/v16/process"
|
||||||
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
"github.com/datarhei/core/v16/session"
|
"github.com/datarhei/core/v16/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@ type Config struct {
|
|||||||
ValidatorOutput Validator
|
ValidatorOutput Validator
|
||||||
Portrange net.Portranger
|
Portrange net.Portranger
|
||||||
Collector session.Collector
|
Collector session.Collector
|
||||||
|
PSUtil psutil.Util
|
||||||
}
|
}
|
||||||
|
|
||||||
type ffmpeg struct {
|
type ffmpeg struct {
|
||||||
@@ -80,11 +82,19 @@ type ffmpeg struct {
|
|||||||
|
|
||||||
states process.States
|
states process.States
|
||||||
statesLock sync.RWMutex
|
statesLock sync.RWMutex
|
||||||
|
|
||||||
|
psutil psutil.Util
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config Config) (FFmpeg, error) {
|
func New(config Config) (FFmpeg, error) {
|
||||||
f := &ffmpeg{}
|
f := &ffmpeg{}
|
||||||
|
|
||||||
|
if config.PSUtil == nil {
|
||||||
|
return nil, fmt.Errorf("psutils required")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.psutil = config.PSUtil
|
||||||
|
|
||||||
binary, err := exec.LookPath(config.Binary)
|
binary, err := exec.LookPath(config.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid ffmpeg binary given: %w", err)
|
return nil, fmt.Errorf("invalid ffmpeg binary given: %w", err)
|
||||||
@@ -184,6 +194,7 @@ func (f *ffmpeg) New(config ProcessConfig) (process.Process, error) {
|
|||||||
config.OnStateChange(from, to)
|
config.OnStateChange(from, to)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
PSUtil: f.psutil,
|
||||||
})
|
})
|
||||||
|
|
||||||
return ffmpeg, err
|
return ffmpeg, err
|
||||||
|
@@ -155,13 +155,13 @@ type ProcessConfigIOCleanup struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProcessConfigLimits struct {
|
type ProcessConfigLimits struct {
|
||||||
CPU float64 `json:"cpu_usage" jsonschema:"minimum=0"`
|
CPU float64 `json:"cpu_usage" jsonschema:"minimum=0"` // percent 0-100*ncpu
|
||||||
Memory uint64 `json:"memory_mbytes" jsonschema:"minimum=0" format:"uint64"`
|
Memory uint64 `json:"memory_mbytes" jsonschema:"minimum=0" format:"uint64"` // megabytes
|
||||||
GPUUsage float64 `json:"gpu_usage" jsonschema:"minimum=0"`
|
GPUUsage float64 `json:"gpu_usage" jsonschema:"minimum=0"` // percent 0-100
|
||||||
GPUEncoder float64 `json:"gpu_encoder" jsonschema:"minimum=0"`
|
GPUEncoder float64 `json:"gpu_encoder" jsonschema:"minimum=0"` // percent 0-100
|
||||||
GPUDecoder float64 `json:"gpu_decoder" jsonschema:"minimum=0"`
|
GPUDecoder float64 `json:"gpu_decoder" jsonschema:"minimum=0"` // percent 0-100
|
||||||
GPUMemory uint64 `json:"gpu_memory_mbytes" jsonschema:"minimum=0" format:"uint64"`
|
GPUMemory uint64 `json:"gpu_memory_mbytes" jsonschema:"minimum=0" format:"uint64"` // megabytes
|
||||||
WaitFor uint64 `json:"waitfor_seconds" jsonschema:"minimum=0" format:"uint64"`
|
WaitFor uint64 `json:"waitfor_seconds" jsonschema:"minimum=0" format:"uint64"` // seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessConfig represents the configuration of an ffmpeg process
|
// ProcessConfig represents the configuration of an ffmpeg process
|
||||||
|
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/datarhei/core/v16/http/validator"
|
"github.com/datarhei/core/v16/http/validator"
|
||||||
"github.com/datarhei/core/v16/internal/testhelper"
|
"github.com/datarhei/core/v16/internal/testhelper"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
"github.com/datarhei/core/v16/restream"
|
"github.com/datarhei/core/v16/restream"
|
||||||
jsonstore "github.com/datarhei/core/v16/restream/store/json"
|
jsonstore "github.com/datarhei/core/v16/restream/store/json"
|
||||||
|
|
||||||
@@ -44,10 +45,16 @@ func DummyRestreamer(pathPrefix string) (restream.Restreamer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
psutil, err := psutil.New("", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ffmpeg, err := ffmpeg.New(ffmpeg.Config{
|
ffmpeg, err := ffmpeg.New(ffmpeg.Config{
|
||||||
Binary: binary,
|
Binary: binary,
|
||||||
MaxLogLines: 100,
|
MaxLogLines: 100,
|
||||||
LogHistoryLength: 3,
|
LogHistoryLength: 3,
|
||||||
|
PSUtil: psutil,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -2,19 +2,21 @@ package monitor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/datarhei/core/v16/monitor/metric"
|
"github.com/datarhei/core/v16/monitor/metric"
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources"
|
||||||
)
|
)
|
||||||
|
|
||||||
type diskCollector struct {
|
type diskCollector struct {
|
||||||
path string
|
path string
|
||||||
|
resources resources.Resources
|
||||||
|
|
||||||
totalDescr *metric.Description
|
totalDescr *metric.Description
|
||||||
usageDescr *metric.Description
|
usageDescr *metric.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDiskCollector(path string) metric.Collector {
|
func NewDiskCollector(path string, rsc resources.Resources) metric.Collector {
|
||||||
c := &diskCollector{
|
c := &diskCollector{
|
||||||
path: path,
|
path: path,
|
||||||
|
resources: rsc,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.totalDescr = metric.NewDesc("disk_total", "Total size of the disk in bytes", []string{"path"})
|
c.totalDescr = metric.NewDesc("disk_total", "Total size of the disk in bytes", []string{"path"})
|
||||||
@@ -37,7 +39,7 @@ func (c *diskCollector) Describe() []*metric.Description {
|
|||||||
func (c *diskCollector) Collect() metric.Metrics {
|
func (c *diskCollector) Collect() metric.Metrics {
|
||||||
metrics := metric.NewMetrics()
|
metrics := metric.NewMetrics()
|
||||||
|
|
||||||
stat, err := psutil.Disk(c.path)
|
stat, err := c.resources.Disk(c.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics
|
return metrics
|
||||||
}
|
}
|
||||||
|
@@ -2,16 +2,20 @@ package monitor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/datarhei/core/v16/monitor/metric"
|
"github.com/datarhei/core/v16/monitor/metric"
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources"
|
||||||
)
|
)
|
||||||
|
|
||||||
type netCollector struct {
|
type netCollector struct {
|
||||||
rxDescr *metric.Description
|
rxDescr *metric.Description
|
||||||
txDescr *metric.Description
|
txDescr *metric.Description
|
||||||
|
|
||||||
|
resources resources.Resources
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNetCollector() metric.Collector {
|
func NewNetCollector(rsc resources.Resources) metric.Collector {
|
||||||
c := &netCollector{}
|
c := &netCollector{
|
||||||
|
resources: rsc,
|
||||||
|
}
|
||||||
|
|
||||||
c.rxDescr = metric.NewDesc("net_rx", "Number of received bytes", []string{"interface"})
|
c.rxDescr = metric.NewDesc("net_rx", "Number of received bytes", []string{"interface"})
|
||||||
c.txDescr = metric.NewDesc("net_tx", "Number of transmitted bytes", []string{"interface"})
|
c.txDescr = metric.NewDesc("net_tx", "Number of transmitted bytes", []string{"interface"})
|
||||||
@@ -33,7 +37,7 @@ func (c *netCollector) Describe() []*metric.Description {
|
|||||||
func (c *netCollector) Collect() metric.Metrics {
|
func (c *netCollector) Collect() metric.Metrics {
|
||||||
metrics := metric.NewMetrics()
|
metrics := metric.NewMetrics()
|
||||||
|
|
||||||
devs, err := psutil.Network()
|
devs, err := c.resources.Network()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return metrics
|
return metrics
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Usage struct {
|
type Usage struct {
|
||||||
@@ -257,7 +257,7 @@ type limiter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewLimiter returns a new Limiter
|
// NewLimiter returns a new Limiter
|
||||||
func NewLimiter(config LimiterConfig) Limiter {
|
func NewLimiter(config LimiterConfig) (Limiter, error) {
|
||||||
l := &limiter{
|
l := &limiter{
|
||||||
waitFor: config.WaitFor,
|
waitFor: config.WaitFor,
|
||||||
onLimit: config.OnLimit,
|
onLimit: config.OnLimit,
|
||||||
@@ -278,7 +278,7 @@ func NewLimiter(config LimiterConfig) Limiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if l.psutil == nil {
|
if l.psutil == nil {
|
||||||
l.psutil = psutil.DefaultUtil
|
return nil, fmt.Errorf("no psutil provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ncpu, err := l.psutil.CPUCounts(); err != nil {
|
if ncpu, err := l.psutil.CPUCounts(); err != nil {
|
||||||
@@ -318,7 +318,7 @@ func NewLimiter(config LimiterConfig) Limiter {
|
|||||||
"mode": mode,
|
"mode": mode,
|
||||||
})
|
})
|
||||||
|
|
||||||
return l
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *limiter) reset() {
|
func (l *limiter) reset() {
|
||||||
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -52,11 +52,12 @@ func TestCPULimit(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
CPU: 42,
|
CPU: 42,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
@@ -88,12 +89,13 @@ func TestCPULimitWaitFor(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
CPU: 42,
|
CPU: 42,
|
||||||
WaitFor: 3 * time.Second,
|
WaitFor: 3 * time.Second,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
@@ -125,11 +127,12 @@ func TestMemoryLimit(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
Memory: 42,
|
Memory: 42,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
@@ -161,12 +164,13 @@ func TestMemoryLimitWaitFor(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
Memory: 42,
|
Memory: 42,
|
||||||
WaitFor: 3 * time.Second,
|
WaitFor: 3 * time.Second,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
@@ -198,11 +202,12 @@ func TestGPUMemoryLimit(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
GPUMemory: 42,
|
GPUMemory: 42,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
@@ -234,12 +239,13 @@ func TestGPUMemoryLimitWaitFor(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
GPUMemory: 42,
|
GPUMemory: 42,
|
||||||
WaitFor: 3 * time.Second,
|
WaitFor: 3 * time.Second,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
@@ -271,12 +277,13 @@ func TestMemoryLimitSoftMode(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
Memory: 42,
|
Memory: 42,
|
||||||
Mode: LimitModeSoft,
|
Mode: LimitModeSoft,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
@@ -310,12 +317,13 @@ func TestGPUMemoryLimitSoftMode(t *testing.T) {
|
|||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
l := NewLimiter(LimiterConfig{
|
l, _ := NewLimiter(LimiterConfig{
|
||||||
GPUMemory: 42,
|
GPUMemory: 42,
|
||||||
Mode: LimitModeSoft,
|
Mode: LimitModeSoft,
|
||||||
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
OnLimit: func(float64, uint64, float64, float64, float64, uint64) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Start(&psproc{})
|
l.Start(&psproc{})
|
||||||
|
@@ -18,7 +18,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Process represents a process and ways to control it
|
// Process represents a process and ways to control it
|
||||||
@@ -71,6 +71,7 @@ type Config struct {
|
|||||||
OnStart func() // A callback which is called after the process started.
|
OnStart func() // A callback which is called after the process started.
|
||||||
OnExit func(state string) // A callback which is called after the process exited with the exit state.
|
OnExit func(state string) // A callback which is called after the process exited with the exit state.
|
||||||
OnStateChange func(from, to string) // A callback which is called after a state changed.
|
OnStateChange func(from, to string) // A callback which is called after a state changed.
|
||||||
|
PSUtil psutil.Util
|
||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +245,7 @@ type process struct {
|
|||||||
}
|
}
|
||||||
limits Limiter
|
limits Limiter
|
||||||
scheduler Scheduler
|
scheduler Scheduler
|
||||||
|
psutil psutil.Util
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Process = &process{}
|
var _ Process = &process{}
|
||||||
@@ -257,6 +259,11 @@ func New(config Config) (Process, error) {
|
|||||||
parser: config.Parser,
|
parser: config.Parser,
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
scheduler: config.Scheduler,
|
scheduler: config.Scheduler,
|
||||||
|
psutil: config.PSUtil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.psutil == nil {
|
||||||
|
return nil, fmt.Errorf("no psutils given")
|
||||||
}
|
}
|
||||||
|
|
||||||
p.args = make([]string, len(config.Args))
|
p.args = make([]string, len(config.Args))
|
||||||
@@ -269,6 +276,10 @@ func New(config Config) (Process, error) {
|
|||||||
return nil, fmt.Errorf("no valid binary given")
|
return nil, fmt.Errorf("no valid binary given")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.psutil == nil {
|
||||||
|
return nil, fmt.Errorf("no psutils provided")
|
||||||
|
}
|
||||||
|
|
||||||
if p.parser == nil {
|
if p.parser == nil {
|
||||||
p.parser = NewNullParser()
|
p.parser = NewNullParser()
|
||||||
}
|
}
|
||||||
@@ -297,7 +308,7 @@ func New(config Config) (Process, error) {
|
|||||||
p.callbacks.onExit = config.OnExit
|
p.callbacks.onExit = config.OnExit
|
||||||
p.callbacks.onStateChange = config.OnStateChange
|
p.callbacks.onStateChange = config.OnStateChange
|
||||||
|
|
||||||
p.limits = NewLimiter(LimiterConfig{
|
limits, err := NewLimiter(LimiterConfig{
|
||||||
CPU: config.LimitCPU,
|
CPU: config.LimitCPU,
|
||||||
Memory: config.LimitMemory,
|
Memory: config.LimitMemory,
|
||||||
GPUUsage: config.LimitGPUUsage,
|
GPUUsage: config.LimitGPUUsage,
|
||||||
@@ -322,7 +333,12 @@ func New(config Config) (Process, error) {
|
|||||||
}).Warn().Log("Killed because limits are exceeded")
|
}).Warn().Log("Killed because limits are exceeded")
|
||||||
p.Kill(false, fmt.Sprintf("Killed because limits are exceeded (mode: %s, tolerance: %s): %.2f (%.2f) CPU, %d (%d) bytes memory, %.2f/%.2f/%.2f (%.2f) GPU usage, %d (%d) bytes GPU memory", config.LimitMode.String(), config.LimitDuration.String(), cpu, config.LimitCPU, memory, config.LimitMemory, gpuusage, gpuencoder, gpudecoder, config.LimitGPUUsage, gpumemory, config.LimitGPUMemory))
|
p.Kill(false, fmt.Sprintf("Killed because limits are exceeded (mode: %s, tolerance: %s): %.2f (%.2f) CPU, %d (%d) bytes memory, %.2f/%.2f/%.2f (%.2f) GPU usage, %d (%d) bytes GPU memory", config.LimitMode.String(), config.LimitDuration.String(), cpu, config.LimitCPU, memory, config.LimitMemory, gpuusage, gpuencoder, gpudecoder, config.LimitGPUUsage, gpumemory, config.LimitGPUMemory))
|
||||||
},
|
},
|
||||||
|
PSUtil: p.psutil,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to initialize limiter")
|
||||||
|
}
|
||||||
|
p.limits = limits
|
||||||
|
|
||||||
p.logger.Info().Log("Created")
|
p.logger.Info().Log("Created")
|
||||||
p.debuglogger.Debug().Log("Created")
|
p.debuglogger.Debug().Log("Created")
|
||||||
@@ -703,7 +719,7 @@ func (p *process) start() error {
|
|||||||
|
|
||||||
p.pid = int32(p.cmd.Process.Pid)
|
p.pid = int32(p.cmd.Process.Pid)
|
||||||
|
|
||||||
if proc, err := psutil.NewProcess(p.pid); err == nil {
|
if proc, err := p.psutil.Process(p.pid); err == nil {
|
||||||
p.limits.Start(proc)
|
p.limits.Start(proc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,9 +10,15 @@ import (
|
|||||||
|
|
||||||
"github.com/datarhei/core/v16/internal/testhelper"
|
"github.com/datarhei/core/v16/internal/testhelper"
|
||||||
"github.com/datarhei/core/v16/math/rand"
|
"github.com/datarhei/core/v16/math/rand"
|
||||||
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newPSUtil() psutil.Util {
|
||||||
|
util, _ := psutil.New("", nil)
|
||||||
|
return util
|
||||||
|
}
|
||||||
|
|
||||||
func TestProcess(t *testing.T) {
|
func TestProcess(t *testing.T) {
|
||||||
p, _ := New(Config{
|
p, _ := New(Config{
|
||||||
Binary: "sleep",
|
Binary: "sleep",
|
||||||
@@ -21,6 +27,7 @@ func TestProcess(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
StaleTimeout: 0,
|
StaleTimeout: 0,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
require.Equal(t, "finished", p.Status().State)
|
require.Equal(t, "finished", p.Status().State)
|
||||||
@@ -59,6 +66,7 @@ func TestReconnectProcess(t *testing.T) {
|
|||||||
OnExit: func(string) {
|
OnExit: func(string) {
|
||||||
wg.Done()
|
wg.Done()
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
@@ -104,6 +112,7 @@ func TestStaleProcess(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
StaleTimeout: 2 * time.Second,
|
StaleTimeout: 2 * time.Second,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
@@ -126,6 +135,7 @@ func TestStaleReconnectProcess(t *testing.T) {
|
|||||||
Reconnect: true,
|
Reconnect: true,
|
||||||
ReconnectDelay: 2 * time.Second,
|
ReconnectDelay: 2 * time.Second,
|
||||||
StaleTimeout: 3 * time.Second,
|
StaleTimeout: 3 * time.Second,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
@@ -156,6 +166,7 @@ func TestNonExistingProcess(t *testing.T) {
|
|||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
ReconnectDelay: 5 * time.Second,
|
ReconnectDelay: 5 * time.Second,
|
||||||
StaleTimeout: 0,
|
StaleTimeout: 0,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
@@ -180,6 +191,7 @@ func TestNonExistingReconnectProcess(t *testing.T) {
|
|||||||
Reconnect: true,
|
Reconnect: true,
|
||||||
ReconnectDelay: 2 * time.Second,
|
ReconnectDelay: 2 * time.Second,
|
||||||
StaleTimeout: 0,
|
StaleTimeout: 0,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
@@ -203,6 +215,7 @@ func TestProcessFailed(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
StaleTimeout: 0,
|
StaleTimeout: 0,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
@@ -228,6 +241,7 @@ func TestFFmpegWaitStop(t *testing.T) {
|
|||||||
OnExit: func(state string) {
|
OnExit: func(state string) {
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
@@ -255,6 +269,7 @@ func TestFFmpegKill(t *testing.T) {
|
|||||||
Args: []string{},
|
Args: []string{},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
StaleTimeout: 0,
|
StaleTimeout: 0,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
@@ -280,6 +295,7 @@ func TestProcessForceKill(t *testing.T) {
|
|||||||
Args: []string{},
|
Args: []string{},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
StaleTimeout: 0,
|
StaleTimeout: 0,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
@@ -312,6 +328,7 @@ func TestProcessDuration(t *testing.T) {
|
|||||||
Binary: binary,
|
Binary: binary,
|
||||||
Args: []string{},
|
Args: []string{},
|
||||||
Timeout: 3 * time.Second,
|
Timeout: 3 * time.Second,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -358,6 +375,7 @@ func TestProcessSchedulePointInTime(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
status := p.Status()
|
status := p.Status()
|
||||||
@@ -399,6 +417,7 @@ func TestProcessSchedulePointInTimeGone(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
status := p.Status()
|
status := p.Status()
|
||||||
@@ -424,6 +443,7 @@ func TestProcessScheduleCron(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
status := p.Status()
|
status := p.Status()
|
||||||
@@ -454,6 +474,7 @@ func TestProcessDelayNoScheduler(t *testing.T) {
|
|||||||
Binary: "sleep",
|
Binary: "sleep",
|
||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
ReconnectDelay: 5 * time.Second,
|
ReconnectDelay: 5 * time.Second,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
px := p.(*process)
|
px := p.(*process)
|
||||||
@@ -470,6 +491,7 @@ func TestProcessDelayNoScheduler(t *testing.T) {
|
|||||||
Binary: "sleep",
|
Binary: "sleep",
|
||||||
Reconnect: true,
|
Reconnect: true,
|
||||||
ReconnectDelay: 5 * time.Second,
|
ReconnectDelay: 5 * time.Second,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
px = p.(*process)
|
px = p.(*process)
|
||||||
@@ -493,6 +515,7 @@ func TestProcessDelaySchedulerNoReconnect(t *testing.T) {
|
|||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
ReconnectDelay: 1 * time.Second,
|
ReconnectDelay: 1 * time.Second,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
px := p.(*process)
|
px := p.(*process)
|
||||||
@@ -514,6 +537,7 @@ func TestProcessDelaySchedulerNoReconnect(t *testing.T) {
|
|||||||
Reconnect: false,
|
Reconnect: false,
|
||||||
ReconnectDelay: 1 * time.Second,
|
ReconnectDelay: 1 * time.Second,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
px = p.(*process)
|
px = p.(*process)
|
||||||
@@ -537,6 +561,7 @@ func TestProcessDelaySchedulerReconnect(t *testing.T) {
|
|||||||
Reconnect: true,
|
Reconnect: true,
|
||||||
ReconnectDelay: 1 * time.Second,
|
ReconnectDelay: 1 * time.Second,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
px := p.(*process)
|
px := p.(*process)
|
||||||
@@ -558,6 +583,7 @@ func TestProcessDelaySchedulerReconnect(t *testing.T) {
|
|||||||
Reconnect: true,
|
Reconnect: true,
|
||||||
ReconnectDelay: 1 * time.Second,
|
ReconnectDelay: 1 * time.Second,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
px = p.(*process)
|
px = p.(*process)
|
||||||
@@ -579,6 +605,7 @@ func TestProcessDelaySchedulerReconnect(t *testing.T) {
|
|||||||
Reconnect: true,
|
Reconnect: true,
|
||||||
ReconnectDelay: 10 * time.Second,
|
ReconnectDelay: 10 * time.Second,
|
||||||
Scheduler: s,
|
Scheduler: s,
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
|
|
||||||
px = p.(*process)
|
px = p.(*process)
|
||||||
@@ -636,6 +663,7 @@ func TestProcessCallbacks(t *testing.T) {
|
|||||||
|
|
||||||
onState = append(onState, from+"/"+to)
|
onState = append(onState, from+"/"+to)
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -678,6 +706,7 @@ func TestProcessCallbacksOnBeforeStart(t *testing.T) {
|
|||||||
OnBeforeStart: func(a []string) ([]string, error) {
|
OnBeforeStart: func(a []string) ([]string, error) {
|
||||||
return a, fmt.Errorf("no, not now")
|
return a, fmt.Errorf("no, not now")
|
||||||
},
|
},
|
||||||
|
PSUtil: newPSUtil(),
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@@ -43,3 +43,14 @@ type GPU interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ErrProcessNotFound = errors.New("process not found")
|
var ErrProcessNotFound = errors.New("process not found")
|
||||||
|
|
||||||
|
type dummy struct{}
|
||||||
|
|
||||||
|
func (d *dummy) Count() (int, error) { return 0, nil }
|
||||||
|
func (d *dummy) Stats() ([]Stats, error) { return nil, nil }
|
||||||
|
func (d *dummy) Process(pid int32) (Process, error) { return Process{}, ErrProcessNotFound }
|
||||||
|
func (d *dummy) Close() {}
|
||||||
|
|
||||||
|
func NewNilGPU() GPU {
|
||||||
|
return &dummy{}
|
||||||
|
}
|
@@ -12,15 +12,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/psutil/gpu"
|
"github.com/datarhei/core/v16/resources/psutil/gpu"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Default gpu.GPU
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Default = New("")
|
|
||||||
}
|
|
||||||
|
|
||||||
type Megabytes uint64
|
type Megabytes uint64
|
||||||
|
|
||||||
func (m *Megabytes) UnmarshalText(text []byte) error {
|
func (m *Megabytes) UnmarshalText(text []byte) error {
|
@@ -9,7 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/internal/testhelper"
|
"github.com/datarhei/core/v16/internal/testhelper"
|
||||||
"github.com/datarhei/core/v16/psutil/gpu"
|
"github.com/datarhei/core/v16/resources/psutil/gpu"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ func TestWriterProcess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNvidiaGPUCount(t *testing.T) {
|
func TestNvidiaGPUCount(t *testing.T) {
|
||||||
binary, err := testhelper.BuildBinary("nvidia-smi", "../../../internal/testhelper")
|
binary, err := testhelper.BuildBinary("nvidia-smi", "../../../../internal/testhelper")
|
||||||
require.NoError(t, err, "Failed to build helper program")
|
require.NoError(t, err, "Failed to build helper program")
|
||||||
|
|
||||||
nv := New(binary)
|
nv := New(binary)
|
||||||
@@ -299,7 +299,7 @@ func TestNvidiaGPUCount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNvidiaGPUStats(t *testing.T) {
|
func TestNvidiaGPUStats(t *testing.T) {
|
||||||
binary, err := testhelper.BuildBinary("nvidia-smi", "../../../internal/testhelper")
|
binary, err := testhelper.BuildBinary("nvidia-smi", "../../../../internal/testhelper")
|
||||||
require.NoError(t, err, "Failed to build helper program")
|
require.NoError(t, err, "Failed to build helper program")
|
||||||
|
|
||||||
nv := New(binary)
|
nv := New(binary)
|
||||||
@@ -400,7 +400,7 @@ func TestNvidiaGPUStats(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNvidiaGPUProcess(t *testing.T) {
|
func TestNvidiaGPUProcess(t *testing.T) {
|
||||||
binary, err := testhelper.BuildBinary("nvidia-smi", "../../../internal/testhelper")
|
binary, err := testhelper.BuildBinary("nvidia-smi", "../../../../internal/testhelper")
|
||||||
require.NoError(t, err, "Failed to build helper program")
|
require.NoError(t, err, "Failed to build helper program")
|
||||||
|
|
||||||
nv := New(binary)
|
nv := New(binary)
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/psutil/gpu/nvidia"
|
"github.com/datarhei/core/v16/resources/psutil/gpu"
|
||||||
psprocess "github.com/shirou/gopsutil/v3/process"
|
psprocess "github.com/shirou/gopsutil/v3/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,6 +46,8 @@ type process struct {
|
|||||||
statPreviousTime time.Time
|
statPreviousTime time.Time
|
||||||
nTicks uint64
|
nTicks uint64
|
||||||
memRSS uint64
|
memRSS uint64
|
||||||
|
|
||||||
|
gpu gpu.GPU
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *util) Process(pid int32) (Process, error) {
|
func (u *util) Process(pid int32) (Process, error) {
|
||||||
@@ -54,6 +56,7 @@ func (u *util) Process(pid int32) (Process, error) {
|
|||||||
hasCgroup: u.hasCgroup,
|
hasCgroup: u.hasCgroup,
|
||||||
cpuLimit: u.cpuLimit,
|
cpuLimit: u.cpuLimit,
|
||||||
ncpu: u.ncpu,
|
ncpu: u.ncpu,
|
||||||
|
gpu: u.gpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
proc, err := psprocess.NewProcess(pid)
|
proc, err := psprocess.NewProcess(pid)
|
||||||
@@ -71,10 +74,6 @@ func (u *util) Process(pid int32) (Process, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProcess(pid int32) (Process, error) {
|
|
||||||
return DefaultUtil.Process(pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *process) tickCPU(ctx context.Context, interval time.Duration) {
|
func (p *process) tickCPU(ctx context.Context, interval time.Duration) {
|
||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
@@ -202,7 +201,7 @@ func (p *process) GPU() (*GPUInfo, error) {
|
|||||||
Index: -1,
|
Index: -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
proc, err := nvidia.Default.Process(p.pid)
|
proc, err := p.gpu.Process(p.pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
@@ -13,7 +13,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/psutil/gpu/nvidia"
|
psutilgpu "github.com/datarhei/core/v16/resources/psutil/gpu"
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
"github.com/shirou/gopsutil/v3/cpu"
|
||||||
"github.com/shirou/gopsutil/v3/disk"
|
"github.com/shirou/gopsutil/v3/disk"
|
||||||
@@ -41,12 +41,6 @@ var cgroup2Files = []string{
|
|||||||
// https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sect-cpu-example_usage
|
// https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sect-cpu-example_usage
|
||||||
// https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
|
// https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
|
||||||
|
|
||||||
var DefaultUtil Util
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
DefaultUtil, _ = New("/sys/fs/cgroup")
|
|
||||||
}
|
|
||||||
|
|
||||||
type DiskInfo struct {
|
type DiskInfo struct {
|
||||||
Path string
|
Path string
|
||||||
Fstype string
|
Fstype string
|
||||||
@@ -141,10 +135,16 @@ type util struct {
|
|||||||
statPreviousTime time.Time
|
statPreviousTime time.Time
|
||||||
nTicks uint64
|
nTicks uint64
|
||||||
mem MemoryInfo
|
mem MemoryInfo
|
||||||
|
|
||||||
|
gpu psutilgpu.GPU
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new util, it will be started automatically
|
// New returns a new util, it will be started automatically
|
||||||
func New(root string) (Util, error) {
|
func New(root string, gpu psutilgpu.GPU) (Util, error) {
|
||||||
|
if len(root) == 0 {
|
||||||
|
root = "/sys/fs/cgroup"
|
||||||
|
}
|
||||||
|
|
||||||
u := &util{
|
u := &util{
|
||||||
root: os.DirFS(root),
|
root: os.DirFS(root),
|
||||||
}
|
}
|
||||||
@@ -173,6 +173,11 @@ func New(root string) (Util, error) {
|
|||||||
|
|
||||||
u.mem = *mem
|
u.mem = *mem
|
||||||
|
|
||||||
|
u.gpu = gpu
|
||||||
|
if u.gpu == nil {
|
||||||
|
u.gpu = psutilgpu.NewNilGPU()
|
||||||
|
}
|
||||||
|
|
||||||
u.stopOnce.Do(func() {})
|
u.stopOnce.Do(func() {})
|
||||||
|
|
||||||
u.Start()
|
u.Start()
|
||||||
@@ -353,10 +358,6 @@ func (u *util) CPUCounts() (float64, error) {
|
|||||||
return float64(ncpu), nil
|
return float64(ncpu), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CPUCounts() (float64, error) {
|
|
||||||
return DefaultUtil.CPUCounts()
|
|
||||||
}
|
|
||||||
|
|
||||||
// cpuTimes returns the current cpu usage times in seconds.
|
// cpuTimes returns the current cpu usage times in seconds.
|
||||||
func (u *util) cpuTimes() (*cpuTimesStat, error) {
|
func (u *util) cpuTimes() (*cpuTimesStat, error) {
|
||||||
if u.hasCgroup && u.cpuLimit > 0 {
|
if u.hasCgroup && u.cpuLimit > 0 {
|
||||||
@@ -439,10 +440,6 @@ func (u *util) CPU() (*CPUInfo, error) {
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CPUPercent() (*CPUInfo, error) {
|
|
||||||
return DefaultUtil.CPU()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *util) cgroupCPUTimes(version int) (*cpuTimesStat, error) {
|
func (u *util) cgroupCPUTimes(version int) (*cpuTimesStat, error) {
|
||||||
info := &cpuTimesStat{}
|
info := &cpuTimesStat{}
|
||||||
|
|
||||||
@@ -494,10 +491,6 @@ func (u *util) Disk(path string) (*DiskInfo, error) {
|
|||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Disk(path string) (*DiskInfo, error) {
|
|
||||||
return DefaultUtil.Disk(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *util) virtualMemory() (*MemoryInfo, error) {
|
func (u *util) virtualMemory() (*MemoryInfo, error) {
|
||||||
info, err := mem.VirtualMemory()
|
info, err := mem.VirtualMemory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -533,10 +526,6 @@ func (u *util) Memory() (*MemoryInfo, error) {
|
|||||||
return stat, nil
|
return stat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Memory() (*MemoryInfo, error) {
|
|
||||||
return DefaultUtil.Memory()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *util) cgroupVirtualMemory(version int) (*MemoryInfo, error) {
|
func (u *util) cgroupVirtualMemory(version int) (*MemoryInfo, error) {
|
||||||
info := &MemoryInfo{}
|
info := &MemoryInfo{}
|
||||||
|
|
||||||
@@ -612,10 +601,6 @@ func (u *util) Network() ([]NetworkInfo, error) {
|
|||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Network() ([]NetworkInfo, error) {
|
|
||||||
return DefaultUtil.Network()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *util) readFile(path string) ([]string, error) {
|
func (u *util) readFile(path string) ([]string, error) {
|
||||||
file, err := u.root.Open(path)
|
file, err := u.root.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -653,7 +638,7 @@ func cpuTotal(c *cpu.TimesStat) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *util) GPU() ([]GPUInfo, error) {
|
func (u *util) GPU() ([]GPUInfo, error) {
|
||||||
nvstats, err := nvidia.Default.Stats()
|
nvstats, err := u.gpu.Stats()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -673,7 +658,3 @@ func (u *util) GPU() ([]GPUInfo, error) {
|
|||||||
|
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GPU() ([]GPUInfo, error) {
|
|
||||||
return DefaultUtil.GPU()
|
|
||||||
}
|
|
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getUtil(path string) *util {
|
func getUtil(path string) *util {
|
||||||
u, _ := New(path)
|
u, _ := New(path, nil)
|
||||||
|
|
||||||
return u.(*util)
|
return u.(*util)
|
||||||
}
|
}
|
@@ -8,7 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/log"
|
"github.com/datarhei/core/v16/log"
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
"github.com/datarhei/core/v16/slices"
|
"github.com/datarhei/core/v16/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,6 +18,21 @@ type Info struct {
|
|||||||
GPU GPUInfo
|
GPU GPUInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DiskInfo struct {
|
||||||
|
Path string
|
||||||
|
Fstype string
|
||||||
|
Total uint64
|
||||||
|
Used uint64
|
||||||
|
InodesTotal uint64
|
||||||
|
InodesUsed uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkInfo struct {
|
||||||
|
Name string // interface name
|
||||||
|
BytesSent uint64 // number of bytes sent
|
||||||
|
BytesRecv uint64 // number of bytes received
|
||||||
|
}
|
||||||
|
|
||||||
type MemoryInfo struct {
|
type MemoryInfo struct {
|
||||||
Total uint64 // bytes
|
Total uint64 // bytes
|
||||||
Available uint64 // bytes
|
Available uint64 // bytes
|
||||||
@@ -123,6 +138,9 @@ type Resources interface {
|
|||||||
|
|
||||||
// Info returns the current resource usage.
|
// Info returns the current resource usage.
|
||||||
Info() Info
|
Info() Info
|
||||||
|
|
||||||
|
Disk(path string) (*DiskInfo, error)
|
||||||
|
Network() ([]NetworkInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -136,7 +154,11 @@ type Config struct {
|
|||||||
|
|
||||||
func New(config Config) (Resources, error) {
|
func New(config Config) (Resources, error) {
|
||||||
if config.PSUtil == nil {
|
if config.PSUtil == nil {
|
||||||
config.PSUtil = psutil.DefaultUtil
|
psutil, err := psutil.New("", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to initialize psutils: %w", err)
|
||||||
|
}
|
||||||
|
config.PSUtil = psutil
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu, err := config.PSUtil.GPU()
|
gpu, err := config.PSUtil.GPU()
|
||||||
@@ -572,3 +594,40 @@ func (r *resources) Info() Info {
|
|||||||
|
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *resources) Disk(path string) (*DiskInfo, error) {
|
||||||
|
info, err := r.psutil.Disk(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
diskinfo := &DiskInfo{
|
||||||
|
Path: info.Path,
|
||||||
|
Fstype: info.Fstype,
|
||||||
|
Total: info.Total,
|
||||||
|
Used: info.Used,
|
||||||
|
InodesTotal: info.InodesTotal,
|
||||||
|
InodesUsed: info.InodesUsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
return diskinfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resources) Network() ([]NetworkInfo, error) {
|
||||||
|
netio, err := r.psutil.Network()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := []NetworkInfo{}
|
||||||
|
|
||||||
|
for _, io := range netio {
|
||||||
|
info = append(info, NetworkInfo{
|
||||||
|
Name: io.Name,
|
||||||
|
BytesSent: io.BytesSent,
|
||||||
|
BytesRecv: io.BytesRecv,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@@ -16,7 +16,8 @@ import (
|
|||||||
"github.com/datarhei/core/v16/internal/testhelper"
|
"github.com/datarhei/core/v16/internal/testhelper"
|
||||||
"github.com/datarhei/core/v16/io/fs"
|
"github.com/datarhei/core/v16/io/fs"
|
||||||
"github.com/datarhei/core/v16/net"
|
"github.com/datarhei/core/v16/net"
|
||||||
"github.com/datarhei/core/v16/psutil"
|
"github.com/datarhei/core/v16/resources"
|
||||||
|
"github.com/datarhei/core/v16/resources/psutil"
|
||||||
"github.com/datarhei/core/v16/restream/app"
|
"github.com/datarhei/core/v16/restream/app"
|
||||||
rfs "github.com/datarhei/core/v16/restream/fs"
|
rfs "github.com/datarhei/core/v16/restream/fs"
|
||||||
"github.com/datarhei/core/v16/restream/replace"
|
"github.com/datarhei/core/v16/restream/replace"
|
||||||
@@ -32,12 +33,18 @@ func getDummyRestreamer(portrange net.Portranger, validatorIn, validatorOut ffmp
|
|||||||
return nil, fmt.Errorf("failed to build helper program: %w", err)
|
return nil, fmt.Errorf("failed to build helper program: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
psutil, err := psutil.New("", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ffmpeg, err := ffmpeg.New(ffmpeg.Config{
|
ffmpeg, err := ffmpeg.New(ffmpeg.Config{
|
||||||
Binary: binary,
|
Binary: binary,
|
||||||
LogHistoryLength: 3,
|
LogHistoryLength: 3,
|
||||||
Portrange: portrange,
|
Portrange: portrange,
|
||||||
ValidatorInput: validatorIn,
|
ValidatorInput: validatorIn,
|
||||||
ValidatorOutput: validatorOut,
|
ValidatorOutput: validatorOut,
|
||||||
|
PSUtil: psutil,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -81,11 +88,19 @@ func getDummyRestreamer(portrange net.Portranger, validatorIn, validatorOut ffmp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resources, err := resources.New(resources.Config{
|
||||||
|
PSUtil: psutil,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
rs, err := New(Config{
|
rs, err := New(Config{
|
||||||
FFmpeg: ffmpeg,
|
FFmpeg: ffmpeg,
|
||||||
Replace: replacer,
|
Replace: replacer,
|
||||||
Filesystems: []fs.Filesystem{memfs},
|
Filesystems: []fs.Filesystem{memfs},
|
||||||
Rewrite: rewriter,
|
Rewrite: rewriter,
|
||||||
|
Resources: resources,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1531,8 +1546,7 @@ func TestProcessLimit(t *testing.T) {
|
|||||||
|
|
||||||
status := task.ffmpeg.Status()
|
status := task.ffmpeg.Status()
|
||||||
|
|
||||||
ncpu, err := psutil.CPUCounts()
|
ncpu := rs.resources.Info().CPU.NCPU
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, ncpu*process.LimitCPU, status.CPU.Limit)
|
require.Equal(t, ncpu*process.LimitCPU, status.CPU.Limit)
|
||||||
require.Equal(t, process.LimitMemory, status.Memory.Limit)
|
require.Equal(t, process.LimitMemory, status.Memory.Limit)
|
||||||
|
Reference in New Issue
Block a user