mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 08:00:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23f07da9ee | ||
|
|
d21288788e | ||
|
|
f65e4b06ab | ||
|
|
d26bc9a3a4 | ||
|
|
7088a903fe | ||
|
|
864ebeed82 | ||
|
|
c1b2b92708 | ||
|
|
2189d9229a | ||
|
|
d0183f460e | ||
|
|
d27d6db1ea | ||
|
|
d2bb3813d9 | ||
|
|
cac73e6d45 | ||
|
|
4712ceaf2c |
+10
-23
@@ -1,13 +1,9 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
)
|
||||
|
||||
// @Tags Monitor
|
||||
@@ -32,14 +28,19 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
// @Summary Clean monitor data
|
||||
// @Summary Clean host or GPU monitor data
|
||||
// @Param request body dto.MonitorClean true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/monitor/clean [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空 [type] 监控数据","formatEN":"clean [type] monitoring data"}
|
||||
func (b *BaseApi) CleanMonitor(c *gin.Context) {
|
||||
if err := monitorService.CleanData(); err != nil {
|
||||
var req dto.MonitorClean
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := monitorService.CleanData(req.Type); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
@@ -91,14 +92,7 @@ func (b *BaseApi) UpdateMonitorSetting(c *gin.Context) {
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/monitor/netoptions [get]
|
||||
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
||||
netStat, _ := net.IOCounters(true)
|
||||
var options []string
|
||||
options = append(options, "all")
|
||||
for _, net := range netStat {
|
||||
options = append(options, net.Name)
|
||||
}
|
||||
sort.Strings(options)
|
||||
helper.SuccessWithData(c, options)
|
||||
helper.SuccessWithData(c, monitorService.LoadNetworkOptions())
|
||||
}
|
||||
|
||||
// @Tags Monitor
|
||||
@@ -108,12 +102,5 @@ func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
||||
// @Security Timestamp
|
||||
// @Router /hosts/monitor/iooptions [get]
|
||||
func (b *BaseApi) GetIOOptions(c *gin.Context) {
|
||||
diskStat, _ := disk.IOCounters()
|
||||
var options []string
|
||||
options = append(options, "all")
|
||||
for _, net := range diskStat {
|
||||
options = append(options, net.Name)
|
||||
}
|
||||
sort.Strings(options)
|
||||
helper.SuccessWithData(c, options)
|
||||
helper.SuccessWithData(c, monitorService.LoadIOOptions())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) LoadVLLMMonitor(c *gin.Context) {
|
||||
var req dto.MonitorVLLMSearch
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := monitorService.LoadVLLMMonitorData(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
func (b *BaseApi) LoadVLLMCurrent(c *gin.Context) {
|
||||
var req dto.MonitorVLLMCurrent
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := monitorService.LoadVLLMCurrent(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
func (b *BaseApi) CleanVLLMMonitor(c *gin.Context) {
|
||||
var req dto.MonitorVLLMClean
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := monitorService.CleanVLLMMonitor(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ type AlertBase struct {
|
||||
}
|
||||
|
||||
type PushAlert struct {
|
||||
Result string `json:"result,omitempty"`
|
||||
TaskName string `json:"taskName"`
|
||||
AlertType string `json:"alertType"`
|
||||
EntryID uint `json:"entryID"`
|
||||
@@ -53,6 +54,7 @@ type AlertDTO struct {
|
||||
Method string `json:"method"`
|
||||
Title string `json:"title"`
|
||||
Project string `json:"project"`
|
||||
TaskName string `json:"taskName,omitempty"`
|
||||
Status string `json:"status"`
|
||||
SendCount uint `json:"sendCount"`
|
||||
AdvancedParams string `json:"advancedParams"`
|
||||
|
||||
@@ -51,9 +51,10 @@ type CronjobOperate struct {
|
||||
Secret string `json:"secret"`
|
||||
Args string `json:"args"`
|
||||
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
AlertTriggerMode string `json:"alertTriggerMode" validate:"omitempty,oneof=failed success both"`
|
||||
|
||||
CleanLogConfig
|
||||
}
|
||||
@@ -126,7 +127,8 @@ type CronjobInfo struct {
|
||||
Secret string `json:"secret"`
|
||||
Args string `json:"args"`
|
||||
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTriggerMode string `json:"alertTriggerMode"`
|
||||
}
|
||||
|
||||
type CronjobImport struct {
|
||||
@@ -169,9 +171,10 @@ type CronjobTrans struct {
|
||||
SourceAccounts []string `json:"sourceAccounts"`
|
||||
DownloadAccount string `json:"downloadAccount"`
|
||||
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
AlertCount uint `json:"alertCount"`
|
||||
AlertTitle string `json:"alertTitle"`
|
||||
AlertMethod string `json:"alertMethod"`
|
||||
AlertTriggerMode string `json:"alertTriggerMode" validate:"omitempty,oneof=failed success both"`
|
||||
}
|
||||
type TransHelper struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
+87
-18
@@ -1,6 +1,10 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
)
|
||||
|
||||
type MonitorSearch struct {
|
||||
Param string `json:"param" validate:"required,oneof=all cpu memory load io network"`
|
||||
@@ -26,24 +30,33 @@ type Process struct {
|
||||
}
|
||||
|
||||
type MonitorSetting struct {
|
||||
MonitorStatus string `json:"monitorStatus"`
|
||||
MonitorStoreDays string `json:"monitorStoreDays"`
|
||||
MonitorInterval string `json:"monitorInterval"`
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
DefaultIO string `json:"defaultIO"`
|
||||
GPUMonitorStatus string `json:"gpuMonitorStatus"`
|
||||
GPUMonitorStoreDays string `json:"gpuMonitorStoreDays"`
|
||||
GPUMonitorInterval string `json:"gpuMonitorInterval"`
|
||||
VLLMMonitorStatus string `json:"vllmMonitorStatus"`
|
||||
VLLMMonitorStoreDays string `json:"vllmMonitorStoreDays"`
|
||||
VLLMMonitorInterval string `json:"vllmMonitorInterval"`
|
||||
MonitorStatus string `json:"monitorStatus"`
|
||||
MonitorStoreDays string `json:"monitorStoreDays"`
|
||||
MonitorInterval string `json:"monitorInterval"`
|
||||
DefaultNetwork string `json:"defaultNetwork"`
|
||||
DefaultIO string `json:"defaultIO"`
|
||||
}
|
||||
|
||||
type MonitorSettingUpdate struct {
|
||||
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork DefaultIO"`
|
||||
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval GPUMonitorStatus GPUMonitorStoreDays GPUMonitorInterval VLLMMonitorStatus VLLMMonitorStoreDays VLLMMonitorInterval DefaultNetwork DefaultIO"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type MonitorGPUOptions struct {
|
||||
Supported bool `json:"supported"`
|
||||
GPUType string `json:"gpuType"`
|
||||
ChartHide []GPUChartHide `json:"chartHide"`
|
||||
Options []string `json:"options"`
|
||||
}
|
||||
type GPUChartHide struct {
|
||||
DeviceID string `json:"deviceID"`
|
||||
Legacy bool `json:"legacy"`
|
||||
ProductName string `json:"productName"`
|
||||
Type string `json:"type"`
|
||||
Process bool `json:"process"`
|
||||
@@ -55,23 +68,54 @@ type GPUChartHide struct {
|
||||
Speed bool `json:"speed"`
|
||||
}
|
||||
type MonitorGPUSearch struct {
|
||||
Aggregation string `json:"aggregation" validate:"omitempty,oneof=avg max"`
|
||||
DeviceID string `json:"deviceID"`
|
||||
Legacy bool `json:"legacy"`
|
||||
ProductName string `json:"productName"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
}
|
||||
type MonitorGPUData struct {
|
||||
Date []time.Time `json:"date"`
|
||||
GPUValue []float64 `json:"gpuValue"`
|
||||
TemperatureValue []float64 `json:"temperatureValue"`
|
||||
PowerTotal []float64 `json:"powerTotal"`
|
||||
PowerUsed []float64 `json:"powerUsed"`
|
||||
PowerPercent []float64 `json:"powerPercent"`
|
||||
MemoryTotal []float64 `json:"memoryTotal"`
|
||||
MemoryUsed []float64 `json:"memoryUsed"`
|
||||
MemoryPercent []float64 `json:"memoryPercent"`
|
||||
SpeedValue []int `json:"speedValue"`
|
||||
MemoryActivity []*float64 `json:"memoryActivity"`
|
||||
EncoderUtil []*float64 `json:"encoderUtil"`
|
||||
DecoderUtil []*float64 `json:"decoderUtil"`
|
||||
JPEGUtil []*float64 `json:"jpegUtil"`
|
||||
OFAUtil []*float64 `json:"ofaUtil"`
|
||||
MediaUtil []*float64 `json:"mediaUtil"`
|
||||
ComputeUtil []*float64 `json:"computeUtil"`
|
||||
CopyUtil []*float64 `json:"copyUtil"`
|
||||
HotspotTemperature []*float64 `json:"hotspotTemperature"`
|
||||
FanRPM []*float64 `json:"fanRPM"`
|
||||
AICPUUtil []*float64 `json:"aiCPUUtil"`
|
||||
CtrlCPUUtil []*float64 `json:"ctrlCPUUtil"`
|
||||
DDRUsed []*float64 `json:"ddrUsed"`
|
||||
DDRTotal []*float64 `json:"ddrTotal"`
|
||||
HBMUsed []*float64 `json:"hbmUsed"`
|
||||
HBMTotal []*float64 `json:"hbmTotal"`
|
||||
DDRBandwidth []*float64 `json:"ddrBandwidth"`
|
||||
HBMBandwidth []*float64 `json:"hbmBandwidth"`
|
||||
MemoryBandwidth []*float64 `json:"memoryBandwidth"`
|
||||
MediaFrequency []*float64 `json:"mediaFrequency"`
|
||||
HugepagesUsed []*float64 `json:"hugepagesUsed"`
|
||||
HugepagesTotal []*float64 `json:"hugepagesTotal"`
|
||||
|
||||
ProcessCount []int `json:"processCount"`
|
||||
BucketSeconds int64 `json:"bucketSeconds"`
|
||||
SampleCount int64 `json:"sampleCount"`
|
||||
MemoryTemperatureValue []*float64 `json:"memoryTemperatureValue"`
|
||||
FrequencyValue []*float64 `json:"frequencyValue"`
|
||||
MemoryFrequencyValue []*float64 `json:"memoryFrequencyValue"`
|
||||
Date []time.Time `json:"date"`
|
||||
GPUValue []*float64 `json:"gpuValue"`
|
||||
TemperatureValue []*float64 `json:"temperatureValue"`
|
||||
PowerTotal []*float64 `json:"powerTotal"`
|
||||
PowerUsed []*float64 `json:"powerUsed"`
|
||||
PowerPercent []*float64 `json:"powerPercent"`
|
||||
MemoryTotal []*float64 `json:"memoryTotal"`
|
||||
MemoryUsed []*float64 `json:"memoryUsed"`
|
||||
MemoryPercent []*float64 `json:"memoryPercent"`
|
||||
SpeedValue []*float64 `json:"speedValue"`
|
||||
|
||||
ProcessCount []*float64 `json:"processCount"`
|
||||
GPUProcesses [][]GPUProcess `json:"gpuProcesses"`
|
||||
}
|
||||
|
||||
@@ -81,3 +125,28 @@ type GPUProcess struct {
|
||||
ProcessName string `json:"processName"`
|
||||
UsedMemory string `json:"usedMemory"`
|
||||
}
|
||||
|
||||
type MonitorVLLMSearch struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
StartTime time.Time `json:"startTime" validate:"required"`
|
||||
EndTime time.Time `json:"endTime" validate:"required"`
|
||||
Aggregation string `json:"aggregation" validate:"omitempty,oneof=avg max"`
|
||||
}
|
||||
|
||||
type MonitorVLLMData struct {
|
||||
SampleCount int64 `json:"sampleCount"`
|
||||
BucketSeconds int64 `json:"bucketSeconds"`
|
||||
Points []model.MonitorVLLM `json:"points"`
|
||||
}
|
||||
|
||||
type MonitorVLLMCurrent struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
}
|
||||
|
||||
type MonitorVLLMClean struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
}
|
||||
|
||||
type MonitorClean struct {
|
||||
Type string `json:"type" validate:"required,oneof=host gpu"`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type MonitorBase struct {
|
||||
BaseModel
|
||||
Cpu float64 `json:"cpu"`
|
||||
@@ -33,14 +35,78 @@ type MonitorNetwork struct {
|
||||
}
|
||||
|
||||
type MonitorGPU struct {
|
||||
MemoryUtil *float64 `json:"memoryUtil"`
|
||||
MemoryActivity *float64 `json:"memoryActivity"`
|
||||
EncoderUtil *float64 `json:"encoderUtil"`
|
||||
DecoderUtil *float64 `json:"decoderUtil"`
|
||||
JPEGUtil *float64 `json:"jpegUtil"`
|
||||
OFAUtil *float64 `json:"ofaUtil"`
|
||||
MediaUtil *float64 `json:"mediaUtil"`
|
||||
ComputeUtil *float64 `json:"computeUtil"`
|
||||
CopyUtil *float64 `json:"copyUtil"`
|
||||
HotspotTemperature *float64 `json:"hotspotTemperature"`
|
||||
FanRPM *float64 `json:"fanRPM"`
|
||||
AICPUUtil *float64 `json:"aiCPUUtil"`
|
||||
CtrlCPUUtil *float64 `json:"ctrlCPUUtil"`
|
||||
DDRUsed *float64 `json:"ddrUsed"`
|
||||
DDRTotal *float64 `json:"ddrTotal"`
|
||||
HBMUsed *float64 `json:"hbmUsed"`
|
||||
HBMTotal *float64 `json:"hbmTotal"`
|
||||
DDRBandwidth *float64 `json:"ddrBandwidth"`
|
||||
HBMBandwidth *float64 `json:"hbmBandwidth"`
|
||||
MemoryBandwidth *float64 `json:"memoryBandwidth"`
|
||||
MediaFrequency *float64 `json:"mediaFrequency"`
|
||||
HugepagesUsed *float64 `json:"hugepagesUsed"`
|
||||
HugepagesTotal *float64 `json:"hugepagesTotal"`
|
||||
|
||||
MemoryTemperature *float64 `json:"memoryTemperature"`
|
||||
DeviceID string `json:"deviceID"`
|
||||
DeviceType string `json:"deviceType"`
|
||||
ProcessStatus string `json:"processStatus"`
|
||||
Frequency *float64 `json:"frequency"`
|
||||
MemoryFrequency *float64 `json:"memoryFrequency"`
|
||||
IntervalSeconds int `json:"intervalSeconds"`
|
||||
BaseModel
|
||||
ProductName string `json:"productName"`
|
||||
GPUUtil float64 `json:"gpuUtil"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
PowerDraw float64 `json:"powerDraw"`
|
||||
MaxPowerLimit float64 `json:"maxPowerLimit"`
|
||||
MemUsed float64 `json:"memUsed"`
|
||||
MemTotal float64 `json:"memTotal"`
|
||||
FanSpeed int `json:"fanSpeed"`
|
||||
Processes string `json:"processes"`
|
||||
ProductName string `json:"productName"`
|
||||
GPUUtil *float64 `json:"gpuUtil"`
|
||||
Temperature *float64 `json:"temperature"`
|
||||
PowerDraw *float64 `json:"powerDraw"`
|
||||
MaxPowerLimit *float64 `json:"maxPowerLimit"`
|
||||
MemUsed *float64 `json:"memUsed"`
|
||||
MemTotal *float64 `json:"memTotal"`
|
||||
FanSpeed *float64 `json:"fanSpeed"`
|
||||
Processes string `json:"processes"`
|
||||
}
|
||||
|
||||
type MonitorVLLM struct {
|
||||
ID uint `json:"-" gorm:"primarykey;autoIncrement"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
AppInstallID uint `json:"appInstallID"`
|
||||
Status string `json:"status"`
|
||||
RawMetrics string `json:"-"`
|
||||
HistogramDeltas string `json:"-"`
|
||||
|
||||
Running *float64 `json:"running"`
|
||||
Waiting *float64 `json:"waiting"`
|
||||
CacheUsage *float64 `json:"cacheUsage"`
|
||||
PromptThroughput *float64 `json:"promptThroughput"`
|
||||
GenerationThroughput *float64 `json:"generationThroughput"`
|
||||
RequestThroughput *float64 `json:"requestThroughput"`
|
||||
TimeToFirstToken *float64 `json:"timeToFirstToken"`
|
||||
TimePerOutputToken *float64 `json:"timePerOutputToken"`
|
||||
RequestLatency *float64 `json:"requestLatency"`
|
||||
PrefillTime *float64 `json:"prefillTime"`
|
||||
DecodeTime *float64 `json:"decodeTime"`
|
||||
TimeToFirstTokenP50 *float64 `json:"timeToFirstTokenP50"`
|
||||
TimeToFirstTokenP90 *float64 `json:"timeToFirstTokenP90"`
|
||||
TimeToFirstTokenP95 *float64 `json:"timeToFirstTokenP95"`
|
||||
TimeToFirstTokenP99 *float64 `json:"timeToFirstTokenP99"`
|
||||
TimePerOutputTokenP50 *float64 `json:"timePerOutputTokenP50"`
|
||||
TimePerOutputTokenP90 *float64 `json:"timePerOutputTokenP90"`
|
||||
TimePerOutputTokenP95 *float64 `json:"timePerOutputTokenP95"`
|
||||
TimePerOutputTokenP99 *float64 `json:"timePerOutputTokenP99"`
|
||||
RequestLatencyP50 *float64 `json:"requestLatencyP50"`
|
||||
RequestLatencyP90 *float64 `json:"requestLatencyP90"`
|
||||
RequestLatencyP95 *float64 `json:"requestLatencyP95"`
|
||||
RequestLatencyP99 *float64 `json:"requestLatencyP99"`
|
||||
}
|
||||
|
||||
+103
-13
@@ -1,6 +1,8 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -10,11 +12,25 @@ import (
|
||||
|
||||
type MonitorRepo struct{}
|
||||
|
||||
type GPUHistoryPoint struct {
|
||||
model.MonitorGPU
|
||||
Bucket int64
|
||||
PowerPercent *float64
|
||||
MemoryPercent *float64
|
||||
ProcessCount *float64
|
||||
}
|
||||
|
||||
type IMonitorRepo interface {
|
||||
CleanHost() error
|
||||
CleanGPU() error
|
||||
GetBase(opts ...DBOption) ([]model.MonitorBase, error)
|
||||
GetGPU(opts ...DBOption) ([]model.MonitorGPU, error)
|
||||
CountGPU(opts ...DBOption) (int64, error)
|
||||
GetGPUHistory(start time.Time, bucketSeconds int64, aggregation string, opts ...DBOption) ([]GPUHistoryPoint, error)
|
||||
GetGPUDevices() ([]model.MonitorGPU, error)
|
||||
GetIO(opts ...DBOption) ([]model.MonitorIO, error)
|
||||
GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, error)
|
||||
GetIONames() ([]string, error)
|
||||
GetNetworkNames() ([]string, error)
|
||||
|
||||
CreateMonitorBase(model model.MonitorBase) error
|
||||
BatchCreateMonitorGPU(list []model.MonitorGPU) error
|
||||
@@ -25,13 +41,26 @@ type IMonitorRepo interface {
|
||||
DelMonitorIO(timeForDelete time.Time) error
|
||||
DelMonitorNet(timeForDelete time.Time) error
|
||||
|
||||
WithByProductName(name string) DBOption
|
||||
WithByGPUDevice(deviceID, name string, legacy bool) DBOption
|
||||
}
|
||||
|
||||
func NewIMonitorRepo() IMonitorRepo {
|
||||
return &MonitorRepo{}
|
||||
}
|
||||
|
||||
func (s *MonitorRepo) CleanHost() error {
|
||||
for _, item := range []interface{}{&model.MonitorBase{}, &model.MonitorIO{}, &model.MonitorNetwork{}} {
|
||||
if err := global.MonitorDB.Where("1 = 1").Delete(item).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MonitorRepo) CleanGPU() error {
|
||||
return global.GPUMonitorDB.Where("1 = 1").Delete(&model.MonitorGPU{}).Error
|
||||
}
|
||||
|
||||
func (u *MonitorRepo) GetBase(opts ...DBOption) ([]model.MonitorBase, error) {
|
||||
var data []model.MonitorBase
|
||||
db := global.MonitorDB
|
||||
@@ -59,14 +88,16 @@ func (u *MonitorRepo) GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, erro
|
||||
err := db.Find(&data).Error
|
||||
return data, err
|
||||
}
|
||||
func (u *MonitorRepo) GetGPU(opts ...DBOption) ([]model.MonitorGPU, error) {
|
||||
var data []model.MonitorGPU
|
||||
db := global.GPUMonitorDB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&data).Error
|
||||
return data, err
|
||||
func (u *MonitorRepo) GetIONames() ([]string, error) {
|
||||
var names []string
|
||||
err := global.MonitorDB.Model(&model.MonitorIO{}).Distinct().Pluck("name", &names).Error
|
||||
return names, err
|
||||
}
|
||||
|
||||
func (u *MonitorRepo) GetNetworkNames() ([]string, error) {
|
||||
var names []string
|
||||
err := global.MonitorDB.Model(&model.MonitorNetwork{}).Distinct().Pluck("name", &names).Error
|
||||
return names, err
|
||||
}
|
||||
|
||||
func (u *MonitorRepo) CreateMonitorBase(model model.MonitorBase) error {
|
||||
@@ -97,8 +128,67 @@ func (s *MonitorRepo) DelMonitorGPU(timeForDelete time.Time) error {
|
||||
return global.GPUMonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorGPU{}).Error
|
||||
}
|
||||
|
||||
func (s *MonitorRepo) WithByProductName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("product_name = ?", name)
|
||||
func (u *MonitorRepo) GetGPUDevices() ([]model.MonitorGPU, error) {
|
||||
var data []model.MonitorGPU
|
||||
err := global.GPUMonitorDB.Model(&model.MonitorGPU{}).Select("device_id, product_name, device_type").Group("device_id, product_name, device_type").Order("product_name, device_id").Find(&data).Error
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (u *MonitorRepo) WithByGPUDevice(deviceID, name string, legacy bool) DBOption {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
if deviceID != "" {
|
||||
return db.Where("device_id = ?", deviceID)
|
||||
}
|
||||
db = db.Where("product_name = ?", name)
|
||||
if legacy {
|
||||
db = db.Where("device_id IS NULL OR device_id = ''")
|
||||
}
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
func (u *MonitorRepo) CountGPU(opts ...DBOption) (int64, error) {
|
||||
db := global.GPUMonitorDB.Model(&model.MonitorGPU{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
var count int64
|
||||
err := db.Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (u *MonitorRepo) GetGPUHistory(start time.Time, bucketSeconds int64, aggregation string, opts ...DBOption) ([]GPUHistoryPoint, error) {
|
||||
db := global.GPUMonitorDB.Model(&model.MonitorGPU{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
expressions := []string{
|
||||
"CASE WHEN max_power_limit > 0 THEN 100.0 * power_draw / max_power_limit END",
|
||||
"CASE WHEN mem_total > 0 AND mem_used IS NOT NULL THEN 100.0 * mem_used / mem_total ELSE memory_util END",
|
||||
"CASE WHEN (process_status = 'ok' OR process_status IS NULL OR process_status = '') AND json_valid(processes) THEN CASE WHEN json_type(processes) = 'array' THEN json_array_length(processes) END END",
|
||||
}
|
||||
aliases := []string{"power_percent", "memory_percent", "process_count"}
|
||||
columns := []string{"*"}
|
||||
if bucketSeconds > 0 {
|
||||
operation := "AVG"
|
||||
if aggregation == "max" {
|
||||
operation = "MAX"
|
||||
}
|
||||
columns = []string{fmt.Sprintf("(CAST(strftime('%%s', created_at) AS INTEGER) - %d) / %d AS bucket", start.Unix(), bucketSeconds)}
|
||||
for _, column := range []string{"memory_activity", "encoder_util", "decoder_util", "jpeg_util", "ofa_util", "media_util", "compute_util", "copy_util", "hotspot_temperature", "fan_rpm", "ai_cpu_util", "ctrl_cpu_util", "ddr_used", "ddr_total", "hbm_used", "hbm_total", "ddr_bandwidth", "hbm_bandwidth", "memory_bandwidth", "media_frequency", "hugepages_used", "hugepages_total", "gpu_util", "temperature", "memory_temperature", "power_draw", "max_power_limit", "mem_used", "mem_total", "frequency", "memory_frequency", "fan_speed"} {
|
||||
columns = append(columns, operation+"("+column+") AS "+column)
|
||||
}
|
||||
for i := range expressions {
|
||||
expressions[i] = operation + "(" + expressions[i] + ")"
|
||||
}
|
||||
db = db.Group("bucket").Order("bucket ASC")
|
||||
} else {
|
||||
db = db.Order("created_at ASC, id ASC")
|
||||
}
|
||||
for i, expression := range expressions {
|
||||
columns = append(columns, expression+" AS "+aliases[i])
|
||||
}
|
||||
var data []GPUHistoryPoint
|
||||
err := db.Select(strings.Join(columns, ", ")).Scan(&data).Error
|
||||
return data, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type VLLMMonitorRepo struct{}
|
||||
|
||||
type VLLMHistoryPoint struct {
|
||||
model.MonitorVLLM
|
||||
Bucket int64
|
||||
HistogramSamples string
|
||||
}
|
||||
|
||||
func (r *VLLMMonitorRepo) Create(point *model.MonitorVLLM) error {
|
||||
return global.VLLMMonitorDB.Create(point).Error
|
||||
}
|
||||
|
||||
func (r *VLLMMonitorRepo) Latest(id uint) (model.MonitorVLLM, error) {
|
||||
var point model.MonitorVLLM
|
||||
db := global.VLLMMonitorDB.Where("app_install_id = ?", id)
|
||||
err := db.Order("created_at DESC, id DESC").First(&point).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return point, nil
|
||||
}
|
||||
return point, err
|
||||
}
|
||||
|
||||
func (r *VLLMMonitorRepo) CleanTarget(id uint) error {
|
||||
return global.VLLMMonitorDB.Where("app_install_id = ?", id).Delete(&model.MonitorVLLM{}).Error
|
||||
}
|
||||
|
||||
func (r *VLLMMonitorRepo) DeleteBefore(before time.Time) error {
|
||||
return global.VLLMMonitorDB.Where("created_at < ?", before).Delete(&model.MonitorVLLM{}).Error
|
||||
}
|
||||
|
||||
func (r *VLLMMonitorRepo) Count(id uint, start, end time.Time) (int64, error) {
|
||||
var count int64
|
||||
db := global.VLLMMonitorDB.Model(&model.MonitorVLLM{}).Where("app_install_id = ? AND created_at >= ? AND created_at <= ?", id, start, end)
|
||||
err := db.Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (r *VLLMMonitorRepo) History(id uint, start, end time.Time, seconds int64, aggregation string) ([]VLLMHistoryPoint, error) {
|
||||
db := global.VLLMMonitorDB.Model(&model.MonitorVLLM{}).Where("app_install_id = ? AND created_at >= ? AND created_at <= ?", id, start, end)
|
||||
metrics := []string{"running", "waiting", "cache_usage", "prompt_throughput", "generation_throughput", "request_throughput", "time_to_first_token", "time_per_output_token", "request_latency", "prefill_time", "decode_time", "time_to_first_token_p50", "time_to_first_token_p90", "time_to_first_token_p95", "time_to_first_token_p99", "time_per_output_token_p50", "time_per_output_token_p90", "time_per_output_token_p95", "time_per_output_token_p99", "request_latency_p50", "request_latency_p90", "request_latency_p95", "request_latency_p99"}
|
||||
var columns []string
|
||||
if seconds > 0 {
|
||||
operation := "AVG"
|
||||
if aggregation == "max" {
|
||||
operation = "MAX"
|
||||
}
|
||||
columns = []string{fmt.Sprintf("(CAST(strftime('%%s', created_at) AS INTEGER) - %d) / %d AS bucket", start.Unix(), seconds)}
|
||||
for _, column := range metrics {
|
||||
if aggregation != "max" && (strings.HasPrefix(column, "time_to_first_token_p") || strings.HasPrefix(column, "time_per_output_token_p") || strings.HasPrefix(column, "request_latency_p")) {
|
||||
continue
|
||||
}
|
||||
columns = append(columns, operation+"("+column+") AS "+column)
|
||||
}
|
||||
if aggregation != "max" {
|
||||
columns = append(columns, "json_group_array(json(NULLIF(histogram_deltas, ''))) AS histogram_samples")
|
||||
}
|
||||
db = db.Group("bucket").Order("bucket ASC")
|
||||
} else {
|
||||
columns = append([]string{"id", "created_at", "app_install_id", "status"}, metrics...)
|
||||
db = db.Order("created_at ASC, id ASC")
|
||||
}
|
||||
var points []VLLMHistoryPoint
|
||||
err := db.Select(strings.Join(columns, ", ")).Scan(&points).Error
|
||||
return points, err
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
||||
alertconfig "github.com/1Panel-dev/1Panel/agent/utils/alert_config"
|
||||
alertwebhook "github.com/1Panel-dev/1Panel/agent/utils/alert_webhook"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
@@ -109,7 +110,38 @@ func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO,
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
cronjobProjects := make(map[string]uint)
|
||||
var cronjobIDs []uint
|
||||
for _, item := range alerts {
|
||||
if alertUtil.GetCronJobType(item.Type) != "cronJob" {
|
||||
continue
|
||||
}
|
||||
if _, exists := cronjobProjects[item.Project]; exists {
|
||||
continue
|
||||
}
|
||||
id, parseErr := strconv.ParseUint(item.Project, 10, strconv.IntSize)
|
||||
if parseErr != nil || id == 0 {
|
||||
continue
|
||||
}
|
||||
cronjobProjects[item.Project] = uint(id)
|
||||
cronjobIDs = append(cronjobIDs, uint(id))
|
||||
}
|
||||
cronjobsByID := make(map[uint]model.Cronjob)
|
||||
if len(cronjobIDs) > 0 {
|
||||
cronjobs, err := cronjobRepo.List(repo.WithByIDs(cronjobIDs))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, cronjob := range cronjobs {
|
||||
cronjobsByID[cronjob.ID] = cronjob
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range alerts {
|
||||
var taskName string
|
||||
if cronjob, exists := cronjobsByID[cronjobProjects[item.Project]]; exists && cronjob.Type == item.Type {
|
||||
taskName = cronjob.Name
|
||||
}
|
||||
|
||||
result = append(result, dto.AlertDTO{
|
||||
ID: item.ID,
|
||||
@@ -119,6 +151,7 @@ func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO,
|
||||
Method: item.Method,
|
||||
Title: item.Title,
|
||||
Project: item.Project,
|
||||
TaskName: taskName,
|
||||
Status: item.Status,
|
||||
SendCount: item.SendCount,
|
||||
AdvancedParams: item.AdvancedParams,
|
||||
@@ -190,6 +223,16 @@ func (a AlertService) CreateAlert(create dto.AlertCreate, operator string) error
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
advanced, err := prepareCronJobAlertParams(create.Type, "", create.AdvancedParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
create.AdvancedParams = advanced
|
||||
if create.Status != constant.AlertDisable {
|
||||
if err := a.validateCronJobAlertChannels(create.Type, advanced, create.Method); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
alertInfo.Status = constant.AlertEnable
|
||||
if err := copier.Copy(&alertInfo, &create); err != nil {
|
||||
return buserr.WithErr("ErrStructTransform", err)
|
||||
@@ -207,11 +250,24 @@ func (a AlertService) CreateAlert(create dto.AlertCreate, operator string) error
|
||||
}
|
||||
|
||||
func (a AlertService) UpdateAlert(req dto.AlertUpdate, operator string) error {
|
||||
if alertUtil.GetCronJobType(req.Type) == "cronJob" {
|
||||
previous, err := alertRepo.Get(repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.AdvancedParams, err = prepareCronJobAlertParams(req.Type, previous.AdvancedParams, req.AdvancedParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
methodTypes, err := a.validateAlertMethodReferences(req.Method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.Status != constant.AlertDisable {
|
||||
if err := a.validateCronJobAlertChannels(req.Type, req.AdvancedParams, req.Method); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.validateAlertMethodEntitlement(methodTypes); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -278,6 +334,9 @@ func (a AlertService) UpdateStatus(id uint, status string) error {
|
||||
return err
|
||||
}
|
||||
if status == constant.AlertEnable {
|
||||
if err := a.validateCronJobAlertChannels(alertInfo.Type, alertInfo.AdvancedParams, alertInfo.Method); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.validateAlertMethodEntitlement(methodTypes); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1021,6 +1080,23 @@ func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate, operator
|
||||
alertRepo.WithByType(updateAlert.Type),
|
||||
alertRepo.WithByProject(updateAlert.Project),
|
||||
)
|
||||
advanced, err := prepareCronJobAlertParams(updateAlert.Type, alertInfo.AdvancedParams, updateAlert.AdvancedParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateAlert.AdvancedParams = advanced
|
||||
if alertUtil.GetCronJobType(updateAlert.Type) == "cronJob" {
|
||||
upMap["advanced_params"] = advanced
|
||||
}
|
||||
if newStatus == constant.AlertEnable {
|
||||
method := updateAlert.Method
|
||||
if method == "" {
|
||||
method = alertInfo.Method
|
||||
}
|
||||
if err := a.validateCronJobAlertChannels(updateAlert.Type, advanced, method); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if alertInfo.ID > 0 {
|
||||
shouldUpdate := false
|
||||
@@ -1034,6 +1110,9 @@ func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate, operator
|
||||
if val, ok := upMap["method"]; ok && val != "" && val != alertInfo.Method {
|
||||
shouldUpdate = true
|
||||
}
|
||||
if val, ok := upMap["advanced_params"]; ok && val != alertInfo.AdvancedParams {
|
||||
shouldUpdate = true
|
||||
}
|
||||
|
||||
if shouldUpdate {
|
||||
if err := alertRepo.Update(
|
||||
@@ -1055,3 +1134,22 @@ func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate, operator
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareCronJobAlertParams(alertType, previous, incoming string) (string, error) {
|
||||
if alertUtil.GetCronJobType(alertType) != "cronJob" {
|
||||
return incoming, nil
|
||||
}
|
||||
return alertUtil.MergeCronJobAlertParams(previous, incoming)
|
||||
}
|
||||
|
||||
func (a AlertService) validateCronJobAlertChannels(alertType, advanced, method string) error {
|
||||
if alertUtil.GetCronJobType(alertType) != "cronJob" {
|
||||
return nil
|
||||
}
|
||||
mode, err := alertUtil.CronJobAlertTriggerMode(advanced)
|
||||
if err != nil || mode != alertUtil.CronJobAlertSuccess {
|
||||
return err
|
||||
}
|
||||
_, err = a.validateAlertMethodReferences(method)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -530,11 +530,15 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to load ssh login logs: %v", err)
|
||||
}
|
||||
interfaceAddrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
global.LOG.Warnf("Failed to load local IP addresses for ssh login alert: %v", err)
|
||||
}
|
||||
count, records := summarizeSSHLoginHistories(
|
||||
histories,
|
||||
now,
|
||||
failedWindow,
|
||||
strings.Split(strings.TrimSpace(alert.AdvancedParams), "\n"),
|
||||
sshSuccessLoginWhitelist(alert.AdvancedParams, interfaceAddrs),
|
||||
)
|
||||
isAlert := count >= int(alert.Count)
|
||||
if isAlert {
|
||||
@@ -570,6 +574,19 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
||||
}
|
||||
}
|
||||
|
||||
func sshSuccessLoginWhitelist(configured string, interfaceAddrs []net.Addr) []string {
|
||||
whitelist := strings.Split(strings.TrimSpace(configured), "\n")
|
||||
whitelist = append(whitelist, "127.0.0.0/8", "::1")
|
||||
for _, addr := range interfaceAddrs {
|
||||
ipNet, ok := addr.(*net.IPNet)
|
||||
if !ok || ipNet.IP == nil || ipNet.IP.IsUnspecified() {
|
||||
continue
|
||||
}
|
||||
whitelist = append(whitelist, ipNet.IP.String())
|
||||
}
|
||||
return whitelist
|
||||
}
|
||||
|
||||
func filterLoginLogsNotInWhitelist(records []model.LoginLog, whitelist []string) []model.LoginLog {
|
||||
filtered := make([]model.LoginLog, 0, len(records))
|
||||
for _, record := range records {
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@@ -75,6 +76,7 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
|
||||
EntryID: cronjob.ID,
|
||||
}
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))
|
||||
item.AlertTriggerMode, _ = alertUtil.CronJobAlertTriggerMode(alertInfo.AdvancedParams)
|
||||
if alertInfo.SendCount != 0 {
|
||||
item.AlertCount = alertInfo.SendCount
|
||||
} else {
|
||||
@@ -98,9 +100,11 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
|
||||
AlertType: cronjob.Type,
|
||||
EntryID: cronjob.ID,
|
||||
}
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))))
|
||||
item.AlertMethod = alertInfo.Method
|
||||
if alertInfo.SendCount != 0 {
|
||||
item.AlertTitle = alertInfo.Title
|
||||
item.AlertTriggerMode, _ = alertUtil.CronJobAlertTriggerMode(alertInfo.AdvancedParams)
|
||||
if alertInfo.Status == constant.AlertEnable {
|
||||
item.AlertCount = alertInfo.SendCount
|
||||
} else {
|
||||
item.AlertCount = 0
|
||||
@@ -195,11 +199,12 @@ func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
|
||||
}
|
||||
}
|
||||
item.SourceAccounts, item.DownloadAccount, _ = loadBackupNamesByID(cronjob.SourceAccountIDs, cronjob.DownloadAccountID)
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(cronjob.Type), alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))), repo.WithByStatus(constant.AlertEnable))
|
||||
if alertInfo.SendCount != 0 {
|
||||
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(cronjob.Type), alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))))
|
||||
item.AlertTitle = alertInfo.Title
|
||||
item.AlertMethod = alertInfo.Method
|
||||
item.AlertTriggerMode, _ = alertUtil.CronJobAlertTriggerMode(alertInfo.AdvancedParams)
|
||||
if alertInfo.Status == constant.AlertEnable {
|
||||
item.AlertCount = alertInfo.SendCount
|
||||
item.AlertTitle = alertInfo.Title
|
||||
item.AlertMethod = alertInfo.Method
|
||||
} else {
|
||||
item.AlertCount = 0
|
||||
}
|
||||
@@ -213,6 +218,17 @@ func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
|
||||
}
|
||||
|
||||
func (u *CronjobService) Import(req []dto.CronjobTrans, operator string) error {
|
||||
for _, item := range req {
|
||||
advanced, err := cronJobAlertAdvancedParams(item.AlertTriggerMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if item.AlertCount != 0 {
|
||||
if err := (AlertService{}).validateCronJobAlertChannels(item.Type, advanced, item.AlertMethod); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, item := range req {
|
||||
cronjobItem, _ := cronjobRepo.Get(repo.WithByName(item.Name))
|
||||
if cronjobItem.ID != 0 {
|
||||
@@ -395,17 +411,27 @@ func (u *CronjobService) Import(req []dto.CronjobTrans, operator string) error {
|
||||
} else {
|
||||
cronjob.Status = constant.StatusDisable
|
||||
}
|
||||
_ = cronjobRepo.Create(&cronjob)
|
||||
if item.AlertCount != 0 && item.AlertTitle != "" && item.AlertMethod != "" {
|
||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||
return err
|
||||
}
|
||||
if item.AlertTitle != "" && item.AlertMethod != "" {
|
||||
advanced, _ := cronJobAlertAdvancedParams(item.AlertTriggerMode)
|
||||
status := constant.AlertEnable
|
||||
if item.AlertCount == 0 {
|
||||
status = constant.AlertDisable
|
||||
}
|
||||
createAlert := dto.AlertCreate{
|
||||
Title: item.AlertTitle,
|
||||
SendCount: item.AlertCount,
|
||||
Method: item.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronjob.ID)),
|
||||
Status: constant.AlertEnable,
|
||||
Title: item.AlertTitle,
|
||||
SendCount: item.AlertCount,
|
||||
Method: item.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronjob.ID)),
|
||||
Status: status,
|
||||
AdvancedParams: advanced,
|
||||
}
|
||||
if err := NewIAlertService().CreateAlert(createAlert, operator); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = NewIAlertService().CreateAlert(createAlert, operator)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -562,6 +588,15 @@ func (u *CronjobService) HandleOnce(id uint) error {
|
||||
}
|
||||
|
||||
func (u *CronjobService) Create(req dto.CronjobOperate, operator string) error {
|
||||
advanced, err := cronJobAlertAdvancedParams(req.AlertTriggerMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.AlertCount != 0 {
|
||||
if err := (AlertService{}).validateCronJobAlertChannels(req.Type, advanced, req.AlertMethod); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cronjob, _ := cronjobRepo.Get(repo.WithByName(req.Name))
|
||||
if cronjob.ID != 0 {
|
||||
return buserr.New("ErrRecordExist")
|
||||
@@ -603,12 +638,13 @@ func (u *CronjobService) Create(req dto.CronjobOperate, operator string) error {
|
||||
}
|
||||
if req.AlertCount != 0 && req.AlertTitle != "" && req.AlertMethod != "" {
|
||||
createAlert := dto.AlertCreate{
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronjob.ID)),
|
||||
Status: constant.AlertEnable,
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronjob.ID)),
|
||||
Status: constant.AlertEnable,
|
||||
AdvancedParams: advanced,
|
||||
}
|
||||
err := NewIAlertService().CreateAlert(createAlert, operator)
|
||||
if err != nil {
|
||||
@@ -682,6 +718,10 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
|
||||
}
|
||||
|
||||
func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string) error {
|
||||
advanced, err := cronJobAlertAdvancedParams(req.AlertTriggerMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var cronjob model.Cronjob
|
||||
if err := copier.Copy(&cronjob, &req); err != nil {
|
||||
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
|
||||
@@ -697,6 +737,20 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string
|
||||
if err != nil {
|
||||
return buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if req.AlertCount != 0 {
|
||||
previous, _ := alertRepo.Get(alertRepo.WithByType(cronModel.Type), alertRepo.WithByProject(strconv.Itoa(int(id))))
|
||||
merged, err := prepareCronJobAlertParams(cronModel.Type, previous.AdvancedParams, advanced)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
method := req.AlertMethod
|
||||
if method == "" {
|
||||
method = previous.Method
|
||||
}
|
||||
if err := (AlertService{}).validateCronJobAlertChannels(cronModel.Type, merged, method); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
upMap := make(map[string]interface{})
|
||||
cronjob.EntryIDs = cronModel.EntryIDs
|
||||
cronjob.Type = cronModel.Type
|
||||
@@ -753,11 +807,12 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string
|
||||
return err
|
||||
}
|
||||
updateAlert := dto.AlertCreate{
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronModel.ID)),
|
||||
Title: req.AlertTitle,
|
||||
SendCount: req.AlertCount,
|
||||
Method: req.AlertMethod,
|
||||
Type: cronjob.Type,
|
||||
Project: strconv.Itoa(int(cronModel.ID)),
|
||||
AdvancedParams: advanced,
|
||||
}
|
||||
err = NewIAlertService().ExternalUpdateAlert(updateAlert, operator)
|
||||
if err != nil {
|
||||
@@ -766,6 +821,17 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate, operator string
|
||||
return nil
|
||||
}
|
||||
|
||||
func cronJobAlertAdvancedParams(mode string) (string, error) {
|
||||
if mode == "" {
|
||||
return "", nil
|
||||
}
|
||||
data, err := json.Marshal(map[string]string{"alertTriggerMode": mode})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return alertUtil.MergeCronJobAlertParams("", string(data))
|
||||
}
|
||||
|
||||
func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||
cronjob, _ := cronjobRepo.Get(repo.WithByID(id))
|
||||
if cronjob.ID == 0 {
|
||||
|
||||
@@ -412,6 +412,7 @@ func addSkipTask(source string, taskItem *task.Task) {
|
||||
taskItem.Log(i18n.GetMsgByKey("NoSuchResource"))
|
||||
return nil
|
||||
}, nil)
|
||||
taskItem.SubTasks[len(taskItem.SubTasks)-1].StepAlias = cronJobSkippedStep
|
||||
}
|
||||
|
||||
func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
|
||||
@@ -56,10 +58,11 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
_ = taskRepo.Save(context.Background(), taskItem.Task)
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||
handleCronJobAlert(cronjob)
|
||||
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, err))
|
||||
return
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, nil))
|
||||
}()
|
||||
return
|
||||
}
|
||||
@@ -70,19 +73,20 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
||||
record.TaskID = ""
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||
handleCronJobAlert(cronjob)
|
||||
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, err))
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
if err := taskItem.Execute(); err != nil {
|
||||
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
|
||||
if len(taskItem.ID) == 0 {
|
||||
storedTask, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
|
||||
if len(storedTask.ID) == 0 {
|
||||
record.TaskID = ""
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
|
||||
handleCronJobAlert(cronjob)
|
||||
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, err))
|
||||
} else {
|
||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||
handleCronJobAlert(cronjob, cronJobAlertResult(taskItem, nil))
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -482,8 +486,33 @@ func hasBackup(cronjobType string) bool {
|
||||
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log" || cronjobType == "cutWebsiteLog"
|
||||
}
|
||||
|
||||
func handleCronJobAlert(cronjob *model.Cronjob) {
|
||||
const cronJobSkippedStep = "cronjob-skipped"
|
||||
|
||||
func cronJobAlertResult(taskItem *task.Task, err error) string {
|
||||
if errors.Is(err, context.Canceled) || taskItem.Task.Status == constant.StatusCanceled ||
|
||||
(taskItem.TaskCtx != nil && taskItem.TaskCtx.Err() != nil) {
|
||||
return ""
|
||||
}
|
||||
if err != nil {
|
||||
return alertUtil.CronJobAlertFailed
|
||||
}
|
||||
if taskItem.Task.Status != constant.StatusSuccess {
|
||||
return ""
|
||||
}
|
||||
for _, subTask := range taskItem.SubTasks {
|
||||
if subTask.StepAlias != cronJobSkippedStep {
|
||||
return alertUtil.CronJobAlertSuccess
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func handleCronJobAlert(cronjob *model.Cronjob, result string) {
|
||||
if result == "" {
|
||||
return
|
||||
}
|
||||
pushAlert := dto.PushAlert{
|
||||
Result: result,
|
||||
TaskName: cronjob.Name,
|
||||
AlertType: cronjob.Type,
|
||||
EntryID: cronjob.ID,
|
||||
|
||||
@@ -588,6 +588,9 @@ func loadAcceleratorInfo() ([]dto.GPUInfo, []dto.NPUInfo, []dto.XPUInfo) {
|
||||
xpuData []dto.XPUInfo
|
||||
)
|
||||
for _, device := range snapshot.Devices {
|
||||
if device.ParentID != "" {
|
||||
continue
|
||||
}
|
||||
switch device.Kind {
|
||||
case accelerator.KindGPU:
|
||||
if device.GPU == nil {
|
||||
@@ -597,7 +600,11 @@ func loadAcceleratorInfo() ([]dto.GPUInfo, []dto.NPUInfo, []dto.XPUInfo) {
|
||||
if err := copier.Copy(&dataItem, device.GPU); err != nil {
|
||||
continue
|
||||
}
|
||||
dataItem.PowerUsage = dataItem.PowerDraw + " / " + dataItem.MaxPowerLimit
|
||||
dataItem.MaxPowerLimit = device.GPU.PowerLimit
|
||||
dataItem.PowerUsage = dataItem.PowerDraw
|
||||
if dataItem.MaxPowerLimit != "" {
|
||||
dataItem.PowerUsage += " / " + dataItem.MaxPowerLimit
|
||||
}
|
||||
dataItem.MemoryUsage = dataItem.MemUsed + " / " + dataItem.MemTotal
|
||||
gpuData = append(gpuData, dataItem)
|
||||
case accelerator.KindNPU:
|
||||
|
||||
@@ -32,10 +32,11 @@ var (
|
||||
scriptRepo = repo.NewIScriptRepo()
|
||||
cronjobRepo = repo.NewICronjobRepo()
|
||||
|
||||
hostRepo = repo.NewIHostRepo()
|
||||
ftpRepo = repo.NewIFtpRepo()
|
||||
clamRepo = repo.NewIClamRepo()
|
||||
monitorRepo = repo.NewIMonitorRepo()
|
||||
hostRepo = repo.NewIHostRepo()
|
||||
ftpRepo = repo.NewIFtpRepo()
|
||||
clamRepo = repo.NewIClamRepo()
|
||||
monitorRepo = repo.NewIMonitorRepo()
|
||||
vllmMonitorRepo = &repo.VLLMMonitorRepo{}
|
||||
|
||||
settingRepo = repo.NewISettingRepo()
|
||||
forwardingRuleRepo = repo.NewIForwardingRuleRepo()
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/moby/go-archive"
|
||||
)
|
||||
|
||||
type ImageService struct{}
|
||||
|
||||
+437
-138
@@ -33,11 +33,14 @@ import (
|
||||
type MonitorService struct {
|
||||
DiskIO chan ([]disk.IOCountersStat)
|
||||
NetIO chan ([]net.IOCountersStat)
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
var (
|
||||
monitorCancel context.CancelFunc
|
||||
hostSysPath = loadHostSysPath()
|
||||
monitorSettingMutex sync.Mutex
|
||||
gpuMonitorMutex sync.Mutex
|
||||
monitorCancel context.CancelFunc
|
||||
hostSysPath = loadHostSysPath()
|
||||
|
||||
blockDevicePartitionCache sync.Map
|
||||
)
|
||||
@@ -47,8 +50,13 @@ type IMonitorService interface {
|
||||
LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorData, error)
|
||||
LoadSetting() (*dto.MonitorSetting, error)
|
||||
UpdateSetting(key, value string) error
|
||||
CleanData() error
|
||||
CleanData(monitorType string) error
|
||||
|
||||
LoadIOOptions() []string
|
||||
LoadNetworkOptions() []string
|
||||
LoadVLLMMonitorData(req dto.MonitorVLLMSearch) (dto.MonitorVLLMData, error)
|
||||
LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (model.MonitorVLLM, error)
|
||||
CleanVLLMMonitor(req dto.MonitorVLLMClean) error
|
||||
LoadGPUOptions() dto.MonitorGPUOptions
|
||||
LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)
|
||||
|
||||
@@ -60,6 +68,7 @@ func NewIMonitorService() IMonitorService {
|
||||
return &MonitorService{
|
||||
DiskIO: make(chan []disk.IOCountersStat, 2),
|
||||
NetIO: make(chan []net.IOCountersStat, 2),
|
||||
ctx: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +94,7 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
|
||||
base.TopCPUItems = processes
|
||||
base.TopCPU = ""
|
||||
}
|
||||
if req.Param == "all" || req.Param == "mem" {
|
||||
if req.Param == "all" || req.Param == "memory" {
|
||||
var processes []dto.Process
|
||||
_ = json.Unmarshal([]byte(base.TopMem), &processes)
|
||||
base.TopMemItems = processes
|
||||
@@ -96,7 +105,11 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
|
||||
data = append(data, itemData)
|
||||
}
|
||||
if req.Param == "all" || req.Param == "io" {
|
||||
bases, err := monitorRepo.GetIO(repo.WithByName(req.IO), repo.WithByCreatedAt(req.StartTime, req.EndTime))
|
||||
ioOpts := []repo.DBOption{repo.WithByCreatedAt(req.StartTime, req.EndTime)}
|
||||
if len(req.IO) != 0 {
|
||||
ioOpts = append(ioOpts, repo.WithByName(req.IO))
|
||||
}
|
||||
bases, err := monitorRepo.GetIO(ioOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -110,7 +123,11 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
|
||||
data = append(data, itemData)
|
||||
}
|
||||
if req.Param == "all" || req.Param == "network" {
|
||||
bases, err := monitorRepo.GetNetwork(repo.WithByName(req.Network), repo.WithByCreatedAt(req.StartTime, req.EndTime))
|
||||
netOpts := []repo.DBOption{repo.WithByCreatedAt(req.StartTime, req.EndTime)}
|
||||
if len(req.Network) != 0 {
|
||||
netOpts = append(netOpts, repo.WithByName(req.Network))
|
||||
}
|
||||
bases, err := monitorRepo.GetNetwork(netOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -126,117 +143,171 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadIOOptions() []string {
|
||||
optionSet := make(map[string]struct{})
|
||||
if diskStat, err := disk.IOCounters(); err == nil {
|
||||
for _, item := range diskStat {
|
||||
optionSet[item.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
// union with names recorded in the monitor db so removed devices stay selectable
|
||||
if names, err := monitorRepo.GetIONames(); err == nil {
|
||||
for _, name := range names {
|
||||
optionSet[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
return sortedMonitorOptions(optionSet)
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadNetworkOptions() []string {
|
||||
optionSet := make(map[string]struct{})
|
||||
if netStat, err := net.IOCounters(true); err == nil {
|
||||
for _, item := range netStat {
|
||||
optionSet[item.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
if names, err := monitorRepo.GetNetworkNames(); err == nil {
|
||||
for _, name := range names {
|
||||
optionSet[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
return sortedMonitorOptions(optionSet)
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadGPUOptions() dto.MonitorGPUOptions {
|
||||
var data dto.MonitorGPUOptions
|
||||
exist, client := accelerator.New()
|
||||
if !exist {
|
||||
return data
|
||||
}
|
||||
seen := make(map[string]bool)
|
||||
snapshot, err := client.Collect(context.Background())
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Load accelerator info failed, err: %v", err)
|
||||
global.LOG.Warnf("Load accelerator options failed: %v", err)
|
||||
} else {
|
||||
data = loadGPUOptions(snapshot)
|
||||
for _, item := range data.ChartHide {
|
||||
seen[item.DeviceID] = true
|
||||
}
|
||||
}
|
||||
data.Supported = true
|
||||
devices, err := monitorRepo.GetGPUDevices()
|
||||
if err != nil {
|
||||
global.LOG.Warnf("Load accelerator history options failed: %v", err)
|
||||
return data
|
||||
}
|
||||
if warning := snapshot.Warning(); warning != nil {
|
||||
global.LOG.Warnf("Load accelerator info partially failed, err: %v", warning)
|
||||
}
|
||||
return loadGPUOptions(snapshot)
|
||||
}
|
||||
|
||||
func loadGPUOptions(snapshot *accelerator.Snapshot) dto.MonitorGPUOptions {
|
||||
var data dto.MonitorGPUOptions
|
||||
hasGPUOrNPU := false
|
||||
hasXPU := false
|
||||
for _, item := range snapshot.Devices {
|
||||
if item.Kind == accelerator.KindXPU {
|
||||
hasXPU = true
|
||||
} else {
|
||||
hasGPUOrNPU = true
|
||||
for _, device := range devices {
|
||||
key := device.DeviceID
|
||||
if key == "" {
|
||||
key = "legacy:" + device.ProductName
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case hasGPUOrNPU && hasXPU:
|
||||
data.GPUType = "mixed"
|
||||
case hasXPU:
|
||||
data.GPUType = "xpu"
|
||||
case hasGPUOrNPU:
|
||||
data.GPUType = "gpu"
|
||||
}
|
||||
|
||||
sort.Slice(snapshot.Devices, func(i, j int) bool {
|
||||
if snapshot.Devices[i].Kind != snapshot.Devices[j].Kind {
|
||||
return snapshot.Devices[i].Kind < snapshot.Devices[j].Kind
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
if snapshot.Devices[i].Vendor != snapshot.Devices[j].Vendor {
|
||||
return snapshot.Devices[i].Vendor < snapshot.Devices[j].Vendor
|
||||
}
|
||||
if snapshot.Devices[i].NPUIndex != snapshot.Devices[j].NPUIndex {
|
||||
return snapshot.Devices[i].NPUIndex < snapshot.Devices[j].NPUIndex
|
||||
}
|
||||
if snapshot.Devices[i].ChipIndex != snapshot.Devices[j].ChipIndex {
|
||||
return snapshot.Devices[i].ChipIndex < snapshot.Devices[j].ChipIndex
|
||||
}
|
||||
return snapshot.Devices[i].Index < snapshot.Devices[j].Index
|
||||
})
|
||||
for _, item := range snapshot.Devices {
|
||||
optionType := "gpu"
|
||||
if item.Kind == accelerator.KindXPU {
|
||||
optionType = "xpu"
|
||||
}
|
||||
chartHide := dto.GPUChartHide{
|
||||
ProductName: item.Label,
|
||||
Type: optionType,
|
||||
GPU: !item.Capabilities.Utilization,
|
||||
Memory: !item.Capabilities.Memory,
|
||||
Power: !item.Capabilities.Power,
|
||||
PowerLimit: !item.Capabilities.PowerLimit,
|
||||
Temperature: !item.Capabilities.Temperature,
|
||||
Speed: !item.Capabilities.FanSpeed,
|
||||
}
|
||||
data.ChartHide = append(data.ChartHide, chartHide)
|
||||
data.Options = append(data.Options, chartHide.ProductName)
|
||||
seen[key] = true
|
||||
data.ChartHide = append(data.ChartHide, dto.GPUChartHide{DeviceID: device.DeviceID, ProductName: device.ProductName, Type: device.DeviceType, Legacy: device.DeviceID == ""})
|
||||
data.Options = append(data.Options, device.ProductName)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error) {
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
req.StartTime = req.StartTime.In(loc)
|
||||
req.EndTime = req.EndTime.In(loc)
|
||||
var data dto.MonitorGPUData
|
||||
gpuList, err := monitorRepo.GetGPU(repo.WithByCreatedAt(req.StartTime, req.EndTime), monitorRepo.WithByProductName(req.ProductName))
|
||||
if req.StartTime.IsZero() || req.EndTime.IsZero() || !req.EndTime.After(req.StartTime) {
|
||||
return data, fmt.Errorf("invalid GPU history time range")
|
||||
}
|
||||
if req.DeviceID == "" && req.ProductName == "" {
|
||||
return data, fmt.Errorf("GPU history requires a device")
|
||||
}
|
||||
if req.Aggregation != "" && req.Aggregation != "avg" && req.Aggregation != "max" {
|
||||
return data, fmt.Errorf("invalid GPU history aggregation")
|
||||
}
|
||||
loc, err := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
for _, gpu := range gpuList {
|
||||
data.Date = append(data.Date, gpu.CreatedAt)
|
||||
data.GPUValue = append(data.GPUValue, gpu.GPUUtil)
|
||||
data.TemperatureValue = append(data.TemperatureValue, gpu.Temperature)
|
||||
data.PowerUsed = append(data.PowerUsed, gpu.PowerDraw)
|
||||
data.PowerTotal = append(data.PowerTotal, gpu.MaxPowerLimit)
|
||||
if gpu.MaxPowerLimit != 0 {
|
||||
data.PowerPercent = append(data.PowerPercent, gpu.PowerDraw/gpu.MaxPowerLimit*100)
|
||||
} else {
|
||||
data.PowerPercent = append(data.PowerPercent, float64(0))
|
||||
req.StartTime, req.EndTime = req.StartTime.In(loc), req.EndTime.In(loc)
|
||||
opts := []repo.DBOption{repo.WithByCreatedAt(req.StartTime, req.EndTime), monitorRepo.WithByGPUDevice(req.DeviceID, req.ProductName, req.Legacy)}
|
||||
data.SampleCount, err = monitorRepo.CountGPU(opts...)
|
||||
if err != nil || data.SampleCount == 0 {
|
||||
return data, err
|
||||
}
|
||||
if data.SampleCount > 1200 {
|
||||
seconds := req.EndTime.Unix() - req.StartTime.Unix() + 1
|
||||
data.BucketSeconds = (seconds + 599) / 600
|
||||
}
|
||||
points, err := monitorRepo.GetGPUHistory(req.StartTime, data.BucketSeconds, req.Aggregation, opts...)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
samples := make([]repo.GPUHistoryPoint, 0, len(points))
|
||||
if data.BucketSeconds > 0 {
|
||||
next := 0
|
||||
for bucket := int64(0); bucket <= (req.EndTime.Unix()-req.StartTime.Unix())/data.BucketSeconds; bucket++ {
|
||||
point := repo.GPUHistoryPoint{}
|
||||
if next < len(points) && points[next].Bucket == bucket {
|
||||
point = points[next]
|
||||
next++
|
||||
}
|
||||
point.CreatedAt = time.Unix(req.StartTime.Unix()+bucket*data.BucketSeconds, 0).In(loc)
|
||||
if bucket == 0 {
|
||||
point.CreatedAt = req.StartTime
|
||||
}
|
||||
samples = append(samples, point)
|
||||
}
|
||||
|
||||
data.MemoryTotal = append(data.MemoryTotal, gpu.MemTotal)
|
||||
data.MemoryUsed = append(data.MemoryUsed, gpu.MemUsed)
|
||||
if gpu.MemTotal != 0 {
|
||||
data.MemoryPercent = append(data.MemoryPercent, gpu.MemUsed/gpu.MemTotal*100)
|
||||
} else {
|
||||
data.MemoryPercent = append(data.MemoryPercent, float64(0))
|
||||
} else {
|
||||
for i, point := range points {
|
||||
if i > 0 && points[i-1].IntervalSeconds > 0 {
|
||||
interval := time.Duration(points[i-1].IntervalSeconds) * time.Second
|
||||
if point.CreatedAt.Sub(points[i-1].CreatedAt) > 2*interval {
|
||||
samples = append(samples, repo.GPUHistoryPoint{MonitorGPU: model.MonitorGPU{BaseModel: model.BaseModel{CreatedAt: points[i-1].CreatedAt.Add(interval)}}})
|
||||
}
|
||||
}
|
||||
samples = append(samples, point)
|
||||
}
|
||||
var process []dto.GPUProcess
|
||||
if err := json.Unmarshal([]byte(gpu.Processes), &process); err == nil {
|
||||
data.ProcessCount = append(data.ProcessCount, len(process))
|
||||
data.GPUProcesses = append(data.GPUProcesses, process)
|
||||
} else {
|
||||
data.ProcessCount = append(data.ProcessCount, 0)
|
||||
data.GPUProcesses = append(data.GPUProcesses, []dto.GPUProcess{})
|
||||
}
|
||||
for _, point := range samples {
|
||||
data.Date = append(data.Date, point.CreatedAt)
|
||||
data.MemoryActivity = append(data.MemoryActivity, point.MemoryActivity)
|
||||
data.EncoderUtil = append(data.EncoderUtil, point.EncoderUtil)
|
||||
data.DecoderUtil = append(data.DecoderUtil, point.DecoderUtil)
|
||||
data.JPEGUtil = append(data.JPEGUtil, point.JPEGUtil)
|
||||
data.OFAUtil = append(data.OFAUtil, point.OFAUtil)
|
||||
data.MediaUtil = append(data.MediaUtil, point.MediaUtil)
|
||||
data.ComputeUtil = append(data.ComputeUtil, point.ComputeUtil)
|
||||
data.CopyUtil = append(data.CopyUtil, point.CopyUtil)
|
||||
data.HotspotTemperature = append(data.HotspotTemperature, point.HotspotTemperature)
|
||||
data.FanRPM = append(data.FanRPM, point.FanRPM)
|
||||
data.AICPUUtil = append(data.AICPUUtil, point.AICPUUtil)
|
||||
data.CtrlCPUUtil = append(data.CtrlCPUUtil, point.CtrlCPUUtil)
|
||||
data.DDRUsed = append(data.DDRUsed, point.DDRUsed)
|
||||
data.DDRTotal = append(data.DDRTotal, point.DDRTotal)
|
||||
data.HBMUsed = append(data.HBMUsed, point.HBMUsed)
|
||||
data.HBMTotal = append(data.HBMTotal, point.HBMTotal)
|
||||
data.DDRBandwidth = append(data.DDRBandwidth, point.DDRBandwidth)
|
||||
data.HBMBandwidth = append(data.HBMBandwidth, point.HBMBandwidth)
|
||||
data.MemoryBandwidth = append(data.MemoryBandwidth, point.MemoryBandwidth)
|
||||
data.MediaFrequency = append(data.MediaFrequency, point.MediaFrequency)
|
||||
data.HugepagesUsed = append(data.HugepagesUsed, point.HugepagesUsed)
|
||||
data.HugepagesTotal = append(data.HugepagesTotal, point.HugepagesTotal)
|
||||
data.GPUValue = append(data.GPUValue, point.GPUUtil)
|
||||
data.TemperatureValue = append(data.TemperatureValue, point.Temperature)
|
||||
data.MemoryTemperatureValue = append(data.MemoryTemperatureValue, point.MemoryTemperature)
|
||||
data.PowerUsed = append(data.PowerUsed, point.PowerDraw)
|
||||
data.PowerTotal = append(data.PowerTotal, point.MaxPowerLimit)
|
||||
data.PowerPercent = append(data.PowerPercent, point.PowerPercent)
|
||||
data.MemoryPercent = append(data.MemoryPercent, point.MemoryPercent)
|
||||
data.MemoryTotal = append(data.MemoryTotal, point.MemTotal)
|
||||
data.MemoryUsed = append(data.MemoryUsed, point.MemUsed)
|
||||
data.SpeedValue = append(data.SpeedValue, point.FanSpeed)
|
||||
data.FrequencyValue = append(data.FrequencyValue, point.Frequency)
|
||||
data.MemoryFrequencyValue = append(data.MemoryFrequencyValue, point.MemoryFrequency)
|
||||
data.ProcessCount = append(data.ProcessCount, point.ProcessCount)
|
||||
var processes []dto.GPUProcess
|
||||
if data.BucketSeconds == 0 && point.ProcessCount != nil {
|
||||
_ = json.Unmarshal([]byte(point.Processes), &processes)
|
||||
}
|
||||
data.SpeedValue = append(data.SpeedValue, gpu.FanSpeed)
|
||||
data.GPUProcesses = append(data.GPUProcesses, processes)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -262,9 +333,27 @@ func (m *MonitorService) LoadSetting() (*dto.MonitorSetting, error) {
|
||||
}
|
||||
|
||||
func (m *MonitorService) UpdateSetting(key, value string) error {
|
||||
monitorSettingMutex.Lock()
|
||||
defer monitorSettingMutex.Unlock()
|
||||
switch key {
|
||||
case "MonitorStatus", "GPUMonitorStatus", "VLLMMonitorStatus":
|
||||
if value != constant.StatusEnable && value != constant.StatusDisable {
|
||||
return fmt.Errorf("invalid monitoring status")
|
||||
}
|
||||
case "MonitorInterval", "GPUMonitorInterval", "VLLMMonitorInterval":
|
||||
interval, err := strconv.Atoi(value)
|
||||
if err != nil || interval < 10 || interval > 43200 {
|
||||
return fmt.Errorf("monitoring interval must be between 10 and 43200 seconds")
|
||||
}
|
||||
case "MonitorStoreDays", "GPUMonitorStoreDays", "VLLMMonitorStoreDays":
|
||||
days, err := strconv.Atoi(value)
|
||||
if err != nil || days < 1 {
|
||||
return fmt.Errorf("monitoring retention must be a positive integer")
|
||||
}
|
||||
}
|
||||
switch key {
|
||||
case "MonitorStatus":
|
||||
if value == constant.StatusEnable && global.MonitorCronID == 0 {
|
||||
if value == constant.StatusEnable && monitorCancel == nil {
|
||||
interval, err := settingRepo.Get(settingRepo.WithByKey("MonitorInterval"))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -273,8 +362,9 @@ func (m *MonitorService) UpdateSetting(key, value string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if value == constant.StatusDisable && global.MonitorCronID != 0 {
|
||||
if value == constant.StatusDisable && monitorCancel != nil {
|
||||
monitorCancel()
|
||||
monitorCancel = nil
|
||||
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
|
||||
global.MonitorCronID = 0
|
||||
}
|
||||
@@ -283,31 +373,80 @@ func (m *MonitorService) UpdateSetting(key, value string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status.Value == constant.StatusEnable && global.MonitorCronID != 0 {
|
||||
if status.Value == constant.StatusEnable && monitorCancel != nil {
|
||||
if err := StartMonitor(true, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case "GPUMonitorStatus":
|
||||
if value == constant.StatusEnable && global.GPUMonitorCronID == 0 {
|
||||
interval, err := settingRepo.GetValueByKey("GPUMonitorInterval")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := StartGPUMonitor(interval); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if value == constant.StatusDisable && global.GPUMonitorCronID != 0 {
|
||||
global.Cron.Remove(global.GPUMonitorCronID)
|
||||
global.GPUMonitorCronID = 0
|
||||
}
|
||||
case "GPUMonitorInterval":
|
||||
status, err := settingRepo.GetValueByKey("GPUMonitorStatus")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status == constant.StatusEnable {
|
||||
if err := StartGPUMonitor(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case "VLLMMonitorStatus":
|
||||
if value == constant.StatusEnable && global.VLLMMonitorCronID == 0 {
|
||||
interval, err := settingRepo.GetValueByKey("VLLMMonitorInterval")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := StartVLLMMonitor(interval); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if value == constant.StatusDisable && global.VLLMMonitorCronID != 0 {
|
||||
global.Cron.Remove(global.VLLMMonitorCronID)
|
||||
global.VLLMMonitorCronID = 0
|
||||
}
|
||||
case "VLLMMonitorInterval":
|
||||
status, err := settingRepo.GetValueByKey("VLLMMonitorStatus")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status == constant.StatusEnable {
|
||||
if err := StartVLLMMonitor(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return settingRepo.Update(key, value)
|
||||
}
|
||||
|
||||
func (m *MonitorService) CleanData() error {
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_bases").Error; err != nil {
|
||||
return err
|
||||
func (m *MonitorService) CleanData(monitorType string) error {
|
||||
switch monitorType {
|
||||
case "host":
|
||||
return monitorRepo.CleanHost()
|
||||
case "gpu":
|
||||
gpuMonitorMutex.Lock()
|
||||
defer gpuMonitorMutex.Unlock()
|
||||
return monitorRepo.CleanGPU()
|
||||
default:
|
||||
return fmt.Errorf("unsupported monitoring cleanup type: %s", monitorType)
|
||||
}
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_ios").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := global.MonitorDB.Exec("DELETE FROM monitor_networks").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
_ = global.GPUMonitorDB.Exec("DELETE FROM monitor_gpus").Error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) Run() {
|
||||
saveAcceleratorDataToDB()
|
||||
if m.ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
var itemModel model.MonitorBase
|
||||
totalPercent, _ := cpu.Percent(3*time.Second, false)
|
||||
if len(totalPercent) == 1 {
|
||||
@@ -353,30 +492,34 @@ func (m *MonitorService) Run() {
|
||||
_ = monitorRepo.DelMonitorBase(timeForDelete)
|
||||
_ = monitorRepo.DelMonitorIO(timeForDelete)
|
||||
_ = monitorRepo.DelMonitorNet(timeForDelete)
|
||||
_ = monitorRepo.DelMonitorGPU(timeForDelete)
|
||||
}
|
||||
|
||||
func (m *MonitorService) loadDiskIO() {
|
||||
ioStat, _ := disk.IOCounters()
|
||||
ioStat, _ := disk.IOCountersWithContext(m.ctx)
|
||||
var diskIOList []disk.IOCountersStat
|
||||
for _, io := range ioStat {
|
||||
diskIOList = append(diskIOList, io)
|
||||
}
|
||||
diskIOList = append(diskIOList, sumDiskIOCounters(ioStat))
|
||||
m.DiskIO <- diskIOList
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
case m.DiskIO <- diskIOList:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MonitorService) loadNetIO() {
|
||||
netStat, _ := net.IOCounters(true)
|
||||
netStatAll, _ := net.IOCounters(false)
|
||||
netStat, _ := net.IOCountersWithContext(m.ctx, true)
|
||||
netStatAll, _ := net.IOCountersWithContext(m.ctx, false)
|
||||
var netList []net.IOCountersStat
|
||||
netList = append(netList, netStat...)
|
||||
netList = append(netList, netStatAll...)
|
||||
m.NetIO <- netList
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
case m.NetIO <- netList:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) {
|
||||
defer close(m.DiskIO)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -426,14 +569,17 @@ func (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) {
|
||||
}
|
||||
}
|
||||
_ = monitorRepo.BatchCreateMonitorIO(ioList)
|
||||
m.DiskIO <- ioStat2
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case m.DiskIO <- ioStat2:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64) {
|
||||
defer close(m.NetIO)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -463,7 +609,11 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
|
||||
}
|
||||
|
||||
_ = monitorRepo.BatchCreateMonitorNet(netList)
|
||||
m.NetIO <- netStat2
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case m.NetIO <- netStat2:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -572,27 +722,38 @@ func loadTopMem() []dto.Process {
|
||||
}
|
||||
|
||||
func StartMonitor(removeBefore bool, interval string) error {
|
||||
intervalItem, err := strconv.Atoi(interval)
|
||||
if err != nil || intervalItem < 10 || intervalItem > 43200 {
|
||||
return fmt.Errorf("invalid host monitoring interval: %s", interval)
|
||||
}
|
||||
if removeBefore {
|
||||
monitorCancel()
|
||||
global.Cron.Remove(cron.EntryID(global.MonitorCronID))
|
||||
}
|
||||
intervalItem, err := strconv.Atoi(interval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service := NewIMonitorService()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
monitorCancel = cancel
|
||||
service := &MonitorService{
|
||||
DiskIO: make(chan []disk.IOCountersStat, 2),
|
||||
NetIO: make(chan []net.IOCountersStat, 2),
|
||||
ctx: ctx,
|
||||
}
|
||||
now := time.Now()
|
||||
nextMinute := now.Truncate(time.Minute).Add(time.Minute)
|
||||
time.AfterFunc(time.Until(nextMinute), func() {
|
||||
timer := time.AfterFunc(time.Until(nextMinute), func() {
|
||||
monitorSettingMutex.Lock()
|
||||
defer monitorSettingMutex.Unlock()
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %ss", interval), service)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
global.MonitorCronID = monitorID
|
||||
})
|
||||
monitorCancel = func() {
|
||||
cancel()
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
service.Run()
|
||||
|
||||
@@ -602,7 +763,97 @@ func StartMonitor(removeBefore bool, interval string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveAcceleratorDataToDB() {
|
||||
func loadGPUOptions(snapshot *accelerator.Snapshot) dto.MonitorGPUOptions {
|
||||
var data dto.MonitorGPUOptions
|
||||
hasGPUOrNPU := false
|
||||
hasXPU := false
|
||||
for _, item := range snapshot.Devices {
|
||||
if item.Kind == accelerator.KindXPU {
|
||||
hasXPU = true
|
||||
} else {
|
||||
hasGPUOrNPU = true
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case hasGPUOrNPU && hasXPU:
|
||||
data.GPUType = "mixed"
|
||||
case hasXPU:
|
||||
data.GPUType = "xpu"
|
||||
case hasGPUOrNPU:
|
||||
data.GPUType = "gpu"
|
||||
}
|
||||
|
||||
sort.Slice(snapshot.Devices, func(i, j int) bool {
|
||||
if snapshot.Devices[i].Kind != snapshot.Devices[j].Kind {
|
||||
return snapshot.Devices[i].Kind < snapshot.Devices[j].Kind
|
||||
}
|
||||
if snapshot.Devices[i].Vendor != snapshot.Devices[j].Vendor {
|
||||
return snapshot.Devices[i].Vendor < snapshot.Devices[j].Vendor
|
||||
}
|
||||
if snapshot.Devices[i].NPUIndex != snapshot.Devices[j].NPUIndex {
|
||||
return snapshot.Devices[i].NPUIndex < snapshot.Devices[j].NPUIndex
|
||||
}
|
||||
if snapshot.Devices[i].ChipIndex != snapshot.Devices[j].ChipIndex {
|
||||
return snapshot.Devices[i].ChipIndex < snapshot.Devices[j].ChipIndex
|
||||
}
|
||||
return snapshot.Devices[i].Index < snapshot.Devices[j].Index
|
||||
})
|
||||
for _, item := range snapshot.Devices {
|
||||
chartHide := dto.GPUChartHide{
|
||||
DeviceID: item.ID,
|
||||
ProductName: item.Label,
|
||||
Type: string(item.Kind),
|
||||
}
|
||||
data.ChartHide = append(data.ChartHide, chartHide)
|
||||
data.Options = append(data.Options, chartHide.ProductName)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func StartGPUMonitor(interval string) error {
|
||||
seconds, err := strconv.Atoi(interval)
|
||||
if err != nil || seconds < 10 || seconds > 43200 {
|
||||
return fmt.Errorf("invalid GPU monitoring interval: %s", interval)
|
||||
}
|
||||
service := &MonitorService{}
|
||||
job := cron.NewChain(cron.Recover(cron.DefaultLogger)).Then(cron.FuncJob(service.saveGPUData))
|
||||
id, err := global.Cron.AddFunc(fmt.Sprintf("@every %ds", seconds), func() { go job.Run() })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if global.GPUMonitorCronID != 0 {
|
||||
global.Cron.Remove(global.GPUMonitorCronID)
|
||||
}
|
||||
global.GPUMonitorCronID = id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) saveGPUData() {
|
||||
if !gpuMonitorMutex.TryLock() {
|
||||
return
|
||||
}
|
||||
defer gpuMonitorMutex.Unlock()
|
||||
status, err := settingRepo.GetValueByKey("GPUMonitorStatus")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Load GPU monitoring status failed: %v", err)
|
||||
return
|
||||
}
|
||||
if status != constant.StatusEnable {
|
||||
return
|
||||
}
|
||||
retention, err := settingRepo.GetValueByKey("GPUMonitorStoreDays")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Load GPU monitoring retention failed: %v", err)
|
||||
return
|
||||
}
|
||||
days, err := strconv.Atoi(retention)
|
||||
if err != nil || days < 1 {
|
||||
global.LOG.Errorf("Invalid GPU monitoring retention: %s", retention)
|
||||
return
|
||||
}
|
||||
if err := monitorRepo.DelMonitorGPU(time.Now().AddDate(0, 0, -days)); err != nil {
|
||||
global.LOG.Errorf("Clean GPU monitoring data failed: %v", err)
|
||||
}
|
||||
exist, client := accelerator.New()
|
||||
if !exist {
|
||||
return
|
||||
@@ -615,27 +866,64 @@ func saveAcceleratorDataToDB() {
|
||||
if warning := snapshot.Warning(); warning != nil {
|
||||
global.LOG.Warnf("load accelerator monitor data partially failed, err: %v", warning)
|
||||
}
|
||||
intervalSeconds := 0
|
||||
if setting, err := settingRepo.Get(settingRepo.WithByKey("GPUMonitorInterval")); err == nil {
|
||||
intervalSeconds, _ = strconv.Atoi(setting.Value)
|
||||
}
|
||||
list := make([]model.MonitorGPU, 0, len(snapshot.Devices))
|
||||
for _, device := range snapshot.Devices {
|
||||
list = append(list, newMonitorGPU(device))
|
||||
item := newMonitorGPU(device)
|
||||
item.CreatedAt = snapshot.Info.CollectedAt
|
||||
item.IntervalSeconds = intervalSeconds
|
||||
list = append(list, item)
|
||||
}
|
||||
if err := repo.NewIMonitorRepo().BatchCreateMonitorGPU(list); err != nil {
|
||||
if err := monitorRepo.BatchCreateMonitorGPU(list); err != nil {
|
||||
global.LOG.Errorf("batch create accelerator monitor data failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func newMonitorGPU(device accelerator.Device) model.MonitorGPU {
|
||||
item := model.MonitorGPU{
|
||||
ProductName: device.Label,
|
||||
GPUUtil: device.Metrics.Utilization.ValueOrZero(),
|
||||
Temperature: device.Metrics.Temperature.ValueOrZero(),
|
||||
PowerDraw: device.Metrics.Power.ValueOrZero(),
|
||||
MaxPowerLimit: device.Metrics.PowerLimit.ValueOrZero(),
|
||||
MemUsed: device.Metrics.MemoryUsed.ValueOrZero(),
|
||||
MemTotal: device.Metrics.MemoryTotal.ValueOrZero(),
|
||||
FanSpeed: int(device.Metrics.FanSpeed.ValueOrZero()),
|
||||
MemoryUtil: device.Metrics.MemoryUtil.Value,
|
||||
MemoryActivity: device.Metrics.MemoryActivity.Value,
|
||||
EncoderUtil: device.Metrics.EncoderUtil.Value,
|
||||
DecoderUtil: device.Metrics.DecoderUtil.Value,
|
||||
JPEGUtil: device.Metrics.JPEGUtil.Value,
|
||||
OFAUtil: device.Metrics.OFAUtil.Value,
|
||||
MediaUtil: device.Metrics.MediaUtil.Value,
|
||||
ComputeUtil: device.Metrics.ComputeUtil.Value,
|
||||
CopyUtil: device.Metrics.CopyUtil.Value,
|
||||
HotspotTemperature: device.Metrics.HotspotTemperature.Value,
|
||||
FanRPM: device.Metrics.FanRPM.Value,
|
||||
AICPUUtil: device.Metrics.AICPUUtil.Value,
|
||||
CtrlCPUUtil: device.Metrics.CtrlCPUUtil.Value,
|
||||
DDRUsed: device.Metrics.DDRUsed.Value,
|
||||
DDRTotal: device.Metrics.DDRTotal.Value,
|
||||
HBMUsed: device.Metrics.HBMUsed.Value,
|
||||
HBMTotal: device.Metrics.HBMTotal.Value,
|
||||
DDRBandwidth: device.Metrics.DDRBandwidth.Value,
|
||||
HBMBandwidth: device.Metrics.HBMBandwidth.Value,
|
||||
MemoryBandwidth: device.Metrics.MemoryBandwidth.Value,
|
||||
MediaFrequency: device.Metrics.MediaFrequency.Value,
|
||||
HugepagesUsed: device.Metrics.HugepagesUsed.Value,
|
||||
HugepagesTotal: device.Metrics.HugepagesTotal.Value,
|
||||
|
||||
ProductName: device.Label,
|
||||
DeviceID: device.ID,
|
||||
DeviceType: string(device.Kind),
|
||||
ProcessStatus: device.ProcessStatus,
|
||||
Frequency: device.Metrics.Frequency.Value,
|
||||
MemoryFrequency: device.Metrics.MemoryFrequency.Value,
|
||||
MemoryTemperature: device.Metrics.MemoryTemperature.Value,
|
||||
GPUUtil: device.Metrics.Utilization.Value,
|
||||
Temperature: device.Metrics.Temperature.Value,
|
||||
PowerDraw: device.Metrics.Power.Value,
|
||||
MaxPowerLimit: device.Metrics.PowerLimit.Value,
|
||||
MemUsed: device.Metrics.MemoryUsed.Value,
|
||||
MemTotal: device.Metrics.MemoryTotal.Value,
|
||||
FanSpeed: device.Metrics.FanSpeed.Value,
|
||||
}
|
||||
if len(device.Processes) == 0 {
|
||||
if device.ProcessStatus != "ok" {
|
||||
return item
|
||||
}
|
||||
processes := make([]dto.GPUProcess, 0, len(device.Processes))
|
||||
@@ -695,3 +983,14 @@ func loadHostSysPath() string {
|
||||
}
|
||||
return hostSys
|
||||
}
|
||||
|
||||
func sortedMonitorOptions(optionSet map[string]struct{}) []string {
|
||||
options := make([]string, 0, len(optionSet))
|
||||
for name := range optionSet {
|
||||
if len(name) != 0 && name != "all" {
|
||||
options = append(options, name)
|
||||
}
|
||||
}
|
||||
sort.Strings(options)
|
||||
return append([]string{"all"}, options...)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/vllm"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/robfig/cron/v3"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var vllmMonitorMutex sync.Mutex
|
||||
var vllmMetricsClient = &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{DialContext: (&net.Dialer{Timeout: 3 * time.Second}).DialContext, IdleConnTimeout: 30 * time.Second},
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadVLLMMonitorData(req dto.MonitorVLLMSearch) (dto.MonitorVLLMData, error) {
|
||||
data := dto.MonitorVLLMData{Points: []model.MonitorVLLM{}}
|
||||
if !req.EndTime.After(req.StartTime) {
|
||||
return data, fmt.Errorf("invalid vLLM history request")
|
||||
}
|
||||
install, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
if install.App.Key != "vllm" {
|
||||
return data, fmt.Errorf("not a vLLM installation")
|
||||
}
|
||||
|
||||
loc, err := time.LoadLocation(common.LoadTimeZoneByCmd())
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
req.StartTime, req.EndTime = req.StartTime.In(loc), req.EndTime.In(loc)
|
||||
data.SampleCount, err = vllmMonitorRepo.Count(req.AppInstallID, req.StartTime, req.EndTime)
|
||||
if err != nil || data.SampleCount == 0 {
|
||||
return data, err
|
||||
}
|
||||
if data.SampleCount > 1200 {
|
||||
data.BucketSeconds = (req.EndTime.Unix() - req.StartTime.Unix() + 600) / 600
|
||||
}
|
||||
points, err := vllmMonitorRepo.History(req.AppInstallID, req.StartTime, req.EndTime, data.BucketSeconds, req.Aggregation)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
if data.BucketSeconds > 0 {
|
||||
if req.Aggregation != "max" {
|
||||
for i := range points {
|
||||
if err := vllm.AggregateHistograms(points[i].HistogramSamples, &points[i].MonitorVLLM); err != nil {
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
}
|
||||
next := 0
|
||||
for bucket := int64(0); bucket <= (req.EndTime.Unix()-req.StartTime.Unix())/data.BucketSeconds; bucket++ {
|
||||
var point model.MonitorVLLM
|
||||
if next < len(points) && points[next].Bucket == bucket {
|
||||
point = points[next].MonitorVLLM
|
||||
next++
|
||||
}
|
||||
point.AppInstallID = req.AppInstallID
|
||||
point.CreatedAt = time.Unix(req.StartTime.Unix()+bucket*data.BucketSeconds, 0).In(loc)
|
||||
if bucket == 0 {
|
||||
point.CreatedAt = req.StartTime
|
||||
}
|
||||
data.Points = append(data.Points, point)
|
||||
}
|
||||
} else {
|
||||
setting, err := settingRepo.GetValueByKey("VLLMMonitorInterval")
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
intervalSeconds, err := strconv.Atoi(setting)
|
||||
if err != nil || intervalSeconds <= 0 {
|
||||
return data, fmt.Errorf("invalid vLLM monitoring interval: %s", setting)
|
||||
}
|
||||
interval := time.Duration(intervalSeconds) * time.Second
|
||||
for i, point := range points {
|
||||
if i > 0 && point.CreatedAt.Sub(points[i-1].CreatedAt) > 2*interval {
|
||||
data.Points = append(data.Points, model.MonitorVLLM{CreatedAt: points[i-1].CreatedAt.Add(interval), AppInstallID: req.AppInstallID})
|
||||
}
|
||||
data.Points = append(data.Points, point.MonitorVLLM)
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (model.MonitorVLLM, error) {
|
||||
install, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))
|
||||
if err != nil {
|
||||
return model.MonitorVLLM{}, err
|
||||
}
|
||||
if install.App.Key != "vllm" {
|
||||
return model.MonitorVLLM{}, fmt.Errorf("not a vLLM installation")
|
||||
}
|
||||
previous, err := collectVLLMMetrics(ctx, install, model.MonitorVLLM{})
|
||||
if err != nil {
|
||||
global.LOG.Debugf("Collect vLLM metrics on port %d failed: %v", install.HttpPort, err)
|
||||
return previous, nil
|
||||
}
|
||||
timer := time.NewTimer(time.Second)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return model.MonitorVLLM{}, ctx.Err()
|
||||
case <-timer.C:
|
||||
}
|
||||
point, err := collectVLLMMetrics(ctx, install, previous)
|
||||
if err != nil {
|
||||
global.LOG.Debugf("Collect vLLM metrics on port %d failed: %v", install.HttpPort, err)
|
||||
}
|
||||
return point, nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) CleanVLLMMonitor(req dto.MonitorVLLMClean) error {
|
||||
install, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if install.App.Key != "vllm" {
|
||||
return fmt.Errorf("not a vLLM installation")
|
||||
}
|
||||
vllmMonitorMutex.Lock()
|
||||
defer vllmMonitorMutex.Unlock()
|
||||
return vllmMonitorRepo.CleanTarget(req.AppInstallID)
|
||||
}
|
||||
|
||||
func StartVLLMMonitor(interval string) error {
|
||||
seconds, err := strconv.Atoi(interval)
|
||||
if err != nil || seconds < 10 || seconds > 43200 {
|
||||
return fmt.Errorf("invalid vLLM monitoring interval: %s", interval)
|
||||
}
|
||||
service := &MonitorService{}
|
||||
job := cron.NewChain(cron.Recover(cron.DefaultLogger)).Then(cron.FuncJob(service.saveVLLMData))
|
||||
id, err := global.Cron.AddFunc(fmt.Sprintf("@every %ds", seconds), func() { go job.Run() })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if global.VLLMMonitorCronID != 0 {
|
||||
global.Cron.Remove(global.VLLMMonitorCronID)
|
||||
}
|
||||
global.VLLMMonitorCronID = id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MonitorService) saveVLLMData() {
|
||||
if !vllmMonitorMutex.TryLock() {
|
||||
return
|
||||
}
|
||||
defer vllmMonitorMutex.Unlock()
|
||||
status, err := settingRepo.GetValueByKey("VLLMMonitorStatus")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Load vLLM monitoring status failed: %v", err)
|
||||
return
|
||||
}
|
||||
if status != constant.StatusEnable {
|
||||
return
|
||||
}
|
||||
retention, err := settingRepo.GetValueByKey("VLLMMonitorStoreDays")
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Load vLLM monitoring retention failed: %v", err)
|
||||
return
|
||||
}
|
||||
days, err := strconv.Atoi(retention)
|
||||
if err != nil || days < 1 {
|
||||
global.LOG.Errorf("Invalid vLLM monitoring retention: %s", retention)
|
||||
return
|
||||
}
|
||||
if err := vllmMonitorRepo.DeleteBefore(time.Now().AddDate(0, 0, -days)); err != nil {
|
||||
global.LOG.Errorf("Clean vLLM monitoring data failed: %v", err)
|
||||
}
|
||||
apps, err := appRepo.GetBy(appRepo.WithKey("vllm"))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Load vLLM apps for monitoring failed: %v", err)
|
||||
return
|
||||
}
|
||||
if len(apps) == 0 {
|
||||
return
|
||||
}
|
||||
ids := make([]uint, 0, len(apps))
|
||||
for _, app := range apps {
|
||||
ids = append(ids, app.ID)
|
||||
}
|
||||
installs, err := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppIdsIn(ids))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Load vLLM instances for monitoring failed: %v", err)
|
||||
return
|
||||
}
|
||||
var group errgroup.Group
|
||||
group.SetLimit(4)
|
||||
for _, install := range installs {
|
||||
group.Go(func() error {
|
||||
previous, err := vllmMonitorRepo.Latest(install.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
point, err := collectVLLMMetrics(context.Background(), install, previous)
|
||||
if err != nil {
|
||||
global.LOG.Debugf("Collect vLLM metrics for %s failed: %v", install.Name, err)
|
||||
}
|
||||
return vllmMonitorRepo.Create(&point)
|
||||
})
|
||||
}
|
||||
if err := group.Wait(); err != nil {
|
||||
global.LOG.Errorf("Save vLLM monitoring data failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func collectVLLMMetrics(ctx context.Context, install model.AppInstall, previous model.MonitorVLLM) (model.MonitorVLLM, error) {
|
||||
point := model.MonitorVLLM{AppInstallID: install.ID, CreatedAt: time.Now(), Status: "unavailable"}
|
||||
if install.HttpPort <= 0 || install.HttpPort > 65535 {
|
||||
return point, fmt.Errorf("invalid vLLM port")
|
||||
}
|
||||
var env map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(install.Env), &env); err != nil {
|
||||
return point, err
|
||||
}
|
||||
host, _ := env[constant.HostIP].(string)
|
||||
host = strings.Trim(host, "[]")
|
||||
switch host {
|
||||
case "", "0.0.0.0":
|
||||
host = "127.0.0.1"
|
||||
case "::":
|
||||
host = "::1"
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://"+net.JoinHostPort(host, strconv.Itoa(install.HttpPort))+"/metrics", nil)
|
||||
if err != nil {
|
||||
return point, err
|
||||
}
|
||||
response, err := vllmMetricsClient.Do(req)
|
||||
if err != nil {
|
||||
return point, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return point, fmt.Errorf("vLLM metrics returned HTTP %d", response.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(response.Body, 8*1024*1024+1))
|
||||
if err != nil {
|
||||
return point, err
|
||||
}
|
||||
if len(body) > 8*1024*1024 {
|
||||
return point, fmt.Errorf("vLLM metrics response too large")
|
||||
}
|
||||
metrics, err := vllm.Parse(bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return point, err
|
||||
}
|
||||
point.CreatedAt = time.Now()
|
||||
raw, err := json.Marshal(metrics)
|
||||
if err != nil {
|
||||
return point, err
|
||||
}
|
||||
var before vllm.Metrics
|
||||
elapsed := point.CreatedAt.Sub(previous.CreatedAt).Seconds()
|
||||
if previous.Status == "ok" && elapsed > 0 {
|
||||
if err := json.Unmarshal([]byte(previous.RawMetrics), &before); err != nil {
|
||||
return point, err
|
||||
}
|
||||
}
|
||||
calculated, err := vllm.Calculate(metrics, before, elapsed)
|
||||
if err != nil {
|
||||
return point, err
|
||||
}
|
||||
calculated.AppInstallID = install.ID
|
||||
calculated.CreatedAt = point.CreatedAt
|
||||
calculated.RawMetrics = string(raw)
|
||||
calculated.Status = "ok"
|
||||
return calculated, nil
|
||||
}
|
||||
@@ -2486,6 +2486,17 @@
|
||||
"formatZH": "删除虚拟机 [name]",
|
||||
"formatEN": "delete VM [name]"
|
||||
},
|
||||
"/core/xpack/vms/disks/resize": {
|
||||
"bodyKeys": [
|
||||
"name",
|
||||
"target",
|
||||
"sizeGiB"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "扩容虚拟机 [name] 磁盘 [target] 至 [sizeGiB] GiB",
|
||||
"formatEN": "expand VM [name] disk [target] to [sizeGiB] GiB"
|
||||
},
|
||||
"/core/xpack/vms/environment/enable": {
|
||||
"bodyKeys": [],
|
||||
"paramKeys": [],
|
||||
|
||||
@@ -38,6 +38,28 @@ func Run() {
|
||||
}
|
||||
}
|
||||
|
||||
var gpuStatus, gpuInterval model.Setting
|
||||
if err := global.DB.Where("key = ?", "GPUMonitorStatus").First(&gpuStatus).Error; err != nil {
|
||||
global.LOG.Errorf("Load GPU monitoring status failed: %v", err)
|
||||
} else if gpuStatus.Value == constant.StatusEnable {
|
||||
if err := global.DB.Where("key = ?", "GPUMonitorInterval").First(&gpuInterval).Error; err != nil {
|
||||
global.LOG.Errorf("Load GPU monitoring interval failed: %v", err)
|
||||
} else if err := service.StartGPUMonitor(gpuInterval.Value); err != nil {
|
||||
global.LOG.Errorf("Start GPU monitoring failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var vllmStatus, vllmInterval model.Setting
|
||||
if err := global.DB.Where("key = ?", "VLLMMonitorStatus").First(&vllmStatus).Error; err != nil {
|
||||
global.LOG.Errorf("Load vLLM monitoring status failed: %v", err)
|
||||
} else if vllmStatus.Value == constant.StatusEnable {
|
||||
if err := global.DB.Where("key = ?", "VLLMMonitorInterval").First(&vllmInterval).Error; err != nil {
|
||||
global.LOG.Errorf("Load vLLM monitoring interval failed: %v", err)
|
||||
} else if err := service.StartVLLMMonitor(vllmInterval.Value); err != nil {
|
||||
global.LOG.Errorf("Start vLLM monitoring failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := global.Cron.AddJob("@daily", job.NewWebsiteJob()); err != nil {
|
||||
global.LOG.Errorf("can not add website corn job: %s", err.Error())
|
||||
}
|
||||
|
||||
+11
-8
@@ -14,12 +14,13 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
DB *gorm.DB
|
||||
MonitorDB *gorm.DB
|
||||
GPUMonitorDB *gorm.DB
|
||||
TaskDB *gorm.DB
|
||||
CoreDB *gorm.DB
|
||||
AlertDB *gorm.DB
|
||||
DB *gorm.DB
|
||||
MonitorDB *gorm.DB
|
||||
GPUMonitorDB *gorm.DB
|
||||
VLLMMonitorDB *gorm.DB
|
||||
TaskDB *gorm.DB
|
||||
CoreDB *gorm.DB
|
||||
AlertDB *gorm.DB
|
||||
|
||||
LOG *logrus.Logger
|
||||
CONF ServerConfig
|
||||
@@ -29,8 +30,10 @@ var (
|
||||
|
||||
Dir SystemDir
|
||||
|
||||
Cron *cron.Cron
|
||||
MonitorCronID cron.EntryID
|
||||
Cron *cron.Cron
|
||||
MonitorCronID cron.EntryID
|
||||
VLLMMonitorCronID cron.EntryID
|
||||
GPUMonitorCronID cron.EntryID
|
||||
|
||||
IsMaster bool
|
||||
|
||||
|
||||
+17
-20
@@ -1,14 +1,12 @@
|
||||
module github.com/1Panel-dev/1Panel/agent
|
||||
|
||||
go 1.26.1
|
||||
|
||||
replace github.com/moby/go-archive => github.com/moby/go-archive v0.1.0
|
||||
go 1.26.6
|
||||
|
||||
replace github.com/go-acme/lego/v5 => github.com/1Panel-dev/lego/v5 v5.3.1
|
||||
|
||||
require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/compose-spec/compose-go/v2 v2.14.0
|
||||
github.com/compose-spec/compose-go/v2 v2.15.0
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/docker/cli v29.7.2+incompatible
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
@@ -33,6 +31,7 @@ require (
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8
|
||||
github.com/miekg/dns v1.1.73
|
||||
github.com/minio/minio-go/v7 v7.3.0
|
||||
github.com/moby/go-archive v0.3.0
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
@@ -52,13 +51,13 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.4.0
|
||||
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19
|
||||
github.com/upyun/go-sdk v2.1.0+incompatible
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2
|
||||
golang.org/x/crypto v0.55.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.9.0
|
||||
golang.org/x/crypto v0.56.0
|
||||
golang.org/x/net v0.58.0
|
||||
golang.org/x/sync v0.22.0
|
||||
golang.org/x/sync v0.23.0
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/text v0.41.0
|
||||
golang.org/x/time v0.15.0
|
||||
golang.org/x/time v0.16.0
|
||||
google.golang.org/genproto v0.0.0-20260414002931-afd174a4e478
|
||||
gopkg.in/ini.v1 v1.67.3
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -121,7 +120,7 @@ require (
|
||||
github.com/go-acme/alidns-20150109/v5 v5.5.0 // indirect
|
||||
github.com/go-acme/esa-20240910/v3 v3.4.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
@@ -132,7 +131,6 @@ require (
|
||||
github.com/gofrs/flock v0.13.0 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/go-querystring v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.205 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
@@ -155,11 +153,10 @@ require (
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.2.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.1 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/sequential v0.7.0 // indirect
|
||||
github.com/moby/sys/user v0.4.1 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -209,18 +206,18 @@ require (
|
||||
go.mongodb.org/mongo-driver v1.17.9 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.45.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/arch v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
google.golang.org/grpc v1.83.1 // indirect
|
||||
google.golang.org/grpc v1.83.2 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
modernc.org/fileutil v1.4.0 // indirect
|
||||
modernc.org/libc v1.72.0 // indirect
|
||||
|
||||
+41
-40
@@ -205,8 +205,8 @@ github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gE
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/compose-spec/compose-go/v2 v2.14.0 h1:uaJeo5B3+OVlu+Rx2qLBcAdXPEUUzm5nQrRiGJafRAQ=
|
||||
github.com/compose-spec/compose-go/v2 v2.14.0/go.mod h1:ZU6zlcweCZKyiB7BVfCizQT9XmkEIMFE+PRZydVcsZg=
|
||||
github.com/compose-spec/compose-go/v2 v2.15.0 h1:tdQw+eMyT+P6ZIb09JfcIVvbMmIa+PjST7cWezVLf00=
|
||||
github.com/compose-spec/compose-go/v2 v2.15.0/go.mod h1:Q1+qtN4vhzEjGrnqRtzx1xa8raDZQlMUe3WJxndYNiQ=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
@@ -307,8 +307,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
@@ -438,6 +438,7 @@ github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
|
||||
@@ -597,16 +598,16 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/go-archive v0.3.0 h1:nos4BtzzUIqB406BgQnWGMI4qib9BZ8XUHU+ucv/n1c=
|
||||
github.com/moby/go-archive v0.3.0/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE=
|
||||
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
|
||||
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/sequential v0.7.0 h1:ASQNGNROJSuOO6LL6bPHbKvuZu6NU8P4ldPWk31zj/8=
|
||||
github.com/moby/sys/sequential v0.7.0/go.mod h1:NfSTAp6V3fw4tmkD62PEcOKeZKquXT8VKCkf7aVR79o=
|
||||
github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0=
|
||||
github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
@@ -877,8 +878,8 @@ go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lL
|
||||
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
|
||||
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
|
||||
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2 h1:b6o2m7zL8g2URuO8urBedAylxojybKXNZTxgkOcl+2w=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.mongodb.org/mongo-driver/v2 v2.9.0 h1:e2mQdOmbkiYz+dj3faM7lVDwl7WdnRD+g5VicafMhL0=
|
||||
go.mongodb.org/mongo-driver/v2 v2.9.0/go.mod h1:SHKN0IWkKmEVGHLjXnni6s4wPKX4v86FTgOeJJFuXcA=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -889,23 +890,23 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
|
||||
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 h1:QRefszxJmfPdjXUUm3j6iDzY03mTPXMjqErFqQ67vUg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0/go.mod h1:Tiz03lTBVBrm7eWZBOidzEaYaJa8tjwGUGv6d8mlTyk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 h1:QBajQ2SrwQijzHyZbQlPsuIzpl/ll8DY6wPWsajeGcI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0/go.mod h1:08ZQLjrPLQ6R4kAXvuOvODEer5Yh4CoFvll5qB2BCI8=
|
||||
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
|
||||
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
|
||||
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
|
||||
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk=
|
||||
go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
@@ -951,8 +952,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
|
||||
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1076,8 +1077,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1193,8 +1194,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
||||
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -1315,10 +1316,10 @@ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxH
|
||||
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20260414002931-afd174a4e478 h1:aLsVTW0lZ8+IY5u/ERjZSCvAmhuR7slKzyha3YikDNA=
|
||||
google.golang.org/genproto v0.0.0-20260414002931-afd174a4e478/go.mod h1:YJAzKjfHIUHb9T+bfu8L7mthAp7VVXQBUs1PLdBWS7M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7 h1:eM/YSd5bBFagF51o1E745Ta7RwzpW0h+z+QDNZOgmQ8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1336,8 +1337,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y=
|
||||
google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
|
||||
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
|
||||
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: "Panel {{ .node }}{{ .ip }}: {{ .msg }}. Log in to view details."
|
||||
NodeExceptionAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} nodes are abnormal. Log in to view details."
|
||||
LicenseExceptionAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} licenses are abnormal. Log in to view details."
|
||||
SSHAndPanelLoginAlert: "Panel {{ .node }}{{ .ip }}: abnormal {{ .name }} login from {{ .loginIp }}. Log in to view details."
|
||||
CronJobSuccessAlert: "Panel {{ .node }}{{ .ip }}: scheduled task {{ .name }} completed successfully. Log in to view details."
|
||||
|
||||
# disk
|
||||
DeviceNotFound: "Device {{ .name }} not found"
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .msg }}. Inicie sesión en el pa
|
||||
NodeExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} nodos son anómalos. Inicie sesión en el panel para ver los detalles.'
|
||||
LicenseExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} licencias son anómalas. Inicie sesión en el panel para ver los detalles.'
|
||||
SSHAndPanelLoginAlert: 'Su Panel {{ .node }}{{ .ip }}, el inicio de sesión {{ .name }} desde {{ .loginIp }} es anómalo. Inicie sesión en el panel para ver los detalles.'
|
||||
CronJobSuccessAlert: 'Panel {{ .node }}{{ .ip }}: la tarea programada {{ .name }} se completó correctamente. Inicie sesión para ver los detalles.'
|
||||
|
||||
# disco
|
||||
DeviceNotFound: 'Dispositivo {{ .name }} no encontrado'
|
||||
|
||||
@@ -649,6 +649,8 @@ CommonAlert: "پنل {{ .node }}{{ .ip }}: {{ .msg }}. برای مشاهده ج
|
||||
NodeExceptionAlert: "پنل {{ .node }}{{ .ip }}: {{ .num }} گره غیرعادی هستند. برای مشاهده جزئیات وارد شوید."
|
||||
LicenseExceptionAlert: "پنل {{ .node }}{{ .ip }}: {{ .num }} مجوز غیرعادی است. برای مشاهده جزئیات وارد شوید."
|
||||
SSHAndPanelLoginAlert: "پنل {{ .node }}{{ .ip }}: ورود غیرعادی {{ .name }} از {{ .loginIp }}. برای مشاهده جزئیات وارد شوید."
|
||||
CronJobSuccessAlert: "پنل {{ .node }}{{ .ip }}: وظیفه زمانبندیشده {{ .name }} با موفقیت تکمیل شد. برای مشاهده جزئیات وارد شوید."
|
||||
|
||||
|
||||
# دیسک
|
||||
DeviceNotFound: "دستگاه {{ .name }} یافت نشد"
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .msg }}。詳細
|
||||
NodeExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のノードに異常が発生しています。詳細はパネルにログインして'
|
||||
LicenseExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のライセンスに異常が発生しています。詳細はパネルにログインして'
|
||||
SSHAndPanelLoginAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .loginIp }} からの {{ .name }} ログインに異常があります。詳細はパネルにログインして'
|
||||
CronJobSuccessAlert: 'パネル {{ .node }}{{ .ip }}: スケジュールタスク {{ .name }} が正常に完了しました。詳細はパネルにログインして確認してください。'
|
||||
|
||||
# ディスク
|
||||
DeviceNotFound: 'デバイス {{ .name }} が見つかりません'
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .msg }}。자세한 내
|
||||
NodeExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 노드에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
LicenseExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 라이센스에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
SSHAndPanelLoginAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .loginIp }}에서의 {{ .name }} 로그인에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
CronJobSuccessAlert: '패널 {{ .node }}{{ .ip }}: 예약 작업 {{ .name }}이(가) 성공적으로 완료되었습니다. 로그인하여 자세한 내용을 확인하십시오.'
|
||||
|
||||
# 디스크
|
||||
DeviceNotFound: '장치 {{ .name }} 을(를) 찾을 수 없습니다'
|
||||
|
||||
@@ -639,6 +639,7 @@ CommonAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: {{ .msg }}. ເ
|
||||
NodeExceptionAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ໂນດ {{ .num }} ແຫ່ງຜິດປົກກະຕິ. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
|
||||
LicenseExceptionAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ໃບອະນຸຍາດ {{ .num }} ສະບັບຜິດປົກກະຕິ. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
|
||||
SSHAndPanelLoginAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ມີການເຂົ້າສູ່ລະບົບ {{ .name }} ທີ່ຜິດປົກກະຕິຈາກ {{ .loginIp }}. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
|
||||
CronJobSuccessAlert: "ແຜງຄວບຄຸມ {{ .node }}{{ .ip }}: ວຽກທີ່ຕັ້ງເວລາ {{ .name }} ສຳເລັດແລ້ວ. ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງລາຍລະອຽດ."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "ບໍ່ພົບອຸປະກອນ {{ .name }}"
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .msg }}. Sila log masuk ke pan
|
||||
NodeExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} nod tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
LicenseExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} lesen tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
SSHAndPanelLoginAlert: 'Panel {{ .node }}{{ .ip }} Anda, log masuk {{ .name }} dari {{ .loginIp }} tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
CronJobSuccessAlert: 'Panel {{ .node }}{{ .ip }}: tugas berjadual {{ .name }} berjaya diselesaikan. Log masuk untuk melihat butiran.'
|
||||
|
||||
# cakera
|
||||
DeviceNotFound: 'Peranti {{ .name }} tidak ditemui'
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .msg }}. Faça login no painel
|
||||
NodeExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} nós estão anormais. Faça login no painel para obter detalhes.'
|
||||
LicenseExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} licenças estão anormais. Faça login no painel para obter detalhes.'
|
||||
SSHAndPanelLoginAlert: 'Seu Painel {{ .node }}{{ .ip }}, o login {{ .name }} a partir de {{ .loginIp }} é anormal. Faça login no painel para obter detalhes.'
|
||||
CronJobSuccessAlert: 'Painel {{ .node }}{{ .ip }}: a tarefa agendada {{ .name }} foi concluída com sucesso. Faça login para ver os detalhes.'
|
||||
|
||||
# disco
|
||||
DeviceNotFound: 'Dispositivo {{ .name }} não encontrado'
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .msg }}. Войдит
|
||||
NodeExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} узлов работают с ошибками. Войдите в панель для получения деталей.'
|
||||
LicenseExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} лицензий имеют ошибки. Войдите в панель для получения деталей.'
|
||||
SSHAndPanelLoginAlert: 'Ваш панель {{ .node }}{{ .ip }}, вход {{ .name }} с адреса {{ .loginIp }} является аномальным. Войдите в панель для получения деталей.'
|
||||
CronJobSuccessAlert: 'Панель {{ .node }}{{ .ip }}: запланированная задача {{ .name }} успешно завершена. Войдите для просмотра подробностей.'
|
||||
|
||||
# диск
|
||||
DeviceNotFound: 'Устройство {{ .name }} не найдено'
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .msg }}. Detaylar için panelin
|
||||
NodeExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} düğüm anormal durumda. Detaylar için paneline giriş yapın.'
|
||||
LicenseExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} lisans anormal durumda. Detaylar için paneline giriş yapın.'
|
||||
SSHAndPanelLoginAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .loginIp }} adresinden {{ .name }} girişi anormal. Detaylar için paneline giriş yapın.'
|
||||
CronJobSuccessAlert: 'Panel {{ .node }}{{ .ip }}: zamanlanmış görev {{ .name }} başarıyla tamamlandı. Ayrıntıları görmek için giriş yapın.'
|
||||
|
||||
# disk
|
||||
DeviceNotFound: 'Cihaz {{ .name }} bulunamadı'
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},詳情請登入
|
||||
NodeExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個節點出現異常,詳情請登入面板檢視。'
|
||||
LicenseExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個授權出現異常,詳情請登入面板檢視。'
|
||||
SSHAndPanelLoginAlert: '您的 {{ .node }}{{ .ip }} 面板,來自 {{ .loginIp }} 的 {{ .name }} 登入出現異常,詳情請登入面板檢視。'
|
||||
CronJobSuccessAlert: '您的 {{ .node }}{{ .ip }} 面板,排程任務 {{ .name }} 執行成功,詳情請登入面板檢視。'
|
||||
|
||||
# 磁碟
|
||||
DeviceNotFound: '裝置 {{ .name }} 未找到'
|
||||
|
||||
@@ -649,6 +649,7 @@ CommonAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},请登录面板
|
||||
NodeExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个节点存在异常,请登录面板查看详情。"
|
||||
LicenseExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个许可证存在异常,请登录面板查看详情。"
|
||||
SSHAndPanelLoginAlert: "您的 {{ .node }}{{ .ip }} 面板,面板 {{ .name }} 登录 {{ .loginIp }} 异常,请登录面板查看详情。"
|
||||
CronJobSuccessAlert: "您的 {{ .node }}{{ .ip }} 面板,计划任务-{{ .name }}执行成功,请登录面板查看详情。"
|
||||
|
||||
# 磁盘
|
||||
DeviceNotFound: "设备 {{ .name }} 未找到"
|
||||
|
||||
@@ -13,6 +13,7 @@ func Init() {
|
||||
global.TaskDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "task.db"), "task")
|
||||
global.MonitorDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "monitor.db"), "monitor")
|
||||
global.GPUMonitorDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "gpu_monitor.db"), "gpu_monitor")
|
||||
global.VLLMMonitorDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "vllm_monitor.db"), "vllm_monitor")
|
||||
global.AlertDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, "alert.db"), "alert")
|
||||
|
||||
if _, err := os.Stat(path.Join(global.Dir.DbDir, "core.db")); err == nil {
|
||||
|
||||
+33
-1
@@ -4,6 +4,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
@@ -174,6 +176,36 @@ func initAlertTask() {
|
||||
}
|
||||
|
||||
func initMonitorDB() {
|
||||
_ = global.MonitorDB.AutoMigrate(&model.MonitorBase{}, &model.MonitorNetwork{}, &model.MonitorGPU{}, &model.MonitorIO{})
|
||||
_ = global.MonitorDB.AutoMigrate(&model.MonitorBase{}, &model.MonitorNetwork{}, &model.MonitorIO{})
|
||||
_ = global.GPUMonitorDB.AutoMigrate(&model.MonitorGPU{})
|
||||
_ = global.TaskDB.AutoMigrate(&model.Task{})
|
||||
// building indexes on large monitor tables can take seconds, keep it off the startup path;
|
||||
// WAL mode leaves readers unblocked and busy_timeout covers the collector's inserts meanwhile
|
||||
go ensureMonitorIndexes()
|
||||
}
|
||||
|
||||
func ensureMonitorIndexes() {
|
||||
indexes := []struct {
|
||||
db *gorm.DB
|
||||
stmt string
|
||||
}{
|
||||
// created_at alone serves unfiltered range queries and retention cleanup
|
||||
{global.MonitorDB, "CREATE INDEX IF NOT EXISTS idx_monitor_bases_created ON monitor_bases(created_at)"},
|
||||
{global.MonitorDB, "CREATE INDEX IF NOT EXISTS idx_monitor_ios_created ON monitor_ios(created_at)"},
|
||||
{global.MonitorDB, "CREATE INDEX IF NOT EXISTS idx_monitor_networks_created ON monitor_networks(created_at)"},
|
||||
{global.GPUMonitorDB, "CREATE INDEX IF NOT EXISTS idx_monitor_gpus_created ON monitor_gpus(created_at)"},
|
||||
// (name, created_at) serves per-device range queries and distinct name lookups
|
||||
{global.MonitorDB, "CREATE INDEX IF NOT EXISTS idx_monitor_ios_name_created ON monitor_ios(name, created_at)"},
|
||||
{global.MonitorDB, "CREATE INDEX IF NOT EXISTS idx_monitor_networks_name_created ON monitor_networks(name, created_at)"},
|
||||
{global.GPUMonitorDB, "CREATE INDEX IF NOT EXISTS idx_monitor_gpus_product_created ON monitor_gpus(product_name, created_at)"},
|
||||
}
|
||||
start := time.Now()
|
||||
for _, index := range indexes {
|
||||
if err := index.db.Exec(index.stmt).Error; err != nil {
|
||||
global.LOG.Warnf("create monitor index failed, stmt: %s, err: %v", index.stmt, err)
|
||||
}
|
||||
}
|
||||
if elapsed := time.Since(start); elapsed > time.Second {
|
||||
global.LOG.Infof("monitor indexes ready, took %s", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,9 @@ func agentDBMigrations() []*gormigrate.Migration {
|
||||
migrations.SimplifyFirewallRulePolicy,
|
||||
migrations.AddDockerPortGuardReadOnly,
|
||||
migrations.MigrateFirewallPortWhitelistSources,
|
||||
migrations.AddAcceleratorMetrics,
|
||||
migrations.AddVLLMMonitor,
|
||||
migrations.AddMonitorSettings,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1908,3 +1908,35 @@ var AddDockerPortGuardReadOnly = &gormigrate.Migration{
|
||||
return tx.Migrator().CreateIndex(&model.DockerPortGuardPolicy{}, "idx_docker_port_guard_endpoint")
|
||||
},
|
||||
}
|
||||
|
||||
var AddAcceleratorMetrics = &gormigrate.Migration{
|
||||
ID: "20260928-accelerator-vendor-metrics",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return global.GPUMonitorDB.AutoMigrate(&model.MonitorGPU{})
|
||||
},
|
||||
}
|
||||
|
||||
var AddVLLMMonitor = &gormigrate.Migration{
|
||||
ID: "20260928-vllm-monitor",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return global.VLLMMonitorDB.AutoMigrate(&model.MonitorVLLM{})
|
||||
},
|
||||
}
|
||||
|
||||
var AddMonitorSettings = &gormigrate.Migration{
|
||||
ID: "20260929-monitor-settings",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
for _, key := range []string{"MonitorStatus", "MonitorInterval", "MonitorStoreDays"} {
|
||||
var setting model.Setting
|
||||
if err := tx.Where("key = ?", key).First(&setting).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, prefix := range []string{"GPU", "VLLM"} {
|
||||
if err := tx.Where("key = ?", prefix+key).FirstOrCreate(&model.Setting{Key: prefix + key, Value: setting.Value}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
aiToolsRouter.GET("/gpu/load", baseApi.LoadGpuInfo)
|
||||
aiToolsRouter.POST("/gpu/search", baseApi.LoadGPUMonitor)
|
||||
aiToolsRouter.GET("/gpu/options", baseApi.GetCPUOptions)
|
||||
aiToolsRouter.POST("/vllm/monitor/search", baseApi.LoadVLLMMonitor)
|
||||
aiToolsRouter.POST("/vllm/monitor/current", baseApi.LoadVLLMCurrent)
|
||||
aiToolsRouter.POST("/vllm/monitor/clean", baseApi.CleanVLLMMonitor)
|
||||
|
||||
aiToolsRouter.POST("/mcp/search", baseApi.PageMcpServers)
|
||||
aiToolsRouter.POST("/mcp/server/detail", baseApi.LoadMcpServerDetail)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/npu"
|
||||
@@ -22,14 +23,6 @@ type providerResult struct {
|
||||
}
|
||||
|
||||
func New() (bool, Client) {
|
||||
return newClient()
|
||||
}
|
||||
|
||||
func NewAll() (bool, Client) {
|
||||
return New()
|
||||
}
|
||||
|
||||
func newClient() (bool, Client) {
|
||||
client := Client{}
|
||||
if available, gpuClient := gpu.New(); available {
|
||||
client.providers = append(client.providers, gpuProvider{client: gpuClient})
|
||||
@@ -52,6 +45,11 @@ func (c Client) LoadInfo() (*Info, error) {
|
||||
}
|
||||
|
||||
func (c Client) Collect(ctx context.Context) (*Snapshot, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
defer cancel()
|
||||
results := make([]providerResult, len(c.providers))
|
||||
var wg sync.WaitGroup
|
||||
for index, item := range c.providers {
|
||||
@@ -66,8 +64,11 @@ func (c Client) Collect(ctx context.Context) (*Snapshot, error) {
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
snapshot := &Snapshot{DriverVersions: make(map[string]string)}
|
||||
snapshot := &Snapshot{}
|
||||
var (
|
||||
errs []error
|
||||
active []*ProviderSnapshot
|
||||
@@ -78,6 +79,11 @@ func (c Client) Collect(ctx context.Context) (*Snapshot, error) {
|
||||
errs = append(errs, result.err)
|
||||
continue
|
||||
}
|
||||
if result.snapshot != nil {
|
||||
for _, warning := range result.snapshot.Warnings {
|
||||
errs = append(errs, errors.New(warning))
|
||||
}
|
||||
}
|
||||
if result.snapshot == nil || len(result.snapshot.Devices) == 0 {
|
||||
continue
|
||||
}
|
||||
@@ -90,11 +96,8 @@ func (c Client) Collect(ctx context.Context) (*Snapshot, error) {
|
||||
if item.CudaVersion != "" {
|
||||
snapshot.Info.CudaVersion = item.CudaVersion
|
||||
}
|
||||
if item.DriverVersion != "" {
|
||||
snapshot.DriverVersions[item.Type] = item.DriverVersion
|
||||
if item.Type == "xpu" {
|
||||
xpuVersion = item.DriverVersion
|
||||
}
|
||||
if item.Type == "xpu" {
|
||||
xpuVersion = item.DriverVersion
|
||||
}
|
||||
}
|
||||
snapshot.Warnings = append(snapshot.Warnings, errs...)
|
||||
@@ -102,6 +105,10 @@ func (c Client) Collect(ctx context.Context) (*Snapshot, error) {
|
||||
return nil, fmt.Errorf("calling accelerator monitoring tools failed: %w", errors.Join(errs...))
|
||||
}
|
||||
|
||||
snapshot.Info.CollectedAt = time.Now()
|
||||
for _, err := range errs {
|
||||
snapshot.Info.Warnings = append(snapshot.Info.Warnings, err.Error())
|
||||
}
|
||||
snapshot.Info.XPUDriverVersion = xpuVersion
|
||||
snapshot.Info.Type, snapshot.Info.DriverVersion = mergeProviderMetadata(active)
|
||||
return snapshot, nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package accelerator
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -18,11 +19,11 @@ func metric(display, unit string) Metric {
|
||||
return result
|
||||
}
|
||||
parsed, err := strconv.ParseFloat(matched[1], 64)
|
||||
if err != nil {
|
||||
if err != nil || math.IsNaN(parsed) || math.IsInf(parsed, 0) || (parsed < 0 && unit != "°C") {
|
||||
return result
|
||||
}
|
||||
normalized, ok := convertMetricUnit(parsed, matched[2], unit)
|
||||
if !ok {
|
||||
if !ok || math.IsNaN(normalized) || math.IsInf(normalized, 0) || (unit == "%" && normalized > 100) {
|
||||
return result
|
||||
}
|
||||
result.Value = &normalized
|
||||
|
||||
@@ -12,26 +12,38 @@ import (
|
||||
|
||||
func normalizeGPU(item *gpu.Device) Device {
|
||||
metrics := Metrics{
|
||||
Utilization: metric(item.GPUUtil, "%"),
|
||||
Temperature: metric(item.Temperature, "°C"),
|
||||
Power: metric(item.PowerDraw, "W"),
|
||||
PowerLimit: metric(item.MaxPowerLimit, "W"),
|
||||
MemoryUsed: memoryMetric(item.MemUsed),
|
||||
MemoryTotal: memoryMetric(item.MemTotal),
|
||||
FanSpeed: metric(item.FanSpeed, "%"),
|
||||
MemoryActivity: metric(item.MemoryActivity, "%"),
|
||||
EncoderUtil: metric(item.EncoderUtil, "%"),
|
||||
DecoderUtil: metric(item.DecoderUtil, "%"),
|
||||
JPEGUtil: metric(item.JPEGUtil, "%"),
|
||||
OFAUtil: metric(item.OFAUtil, "%"),
|
||||
MediaUtil: metric(item.MediaUtil, "%"),
|
||||
HotspotTemperature: metric(item.HotspotTemperature, "°C"),
|
||||
FanRPM: metric(item.FanRPM, "RPM"),
|
||||
MediaFrequency: metric(item.MediaFrequency, "MHz"),
|
||||
Utilization: metric(item.GPUUtil, "%"),
|
||||
Temperature: metric(item.Temperature, "°C"),
|
||||
MemoryTemperature: metric(item.MemoryTemperature, "°C"),
|
||||
Power: metric(item.PowerDraw, "W"),
|
||||
PowerLimit: metric(item.PowerLimit, "W"),
|
||||
Frequency: metric(item.Frequency, "MHz"),
|
||||
MemoryFrequency: metric(item.MemoryFrequency, "MHz"),
|
||||
MemoryUsed: memoryMetric(item.MemUsed),
|
||||
MemoryTotal: memoryMetric(item.MemTotal),
|
||||
FanSpeed: metric(item.FanSpeed, "%"),
|
||||
}
|
||||
device := Device{
|
||||
ID: stableID(item.Type, item.BusID, strconv.FormatUint(uint64(item.Index), 10)),
|
||||
Kind: KindGPU,
|
||||
Vendor: item.Type,
|
||||
Index: int(item.Index),
|
||||
Name: item.ProductName,
|
||||
Label: fmt.Sprintf("%d - %s", item.Index, item.ProductName),
|
||||
BusID: item.BusID,
|
||||
Metrics: metrics,
|
||||
GPU: item,
|
||||
ProcessStatus: item.ProcessStatus,
|
||||
ID: stableID(item.Type, item.UUID, stableID("pci", item.BusID, strconv.FormatUint(uint64(item.Index), 10))),
|
||||
Kind: KindGPU,
|
||||
Vendor: item.Type,
|
||||
Index: int(item.Index),
|
||||
Name: item.ProductName,
|
||||
Label: fmt.Sprintf("%d - %s", item.Index, item.ProductName),
|
||||
BusID: item.BusID,
|
||||
Metrics: metrics,
|
||||
GPU: item,
|
||||
}
|
||||
device.Capabilities = capabilities(metrics)
|
||||
for _, process := range item.Processes {
|
||||
device.Processes = append(device.Processes, Process{
|
||||
PID: process.PID,
|
||||
@@ -45,26 +57,36 @@ func normalizeGPU(item *gpu.Device) Device {
|
||||
|
||||
func normalizeNPU(item *npu.Device) Device {
|
||||
metrics := Metrics{
|
||||
Utilization: metric(item.AICore, "%"),
|
||||
Temperature: metric(item.Temperature, "°C"),
|
||||
Power: metric(item.PowerDraw, "W"),
|
||||
MemoryUsed: memoryMetric(item.MemUsed),
|
||||
MemoryTotal: memoryMetric(item.MemTotal),
|
||||
AICPUUtil: metric(item.AICPUUtil, "%"),
|
||||
CtrlCPUUtil: metric(item.CtrlCPUUtil, "%"),
|
||||
DDRBandwidth: metric(item.DDRBandwidth, "%"),
|
||||
HBMBandwidth: metric(item.HBMBandwidth, "%"),
|
||||
DDRUsed: metric(item.MemoryUsed, "MiB"),
|
||||
DDRTotal: metric(item.MemoryTotal, "MiB"),
|
||||
HBMUsed: metric(item.HBMUsed, "MiB"),
|
||||
HBMTotal: metric(item.HBMTotal, "MiB"),
|
||||
HugepagesUsed: metric(item.HugepagesUsed, "pages"),
|
||||
HugepagesTotal: metric(item.HugepagesTotal, "pages"),
|
||||
Utilization: metric(item.AICore, "%"),
|
||||
Temperature: metric(item.Temperature, "°C"),
|
||||
Power: metric(item.PowerDraw, "W"),
|
||||
MemoryUsed: memoryMetric(item.MemUsed),
|
||||
MemoryTotal: memoryMetric(item.MemTotal),
|
||||
}
|
||||
device := Device{
|
||||
ID: fmt.Sprintf("ascend:%d:%d", item.NPUIndex, item.ChipIndex),
|
||||
Kind: KindNPU,
|
||||
Vendor: "ascend",
|
||||
Index: int(item.Index),
|
||||
NPUIndex: int(item.NPUIndex),
|
||||
ChipIndex: int(item.ChipIndex),
|
||||
Name: item.ProductName,
|
||||
Label: fmt.Sprintf("NPU %d / Chip %d - %s", item.NPUIndex, item.ChipIndex, item.ProductName),
|
||||
BusID: item.BusID,
|
||||
Metrics: metrics,
|
||||
NPU: item,
|
||||
ProcessStatus: item.ProcessStatus,
|
||||
ID: fmt.Sprintf("%s:%d", stableID("ascend", item.BusID, strconv.FormatUint(uint64(item.NPUIndex), 10)), item.ChipIndex),
|
||||
Kind: KindNPU,
|
||||
Vendor: "ascend",
|
||||
Index: int(item.Index),
|
||||
NPUIndex: int(item.NPUIndex),
|
||||
ChipIndex: int(item.ChipIndex),
|
||||
Name: item.ProductName,
|
||||
Label: fmt.Sprintf("NPU %d / Chip %d - %s", item.NPUIndex, item.ChipIndex, item.ProductName),
|
||||
BusID: item.BusID,
|
||||
Metrics: metrics,
|
||||
NPU: item,
|
||||
}
|
||||
device.Capabilities = capabilities(metrics)
|
||||
for _, process := range item.Processes {
|
||||
device.Processes = append(device.Processes, Process{
|
||||
PID: process.PID,
|
||||
@@ -78,26 +100,32 @@ func normalizeNPU(item *npu.Device) Device {
|
||||
|
||||
func normalizeXPU(item *xpu.Device) Device {
|
||||
metrics := Metrics{
|
||||
Utilization: metric(item.Stats.GPUUtil, "%"),
|
||||
Temperature: metric(item.Stats.Temperature, "°C"),
|
||||
Power: metric(item.Stats.Power, "W"),
|
||||
MemoryUsed: memoryMetric(item.Stats.MemoryUsed),
|
||||
MemoryTotal: memoryMetric(item.Basic.Memory),
|
||||
MemoryUtil: metric(item.Stats.MemoryUtil, "%"),
|
||||
Frequency: metric(item.Stats.Frequency, "MHz"),
|
||||
MediaUtil: metric(item.Stats.MediaUtil, "%"),
|
||||
ComputeUtil: metric(item.Stats.ComputeUtil, "%"),
|
||||
CopyUtil: metric(item.Stats.CopyUtil, "%"),
|
||||
MediaFrequency: metric(item.Stats.MediaFrequency, "MHz"),
|
||||
MemoryTemperature: metric(item.Stats.MemoryTemperature, "°C"),
|
||||
MemoryBandwidth: metric(item.Stats.MemoryBandwidthUtil, "%"),
|
||||
Utilization: metric(item.Stats.GPUUtil, "%"),
|
||||
Temperature: metric(item.Stats.Temperature, "°C"),
|
||||
Power: metric(item.Stats.Power, "W"),
|
||||
MemoryUsed: memoryMetric(item.Stats.MemoryUsed),
|
||||
MemoryTotal: memoryMetric(item.Basic.Memory),
|
||||
MemoryUtil: metric(item.Stats.MemoryUtil, "%"),
|
||||
Frequency: metric(item.Stats.Frequency, "MHz"),
|
||||
}
|
||||
device := Device{
|
||||
ID: stableID("xpu", item.Basic.PciBdfAddress, strconv.Itoa(item.Basic.DeviceID)),
|
||||
Kind: KindXPU,
|
||||
Vendor: item.Basic.VendorName,
|
||||
Index: item.Basic.DeviceID,
|
||||
Name: item.Basic.DeviceName,
|
||||
Label: fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName),
|
||||
BusID: item.Basic.PciBdfAddress,
|
||||
Metrics: metrics,
|
||||
XPU: item,
|
||||
ProcessStatus: item.ProcessStatus,
|
||||
ID: stableID("xpu", item.Basic.UUID, stableID("pci", item.Basic.PciBdfAddress, strconv.Itoa(item.Basic.DeviceID))),
|
||||
Kind: KindXPU,
|
||||
Vendor: item.Basic.VendorName,
|
||||
Index: item.Basic.DeviceID,
|
||||
Name: item.Basic.DeviceName,
|
||||
Label: fmt.Sprintf("%d - %s", item.Basic.DeviceID, item.Basic.DeviceName),
|
||||
BusID: item.Basic.PciBdfAddress,
|
||||
Metrics: metrics,
|
||||
XPU: item,
|
||||
}
|
||||
device.Capabilities = capabilities(metrics)
|
||||
for _, process := range item.Processes {
|
||||
device.Processes = append(device.Processes, Process{
|
||||
PID: strconv.Itoa(process.PID),
|
||||
@@ -110,18 +138,6 @@ func normalizeXPU(item *xpu.Device) Device {
|
||||
return device
|
||||
}
|
||||
|
||||
func capabilities(metrics Metrics) Capabilities {
|
||||
return Capabilities{
|
||||
Utilization: metrics.Utilization.Available(),
|
||||
Temperature: metrics.Temperature.Available(),
|
||||
Power: metrics.Power.Available(),
|
||||
PowerLimit: metrics.PowerLimit.Available(),
|
||||
Memory: metrics.MemoryUsed.Available() || metrics.MemoryTotal.Available(),
|
||||
FanSpeed: metrics.FanSpeed.Available(),
|
||||
Frequency: metrics.Frequency.Available(),
|
||||
}
|
||||
}
|
||||
|
||||
func stableID(vendor, busID, fallback string) string {
|
||||
if busID != "" && !strings.EqualFold(busID, "N/A") {
|
||||
return vendor + ":" + busID
|
||||
|
||||
@@ -2,6 +2,7 @@ package accelerator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/npu"
|
||||
@@ -26,6 +27,7 @@ func (p gpuProvider) Collect(ctx context.Context) (*ProviderSnapshot, error) {
|
||||
}
|
||||
result := &ProviderSnapshot{
|
||||
Type: info.Type,
|
||||
Warnings: info.Warnings,
|
||||
DriverVersion: info.DriverVersion,
|
||||
CudaVersion: info.CudaVersion,
|
||||
GPUs: info.Devices,
|
||||
@@ -49,6 +51,7 @@ func (p npuProvider) Collect(ctx context.Context) (*ProviderSnapshot, error) {
|
||||
}
|
||||
result := &ProviderSnapshot{
|
||||
Type: info.Type,
|
||||
Warnings: info.Warnings,
|
||||
DriverVersion: info.DriverVersion,
|
||||
NPUs: info.Devices,
|
||||
}
|
||||
@@ -71,11 +74,24 @@ func (p xpuProvider) Collect(ctx context.Context) (*ProviderSnapshot, error) {
|
||||
}
|
||||
result := &ProviderSnapshot{
|
||||
Type: info.Type,
|
||||
Warnings: info.Warnings,
|
||||
DriverVersion: info.DriverVersion,
|
||||
XPUs: info.Devices,
|
||||
}
|
||||
for index := range result.XPUs {
|
||||
result.Devices = append(result.Devices, normalizeXPU(&result.XPUs[index]))
|
||||
|
||||
parent := normalizeXPU(&result.XPUs[index])
|
||||
result.Devices = append(result.Devices, parent)
|
||||
for _, tile := range result.XPUs[index].Tiles {
|
||||
raw := xpu.Device{Basic: result.XPUs[index].Basic, Stats: tile.Stats, ProcessStatus: "unavailable"}
|
||||
raw.Basic.Memory = ""
|
||||
raw.Basic.FreeMemory = ""
|
||||
device := normalizeXPU(&raw)
|
||||
device.ParentID = parent.ID
|
||||
device.ID = fmt.Sprintf("%s:tile:%d", parent.ID, tile.TileID)
|
||||
device.Label = fmt.Sprintf("%s / Tile %d", parent.Label, tile.TileID)
|
||||
result.Devices = append(result.Devices, device)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package accelerator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/npu"
|
||||
@@ -17,6 +18,9 @@ const (
|
||||
)
|
||||
|
||||
type Info struct {
|
||||
CollectedAt time.Time `json:"collectedAt"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
Type string `json:"type"`
|
||||
CudaVersion string `json:"cudaVersion"`
|
||||
DriverVersion string `json:"driverVersion"`
|
||||
@@ -44,25 +48,40 @@ func (m Metric) ValueOrZero() float64 {
|
||||
}
|
||||
|
||||
type Metrics struct {
|
||||
Utilization Metric
|
||||
Temperature Metric
|
||||
Power Metric
|
||||
PowerLimit Metric
|
||||
MemoryUsed Metric
|
||||
MemoryTotal Metric
|
||||
MemoryUtil Metric
|
||||
FanSpeed Metric
|
||||
Frequency Metric
|
||||
}
|
||||
MemoryActivity Metric
|
||||
EncoderUtil Metric
|
||||
DecoderUtil Metric
|
||||
JPEGUtil Metric
|
||||
OFAUtil Metric
|
||||
MediaUtil Metric
|
||||
ComputeUtil Metric
|
||||
CopyUtil Metric
|
||||
HotspotTemperature Metric
|
||||
FanRPM Metric
|
||||
AICPUUtil Metric
|
||||
CtrlCPUUtil Metric
|
||||
DDRUsed Metric
|
||||
DDRTotal Metric
|
||||
HBMUsed Metric
|
||||
HBMTotal Metric
|
||||
DDRBandwidth Metric
|
||||
HBMBandwidth Metric
|
||||
MemoryBandwidth Metric
|
||||
MediaFrequency Metric
|
||||
HugepagesUsed Metric
|
||||
HugepagesTotal Metric
|
||||
|
||||
type Capabilities struct {
|
||||
Utilization bool
|
||||
Temperature bool
|
||||
Power bool
|
||||
PowerLimit bool
|
||||
Memory bool
|
||||
FanSpeed bool
|
||||
Frequency bool
|
||||
MemoryTemperature Metric
|
||||
Utilization Metric
|
||||
Temperature Metric
|
||||
Power Metric
|
||||
PowerLimit Metric
|
||||
MemoryUsed Metric
|
||||
MemoryTotal Metric
|
||||
MemoryUtil Metric
|
||||
FanSpeed Metric
|
||||
Frequency Metric
|
||||
MemoryFrequency Metric
|
||||
}
|
||||
|
||||
type Process struct {
|
||||
@@ -74,18 +93,19 @@ type Process struct {
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
ID string
|
||||
Kind Kind
|
||||
Vendor string
|
||||
Index int
|
||||
NPUIndex int
|
||||
ChipIndex int
|
||||
Name string
|
||||
Label string
|
||||
BusID string
|
||||
Metrics Metrics
|
||||
Capabilities Capabilities
|
||||
Processes []Process
|
||||
ParentID string
|
||||
ProcessStatus string
|
||||
ID string
|
||||
Kind Kind
|
||||
Vendor string
|
||||
Index int
|
||||
NPUIndex int
|
||||
ChipIndex int
|
||||
Name string
|
||||
Label string
|
||||
BusID string
|
||||
Metrics Metrics
|
||||
Processes []Process
|
||||
|
||||
GPU *gpu.Device `json:"-"`
|
||||
NPU *npu.Device `json:"-"`
|
||||
@@ -93,10 +113,9 @@ type Device struct {
|
||||
}
|
||||
|
||||
type Snapshot struct {
|
||||
Info Info
|
||||
Devices []Device
|
||||
DriverVersions map[string]string
|
||||
Warnings []error
|
||||
Info Info
|
||||
Devices []Device
|
||||
Warnings []error
|
||||
}
|
||||
|
||||
func (s Snapshot) Warning() error {
|
||||
@@ -104,6 +123,7 @@ func (s Snapshot) Warning() error {
|
||||
}
|
||||
|
||||
type ProviderSnapshot struct {
|
||||
Warnings []string
|
||||
Type string
|
||||
DriverVersion string
|
||||
CudaVersion string
|
||||
|
||||
@@ -3,12 +3,13 @@ package gpu
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
@@ -32,16 +33,22 @@ func findAMDSMI() (string, bool) {
|
||||
|
||||
func (a amdSMI) LoadInfo(ctx context.Context) (*Info, error) {
|
||||
var (
|
||||
staticData string
|
||||
metricData string
|
||||
processData string
|
||||
staticErr error
|
||||
metricErr error
|
||||
processErr error
|
||||
wg sync.WaitGroup
|
||||
staticData string
|
||||
metricData string
|
||||
extendedData string
|
||||
extendedErr error
|
||||
processData string
|
||||
staticErr error
|
||||
metricErr error
|
||||
processErr error
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
wg.Add(3)
|
||||
wg.Add(4)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
extendedData, extendedErr = runAMDSMI(ctx, a.command, "metric", "--clock", "--ecc", "--json")
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
staticData, staticErr = runAMDSMI(ctx, a.command, "static", "--asic", "--bus", "--driver", "--limit", "--json")
|
||||
@@ -64,16 +71,25 @@ func (a amdSMI) LoadInfo(ctx context.Context) (*Info, error) {
|
||||
return nil, fmt.Errorf("parsing %s static output failed: %w", a.command, err)
|
||||
}
|
||||
|
||||
if metricErr == nil {
|
||||
metricErr = applyAMDMetrics(info, metricData)
|
||||
}
|
||||
if metricErr != nil {
|
||||
global.LOG.Warnf("calling %s metric failed, metrics will be omitted: %v", a.command, metricErr)
|
||||
} else if err := applyAMDMetrics(info, metricData); err != nil {
|
||||
global.LOG.Warnf("parsing %s metric output failed, metrics will be omitted: %v", a.command, err)
|
||||
info.Warnings = append(info.Warnings, fmt.Sprintf("%s metrics: %v", a.command, metricErr))
|
||||
}
|
||||
if extendedErr == nil {
|
||||
extendedErr = applyAMDMetrics(info, extendedData)
|
||||
}
|
||||
if extendedErr != nil {
|
||||
info.Warnings = append(info.Warnings, fmt.Sprintf("%s extended metrics: %v", a.command, extendedErr))
|
||||
}
|
||||
if processErr == nil {
|
||||
processErr = applyAMDProcesses(info, processData)
|
||||
}
|
||||
if processErr != nil {
|
||||
global.LOG.Warnf("calling %s process failed, process information will be omitted: %v", a.command, processErr)
|
||||
} else if err := applyAMDProcesses(info, processData); err != nil {
|
||||
global.LOG.Warnf("parsing %s process output failed, process information will be omitted: %v", a.command, err)
|
||||
info.Warnings = append(info.Warnings, fmt.Sprintf("%s processes: %v", a.command, processErr))
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -96,6 +112,9 @@ func parseAMDStatic(data string) (*Info, error) {
|
||||
}
|
||||
device := Device{
|
||||
Type: "amd",
|
||||
UUID: amdStringAt(row, "uuid", "asic.uuid"),
|
||||
DriverVersion: amdStringAt(row, "driver.version", "driver_version", "amdgpu_version"),
|
||||
ProcessStatus: "unavailable",
|
||||
Index: index,
|
||||
ProductName: amdStringAt(row, "asic.market_name", "market_name", "gpu_name"),
|
||||
PersistenceMode: "N/A",
|
||||
@@ -111,6 +130,7 @@ func parseAMDStatic(data string) (*Info, error) {
|
||||
"limit.max_power_limit",
|
||||
"limit.max_power",
|
||||
),
|
||||
PowerLimit: amdMetricAt(row, "W", "limit.ppt0.socket_power_limit", "limit.socket_power_limit"),
|
||||
MemUsed: "N/A",
|
||||
MemTotal: "N/A",
|
||||
GPUUtil: "N/A",
|
||||
@@ -150,11 +170,31 @@ func applyAMDMetrics(info *Info, data string) error {
|
||||
continue
|
||||
}
|
||||
setAMDMetric(&device.GPUUtil, row, "%", "usage.gfx_activity", "usage.gfx", "gfx_activity", "gfx_usage")
|
||||
setAMDMetric(&device.Temperature, row, "°C", "temperature.hotspot", "temperature.edge", "hotspot_temperature", "gpu_temperature", "gpu_temp")
|
||||
setAMDMetric(&device.Temperature, row, "°C", "temperature.edge", "gpu_temperature", "gpu_temp")
|
||||
setAMDMetric(&device.PowerDraw, row, "W", "power.socket_power", "socket_power", "power_usage")
|
||||
setAMDMetric(&device.MemUsed, row, "MB", "mem_usage.used_vram", "vram.used", "used_vram", "vram_used")
|
||||
setAMDMetric(&device.MemTotal, row, "MB", "mem_usage.total_vram", "vram.total", "total_vram", "vram_total")
|
||||
setAMDMetric(&device.FanSpeed, row, "%", "fan.speed", "fan_speed")
|
||||
|
||||
setAMDMetric(&device.FanSpeed, row, "%", "fan.usage")
|
||||
if amdMetricAt(row, "%", "fan.usage") == "" {
|
||||
speed, speedErr := strconv.ParseFloat(amdStringAt(row, "fan.speed"), 64)
|
||||
maximum, maxErr := strconv.ParseFloat(amdStringAt(row, "fan.max"), 64)
|
||||
if speedErr == nil && maxErr == nil && !math.IsNaN(speed) && !math.IsInf(speed, 0) && maximum > 0 && !math.IsInf(maximum, 0) && speed >= 0 && speed <= maximum {
|
||||
device.FanSpeed = fmt.Sprintf("%.2f %%", speed/maximum*100)
|
||||
}
|
||||
}
|
||||
setAMDMetric(&device.FanRPM, row, "RPM", "fan.rpm")
|
||||
setAMDMetric(&device.HotspotTemperature, row, "°C", "temperature.hotspot", "hotspot_temperature")
|
||||
setAMDMetric(&device.MemoryTemperature, row, "°C", "temperature.mem")
|
||||
setAMDMetric(&device.MemoryActivity, row, "%", "usage.umc_activity")
|
||||
setAMDMetric(&device.MediaUtil, row, "%", "usage.mm_activity")
|
||||
setAMDMetric(&device.Frequency, row, "MHz", "clock.gfx_0.clk")
|
||||
setAMDMetric(&device.MemoryFrequency, row, "MHz", "clock.mem_0.clk")
|
||||
correctable := amdStringAt(row, "ecc.total_correctable_count", "ecc.correctable_count")
|
||||
uncorrectable := amdStringAt(row, "ecc.total_uncorrectable_count", "ecc.uncorrectable_count")
|
||||
if correctable != "" || uncorrectable != "" {
|
||||
device.ECCErrors = []ECCError{{Scope: "Total", Correctable: correctable, Uncorrectable: uncorrectable}}
|
||||
}
|
||||
if value := amdStringAt(row, "perf_level", "performance_level"); value != "" {
|
||||
device.PerformanceState = value
|
||||
}
|
||||
@@ -177,6 +217,7 @@ func applyAMDProcesses(info *Info, data string) error {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
device.ProcessStatus = "ok"
|
||||
processList, _ := amdValueAt(row, "process_list")
|
||||
items := amdObjectList(processList)
|
||||
if len(items) == 0 {
|
||||
|
||||
@@ -64,6 +64,7 @@ func (c Client) LoadInfoContext(ctx context.Context) (*Info, error) {
|
||||
if result.info == nil {
|
||||
continue
|
||||
}
|
||||
merged.Warnings = append(merged.Warnings, result.info.Warnings...)
|
||||
if result.info.Type != "" {
|
||||
types = append(types, result.info.Type)
|
||||
}
|
||||
@@ -86,6 +87,9 @@ func (c Client) LoadInfoContext(ctx context.Context) (*Info, error) {
|
||||
if len(merged.Devices) == 0 && len(errs) > 0 {
|
||||
return nil, fmt.Errorf("calling GPU monitoring tools failed: %w", errors.Join(errs...))
|
||||
}
|
||||
for _, err := range errs {
|
||||
merged.Warnings = append(merged.Warnings, err.Error())
|
||||
}
|
||||
return merged, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
@@ -24,41 +20,10 @@ func (n nvidiaSMI) LoadInfo(ctx context.Context) (*Info, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calling %s failed: %w", nvidiaSMICommand, err)
|
||||
}
|
||||
data := []byte(itemData)
|
||||
version := "v11"
|
||||
|
||||
buf := bytes.NewBuffer(data)
|
||||
decoder := xml.NewDecoder(buf)
|
||||
for {
|
||||
token, err := decoder.Token()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("reading token failed: %w", err)
|
||||
}
|
||||
d, ok := token.(xml.Directive)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
directive := string(d)
|
||||
if !strings.HasPrefix(directive, "DOCTYPE") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Split(directive, " ")
|
||||
s := strings.Trim(parts[len(parts)-1], "\" ")
|
||||
if strings.HasPrefix(s, "nvsmi_device_") && strings.HasSuffix(s, ".dtd") {
|
||||
version = strings.TrimSuffix(strings.TrimPrefix(s, "nvsmi_device_"), ".dtd")
|
||||
} else {
|
||||
global.LOG.Debugf("Cannot find schema version in %q", directive)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return parseNvidiaSMI(data, version)
|
||||
return parseNvidiaSMI([]byte(itemData))
|
||||
}
|
||||
|
||||
func parseNvidiaSMI(buf []byte, version string) (*Info, error) {
|
||||
func parseNvidiaSMI(buf []byte) (*Info, error) {
|
||||
var (
|
||||
s nvidiaSMIResponse
|
||||
info Info
|
||||
@@ -72,42 +37,84 @@ func parseNvidiaSMI(buf []byte, version string) (*Info, error) {
|
||||
info.DriverVersion = s.DriverVersion
|
||||
for i := range s.Gpu {
|
||||
gpuItem := Device{
|
||||
Type: "nvidia",
|
||||
Index: uint(i),
|
||||
ProductName: s.Gpu[i].ProductName,
|
||||
PersistenceMode: s.Gpu[i].PersistenceMode,
|
||||
BusID: s.Gpu[i].ID,
|
||||
DisplayActive: s.Gpu[i].DisplayActive,
|
||||
ECC: s.Gpu[i].EccErrors.Volatile.DramUncorrectable,
|
||||
FanSpeed: s.Gpu[i].FanSpeed,
|
||||
Temperature: s.Gpu[i].Temperature.GpuTemp,
|
||||
PerformanceState: s.Gpu[i].PerformanceState,
|
||||
MemUsed: s.Gpu[i].FbMemoryUsage.Used,
|
||||
MemTotal: s.Gpu[i].FbMemoryUsage.Total,
|
||||
GPUUtil: s.Gpu[i].Utilization.GpuUtil,
|
||||
ComputeMode: s.Gpu[i].ComputeMode,
|
||||
MigMode: s.Gpu[i].MigMode.CurrentMig,
|
||||
}
|
||||
if version == "v12" || version == "v13" {
|
||||
gpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.PowerDraw
|
||||
if gpuItem.PowerDraw == "" {
|
||||
gpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.InstantPowerDraw
|
||||
}
|
||||
gpuItem.MaxPowerLimit = s.Gpu[i].GpuPowerReadings.CurrentPowerLimit
|
||||
} else {
|
||||
gpuItem.PowerDraw = s.Gpu[i].PowerReadings.PowerDraw
|
||||
gpuItem.MaxPowerLimit = s.Gpu[i].PowerReadings.MaxPowerLimit
|
||||
Type: "nvidia",
|
||||
MemoryActivity: s.Gpu[i].Utilization.MemoryUtil,
|
||||
EncoderUtil: s.Gpu[i].Utilization.EncoderUtil,
|
||||
DecoderUtil: s.Gpu[i].Utilization.DecoderUtil,
|
||||
JPEGUtil: s.Gpu[i].Utilization.JpegUtil,
|
||||
OFAUtil: s.Gpu[i].Utilization.OfaUtil,
|
||||
MediaFrequency: s.Gpu[i].Clocks.VideoClock,
|
||||
UUID: s.Gpu[i].UUID,
|
||||
DriverVersion: s.DriverVersion,
|
||||
Architecture: s.Gpu[i].ProductArchitecture,
|
||||
Frequency: s.Gpu[i].Clocks.GraphicsClock,
|
||||
MemoryFrequency: s.Gpu[i].Clocks.MemClock,
|
||||
MemoryTemperature: s.Gpu[i].Temperature.MemoryTemp,
|
||||
MemoryFree: s.Gpu[i].FbMemoryUsage.Free,
|
||||
MemoryReserved: s.Gpu[i].FbMemoryUsage.Reserved,
|
||||
PCIeGeneration: firstSMIValue(s.Gpu[i].Pci.PciGpuLinkInfo.PcieGen.DeviceCurrentLinkGen, s.Gpu[i].Pci.PciGpuLinkInfo.PcieGen.CurrentLinkGen),
|
||||
PCIeMaxGeneration: firstSMIValue(s.Gpu[i].Pci.PciGpuLinkInfo.PcieGen.MaxDeviceLinkGen, s.Gpu[i].Pci.PciGpuLinkInfo.PcieGen.MaxLinkGen),
|
||||
PCIeWidth: s.Gpu[i].Pci.PciGpuLinkInfo.LinkWidths.CurrentLinkWidth,
|
||||
PCIeMaxWidth: s.Gpu[i].Pci.PciGpuLinkInfo.LinkWidths.MaxLinkWidth,
|
||||
ProcessStatus: "unavailable",
|
||||
Index: uint(i),
|
||||
ProductName: s.Gpu[i].ProductName,
|
||||
PersistenceMode: s.Gpu[i].PersistenceMode,
|
||||
BusID: s.Gpu[i].ID,
|
||||
DisplayActive: s.Gpu[i].DisplayActive,
|
||||
ECC: s.Gpu[i].EccMode.CurrentEcc,
|
||||
FanSpeed: s.Gpu[i].FanSpeed,
|
||||
Temperature: s.Gpu[i].Temperature.GpuTemp,
|
||||
PerformanceState: s.Gpu[i].PerformanceState,
|
||||
MemUsed: s.Gpu[i].FbMemoryUsage.Used,
|
||||
MemTotal: s.Gpu[i].FbMemoryUsage.Total,
|
||||
GPUUtil: s.Gpu[i].Utilization.GpuUtil,
|
||||
ComputeMode: s.Gpu[i].ComputeMode,
|
||||
MigMode: s.Gpu[i].MigMode.CurrentMig,
|
||||
}
|
||||
gpuItem.ECCPending = s.Gpu[i].EccMode.PendingEcc
|
||||
gpuItem.ECCErrors = []ECCError{
|
||||
{Scope: "Volatile Total", Correctable: s.Gpu[i].EccErrors.Volatile.SingleBit.Total, Uncorrectable: s.Gpu[i].EccErrors.Volatile.DoubleBit.Total},
|
||||
{Scope: "Aggregate Total", Correctable: s.Gpu[i].EccErrors.Aggregate.SingleBit.Total, Uncorrectable: s.Gpu[i].EccErrors.Aggregate.DoubleBit.Total},
|
||||
|
||||
for _, process := range s.Gpu[i].Processes.ProcessInfo {
|
||||
gpuItem.Processes = append(gpuItem.Processes, Process{
|
||||
PID: process.Pid,
|
||||
Type: process.Type,
|
||||
ProcessName: process.ProcessName,
|
||||
UsedMemory: process.UsedMemory,
|
||||
})
|
||||
{Scope: "Volatile DRAM", Correctable: s.Gpu[i].EccErrors.Volatile.DramCorrectable, Uncorrectable: s.Gpu[i].EccErrors.Volatile.DramUncorrectable},
|
||||
{Scope: "Volatile SRAM", Correctable: s.Gpu[i].EccErrors.Volatile.SramCorrectable, Uncorrectable: s.Gpu[i].EccErrors.Volatile.SramUncorrectable},
|
||||
{Scope: "Aggregate DRAM", Correctable: s.Gpu[i].EccErrors.Aggregate.DramCorrectable, Uncorrectable: s.Gpu[i].EccErrors.Aggregate.DramUncorrectable},
|
||||
{Scope: "Aggregate SRAM", Correctable: s.Gpu[i].EccErrors.Aggregate.SramCorrectable, Uncorrectable: s.Gpu[i].EccErrors.Aggregate.SramUncorrectable},
|
||||
}
|
||||
gpuItem.PowerDraw = firstSMIValue(s.Gpu[i].GpuPowerReadings.PowerDraw, s.Gpu[i].GpuPowerReadings.InstantPowerDraw, s.Gpu[i].PowerReadings.PowerDraw)
|
||||
gpuItem.PowerLimit = firstSMIValue(s.Gpu[i].GpuPowerReadings.CurrentPowerLimit, s.Gpu[i].PowerReadings.EnforcedPowerLimit, s.Gpu[i].PowerReadings.PowerLimit)
|
||||
gpuItem.MaxPowerLimit = firstSMIValue(s.Gpu[i].GpuPowerReadings.MaxPowerLimit, s.Gpu[i].PowerReadings.MaxPowerLimit)
|
||||
gpuItem.DefaultPowerLimit = firstSMIValue(s.Gpu[i].GpuPowerReadings.DefaultPowerLimit, s.Gpu[i].PowerReadings.DefaultPowerLimit)
|
||||
for _, event := range append(s.Gpu[i].ClocksEventReasons.Reasons, s.Gpu[i].ClocksThrottleReasons.Reasons...) {
|
||||
if strings.EqualFold(strings.TrimSpace(event.Value), "Active") {
|
||||
gpuItem.ClockEvents = append(gpuItem.ClockEvents, strings.TrimPrefix(strings.TrimPrefix(event.XMLName.Local, "clocks_event_reason_"), "clocks_throttle_reason_"))
|
||||
}
|
||||
}
|
||||
if s.Gpu[i].Processes != nil && strings.TrimSpace(s.Gpu[i].Processes.Text) == "" {
|
||||
gpuItem.ProcessStatus = "ok"
|
||||
}
|
||||
if s.Gpu[i].Processes != nil {
|
||||
for _, process := range s.Gpu[i].Processes.ProcessInfo {
|
||||
gpuItem.Processes = append(gpuItem.Processes, Process{
|
||||
PID: process.Pid,
|
||||
Type: process.Type,
|
||||
ProcessName: process.ProcessName,
|
||||
UsedMemory: process.UsedMemory,
|
||||
})
|
||||
}
|
||||
}
|
||||
info.Devices = append(info.Devices, gpuItem)
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func firstSMIValue(values ...string) string {
|
||||
for _, value := range values {
|
||||
value = strings.TrimSpace(value)
|
||||
if value != "" && !strings.EqualFold(value, "N/A") && !strings.EqualFold(value, "Not Supported") {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package gpu
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
type nvidiaSMIResponse struct {
|
||||
AttachedGpus string `xml:"attached_gpus"`
|
||||
CudaVersion string `xml:"cuda_version"`
|
||||
DriverVersion string `xml:"driver_version"`
|
||||
XMLName xml.Name `xml:"nvidia_smi_log"`
|
||||
AttachedGpus string `xml:"attached_gpus"`
|
||||
CudaVersion string `xml:"cuda_version"`
|
||||
DriverVersion string `xml:"driver_version"`
|
||||
Gpu []struct {
|
||||
ID string `xml:"id,attr"`
|
||||
AccountedProcesses struct{} `xml:"accounted_processes"`
|
||||
@@ -37,16 +40,17 @@ type nvidiaSMIResponse struct {
|
||||
VideoClock string `xml:"video_clock"`
|
||||
} `xml:"clocks"`
|
||||
ClocksEventReasons struct {
|
||||
ClocksEventReasonApplicationsClocksSetting string `xml:"clocks_event_reason_applications_clocks_setting"`
|
||||
ClocksEventReasonDisplayClocksSetting string `xml:"clocks_event_reason_display_clocks_setting"`
|
||||
ClocksEventReasonGpuIdle string `xml:"clocks_event_reason_gpu_idle"`
|
||||
ClocksEventReasonHwPowerBrakeSlowdown string `xml:"clocks_event_reason_hw_power_brake_slowdown"`
|
||||
ClocksEventReasonHwSlowdown string `xml:"clocks_event_reason_hw_slowdown"`
|
||||
ClocksEventReasonHwThermalSlowdown string `xml:"clocks_event_reason_hw_thermal_slowdown"`
|
||||
ClocksEventReasonSwPowerCap string `xml:"clocks_event_reason_sw_power_cap"`
|
||||
ClocksEventReasonSwThermalSlowdown string `xml:"clocks_event_reason_sw_thermal_slowdown"`
|
||||
ClocksEventReasonSyncBoost string `xml:"clocks_event_reason_sync_boost"`
|
||||
Reasons []struct {
|
||||
XMLName xml.Name
|
||||
Value string `xml:",chardata"`
|
||||
} `xml:",any"`
|
||||
} `xml:"clocks_event_reasons"`
|
||||
ClocksThrottleReasons struct {
|
||||
Reasons []struct {
|
||||
XMLName xml.Name
|
||||
Value string `xml:",chardata"`
|
||||
} `xml:",any"`
|
||||
} `xml:"clocks_throttle_reasons"`
|
||||
ComputeMode string `xml:"compute_mode"`
|
||||
DefaultApplicationsClocks struct {
|
||||
GraphicsClock string `xml:"graphics_clock"`
|
||||
@@ -63,12 +67,24 @@ type nvidiaSMIResponse struct {
|
||||
} `xml:"driver_model"`
|
||||
EccErrors struct {
|
||||
Aggregate struct {
|
||||
SingleBit struct {
|
||||
Total string `xml:"total"`
|
||||
} `xml:"single_bit"`
|
||||
DoubleBit struct {
|
||||
Total string `xml:"total"`
|
||||
} `xml:"double_bit"`
|
||||
DramCorrectable string `xml:"dram_correctable"`
|
||||
DramUncorrectable string `xml:"dram_uncorrectable"`
|
||||
SramCorrectable string `xml:"sram_correctable"`
|
||||
SramUncorrectable string `xml:"sram_uncorrectable"`
|
||||
} `xml:"aggregate"`
|
||||
Volatile struct {
|
||||
SingleBit struct {
|
||||
Total string `xml:"total"`
|
||||
} `xml:"single_bit"`
|
||||
DoubleBit struct {
|
||||
Total string `xml:"total"`
|
||||
} `xml:"double_bit"`
|
||||
DramCorrectable string `xml:"dram_correctable"`
|
||||
DramUncorrectable string `xml:"dram_uncorrectable"`
|
||||
SramCorrectable string `xml:"sram_correctable"`
|
||||
@@ -226,7 +242,8 @@ type nvidiaSMIResponse struct {
|
||||
MinPowerLimit string `xml:"min_power_limit"`
|
||||
MaxPowerLimit string `xml:"max_power_limit"`
|
||||
} `xml:"power_readings"`
|
||||
Processes struct {
|
||||
Processes *struct {
|
||||
Text string `xml:",chardata"`
|
||||
ProcessInfo []struct {
|
||||
Pid string `xml:"pid"`
|
||||
Type string `xml:"type"`
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package gpu
|
||||
|
||||
type Info struct {
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
CudaVersion string `json:"cudaVersion"`
|
||||
DriverVersion string `json:"driverVersion"`
|
||||
Type string `json:"type"`
|
||||
@@ -8,7 +10,42 @@ type Info struct {
|
||||
Devices []Device `json:"gpu"`
|
||||
}
|
||||
|
||||
type ECCError struct {
|
||||
Scope string `json:"scope"`
|
||||
Correctable string `json:"correctable"`
|
||||
Uncorrectable string `json:"uncorrectable"`
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
ECCPending string `json:"eccPending"`
|
||||
ECCErrors []ECCError `json:"eccErrors"`
|
||||
MemoryActivity string `json:"memoryActivity"`
|
||||
EncoderUtil string `json:"encoderUtil"`
|
||||
DecoderUtil string `json:"decoderUtil"`
|
||||
JPEGUtil string `json:"jpegUtil"`
|
||||
OFAUtil string `json:"ofaUtil"`
|
||||
MediaUtil string `json:"mediaUtil"`
|
||||
HotspotTemperature string `json:"hotspotTemperature"`
|
||||
FanRPM string `json:"fanRPM"`
|
||||
MediaFrequency string `json:"mediaFrequency"`
|
||||
|
||||
UUID string `json:"uuid"`
|
||||
DriverVersion string `json:"driverVersion"`
|
||||
Architecture string `json:"architecture"`
|
||||
Frequency string `json:"frequency"`
|
||||
MemoryFrequency string `json:"memoryFrequency"`
|
||||
MemoryTemperature string `json:"memoryTemperature"`
|
||||
MemoryFree string `json:"memoryFree"`
|
||||
MemoryReserved string `json:"memoryReserved"`
|
||||
PowerLimit string `json:"powerLimit"`
|
||||
DefaultPowerLimit string `json:"defaultPowerLimit"`
|
||||
PCIeGeneration string `json:"pcieGeneration"`
|
||||
PCIeMaxGeneration string `json:"pcieMaxGeneration"`
|
||||
PCIeWidth string `json:"pcieWidth"`
|
||||
PCIeMaxWidth string `json:"pcieMaxWidth"`
|
||||
ProcessStatus string `json:"processStatus"`
|
||||
ClockEvents []string `json:"clockEvents"`
|
||||
|
||||
Type string `json:"type"`
|
||||
Index uint `json:"index"`
|
||||
ProductName string `json:"productName"`
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
@@ -34,7 +35,31 @@ func (c Client) LoadInfoContext(ctx context.Context) (*Info, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calling %s failed: %w", ascendSMICommand, err)
|
||||
}
|
||||
return parseAscendSMI(itemData), nil
|
||||
|
||||
info := parseAscendSMI(itemData)
|
||||
var wg sync.WaitGroup
|
||||
warnings := make([]string, len(info.Devices))
|
||||
for i := range info.Devices {
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
defer wg.Done()
|
||||
device := &info.Devices[index]
|
||||
mgr := cmd.NewCommandMgr(cmd.WithContext(ctx), cmd.WithTimeout(5*time.Second))
|
||||
data, err := mgr.RunWithStdout(ascendSMICommand, "info", "-t", "usages", "-i", strconv.FormatUint(uint64(device.NPUIndex), 10), "-c", strconv.FormatUint(uint64(device.ChipIndex), 10))
|
||||
if err != nil {
|
||||
warnings[index] = fmt.Sprintf("npu-smi usages %d/%d: %v", device.NPUIndex, device.ChipIndex, err)
|
||||
return
|
||||
}
|
||||
applyAscendUsages(device, data)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
for _, warning := range warnings {
|
||||
if warning != "" {
|
||||
info.Warnings = append(info.Warnings, warning)
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func parseAscendSMI(data string) *Info {
|
||||
@@ -158,6 +183,12 @@ func parseAscendSMI(data string) *Info {
|
||||
pending = nil
|
||||
}
|
||||
|
||||
for i := range info.Devices {
|
||||
info.Devices[i].ProcessStatus = "unavailable"
|
||||
if processSection {
|
||||
info.Devices[i].ProcessStatus = "ok"
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
@@ -208,6 +239,9 @@ func ascendMemoryPools(value, header string) (string, string, string, string) {
|
||||
}
|
||||
|
||||
hbm := usage[len(usage)-1]
|
||||
if !strings.Contains(normalizedHeader, "MEMORYUSAGE") {
|
||||
memoryUsed, memoryTotal = "", ""
|
||||
}
|
||||
return memoryUsed, memoryTotal, hbm[0] + " MB", hbm[1] + " MB"
|
||||
}
|
||||
|
||||
@@ -233,3 +267,29 @@ func ascendValueWithUnit(value, unit string) string {
|
||||
}
|
||||
return value + " " + unit
|
||||
}
|
||||
|
||||
func applyAscendUsages(device *Device, data string) {
|
||||
for _, line := range strings.Split(data, "\n") {
|
||||
key, value, ok := strings.Cut(line, ":")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key = strings.ToLower(strings.Join(strings.Fields(key), ""))
|
||||
value = strings.TrimSpace(value)
|
||||
switch key {
|
||||
case "aicoreusagerate(%)":
|
||||
usage, err := strconv.ParseFloat(strings.TrimSpace(strings.TrimSuffix(value, "%")), 64)
|
||||
if err == nil && usage >= 0 && usage <= 100 {
|
||||
device.AICore = ascendValueWithUnit(value, "%")
|
||||
}
|
||||
case "aicpuusagerate(%)":
|
||||
device.AICPUUtil = ascendValueWithUnit(value, "%")
|
||||
case "ctrlcpuusagerate(%)":
|
||||
device.CtrlCPUUtil = ascendValueWithUnit(value, "%")
|
||||
case "ddrbandwidthusagerate(%)", "memorybandwidthusagerate(%)":
|
||||
device.DDRBandwidth = ascendValueWithUnit(value, "%")
|
||||
case "hbmbandwidthusagerate(%)":
|
||||
device.HBMBandwidth = ascendValueWithUnit(value, "%")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
package npu
|
||||
|
||||
type Info struct {
|
||||
Warnings []string `json:"warnings"`
|
||||
Type string `json:"type"`
|
||||
DriverVersion string `json:"driverVersion"`
|
||||
Devices []Device `json:"npu"`
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
AICPUUtil string `json:"aiCPUUtil"`
|
||||
CtrlCPUUtil string `json:"ctrlCPUUtil"`
|
||||
DDRBandwidth string `json:"ddrBandwidth"`
|
||||
HBMBandwidth string `json:"hbmBandwidth"`
|
||||
|
||||
ProcessStatus string `json:"processStatus"`
|
||||
|
||||
Type string `json:"type"`
|
||||
Index uint `json:"index"`
|
||||
NPUIndex uint `json:"npuIndex"`
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
package vllm
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
||||
)
|
||||
|
||||
type Metrics map[string]map[string]float64
|
||||
|
||||
type histogram struct {
|
||||
Sum float64 `json:"sum"`
|
||||
Count float64 `json:"count"`
|
||||
Buckets map[string]float64 `json:"buckets,omitempty"`
|
||||
}
|
||||
|
||||
func Parse(reader io.Reader) (Metrics, error) {
|
||||
metrics := Metrics{}
|
||||
scanner := bufio.NewScanner(reader)
|
||||
scanner.Buffer(make([]byte, 4096), 1024*1024)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if !strings.HasPrefix(line, "vllm:") {
|
||||
continue
|
||||
}
|
||||
match := re.GetRegex(re.VLLMMetricSamplePattern).FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return nil, fmt.Errorf("invalid vLLM metric sample")
|
||||
}
|
||||
name := strings.TrimPrefix(match[1], "vllm:")
|
||||
switch name {
|
||||
case "num_requests_running", "num_requests_waiting", "kv_cache_usage_perc", "gpu_cache_usage_perc", "prompt_tokens_total", "generation_tokens_total", "request_success_total":
|
||||
default:
|
||||
base := strings.TrimSuffix(strings.TrimSuffix(strings.TrimSuffix(name, "_sum"), "_count"), "_bucket")
|
||||
if base == name {
|
||||
continue
|
||||
}
|
||||
switch base {
|
||||
case "time_to_first_token_seconds", "time_per_output_token_seconds", "inter_token_latency_seconds", "e2e_request_latency_seconds", "request_prefill_time_seconds", "request_decode_time_seconds":
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
value, err := strconv.ParseFloat(match[3], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid vLLM metric value: %w", err)
|
||||
}
|
||||
if math.IsNaN(value) || math.IsInf(value, 0) || value < 0 {
|
||||
continue
|
||||
}
|
||||
if metrics[name] == nil {
|
||||
metrics[name] = map[string]float64{}
|
||||
}
|
||||
labels := re.GetRegex(re.VLLMMetricLabelPattern).FindAllStringSubmatch(match[2], -1)
|
||||
parts := make([]string, 0, len(labels))
|
||||
for _, label := range labels {
|
||||
parts = append(parts, label[1]+"="+label[2])
|
||||
}
|
||||
sort.Strings(parts)
|
||||
metrics[name]["{"+strings.Join(parts, ",")+"}"] = value
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(metrics) == 0 {
|
||||
return nil, fmt.Errorf("no supported vLLM metrics")
|
||||
}
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func Calculate(current, previous Metrics, elapsed float64) (model.MonitorVLLM, error) {
|
||||
result := model.MonitorVLLM{}
|
||||
for name, target := range map[string]**float64{
|
||||
"num_requests_running": &result.Running,
|
||||
"num_requests_waiting": &result.Waiting,
|
||||
} {
|
||||
if len(current[name]) == 0 {
|
||||
continue
|
||||
}
|
||||
value := 0.0
|
||||
for _, sample := range current[name] {
|
||||
value += sample
|
||||
}
|
||||
*target = &value
|
||||
}
|
||||
cache := current["kv_cache_usage_perc"]
|
||||
if len(cache) == 0 {
|
||||
cache = current["gpu_cache_usage_perc"]
|
||||
}
|
||||
if len(cache) > 0 {
|
||||
value := 0.0
|
||||
for _, sample := range cache {
|
||||
value += sample
|
||||
}
|
||||
value = value / float64(len(cache)) * 100
|
||||
result.CacheUsage = &value
|
||||
}
|
||||
if elapsed <= 0 {
|
||||
return result, nil
|
||||
}
|
||||
for name, target := range map[string]**float64{
|
||||
"prompt_tokens_total": &result.PromptThroughput,
|
||||
"generation_tokens_total": &result.GenerationThroughput,
|
||||
"request_success_total": &result.RequestThroughput,
|
||||
} {
|
||||
if value := counterDelta(current[name], previous[name]); value != nil {
|
||||
rate := *value / elapsed
|
||||
*target = &rate
|
||||
}
|
||||
}
|
||||
histograms := map[string]histogram{}
|
||||
for _, name := range []string{"time_to_first_token_seconds", "inter_token_latency_seconds", "e2e_request_latency_seconds", "request_prefill_time_seconds", "request_decode_time_seconds"} {
|
||||
source := name
|
||||
if name == "inter_token_latency_seconds" && len(current[name+"_count"]) == 0 {
|
||||
source = "time_per_output_token_seconds"
|
||||
}
|
||||
sum := counterDelta(current[source+"_sum"], previous[source+"_sum"])
|
||||
count := counterDelta(current[source+"_count"], previous[source+"_count"])
|
||||
if sum == nil || count == nil {
|
||||
continue
|
||||
}
|
||||
histograms[name] = histogram{Sum: *sum, Count: *count, Buckets: bucketDeltas(current[source+"_bucket"], previous[source+"_bucket"], *count)}
|
||||
}
|
||||
applyHistograms(&result, histograms)
|
||||
encoded, err := json.Marshal(histograms)
|
||||
if err != nil {
|
||||
return model.MonitorVLLM{}, err
|
||||
}
|
||||
result.HistogramDeltas = string(encoded)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func AggregateHistograms(encoded string, result *model.MonitorVLLM) error {
|
||||
var samples []map[string]histogram
|
||||
if err := json.Unmarshal([]byte(encoded), &samples); err != nil {
|
||||
return err
|
||||
}
|
||||
merged := map[string]histogram{}
|
||||
for _, sample := range samples {
|
||||
for name, item := range sample {
|
||||
if item.Count == 0 {
|
||||
continue
|
||||
}
|
||||
total, exists := merged[name]
|
||||
if !exists {
|
||||
merged[name] = item
|
||||
continue
|
||||
}
|
||||
total.Sum += item.Sum
|
||||
total.Count += item.Count
|
||||
if len(total.Buckets) != len(item.Buckets) {
|
||||
total.Buckets = nil
|
||||
}
|
||||
for bound, count := range total.Buckets {
|
||||
addition, ok := item.Buckets[bound]
|
||||
if !ok {
|
||||
total.Buckets = nil
|
||||
break
|
||||
}
|
||||
total.Buckets[bound] = count + addition
|
||||
}
|
||||
merged[name] = total
|
||||
}
|
||||
}
|
||||
applyHistograms(result, merged)
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyHistograms(result *model.MonitorVLLM, histograms map[string]histogram) {
|
||||
for _, item := range []struct {
|
||||
name string
|
||||
mean **float64
|
||||
quantiles []**float64
|
||||
}{
|
||||
{"time_to_first_token_seconds", &result.TimeToFirstToken, []**float64{&result.TimeToFirstTokenP50, &result.TimeToFirstTokenP90, &result.TimeToFirstTokenP95, &result.TimeToFirstTokenP99}},
|
||||
{"inter_token_latency_seconds", &result.TimePerOutputToken, []**float64{&result.TimePerOutputTokenP50, &result.TimePerOutputTokenP90, &result.TimePerOutputTokenP95, &result.TimePerOutputTokenP99}},
|
||||
{"e2e_request_latency_seconds", &result.RequestLatency, []**float64{&result.RequestLatencyP50, &result.RequestLatencyP90, &result.RequestLatencyP95, &result.RequestLatencyP99}},
|
||||
{"request_prefill_time_seconds", &result.PrefillTime, nil},
|
||||
{"request_decode_time_seconds", &result.DecodeTime, nil},
|
||||
} {
|
||||
histogram, ok := histograms[item.name]
|
||||
if !ok || histogram.Count <= 0 {
|
||||
continue
|
||||
}
|
||||
mean := histogram.Sum / histogram.Count
|
||||
*item.mean = &mean
|
||||
if len(item.quantiles) == 0 {
|
||||
continue
|
||||
}
|
||||
quantiles := histogramQuantiles(histogram.Buckets)
|
||||
for i, target := range item.quantiles {
|
||||
*target = quantiles[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func counterDelta(current, previous map[string]float64) *float64 {
|
||||
if len(current) == 0 || len(current) != len(previous) {
|
||||
return nil
|
||||
}
|
||||
value := 0.0
|
||||
for label, sample := range current {
|
||||
before, ok := previous[label]
|
||||
if !ok || sample < before {
|
||||
return nil
|
||||
}
|
||||
value += sample - before
|
||||
}
|
||||
if math.IsInf(value, 0) || math.IsNaN(value) {
|
||||
return nil
|
||||
}
|
||||
return &value
|
||||
}
|
||||
|
||||
func bucketDeltas(current, previous map[string]float64, count float64) map[string]float64 {
|
||||
if len(current) == 0 || len(current) != len(previous) {
|
||||
return nil
|
||||
}
|
||||
groups := map[string]map[string]float64{}
|
||||
for labels, sample := range current {
|
||||
before, ok := previous[labels]
|
||||
if !ok || sample < before {
|
||||
return nil
|
||||
}
|
||||
bound := ""
|
||||
var identity []string
|
||||
for _, label := range re.GetRegex(re.VLLMMetricLabelPattern).FindAllStringSubmatch(labels, -1) {
|
||||
if label[1] == "le" {
|
||||
raw, err := strconv.Unquote(label[2])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
upper, err := strconv.ParseFloat(raw, 64)
|
||||
if err != nil || math.IsNaN(upper) || upper < 0 {
|
||||
return nil
|
||||
}
|
||||
bound = strconv.FormatFloat(upper, 'g', -1, 64)
|
||||
} else {
|
||||
identity = append(identity, label[1]+"="+label[2])
|
||||
}
|
||||
}
|
||||
if bound == "" {
|
||||
return nil
|
||||
}
|
||||
group := strings.Join(identity, ",")
|
||||
if groups[group] == nil {
|
||||
groups[group] = map[string]float64{}
|
||||
}
|
||||
if _, exists := groups[group][bound]; exists {
|
||||
return nil
|
||||
}
|
||||
groups[group][bound] = sample - before
|
||||
}
|
||||
result := map[string]float64{}
|
||||
for _, buckets := range groups {
|
||||
if _, ok := buckets["+Inf"]; !ok || len(buckets) < 2 {
|
||||
return nil
|
||||
}
|
||||
if len(result) > 0 && len(result) != len(buckets) {
|
||||
return nil
|
||||
}
|
||||
for bound := range result {
|
||||
if _, ok := buckets[bound]; !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
bounds := make([]float64, 0, len(buckets))
|
||||
for bound := range buckets {
|
||||
value, _ := strconv.ParseFloat(bound, 64)
|
||||
bounds = append(bounds, value)
|
||||
}
|
||||
sort.Float64s(bounds)
|
||||
previousCount := 0.0
|
||||
for _, bound := range bounds {
|
||||
key := strconv.FormatFloat(bound, 'g', -1, 64)
|
||||
value := buckets[key]
|
||||
if value < previousCount {
|
||||
if previousCount-value > 1e-12*(previousCount+value) {
|
||||
return nil
|
||||
}
|
||||
value = previousCount
|
||||
}
|
||||
result[key] += value
|
||||
previousCount = value
|
||||
}
|
||||
}
|
||||
if math.Abs(result["+Inf"]-count) > 1e-12*(result["+Inf"]+count) {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func histogramQuantiles(buckets map[string]float64) [4]*float64 {
|
||||
var quantiles [4]*float64
|
||||
if len(buckets) < 2 || buckets["+Inf"] <= 0 {
|
||||
return quantiles
|
||||
}
|
||||
bounds := make([]float64, 0, len(buckets)-1)
|
||||
for bound := range buckets {
|
||||
value, err := strconv.ParseFloat(bound, 64)
|
||||
if err != nil || math.IsNaN(value) || value < 0 {
|
||||
return quantiles
|
||||
}
|
||||
if !math.IsInf(value, 1) {
|
||||
bounds = append(bounds, value)
|
||||
}
|
||||
}
|
||||
if len(bounds) == 0 {
|
||||
return quantiles
|
||||
}
|
||||
sort.Float64s(bounds)
|
||||
for i, q := range [...]float64{0.5, 0.9, 0.95, 0.99} {
|
||||
rank := q * buckets["+Inf"]
|
||||
lower, before := 0.0, 0.0
|
||||
value := bounds[len(bounds)-1]
|
||||
for _, upper := range bounds {
|
||||
count := buckets[strconv.FormatFloat(upper, 'g', -1, 64)]
|
||||
if count >= rank && count > before {
|
||||
value = lower + (upper-lower)*(rank-before)/(count-before)
|
||||
break
|
||||
}
|
||||
lower, before = upper, count
|
||||
}
|
||||
quantiles[i] = &value
|
||||
}
|
||||
return quantiles
|
||||
}
|
||||
@@ -33,16 +33,29 @@ type discoveryInfo struct {
|
||||
}
|
||||
|
||||
type DeviceLevelMetric struct {
|
||||
MetricsType string `json:"metrics_type"`
|
||||
Value float64 `json:"value"`
|
||||
MetricsType string `json:"metrics_type"`
|
||||
Value *float64 `json:"value"`
|
||||
}
|
||||
|
||||
type TileMetrics struct {
|
||||
TileID int `json:"tile_id"`
|
||||
DataList []DeviceLevelMetric `json:"data_list"`
|
||||
}
|
||||
|
||||
type TileStats struct {
|
||||
TileID int `json:"tileID"`
|
||||
Stats Stats `json:"stats"`
|
||||
}
|
||||
|
||||
type DeviceStats struct {
|
||||
TileLevel []TileMetrics `json:"tile_level"`
|
||||
DeviceID int `json:"device_id"`
|
||||
DeviceLevel []DeviceLevelMetric `json:"device_level"`
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
Type string `json:"type"`
|
||||
DriverVersion string `json:"driverVersion"`
|
||||
|
||||
@@ -50,12 +63,17 @@ type Info struct {
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
Tiles []TileStats `json:"tiles"`
|
||||
ProcessStatus string `json:"processStatus"`
|
||||
|
||||
Basic Basic `json:"basic"`
|
||||
Stats Stats `json:"stats"`
|
||||
Processes []Process `json:"processes"`
|
||||
}
|
||||
|
||||
type Basic struct {
|
||||
UUID string `json:"uuid"`
|
||||
|
||||
DeviceID int `json:"deviceID"`
|
||||
DeviceName string `json:"deviceName"`
|
||||
VendorName string `json:"vendorName"`
|
||||
@@ -66,6 +84,14 @@ type Basic struct {
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
MediaUtil string `json:"mediaUtil"`
|
||||
ComputeUtil string `json:"computeUtil"`
|
||||
CopyUtil string `json:"copyUtil"`
|
||||
MediaFrequency string `json:"mediaFrequency"`
|
||||
MemoryTemperature string `json:"memoryTemperature"`
|
||||
|
||||
MemoryBandwidthUtil string `json:"memoryBandwidthUtil"`
|
||||
|
||||
Power string `json:"power"`
|
||||
GPUUtil string `json:"gpuUtil"`
|
||||
Frequency string `json:"frequency"`
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
@@ -51,12 +52,15 @@ func (c Client) LoadInfoContext(ctx context.Context) (*Info, error) {
|
||||
|
||||
processData, err := cmdMgr.RunWithStdout(xpuSMICommand, "ps", "-j")
|
||||
if err != nil {
|
||||
global.LOG.Warnf("calling xpu-smi ps failed, process information will be omitted: %v", err)
|
||||
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi ps: %v", err))
|
||||
} else {
|
||||
var psList DeviceUtilByProcList
|
||||
if err := json.Unmarshal([]byte(processData), &psList); err != nil {
|
||||
global.LOG.Warnf("processData json unmarshal failed, process information will be omitted: %v", err)
|
||||
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi ps output: %v", err))
|
||||
} else {
|
||||
for i := range res.Devices {
|
||||
res.Devices[i].ProcessStatus = "ok"
|
||||
}
|
||||
for _, ps := range psList.DeviceUtilByProcList {
|
||||
process := Process{
|
||||
PID: ps.ProcessID,
|
||||
@@ -87,7 +91,9 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
|
||||
defer wg.Done()
|
||||
|
||||
xpu := Device{
|
||||
ProcessStatus: "unavailable",
|
||||
Basic: Basic{
|
||||
UUID: device.UUID,
|
||||
DeviceID: device.DeviceID,
|
||||
DeviceName: device.DeviceName,
|
||||
VendorName: device.VendorName,
|
||||
@@ -115,13 +121,19 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
|
||||
wgCmd.Wait()
|
||||
|
||||
if xpuErr != nil {
|
||||
global.LOG.Errorf("calling xpu-smi discovery failed for device %d, %v", device.DeviceID, xpuErr)
|
||||
mu.Lock()
|
||||
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi device %d: %v", device.DeviceID, xpuErr))
|
||||
res.Devices = append(res.Devices, xpu)
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
var info discoveryDevice
|
||||
if err := json.Unmarshal([]byte(xpuData), &info); err != nil {
|
||||
global.LOG.Errorf("xpuData json unmarshal failed for device %d, err: %v", device.DeviceID, err)
|
||||
mu.Lock()
|
||||
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi device %d output: %v", device.DeviceID, err))
|
||||
res.Devices = append(res.Devices, xpu)
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -142,13 +154,22 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
|
||||
}
|
||||
|
||||
if statsErr != nil {
|
||||
global.LOG.Warnf("calling xpu-smi stats failed for device %d, metrics will be omitted: %v", device.DeviceID, statsErr)
|
||||
mu.Lock()
|
||||
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi stats %d: %v", device.DeviceID, statsErr))
|
||||
mu.Unlock()
|
||||
} else {
|
||||
var stats DeviceStats
|
||||
if err := json.Unmarshal([]byte(statsData), &stats); err != nil {
|
||||
global.LOG.Warnf("statsData json unmarshal failed for device %d, metrics will be omitted: %v", device.DeviceID, err)
|
||||
mu.Lock()
|
||||
res.Warnings = append(res.Warnings, fmt.Sprintf("xpu-smi stats %d output: %v", device.DeviceID, err))
|
||||
mu.Unlock()
|
||||
} else {
|
||||
loadStats(&xpu.Stats, stats.DeviceLevel)
|
||||
for _, tile := range stats.TileLevel {
|
||||
item := TileStats{TileID: tile.TileID}
|
||||
loadStats(&item.Stats, tile.DataList)
|
||||
xpu.Tiles = append(xpu.Tiles, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,19 +183,38 @@ func (c Client) loadDeviceInfo(ctx context.Context, device discoveryDevice, wg *
|
||||
|
||||
func loadStats(stats *Stats, metrics []DeviceLevelMetric) {
|
||||
for _, stat := range metrics {
|
||||
if stat.Value == nil || math.IsNaN(*stat.Value) || math.IsInf(*stat.Value, 0) {
|
||||
continue
|
||||
}
|
||||
value := *stat.Value
|
||||
if value < 0 && stat.MetricsType != "XPUM_STATS_GPU_CORE_TEMPERATURE" && stat.MetricsType != "XPUM_STATS_MEMORY_TEMPERATURE" {
|
||||
continue
|
||||
}
|
||||
switch stat.MetricsType {
|
||||
case "XPUM_STATS_MEMORY_TEMPERATURE":
|
||||
stats.MemoryTemperature = fmt.Sprintf("%.1f°C", value)
|
||||
case "XPUM_STATS_ENGINE_GROUP_COMPUTE_ALL_UTILIZATION":
|
||||
stats.ComputeUtil = fmt.Sprintf("%.1f%%", value)
|
||||
case "XPUM_STATS_ENGINE_GROUP_MEDIA_ALL_UTILIZATION":
|
||||
stats.MediaUtil = fmt.Sprintf("%.1f%%", value)
|
||||
case "XPUM_STATS_ENGINE_GROUP_COPY_ALL_UTILIZATION":
|
||||
stats.CopyUtil = fmt.Sprintf("%.1f%%", value)
|
||||
case "XPUM_STATS_MEDIA_ENGINE_FREQUENCY":
|
||||
stats.MediaFrequency = fmt.Sprintf("%.1fMHz", value)
|
||||
case "XPUM_STATS_POWER":
|
||||
stats.Power = fmt.Sprintf("%.1fW", stat.Value)
|
||||
stats.Power = fmt.Sprintf("%.1fW", value)
|
||||
case "XPUM_STATS_GPU_UTILIZATION":
|
||||
stats.GPUUtil = fmt.Sprintf("%.1f%%", stat.Value)
|
||||
stats.GPUUtil = fmt.Sprintf("%.1f%%", value)
|
||||
case "XPUM_STATS_GPU_FREQUENCY":
|
||||
stats.Frequency = fmt.Sprintf("%.1fMHz", stat.Value)
|
||||
stats.Frequency = fmt.Sprintf("%.1fMHz", value)
|
||||
case "XPUM_STATS_GPU_CORE_TEMPERATURE":
|
||||
stats.Temperature = fmt.Sprintf("%.1f°C", stat.Value)
|
||||
stats.Temperature = fmt.Sprintf("%.1f°C", value)
|
||||
case "XPUM_STATS_MEMORY_USED":
|
||||
stats.MemoryUsed = fmt.Sprintf("%.1f MiB", stat.Value)
|
||||
case "XPUM_STATS_MEMORY_UTILIZATION", "XPUM_STATS_MEMORY_BANDWIDTH", "XPUM_STATS_MEMORY_BANDWIDTH_UTILIZATION":
|
||||
stats.MemoryUtil = fmt.Sprintf("%.1f%%", stat.Value)
|
||||
stats.MemoryUsed = fmt.Sprintf("%.1f MiB", value)
|
||||
case "XPUM_STATS_MEMORY_UTILIZATION":
|
||||
stats.MemoryUtil = fmt.Sprintf("%.1f%%", value)
|
||||
case "XPUM_STATS_MEMORY_BANDWIDTH", "XPUM_STATS_MEMORY_BANDWIDTH_UTILIZATION":
|
||||
stats.MemoryBandwidthUtil = fmt.Sprintf("%.1f%%", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ import (
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
var cronJobAlertTypes = []string{"shell", "app", "website", "database", "directory", "log", "snapshot", "curl", "cutWebsiteLog", "clean", "ntp"}
|
||||
var cronJobAlertTypes = []string{"shell", "app", "website", "database", "directory", "log", "snapshot", "curl", "cutWebsiteLog", "clean", "ntp", "syncIpGroup", "cleanLog"}
|
||||
|
||||
func CreateTaskScanEmailAlertLog(alert dto.AlertDTO, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo, emailConfig model.AlertConfig) error {
|
||||
params := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))
|
||||
params := CreateTaskAlertParams(pushAlert)
|
||||
alertDetail := ProcessAlertDetail(alert, pushAlert.TaskName, params, method)
|
||||
alertRule := ProcessAlertRule(alert)
|
||||
create.AlertRule = alertRule
|
||||
@@ -76,14 +76,14 @@ func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params [
|
||||
Encryption: emailInfo.Encryption,
|
||||
Recipient: emailInfo.Recipient,
|
||||
}
|
||||
content := GetSendContent(alert.Type, params, agentInfo)
|
||||
content := GetAlertLogContent(create, alert, params, agentInfo)
|
||||
if content == "" {
|
||||
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
|
||||
}
|
||||
msg := email.EmailMessage{
|
||||
Subject: i18n.GetMsgByKey("PanelAlertTitle"),
|
||||
Body: content,
|
||||
IsHTML: true,
|
||||
IsHTML: GetCronJobType(alert.Type) != "cronJob",
|
||||
}
|
||||
|
||||
if err = email.SendMail(smtpConfig, msg, transport); err != nil {
|
||||
@@ -110,7 +110,7 @@ func CreateBarkAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params []
|
||||
return SaveAlertLog(create, &alertLog)
|
||||
}
|
||||
|
||||
content := GetSendContent(alert.Type, params, agentInfo)
|
||||
content := GetAlertLogContent(create, alert, params, agentInfo)
|
||||
if content == "" {
|
||||
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
|
||||
}
|
||||
@@ -268,7 +268,7 @@ func ProcessAlertDetail(alert dto.AlertDTO, project string, params []dto.Param,
|
||||
alertDetail := dto.AlertDetail{
|
||||
Type: GetCronJobType(alert.Type),
|
||||
SubType: alert.Type,
|
||||
Title: alert.Title,
|
||||
Title: TaskAlertTitle(alert.Type, alert.Title, project, params),
|
||||
Method: method,
|
||||
Project: project,
|
||||
Params: params,
|
||||
@@ -281,6 +281,14 @@ func ProcessAlertDetail(alert dto.AlertDTO, project string, params []dto.Param,
|
||||
return string(marshal)
|
||||
}
|
||||
|
||||
func TaskAlertTitle(alertType, title, project string, params []dto.Param) string {
|
||||
if GetCronJobType(alertType) != "cronJob" || CronJobAlertResultFromParams(params) != CronJobAlertSuccess {
|
||||
return title
|
||||
}
|
||||
name := cronJobTaskName(project, params)
|
||||
return i18n.GetMsgWithMap("TaskSuccess", map[string]interface{}{"name": name})
|
||||
}
|
||||
|
||||
func ProcessAlertRule(alert dto.AlertDTO) string {
|
||||
marshal, err := json.Marshal(alert)
|
||||
if err != nil {
|
||||
@@ -324,6 +332,10 @@ func GetCronJobTypeName(cronJobType string) string {
|
||||
module = "系统快照"
|
||||
case "ntp":
|
||||
module = "同步服务器时间"
|
||||
case "syncIpGroup":
|
||||
module = "同步 IP 组"
|
||||
case "cleanLog":
|
||||
module = "清理日志"
|
||||
default:
|
||||
}
|
||||
return module
|
||||
@@ -444,6 +456,30 @@ func isWithinTimeRange(savedTimeString string) bool {
|
||||
}
|
||||
|
||||
func GetSendContent(alertType string, params []dto.Param, agentInfo *dto.AgentInfo) string {
|
||||
return GetAlertDetailContent(dto.AlertDetail{Type: alertType, Params: params}, agentInfo)
|
||||
}
|
||||
|
||||
func GetAlertLogContent(create dto.AlertLogCreate, alert dto.AlertDTO, params []dto.Param, agentInfo *dto.AgentInfo) string {
|
||||
detail := dto.AlertDetail{Type: alert.Type, Params: params}
|
||||
var stored dto.AlertDetail
|
||||
if json.Unmarshal([]byte(create.AlertDetail), &stored) == nil {
|
||||
detail = stored
|
||||
if detail.Type == "" {
|
||||
detail.Type = alert.Type
|
||||
}
|
||||
if detail.Params == nil {
|
||||
detail.Params = params
|
||||
}
|
||||
}
|
||||
return GetAlertDetailContent(detail, agentInfo)
|
||||
}
|
||||
|
||||
func GetAlertDetailContent(detail dto.AlertDetail, agentInfo *dto.AgentInfo) string {
|
||||
alertType := detail.SubType
|
||||
if alertType == "" {
|
||||
alertType = detail.Type
|
||||
}
|
||||
params := detail.Params
|
||||
switch GetCronJobType(alertType) {
|
||||
case "ssl":
|
||||
return i18n.GetMsgWithMap("SSLAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "day": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
|
||||
@@ -464,7 +500,12 @@ func GetSendContent(alertType string, params []dto.Param, agentInfo *dto.AgentIn
|
||||
case "disk":
|
||||
return i18n.GetMsgWithMap("DiskUsedAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "used": getValueByIndex(params, "2"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
|
||||
case "cronJob":
|
||||
return i18n.GetMsgWithMap("CronJobFailedAlert", map[string]interface{}{"name": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
|
||||
messageKey := "CronJobFailedAlert"
|
||||
if CronJobAlertResultFromParams(params) == CronJobAlertSuccess {
|
||||
messageKey = "CronJobSuccessAlert"
|
||||
}
|
||||
name := cronJobTaskName(detail.Project, params)
|
||||
return i18n.GetMsgWithMap(messageKey, map[string]interface{}{"name": name, "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
|
||||
case "clams":
|
||||
return i18n.GetMsgWithMap("ClamAlert", map[string]interface{}{"num": getValueByIndex(params, "1"), "node": getNodeName(agentInfo), "ip": getNodeIp(agentInfo)})
|
||||
case "panelLogin":
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package alert
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
)
|
||||
|
||||
const (
|
||||
CronJobAlertFailed = "failed"
|
||||
CronJobAlertSuccess = "success"
|
||||
CronJobAlertBoth = "both"
|
||||
)
|
||||
|
||||
func cronJobAlertParams(advanced string) (map[string]json.RawMessage, error) {
|
||||
params := make(map[string]json.RawMessage)
|
||||
if strings.TrimSpace(advanced) != "" {
|
||||
if err := json.Unmarshal([]byte(advanced), ¶ms); err != nil {
|
||||
return nil, fmt.Errorf("invalid cronjob alert advanced parameters: %w", err)
|
||||
}
|
||||
}
|
||||
if params == nil {
|
||||
params = make(map[string]json.RawMessage)
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func CronJobAlertTriggerMode(advanced string) (string, error) {
|
||||
params, err := cronJobAlertParams(advanced)
|
||||
if err != nil {
|
||||
return CronJobAlertFailed, err
|
||||
}
|
||||
mode := CronJobAlertFailed
|
||||
if raw, ok := params["alertTriggerMode"]; ok {
|
||||
if err := json.Unmarshal(raw, &mode); err != nil {
|
||||
return CronJobAlertFailed, fmt.Errorf("invalid cronjob alert trigger mode: %w", err)
|
||||
}
|
||||
}
|
||||
switch mode {
|
||||
case CronJobAlertFailed, CronJobAlertSuccess, CronJobAlertBoth:
|
||||
return mode, nil
|
||||
default:
|
||||
return CronJobAlertFailed, fmt.Errorf("invalid cronjob alert trigger mode %q", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func MergeCronJobAlertParams(previous, incoming string) (string, error) {
|
||||
params, err := cronJobAlertParams(previous)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
updates, err := cronJobAlertParams(incoming)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for key, value := range updates {
|
||||
params[key] = value
|
||||
}
|
||||
if _, ok := params["alertTriggerMode"]; !ok {
|
||||
params["alertTriggerMode"] = json.RawMessage(`"failed"`)
|
||||
}
|
||||
data, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := CronJobAlertTriggerMode(string(data)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func MatchCronJobAlertResult(advanced, result string) bool {
|
||||
mode, err := CronJobAlertTriggerMode(advanced)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if result == "" {
|
||||
result = CronJobAlertFailed
|
||||
}
|
||||
if result != CronJobAlertFailed && result != CronJobAlertSuccess {
|
||||
return false
|
||||
}
|
||||
return mode == CronJobAlertBoth || mode == result
|
||||
}
|
||||
|
||||
func CreateTaskAlertParams(pushAlert dto.PushAlert) []dto.Param {
|
||||
params := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))
|
||||
if GetCronJobType(pushAlert.AlertType) != "cronJob" {
|
||||
return params
|
||||
}
|
||||
params = append(params, CreateCronJobResultParam(pushAlert.Result))
|
||||
return params
|
||||
}
|
||||
|
||||
func cronJobTaskName(project string, params []dto.Param) string {
|
||||
if project != "" {
|
||||
return project
|
||||
}
|
||||
if name := getValueByIndex(params, "taskName"); name != "" {
|
||||
return name
|
||||
}
|
||||
return getValueByIndex(params, "1")
|
||||
}
|
||||
|
||||
func CreateCronJobResultParam(result string) dto.Param {
|
||||
value := "失败"
|
||||
if result == CronJobAlertSuccess {
|
||||
value = "成功"
|
||||
}
|
||||
return dto.Param{Index: "2", Key: "result", Value: value}
|
||||
}
|
||||
|
||||
func CronJobAlertResultFromParams(params []dto.Param) string {
|
||||
for _, param := range params {
|
||||
if param.Index == "result" {
|
||||
if param.Value == CronJobAlertSuccess {
|
||||
return CronJobAlertSuccess
|
||||
}
|
||||
return CronJobAlertFailed
|
||||
}
|
||||
}
|
||||
for _, param := range params {
|
||||
if param.Index == "2" && param.Key == "result" && param.Value == "成功" {
|
||||
return CronJobAlertSuccess
|
||||
}
|
||||
}
|
||||
return CronJobAlertFailed
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func CreateTaskScanCustomWebhookAlertLog(
|
||||
transport *http.Transport,
|
||||
agentInfo *dto.AgentInfo,
|
||||
) error {
|
||||
params := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))
|
||||
params := CreateTaskAlertParams(pushAlert)
|
||||
alertInfo := info
|
||||
alertInfo.Type = alertType
|
||||
create.Type = GetCronJobType(alertType)
|
||||
@@ -99,7 +99,7 @@ func customWebhookTemplateData(rawDetail string, agentInfo *dto.AgentInfo, occur
|
||||
if businessType == "" {
|
||||
return webhook_sender.TemplateData{}, errors.New("resolve custom webhook alert detail failed")
|
||||
}
|
||||
content := GetSendContent(businessType, detail.Params, agentInfo)
|
||||
content := GetAlertDetailContent(detail, agentInfo)
|
||||
if content == "" {
|
||||
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": detail.Title})
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ func PushAlert(pushAlert dto.PushAlert) error {
|
||||
}
|
||||
var alert dto.AlertDTO
|
||||
_ = copier.Copy(&alert, &alertInfo)
|
||||
if alertUtil.GetCronJobType(pushAlert.AlertType) == "cronJob" && !alertUtil.MatchCronJobAlertResult(alert.AdvancedParams, pushAlert.Result) {
|
||||
return nil
|
||||
}
|
||||
|
||||
methods := strings.Split(alert.Method, ",")
|
||||
for _, m := range methods {
|
||||
@@ -126,7 +129,7 @@ func sendAlert(alertRepo repo.IAlertRepo, alert dto.AlertDTO, pushAlert dto.Push
|
||||
}
|
||||
transport := xpack.MultiNodeProvider.LoadRequestTransport()
|
||||
agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
|
||||
params := alertUtil.CreateAlertParams(alertUtil.GetCronJobTypeName(pushAlert.Param))
|
||||
params := alertUtil.CreateTaskAlertParams(pushAlert)
|
||||
alertDetail := alertUtil.ProcessAlertDetail(alert, pushAlert.TaskName, params, constant.Bark)
|
||||
alertRule := alertUtil.ProcessAlertRule(alert)
|
||||
create.AlertRule = alertRule
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
VLLMMetricSamplePattern = `^([a-zA-Z_:][a-zA-Z0-9_:]*)(\{(?:[^"\\]|"(?:\\.|[^"\\])*")*\})?\s+(\S+)(?:\s+.*)?$`
|
||||
VLLMMetricLabelPattern = `([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*("(?:[^"\\]|\\.)*")`
|
||||
NumberAlphaPattern = `(\d+)([A-Za-z]+)`
|
||||
ComposeDisallowedCharsPattern = `[^a-z0-9_-]+`
|
||||
ComposeNamePattern = `^[a-z0-9][a-z0-9_-]{0,255}$`
|
||||
@@ -61,6 +63,8 @@ var regexMap = make(map[string]*regexp.Regexp)
|
||||
|
||||
func Init() {
|
||||
patterns := []string{
|
||||
VLLMMetricSamplePattern,
|
||||
VLLMMetricLabelPattern,
|
||||
NumberAlphaPattern,
|
||||
ComposeDisallowedCharsPattern,
|
||||
ComposeNamePattern,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
@@ -25,6 +26,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/core/utils/req_helper"
|
||||
upgradeUtil "github.com/1Panel-dev/1Panel/core/utils/upgrade"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
type serviceInfo struct {
|
||||
@@ -299,7 +301,7 @@ func (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) {
|
||||
docSource, _ := settingRepo.GetValueByKey("DocSource")
|
||||
lang, _ := settingRepo.GetValueByKey("Language")
|
||||
var notes []dto.ReleasesNotes
|
||||
url := "https://1panel.cn/docs/v2/search/search_index.json"
|
||||
url := "https://docs.fit2cloud.com/1panel/changelog/"
|
||||
useIntlDocs := false
|
||||
lang = strings.ToLower(strings.TrimSpace(lang))
|
||||
if docSource == "withByRegion" {
|
||||
@@ -315,6 +317,12 @@ func (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) {
|
||||
return notes, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if !useIntlDocs {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return notes, fmt.Errorf("load release notes failed: HTTP %d", resp.StatusCode)
|
||||
}
|
||||
return parseReleaseHTML(resp.Body)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return notes, err
|
||||
@@ -336,6 +344,89 @@ func (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) {
|
||||
return notes, nil
|
||||
}
|
||||
|
||||
func parseReleaseHTML(reader io.Reader) ([]dto.ReleasesNotes, error) {
|
||||
doc, err := html.Parse(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var article *html.Node
|
||||
for node := range doc.Descendants() {
|
||||
if node.Type == html.ElementNode && node.Data == "article" {
|
||||
article = node
|
||||
break
|
||||
}
|
||||
}
|
||||
if article == nil {
|
||||
return nil, fmt.Errorf("release notes article not found")
|
||||
}
|
||||
textContent := func(node *html.Node) string {
|
||||
var text strings.Builder
|
||||
for child := range node.Descendants() {
|
||||
if child.Type == html.TextNode {
|
||||
text.WriteString(child.Data)
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(strings.ReplaceAll(text.String(), "\u200b", ""))
|
||||
}
|
||||
var notes []dto.ReleasesNotes
|
||||
for heading := range article.Descendants() {
|
||||
if heading.Type != html.ElementNode || heading.Data != "h3" {
|
||||
continue
|
||||
}
|
||||
version := textContent(heading)
|
||||
if !strings.HasPrefix(version, "v") {
|
||||
continue
|
||||
}
|
||||
item := dto.ReleasesNotes{Version: version}
|
||||
var content strings.Builder
|
||||
section := ""
|
||||
for node := heading.NextSibling; node != nil; node = node.NextSibling {
|
||||
if node.Type != html.ElementNode {
|
||||
continue
|
||||
}
|
||||
if node.Data == "h1" || node.Data == "h2" || node.Data == "h3" {
|
||||
break
|
||||
}
|
||||
if item.CreatedAt == "" {
|
||||
date := textContent(node)
|
||||
if _, err := time.Parse("2006年1月2日", date); node.Data != "p" || err != nil {
|
||||
return nil, fmt.Errorf("release date not found for %s", version)
|
||||
}
|
||||
item.CreatedAt = date
|
||||
continue
|
||||
}
|
||||
if node.Data == "p" {
|
||||
section = textContent(node)
|
||||
}
|
||||
for child := range node.Descendants() {
|
||||
if child.Type != html.ElementNode || child.Data != "li" {
|
||||
continue
|
||||
}
|
||||
switch section {
|
||||
case "新增功能":
|
||||
item.NewCount++
|
||||
case "功能优化":
|
||||
item.OptimizationCount++
|
||||
case "问题修复":
|
||||
item.FixCount++
|
||||
}
|
||||
}
|
||||
if err := html.Render(&content, node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if item.CreatedAt == "" || content.Len() == 0 {
|
||||
return nil, fmt.Errorf("release notes content not found for %s", version)
|
||||
}
|
||||
item.Content = content.String()
|
||||
notes = append(notes, item)
|
||||
}
|
||||
if len(notes) == 0 {
|
||||
return nil, fmt.Errorf("release notes versions not found")
|
||||
}
|
||||
return notes, nil
|
||||
}
|
||||
|
||||
func analyzeDoc(version, content string) dto.ReleasesNotes {
|
||||
var item dto.ReleasesNotes
|
||||
parts := strings.Split(content, "<p>")
|
||||
|
||||
@@ -2486,6 +2486,17 @@
|
||||
"formatZH": "删除虚拟机 [name]",
|
||||
"formatEN": "delete VM [name]"
|
||||
},
|
||||
"/core/xpack/vms/disks/resize": {
|
||||
"bodyKeys": [
|
||||
"name",
|
||||
"target",
|
||||
"sizeGiB"
|
||||
],
|
||||
"paramKeys": [],
|
||||
"beforeFunctions": [],
|
||||
"formatZH": "扩容虚拟机 [name] 磁盘 [target] 至 [sizeGiB] GiB",
|
||||
"formatEN": "expand VM [name] disk [target] to [sizeGiB] GiB"
|
||||
},
|
||||
"/core/xpack/vms/environment/enable": {
|
||||
"bodyKeys": [],
|
||||
"paramKeys": [],
|
||||
|
||||
+6
-29
@@ -1,19 +1,14 @@
|
||||
module github.com/1Panel-dev/1Panel/core
|
||||
|
||||
go 1.26.1
|
||||
go 1.26.6
|
||||
|
||||
require (
|
||||
github.com/beevik/etree v1.7.0
|
||||
github.com/coreos/go-oidc/v3 v3.20.0
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/crewjam/saml v0.5.1
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/gin-contrib/gzip v1.2.6
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.6
|
||||
github.com/go-jose/go-jose/v4 v4.1.4
|
||||
github.com/go-ldap/ldap/v3 v3.4.14
|
||||
github.com/go-playground/validator/v10 v10.30.3
|
||||
github.com/go-resty/resty/v2 v2.17.2
|
||||
github.com/go-webauthn/webauthn v0.17.4
|
||||
@@ -21,16 +16,13 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/johnfercher/maroto/v2 v2.4.1
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.11
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/russellhaering/goxmldsig v1.6.1
|
||||
github.com/shirou/gopsutil/v4 v4.26.7
|
||||
github.com/shirou/gopsutil/v4 v4.26.8
|
||||
github.com/sirupsen/logrus v1.10.2
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/soheilhy/cmux v0.1.5
|
||||
@@ -39,10 +31,9 @@ require (
|
||||
github.com/swaggo/files/v2 v2.0.2
|
||||
github.com/swaggo/swag v1.16.6
|
||||
github.com/xlzd/gotp v0.1.0
|
||||
golang.org/x/crypto v0.55.0
|
||||
golang.org/x/image v0.41.0
|
||||
golang.org/x/crypto v0.56.0
|
||||
golang.org/x/image v0.45.0
|
||||
golang.org/x/net v0.58.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/term v0.45.0
|
||||
golang.org/x/text v0.41.0
|
||||
@@ -52,22 +43,17 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.1.1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.10.2 // indirect
|
||||
github.com/f-amaral/go-async v0.3.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/gin-contrib/sse v1.1.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.23.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
@@ -85,31 +71,22 @@ require (
|
||||
github.com/go-webauthn/x v0.2.6 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/google/go-tpm v0.9.8 // indirect
|
||||
github.com/hhrutter/lzw v1.0.0 // indirect
|
||||
github.com/hhrutter/pkcs7 v0.2.0 // indirect
|
||||
github.com/hhrutter/tiff v1.0.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/johnfercher/go-tree v1.1.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.21 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/pdfcpu/pdfcpu v0.11.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/phpdave11/gofpdf v1.4.3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.1 // indirect
|
||||
@@ -129,9 +106,9 @@ require (
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.1 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/arch v0.26.0 // indirect
|
||||
golang.org/x/mod v0.38.0 // indirect
|
||||
golang.org/x/mod v0.40.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/tools v0.48.0 // indirect
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
modernc.org/libc v1.72.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
|
||||
+10
-86
@@ -1,33 +1,18 @@
|
||||
github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw=
|
||||
github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/beevik/etree v1.7.0 h1:xjBk9O4p4x7D1YajePjfLzdaFC4/uYUENA7P0pv6gXA=
|
||||
github.com/beevik/etree v1.7.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
|
||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
|
||||
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/coreos/go-oidc/v3 v3.20.0 h1:EtE0WIBHk03N+DqGkY4+UONzzZHk7amKt6IyNd7OsZE=
|
||||
github.com/coreos/go-oidc/v3 v3.20.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/crewjam/saml v0.5.1 h1:g+mfp0CrLuLRZCK793PgJcZeg5dS/0CDwoeAX2zcwNI=
|
||||
github.com/crewjam/saml v0.5.1/go.mod h1:r0fDkmFe5URDgPrmtH0IYokva6fac3AUdstiPhyEolQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -35,8 +20,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE=
|
||||
github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/f-amaral/go-async v0.3.0 h1:h4kLsX7aKfdWaHvV0lf+/EE3OIeCzyeDYJDb/vDZUyg=
|
||||
github.com/f-amaral/go-async v0.3.0/go.mod h1:Hz5Qr6DAWpbTTUjytnrg1WIsDgS7NtOei5y8SipYS7U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
@@ -55,14 +38,8 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.6 h1:VtX+l1Stj2v5RGubVQk0LS/8EPGXR+ldcOyCmlmKoyg=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.6/go.mod h1:PZpedQc4tWaxn6kvXicwhinh3L0seLpMc5ReKRX5id4=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.23.0 h1:c25HFTJ6uWGmoe5BQI6p72p4o7KnlWYsy1MeFlAumsw=
|
||||
@@ -112,8 +89,6 @@ github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -124,53 +99,24 @@ github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=
|
||||
github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo=
|
||||
github.com/hhrutter/pkcs7 v0.2.0 h1:i4HN2XMbGQpZRnKBLsUwO3dSckzgX142TNqY/KfXg+I=
|
||||
github.com/hhrutter/pkcs7 v0.2.0/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE=
|
||||
github.com/hhrutter/tiff v1.0.2 h1:7H3FQQpKu/i5WaSChoD1nnJbGx4MxU5TlNqqpxw55z8=
|
||||
github.com/hhrutter/tiff v1.0.2/go.mod h1:pcOeuK5loFUE7Y/WnzGw20YxUdnqjY1P0Jlcieb/cCw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/johnfercher/go-tree v1.1.0 h1:L0Fs5jLR1uA2e/CwfHjNdO/Lt4IGQ46QgxarAC1yeXs=
|
||||
github.com/johnfercher/go-tree v1.1.0/go.mod h1:DUO6QkXIFh1K7jeGBIkLCZaeUgnkdQAsB64FDSoHswg=
|
||||
github.com/johnfercher/maroto/v2 v2.4.1 h1:3B5gJQBA5NW1w41uDOr7Aer3NkVGSqdkkZ5Ixw6FS3I=
|
||||
github.com/johnfercher/maroto/v2 v2.4.1/go.mod h1:7kXCDMMD931qmAfv/aCCVyjG+FCzNd+0P1Q0wkGpu6c=
|
||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
@@ -183,12 +129,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
|
||||
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -204,16 +146,10 @@ github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pdfcpu/pdfcpu v0.11.1 h1:htHBSkGH5jMKWC6e0sihBFbcKZ8vG1M67c8/dJxhjas=
|
||||
github.com/pdfcpu/pdfcpu v0.11.1/go.mod h1:pP3aGga7pRvwFWAm9WwFvo+V68DfANi9kxSQYioNYcw=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/phpdave11/gofpdf v1.4.3 h1:M/zHvS8FO3zh9tUd2RCOPEjyuVcs281FCyF22Qlz/IA=
|
||||
github.com/phpdave11/gofpdf v1.4.3/go.mod h1:MAwzoUIgD3J55u0rxIG2eu37c+XWhBtXSpPAhnQXf/o=
|
||||
github.com/phpdave11/gofpdi v1.0.15/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.11 h1:0N92SLTB8JqASJB14ZLHHzFnBV8mG9zw4K7jghEFWuE=
|
||||
@@ -231,14 +167,11 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/russellhaering/goxmldsig v1.6.1 h1:SB7R5ttvrGIDB2juJAK/i7DQ2Ivr7agG+ohfNJjwyYU=
|
||||
github.com/russellhaering/goxmldsig v1.6.1/go.mod h1:haZkRcLs9W/Xp989fIjP3BrTdbFQveRF0QNZSYoH09w=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/shirou/gopsutil/v4 v4.26.7 h1:IXzpHz/dkMRYAhKkOXr1HB6SuzWU3eoyyeWe7g3bNZc=
|
||||
github.com/shirou/gopsutil/v4 v4.26.7/go.mod h1:5O9FjBiXoTDFatIWjZZosqj4pV0DRtLx598xGbBehzM=
|
||||
github.com/shirou/gopsutil/v4 v4.26.8 h1:YQMTF/1J50B5+Y0vlo1eDRf5DoR7Gk69hY+8wjYkQeo=
|
||||
github.com/shirou/gopsutil/v4 v4.26.8/go.mod h1:5O9FjBiXoTDFatIWjZZosqj4pV0DRtLx598xGbBehzM=
|
||||
github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo=
|
||||
github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
@@ -260,11 +193,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
@@ -304,19 +233,16 @@ golang.org/x/arch v0.26.0 h1:jZ6dpec5haP/fUv1kLCbuJy6dnRrfX6iVK08lZBFpk4=
|
||||
golang.org/x/arch v0.26.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo=
|
||||
golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=
|
||||
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
|
||||
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
|
||||
golang.org/x/image v0.45.0 h1:FMb1nTbH5H9vF55SriQHgFw5GnNL9Jg6L25BwXKzhB0=
|
||||
golang.org/x/image v0.45.0/go.mod h1:n62x/7RqlwXDvGsSU4u6IUTUf6KghUZ9Bt7cG/T9Fx4=
|
||||
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
|
||||
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -335,8 +261,8 @@ golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
|
||||
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
|
||||
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
@@ -352,8 +278,6 @@ gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
|
||||
gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
modernc.org/cc/v4 v4.27.3 h1:uNCgn37E5U09mTv1XgskEVUJ8ADKpmFMPxzGJ0TSo+U=
|
||||
modernc.org/cc/v4 v4.27.3/go.mod h1:3YjcbCqhoTTHPycJDRl2WZKKFj0nwcOIPBfEZK0Hdk8=
|
||||
modernc.org/ccgo/v4 v4.32.4 h1:L5OB8rpEX4ZsXEQwGozRfJyJSFHbbNVOoQ59DU9/KuU=
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "Virtual machine network does not exist: {{ .detail }}"
|
||||
ErrVMInvalidPath: "Invalid virtual machine path: {{ .detail }}"
|
||||
ErrVMInvalidInput: "Invalid virtual machine parameter: {{ .detail }}"
|
||||
ErrVMCommandFailed: "Virtual machine command failed: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "The disk does not meet expansion requirements or is referenced by another device. Check the disk status."
|
||||
ErrVMDiskReferenced: "This virtual disk is used by another virtual machine or device and cannot be expanded yet."
|
||||
ErrVMDiskReferenceCheck: "Cannot complete virtual disk reference checks. Expansion is unavailable; check the disks of other virtual machines."
|
||||
ErrVMSnapshotMetadataSync: "The snapshot was restored, but disk information could not be synced. Refresh and check; do not restore the snapshot again."
|
||||
VMCreateTask: "Create virtual machine [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "Create virtual machine [{{ .name }}] from template"
|
||||
VMUpdateTask: "Update virtual machine [{{ .name }}]"
|
||||
VMDiskResizeTask: "Expand disk [{{ .disk }}] of virtual machine [{{ .name }}]"
|
||||
VMRenameTask: "Rename virtual machine [{{ .name }}] to [{{ .newName }}]"
|
||||
VMDeleteTask: "Delete virtual machine [{{ .name }}]"
|
||||
VMTemplateCreateTask: "Save virtual machine [{{ .name }}] as template [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "La red de la máquina virtual no existe: {{ .detail }}"
|
||||
ErrVMInvalidPath: "Ruta de máquina virtual no válida: {{ .detail }}"
|
||||
ErrVMInvalidInput: "Parámetro de máquina virtual no válido: {{ .detail }}"
|
||||
ErrVMCommandFailed: "Error al ejecutar el comando de la máquina virtual: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "El disco no cumple los requisitos de ampliación o está referenciado por otro dispositivo. Compruebe su estado."
|
||||
ErrVMDiskReferenced: "Otro equipo virtual o dispositivo utiliza este disco virtual; la ampliación aún no se admite."
|
||||
ErrVMDiskReferenceCheck: "No se pueden comprobar las referencias al disco virtual. La ampliación no está disponible; revise los discos de otras máquinas virtuales."
|
||||
ErrVMSnapshotMetadataSync: "La instantánea se restauró, pero falló la sincronización de los datos del disco. Actualice y compruebe; no restaure de nuevo la instantánea."
|
||||
VMCreateTask: "Crear máquina virtual [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "Crear máquina virtual [{{ .name }}] desde plantilla"
|
||||
VMUpdateTask: "Actualizar máquina virtual [{{ .name }}]"
|
||||
VMDiskResizeTask: "Ampliar disco [{{ .disk }}] de la máquina virtual [{{ .name }}]"
|
||||
VMRenameTask: "Renombrar máquina virtual [{{ .name }}] a [{{ .newName }}]"
|
||||
VMDeleteTask: "Eliminar máquina virtual [{{ .name }}]"
|
||||
VMTemplateCreateTask: "Guardar máquina virtual [{{ .name }}] como plantilla [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "شبکه ماشین مجازی وجود ندارد: {{ .d
|
||||
ErrVMInvalidPath: "مسیر ماشین مجازی نامعتبر است: {{ .detail }}"
|
||||
ErrVMInvalidInput: "پارامتر ماشین مجازی نامعتبر است: {{ .detail }}"
|
||||
ErrVMCommandFailed: "اجرای دستور ماشین مجازی ناموفق بود: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "دیسک شرایط افزایش ظرفیت را ندارد یا دستگاه دیگری به آن ارجاع میدهد. وضعیت دیسک را بررسی کنید."
|
||||
ErrVMDiskReferenced: "این دیسک مجازی توسط ماشین مجازی یا دستگاه دیگری استفاده میشود؛ افزایش ظرفیت فعلاً پشتیبانی نمیشود."
|
||||
ErrVMDiskReferenceCheck: "بررسی ارجاعهای دیسک مجازی کامل نشد. افزایش ظرفیت ممکن نیست؛ دیسکهای سایر ماشینهای مجازی را بررسی کنید."
|
||||
ErrVMSnapshotMetadataSync: "اسنپشات بازیابی شد، اما همگامسازی اطلاعات دیسک ناموفق بود. تازهسازی و بررسی کنید؛ نیازی به بازیابی دوباره اسنپشات نیست."
|
||||
VMCreateTask: "ایجاد ماشین مجازی [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "ایجاد ماشین مجازی [{{ .name }}] از قالب"
|
||||
VMUpdateTask: "بهروزرسانی ماشین مجازی [{{ .name }}]"
|
||||
VMDiskResizeTask: "افزایش ظرفیت دیسک [{{ .disk }}] ماشین مجازی [{{ .name }}]"
|
||||
VMRenameTask: "تغییر نام ماشین مجازی [{{ .name }}] به [{{ .newName }}]"
|
||||
VMDeleteTask: "حذف ماشین مجازی [{{ .name }}]"
|
||||
VMTemplateCreateTask: "ذخیره ماشین مجازی [{{ .name }}] به عنوان قالب [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "仮想マシンネットワークが存在しません: {
|
||||
ErrVMInvalidPath: "仮想マシンのパスが無効です: {{ .detail }}"
|
||||
ErrVMInvalidInput: "仮想マシンのパラメータが無効です: {{ .detail }}"
|
||||
ErrVMCommandFailed: "仮想マシンコマンドの実行に失敗しました: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "ディスクが拡張条件を満たしていないか、他のデバイスから参照されています。ディスクの状態を確認してください。"
|
||||
ErrVMDiskReferenced: "この仮想ディスクは他の仮想マシンまたはデバイスで使用されているため、現在は拡張できません。"
|
||||
ErrVMDiskReferenceCheck: "仮想ディスクの参照を確認できないため拡張できません。他の仮想マシンのディスク状態を確認してください。"
|
||||
ErrVMSnapshotMetadataSync: "スナップショットは復元されましたが、ディスク情報の同期に失敗しました。更新して確認してください。再度復元する必要はありません。"
|
||||
VMCreateTask: "仮想マシン [{{ .name }}] を作成"
|
||||
VMCreateFromTemplateTask: "テンプレートから仮想マシン [{{ .name }}] を作成"
|
||||
VMUpdateTask: "仮想マシン [{{ .name }}] を更新"
|
||||
VMDiskResizeTask: "仮想マシン [{{ .name }}] のディスク [{{ .disk }}] を拡張"
|
||||
VMRenameTask: "仮想マシン [{{ .name }}] を [{{ .newName }}] にリネーム"
|
||||
VMDeleteTask: "仮想マシン [{{ .name }}] を削除"
|
||||
VMTemplateCreateTask: "仮想マシン [{{ .name }}] をテンプレート [{{ .template }}] として保存"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "가상 머신 네트워크가 존재하지 않습니다:
|
||||
ErrVMInvalidPath: "가상 머신 경로가 유효하지 않습니다: {{ .detail }}"
|
||||
ErrVMInvalidInput: "가상 머신 매개변수가 유효하지 않습니다: {{ .detail }}"
|
||||
ErrVMCommandFailed: "가상 머신 명령 실행 실패: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "디스크가 확장 조건을 충족하지 않거나 다른 장치에서 참조하고 있습니다. 디스크 상태를 확인하세요."
|
||||
ErrVMDiskReferenced: "다른 가상 머신 또는 장치가 이 가상 디스크를 사용 중이므로 현재 확장할 수 없습니다."
|
||||
ErrVMDiskReferenceCheck: "가상 디스크 참조를 확인할 수 없어 확장할 수 없습니다. 다른 가상 머신의 디스크 상태를 확인하세요."
|
||||
ErrVMSnapshotMetadataSync: "스냅샷은 복원되었지만 디스크 정보 동기화에 실패했습니다. 새로 고쳐 확인하세요. 스냅샷을 다시 복원할 필요는 없습니다."
|
||||
VMCreateTask: "가상 머신 [{{ .name }}] 생성"
|
||||
VMCreateFromTemplateTask: "템플릿에서 가상 머신 [{{ .name }}] 생성"
|
||||
VMUpdateTask: "가상 머신 [{{ .name }}] 업데이트"
|
||||
VMDiskResizeTask: "가상 머신 [{{ .name }}]의 디스크 [{{ .disk }}] 확장"
|
||||
VMRenameTask: "가상 머신 [{{ .name }}]을(를) [{{ .newName }}](으)로 이름 변경"
|
||||
VMDeleteTask: "가상 머신 [{{ .name }}] 삭제"
|
||||
VMTemplateCreateTask: "가상 머신 [{{ .name }}]을(를) 템플릿 [{{ .template }}](으)로 저장"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "ບໍ່ມີເຄືອຂ່າຍເຄື່ອງ
|
||||
ErrVMInvalidPath: "ເສັ້ນທາງເຄື່ອງສະເໝືອນບໍ່ຖືກຕ້ອງ: {{ .detail }}"
|
||||
ErrVMInvalidInput: "ພາລາມິເຕີເຄື່ອງສະເໝືອນບໍ່ຖືກຕ້ອງ: {{ .detail }}"
|
||||
ErrVMCommandFailed: "ຄຳສັ່ງເຄື່ອງສະເໝືອນລົ້ມເຫຼວ: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "ດິສກ໌ບໍ່ຕອບສະໜອງເງື່ອນໄຂການຂະຫຍາຍ ຫຼື ຖືກອ້າງອີງໂດຍອຸປະກອນອື່ນ. ກະລຸນາກວດສອບສະຖານະດິສກ໌."
|
||||
ErrVMDiskReferenced: "ດິສກ໌ເສມືອນນີ້ຖືກໃຊ້ໂດຍເຄື່ອງເສມືອນ ຫຼື ອຸປະກອນອື່ນ; ຍັງບໍ່ຮອງຮັບການຂະຫຍາຍ."
|
||||
ErrVMDiskReferenceCheck: "ບໍ່ສາມາດກວດສອບການອ້າງອີງດິສກ໌ເສມືອນໄດ້. ຍັງຂະຫຍາຍບໍ່ໄດ້; ກະລຸນາກວດສອບດິສກ໌ຂອງເຄື່ອງເສມືອນອື່ນ."
|
||||
ErrVMSnapshotMetadataSync: "ກູ້ຄືນສະແນັບຊັອດແລ້ວ ແຕ່ຊິງຂໍ້ມູນດິສກ໌ບໍ່ສຳເລັດ. ກະລຸນາໂຫຼດໃໝ່ ແລະ ກວດສອບ; ບໍ່ຈຳເປັນຕ້ອງກູ້ຄືນສະແນັບຊັອດອີກ."
|
||||
VMCreateTask: "ສ້າງເຄື່ອງສະເໝືອນ [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "ສ້າງເຄື່ອງສະເໝືອນ [{{ .name }}] ຈາກແມ່ແບບ"
|
||||
VMUpdateTask: "ອັບເດດເຄື່ອງສະເໝືອນ [{{ .name }}]"
|
||||
VMDiskResizeTask: "ຂະຫຍາຍດິສກ໌ [{{ .disk }}] ຂອງເຄື່ອງເສມືອນ [{{ .name }}]"
|
||||
VMRenameTask: "ປ່ຽນຊື່ເຄື່ອງສະເໝືອນ [{{ .name }}] ເປັນ [{{ .newName }}]"
|
||||
VMDeleteTask: "ລຶບເຄື່ອງສະເໝືອນ [{{ .name }}]"
|
||||
VMTemplateCreateTask: "ບັນທຶກເຄື່ອງສະເໝືອນ [{{ .name }}] ເປັນແມ່ແບບ [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "Rangkaian mesin maya tidak wujud: {{ .detail }}"
|
||||
ErrVMInvalidPath: "Laluan mesin maya tidak sah: {{ .detail }}"
|
||||
ErrVMInvalidInput: "Parameter mesin maya tidak sah: {{ .detail }}"
|
||||
ErrVMCommandFailed: "Arahan mesin maya gagal: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "Cakera tidak memenuhi syarat pembesaran atau dirujuk oleh peranti lain. Semak status cakera."
|
||||
ErrVMDiskReferenced: "Cakera maya ini digunakan oleh mesin maya atau peranti lain; pembesaran belum disokong."
|
||||
ErrVMDiskReferenceCheck: "Rujukan cakera maya tidak dapat disahkan. Pembesaran tidak tersedia; semak cakera mesin maya lain."
|
||||
ErrVMSnapshotMetadataSync: "Syot kilat telah dipulihkan, tetapi maklumat cakera gagal disegerakkan. Muat semula dan semak; tidak perlu memulihkan syot kilat sekali lagi."
|
||||
VMCreateTask: "Cipta mesin maya [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "Cipta mesin maya [{{ .name }}] daripada templat"
|
||||
VMUpdateTask: "Kemas kini mesin maya [{{ .name }}]"
|
||||
VMDiskResizeTask: "Besarkan cakera [{{ .disk }}] mesin maya [{{ .name }}]"
|
||||
VMRenameTask: "Namakan semula mesin maya [{{ .name }}] kepada [{{ .newName }}]"
|
||||
VMDeleteTask: "Padam mesin maya [{{ .name }}]"
|
||||
VMTemplateCreateTask: "Simpan mesin maya [{{ .name }}] sebagai templat [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "Rede da máquina virtual não existe: {{ .detail }}"
|
||||
ErrVMInvalidPath: "Caminho da máquina virtual inválido: {{ .detail }}"
|
||||
ErrVMInvalidInput: "Parâmetro da máquina virtual inválido: {{ .detail }}"
|
||||
ErrVMCommandFailed: "Falha no comando da máquina virtual: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "O disco não atende aos requisitos de expansão ou é referenciado por outro dispositivo. Verifique seu estado."
|
||||
ErrVMDiskReferenced: "Este disco virtual é usado por outra máquina virtual ou dispositivo e ainda não pode ser expandido."
|
||||
ErrVMDiskReferenceCheck: "Não foi possível verificar as referências ao disco virtual. A expansão está indisponível; verifique os discos das outras máquinas virtuais."
|
||||
ErrVMSnapshotMetadataSync: "O snapshot foi restaurado, mas a sincronização dos dados do disco falhou. Atualize e verifique; não restaure o snapshot novamente."
|
||||
VMCreateTask: "Criar máquina virtual [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "Criar máquina virtual [{{ .name }}] a partir do modelo"
|
||||
VMUpdateTask: "Atualizar máquina virtual [{{ .name }}]"
|
||||
VMDiskResizeTask: "Expandir disco [{{ .disk }}] da máquina virtual [{{ .name }}]"
|
||||
VMRenameTask: "Renomear máquina virtual [{{ .name }}] para [{{ .newName }}]"
|
||||
VMDeleteTask: "Excluir máquina virtual [{{ .name }}]"
|
||||
VMTemplateCreateTask: "Salvar máquina virtual [{{ .name }}] como modelo [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "Сеть виртуальной машины не сущ
|
||||
ErrVMInvalidPath: "Недопустимый путь виртуальной машины: {{ .detail }}"
|
||||
ErrVMInvalidInput: "Недопустимые параметры виртуальной машины: {{ .detail }}"
|
||||
ErrVMCommandFailed: "Ошибка выполнения команды виртуальной машины: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "Диск не соответствует условиям расширения или используется другим устройством. Проверьте состояние диска."
|
||||
ErrVMDiskReferenced: "Этот виртуальный диск используется другой виртуальной машиной или устройством; расширение пока недоступно."
|
||||
ErrVMDiskReferenceCheck: "Не удалось проверить ссылки на виртуальный диск. Расширение недоступно; проверьте диски других виртуальных машин."
|
||||
ErrVMSnapshotMetadataSync: "Снимок восстановлен, но синхронизация данных диска не удалась. Обновите данные и проверьте их; повторно восстанавливать снимок не нужно."
|
||||
VMCreateTask: "Создать виртуальную машину [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "Создать виртуальную машину [{{ .name }}] из шаблона"
|
||||
VMUpdateTask: "Обновить виртуальную машину [{{ .name }}]"
|
||||
VMDiskResizeTask: "Расширить диск [{{ .disk }}] виртуальной машины [{{ .name }}]"
|
||||
VMRenameTask: "Переименовать виртуальную машину [{{ .name }}] в [{{ .newName }}]"
|
||||
VMDeleteTask: "Удалить виртуальную машину [{{ .name }}]"
|
||||
VMTemplateCreateTask: "Сохранить виртуальную машину [{{ .name }}] как шаблон [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "Sanal makine ağı mevcut değil: {{ .detail }}"
|
||||
ErrVMInvalidPath: "Geçersiz sanal makine yolu: {{ .detail }}"
|
||||
ErrVMInvalidInput: "Geçersiz sanal makine parametresi: {{ .detail }}"
|
||||
ErrVMCommandFailed: "Sanal makine komutu başarısız: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "Disk genişletme koşullarını karşılamıyor veya başka bir aygıt tarafından kullanılıyor. Disk durumunu kontrol edin."
|
||||
ErrVMDiskReferenced: "Bu sanal disk başka bir sanal makine veya aygıt tarafından kullanılıyor; genişletme henüz desteklenmiyor."
|
||||
ErrVMDiskReferenceCheck: "Sanal disk başvuruları doğrulanamadı. Genişletme kullanılamıyor; diğer sanal makinelerin disklerini kontrol edin."
|
||||
ErrVMSnapshotMetadataSync: "Anlık görüntü geri yüklendi ancak disk bilgileri eşitlenemedi. Yenileyip kontrol edin; anlık görüntüyü tekrar geri yüklemeyin."
|
||||
VMCreateTask: "Sanal makine [{{ .name }}] oluştur"
|
||||
VMCreateFromTemplateTask: "Şablondan sanal makine [{{ .name }}] oluştur"
|
||||
VMUpdateTask: "Sanal makine [{{ .name }}] güncelle"
|
||||
VMDiskResizeTask: "[{{ .name }}] sanal makinesinin [{{ .disk }}] diskini genişlet"
|
||||
VMRenameTask: "Sanal makine [{{ .name }}] adını [{{ .newName }}] olarak değiştir"
|
||||
VMDeleteTask: "Sanal makine [{{ .name }}] sil"
|
||||
VMTemplateCreateTask: "Sanal makine [{{ .name }}] öğesini şablon [{{ .template }}] olarak kaydet"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "虛擬機網路不存在: {{ .detail }}"
|
||||
ErrVMInvalidPath: "虛擬機路徑非法: {{ .detail }}"
|
||||
ErrVMInvalidInput: "虛擬機參數非法: {{ .detail }}"
|
||||
ErrVMCommandFailed: "虛擬機指令執行失敗: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "磁碟不符合擴容條件,或被其他裝置引用,請檢查磁碟狀態"
|
||||
ErrVMDiskReferenced: "此虛擬硬碟被其他虛擬機或裝置使用,暫不支援擴容"
|
||||
ErrVMDiskReferenceCheck: "無法完成虛擬硬碟引用檢查,暫不支援擴容,請檢查其他虛擬機的磁碟狀態"
|
||||
ErrVMSnapshotMetadataSync: "快照已還原,但磁碟資訊同步失敗,請重新整理檢查,無需重複還原快照"
|
||||
VMCreateTask: "建立虛擬機 [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "從範本建立虛擬機 [{{ .name }}]"
|
||||
VMUpdateTask: "更新虛擬機 [{{ .name }}]"
|
||||
VMDiskResizeTask: "擴容虛擬機 [{{ .name }}] 磁碟 [{{ .disk }}]"
|
||||
VMRenameTask: "重新命名虛擬機 [{{ .name }}] 為 [{{ .newName }}]"
|
||||
VMDeleteTask: "刪除虛擬機 [{{ .name }}]"
|
||||
VMTemplateCreateTask: "轉存虛擬機 [{{ .name }}] 為範本 [{{ .template }}]"
|
||||
|
||||
@@ -152,9 +152,14 @@ ErrVMNetworkNotFound: "虚拟机网络不存在: {{ .detail }}"
|
||||
ErrVMInvalidPath: "虚拟机路径非法: {{ .detail }}"
|
||||
ErrVMInvalidInput: "虚拟机参数非法: {{ .detail }}"
|
||||
ErrVMCommandFailed: "虚拟机命令执行失败: {{ .err }}"
|
||||
ErrVMDiskResizeBlocked: "磁盘不符合扩容条件,或被其他设备引用,请检查磁盘状态"
|
||||
ErrVMDiskReferenced: "该虚拟硬盘被其他虚拟机或设备使用,暂不支持扩容"
|
||||
ErrVMDiskReferenceCheck: "无法完成虚拟硬盘引用检查,暂不支持扩容,请检查其他虚拟机的磁盘状态"
|
||||
ErrVMSnapshotMetadataSync: "快照已恢复,但磁盘信息同步失败,请刷新检查,无需重复恢复快照"
|
||||
VMCreateTask: "创建虚拟机 [{{ .name }}]"
|
||||
VMCreateFromTemplateTask: "从模板创建虚拟机 [{{ .name }}]"
|
||||
VMUpdateTask: "更新虚拟机 [{{ .name }}]"
|
||||
VMDiskResizeTask: "扩容虚拟机 [{{ .name }}] 磁盘 [{{ .disk }}]"
|
||||
VMRenameTask: "重命名虚拟机 [{{ .name }}] 为 [{{ .newName }}]"
|
||||
VMDeleteTask: "删除虚拟机 [{{ .name }}]"
|
||||
VMTemplateCreateTask: "转存虚拟机 [{{ .name }}] 为模板 [{{ .template }}]"
|
||||
|
||||
@@ -87,6 +87,7 @@ func Routers() *gin.Engine {
|
||||
}
|
||||
|
||||
Router.Use(middleware.FrontendFallback())
|
||||
Router.Use(middleware.WebSocketOriginGuard())
|
||||
Router.Use(middleware.OperationLog())
|
||||
Router.Use(middleware.GlobalLoading())
|
||||
Router.Use(xpack.AuthProvider.CoreAPIAuthMiddleware())
|
||||
|
||||
@@ -27,6 +27,11 @@ var demoAllowedRoutes = map[demoRoute]struct{}{
|
||||
{http.MethodPost, "/api/v2/toolbox/clam/base"}: {},
|
||||
{http.MethodPost, "/api/v2/backups/record/size"}: {},
|
||||
|
||||
{http.MethodPost, "/api/v2/hosts/firewall/base"}: {},
|
||||
{http.MethodPost, "/api/v2/hosts/firewall/forward/base"}: {},
|
||||
{http.MethodPost, "/api/v2/hosts/firewall/rules/native/detail"}: {},
|
||||
{http.MethodPost, "/api/v2/hosts/firewall/rules/sync/preview"}: {},
|
||||
|
||||
{http.MethodPost, "/api/v2/core/auth/login"}: {},
|
||||
{http.MethodPost, "/api/v2/core/logs/login"}: {},
|
||||
{http.MethodPost, "/api/v2/core/logs/operation"}: {},
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func WebSocketOriginGuard() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if !websocket.IsWebSocketUpgrade(c.Request) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
if site := c.GetHeader("Sec-Fetch-Site"); site != "" && site != "same-origin" {
|
||||
c.AbortWithStatus(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
origins := c.Request.Header.Values("Origin")
|
||||
if len(origins) == 0 {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if len(origins) != 1 {
|
||||
c.AbortWithStatus(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
origin, err := url.Parse(origins[0])
|
||||
if err != nil || (origin.Scheme != "http" && origin.Scheme != "https") ||
|
||||
origin.Host == "" || !strings.EqualFold(origin.Host, c.Request.Host) ||
|
||||
origin.User != nil || origin.Path != "" || origin.RawQuery != "" || origin.ForceQuery || origin.Fragment != "" {
|
||||
c.AbortWithStatus(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,11 @@
|
||||
|
||||
$ bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_start.sh)"
|
||||
|
||||
如果是用于离线环境,推荐使用 [安装包方式](https://1panel.cn/docs/v2/installation/enterprise_installation/) 进行安装部署。
|
||||
如果是用于离线环境,推荐使用 [安装包方式](https://docs.fit2cloud.com/1panel/installation/enterprise-installation/) 进行安装部署。
|
||||
|
||||
**学习资料**
|
||||
|
||||
- [在线文档](https://1panel.cn/docs/)
|
||||
- [在线文档](https://docs.fit2cloud.com/1panel/)
|
||||
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
|
||||
|
||||
## 版本对比
|
||||
|
||||
@@ -55,11 +55,11 @@
|
||||
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
|
||||
```
|
||||
|
||||
如果是用於離線環境,推薦使用 [安裝包方式](https://1panel.cn/docs/installation/package_installation/) 進行安裝部署。
|
||||
如果是用於離線環境,推薦使用 [安裝包方式](https://docs.fit2cloud.com/1panel/installation/enterprise-installation/) 進行安裝部署。
|
||||
|
||||
**學習資料**
|
||||
|
||||
- [線上文件](https://1panel.cn/docs/)
|
||||
- [線上文件](https://docs.fit2cloud.com/1panel/)
|
||||
- [社區論壇](https://bbs.fit2cloud.com/c/1p/7)
|
||||
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
|
||||
|
||||
|
||||
Generated
+78
-139
@@ -46,18 +46,18 @@
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-demi": "^0.14.6",
|
||||
"vue-i18n": "^11.4.4",
|
||||
"vue-router": "^5.2.0"
|
||||
"vue-router": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.3.3",
|
||||
"@types/node": "^26.4.0",
|
||||
"@types/node": "^26.6.3",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.65.0",
|
||||
"@typescript-eslint/parser": "^8.68.0",
|
||||
"@vitejs/plugin-vue": "^6.0.7",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.6",
|
||||
"@vue/compiler-sfc": "^3.5.42",
|
||||
"autoprefixer": "^10.5.4",
|
||||
"autoprefixer": "^10.6.1",
|
||||
"esbuild": "^0.28.2",
|
||||
"eslint": "^10.8.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
@@ -68,7 +68,7 @@
|
||||
"postcss-html": "^2.0.0",
|
||||
"prettier": "^3.9.6",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"sass": "^1.103.1",
|
||||
"sass": "^1.105.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^6.0.3",
|
||||
@@ -77,7 +77,7 @@
|
||||
"vite": "^8.2.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-eslint2": "^5.3.0",
|
||||
"vite-svg-loader": "^5.1.2",
|
||||
"vite-svg-loader": "^5.1.3",
|
||||
"vue-eslint-parser": "^10.4.1",
|
||||
"vue-tsc": "^3.3.9"
|
||||
}
|
||||
@@ -2954,12 +2954,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/jsesc": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz",
|
||||
"integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
@@ -3013,14 +3007,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.4.0.tgz",
|
||||
"integrity": "sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==",
|
||||
"version": "26.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.3.tgz",
|
||||
"integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~8.3.0"
|
||||
"undici-types": "~8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/normalize-package-data": {
|
||||
@@ -4218,9 +4212,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.5.4",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz",
|
||||
"integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==",
|
||||
"version": "10.6.1",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.6.1.tgz",
|
||||
"integrity": "sha512-cL1Qz6ADZhcEbny/8HPfe99J6HhNoYtpX2LFLIbhgGE7Q1hlQVkYFdetDN7Id3KiQxhDrHwzlHr/YQCnZ8+xSA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4238,8 +4232,8 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.28.6",
|
||||
"caniuse-lite": "^1.0.30001806",
|
||||
"browserslist": "^4.28.9",
|
||||
"caniuse-lite": "^1.0.30001810",
|
||||
"fraction.js": "^5.3.4",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
@@ -4274,9 +4268,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.11.20",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz",
|
||||
"integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==",
|
||||
"version": "2.11.26",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.26.tgz",
|
||||
"integrity": "sha512-GLQdD3y6UF8iVuMJl5fHgE4jdn/ua7n+toKfLgNlg3BqQtOZjpy68T8Tup8/wGWZCDlm7KMg7tPb4MPn7oN0TQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -4328,9 +4322,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.28.8",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
|
||||
"integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
|
||||
"version": "4.29.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.2.tgz",
|
||||
"integrity": "sha512-/u9r8k8ue4ZhHCw9ovDtltpWdXeXzjhdxCGj6vm1RGLPXb8lu33EMdoCHF5Sx8oYBS+Q/FYYNmAqlHncxR3RmA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4349,11 +4343,11 @@
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.11.12",
|
||||
"caniuse-lite": "^1.0.30001809",
|
||||
"electron-to-chromium": "^1.5.402",
|
||||
"node-releases": "^2.0.53",
|
||||
"update-browserslist-db": "^1.3.0"
|
||||
"baseline-browser-mapping": "^2.11.26",
|
||||
"caniuse-lite": "^1.0.30001812",
|
||||
"electron-to-chromium": "^1.5.439",
|
||||
"node-releases": "^2.0.57",
|
||||
"update-browserslist-db": "^1.3.3"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@@ -4443,9 +4437,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001810",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
|
||||
"integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
|
||||
"version": "1.0.30001813",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001813.tgz",
|
||||
"integrity": "sha512-zfjJo4rM0+fUomGDBW/xcDjhIwz/210DGvip2MAMDZ8KHcRPnOHmEgHPZP0UHlZoxr8fYKVJOqcORhYQcG4FKQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4728,6 +4722,16 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/compare-func": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
|
||||
@@ -5579,9 +5583,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.420",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.420.tgz",
|
||||
"integrity": "sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==",
|
||||
"version": "1.5.440",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.440.tgz",
|
||||
"integrity": "sha512-SghDzqdJokdz8zP8YNlvS74+CwLRoUPDGvP/gFA+HrKVw+pOshUAgx8pXR37xl/u16zGae746rFNWvZ/22kU2Q==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -7081,6 +7085,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
@@ -7128,6 +7133,7 @@
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
@@ -8446,9 +8452,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.54",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
|
||||
"integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
|
||||
"version": "2.0.57",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz",
|
||||
"integrity": "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -9391,9 +9397,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.103.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.103.1.tgz",
|
||||
"integrity": "sha512-9icZURbP51S6S0QGoyaeqk9uB06GNWxsFYWfH5RgpFgqK5FA8tJcM3AdVxrZEVJ7dz+L87nG95gBKf4VuaMHGw==",
|
||||
"version": "1.105.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.105.0.tgz",
|
||||
"integrity": "sha512-4EjSuRTyG8M4yRXXY1PT9OUV5zz23xBxJkbIAprohKHqf6HOU4MCpE3U3vAC+iO1gxobBjESNbcj8rYAQZWnyA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -9413,9 +9419,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
|
||||
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
|
||||
"integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
@@ -9917,9 +9923,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.4.tgz",
|
||||
"integrity": "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==",
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.5.tgz",
|
||||
"integrity": "sha512-8SQMzdrvWaD8deUmrnYB+ASyxBVgWUOilg+A75nE/76WdLpj6LopCwiAVvkzkcqy/9b7t2Mg7faFLjg0ZRcZ3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9942,16 +9948,6 @@
|
||||
"url": "https://opencollective.com/svgo"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.13",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz",
|
||||
@@ -10211,9 +10207,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz",
|
||||
"integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -10527,9 +10523,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
|
||||
"integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz",
|
||||
"integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -10819,14 +10815,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-svg-loader": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.2.tgz",
|
||||
"integrity": "sha512-/ANGO8+F64Cd6fXRaefCY0mlHHIdstySQ+nr+blHpaXSJaXq+TH68KkVHbumtQIVLt6HZFBtlaxMV3dQyzg6lA==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.3.tgz",
|
||||
"integrity": "sha512-JLROmqf5lrg2EXqQf6uXHiXoY1ea8xt3+i5nI/cjXgEmEZL4P7rrQZczi/aWF+V9JTK6pyfMmhWmVW2FAYji1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"svgo": "^3.3.4"
|
||||
"svgo": "^3.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.2.13"
|
||||
@@ -11263,17 +11259,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.2.0.tgz",
|
||||
"integrity": "sha512-QAC5i0LEb1GLG0LXDQmHu8L7FX12j0KwU/JTKmLQUJMrn04gQdKP6Du+p0QwpHb3iy71vBlqnHQ8WAfOSAWhqw==",
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.3.1.tgz",
|
||||
"integrity": "sha512-GDBZzgmILxA/kFnkFbJjQZdZ2QQbngnIMMuoUcjhZIfH1RGMaPjPwX5ASnV38qamuA9uhO0RDjSBHTDNG2uXyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/generator": "^8.0.0",
|
||||
"@vue-macros/common": "^3.1.3",
|
||||
"@vue/devtools-api": "^8.1.5",
|
||||
"ast-walker-scope": "^0.9.0",
|
||||
"chokidar": "^5.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"confbox": "^0.2.4",
|
||||
"local-pkg": "^1.2.1",
|
||||
"magic-string": "^0.30.21",
|
||||
"mlly": "^1.8.2",
|
||||
@@ -11284,8 +11279,7 @@
|
||||
"scule": "^1.3.0",
|
||||
"tinyglobby": "^0.2.17",
|
||||
"unplugin": "^3.3.0",
|
||||
"unplugin-utils": "^0.3.2",
|
||||
"yaml": "^2.9.0"
|
||||
"unplugin-utils": "^0.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
@@ -11312,69 +11306,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@babel/generator": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz",
|
||||
"integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^8.0.0",
|
||||
"@babel/types": "^8.0.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"@types/jsesc": "^2.5.0",
|
||||
"jsesc": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@babel/helper-string-parser": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz",
|
||||
"integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.2.tgz",
|
||||
"integrity": "sha512-9Fr9QeyCAyi1BR1jKZ6uYQ24EIhQUx5ReHfQU7drOE+TPOb+w11/dsqLkMOT2U29OdCT71XajrOT8xDc1C7orA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@babel/parser": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0.tgz",
|
||||
"integrity": "sha512-aLxAE+imI9bCcyaPrUDjBv3uSkWieifjLe0kuFOZF0zli0L6GCsTmsePnTr55adbIAgYz2zhN1vnFimCBUYcRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^8.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@babel/types": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0.tgz",
|
||||
"integrity": "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^8.0.0",
|
||||
"@babel/helper-validator-identifier": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@vue/devtools-api": {
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.1.5.tgz",
|
||||
@@ -11402,6 +11333,12 @@
|
||||
"integrity": "sha512-mhT4zcPFhF+Xk1O4BfhhrbXzpmfqY03fS6xGpcllbQG7lDjhQf8pQHcTIhqQIYx1hfwtHmk/6jM96ele0UxPqQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue-router/node_modules/confbox": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
|
||||
"integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue-router/node_modules/perfect-debounce": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
|
||||
@@ -11616,7 +11553,9 @@
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
|
||||
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
|
||||
@@ -57,18 +57,18 @@
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-demi": "^0.14.6",
|
||||
"vue-i18n": "^11.4.4",
|
||||
"vue-router": "^5.2.0"
|
||||
"vue-router": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.3.3",
|
||||
"@types/node": "^26.4.0",
|
||||
"@types/node": "^26.6.3",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.65.0",
|
||||
"@typescript-eslint/parser": "^8.68.0",
|
||||
"@vitejs/plugin-vue": "^6.0.7",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.6",
|
||||
"@vue/compiler-sfc": "^3.5.42",
|
||||
"autoprefixer": "^10.5.4",
|
||||
"autoprefixer": "^10.6.1",
|
||||
"esbuild": "^0.28.2",
|
||||
"eslint": "^10.8.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
@@ -79,7 +79,7 @@
|
||||
"postcss-html": "^2.0.0",
|
||||
"prettier": "^3.9.6",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"sass": "^1.103.1",
|
||||
"sass": "^1.105.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^6.0.3",
|
||||
@@ -88,7 +88,7 @@
|
||||
"vite": "^8.2.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-eslint2": "^5.3.0",
|
||||
"vite-svg-loader": "^5.1.2",
|
||||
"vite-svg-loader": "^5.1.3",
|
||||
"vue-eslint-parser": "^10.4.1",
|
||||
"vue-tsc": "^3.3.9"
|
||||
},
|
||||
|
||||
@@ -22,6 +22,8 @@ export namespace AI {
|
||||
}
|
||||
|
||||
export interface Info {
|
||||
collectedAt?: string;
|
||||
warnings?: string[];
|
||||
cudaVersion: string;
|
||||
driverVersion: string;
|
||||
type: string;
|
||||
@@ -31,6 +33,34 @@ export namespace AI {
|
||||
xpu: XpuInfo['xpu'];
|
||||
}
|
||||
export interface GPU {
|
||||
eccPending?: string;
|
||||
eccErrors?: { scope: string; correctable: string; uncorrectable: string }[];
|
||||
memoryActivity?: string;
|
||||
encoderUtil?: string;
|
||||
decoderUtil?: string;
|
||||
jpegUtil?: string;
|
||||
ofaUtil?: string;
|
||||
mediaUtil?: string;
|
||||
hotspotTemperature?: string;
|
||||
fanRPM?: string;
|
||||
mediaFrequency?: string;
|
||||
|
||||
uuid?: string;
|
||||
driverVersion?: string;
|
||||
architecture?: string;
|
||||
frequency?: string;
|
||||
memoryFrequency?: string;
|
||||
memoryTemperature?: string;
|
||||
memoryFree?: string;
|
||||
memoryReserved?: string;
|
||||
powerLimit?: string;
|
||||
defaultPowerLimit?: string;
|
||||
pcieGeneration?: string;
|
||||
pcieMaxGeneration?: string;
|
||||
pcieWidth?: string;
|
||||
pcieMaxWidth?: string;
|
||||
clockEvents?: string[];
|
||||
processStatus?: string;
|
||||
type: 'nvidia' | 'amd';
|
||||
index: number;
|
||||
productName: string;
|
||||
@@ -52,6 +82,12 @@ export namespace AI {
|
||||
processes: Process[];
|
||||
}
|
||||
export interface NPU {
|
||||
aiCPUUtil?: string;
|
||||
ctrlCPUUtil?: string;
|
||||
ddrBandwidth?: string;
|
||||
hbmBandwidth?: string;
|
||||
|
||||
processStatus?: string;
|
||||
type: 'ascend';
|
||||
index: number;
|
||||
npuIndex: number;
|
||||
@@ -73,18 +109,24 @@ export namespace AI {
|
||||
processes: NPUProcess[];
|
||||
}
|
||||
export interface MonitorGPUSearch {
|
||||
aggregation?: 'avg' | 'max';
|
||||
deviceID?: string;
|
||||
legacy?: boolean;
|
||||
productName: string;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
}
|
||||
export interface MonitorGPUOptions {
|
||||
supported: boolean;
|
||||
gpuType: string;
|
||||
options: Array<string>;
|
||||
chartHide: Array<ChartHide>;
|
||||
}
|
||||
export interface ChartHide {
|
||||
deviceID?: string;
|
||||
legacy?: boolean;
|
||||
productName: string;
|
||||
type?: 'gpu' | 'xpu';
|
||||
type?: 'gpu' | 'npu' | 'xpu';
|
||||
process: boolean;
|
||||
gpu: boolean;
|
||||
memory: boolean;
|
||||
@@ -94,17 +136,45 @@ export namespace AI {
|
||||
speed: boolean;
|
||||
}
|
||||
export interface MonitorGPUData {
|
||||
memoryActivity: Array<number | null>;
|
||||
encoderUtil: Array<number | null>;
|
||||
decoderUtil: Array<number | null>;
|
||||
jpegUtil: Array<number | null>;
|
||||
ofaUtil: Array<number | null>;
|
||||
mediaUtil: Array<number | null>;
|
||||
computeUtil: Array<number | null>;
|
||||
copyUtil: Array<number | null>;
|
||||
hotspotTemperature: Array<number | null>;
|
||||
fanRPM: Array<number | null>;
|
||||
aiCPUUtil: Array<number | null>;
|
||||
ctrlCPUUtil: Array<number | null>;
|
||||
ddrUsed: Array<number | null>;
|
||||
ddrTotal: Array<number | null>;
|
||||
hbmUsed: Array<number | null>;
|
||||
hbmTotal: Array<number | null>;
|
||||
ddrBandwidth: Array<number | null>;
|
||||
hbmBandwidth: Array<number | null>;
|
||||
memoryBandwidth: Array<number | null>;
|
||||
mediaFrequency: Array<number | null>;
|
||||
hugepagesUsed: Array<number | null>;
|
||||
hugepagesTotal: Array<number | null>;
|
||||
|
||||
bucketSeconds: number;
|
||||
sampleCount: number;
|
||||
memoryTemperatureValue: Array<number | null>;
|
||||
frequencyValue: Array<number | null>;
|
||||
memoryFrequencyValue: Array<number | null>;
|
||||
date: Array<Date>;
|
||||
gpuValue: Array<number>;
|
||||
temperatureValue: Array<number>;
|
||||
powerTotal: Array<number>;
|
||||
powerUsed: Array<number>;
|
||||
powerPercent: Array<number>;
|
||||
memoryTotal: Array<number>;
|
||||
memoryUsed: Array<number>;
|
||||
memoryPercent: Array<number>;
|
||||
speedValue: Array<number>;
|
||||
processCount: Array<number>;
|
||||
gpuValue: Array<number | null>;
|
||||
temperatureValue: Array<number | null>;
|
||||
powerTotal: Array<number | null>;
|
||||
powerUsed: Array<number | null>;
|
||||
powerPercent: Array<number | null>;
|
||||
memoryTotal: Array<number | null>;
|
||||
memoryUsed: Array<number | null>;
|
||||
memoryPercent: Array<number | null>;
|
||||
speedValue: Array<number | null>;
|
||||
processCount: Array<number | null>;
|
||||
gpuProcesses: Array<Array<GPUProcess>>;
|
||||
}
|
||||
export interface GPUProcess {
|
||||
@@ -132,12 +202,15 @@ export namespace AI {
|
||||
}
|
||||
|
||||
interface Xpu {
|
||||
tiles?: { tileID: number; stats: Stats }[];
|
||||
processStatus?: string;
|
||||
basic: Basic;
|
||||
stats: Stats;
|
||||
processes: XpuProcess[];
|
||||
}
|
||||
|
||||
interface Basic {
|
||||
uuid?: string;
|
||||
deviceID: number;
|
||||
deviceName: string;
|
||||
vendorName: string;
|
||||
@@ -148,6 +221,13 @@ export namespace AI {
|
||||
}
|
||||
|
||||
interface Stats {
|
||||
mediaUtil?: string;
|
||||
computeUtil?: string;
|
||||
copyUtil?: string;
|
||||
mediaFrequency?: string;
|
||||
memoryTemperature?: string;
|
||||
|
||||
memoryBandwidthUtil?: string;
|
||||
power: string;
|
||||
gpuUtil: string;
|
||||
frequency: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CommonModel, ReqPage } from '@/api/interface';
|
||||
import type { CronjobAlertTriggerMode } from '@/utils/cronjob-alert';
|
||||
|
||||
export namespace Alert {
|
||||
export interface AlertInfo extends CommonModel {
|
||||
@@ -9,22 +10,25 @@ export namespace Alert {
|
||||
interval: number;
|
||||
method: string;
|
||||
title: string;
|
||||
taskName?: string;
|
||||
project: string;
|
||||
status: string;
|
||||
sendCount: number;
|
||||
sendMethod: string[];
|
||||
advancedParams: string;
|
||||
alertTriggerMode?: CronjobAlertTriggerMode;
|
||||
createUser?: string;
|
||||
updateUser?: string;
|
||||
}
|
||||
|
||||
export interface AlertDetail {
|
||||
type: string;
|
||||
subType?: string;
|
||||
licenseId: string;
|
||||
title: string;
|
||||
project: string;
|
||||
method: string;
|
||||
params: string;
|
||||
params: { index: string; key: string; value: string }[] | string;
|
||||
}
|
||||
|
||||
export interface AlertUpdateStatusReq {
|
||||
@@ -74,6 +78,7 @@ export namespace Alert {
|
||||
project: string;
|
||||
status: string;
|
||||
sendCount: number;
|
||||
advancedParams?: string;
|
||||
}
|
||||
|
||||
export interface AlertUpdateReq extends CommonModel {
|
||||
@@ -86,6 +91,7 @@ export namespace Alert {
|
||||
project: string;
|
||||
status: string;
|
||||
sendCount: number;
|
||||
advancedParams?: string;
|
||||
}
|
||||
|
||||
export interface DelReq {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ReqPage } from '.';
|
||||
import type { CronjobAlertTriggerMode } from '@/utils/cronjob-alert';
|
||||
|
||||
export namespace Cronjob {
|
||||
export interface Search extends ReqPage {
|
||||
@@ -62,6 +63,7 @@ export namespace Cronjob {
|
||||
secret: string;
|
||||
hasAlert: boolean;
|
||||
alertCount: number;
|
||||
alertTriggerMode?: CronjobAlertTriggerMode;
|
||||
alertTitle: string;
|
||||
alertMethod: string;
|
||||
alertMethodItems: Array<string>;
|
||||
@@ -111,6 +113,7 @@ export namespace Cronjob {
|
||||
secret: string;
|
||||
|
||||
alertCount: number;
|
||||
alertTriggerMode?: CronjobAlertTriggerMode;
|
||||
alertTitle: string;
|
||||
alertMethod: string;
|
||||
|
||||
@@ -154,6 +157,7 @@ export namespace Cronjob {
|
||||
downloadAccount: string;
|
||||
|
||||
alertCount: number;
|
||||
alertTriggerMode?: CronjobAlertTriggerMode;
|
||||
}
|
||||
export interface TransHelper {
|
||||
name: string;
|
||||
|
||||
@@ -65,6 +65,12 @@ export namespace Host {
|
||||
}
|
||||
|
||||
export interface MonitorSetting {
|
||||
gpuMonitorStatus: string;
|
||||
gpuMonitorStoreDays: string;
|
||||
gpuMonitorInterval: string;
|
||||
vllmMonitorStatus: string;
|
||||
vllmMonitorStoreDays: string;
|
||||
vllmMonitorInterval: string;
|
||||
defaultNetwork: string;
|
||||
defaultIO: string;
|
||||
monitorStatus: string;
|
||||
|
||||
@@ -41,11 +41,20 @@ export const updateBindDomain = (req: AI.BindDomain) => {
|
||||
export const loadGPUInfo = () => {
|
||||
return http.get<AI.Info>(`/ai/gpu/load`);
|
||||
};
|
||||
export const getGPUOptions = () => {
|
||||
return http.get<AI.MonitorGPUOptions>(`/ai/gpu/options`);
|
||||
export const getGPUOptions = (currentNode?: string) => {
|
||||
return http.get<AI.MonitorGPUOptions>(
|
||||
`/ai/gpu/options`,
|
||||
{},
|
||||
currentNode ? { headers: { CurrentNode: currentNode } } : {},
|
||||
);
|
||||
};
|
||||
export const loadGPUMonitor = (param: AI.MonitorGPUSearch) => {
|
||||
return http.post<AI.MonitorGPUData>(`/ai/gpu/search`, param);
|
||||
export const loadGPUMonitor = (param: AI.MonitorGPUSearch, currentNode?: string) => {
|
||||
return http.post<AI.MonitorGPUData>(
|
||||
`/ai/gpu/search`,
|
||||
param,
|
||||
TimeoutEnum.T_60S,
|
||||
currentNode ? { CurrentNode: currentNode } : undefined,
|
||||
);
|
||||
};
|
||||
|
||||
export const pageMcpServer = (req: AI.McpServerSearch) => {
|
||||
|
||||
@@ -28,8 +28,8 @@ export const getIOOptions = (currentNode?: string) => {
|
||||
currentNode ? { headers: { CurrentNode: currentNode } } : {},
|
||||
);
|
||||
};
|
||||
export const cleanMonitors = () => {
|
||||
return http.post(`/hosts/monitor/clean`, {});
|
||||
export const cleanMonitors = (type: 'host' | 'gpu') => {
|
||||
return http.post(`/hosts/monitor/clean`, { type });
|
||||
};
|
||||
export const loadMonitorSetting = (currentNode?: string) => {
|
||||
return http.get<Host.MonitorSetting>(
|
||||
|
||||
@@ -134,10 +134,12 @@ function initChart() {
|
||||
series.push({
|
||||
name: item?.name,
|
||||
type: 'line',
|
||||
itemStyle: seriesStyle[index + 2],
|
||||
areaStyle: seriesStyle[index],
|
||||
itemStyle: item.itemStyle ?? seriesStyle[index + 2],
|
||||
areaStyle: item.areaStyle ?? seriesStyle[index],
|
||||
data: item?.data,
|
||||
showSymbol: false,
|
||||
showSymbol: item.showSymbol ?? false,
|
||||
symbolSize: item.symbolSize,
|
||||
sampling: 'lttb',
|
||||
yAxisIndex: item.yAxisIndex ? 1 : null,
|
||||
});
|
||||
});
|
||||
@@ -226,9 +228,10 @@ function initChart() {
|
||||
};
|
||||
const tooltip = props.option.tooltip || defaultTooltip;
|
||||
const option = {
|
||||
animation: (props.option.xData?.length ?? 0) <= 3000,
|
||||
title: [
|
||||
{
|
||||
left: 'center',
|
||||
left: props.option.titleLeft ?? 'center',
|
||||
text: props.option.title,
|
||||
show: props.option.title,
|
||||
textStyle: {
|
||||
@@ -256,6 +259,7 @@ function initChart() {
|
||||
itemWidth: 8,
|
||||
icon: 'circle',
|
||||
...props.option.legend,
|
||||
selected: { ...props.option.legend?.selected, ...itemSelect?.selected },
|
||||
textStyle: {
|
||||
...itemSelect?.textStyle,
|
||||
...props.option.legend?.textStyle,
|
||||
@@ -307,7 +311,9 @@ function initChart() {
|
||||
series: series,
|
||||
dataZoom: [
|
||||
{
|
||||
...(props.option.xData?.[0] ? { startValue: props.option.xData[0] } : {}),
|
||||
...((props.option.xAxis?.min ?? props.option.xData?.[0]) != null
|
||||
? { startValue: props.option.xAxis?.min ?? props.option.xData[0] }
|
||||
: {}),
|
||||
left: grid.left,
|
||||
right: grid.right,
|
||||
show: props.dataZoom,
|
||||
|
||||
@@ -1507,7 +1507,88 @@ const message = {
|
||||
ignoreEos: 'Ignore EOS',
|
||||
extraHeaders: 'Extra Headers',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'Clear monitoring history only for this vLLM instance? Other instances, host and GPU monitoring are unaffected.',
|
||||
overview: 'Overview',
|
||||
latencyAnalysis: 'Latency analysis',
|
||||
requests: 'Requests',
|
||||
tokenThroughput: 'Token throughput',
|
||||
running: 'Running requests',
|
||||
waiting: 'Waiting requests',
|
||||
cacheUsage: 'KV cache usage',
|
||||
promptThroughput: 'Input token throughput',
|
||||
generationThroughput: 'Output token throughput',
|
||||
requestThroughput: 'Completed requests',
|
||||
timeToFirstToken: 'Mean time to first token',
|
||||
timePerOutputToken: 'Mean inter-token latency',
|
||||
requestLatency: 'Mean request latency',
|
||||
unavailable: 'Metrics are unavailable. Check that the instance is running and /metrics is accessible.',
|
||||
loadFailed: 'Failed to load monitoring data. Please retry.',
|
||||
helper: 'Manage collection, interval and retention in monitoring settings.',
|
||||
cache: 'KV cache',
|
||||
throughput: 'Request throughput',
|
||||
phaseTime: 'Prefill and decode time',
|
||||
prefillTime: 'Mean prefill time',
|
||||
decodeTime: 'Mean decode time',
|
||||
requestLatencyChart: 'End-to-end request latency',
|
||||
timeToFirstTokenChart: 'Time to first token',
|
||||
timePerOutputTokenChart: 'Inter-token latency',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'Memory read/write activity',
|
||||
hotspotTemperature: 'Hotspot temperature',
|
||||
fanRPM: 'Fan speed (RPM)',
|
||||
eccCorrectable: 'Correctable errors',
|
||||
eccUncorrectable: 'Uncorrectable errors',
|
||||
eccPending: 'Pending ECC mode',
|
||||
engineUtil: 'Engine utilization',
|
||||
cpuUtil: 'CPU · utilization',
|
||||
aiCore: 'AICore · utilization',
|
||||
aiCPUUtil: 'AICPU · utilization',
|
||||
ctrlCPUUtil: 'CtrlCPU · utilization',
|
||||
mediaFrequency: 'Media · frequency',
|
||||
encoderUtil: 'Encoder · utilization',
|
||||
decoderUtil: 'Decoder · utilization',
|
||||
jpegUtil: 'JPEG · utilization',
|
||||
ofaUtil: 'OFA · utilization',
|
||||
mediaUtil: 'Media · utilization',
|
||||
computeUtil: 'Compute · utilization',
|
||||
copyUtil: 'Copy · utilization',
|
||||
ddrUsage: 'DDR · capacity',
|
||||
ddrUsed: 'DDR · used',
|
||||
ddrTotal: 'DDR · total',
|
||||
ddrBandwidth: 'DDR · bandwidth utilization',
|
||||
hbmUsage: 'HBM · capacity',
|
||||
hbmUsed: 'HBM · used',
|
||||
hbmTotal: 'HBM · total',
|
||||
hbmBandwidth: 'HBM · bandwidth utilization',
|
||||
hugepagesUsage: 'Huge pages · count',
|
||||
hugepagesUsed: 'Huge pages · used',
|
||||
hugepagesTotal: 'Huge pages · total',
|
||||
|
||||
historyAverage: 'Average',
|
||||
historyPeak: 'Peak',
|
||||
historyAggregated:
|
||||
'{count} samples aggregated into {seconds}-second intervals; processes show counts only.',
|
||||
historyRaw: '{count} original samples.',
|
||||
historyMetricEmpty: 'No data for this metric in the selected time range',
|
||||
historyLoadFailed: 'Failed to load history. Please retry.',
|
||||
collectedAt: 'Collected at',
|
||||
processUnavailable: 'Process information is unavailable',
|
||||
architecture: 'Architecture',
|
||||
memoryFrequency: 'Memory frequency',
|
||||
memoryTemperature: 'Memory temperature',
|
||||
memoryReserved: 'Reserved memory',
|
||||
powerDefaultLimit: 'Default power limit',
|
||||
powerMaxLimit: 'Maximum configurable power limit',
|
||||
clockEvents: 'Clock limiting reasons',
|
||||
memoryBandwidth: 'Memory bandwidth utilization',
|
||||
legacyDevice: 'Legacy record',
|
||||
legacyHistory:
|
||||
'These records have no stable device ID. Their identity and missing values cannot be verified.',
|
||||
pcieGeneration: 'PCIe generation (current / maximum)',
|
||||
pcieWidth: 'PCIe width (current / maximum)',
|
||||
gpu: 'GPU Monitoring',
|
||||
gpuHelper: 'No supported GPU/XPU/NPU management command was detected. Please check and try again!',
|
||||
process: 'Process Information',
|
||||
@@ -1523,7 +1604,7 @@ const message = {
|
||||
temperature: 'Temperature',
|
||||
performanceState: 'Performance State',
|
||||
powerUsage: 'Power Consumption',
|
||||
memoryUsage: 'Memory Utilization',
|
||||
memoryUsage: 'Memory capacity usage',
|
||||
fanSpeed: 'Fan Speed',
|
||||
power: 'Power',
|
||||
powerCurrent: 'Current Power',
|
||||
@@ -2066,6 +2147,9 @@ const message = {
|
||||
cleanLogscope: 'Log retention',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'Host Monitoring',
|
||||
gpuMonitor: 'GPU Monitoring',
|
||||
vllmMonitor: 'vLLM Monitoring',
|
||||
globalFilter: 'Global Filter',
|
||||
enableMonitor: 'Monitoring Status',
|
||||
storeDays: 'Retention Days',
|
||||
@@ -2074,7 +2158,7 @@ const message = {
|
||||
defaultIO: 'Default Disk',
|
||||
defaultIOHelper: 'Default disk option displayed in monitoring and overview interfaces',
|
||||
cleanMonitor: 'Clear Monitoring Records',
|
||||
cleanHelper: 'This operation will clear all monitoring records including GPU. Continue?',
|
||||
cleanHelper: 'This operation will clear all history for {0}. Continue?',
|
||||
avgLoad: 'Load average',
|
||||
loadDetail: 'Load detail',
|
||||
resourceUsage: 'Utilization',
|
||||
@@ -2132,6 +2216,22 @@ const message = {
|
||||
profileGoroutineDesc: 'Saves the state and call stack of every goroutine and groups identical call paths.',
|
||||
profileMutexDesc: 'Measures wait time caused by mutex contention to locate lock contention hotspots.',
|
||||
profileBlockDesc: 'Measures blocking on channels, select statements, and synchronization primitives.',
|
||||
exportRange: 'Time range',
|
||||
exportMetrics: 'Metrics',
|
||||
exportMetricsHelper: 'CPU, memory and load metrics are merged into one table.',
|
||||
exportAllDevices: 'Each device (per-device detail)',
|
||||
exportAllInterfaces: 'Each interface (per-interface detail)',
|
||||
exportFormat: 'Format',
|
||||
exportCsvHelper: 'Multiple metrics are exported as separate CSV files packed into a ZIP archive.',
|
||||
exportJsonHelper: 'Export as a single structured JSON file.',
|
||||
exportXlsxHelper: 'Each metric is exported as a separate worksheet in one workbook.',
|
||||
exportTruncated: 'Excel row limit reached; these datasets were truncated: {0}',
|
||||
exportEmptyTables: 'No data in the selected time range for: {0}',
|
||||
exportAggregate: 'Combined total (all devices)',
|
||||
exportAllGPUs: 'Each model (per-model detail)',
|
||||
exportOptionsFailed: 'Failed to load the device list for: {0}. Refresh the page and retry.',
|
||||
selectMetrics: 'Select at least one metric to export.',
|
||||
selectTimeRange: 'Select the time range to export.',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'Terminal Shortcut',
|
||||
@@ -6723,6 +6823,15 @@ const message = {
|
||||
snapshot: 'System Snapshot',
|
||||
clamsRulesHelper: 'Virus scanning tasks that require alerts',
|
||||
cronJobRulesHelper: 'This type of scheduled task needs to be configured',
|
||||
alertTriggerMode: 'Notify on',
|
||||
alertTriggerFailed: 'Failure only',
|
||||
alertTriggerSuccess: 'Success only',
|
||||
alertTriggerBoth: 'Success or failure',
|
||||
alertNotificationHelper:
|
||||
'Notify after the final execution result, not during retries. Success and failure notifications share the daily limit and sending hours.',
|
||||
notificationTitle: 'Scheduled task - {0} 「{1}」 execution notification',
|
||||
notificationSuccessTitle: 'Scheduled task - {0} 「{1}」 completed successfully',
|
||||
notificationRule: '{0}: {1}, up to {2} notifications per day',
|
||||
clamsTitle: 'Virus scan task 「 {0} 」 detected infected file alert',
|
||||
cronJobAppTitle: 'Cronjob - Backup App 「 {0} 」 Task Failure Alert',
|
||||
cronJobWebsiteTitle: 'Cronjob - Backup Website「 {0} 」Task Failure Alert',
|
||||
@@ -7084,9 +7193,34 @@ const message = {
|
||||
orphan_snapshot: 'Orphaned Snapshot',
|
||||
partial_file: 'Incomplete File',
|
||||
},
|
||||
disks: 'Disks',
|
||||
resizeDisk: 'Expand',
|
||||
diskTarget: 'Device',
|
||||
diskCapacityAfterResize: 'Total capacity after expansion',
|
||||
diskResizeHelper:
|
||||
'Expansion only increases the virtual disk capacity. Afterwards, extend the partitions and filesystems inside the virtual machine.',
|
||||
diskResizeLimit:
|
||||
'Expansion requires a shut-off VM and an independent, writable qcow2 disk without snapshots or a backing chain. Shrinking is not supported.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'Cannot complete virtual disk reference checks. Expansion is unavailable; check the disks of other virtual machines.',
|
||||
referenced:
|
||||
'This virtual disk is used by another virtual machine or device and cannot be expanded yet.',
|
||||
running: 'Shut down the virtual machine before expanding the disk.',
|
||||
saved: 'Resume the virtual machine, then shut it down normally before expanding.',
|
||||
snapshots: 'Expansion is unavailable while snapshots exist to avoid affecting snapshot recovery.',
|
||||
backing: 'The disk depends on another image; expansion is not supported yet.',
|
||||
shared: 'The disk is marked as shared; expansion is unavailable to avoid affecting other users.',
|
||||
unsupported: 'The disk format, location or access permissions do not support expansion.',
|
||||
unavailable: 'Cannot verify disk status. Check it and refresh.',
|
||||
},
|
||||
diskSize: 'Disk Size',
|
||||
isoHelper:
|
||||
'Mount multiple ISOs. Select the operating system installer first, then driver images such as VirtIO. Each image uses a separate CD-ROM drive.',
|
||||
windowsDriverHelper:
|
||||
'The Windows image may not include the VirtIO network driver (NetKVM). Mount a VirtIO driver ISO and install the driver in Windows. Ignore this if the image already includes the driver.',
|
||||
windowsDiskDriverHelper:
|
||||
'VirtIO disks may also require loading the disk driver (viostor); otherwise, Windows Setup may not detect the disk.',
|
||||
osType: 'Operating System Type',
|
||||
osOther: 'Other',
|
||||
diskBus: 'Disk Bus',
|
||||
@@ -7138,6 +7272,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'Console',
|
||||
consoleShortcutKeys: 'Shortcut keys',
|
||||
consoleCustomKeys: 'Custom key combination',
|
||||
consoleMainKey: 'Select main key',
|
||||
consoleSendKeys: 'Send',
|
||||
consoleNotReady: 'The VNC console is unavailable. Start the VM and check its VNC graphics configuration.',
|
||||
consoleDisconnected: 'The VNC console has been disconnected.',
|
||||
consoleConnectFailed: 'Failed to connect to the VNC console. Check the console proxy service.',
|
||||
|
||||
@@ -1516,7 +1516,89 @@ const message = {
|
||||
ignoreEos: 'Ignorar EOS',
|
||||
extraHeaders: 'Encabezados adicionales',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'¿Borrar solo el historial de monitoreo de esta instancia de vLLM? No afecta a otras instancias ni al monitoreo del host o de GPU.',
|
||||
overview: 'Resumen',
|
||||
latencyAnalysis: 'Análisis de latencia',
|
||||
requests: 'Solicitudes',
|
||||
tokenThroughput: 'Rendimiento de tokens',
|
||||
running: 'Solicitudes en ejecución',
|
||||
waiting: 'Solicitudes en espera',
|
||||
cacheUsage: 'Uso de caché KV',
|
||||
promptThroughput: 'Rendimiento de tokens de entrada',
|
||||
generationThroughput: 'Rendimiento de tokens de salida',
|
||||
requestThroughput: 'Solicitudes completadas',
|
||||
timeToFirstToken: 'Latencia media del primer token',
|
||||
timePerOutputToken: 'Latencia media entre tokens',
|
||||
requestLatency: 'Latencia media de solicitud',
|
||||
unavailable:
|
||||
'Métricas no disponibles. Compruebe que la instancia esté en ejecución y que /metrics sea accesible.',
|
||||
loadFailed: 'No se pudieron cargar los datos. Inténtelo de nuevo.',
|
||||
helper: 'Configura la recopilación, el intervalo y la retención en los ajustes de monitorización.',
|
||||
cache: 'Caché KV',
|
||||
throughput: 'Solicitudes/s',
|
||||
phaseTime: 'Tiempo de precarga y decodificación',
|
||||
prefillTime: 'Tiempo medio de precarga',
|
||||
decodeTime: 'Tiempo medio de decodificación',
|
||||
requestLatencyChart: 'Latencia de solicitud de extremo a extremo',
|
||||
timeToFirstTokenChart: 'Tiempo hasta el primer token',
|
||||
timePerOutputTokenChart: 'Latencia entre tokens',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'Actividad de lectura/escritura de memoria',
|
||||
hotspotTemperature: 'Temperatura del punto caliente',
|
||||
fanRPM: 'Velocidad del ventilador (RPM)',
|
||||
eccCorrectable: 'Errores corregibles',
|
||||
eccUncorrectable: 'Errores no corregibles',
|
||||
eccPending: 'Modo ECC pendiente',
|
||||
engineUtil: 'Utilización de motores',
|
||||
cpuUtil: 'CPU · utilización',
|
||||
aiCore: 'AICore · utilización',
|
||||
aiCPUUtil: 'AICPU · utilización',
|
||||
ctrlCPUUtil: 'CtrlCPU · utilización',
|
||||
mediaFrequency: 'Medios · frecuencia',
|
||||
encoderUtil: 'Codificador · utilización',
|
||||
decoderUtil: 'Decodificador · utilización',
|
||||
jpegUtil: 'JPEG · utilización',
|
||||
ofaUtil: 'OFA · utilización',
|
||||
mediaUtil: 'Medios · utilización',
|
||||
computeUtil: 'Cómputo · utilización',
|
||||
copyUtil: 'Copia · utilización',
|
||||
ddrUsage: 'DDR · capacidad',
|
||||
ddrUsed: 'DDR · usado',
|
||||
ddrTotal: 'DDR · total',
|
||||
ddrBandwidth: 'DDR · utilización del ancho de banda',
|
||||
hbmUsage: 'HBM · capacidad',
|
||||
hbmUsed: 'HBM · usado',
|
||||
hbmTotal: 'HBM · total',
|
||||
hbmBandwidth: 'HBM · utilización del ancho de banda',
|
||||
hugepagesUsage: 'Páginas grandes · cantidad',
|
||||
hugepagesUsed: 'Páginas grandes · usado',
|
||||
hugepagesTotal: 'Páginas grandes · total',
|
||||
|
||||
historyAverage: 'Promedio',
|
||||
historyPeak: 'Máximo',
|
||||
historyAggregated:
|
||||
'{count} muestras agrupadas en intervalos de {seconds} segundos; los procesos solo muestran cantidades.',
|
||||
historyRaw: '{count} muestras originales.',
|
||||
historyMetricEmpty: 'No hay datos de esta métrica en el período seleccionado',
|
||||
historyLoadFailed: 'No se pudo cargar el historial. Inténtelo de nuevo.',
|
||||
collectedAt: 'Fecha de recopilación',
|
||||
processUnavailable: 'Información de procesos no disponible',
|
||||
architecture: 'Arquitectura',
|
||||
memoryFrequency: 'Frecuencia de memoria',
|
||||
memoryTemperature: 'Temperatura de memoria',
|
||||
memoryReserved: 'Memoria reservada',
|
||||
powerDefaultLimit: 'Límite de potencia predeterminado',
|
||||
powerMaxLimit: 'Potencia máxima configurable',
|
||||
clockEvents: 'Motivos de limitación de frecuencia',
|
||||
memoryBandwidth: 'Uso del ancho de banda de memoria',
|
||||
legacyDevice: 'Registro antiguo',
|
||||
legacyHistory:
|
||||
'Estos registros no tienen un ID de dispositivo estable. No se pueden verificar su identidad ni los valores ausentes.',
|
||||
pcieGeneration: 'Generación PCIe (actual / máxima)',
|
||||
pcieWidth: 'Ancho PCIe (actual / máximo)',
|
||||
gpu: 'Monitoreo de GPU',
|
||||
gpuHelper:
|
||||
'No se detectó ningún comando de administración de GPU/XPU/NPU compatible. ¡Compruebe e inténtelo de nuevo!',
|
||||
@@ -1532,7 +1614,7 @@ const message = {
|
||||
temperature: 'Temperatura',
|
||||
performanceState: 'Estado de Rendimiento',
|
||||
powerUsage: 'Consumo de Energía',
|
||||
memoryUsage: 'Utilización de Memoria',
|
||||
memoryUsage: 'Uso de capacidad de memoria',
|
||||
fanSpeed: 'Velocidad del Ventilador',
|
||||
power: 'Energía',
|
||||
powerCurrent: 'Energía Actual',
|
||||
@@ -2102,6 +2184,9 @@ const message = {
|
||||
cleanLogscope: 'Retención de logs',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'Monitoreo del host',
|
||||
gpuMonitor: 'Monitoreo de GPU',
|
||||
vllmMonitor: 'Monitoreo de vLLM',
|
||||
globalFilter: 'Filtro Global',
|
||||
enableMonitor: 'Estado de Monitoreo',
|
||||
storeDays: 'Días de Retención',
|
||||
@@ -2111,7 +2196,7 @@ const message = {
|
||||
defaultIO: 'Disco Predeterminado',
|
||||
defaultIOHelper: 'Opción de disco predeterminada mostrada en las interfaces de monitoreo y resumen',
|
||||
cleanMonitor: 'Limpiar Registros de Monitoreo',
|
||||
cleanHelper: 'Esta operación borrará todos los registros de monitoreo, incluidos los de GPU. ¿Continuar?',
|
||||
cleanHelper: 'Esta operación borrará todo el historial de {0}. ¿Continuar?',
|
||||
avgLoad: 'Carga promedio',
|
||||
loadDetail: 'Detalle de carga',
|
||||
resourceUsage: 'Utilización',
|
||||
@@ -2170,6 +2255,22 @@ const message = {
|
||||
profileMutexDesc:
|
||||
'Mide el tiempo de espera causado por la contención de mutex para localizar bloqueos disputados.',
|
||||
profileBlockDesc: 'Mide los bloqueos en canales, select y primitivas de sincronización.',
|
||||
exportRange: 'Intervalo de tiempo',
|
||||
exportMetrics: 'Métricas',
|
||||
exportMetricsHelper: 'Las métricas de CPU, memoria y carga se combinan en una sola tabla.',
|
||||
exportAllDevices: 'Cada dispositivo (detalle por dispositivo)',
|
||||
exportAllInterfaces: 'Cada interfaz (detalle por interfaz)',
|
||||
exportFormat: 'Formato',
|
||||
exportCsvHelper: 'Al seleccionar varias métricas se generan CSV separados empaquetados en un ZIP.',
|
||||
exportJsonHelper: 'Exportar como un único archivo JSON estructurado.',
|
||||
exportXlsxHelper: 'Cada métrica se exporta en una hoja independiente.',
|
||||
exportTruncated: 'Se superó el límite de filas de Excel; estos conjuntos se truncaron: {0}',
|
||||
exportEmptyTables: 'Sin datos en el rango seleccionado para: {0}',
|
||||
exportAggregate: 'Total combinado (todos los dispositivos)',
|
||||
exportAllGPUs: 'Cada modelo (detalle por modelo)',
|
||||
exportOptionsFailed: 'No se pudo cargar la lista de dispositivos para: {0}. Actualiza la página y reintenta.',
|
||||
selectMetrics: 'Selecciona al menos una métrica para exportar',
|
||||
selectTimeRange: 'Selecciona el intervalo de tiempo a exportar',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'Acceso rápido al terminal',
|
||||
@@ -6835,6 +6936,15 @@ const message = {
|
||||
snapshot: 'Snapshot del sistema',
|
||||
clamsRulesHelper: 'Tareas de escaneo antivirus que requieren alerta',
|
||||
cronJobRulesHelper: 'Este tipo de tareas programadas necesita configuración',
|
||||
alertTriggerMode: 'Notificar cuando',
|
||||
alertTriggerFailed: 'Solo falle',
|
||||
alertTriggerSuccess: 'Solo tenga éxito',
|
||||
alertTriggerBoth: 'Tenga éxito o falle',
|
||||
alertNotificationHelper:
|
||||
'Notificar al finalizar la ejecución, no durante los reintentos. Las notificaciones de éxito y fallo comparten el límite diario y el horario de envío.',
|
||||
notificationTitle: 'Tarea programada - {0} 「{1}」 notificación de ejecución',
|
||||
notificationSuccessTitle: 'Tarea programada - {0} 「{1}」 completada correctamente',
|
||||
notificationRule: '{0}: {1}, hasta {2} notificaciones al día',
|
||||
clamsTitle: 'Tarea antivirus 「 {0} 」 detectó archivo infectado',
|
||||
cronJobAppTitle: 'Cronjob - Backup de app 「 {0} 」 falló',
|
||||
cronJobWebsiteTitle: 'Cronjob - Backup de sitio 「 {0} 」 falló',
|
||||
@@ -7197,9 +7307,34 @@ const message = {
|
||||
orphan_snapshot: 'Instantánea huérfana',
|
||||
partial_file: 'Archivo incompleto',
|
||||
},
|
||||
disks: 'Discos',
|
||||
resizeDisk: 'Ampliar',
|
||||
diskTarget: 'Dispositivo',
|
||||
diskCapacityAfterResize: 'Capacidad total tras la ampliación',
|
||||
diskResizeHelper:
|
||||
'La ampliación solo aumenta la capacidad del disco virtual. Después, amplíe las particiones y los sistemas de archivos dentro de la máquina virtual.',
|
||||
diskResizeLimit:
|
||||
'La máquina virtual debe estar apagada y el disco qcow2 debe ser independiente y escribible, sin instantáneas ni cadena de imágenes de respaldo. No se admite reducir el tamaño.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'No se pueden comprobar las referencias al disco virtual. La ampliación no está disponible; revise los discos de otras máquinas virtuales.',
|
||||
referenced:
|
||||
'Otro equipo virtual o dispositivo utiliza este disco virtual; la ampliación aún no se admite.',
|
||||
running: 'Apague la máquina virtual antes de ampliar el disco.',
|
||||
saved: 'Reanude la máquina virtual y apáguela normalmente antes de ampliar.',
|
||||
snapshots: 'No se admite la ampliación con instantáneas para no afectar a su restauración.',
|
||||
backing: 'El disco depende de otra imagen; la ampliación aún no se admite.',
|
||||
shared: 'El disco está marcado como compartido; no se permite ampliarlo para no afectar a otros usuarios.',
|
||||
unsupported: 'El formato, la ubicación o los permisos del disco no permiten ampliarlo.',
|
||||
unavailable: 'No se puede verificar el estado del disco. Revise y actualice.',
|
||||
},
|
||||
diskSize: 'Tamaño del disco',
|
||||
isoHelper:
|
||||
'Puede montar varios ISO. Seleccione primero el instalador del sistema operativo y después las imágenes de controladores, como VirtIO. Cada imagen usa una unidad de CD-ROM independiente.',
|
||||
windowsDriverHelper:
|
||||
'Es posible que la imagen de Windows no incluya el controlador de red VirtIO (NetKVM). Monte una ISO de controladores VirtIO e instale el controlador en Windows. Omita este aviso si la imagen ya incluye el controlador.',
|
||||
windowsDiskDriverHelper:
|
||||
'Los discos VirtIO también pueden requerir que se cargue el controlador de disco (viostor); de lo contrario, el instalador de Windows podría no detectar el disco.',
|
||||
osType: 'Tipo de sistema operativo',
|
||||
osOther: 'Otro',
|
||||
diskBus: 'Bus de disco',
|
||||
@@ -7252,6 +7387,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'Consola',
|
||||
consoleShortcutKeys: 'Atajos de teclado',
|
||||
consoleCustomKeys: 'Combinación personalizada',
|
||||
consoleMainKey: 'Seleccionar tecla',
|
||||
consoleSendKeys: 'Enviar',
|
||||
consoleNotReady:
|
||||
'La consola VNC no está disponible. Inicie la VM y compruebe su configuración gráfica VNC.',
|
||||
consoleDisconnected: 'La consola VNC se ha desconectado.',
|
||||
|
||||
@@ -1492,7 +1492,87 @@ const message = {
|
||||
ignoreEos: 'نادیده گرفتن EOS',
|
||||
extraHeaders: 'هدرهای اضافی',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'فقط سابقه نظارت این نمونه vLLM پاک شود؟ سایر نمونه\u200cها و نظارت میزبان و GPU تغییری نمی\u200cکنند.',
|
||||
overview: 'نمای کلی',
|
||||
latencyAnalysis: 'تحلیل تأخیر',
|
||||
requests: 'تعداد درخواستها',
|
||||
tokenThroughput: 'توان عملیاتی توکن',
|
||||
running: 'درخواستهای در حال اجرا',
|
||||
waiting: 'درخواستهای در انتظار',
|
||||
cacheUsage: 'مصرف حافظه نهان KV',
|
||||
promptThroughput: 'گذردهی توکن ورودی',
|
||||
generationThroughput: 'گذردهی توکن خروجی',
|
||||
requestThroughput: 'نرخ درخواستهای تکمیلشده',
|
||||
timeToFirstToken: 'میانگین تأخیر توکن اول',
|
||||
timePerOutputToken: 'میانگین تأخیر بین توکنها',
|
||||
requestLatency: 'میانگین تأخیر درخواست',
|
||||
unavailable: 'معیارها در دسترس نیستند. از اجرای نمونه و دسترسی به /metrics مطمئن شوید.',
|
||||
loadFailed: 'بارگیری دادههای پایش ناموفق بود. دوباره تلاش کنید.',
|
||||
helper: 'فعالسازی جمعآوری، فاصله زمانی و مدت نگهداری را در تنظیمات پایش تغییر دهید.',
|
||||
cache: 'حافظه نهان KV',
|
||||
throughput: 'گذردهی درخواست',
|
||||
phaseTime: 'زمان پیشپرکردن و رمزگشایی',
|
||||
prefillTime: 'میانگین زمان پیشپرکردن',
|
||||
decodeTime: 'میانگین زمان رمزگشایی',
|
||||
requestLatencyChart: 'تأخیر سرتاسری درخواست',
|
||||
timeToFirstTokenChart: 'زمان تا توکن اول',
|
||||
timePerOutputTokenChart: 'تأخیر بین توکنها',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'فعالیت خواندن/نوشتن حافظه',
|
||||
hotspotTemperature: 'دمای نقطه داغ',
|
||||
fanRPM: 'سرعت فن (RPM)',
|
||||
eccCorrectable: 'خطاهای قابل اصلاح',
|
||||
eccUncorrectable: 'خطاهای غیرقابل اصلاح',
|
||||
eccPending: 'حالت ECC در انتظار اعمال',
|
||||
engineUtil: 'میزان استفاده از موتور',
|
||||
cpuUtil: 'CPU · میزان استفاده',
|
||||
aiCore: 'AICore · میزان استفاده',
|
||||
aiCPUUtil: 'AICPU · میزان استفاده',
|
||||
ctrlCPUUtil: 'CtrlCPU · میزان استفاده',
|
||||
mediaFrequency: 'رسانه · فرکانس',
|
||||
encoderUtil: 'رمزگذار · میزان استفاده',
|
||||
decoderUtil: 'رمزگشا · میزان استفاده',
|
||||
jpegUtil: 'JPEG · میزان استفاده',
|
||||
ofaUtil: 'OFA · میزان استفاده',
|
||||
mediaUtil: 'رسانه · میزان استفاده',
|
||||
computeUtil: 'محاسبه · میزان استفاده',
|
||||
copyUtil: 'کپی · میزان استفاده',
|
||||
ddrUsage: 'DDR · ظرفیت',
|
||||
ddrUsed: 'DDR · استفادهشده',
|
||||
ddrTotal: 'DDR · کل',
|
||||
ddrBandwidth: 'DDR · میزان استفاده از پهنای باند',
|
||||
hbmUsage: 'HBM · ظرفیت',
|
||||
hbmUsed: 'HBM · استفادهشده',
|
||||
hbmTotal: 'HBM · کل',
|
||||
hbmBandwidth: 'HBM · میزان استفاده از پهنای باند',
|
||||
hugepagesUsage: 'صفحات بزرگ · تعداد',
|
||||
hugepagesUsed: 'صفحات بزرگ · استفادهشده',
|
||||
hugepagesTotal: 'صفحات بزرگ · کل',
|
||||
|
||||
historyAverage: 'میانگین',
|
||||
historyPeak: 'بیشینه',
|
||||
historyAggregated:
|
||||
'{count} نمونه در بازههای {seconds} ثانیهای تجمیع شدهاند؛ فقط تعداد فرایندها نمایش داده میشود.',
|
||||
historyRaw: '{count} نمونه اصلی.',
|
||||
historyMetricEmpty: 'در بازه زمانی انتخابشده دادهای برای این شاخص وجود ندارد',
|
||||
historyLoadFailed: 'بارگیری سابقه ناموفق بود. دوباره تلاش کنید.',
|
||||
collectedAt: 'زمان گردآوری',
|
||||
processUnavailable: 'اطلاعات فرایند در دسترس نیست',
|
||||
architecture: 'معماری',
|
||||
memoryFrequency: 'فرکانس حافظه',
|
||||
memoryTemperature: 'دمای حافظه',
|
||||
memoryReserved: 'حافظه رزروشده',
|
||||
powerDefaultLimit: 'محدودیت توان پیشفرض',
|
||||
powerMaxLimit: 'حداکثر توان قابل تنظیم',
|
||||
clockEvents: 'دلایل محدودیت فرکانس',
|
||||
memoryBandwidth: 'میزان استفاده از پهنای باند حافظه',
|
||||
legacyDevice: 'رکورد قدیمی',
|
||||
legacyHistory: 'این رکوردها شناسه ثابت دستگاه ندارند. هویت دستگاه و مقادیر مفقود قابل تأیید نیستند.',
|
||||
pcieGeneration: 'نسل PCIe (فعلی / حداکثر)',
|
||||
pcieWidth: 'عرض PCIe (فعلی / حداکثر)',
|
||||
gpu: 'نظارت بر GPU',
|
||||
gpuHelper: 'هیچ دستور مدیریت GPU/XPU/NPU پشتیبانیشدهای شناسایی نشد. لطفاً بررسی و دوباره تلاش کنید!',
|
||||
process: 'اطلاعات فرآیند',
|
||||
@@ -1508,7 +1588,7 @@ const message = {
|
||||
temperature: 'دما',
|
||||
performanceState: 'وضعیت عملکرد',
|
||||
powerUsage: 'مصرف برق',
|
||||
memoryUsage: 'استفاده از حافظه',
|
||||
memoryUsage: 'میزان اشغال ظرفیت حافظه',
|
||||
fanSpeed: 'سرعت فن',
|
||||
power: 'برق',
|
||||
powerCurrent: 'برق فعلی',
|
||||
@@ -2051,6 +2131,9 @@ const message = {
|
||||
cleanLogscope: 'نگهداری لاگ',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'نظارت میزبان',
|
||||
gpuMonitor: 'نظارت GPU',
|
||||
vllmMonitor: 'نظارت vLLM',
|
||||
globalFilter: 'فیلتر جهانی',
|
||||
enableMonitor: 'وضعیت نظارت',
|
||||
storeDays: 'روزهای نگهداری',
|
||||
@@ -2059,7 +2142,7 @@ const message = {
|
||||
defaultIO: 'دیسک پیشفرض',
|
||||
defaultIOHelper: 'گزینه دیسک پیشفرض که در رابطهای نظارت و نمای کلی نمایش داده میشود',
|
||||
cleanMonitor: 'پاکسازی رکوردهای نظارت',
|
||||
cleanHelper: 'این عملیات همه رکوردهای نظارت از جمله GPU را پاک میکند. ادامه میدهید؟',
|
||||
cleanHelper: 'این عملیات تمام سوابق {0} را پاک میکند. ادامه میدهید؟',
|
||||
avgLoad: 'میانگین بار',
|
||||
loadDetail: 'جزئیات بار',
|
||||
resourceUsage: 'استفاده از منابع',
|
||||
@@ -2112,6 +2195,23 @@ const message = {
|
||||
profileGoroutineDesc: 'وضعیت و پشته فراخوانی هر Goroutine را ذخیره و مسیرهای یکسان را گروهبندی میکند.',
|
||||
profileMutexDesc: 'زمان انتظار ناشی از رقابت mutex را برای یافتن نقاط رقابت قفل اندازهگیری میکند.',
|
||||
profileBlockDesc: 'زمان انسداد روی Channel، Select و ابزارهای همگامسازی را اندازهگیری میکند.',
|
||||
exportRange: 'بازه زمانی',
|
||||
exportMetrics: 'معیارها',
|
||||
exportMetricsHelper: 'معیارهای CPU، حافظه و بار در یک جدول ادغام میشوند.',
|
||||
exportAllDevices: 'هر دستگاه (جزئیات به تفکیک دستگاه)',
|
||||
exportAllInterfaces: 'هر رابط (جزئیات به تفکیک رابط)',
|
||||
exportFormat: 'قالب',
|
||||
exportCsvHelper: 'هنگام انتخاب چند معیار، فایلهای CSV جداگانه در یک بایگانی ZIP بستهبندی میشوند.',
|
||||
exportJsonHelper: 'بهصورت یک فایل JSON ساختیافته واحد خروجی بگیرید.',
|
||||
exportXlsxHelper: 'هر معیار بهصورت یک کاربرگ جداگانه خروجی گرفته میشود.',
|
||||
exportTruncated: 'از حد ردیف اکسل فراتر رفت؛ این مجموعهدادهها بریده شدند: {0}',
|
||||
exportEmptyTables: 'در بازه انتخابشده دادهای برای این موارد نیست: {0}',
|
||||
exportAggregate: 'مجموع ترکیبی (همه دستگاهها)',
|
||||
exportAllGPUs: 'هر مدل (جزئیات به تفکیک مدل)',
|
||||
exportOptionsFailed:
|
||||
'بارگیری فهرست دستگاه برای موارد زیر ناموفق بود: {0}. صفحه را تازه کرده و دوباره تلاش کنید.',
|
||||
selectMetrics: 'حداقل یک معیار برای خروجی انتخاب کنید',
|
||||
selectTimeRange: 'بازه زمانی خروجی را انتخاب کنید',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'میانبر ترمینال',
|
||||
@@ -6677,6 +6777,15 @@ const message = {
|
||||
snapshot: 'تصویر لحظهای سیستم',
|
||||
clamsRulesHelper: 'وظایف اسکن ویروس که نیاز به هشدار دارند',
|
||||
cronJobRulesHelper: 'این نوع وظیفه زمانبندی باید پیکربندی شود',
|
||||
alertTriggerMode: 'شرط اعلان',
|
||||
alertTriggerFailed: 'فقط شکست',
|
||||
alertTriggerSuccess: 'فقط موفقیت',
|
||||
alertTriggerBoth: 'موفقیت یا شکست',
|
||||
alertNotificationHelper:
|
||||
'اعلان پس از نتیجه نهایی اجرا ارسال میشود، نه هنگام تلاش مجدد. اعلانهای موفقیت و شکست سقف روزانه و ساعات ارسال مشترک دارند.',
|
||||
notificationTitle: 'وظیفه زمانبندی - {0} «{1}» اعلان اجرا',
|
||||
notificationSuccessTitle: 'وظیفه زمانبندی - {0} «{1}» با موفقیت انجام شد',
|
||||
notificationRule: '{0}: {1}، حداکثر {2} اعلان در روز',
|
||||
clamsTitle: 'وظیفه اسکن ویروس « {0} » هشدار فایل آلوده شناسایی شد',
|
||||
cronJobAppTitle: 'وظیفه زمانبندی - پشتیبانگیری از برنامه « {0} » هشدار شکست وظیفه',
|
||||
cronJobWebsiteTitle: 'وظیفه زمانبندی - پشتیبانگیری از وبسایت « {0} » هشدار شکست وظیفه',
|
||||
@@ -7038,9 +7147,34 @@ const message = {
|
||||
orphan_snapshot: 'اسنپشات بدون مرجع',
|
||||
partial_file: 'فایل ناتمام',
|
||||
},
|
||||
disks: 'مدیریت دیسکها',
|
||||
resizeDisk: 'افزایش ظرفیت',
|
||||
diskTarget: 'دستگاه',
|
||||
diskCapacityAfterResize: 'ظرفیت کل پس از افزایش',
|
||||
diskResizeHelper:
|
||||
'این عملیات فقط ظرفیت دیسک مجازی را افزایش میدهد. سپس باید پارتیشنها و سیستمهای فایل را درون ماشین مجازی گسترش دهید.',
|
||||
diskResizeLimit:
|
||||
'ماشین مجازی باید خاموش باشد و دیسک qcow2 مستقل و قابلنوشتن، بدون اسنپشات یا زنجیره تصاویر پایه باشد. کاهش ظرفیت پشتیبانی نمیشود.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'بررسی ارجاعهای دیسک مجازی کامل نشد. افزایش ظرفیت ممکن نیست؛ دیسکهای سایر ماشینهای مجازی را بررسی کنید.',
|
||||
referenced:
|
||||
'این دیسک مجازی توسط ماشین مجازی یا دستگاه دیگری استفاده میشود؛ افزایش ظرفیت فعلاً پشتیبانی نمیشود.',
|
||||
running: 'پیش از افزایش ظرفیت دیسک، ماشین مجازی را خاموش کنید.',
|
||||
saved: 'ماشین مجازی را از سر بگیرید، سپس پیش از افزایش ظرفیت بهطور عادی خاموش کنید.',
|
||||
snapshots: 'برای جلوگیری از اختلال در بازیابی، افزایش ظرفیت با وجود اسنپشات پشتیبانی نمیشود.',
|
||||
backing: 'دیسک به تصویر دیگری وابسته است؛ افزایش ظرفیت فعلاً پشتیبانی نمیشود.',
|
||||
shared: 'دیسک اشتراکی است؛ برای جلوگیری از تأثیر بر دیگر کاربران، افزایش ظرفیت پشتیبانی نمیشود.',
|
||||
unsupported: 'قالب، محل یا مجوزهای دسترسی دیسک از افزایش ظرفیت پشتیبانی نمیکنند.',
|
||||
unavailable: 'وضعیت دیسک قابل تأیید نیست. بررسی و تازهسازی کنید.',
|
||||
},
|
||||
diskSize: 'ظرفیت دیسک',
|
||||
isoHelper:
|
||||
'میتوانید چند ISO متصل کنید. ابتدا تصویر نصب سیستمعامل و سپس تصاویر درایور مانند VirtIO را انتخاب کنید. هر تصویر از یک درایو CD-ROM جداگانه استفاده میکند.',
|
||||
windowsDriverHelper:
|
||||
'ممکن است تصویر Windows شامل درایور شبکه VirtIO (NetKVM) نباشد. یک ISO درایور VirtIO را نیز متصل کرده و درایور را در Windows نصب کنید. اگر تصویر از قبل شامل درایور است، این پیام را نادیده بگیرید.',
|
||||
windowsDiskDriverHelper:
|
||||
'دیسکهای VirtIO ممکن است به بارگذاری درایور دیسک (viostor) نیز نیاز داشته باشند؛ در غیر این صورت، برنامه نصب Windows ممکن است دیسک را شناسایی نکند.',
|
||||
osType: 'نوع سیستمعامل',
|
||||
osOther: 'سایر',
|
||||
diskBus: 'گذرگاه دیسک',
|
||||
@@ -7092,6 +7226,10 @@ const message = {
|
||||
rx: 'دریافت',
|
||||
tx: 'ارسال',
|
||||
console: 'کنسول',
|
||||
consoleShortcutKeys: 'کلیدهای میانبر',
|
||||
consoleCustomKeys: 'ترکیب کلید سفارشی',
|
||||
consoleMainKey: 'انتخاب کلید اصلی',
|
||||
consoleSendKeys: 'ارسال',
|
||||
consoleNotReady: 'کنسول VNC در دسترس نیست. ماشین مجازی را شروع کنید و پیکربندی گرافیکی VNC را بررسی کنید.',
|
||||
consoleDisconnected: 'اتصال کنسول VNC قطع شد.',
|
||||
consoleConnectFailed: 'اتصال به کنسول VNC ناموفق بود. سرویس پراکسی کنسول را بررسی کنید.',
|
||||
|
||||
@@ -1495,7 +1495,86 @@ const message = {
|
||||
ignoreEos: 'EOS を無視',
|
||||
extraHeaders: '追加ヘッダー',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'この vLLM インスタンスの監視履歴のみを削除しますか?他のインスタンス、ホスト、GPU の監視には影響しません。',
|
||||
overview: '概要',
|
||||
latencyAnalysis: 'レイテンシ分析',
|
||||
requests: 'リクエスト数',
|
||||
tokenThroughput: 'トークンスループット',
|
||||
running: '実行中のリクエスト',
|
||||
waiting: '待機中のリクエスト',
|
||||
cacheUsage: 'KVキャッシュ使用率',
|
||||
promptThroughput: '入力トークンスループット',
|
||||
generationThroughput: '出力トークンスループット',
|
||||
requestThroughput: '完了リクエスト数/秒',
|
||||
timeToFirstToken: '平均初回トークン遅延',
|
||||
timePerOutputToken: '平均トークン間遅延',
|
||||
requestLatency: '平均リクエスト遅延',
|
||||
unavailable: '指標を取得できません。インスタンスが稼働し、/metrics にアクセスできるか確認してください。',
|
||||
loadFailed: '監視データの読み込みに失敗しました。再試行してください。',
|
||||
helper: '収集の有効化、間隔、保存期間は監視設定で変更できます。',
|
||||
cache: 'KVキャッシュ',
|
||||
throughput: 'リクエストスループット',
|
||||
phaseTime: 'プリフィルとデコードの所要時間',
|
||||
prefillTime: '平均プリフィル時間',
|
||||
decodeTime: '平均デコード時間',
|
||||
requestLatencyChart: 'エンドツーエンドのリクエスト遅延',
|
||||
timeToFirstTokenChart: '初回トークン遅延',
|
||||
timePerOutputTokenChart: 'トークン間遅延',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'メモリ読み書き稼働率',
|
||||
hotspotTemperature: 'ホットスポット温度',
|
||||
fanRPM: 'ファン回転数(RPM)',
|
||||
eccCorrectable: '訂正可能なエラー数',
|
||||
eccUncorrectable: '訂正不能なエラー数',
|
||||
eccPending: '適用待ちの ECC モード',
|
||||
engineUtil: 'エンジン使用率',
|
||||
cpuUtil: 'CPU · 使用率',
|
||||
aiCore: 'AICore · 使用率',
|
||||
aiCPUUtil: 'AICPU · 使用率',
|
||||
ctrlCPUUtil: 'CtrlCPU · 使用率',
|
||||
mediaFrequency: 'メディア · 周波数',
|
||||
encoderUtil: 'エンコーダー · 使用率',
|
||||
decoderUtil: 'デコーダー · 使用率',
|
||||
jpegUtil: 'JPEG · 使用率',
|
||||
ofaUtil: 'OFA · 使用率',
|
||||
mediaUtil: 'メディア · 使用率',
|
||||
computeUtil: '計算 · 使用率',
|
||||
copyUtil: 'コピー · 使用率',
|
||||
ddrUsage: 'DDR · 容量',
|
||||
ddrUsed: 'DDR · 使用量',
|
||||
ddrTotal: 'DDR · 総容量',
|
||||
ddrBandwidth: 'DDR · 帯域使用率',
|
||||
hbmUsage: 'HBM · 容量',
|
||||
hbmUsed: 'HBM · 使用量',
|
||||
hbmTotal: 'HBM · 総容量',
|
||||
hbmBandwidth: 'HBM · 帯域使用率',
|
||||
hugepagesUsage: 'ヒュージページ · 数',
|
||||
hugepagesUsed: 'ヒュージページ · 使用量',
|
||||
hugepagesTotal: 'ヒュージページ · 総容量',
|
||||
|
||||
historyAverage: '平均値',
|
||||
historyPeak: '最大値',
|
||||
historyAggregated: '{count} 件のサンプルを {seconds} 秒間隔で集計。プロセスは件数のみ表示します。',
|
||||
historyRaw: '元のサンプル {count} 件。',
|
||||
historyMetricEmpty: '選択した期間にこの指標のデータはありません',
|
||||
historyLoadFailed: '履歴の読み込みに失敗しました。再試行してください。',
|
||||
collectedAt: '収集日時',
|
||||
processUnavailable: 'プロセス情報を取得できません',
|
||||
architecture: 'アーキテクチャ',
|
||||
memoryFrequency: 'メモリ周波数',
|
||||
memoryTemperature: 'メモリ温度',
|
||||
memoryReserved: '予約済みメモリ',
|
||||
powerDefaultLimit: '既定の電力制限',
|
||||
powerMaxLimit: '設定可能な最大電力',
|
||||
clockEvents: 'クロック制限の理由',
|
||||
memoryBandwidth: 'メモリ帯域幅使用率',
|
||||
legacyDevice: '旧記録',
|
||||
legacyHistory: 'これらの記録には固定のデバイス ID がなく、デバイスの識別や欠損値を確認できません。',
|
||||
pcieGeneration: 'PCIe 世代(現在 / 最大)',
|
||||
pcieWidth: 'PCIe 幅(現在 / 最大)',
|
||||
gpu: 'GPU 監視',
|
||||
gpuHelper: '対応する GPU/XPU/NPU 管理コマンドが検出されませんでした。確認して再試行してください!',
|
||||
process: 'プロセス情報',
|
||||
@@ -1510,7 +1589,7 @@ const message = {
|
||||
temperature: '温度',
|
||||
performanceState: 'パフォーマンス状態',
|
||||
powerUsage: '消費電力',
|
||||
memoryUsage: 'メモリ使用率',
|
||||
memoryUsage: 'メモリ容量使用率',
|
||||
fanSpeed: 'ファン速度',
|
||||
power: '電力',
|
||||
powerCurrent: '現在の電力',
|
||||
@@ -2057,6 +2136,9 @@ const message = {
|
||||
cleanLogscope: 'ログ保持',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'ホスト監視',
|
||||
gpuMonitor: 'GPU 監視',
|
||||
vllmMonitor: 'vLLM 監視',
|
||||
globalFilter: 'グローバルフィルター',
|
||||
enableMonitor: '監視ステータス',
|
||||
storeDays: '保存日数',
|
||||
@@ -2065,7 +2147,7 @@ const message = {
|
||||
defaultIO: 'デフォルトディスク',
|
||||
defaultIOHelper: '監視および概要インターフェースに表示されるデフォルトのディスクオプション',
|
||||
cleanMonitor: '監視記録をクリア',
|
||||
cleanHelper: 'この操作により、GPUを含むすべての監視記録がクリアされます。続行しますか?',
|
||||
cleanHelper: 'この操作は {0} の履歴をすべて削除します。続行しますか?',
|
||||
avgLoad: 'ロード平均',
|
||||
loadDetail: '詳細を読み込みます',
|
||||
resourceUsage: '利用',
|
||||
@@ -2119,6 +2201,22 @@ const message = {
|
||||
profileGoroutineDesc: 'すべての Goroutine の状態と呼び出しスタックを保存し、同じ呼び出し経路を集約します。',
|
||||
profileMutexDesc: 'Mutex 競合による待機時間を測定し、ロック競合のホットスポットを特定します。',
|
||||
profileBlockDesc: 'Channel、Select、同期プリミティブでのブロック待機時間を測定します。',
|
||||
exportRange: '期間',
|
||||
exportMetrics: 'エクスポート指標',
|
||||
exportMetricsHelper: 'CPU、メモリ、負荷の指標は 1 つのテーブルにまとめられます。',
|
||||
exportAllDevices: 'デバイスごと(デバイス別明細)',
|
||||
exportAllInterfaces: 'インターフェースごと(インターフェース別明細)',
|
||||
exportFormat: 'エクスポート形式',
|
||||
exportCsvHelper: '複数の指標を選択した場合、指標ごとに CSV ファイルを生成して ZIP にまとめます。',
|
||||
exportJsonHelper: '構造化された単一の JSON ファイルとしてエクスポートします。',
|
||||
exportXlsxHelper: '指標ごとに独立したワークシートを生成します。',
|
||||
exportTruncated: 'Excel の行数上限を超えたため、以下のデータセットは切り詰められました: {0}',
|
||||
exportEmptyTables: '選択した期間にデータがない指標: {0}',
|
||||
exportAggregate: '合計(全デバイス合算)',
|
||||
exportAllGPUs: 'モデルごと(モデル別明細)',
|
||||
exportOptionsFailed: '次の指標のデバイス一覧を読み込めませんでした。再読み込みして再試行してください: {0}',
|
||||
selectMetrics: 'エクスポートする指標を 1 つ以上選択してください',
|
||||
selectTimeRange: 'エクスポートする期間を選択してください',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'ターミナルへのショートカット',
|
||||
@@ -6717,6 +6815,15 @@ const message = {
|
||||
snapshot: 'システムスナップショット',
|
||||
clamsRulesHelper: 'アラートが必要なウイルススキャンタスク',
|
||||
cronJobRulesHelper: 'このタイプのスケジュールタスクには設定が必要です',
|
||||
alertTriggerMode: '通知条件',
|
||||
alertTriggerFailed: '失敗時のみ',
|
||||
alertTriggerSuccess: '成功時のみ',
|
||||
alertTriggerBoth: '成功時または失敗時',
|
||||
alertNotificationHelper:
|
||||
'再試行中ではなく、最終的な実行結果に基づいて通知します。成功と失敗の通知は、1日の上限と送信時間帯を共有します。',
|
||||
notificationTitle: '定期タスク - {0}「{1}」実行通知',
|
||||
notificationSuccessTitle: '定期タスク - {0}「{1}」が正常に完了しました',
|
||||
notificationRule: '{0}:{1}、1日最大 {2} 回通知',
|
||||
clamsTitle: 'ウイルススキャンタスク「{0}」が感染ファイルを検出したアラート',
|
||||
cronJobAppTitle: 'Cronジョブ - アプリバックアップ「{0}」タスク失敗アラート',
|
||||
cronJobWebsiteTitle: 'Cronジョブ - ウェブサイトバックアップ「{0}」タスク失敗アラート',
|
||||
@@ -7072,9 +7179,34 @@ const message = {
|
||||
orphan_snapshot: '孤立スナップショット',
|
||||
partial_file: '未完了ファイル',
|
||||
},
|
||||
disks: 'ディスク管理',
|
||||
resizeDisk: '拡張',
|
||||
diskTarget: 'デバイス',
|
||||
diskCapacityAfterResize: '拡張後の合計容量',
|
||||
diskResizeHelper:
|
||||
'拡張されるのは仮想ディスクの容量のみです。その後、仮想マシン内でパーティションとファイルシステムを拡張してください。',
|
||||
diskResizeLimit:
|
||||
'停止中の仮想マシンに接続された、スナップショットやバッキングチェーンのない独立した書き込み可能な qcow2 ディスクのみ拡張できます。縮小はできません。',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'仮想ディスクの参照を確認できないため拡張できません。他の仮想マシンのディスク状態を確認してください。',
|
||||
referenced:
|
||||
'この仮想ディスクは他の仮想マシンまたはデバイスで使用されているため、現在は拡張できません。',
|
||||
running: '仮想マシンを停止してからディスクを拡張してください。',
|
||||
saved: '仮想マシンを再開し、正常にシャットダウンしてから拡張してください。',
|
||||
snapshots: 'スナップショットの復元に影響するため、スナップショットがある場合は拡張できません。',
|
||||
backing: '他のイメージに依存するディスクの拡張には未対応です。',
|
||||
shared: '共有設定のディスクは他の利用者に影響するため拡張できません。',
|
||||
unsupported: 'ディスクの形式、保存場所、またはアクセス権限が拡張に対応していません。',
|
||||
unavailable: 'ディスクの状態を確認できません。確認後に更新してください。',
|
||||
},
|
||||
diskSize: 'ディスク容量',
|
||||
isoHelper:
|
||||
'複数の ISO をマウントできます。OS のインストールイメージを先に選択し、次に VirtIO などのドライバーイメージを選択してください。各イメージは個別の CD-ROM ドライブを使用します。',
|
||||
windowsDriverHelper:
|
||||
'Windows イメージに VirtIO ネットワークドライバー(NetKVM)が含まれていない場合があります。VirtIO ドライバー ISO もマウントし、Windows 内でドライバーをインストールしてください。すでに含まれている場合は不要です。',
|
||||
windowsDiskDriverHelper:
|
||||
'VirtIO ディスクを使用する場合、ディスクドライバー(viostor)の読み込みも必要になることがあります。読み込まないと、Windows セットアップでディスクが認識されない場合があります。',
|
||||
osType: 'OS タイプ',
|
||||
osOther: 'その他',
|
||||
diskBus: 'ディスクバス',
|
||||
@@ -7126,6 +7258,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'コンソール',
|
||||
consoleShortcutKeys: 'ショートカットキー',
|
||||
consoleCustomKeys: 'カスタムキーの組み合わせ',
|
||||
consoleMainKey: 'メインキーを選択',
|
||||
consoleSendKeys: '送信',
|
||||
consoleNotReady: 'VNC コンソールを使用できません。VM を起動し、VNC グラフィック設定を確認してください。',
|
||||
consoleDisconnected: 'VNC コンソールの接続が切断されました。',
|
||||
consoleConnectFailed:
|
||||
|
||||
@@ -1481,7 +1481,86 @@ const message = {
|
||||
ignoreEos: 'EOS 무시',
|
||||
extraHeaders: '추가 헤더',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'현재 vLLM 인스턴스의 모니터링 기록만 삭제하시겠습니까? 다른 인스턴스, 호스트 및 GPU 모니터링에는 영향을 주지 않습니다.',
|
||||
overview: '개요',
|
||||
latencyAnalysis: '지연 시간 분석',
|
||||
requests: '요청 수',
|
||||
tokenThroughput: '토큰 처리량',
|
||||
running: '실행 중인 요청',
|
||||
waiting: '대기 중인 요청',
|
||||
cacheUsage: 'KV 캐시 사용률',
|
||||
promptThroughput: '입력 토큰 처리량',
|
||||
generationThroughput: '출력 토큰 처리량',
|
||||
requestThroughput: '완료 요청 속도',
|
||||
timeToFirstToken: '평균 첫 토큰 지연',
|
||||
timePerOutputToken: '평균 토큰 간 지연',
|
||||
requestLatency: '평균 요청 지연',
|
||||
unavailable: '지표를 수집할 수 없습니다. 인스턴스 실행 상태와 /metrics 접근 가능 여부를 확인하세요.',
|
||||
loadFailed: '모니터링 데이터를 불러오지 못했습니다. 다시 시도하세요.',
|
||||
helper: '수집 활성화, 간격 및 보존 기간은 모니터링 설정에서 조정합니다.',
|
||||
cache: 'KV 캐시',
|
||||
throughput: '요청 처리량',
|
||||
phaseTime: '프리필 및 디코딩 시간',
|
||||
prefillTime: '평균 프리필 시간',
|
||||
decodeTime: '평균 디코딩 시간',
|
||||
requestLatencyChart: '종단 간 요청 지연',
|
||||
timeToFirstTokenChart: '첫 토큰 지연',
|
||||
timePerOutputTokenChart: '토큰 간 지연',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: '메모리 읽기/쓰기 활성률',
|
||||
hotspotTemperature: '핫스폿 온도',
|
||||
fanRPM: '팬 회전 속도 (RPM)',
|
||||
eccCorrectable: '수정 가능한 오류 수',
|
||||
eccUncorrectable: '수정 불가능한 오류 수',
|
||||
eccPending: '적용 대기 중인 ECC 모드',
|
||||
engineUtil: '엔진 사용률',
|
||||
cpuUtil: 'CPU · 사용률',
|
||||
aiCore: 'AICore · 사용률',
|
||||
aiCPUUtil: 'AICPU · 사용률',
|
||||
ctrlCPUUtil: 'CtrlCPU · 사용률',
|
||||
mediaFrequency: '미디어 · 주파수',
|
||||
encoderUtil: '인코더 · 사용률',
|
||||
decoderUtil: '디코더 · 사용률',
|
||||
jpegUtil: 'JPEG · 사용률',
|
||||
ofaUtil: 'OFA · 사용률',
|
||||
mediaUtil: '미디어 · 사용률',
|
||||
computeUtil: '컴퓨팅 · 사용률',
|
||||
copyUtil: '복사 · 사용률',
|
||||
ddrUsage: 'DDR · 용량',
|
||||
ddrUsed: 'DDR · 사용량',
|
||||
ddrTotal: 'DDR · 총량',
|
||||
ddrBandwidth: 'DDR · 대역폭 사용률',
|
||||
hbmUsage: 'HBM · 용량',
|
||||
hbmUsed: 'HBM · 사용량',
|
||||
hbmTotal: 'HBM · 총량',
|
||||
hbmBandwidth: 'HBM · 대역폭 사용률',
|
||||
hugepagesUsage: '대용량 페이지 · 수',
|
||||
hugepagesUsed: '대용량 페이지 · 사용량',
|
||||
hugepagesTotal: '대용량 페이지 · 총량',
|
||||
|
||||
historyAverage: '평균',
|
||||
historyPeak: '최댓값',
|
||||
historyAggregated: '샘플 {count}개를 {seconds}초 간격으로 집계합니다. 프로세스는 개수만 표시합니다.',
|
||||
historyRaw: '원본 샘플 {count}개.',
|
||||
historyMetricEmpty: '선택한 기간에 이 지표의 데이터가 없습니다',
|
||||
historyLoadFailed: '기록을 불러오지 못했습니다. 다시 시도하세요.',
|
||||
collectedAt: '수집 시간',
|
||||
processUnavailable: '프로세스 정보를 사용할 수 없습니다',
|
||||
architecture: '아키텍처',
|
||||
memoryFrequency: '메모리 주파수',
|
||||
memoryTemperature: '메모리 온도',
|
||||
memoryReserved: '예약된 메모리',
|
||||
powerDefaultLimit: '기본 전력 제한',
|
||||
powerMaxLimit: '설정 가능한 최대 전력',
|
||||
clockEvents: '클럭 제한 이유',
|
||||
memoryBandwidth: '메모리 대역폭 사용률',
|
||||
legacyDevice: '이전 기록',
|
||||
legacyHistory: '이 기록에는 고정 장치 ID가 없어 장치 식별 및 누락 값을 확인할 수 없습니다.',
|
||||
pcieGeneration: 'PCIe 세대 (현재 / 최대)',
|
||||
pcieWidth: 'PCIe 너비 (현재 / 최대)',
|
||||
gpu: 'GPU 모니터링',
|
||||
gpuHelper: '지원되는 GPU/XPU/NPU 관리 명령을 감지하지 못했습니다. 확인하고 다시 시도하세요!',
|
||||
process: '프로세스 정보',
|
||||
@@ -1496,7 +1575,7 @@ const message = {
|
||||
temperature: '온도',
|
||||
performanceState: '성능 상태',
|
||||
powerUsage: '전력 소비',
|
||||
memoryUsage: '메모리 사용률',
|
||||
memoryUsage: '메모리 용량 사용률',
|
||||
fanSpeed: '팬 속도',
|
||||
power: '전력',
|
||||
powerCurrent: '현재 전력',
|
||||
@@ -2031,6 +2110,9 @@ const message = {
|
||||
cleanLogscope: '로그 보관 기간',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: '호스트 모니터링',
|
||||
gpuMonitor: 'GPU 모니터링',
|
||||
vllmMonitor: 'vLLM 모니터링',
|
||||
globalFilter: '전역 필터',
|
||||
enableMonitor: '모니터링 상태',
|
||||
storeDays: '보관 일수',
|
||||
@@ -2039,7 +2121,7 @@ const message = {
|
||||
defaultIO: '기본 디스크',
|
||||
defaultIOHelper: '모니터링 및 개요 인터페이스에 표시되는 기본 디스크 옵션',
|
||||
cleanMonitor: '모니터링 기록 지우기',
|
||||
cleanHelper: '이 작업은 GPU를 포함한 모든 모니터링 기록을 지웁니다. 계속하시겠습니까?',
|
||||
cleanHelper: '이 작업은 {0}의 모든 기록을 삭제합니다. 계속하시겠습니까?',
|
||||
avgLoad: '평균 부하',
|
||||
loadDetail: '부하 세부사항',
|
||||
resourceUsage: '자원 사용률',
|
||||
@@ -2093,6 +2175,22 @@ const message = {
|
||||
profileGoroutineDesc: '모든 Goroutine의 상태와 호출 스택을 저장하고 동일한 호출 경로를 그룹화합니다.',
|
||||
profileMutexDesc: 'Mutex 경합으로 인한 대기 시간을 측정하여 잠금 경합 지점을 찾습니다.',
|
||||
profileBlockDesc: 'Channel, Select 및 동기화 프리미티브의 차단 대기 시간을 측정합니다.',
|
||||
exportRange: '기간',
|
||||
exportMetrics: '보낼 지표',
|
||||
exportMetricsHelper: 'CPU, 메모리, 로드 지표는 하나의 테이블로 병합됩니다.',
|
||||
exportAllDevices: '장치별 (장치별 상세)',
|
||||
exportAllInterfaces: '인터페이스별 (인터페이스별 상세)',
|
||||
exportFormat: '보내기 형식',
|
||||
exportCsvHelper: '여러 지표를 선택하면 지표별 CSV 파일을 생성해 ZIP으로 묶습니다.',
|
||||
exportJsonHelper: '하나의 구조화된 JSON 파일로보냅니다.',
|
||||
exportXlsxHelper: '지표별로 개별 워크시트를 생성합니다.',
|
||||
exportTruncated: 'Excel 행 제한을 초과하여 다음 데이터셋이 잘렸습니다: {0}',
|
||||
exportEmptyTables: '선택한 기간에 데이터가 없는 지표: {0}',
|
||||
exportAggregate: '합계 (모든 장치 합산)',
|
||||
exportAllGPUs: '모델별 (모델별 상세)',
|
||||
exportOptionsFailed: '다음 지표의 장치 목록을 불러오지 못했습니다. 새로고침 후 다시 시도하세요: {0}',
|
||||
selectMetrics: '보낼 지표를 하나 이상 선택하세요',
|
||||
selectTimeRange: '보낼 기간을 선택하세요',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: '터미널 바로가기',
|
||||
@@ -6582,6 +6680,15 @@ const message = {
|
||||
snapshot: '시스템 스냅샷',
|
||||
clamsRulesHelper: '알림이 필요한 바이러스 검사 작업',
|
||||
cronJobRulesHelper: '이 유형의 예약된 작업은 구성해야 합니다',
|
||||
alertTriggerMode: '알림 조건',
|
||||
alertTriggerFailed: '실패 시에만',
|
||||
alertTriggerSuccess: '성공 시에만',
|
||||
alertTriggerBoth: '성공 또는 실패 시',
|
||||
alertNotificationHelper:
|
||||
'재시도 중이 아닌 최종 실행 결과에 따라 알립니다. 성공 및 실패 알림은 일일 한도와 전송 시간대를 공유합니다.',
|
||||
notificationTitle: '예약 작업 - {0} 「{1}」 실행 알림',
|
||||
notificationSuccessTitle: '예약 작업 - {0} 「{1}」 실행 성공',
|
||||
notificationRule: '{0}: {1}, 하루 최대 {2}회 알림',
|
||||
clamsTitle: '바이러스 검사 작업 「 {0} 」 감염된 파일 알림',
|
||||
cronJobAppTitle: '크론 작업 - 백업 애플리케이션 「 {0} 」 작업 실패 알림',
|
||||
cronJobWebsiteTitle: '크론 작업 - 백업 웹사이트「 {0} 」작업 실패 알림',
|
||||
@@ -6934,9 +7041,33 @@ const message = {
|
||||
orphan_snapshot: '고아 스냅샷',
|
||||
partial_file: '미완료 파일',
|
||||
},
|
||||
disks: '디스크 관리',
|
||||
resizeDisk: '확장',
|
||||
diskTarget: '장치',
|
||||
diskCapacityAfterResize: '확장 후 전체 용량',
|
||||
diskResizeHelper:
|
||||
'확장은 가상 디스크 용량만 늘립니다. 완료 후 가상 머신 내에서 파티션과 파일 시스템을 확장해야 합니다.',
|
||||
diskResizeLimit:
|
||||
'가상 머신이 종료된 상태에서 스냅샷이나 백킹 체인이 없는 독립적인 쓰기 가능한 qcow2 디스크만 확장할 수 있습니다. 축소는 지원하지 않습니다.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'가상 디스크 참조를 확인할 수 없어 확장할 수 없습니다. 다른 가상 머신의 디스크 상태를 확인하세요.',
|
||||
referenced: '다른 가상 머신 또는 장치가 이 가상 디스크를 사용 중이므로 현재 확장할 수 없습니다.',
|
||||
running: '가상 머신을 종료한 후 디스크를 확장하세요.',
|
||||
saved: '가상 머신을 재개하고 정상 종료한 후 확장하세요.',
|
||||
snapshots: '스냅샷 복원에 영향을 줄 수 있어 스냅샷이 있으면 확장할 수 없습니다.',
|
||||
backing: '다른 이미지에 의존하는 디스크는 아직 확장을 지원하지 않습니다.',
|
||||
shared: '공유로 설정된 디스크는 다른 사용자에게 영향을 줄 수 있어 확장할 수 없습니다.',
|
||||
unsupported: '디스크 형식, 위치 또는 접근 권한이 확장을 지원하지 않습니다.',
|
||||
unavailable: '디스크 상태를 확인할 수 없습니다. 확인 후 새로 고치세요.',
|
||||
},
|
||||
diskSize: '디스크 용량',
|
||||
isoHelper:
|
||||
'여러 ISO를 연결할 수 있습니다. 운영 체제 설치 이미지를 먼저 선택한 다음 VirtIO 등의 드라이버 이미지를 선택하세요. 각 이미지는 별도의 CD-ROM 드라이브를 사용합니다.',
|
||||
windowsDriverHelper:
|
||||
'Windows 이미지에 VirtIO 네트워크 드라이버(NetKVM)가 포함되어 있지 않을 수 있습니다. VirtIO 드라이버 ISO도 마운트하고 Windows에서 드라이버를 설치하세요. 이미 포함된 경우에는 무시해도 됩니다.',
|
||||
windowsDiskDriverHelper:
|
||||
'VirtIO 디스크를 사용하면 디스크 드라이버(viostor)도 로드해야 할 수 있습니다. 그렇지 않으면 Windows 설치 프로그램이 디스크를 인식하지 못할 수 있습니다.',
|
||||
osType: '운영 체제 유형',
|
||||
osOther: '기타',
|
||||
diskBus: '디스크 버스',
|
||||
@@ -6988,6 +7119,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: '콘솔',
|
||||
consoleShortcutKeys: '단축키',
|
||||
consoleCustomKeys: '사용자 지정 키 조합',
|
||||
consoleMainKey: '기본 키 선택',
|
||||
consoleSendKeys: '전송',
|
||||
consoleNotReady: 'VNC 콘솔을 사용할 수 없습니다. VM을 시작하고 VNC 그래픽 구성을 확인하세요.',
|
||||
consoleDisconnected: 'VNC 콘솔 연결이 끊어졌습니다.',
|
||||
consoleConnectFailed: 'VNC 콘솔 연결에 실패했습니다. 콘솔 프록시 서비스를 확인하세요.',
|
||||
|
||||
@@ -1474,7 +1474,85 @@ const message = {
|
||||
ignoreEos: 'ລະເວັ້ນ EOS',
|
||||
extraHeaders: 'Headers ເພີ່ມເຕີມ',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper: 'ລຶບສະເພາະປະຫວັດການຕິດຕາມຂອງ vLLM ນີ້ບໍ? ບໍ່ກະທົບຕໍ່ອິນສະແຕນອື່ນ, ໂຮສ ຫຼື GPU.',
|
||||
overview: 'ພາບລວມ',
|
||||
latencyAnalysis: 'ການວິເຄາະຄວາມລ່າຊ້າ',
|
||||
requests: 'ຈຳນວນຄຳຮ້ອງ',
|
||||
tokenThroughput: 'ອັດຕາປະມວນຜົນໂທເຄັນ',
|
||||
running: 'ຄຳຮ້ອງຂໍທີ່ກຳລັງເຮັດວຽກ',
|
||||
waiting: 'ຄຳຮ້ອງຂໍທີ່ກຳລັງລໍຖ້າ',
|
||||
cacheUsage: 'ການໃຊ້ງານແຄດ KV',
|
||||
promptThroughput: 'ອັດຕາ Token ຂາເຂົ້າ',
|
||||
generationThroughput: 'ອັດຕາ Token ຂາອອກ',
|
||||
requestThroughput: 'ອັດຕາຄຳຮ້ອງຂໍສຳເລັດ',
|
||||
timeToFirstToken: 'ເວລາລໍຖ້າສະເລ່ຍຂອງ Token ທຳອິດ',
|
||||
timePerOutputToken: 'ເວລາລໍຖ້າສະເລ່ຍລະຫວ່າງ Token',
|
||||
requestLatency: 'ເວລາລໍຖ້າສະເລ່ຍຂອງຄຳຮ້ອງຂໍ',
|
||||
unavailable: 'ບໍ່ສາມາດເກັບຕົວຊີ້ວັດໄດ້. ກວດສອບວ່າອິນສະແຕນເຮັດວຽກ ແລະ ເຂົ້າເຖິງ /metrics ໄດ້.',
|
||||
loadFailed: 'ໂຫຼດຂໍ້ມູນຕິດຕາມບໍ່ສຳເລັດ. ກະລຸນາລອງອີກຄັ້ງ.',
|
||||
helper: 'ປັບການເປີດໃຊ້ການເກັບຂໍ້ມູນ, ໄລຍະຫ່າງ ແລະ ຈຳນວນມື້ເກັບຮັກສາໃນການຕັ້ງຄ່າການຕິດຕາມ.',
|
||||
cache: 'ແຄດ KV',
|
||||
throughput: 'ຄຳຮ້ອງຂໍ/ວິນາທີ',
|
||||
phaseTime: 'ເວລາເຕີມລ່ວງໜ້າ ແລະ ຖອດລະຫັດ',
|
||||
prefillTime: 'ເວລາເຕີມລ່ວງໜ້າສະເລ່ຍ',
|
||||
decodeTime: 'ເວລາຖອດລະຫັດສະເລ່ຍ',
|
||||
requestLatencyChart: 'ເວລາລໍຖ້າຄຳຮ້ອງຂໍຕັ້ງແຕ່ຕົ້ນຈົນຈົບ',
|
||||
timeToFirstTokenChart: 'ເວລາລໍຖ້າ Token ທຳອິດ',
|
||||
timePerOutputTokenChart: 'ເວລາລໍຖ້າລະຫວ່າງ Token',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'ກິດຈະກຳອ່ານ/ຂຽນໜ່ວຍຄວາມຈຳ',
|
||||
hotspotTemperature: 'ອຸນຫະພູມຈຸດຮ້ອນ',
|
||||
fanRPM: 'ຄວາມໄວພັດລົມ (RPM)',
|
||||
eccCorrectable: 'ຂໍ້ຜິດພາດທີ່ແກ້ໄຂໄດ້',
|
||||
eccUncorrectable: 'ຂໍ້ຜິດພາດທີ່ແກ້ໄຂບໍ່ໄດ້',
|
||||
eccPending: 'ໂໝດ ECC ທີ່ລໍຖ້າ',
|
||||
engineUtil: 'ອັດຕາການໃຊ້ເຄື່ອງຈັກ',
|
||||
cpuUtil: 'CPU · ອັດຕາການໃຊ້',
|
||||
aiCore: 'AICore · ອັດຕາການໃຊ້',
|
||||
aiCPUUtil: 'AICPU · ອັດຕາການໃຊ້',
|
||||
ctrlCPUUtil: 'CtrlCPU · ອັດຕາການໃຊ້',
|
||||
mediaFrequency: 'ສື່ · ຄວາມຖີ່',
|
||||
encoderUtil: 'ຕົວເຂົ້າລະຫັດ · ອັດຕາການໃຊ້',
|
||||
decoderUtil: 'ຕົວຖອດລະຫັດ · ອັດຕາການໃຊ້',
|
||||
jpegUtil: 'JPEG · ອັດຕາການໃຊ້',
|
||||
ofaUtil: 'OFA · ອັດຕາການໃຊ້',
|
||||
mediaUtil: 'ສື່ · ອັດຕາການໃຊ້',
|
||||
computeUtil: 'ຄຳນວນ · ອັດຕາການໃຊ້',
|
||||
copyUtil: 'ສຳເນົາ · ອັດຕາການໃຊ້',
|
||||
ddrUsage: 'DDR · ຄວາມຈຸ',
|
||||
ddrUsed: 'DDR · ໃຊ້ແລ້ວ',
|
||||
ddrTotal: 'DDR · ທັງໝົດ',
|
||||
ddrBandwidth: 'DDR · ອັດຕາການໃຊ້ແບນວິດ',
|
||||
hbmUsage: 'HBM · ຄວາມຈຸ',
|
||||
hbmUsed: 'HBM · ໃຊ້ແລ້ວ',
|
||||
hbmTotal: 'HBM · ທັງໝົດ',
|
||||
hbmBandwidth: 'HBM · ອັດຕາການໃຊ້ແບນວິດ',
|
||||
hugepagesUsage: 'ໜ້າຂະໜາດໃຫຍ່ · ຈຳນວນ',
|
||||
hugepagesUsed: 'ໜ້າຂະໜາດໃຫຍ່ · ໃຊ້ແລ້ວ',
|
||||
hugepagesTotal: 'ໜ້າຂະໜາດໃຫຍ່ · ທັງໝົດ',
|
||||
|
||||
historyAverage: 'ຄ່າສະເລ່ຍ',
|
||||
historyPeak: 'ຄ່າສູງສຸດ',
|
||||
historyAggregated: 'ລວມ {count} ຕົວຢ່າງເປັນຊ່ວງ {seconds} ວິນາທີ; ສະແດງພຽງຈຳນວນໂປຣເຊສ.',
|
||||
historyRaw: '{count} ຕົວຢ່າງຕົ້ນສະບັບ.',
|
||||
historyMetricEmpty: 'ບໍ່ມີຂໍ້ມູນຕົວຊີ້ວັດນີ້ໃນຊ່ວງເວລາທີ່ເລືອກ',
|
||||
historyLoadFailed: 'ໂຫຼດປະຫວັດບໍ່ສຳເລັດ. ກະລຸນາລອງໃໝ່.',
|
||||
collectedAt: 'ເວລາເກັບຂໍ້ມູນ',
|
||||
processUnavailable: 'ບໍ່ມີຂໍ້ມູນໂປຣເຊສ',
|
||||
architecture: 'ສະຖາປັດຕະຍະກຳ',
|
||||
memoryFrequency: 'ຄວາມຖີ່ໜ່ວຍຄວາມຈຳ',
|
||||
memoryTemperature: 'ອຸນຫະພູມໜ່ວຍຄວາມຈຳ',
|
||||
memoryReserved: 'ໜ່ວຍຄວາມຈຳທີ່ສະຫງວນ',
|
||||
powerDefaultLimit: 'ຂີດຈຳກັດພະລັງງານເລີ່ມຕົ້ນ',
|
||||
powerMaxLimit: 'ພະລັງງານສູງສຸດທີ່ຕັ້ງໄດ້',
|
||||
clockEvents: 'ເຫດຜົນທີ່ຈຳກັດຄວາມຖີ່',
|
||||
memoryBandwidth: 'ການໃຊ້ແບນວິດໜ່ວຍຄວາມຈຳ',
|
||||
legacyDevice: 'ບັນທຶກເກົ່າ',
|
||||
legacyHistory: 'ບັນທຶກເຫຼົ່ານີ້ບໍ່ມີ ID ອຸປະກອນຄົງທີ່. ບໍ່ສາມາດຢືນຢັນອຸປະກອນ ແລະ ຄ່າທີ່ຂາດຫາຍໄດ້.',
|
||||
pcieGeneration: 'ລຸ້ນ PCIe (ປັດຈຸບັນ / ສູງສຸດ)',
|
||||
pcieWidth: 'ຄວາມກວ້າງ PCIe (ປັດຈຸບັນ / ສູງສຸດ)',
|
||||
gpu: 'ຕິດຕາມ GPU',
|
||||
gpuHelper: 'ກວດບໍ່ພົບຄຳສັ່ງຈັດການ GPU/XPU/NPU ທີ່ຮອງຮັບ. ກະລຸນາກວດສອບ ແລະ ລອງໃໝ່!',
|
||||
process: 'ຂໍ້ມູນໂປຣເຊສ',
|
||||
@@ -1490,7 +1568,7 @@ const message = {
|
||||
temperature: 'ອຸນຫະພູມ',
|
||||
performanceState: 'ສະຖານະປະສິດທິພາບ',
|
||||
powerUsage: 'ການໃຊ້ພະລັງງານ',
|
||||
memoryUsage: 'ການໃຊ້ໜ່ວຍຄວາມຈຳ',
|
||||
memoryUsage: 'ການໃຊ້ຄວາມຈຸໜ່ວຍຄວາມຈຳ',
|
||||
fanSpeed: 'ຄວາມໄວພັດລົມ',
|
||||
power: 'ພະລັງງານ',
|
||||
powerCurrent: 'ພະລັງງານປັດຈຸບັນ',
|
||||
@@ -2011,6 +2089,9 @@ const message = {
|
||||
cleanLogscope: 'ການເກັບຮັກສາບັນທຶກ',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'ການຕິດຕາມໂຮສ',
|
||||
gpuMonitor: 'ການຕິດຕາມ GPU',
|
||||
vllmMonitor: 'ການຕິດຕາມ vLLM',
|
||||
globalFilter: 'ຕົວກອງທົ່ວໂລກ',
|
||||
enableMonitor: 'ສະຖານະການຕິດຕາມ',
|
||||
storeDays: 'ຈຳນວນມື້ທີ່ເກັບໄວ້',
|
||||
@@ -2019,7 +2100,7 @@ const message = {
|
||||
defaultIO: 'Disk ເລີ່ມຕົ້ນ',
|
||||
defaultIOHelper: 'Disk ທີ່ຈະສະແດງໃນໜ້າ monitor ແລະ ພາບລວມ',
|
||||
cleanMonitor: 'ລ້າງບັນທຶກການຕິດຕາມ',
|
||||
cleanHelper: 'ການດຳເນີນການນີ້ຈະລ້າງບັນທຶກການຕິດຕາມທັງໝົດລວມເຖິງ GPU. ຕ້ອງການຕໍ່ຫຼືບໍ່?',
|
||||
cleanHelper: 'ການດຳເນີນການນີ້ຈະລຶບປະຫວັດທັງໝົດຂອງ {0}. ສືບຕໍ່ບໍ?',
|
||||
avgLoad: 'ຄ່າ Load ສະເລ່ຍ',
|
||||
loadDetail: 'ລາຍລະອຽດ Load',
|
||||
resourceUsage: 'ການນຳໃຊ້',
|
||||
@@ -2074,6 +2155,22 @@ const message = {
|
||||
profileGoroutineDesc: 'ບັນທຶກສະຖານະ ແລະ Call Stack ຂອງທຸກ Goroutine ແລະ ຈັດກຸ່ມເສັ້ນທາງການເອີ້ນທີ່ຄືກັນ.',
|
||||
profileMutexDesc: 'ວັດແທກເວລາລໍຖ້າທີ່ເກີດຈາກ Mutex Contention ເພື່ອຊອກຫາຈຸດທີ່ມີການແຂ່ງຂັນ Lock ສູງ.',
|
||||
profileBlockDesc: 'ວັດແທກການບລັອກໃນ Channel, Select Statement ແລະ ກົນໄກ Synchronization.',
|
||||
exportRange: 'ຊ່ວງເວລາ',
|
||||
exportMetrics: 'ຕົວຊີ້ວັດ',
|
||||
exportMetricsHelper: 'ຕົວຊີ້ວັດ CPU, ຫນ່ວຍຄວາມຈຳ ແລະ ໂຫຼດ ຈະຖືກຮວມເຂົ້າເປັນຕາຕະລາງດຽວ.',
|
||||
exportAllDevices: 'ແຕ່ລະອຸປະກອນ (ລາຍລະອຽດຕໍ່ອຸປະກອນ)',
|
||||
exportAllInterfaces: 'ແຕ່ລະອິນເຕີເຟສ (ລາຍລະອຽດຕໍ່ອິນເຕີເຟສ)',
|
||||
exportFormat: 'ຮູບແບບ',
|
||||
exportCsvHelper: 'ເມື່ອເລືອກຫຼາຍຕົວຊີ້ວັດ ຈະສ້າງໄຟລ໌ CSV ແຍກກັນແລ້ວຮັດຮວມເປັນ ZIP.',
|
||||
exportJsonHelper: 'ສົ່ງອອກເປັນໄຟລ໌ JSON ໂຄງສ້າງດຽວ.',
|
||||
exportXlsxHelper: 'ແຕ່ລະຕົວຊີ້ວັດຈະຖືກສົ່ງອອກເປັນເວີກຊີດແຍກກັນ.',
|
||||
exportTruncated: 'ເກີນຂີດຈຳກັດແຖວຂອງ Excel, ຊຸດຂໍ້ມູນຕໍ່ໄປນີ້ຖືກຕັດ: {0}',
|
||||
exportEmptyTables: 'ບໍ່ມີຂໍ້ມູນໃນຊ່ວງເວລາທີ່ເລືອກສຳລັບ: {0}',
|
||||
exportAggregate: 'ລວມທັງໝົດ (ທຸກອຸປະກອນ)',
|
||||
exportAllGPUs: 'ແຕ່ລະໂມເດລ (ລາຍລະອຽດຕໍ່ໂມເດລ)',
|
||||
exportOptionsFailed: 'ໂຫຼດລາຍຊື່ອຸປະກອນສຳລັບລາຍການຕໍ່ໄປນີ້ບໍ່ສຳເລັດ: {0}. ໂຫຼດໜ້າໃໝ່ແລ້ວລອງອີກຄັ້ງ.',
|
||||
selectMetrics: 'ກະລຸນາເລືອກຢ່າງໜ້ອຍໜຶ່ງຕົວຊີ້ວັດທີ່ຈະສົ່ງອອກ',
|
||||
selectTimeRange: 'ກະລຸນາເລືອກຊ່ວງເວລາທີ່ຈະສົ່ງອອກ',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'ທາງລັດ Terminal',
|
||||
@@ -6493,6 +6590,15 @@ const message = {
|
||||
snapshot: 'System Snapshot',
|
||||
clamsRulesHelper: 'ງານສະແກນໄວຣັສທີ່ຕ້ອງການແຈ້ງເຕືອນ',
|
||||
cronJobRulesHelper: 'ຕ້ອງຕັ້ງຄ່າງານ Cronjob ປະເພດນີ້',
|
||||
alertTriggerMode: 'ເງື່ອນໄຂແຈ້ງເຕືອນ',
|
||||
alertTriggerFailed: 'ເມື່ອລົ້ມເຫຼວເທົ່ານັ້ນ',
|
||||
alertTriggerSuccess: 'ເມື່ອສຳເລັດເທົ່ານັ້ນ',
|
||||
alertTriggerBoth: 'ເມື່ອສຳເລັດ ຫຼື ລົ້ມເຫຼວ',
|
||||
alertNotificationHelper:
|
||||
'ແຈ້ງເຕືອນຕາມຜົນສຸດທ້າຍ ບໍ່ແມ່ນລະຫວ່າງລອງໃໝ່. ການແຈ້ງເຕືອນສຳເລັດ ແລະ ລົ້ມເຫຼວໃຊ້ຂີດຈຳກັດລາຍວັນ ແລະ ເວລາສົ່ງຮ່ວມກັນ.',
|
||||
notificationTitle: 'ວຽກຕາມກຳນົດເວລາ - {0} 「{1}」 ແຈ້ງເຕືອນການເຮັດວຽກ',
|
||||
notificationSuccessTitle: 'ວຽກຕາມກຳນົດເວລາ - {0} 「{1}」 ສຳເລັດແລ້ວ',
|
||||
notificationRule: '{0}: {1}, ສູງສຸດ {2} ຄັ້ງຕໍ່ມື້',
|
||||
clamsTitle: 'ງານສະແກນໄວຣັສ 「 {0} 」 ກວດພົບໄຟລ໌ອັນຕະລາຍ',
|
||||
cronJobAppTitle: 'ແຈ້ງເຕືອນງານສຳຮອງແອັບ 「 {0} 」 ລົ້ມເຫຼວ',
|
||||
cronJobWebsiteTitle: 'ແຈ້ງເຕືອນງານສຳຮອງເວັບໄຊ 「 {0} 」 ລົ້ມເຫຼວ',
|
||||
@@ -6844,9 +6950,33 @@ const message = {
|
||||
orphan_snapshot: 'ສະແນັບຊັອດທີ່ບໍ່ຖືກອ້າງອີງ',
|
||||
partial_file: 'ໄຟລ໌ບໍ່ສົມບູນ',
|
||||
},
|
||||
disks: 'ຈັດການດິສກ໌',
|
||||
resizeDisk: 'ຂະຫຍາຍ',
|
||||
diskTarget: 'ອຸປະກອນ',
|
||||
diskCapacityAfterResize: 'ຄວາມຈຸລວມຫຼັງຂະຫຍາຍ',
|
||||
diskResizeHelper:
|
||||
'ການຂະຫຍາຍເພີ່ມພຽງຄວາມຈຸດິສກ໌ເສມືອນ. ຫຼັງຈາກນັ້ນ ຕ້ອງຂະຫຍາຍພາທິຊັນ ແລະ ລະບົບໄຟລ໌ພາຍໃນເຄື່ອງເສມືອນ.',
|
||||
diskResizeLimit:
|
||||
'ຕ້ອງປິດເຄື່ອງເສມືອນ ແລະ ໃຊ້ດິສກ໌ qcow2 ອິດສະຫຼະທີ່ຂຽນໄດ້ ໂດຍບໍ່ມີສະແນັບຊັອດ ຫຼື ຕ່ອງໂສ້ອິມເມຈພື້ນຖານ. ບໍ່ຮອງຮັບການຫຼຸດຂະໜາດ.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'ບໍ່ສາມາດກວດສອບການອ້າງອີງດິສກ໌ເສມືອນໄດ້. ຍັງຂະຫຍາຍບໍ່ໄດ້; ກະລຸນາກວດສອບດິສກ໌ຂອງເຄື່ອງເສມືອນອື່ນ.',
|
||||
referenced: 'ດິສກ໌ເສມືອນນີ້ຖືກໃຊ້ໂດຍເຄື່ອງເສມືອນ ຫຼື ອຸປະກອນອື່ນ; ຍັງບໍ່ຮອງຮັບການຂະຫຍາຍ.',
|
||||
running: 'ກະລຸນາປິດເຄື່ອງເສມືອນກ່ອນຂະຫຍາຍດິສກ໌.',
|
||||
saved: 'ກະລຸນາເຮັດວຽກເຄື່ອງເສມືອນຕໍ່ ແລ້ວປິດຕາມປົກກະຕິກ່ອນຂະຫຍາຍ.',
|
||||
snapshots: 'ມີສະແນັບຊັອດ; ຍັງບໍ່ຮອງຮັບການຂະຫຍາຍ ເພື່ອບໍ່ໃຫ້ກະທົບການກູ້ຄືນ.',
|
||||
backing: 'ດິສກ໌ຂຶ້ນກັບອິມເມຈອື່ນ; ຍັງບໍ່ຮອງຮັບການຂະຫຍາຍ.',
|
||||
shared: 'ດິສກ໌ຖືກຕັ້ງໃຫ້ໃຊ້ຮ່ວມກັນ; ບໍ່ຮອງຮັບການຂະຫຍາຍ ເພື່ອບໍ່ໃຫ້ກະທົບຜູ້ໃຊ້ອື່ນ.',
|
||||
unsupported: 'ຮູບແບບ, ຕຳແໜ່ງ ຫຼື ສິດເຂົ້າເຖິງດິສກ໌ບໍ່ຮອງຮັບການຂະຫຍາຍ.',
|
||||
unavailable: 'ບໍ່ສາມາດຢືນຢັນສະຖານະດິສກ໌ໄດ້. ກະລຸນາກວດສອບ ແລະ ໂຫຼດໃໝ່.',
|
||||
},
|
||||
diskSize: 'ຂະໜາດດິສກ໌',
|
||||
isoHelper:
|
||||
'ສາມາດເຊື່ອມຕໍ່ ISO ຫຼາຍໄຟລ໌ໄດ້. ເລືອກໄຟລ໌ຕິດຕັ້ງລະບົບປະຕິບັດການກ່ອນ ແລ້ວຈຶ່ງເລືອກໄຟລ໌ໄດຣເວີ ເຊັ່ນ VirtIO. ແຕ່ລະໄຟລ໌ໃຊ້ໄດຣ CD-ROM ແຍກຕ່າງຫາກ.',
|
||||
windowsDriverHelper:
|
||||
'ອິມເມຈ Windows ອາດບໍ່ມີໄດຣເວີເຄືອຂ່າຍ VirtIO (NetKVM). ແນະນຳໃຫ້ແນບ ISO ໄດຣເວີ VirtIO ແລະ ຕິດຕັ້ງໄດຣເວີໃນ Windows. ຖ້າອິມເມຈມີໄດຣເວີແລ້ວ ສາມາດຂ້າມໄດ້.',
|
||||
windowsDiskDriverHelper:
|
||||
'ເມື່ອໃຊ້ດິສ VirtIO ອາດຕ້ອງໂຫຼດໄດຣເວີດິສ (viostor) ເພີ່ມເຕີມ; ຖ້າບໍ່ໂຫຼດ ຕົວຕິດຕັ້ງ Windows ອາດບໍ່ພົບດິສ.',
|
||||
osType: 'ປະເພດລະບົບປະຕິບັດການ',
|
||||
osOther: 'ອື່ນໆ',
|
||||
diskBus: 'ບັສດິສກ໌',
|
||||
@@ -6897,6 +7027,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'ຄອນໂຊນ',
|
||||
consoleShortcutKeys: 'ປຸ່ມລັດ',
|
||||
consoleCustomKeys: 'ປຸ່ມປະສົມແບບກຳນົດເອງ',
|
||||
consoleMainKey: 'ເລືອກປຸ່ມຫຼັກ',
|
||||
consoleSendKeys: 'ສົ່ງ',
|
||||
consoleNotReady: 'ຄອນໂຊນ VNC ໃຊ້ງານບໍ່ໄດ້. ກະລຸນາເລີ່ມ VM ແລະ ກວດສອບການຕັ້ງຄ່າກຣາຟິກ VNC.',
|
||||
consoleDisconnected: 'ຄອນໂຊນ VNC ຖືກຕັດການເຊື່ອມຕໍ່ແລ້ວ.',
|
||||
consoleConnectFailed: 'ເຊື່ອມຕໍ່ຄອນໂຊນ VNC ລົ້ມເຫຼວ. ກວດສອບບໍລິການພຣັອກຊີຄອນໂຊນ.',
|
||||
|
||||
@@ -1521,7 +1521,88 @@ const message = {
|
||||
ignoreEos: 'Abaikan EOS',
|
||||
extraHeaders: 'Pengepala Tambahan',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'Padamkan sejarah pemantauan untuk instans vLLM ini sahaja? Instans lain serta pemantauan hos dan GPU tidak terjejas.',
|
||||
overview: 'Gambaran keseluruhan',
|
||||
latencyAnalysis: 'Analisis kependaman',
|
||||
requests: 'Permintaan',
|
||||
tokenThroughput: 'Daya pemprosesan token',
|
||||
running: 'Permintaan berjalan',
|
||||
waiting: 'Permintaan menunggu',
|
||||
cacheUsage: 'Penggunaan cache KV',
|
||||
promptThroughput: 'Daya pemprosesan token input',
|
||||
generationThroughput: 'Daya pemprosesan token output',
|
||||
requestThroughput: 'Kadar permintaan selesai',
|
||||
timeToFirstToken: 'Purata kependaman token pertama',
|
||||
timePerOutputToken: 'Purata kependaman antara token',
|
||||
requestLatency: 'Purata kependaman permintaan',
|
||||
unavailable: 'Metrik tidak tersedia. Pastikan tika sedang berjalan dan /metrics boleh dicapai.',
|
||||
loadFailed: 'Gagal memuatkan data pemantauan. Sila cuba lagi.',
|
||||
helper: 'Urus pengumpulan, sela dan tempoh penyimpanan dalam tetapan pemantauan.',
|
||||
cache: 'Cache KV',
|
||||
throughput: 'Permintaan/s',
|
||||
phaseTime: 'Masa praisi dan penyahkodan',
|
||||
prefillTime: 'Purata masa praisi',
|
||||
decodeTime: 'Purata masa penyahkodan',
|
||||
requestLatencyChart: 'Kependaman permintaan hujung ke hujung',
|
||||
timeToFirstTokenChart: 'Masa ke token pertama',
|
||||
timePerOutputTokenChart: 'Kependaman antara token',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'Aktiviti baca/tulis memori',
|
||||
hotspotTemperature: 'Suhu titik panas',
|
||||
fanRPM: 'Kelajuan kipas (RPM)',
|
||||
eccCorrectable: 'Ralat boleh dibetulkan',
|
||||
eccUncorrectable: 'Ralat tidak boleh dibetulkan',
|
||||
eccPending: 'Mod ECC menunggu',
|
||||
engineUtil: 'Penggunaan enjin',
|
||||
cpuUtil: 'CPU · penggunaan',
|
||||
aiCore: 'AICore · penggunaan',
|
||||
aiCPUUtil: 'AICPU · penggunaan',
|
||||
ctrlCPUUtil: 'CtrlCPU · penggunaan',
|
||||
mediaFrequency: 'Media · frekuensi',
|
||||
encoderUtil: 'Pengekod · penggunaan',
|
||||
decoderUtil: 'Penyahkod · penggunaan',
|
||||
jpegUtil: 'JPEG · penggunaan',
|
||||
ofaUtil: 'OFA · penggunaan',
|
||||
mediaUtil: 'Media · penggunaan',
|
||||
computeUtil: 'Pengiraan · penggunaan',
|
||||
copyUtil: 'Salinan · penggunaan',
|
||||
ddrUsage: 'DDR · kapasiti',
|
||||
ddrUsed: 'DDR · digunakan',
|
||||
ddrTotal: 'DDR · jumlah',
|
||||
ddrBandwidth: 'DDR · penggunaan lebar jalur',
|
||||
hbmUsage: 'HBM · kapasiti',
|
||||
hbmUsed: 'HBM · digunakan',
|
||||
hbmTotal: 'HBM · jumlah',
|
||||
hbmBandwidth: 'HBM · penggunaan lebar jalur',
|
||||
hugepagesUsage: 'Halaman besar · bilangan',
|
||||
hugepagesUsed: 'Halaman besar · digunakan',
|
||||
hugepagesTotal: 'Halaman besar · jumlah',
|
||||
|
||||
historyAverage: 'Purata',
|
||||
historyPeak: 'Puncak',
|
||||
historyAggregated:
|
||||
'{count} sampel diagregatkan dalam sela {seconds} saat; proses hanya memaparkan bilangan.',
|
||||
historyRaw: '{count} sampel asal.',
|
||||
historyMetricEmpty: 'Tiada data untuk metrik ini dalam julat masa yang dipilih',
|
||||
historyLoadFailed: 'Gagal memuatkan sejarah. Sila cuba lagi.',
|
||||
collectedAt: 'Masa pengumpulan',
|
||||
processUnavailable: 'Maklumat proses tidak tersedia',
|
||||
architecture: 'Seni bina',
|
||||
memoryFrequency: 'Frekuensi memori',
|
||||
memoryTemperature: 'Suhu memori',
|
||||
memoryReserved: 'Memori terpelihara',
|
||||
powerDefaultLimit: 'Had kuasa lalai',
|
||||
powerMaxLimit: 'Kuasa maksimum boleh ditetapkan',
|
||||
clockEvents: 'Sebab had frekuensi',
|
||||
memoryBandwidth: 'Penggunaan lebar jalur memori',
|
||||
legacyDevice: 'Rekod lama',
|
||||
legacyHistory:
|
||||
'Rekod ini tiada ID peranti yang tetap. Identiti peranti dan nilai yang hilang tidak dapat disahkan.',
|
||||
pcieGeneration: 'Generasi PCIe (semasa / maksimum)',
|
||||
pcieWidth: 'Lebar PCIe (semasa / maksimum)',
|
||||
gpu: 'Pemantauan GPU',
|
||||
gpuHelper: 'Tiada arahan pengurusan GPU/XPU/NPU yang disokong dikesan. Sila periksa dan cuba lagi!',
|
||||
process: 'Maklumat Proses',
|
||||
@@ -1536,7 +1617,7 @@ const message = {
|
||||
temperature: 'Suhu',
|
||||
performanceState: 'Status Prestasi',
|
||||
powerUsage: 'Penggunaan Kuasa',
|
||||
memoryUsage: 'Penggunaan Memori',
|
||||
memoryUsage: 'Penggunaan kapasiti memori',
|
||||
fanSpeed: 'Kelajuan Kipas',
|
||||
power: 'Kuasa',
|
||||
powerCurrent: 'Kuasa Semasa',
|
||||
@@ -2092,6 +2173,9 @@ const message = {
|
||||
cleanLogscope: 'Tempoh simpan log',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'Pemantauan Hos',
|
||||
gpuMonitor: 'Pemantauan GPU',
|
||||
vllmMonitor: 'Pemantauan vLLM',
|
||||
globalFilter: 'Penapis Global',
|
||||
enableMonitor: 'Status Pemantauan',
|
||||
storeDays: 'Hari Penyimpanan',
|
||||
@@ -2101,7 +2185,7 @@ const message = {
|
||||
defaultIO: 'Cakera Lalai',
|
||||
defaultIOHelper: 'Pilihan cakera lalai yang dipaparkan dalam antara muka pemantauan dan gambaran keseluruhan',
|
||||
cleanMonitor: 'Kosongkan Rekod Pemantauan',
|
||||
cleanHelper: 'Operasi ini akan membersihkan semua rekod pemantauan termasuk GPU. Teruskan?',
|
||||
cleanHelper: 'Operasi ini akan memadamkan semua sejarah untuk {0}. Teruskan?',
|
||||
avgLoad: 'Purata beban',
|
||||
loadDetail: 'Butiran beban',
|
||||
resourceUsage: 'Penggunaan sumber',
|
||||
@@ -2159,6 +2243,22 @@ const message = {
|
||||
'Menyimpan keadaan dan tindanan panggilan setiap Goroutine serta mengumpulkan laluan yang sama.',
|
||||
profileMutexDesc: 'Mengukur masa menunggu akibat persaingan mutex untuk mencari titik persaingan kunci.',
|
||||
profileBlockDesc: 'Mengukur sekatan pada Channel, Select dan primitif penyegerakan.',
|
||||
exportRange: 'Julat masa',
|
||||
exportMetrics: 'Metrik',
|
||||
exportMetricsHelper: 'Metrik CPU, memori dan beban digabungkan dalam satu jadual.',
|
||||
exportAllDevices: 'Setiap peranti (perincian per peranti)',
|
||||
exportAllInterfaces: 'Setiap antara muka (perincian per antara muka)',
|
||||
exportFormat: 'Format',
|
||||
exportCsvHelper: 'Apabila berbilang metrik dipilih, fail CSV berasingan dibungkus dalam ZIP.',
|
||||
exportJsonHelper: 'Eksport sebagai satu fail JSON berstruktur.',
|
||||
exportXlsxHelper: 'Setiap metrik dieksport sebagai lembaran kerja berasingan.',
|
||||
exportTruncated: 'Had baris Excel dicapai; set data berikut telah dipotong: {0}',
|
||||
exportEmptyTables: 'Tiada data dalam julat dipilih untuk: {0}',
|
||||
exportAggregate: 'Jumlah gabungan (semua peranti)',
|
||||
exportAllGPUs: 'Setiap model (perincian per model)',
|
||||
exportOptionsFailed: 'Gagal memuat senarai peranti untuk: {0}. Muat semula halaman dan cuba lagi.',
|
||||
selectMetrics: 'Pilih sekurang-kurangnya satu metrik untuk dieksport',
|
||||
selectTimeRange: 'Pilih julat masa untuk dieksport',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'Pintasan Terminal',
|
||||
@@ -6833,6 +6933,15 @@ const message = {
|
||||
snapshot: 'Snapshot Sistem',
|
||||
clamsRulesHelper: 'Tugas imbasan virus yang memerlukan amaran',
|
||||
cronJobRulesHelper: 'Jenis tugas berjadual ini perlu dikonfigurasikan',
|
||||
alertTriggerMode: 'Syarat pemberitahuan',
|
||||
alertTriggerFailed: 'Gagal sahaja',
|
||||
alertTriggerSuccess: 'Berjaya sahaja',
|
||||
alertTriggerBoth: 'Berjaya atau gagal',
|
||||
alertNotificationHelper:
|
||||
'Maklumkan selepas keputusan akhir pelaksanaan, bukan semasa percubaan semula. Pemberitahuan berjaya dan gagal berkongsi had harian dan waktu penghantaran.',
|
||||
notificationTitle: 'Tugas berjadual - {0} 「{1}」 pemberitahuan pelaksanaan',
|
||||
notificationSuccessTitle: 'Tugas berjadual - {0} 「{1}」 berjaya diselesaikan',
|
||||
notificationRule: '{0}: {1}, sehingga {2} pemberitahuan sehari',
|
||||
clamsTitle: 'Tugas imbasan virus 「 {0} 」 mengesan amaran fail dijangkiti',
|
||||
cronJobAppTitle: 'Cronjob - Sandaran Aplikasi 「 {0} 」 Amaran Kegagalan Tugas',
|
||||
cronJobWebsiteTitle: 'Cronjob - Sandaran Laman Web 「 {0} 」 Amaran Kegagalan Tugas',
|
||||
@@ -7195,9 +7304,33 @@ const message = {
|
||||
orphan_snapshot: 'Syot kilat yatim',
|
||||
partial_file: 'Fail tidak lengkap',
|
||||
},
|
||||
disks: 'Cakera',
|
||||
resizeDisk: 'Besarkan',
|
||||
diskTarget: 'Peranti',
|
||||
diskCapacityAfterResize: 'Jumlah kapasiti selepas pembesaran',
|
||||
diskResizeHelper:
|
||||
'Pembesaran hanya menambah kapasiti cakera maya. Selepas itu, besarkan partition dan sistem fail dalam mesin maya.',
|
||||
diskResizeLimit:
|
||||
'Mesin maya mesti dimatikan dan cakera qcow2 mestilah bebas serta boleh ditulis, tanpa syot kilat atau rantaian imej asas. Pengecilan tidak disokong.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'Rujukan cakera maya tidak dapat disahkan. Pembesaran tidak tersedia; semak cakera mesin maya lain.',
|
||||
referenced: 'Cakera maya ini digunakan oleh mesin maya atau peranti lain; pembesaran belum disokong.',
|
||||
running: 'Matikan mesin maya sebelum membesarkan cakera.',
|
||||
saved: 'Sambung semula mesin maya, kemudian matikannya secara biasa sebelum pembesaran.',
|
||||
snapshots: 'Pembesaran tidak disokong jika terdapat syot kilat supaya pemulihan tidak terjejas.',
|
||||
backing: 'Cakera bergantung pada imej lain; pembesaran belum disokong.',
|
||||
shared: 'Cakera ditandakan sebagai dikongsi; pembesaran tidak disokong supaya pengguna lain tidak terjejas.',
|
||||
unsupported: 'Format, lokasi atau kebenaran akses cakera tidak menyokong pembesaran.',
|
||||
unavailable: 'Status cakera tidak dapat disahkan. Semak dan muat semula.',
|
||||
},
|
||||
diskSize: 'Saiz Cakera',
|
||||
isoHelper:
|
||||
'Pasang berbilang ISO. Pilih pemasang sistem pengendalian dahulu, kemudian imej pemacu seperti VirtIO. Setiap imej menggunakan pemacu CD-ROM yang berasingan.',
|
||||
windowsDriverHelper:
|
||||
'Imej Windows mungkin tidak menyertakan pemacu rangkaian VirtIO (NetKVM). Lekapkan ISO pemacu VirtIO dan pasang pemacu dalam Windows. Abaikan jika imej sudah menyertakan pemacu tersebut.',
|
||||
windowsDiskDriverHelper:
|
||||
'Cakera VirtIO juga mungkin memerlukan pemacu cakera (viostor) dimuatkan; jika tidak, pemasang Windows mungkin tidak dapat mengesan cakera.',
|
||||
osType: 'Jenis sistem pengendalian',
|
||||
osOther: 'Lain-lain',
|
||||
diskBus: 'Bas cakera',
|
||||
@@ -7249,6 +7382,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'Konsol',
|
||||
consoleShortcutKeys: 'Kekunci pintasan',
|
||||
consoleCustomKeys: 'Gabungan kekunci tersuai',
|
||||
consoleMainKey: 'Pilih kekunci utama',
|
||||
consoleSendKeys: 'Hantar',
|
||||
consoleNotReady: 'Konsol VNC tidak tersedia. Mulakan VM dan semak konfigurasi grafik VNC.',
|
||||
consoleDisconnected: 'Konsol VNC telah terputus.',
|
||||
consoleConnectFailed: 'Gagal menyambung ke konsol VNC. Semak perkhidmatan proksi konsol.',
|
||||
|
||||
@@ -1517,7 +1517,89 @@ const message = {
|
||||
ignoreEos: 'Ignorar EOS',
|
||||
extraHeaders: 'Cabeçalhos extras',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'Limpar apenas o histórico de monitoramento desta instância do vLLM? Outras instâncias e o monitoramento do host e da GPU não serão afetados.',
|
||||
overview: 'Visão geral',
|
||||
latencyAnalysis: 'Análise de latência',
|
||||
requests: 'Requisições',
|
||||
tokenThroughput: 'Vazão de tokens',
|
||||
running: 'Solicitações em execução',
|
||||
waiting: 'Solicitações em espera',
|
||||
cacheUsage: 'Uso do cache KV',
|
||||
promptThroughput: 'Taxa de tokens de entrada',
|
||||
generationThroughput: 'Taxa de tokens de saída',
|
||||
requestThroughput: 'Solicitações concluídas',
|
||||
timeToFirstToken: 'Latência média do primeiro token',
|
||||
timePerOutputToken: 'Latência média entre tokens',
|
||||
requestLatency: 'Latência média da solicitação',
|
||||
unavailable:
|
||||
'Métricas indisponíveis. Verifique se a instância está em execução e se /metrics está acessível.',
|
||||
loadFailed: 'Falha ao carregar os dados. Tente novamente.',
|
||||
helper: 'Ajuste a coleta, o intervalo e a retenção nas configurações de monitoramento.',
|
||||
cache: 'Cache KV',
|
||||
throughput: 'Solicitações/s',
|
||||
phaseTime: 'Tempo de preenchimento e decodificação',
|
||||
prefillTime: 'Tempo médio de preenchimento',
|
||||
decodeTime: 'Tempo médio de decodificação',
|
||||
requestLatencyChart: 'Latência da solicitação de ponta a ponta',
|
||||
timeToFirstTokenChart: 'Tempo até o primeiro token',
|
||||
timePerOutputTokenChart: 'Latência entre tokens',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'Atividade de leitura/escrita da memória',
|
||||
hotspotTemperature: 'Temperatura do ponto quente',
|
||||
fanRPM: 'Rotação da ventoinha (RPM)',
|
||||
eccCorrectable: 'Erros corrigíveis',
|
||||
eccUncorrectable: 'Erros incorrigíveis',
|
||||
eccPending: 'Modo ECC pendente',
|
||||
engineUtil: 'Utilização dos motores',
|
||||
cpuUtil: 'CPU · utilização',
|
||||
aiCore: 'AICore · utilização',
|
||||
aiCPUUtil: 'AICPU · utilização',
|
||||
ctrlCPUUtil: 'CtrlCPU · utilização',
|
||||
mediaFrequency: 'Mídia · frequência',
|
||||
encoderUtil: 'Codificador · utilização',
|
||||
decoderUtil: 'Decodificador · utilização',
|
||||
jpegUtil: 'JPEG · utilização',
|
||||
ofaUtil: 'OFA · utilização',
|
||||
mediaUtil: 'Mídia · utilização',
|
||||
computeUtil: 'Computação · utilização',
|
||||
copyUtil: 'Cópia · utilização',
|
||||
ddrUsage: 'DDR · capacidade',
|
||||
ddrUsed: 'DDR · usado',
|
||||
ddrTotal: 'DDR · total',
|
||||
ddrBandwidth: 'DDR · utilização da largura de banda',
|
||||
hbmUsage: 'HBM · capacidade',
|
||||
hbmUsed: 'HBM · usado',
|
||||
hbmTotal: 'HBM · total',
|
||||
hbmBandwidth: 'HBM · utilização da largura de banda',
|
||||
hugepagesUsage: 'Páginas grandes · quantidade',
|
||||
hugepagesUsed: 'Páginas grandes · usado',
|
||||
hugepagesTotal: 'Páginas grandes · total',
|
||||
|
||||
historyAverage: 'Média',
|
||||
historyPeak: 'Pico',
|
||||
historyAggregated:
|
||||
'{count} amostras agregadas em intervalos de {seconds} segundos; processos exibem apenas contagens.',
|
||||
historyRaw: '{count} amostras originais.',
|
||||
historyMetricEmpty: 'Sem dados desta métrica no período selecionado',
|
||||
historyLoadFailed: 'Falha ao carregar o histórico. Tente novamente.',
|
||||
collectedAt: 'Data da coleta',
|
||||
processUnavailable: 'Informações de processos indisponíveis',
|
||||
architecture: 'Arquitetura',
|
||||
memoryFrequency: 'Frequência da memória',
|
||||
memoryTemperature: 'Temperatura da memória',
|
||||
memoryReserved: 'Memória reservada',
|
||||
powerDefaultLimit: 'Limite de potência padrão',
|
||||
powerMaxLimit: 'Potência máxima configurável',
|
||||
clockEvents: 'Motivos de limitação de frequência',
|
||||
memoryBandwidth: 'Uso da largura de banda da memória',
|
||||
legacyDevice: 'Registro antigo',
|
||||
legacyHistory:
|
||||
'Estes registros não têm um ID de dispositivo estável. Não é possível verificar a identidade nem os valores ausentes.',
|
||||
pcieGeneration: 'Geração PCIe (atual / máxima)',
|
||||
pcieWidth: 'Largura PCIe (atual / máxima)',
|
||||
gpu: 'Monitoramento de GPU',
|
||||
gpuHelper:
|
||||
'Nenhum comando de gerenciamento de GPU/XPU/NPU compatível foi detectado. Verifique e tente novamente!',
|
||||
@@ -1533,7 +1615,7 @@ const message = {
|
||||
temperature: 'Temperatura',
|
||||
performanceState: 'Estado de Desempenho',
|
||||
powerUsage: 'Consumo de Energia',
|
||||
memoryUsage: 'Utilização de Memória',
|
||||
memoryUsage: 'Uso da capacidade de memória',
|
||||
fanSpeed: 'Velocidade do Ventilador',
|
||||
power: 'Energia',
|
||||
powerCurrent: 'Energia Atual',
|
||||
@@ -2097,6 +2179,9 @@ const message = {
|
||||
cleanLogscope: 'Retenção de logs',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'Monitoramento do host',
|
||||
gpuMonitor: 'Monitoramento da GPU',
|
||||
vllmMonitor: 'Monitoramento do vLLM',
|
||||
globalFilter: 'Filtro Global',
|
||||
enableMonitor: 'Status de Monitoramento',
|
||||
storeDays: 'Dias de Retenção',
|
||||
@@ -2105,7 +2190,7 @@ const message = {
|
||||
defaultIO: 'Disco Padrão',
|
||||
defaultIOHelper: 'Opção de disco padrão exibida nas interfaces de monitoramento e visão geral',
|
||||
cleanMonitor: 'Limpar Registros de Monitoramento',
|
||||
cleanHelper: 'Esta operação limpará todos os registros de monitoramento, incluindo GPU. Continuar?',
|
||||
cleanHelper: 'Esta operação limpará todo o histórico de {0}. Continuar?',
|
||||
avgLoad: 'Média de carga',
|
||||
loadDetail: 'Detalhes da carga',
|
||||
resourceUsage: 'Utilização',
|
||||
@@ -2163,6 +2248,23 @@ const message = {
|
||||
profileGoroutineDesc: 'Salva o estado e a pilha de cada goroutine e agrupa caminhos de chamada idênticos.',
|
||||
profileMutexDesc: 'Mede o tempo de espera causado por contenção de mutex para localizar pontos de disputa.',
|
||||
profileBlockDesc: 'Mede bloqueios em canais, select e primitivas de sincronização.',
|
||||
exportRange: 'Período',
|
||||
exportMetrics: 'Métricas',
|
||||
exportMetricsHelper: 'As métricas de CPU, memória e carga são mescladas em uma única tabela.',
|
||||
exportAllDevices: 'Cada dispositivo (detalhe por dispositivo)',
|
||||
exportAllInterfaces: 'Cada interface (detalhe por interface)',
|
||||
exportFormat: 'Formato',
|
||||
exportCsvHelper: 'Ao selecionar várias métricas, são gerados arquivos CSV separados compactados em ZIP.',
|
||||
exportJsonHelper: 'Exportar como um único arquivo JSON estruturado.',
|
||||
exportXlsxHelper: 'Cada métrica é exportada em uma planilha separada.',
|
||||
exportTruncated: 'Limite de linhas do Excel atingido; estes conjuntos foram truncados: {0}',
|
||||
exportEmptyTables: 'Sem dados no período selecionado para: {0}',
|
||||
exportAggregate: 'Total combinado (todos os dispositivos)',
|
||||
exportAllGPUs: 'Cada modelo (detalhe por modelo)',
|
||||
exportOptionsFailed:
|
||||
'Falha ao carregar a lista de dispositivos para: {0}. Atualize a página e tente novamente.',
|
||||
selectMetrics: 'Selecione pelo menos uma métrica para exportar',
|
||||
selectTimeRange: 'Selecione o período para exportar',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'Atalho para o terminal',
|
||||
@@ -6867,6 +6969,15 @@ const message = {
|
||||
snapshot: 'Instantâneo do Sistema',
|
||||
clamsRulesHelper: 'Tarefas de varredura de vírus que exigem alertas por',
|
||||
cronJobRulesHelper: 'Este tipo de tarefa cron precisa ser configurado',
|
||||
alertTriggerMode: 'Notificar em caso de',
|
||||
alertTriggerFailed: 'Apenas falha',
|
||||
alertTriggerSuccess: 'Apenas sucesso',
|
||||
alertTriggerBoth: 'Sucesso ou falha',
|
||||
alertNotificationHelper:
|
||||
'Notificar após o resultado final, não durante as novas tentativas. As notificações de sucesso e falha compartilham o limite diário e o horário de envio.',
|
||||
notificationTitle: 'Tarefa agendada - {0} 「{1}」 notificação de execução',
|
||||
notificationSuccessTitle: 'Tarefa agendada - {0} 「{1}」 concluída com sucesso',
|
||||
notificationRule: '{0}: {1}, até {2} notificações por dia',
|
||||
clamsTitle: 'Tarefa de Varredura de Vírus 「 {0} 」 detectou arquivo infectado',
|
||||
cronJobAppTitle: 'Tarefa Cron - Falha no Backup do Aplicativo 「 {0} 」',
|
||||
cronJobWebsiteTitle: 'Tarefa Cron - Falha no Backup do Site 「 {0} 」',
|
||||
@@ -7234,9 +7345,34 @@ const message = {
|
||||
orphan_snapshot: 'Snapshot órfão',
|
||||
partial_file: 'Arquivo incompleto',
|
||||
},
|
||||
disks: 'Discos',
|
||||
resizeDisk: 'Expandir',
|
||||
diskTarget: 'Dispositivo',
|
||||
diskCapacityAfterResize: 'Capacidade total após a expansão',
|
||||
diskResizeHelper:
|
||||
'A expansão aumenta apenas a capacidade do disco virtual. Depois, expanda as partições e os sistemas de arquivos dentro da máquina virtual.',
|
||||
diskResizeLimit:
|
||||
'A máquina virtual deve estar desligada e o disco qcow2 deve ser independente e gravável, sem snapshots ou cadeia de imagens base. A redução não é suportada.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'Não foi possível verificar as referências ao disco virtual. A expansão está indisponível; verifique os discos das outras máquinas virtuais.',
|
||||
referenced:
|
||||
'Este disco virtual é usado por outra máquina virtual ou dispositivo e ainda não pode ser expandido.',
|
||||
running: 'Desligue a máquina virtual antes de expandir o disco.',
|
||||
saved: 'Retome a máquina virtual e desligue-a normalmente antes de expandir.',
|
||||
snapshots: 'A expansão não está disponível com snapshots para não afetar a restauração.',
|
||||
backing: 'O disco depende de outra imagem; a expansão ainda não é suportada.',
|
||||
shared: 'O disco está marcado como compartilhado; a expansão não está disponível para não afetar outros usuários.',
|
||||
unsupported: 'O formato, local ou as permissões do disco não permitem expansão.',
|
||||
unavailable: 'Não foi possível verificar o estado do disco. Verifique e atualize.',
|
||||
},
|
||||
diskSize: 'Tamanho do Disco',
|
||||
isoHelper:
|
||||
'Monte vários ISOs. Selecione primeiro o instalador do sistema operacional e depois as imagens de drivers, como VirtIO. Cada imagem usa uma unidade de CD-ROM separada.',
|
||||
windowsDriverHelper:
|
||||
'A imagem do Windows pode não incluir o driver de rede VirtIO (NetKVM). Monte uma ISO de drivers VirtIO e instale o driver no Windows. Ignore este aviso se a imagem já incluir o driver.',
|
||||
windowsDiskDriverHelper:
|
||||
'Os discos VirtIO também podem exigir o carregamento do driver de disco (viostor); caso contrário, o instalador do Windows pode não detectar o disco.',
|
||||
osType: 'Tipo de sistema operacional',
|
||||
osOther: 'Outro',
|
||||
diskBus: 'Barramento de disco',
|
||||
@@ -7288,6 +7424,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'Console',
|
||||
consoleShortcutKeys: 'Atalhos de teclado',
|
||||
consoleCustomKeys: 'Combinação personalizada',
|
||||
consoleMainKey: 'Selecionar tecla',
|
||||
consoleSendKeys: 'Enviar',
|
||||
consoleNotReady: 'O console VNC está indisponível. Inicie a VM e verifique sua configuração gráfica VNC.',
|
||||
consoleDisconnected: 'O console VNC foi desconectado.',
|
||||
consoleConnectFailed: 'Falha ao conectar ao console VNC. Verifique o serviço de proxy do console.',
|
||||
|
||||
@@ -1510,7 +1510,88 @@ const message = {
|
||||
ignoreEos: 'Игнорировать EOS',
|
||||
extraHeaders: 'Дополнительные заголовки',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'Удалить историю мониторинга только этого экземпляра vLLM? Другие экземпляры, мониторинг хоста и GPU не будут затронуты.',
|
||||
overview: 'Обзор',
|
||||
latencyAnalysis: 'Анализ задержек',
|
||||
requests: 'Количество запросов',
|
||||
tokenThroughput: 'Скорость обработки токенов',
|
||||
running: 'Выполняемые запросы',
|
||||
waiting: 'Ожидающие запросы',
|
||||
cacheUsage: 'Использование KV-кэша',
|
||||
promptThroughput: 'Скорость входных токенов',
|
||||
generationThroughput: 'Скорость выходных токенов',
|
||||
requestThroughput: 'Завершённые запросы',
|
||||
timeToFirstToken: 'Среднее время до первого токена',
|
||||
timePerOutputToken: 'Средняя задержка между токенами',
|
||||
requestLatency: 'Средняя задержка запроса',
|
||||
unavailable: 'Метрики недоступны. Проверьте, что экземпляр запущен и /metrics доступен.',
|
||||
loadFailed: 'Не удалось загрузить данные мониторинга. Повторите попытку.',
|
||||
helper: 'Сбор, интервал и срок хранения настраиваются в параметрах мониторинга.',
|
||||
cache: 'KV-кэш',
|
||||
throughput: 'Запросов/с',
|
||||
phaseTime: 'Время предзаполнения и декодирования',
|
||||
prefillTime: 'Среднее время предзаполнения',
|
||||
decodeTime: 'Среднее время декодирования',
|
||||
requestLatencyChart: 'Сквозная задержка запроса',
|
||||
timeToFirstTokenChart: 'Время до первого токена',
|
||||
timePerOutputTokenChart: 'Задержка между токенами',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'Активность чтения/записи памяти',
|
||||
hotspotTemperature: 'Температура горячей точки',
|
||||
fanRPM: 'Скорость вентилятора (об/мин)',
|
||||
eccCorrectable: 'Исправимые ошибки',
|
||||
eccUncorrectable: 'Неисправимые ошибки',
|
||||
eccPending: 'Ожидающий режим ECC',
|
||||
engineUtil: 'Загрузка движков',
|
||||
cpuUtil: 'CPU · загрузка',
|
||||
aiCore: 'AICore · загрузка',
|
||||
aiCPUUtil: 'AICPU · загрузка',
|
||||
ctrlCPUUtil: 'CtrlCPU · загрузка',
|
||||
mediaFrequency: 'Медиа · частота',
|
||||
encoderUtil: 'Кодировщик · загрузка',
|
||||
decoderUtil: 'Декодировщик · загрузка',
|
||||
jpegUtil: 'JPEG · загрузка',
|
||||
ofaUtil: 'OFA · загрузка',
|
||||
mediaUtil: 'Медиа · загрузка',
|
||||
computeUtil: 'Вычисления · загрузка',
|
||||
copyUtil: 'Копирование · загрузка',
|
||||
ddrUsage: 'DDR · объём',
|
||||
ddrUsed: 'DDR · использовано',
|
||||
ddrTotal: 'DDR · всего',
|
||||
ddrBandwidth: 'DDR · использование пропускной способности',
|
||||
hbmUsage: 'HBM · объём',
|
||||
hbmUsed: 'HBM · использовано',
|
||||
hbmTotal: 'HBM · всего',
|
||||
hbmBandwidth: 'HBM · использование пропускной способности',
|
||||
hugepagesUsage: 'Большие страницы · количество',
|
||||
hugepagesUsed: 'Большие страницы · использовано',
|
||||
hugepagesTotal: 'Большие страницы · всего',
|
||||
|
||||
historyAverage: 'Среднее',
|
||||
historyPeak: 'Максимум',
|
||||
historyAggregated:
|
||||
'{count} измерений сгруппированы по {seconds} с; для процессов показано только количество.',
|
||||
historyRaw: 'Исходных измерений: {count}.',
|
||||
historyMetricEmpty: 'Нет данных этой метрики за выбранный период',
|
||||
historyLoadFailed: 'Не удалось загрузить историю. Повторите попытку.',
|
||||
collectedAt: 'Время сбора',
|
||||
processUnavailable: 'Информация о процессах недоступна',
|
||||
architecture: 'Архитектура',
|
||||
memoryFrequency: 'Частота памяти',
|
||||
memoryTemperature: 'Температура памяти',
|
||||
memoryReserved: 'Зарезервированная память',
|
||||
powerDefaultLimit: 'Лимит мощности по умолчанию',
|
||||
powerMaxLimit: 'Максимальная настраиваемая мощность',
|
||||
clockEvents: 'Причины ограничения частоты',
|
||||
memoryBandwidth: 'Использование пропускной способности памяти',
|
||||
legacyDevice: 'Старая запись',
|
||||
legacyHistory:
|
||||
'У этих записей нет постоянного ID устройства. Нельзя проверить принадлежность устройству и отсутствующие значения.',
|
||||
pcieGeneration: 'Поколение PCIe (текущее / максимальное)',
|
||||
pcieWidth: 'Ширина PCIe (текущая / максимальная)',
|
||||
gpu: 'Мониторинг GPU',
|
||||
gpuHelper: 'Поддерживаемая команда управления GPU/XPU/NPU не обнаружена. Проверьте и повторите попытку!',
|
||||
process: 'Информация о Процессе',
|
||||
@@ -1525,7 +1606,7 @@ const message = {
|
||||
temperature: 'Температура',
|
||||
performanceState: 'Состояние Производительности',
|
||||
powerUsage: 'Потребление Мощности',
|
||||
memoryUsage: 'Использование Памяти',
|
||||
memoryUsage: 'Использование объёма памяти',
|
||||
fanSpeed: 'Скорость Вентилятора',
|
||||
power: 'Мощность',
|
||||
powerCurrent: 'Текущая Мощность',
|
||||
@@ -2082,6 +2163,9 @@ const message = {
|
||||
cleanLogscope: 'Хранение логов',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'Мониторинг хоста',
|
||||
gpuMonitor: 'Мониторинг GPU',
|
||||
vllmMonitor: 'Мониторинг vLLM',
|
||||
globalFilter: 'Глобальный Фильтр',
|
||||
enableMonitor: 'Статус Мониторинга',
|
||||
storeDays: 'Дни Хранения',
|
||||
@@ -2090,7 +2174,7 @@ const message = {
|
||||
defaultIO: 'Диск по Умолчанию',
|
||||
defaultIOHelper: 'Опция диска по умолчанию, отображаемая в интерфейсах мониторинга и обзора',
|
||||
cleanMonitor: 'Очистить Записи Мониторинга',
|
||||
cleanHelper: 'Эта операция очистит все записи мониторинга, включая GPU. Продолжить?',
|
||||
cleanHelper: 'Эта операция удалит всю историю для {0}. Продолжить?',
|
||||
avgLoad: 'Средняя нагрузка',
|
||||
loadDetail: 'Детали нагрузки',
|
||||
resourceUsage: 'Использование',
|
||||
@@ -2145,6 +2229,22 @@ const message = {
|
||||
profileGoroutineDesc: 'Сохраняет состояние и стек каждой goroutine и группирует одинаковые пути вызовов.',
|
||||
profileMutexDesc: 'Измеряет ожидание из-за конкуренции mutex для поиска горячих блокировок.',
|
||||
profileBlockDesc: 'Измеряет блокировки на каналах, select и примитивах синхронизации.',
|
||||
exportRange: 'Диапазон времени',
|
||||
exportMetrics: 'Метрики',
|
||||
exportMetricsHelper: 'Метрики CPU, памяти и нагрузки объединяются в одну таблицу.',
|
||||
exportAllDevices: 'Каждое устройство (по отдельности)',
|
||||
exportAllInterfaces: 'Каждый интерфейс (по отдельности)',
|
||||
exportFormat: 'Формат',
|
||||
exportCsvHelper: 'При выборе нескольких метрик CSV-файлы упаковываются в ZIP-архив.',
|
||||
exportJsonHelper: 'Экспорт в один структурированный JSON-файл.',
|
||||
exportXlsxHelper: 'Каждая метрика экспортируется на отдельный лист.',
|
||||
exportTruncated: 'Превышен лимит строк Excel, эти наборы данных обрезаны: {0}',
|
||||
exportEmptyTables: 'Нет данных за выбранный период: {0}',
|
||||
exportAggregate: 'Сводный итог (все устройства)',
|
||||
exportAllGPUs: 'Каждая модель (по отдельности)',
|
||||
exportOptionsFailed: 'Не удалось загрузить список устройств для: {0}. Обновите страницу и повторите.',
|
||||
selectMetrics: 'Выберите хотя бы одну метрику для экспорта',
|
||||
selectTimeRange: 'Выберите диапазон времени для экспорта',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'Быстрый доступ к терминалу',
|
||||
@@ -6835,6 +6935,15 @@ const message = {
|
||||
snapshot: 'Системный снимок',
|
||||
clamsRulesHelper: 'Задачи сканирования на вирусы, требующие уведомлений',
|
||||
cronJobRulesHelper: 'Этот тип планируемой задачи требует конфигурации',
|
||||
alertTriggerMode: 'Условие уведомления',
|
||||
alertTriggerFailed: 'Только при ошибке',
|
||||
alertTriggerSuccess: 'Только при успехе',
|
||||
alertTriggerBoth: 'При успехе или ошибке',
|
||||
alertNotificationHelper:
|
||||
'Уведомление отправляется по окончательному результату, а не во время повторных попыток. Уведомления об успехе и ошибке используют общий дневной лимит и часы отправки.',
|
||||
notificationTitle: 'Плановая задача - {0} «{1}»: уведомление о выполнении',
|
||||
notificationSuccessTitle: 'Плановая задача - {0} «{1}» успешно завершена',
|
||||
notificationRule: '{0}: {1}, до {2} уведомлений в день',
|
||||
clamsTitle: 'Задача сканирования на вирусы 「 {0} 」 обнаружила заражённый файл',
|
||||
cronJobAppTitle: 'Задача Cron - ошибка задачи резервного копирования приложения 「 {0} 」',
|
||||
cronJobWebsiteTitle: 'Задача Cron - ошибка задачи резервного копирования сайта 「 {0} 」',
|
||||
@@ -7202,9 +7311,34 @@ const message = {
|
||||
orphan_snapshot: 'Неиспользуемый снимок',
|
||||
partial_file: 'Незавершенный файл',
|
||||
},
|
||||
disks: 'Диски',
|
||||
resizeDisk: 'Расширить',
|
||||
diskTarget: 'Устройство',
|
||||
diskCapacityAfterResize: 'Общая ёмкость после расширения',
|
||||
diskResizeHelper:
|
||||
'Расширение увеличивает только ёмкость виртуального диска. После этого расширьте разделы и файловые системы внутри виртуальной машины.',
|
||||
diskResizeLimit:
|
||||
'Виртуальная машина должна быть выключена, а диск qcow2 — независимым и доступным для записи, без снимков и цепочки базовых образов. Уменьшение не поддерживается.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'Не удалось проверить ссылки на виртуальный диск. Расширение недоступно; проверьте диски других виртуальных машин.',
|
||||
referenced:
|
||||
'Этот виртуальный диск используется другой виртуальной машиной или устройством; расширение пока недоступно.',
|
||||
running: 'Выключите виртуальную машину перед расширением диска.',
|
||||
saved: 'Возобновите работу виртуальной машины и штатно выключите её перед расширением.',
|
||||
snapshots: 'Расширение недоступно при наличии снимков, чтобы не нарушить их восстановление.',
|
||||
backing: 'Диск зависит от другого образа; расширение пока не поддерживается.',
|
||||
shared: 'Диск помечен как общий; расширение недоступно, чтобы не затронуть других пользователей.',
|
||||
unsupported: 'Формат, расположение или права доступа к диску не позволяют расширить его.',
|
||||
unavailable: 'Не удалось проверить состояние диска. Проверьте его и обновите данные.',
|
||||
},
|
||||
diskSize: 'Размер диска',
|
||||
isoHelper:
|
||||
'Можно подключить несколько ISO. Сначала выберите установочный образ ОС, затем образы драйверов, например VirtIO. Каждый образ подключается к отдельному приводу CD-ROM.',
|
||||
windowsDriverHelper:
|
||||
'Образ Windows может не содержать сетевой драйвер VirtIO (NetKVM). Подключите ISO с драйверами VirtIO и установите драйвер в Windows. Если драйвер уже включён в образ, пропустите этот шаг.',
|
||||
windowsDiskDriverHelper:
|
||||
'Для дисков VirtIO также может потребоваться загрузить драйвер диска (viostor), иначе установщик Windows может не обнаружить диск.',
|
||||
osType: 'Тип операционной системы',
|
||||
osOther: 'Другая',
|
||||
diskBus: 'Шина диска',
|
||||
@@ -7256,6 +7390,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'Консоль',
|
||||
consoleShortcutKeys: 'Сочетания клавиш',
|
||||
consoleCustomKeys: 'Своя комбинация клавиш',
|
||||
consoleMainKey: 'Выберите клавишу',
|
||||
consoleSendKeys: 'Отправить',
|
||||
consoleNotReady: 'Консоль VNC недоступна. Запустите ВМ и проверьте конфигурацию графики VNC.',
|
||||
consoleDisconnected: 'Консоль VNC отключена.',
|
||||
consoleConnectFailed: 'Не удалось подключиться к консоли VNC. Проверьте службу прокси консоли.',
|
||||
|
||||
@@ -1517,7 +1517,88 @@ const message = {
|
||||
ignoreEos: "EOS'u yok say",
|
||||
extraHeaders: 'Ek Başlıklar',
|
||||
},
|
||||
vllmMonitor: {
|
||||
cleanHelper:
|
||||
'Yalnızca bu vLLM örneğinin izleme geçmişi temizlensin mi? Diğer örnekler, ana makine ve GPU izlemesi etkilenmez.',
|
||||
overview: 'Genel bakış',
|
||||
latencyAnalysis: 'Gecikme analizi',
|
||||
requests: 'İstek sayısı',
|
||||
tokenThroughput: 'Token işlem hızı',
|
||||
running: 'Çalışan istekler',
|
||||
waiting: 'Bekleyen istekler',
|
||||
cacheUsage: 'KV önbellek kullanımı',
|
||||
promptThroughput: 'Girdi token hızı',
|
||||
generationThroughput: 'Çıktı token hızı',
|
||||
requestThroughput: 'Tamamlanan istek hızı',
|
||||
timeToFirstToken: 'Ortalama ilk token gecikmesi',
|
||||
timePerOutputToken: 'Ortalama token arası gecikme',
|
||||
requestLatency: 'Ortalama istek gecikmesi',
|
||||
unavailable:
|
||||
'Metrikler kullanılamıyor. Örneğin çalıştığını ve /metrics yolunun erişilebilir olduğunu kontrol edin.',
|
||||
loadFailed: 'İzleme verileri yüklenemedi. Tekrar deneyin.',
|
||||
helper: 'Toplama, aralık ve saklama süresini izleme ayarlarından yönetin.',
|
||||
cache: 'KV önbelleği',
|
||||
throughput: 'İstek hızı',
|
||||
phaseTime: 'Ön doldurma ve çözümleme süresi',
|
||||
prefillTime: 'Ortalama ön doldurma süresi',
|
||||
decodeTime: 'Ortalama çözümleme süresi',
|
||||
requestLatencyChart: 'Uçtan uca istek gecikmesi',
|
||||
timeToFirstTokenChart: 'İlk token süresi',
|
||||
timePerOutputTokenChart: 'Token arası gecikme',
|
||||
},
|
||||
gpu: {
|
||||
memoryActivity: 'Bellek okuma/yazma etkinliği',
|
||||
hotspotTemperature: 'Sıcak nokta sıcaklığı',
|
||||
fanRPM: 'Fan hızı (RPM)',
|
||||
eccCorrectable: 'Düzeltilebilir hatalar',
|
||||
eccUncorrectable: 'Düzeltilemeyen hatalar',
|
||||
eccPending: 'Bekleyen ECC modu',
|
||||
engineUtil: 'Motor kullanımı',
|
||||
cpuUtil: 'CPU · kullanım',
|
||||
aiCore: 'AICore · kullanım',
|
||||
aiCPUUtil: 'AICPU · kullanım',
|
||||
ctrlCPUUtil: 'CtrlCPU · kullanım',
|
||||
mediaFrequency: 'Medya · frekans',
|
||||
encoderUtil: 'Kodlayıcı · kullanım',
|
||||
decoderUtil: 'Kod çözücü · kullanım',
|
||||
jpegUtil: 'JPEG · kullanım',
|
||||
ofaUtil: 'OFA · kullanım',
|
||||
mediaUtil: 'Medya · kullanım',
|
||||
computeUtil: 'Hesaplama · kullanım',
|
||||
copyUtil: 'Kopyalama · kullanım',
|
||||
ddrUsage: 'DDR · kapasite',
|
||||
ddrUsed: 'DDR · kullanılan',
|
||||
ddrTotal: 'DDR · toplam',
|
||||
ddrBandwidth: 'DDR · bant genişliği kullanımı',
|
||||
hbmUsage: 'HBM · kapasite',
|
||||
hbmUsed: 'HBM · kullanılan',
|
||||
hbmTotal: 'HBM · toplam',
|
||||
hbmBandwidth: 'HBM · bant genişliği kullanımı',
|
||||
hugepagesUsage: 'Büyük sayfalar · sayı',
|
||||
hugepagesUsed: 'Büyük sayfalar · kullanılan',
|
||||
hugepagesTotal: 'Büyük sayfalar · toplam',
|
||||
|
||||
historyAverage: 'Ortalama',
|
||||
historyPeak: 'En yüksek',
|
||||
historyAggregated:
|
||||
'{count} örnek {seconds} saniyelik aralıklarla toplandı; işlemler için yalnızca sayılar gösterilir.',
|
||||
historyRaw: '{count} ham örnek.',
|
||||
historyMetricEmpty: 'Seçilen zaman aralığında bu metriğe ait veri yok',
|
||||
historyLoadFailed: 'Geçmiş yüklenemedi. Lütfen tekrar deneyin.',
|
||||
collectedAt: 'Toplanma zamanı',
|
||||
processUnavailable: 'İşlem bilgileri kullanılamıyor',
|
||||
architecture: 'Mimari',
|
||||
memoryFrequency: 'Bellek frekansı',
|
||||
memoryTemperature: 'Bellek sıcaklığı',
|
||||
memoryReserved: 'Ayrılmış bellek',
|
||||
powerDefaultLimit: 'Varsayılan güç sınırı',
|
||||
powerMaxLimit: 'Yapılandırılabilir en yüksek güç',
|
||||
clockEvents: 'Saat frekansı sınırlama nedenleri',
|
||||
memoryBandwidth: 'Bellek bant genişliği kullanımı',
|
||||
legacyDevice: 'Eski kayıt',
|
||||
legacyHistory: 'Bu kayıtların sabit cihaz kimliği yoktur. Cihaz kimliği ve eksik değerler doğrulanamaz.',
|
||||
pcieGeneration: 'PCIe nesli (geçerli / en yüksek)',
|
||||
pcieWidth: 'PCIe genişliği (geçerli / en yüksek)',
|
||||
gpu: 'GPU İzleme',
|
||||
gpuHelper: 'Desteklenen bir GPU/XPU/NPU yönetim komutu algılanmadı. Lütfen kontrol edip tekrar deneyin!',
|
||||
process: 'İşlem Bilgisi',
|
||||
@@ -1532,7 +1613,7 @@ const message = {
|
||||
temperature: 'Sıcaklık',
|
||||
performanceState: 'Performans Durumu',
|
||||
powerUsage: 'Güç Tüketimi',
|
||||
memoryUsage: 'Bellek Kullanımı',
|
||||
memoryUsage: 'Bellek kapasitesi kullanımı',
|
||||
fanSpeed: 'Fan Hızı',
|
||||
power: 'Güç',
|
||||
powerCurrent: 'Mevcut Güç',
|
||||
@@ -2091,6 +2172,9 @@ const message = {
|
||||
cleanLogscope: 'Günlük saklama',
|
||||
},
|
||||
monitor: {
|
||||
hostMonitor: 'Ana Makine İzleme',
|
||||
gpuMonitor: 'GPU İzleme',
|
||||
vllmMonitor: 'vLLM İzleme',
|
||||
globalFilter: 'Genel Filtre',
|
||||
enableMonitor: 'İzleme Durumu',
|
||||
storeDays: 'Saklama Günleri',
|
||||
@@ -2099,7 +2183,7 @@ const message = {
|
||||
defaultIO: 'Varsayılan Disk',
|
||||
defaultIOHelper: 'İzleme ve genel bakış arayüzlerinde görüntülenen varsayılan disk seçeneği',
|
||||
cleanMonitor: 'İzleme Kayıtlarını Temizle',
|
||||
cleanHelper: 'Bu işlem GPU dahil tüm izleme kayıtlarını temizleyecektir. Devam etmek istiyor musunuz?',
|
||||
cleanHelper: 'Bu işlem {0} için tüm geçmişi silecektir. Devam edilsin mi?',
|
||||
avgLoad: 'Ortalama yük',
|
||||
loadDetail: 'Yük detayı',
|
||||
resourceUsage: 'Kullanım',
|
||||
@@ -2152,6 +2236,22 @@ const message = {
|
||||
profileGoroutineDesc: 'Her goroutine durumunu ve çağrı yığınını kaydeder, aynı çağrı yollarını gruplar.',
|
||||
profileMutexDesc: 'Kilit çekişmesi noktalarını bulmak için mutex bekleme süresini ölçer.',
|
||||
profileBlockDesc: 'Channel, select ve eşzamanlama araçlarındaki engelleme süresini ölçer.',
|
||||
exportRange: 'Zaman aralığı',
|
||||
exportMetrics: 'Metrikler',
|
||||
exportMetricsHelper: 'CPU, bellek ve yük metrikleri tek tabloda birleştirilir.',
|
||||
exportAllDevices: 'Her aygıt (aygıt başına ayrıntı)',
|
||||
exportAllInterfaces: 'Her arabirim (arabirim başına ayrıntı)',
|
||||
exportFormat: 'Biçim',
|
||||
exportCsvHelper: 'Birden fazla metrik seçildiğinde ayrı CSV dosyaları ZIP arşivinde paketlenir.',
|
||||
exportJsonHelper: 'Tek bir yapılandırılmış JSON dosyası olarak dışa aktar.',
|
||||
exportXlsxHelper: 'Her metrik ayrı bir çalışma sayfası olarak dışa aktarılır.',
|
||||
exportTruncated: 'Excel satır sınırı aşıldı; şu veri kümeleri kırpıldı: {0}',
|
||||
exportEmptyTables: 'Seçilen aralıkta veri yok: {0}',
|
||||
exportAggregate: 'Birleşik toplam (tüm aygıtlar)',
|
||||
exportAllGPUs: 'Her model (model başına ayrıntı)',
|
||||
exportOptionsFailed: 'Şu metrikler için aygıt listesi yüklenemedi: {0}. Sayfayı yenileyip tekrar deneyin.',
|
||||
selectMetrics: 'Dışa aktarmak için en az bir metrik seçin',
|
||||
selectTimeRange: 'Dışa aktarılacak zaman aralığını seçin',
|
||||
},
|
||||
terminal: {
|
||||
showTerminalButton: 'Terminal kısayolu',
|
||||
@@ -6841,6 +6941,15 @@ const message = {
|
||||
snapshot: 'Sistem Anlık Görüntüsü',
|
||||
clamsRulesHelper: 'Uyarısı gerektiren virüs tarama görevleri',
|
||||
cronJobRulesHelper: 'Bu tür zamanlanmış görevlerin yapılandırılması gerekir',
|
||||
alertTriggerMode: 'Bildirim koşulu',
|
||||
alertTriggerFailed: 'Yalnızca başarısızlık',
|
||||
alertTriggerSuccess: 'Yalnızca başarı',
|
||||
alertTriggerBoth: 'Başarı veya başarısızlık',
|
||||
alertNotificationHelper:
|
||||
'Yeniden denemeler sırasında değil, son yürütme sonucuna göre bildirim gönderilir. Başarı ve başarısızlık bildirimleri günlük sınırı ve gönderim saatlerini paylaşır.',
|
||||
notificationTitle: 'Zamanlanmış görev - {0} 「{1}」 yürütme bildirimi',
|
||||
notificationSuccessTitle: 'Zamanlanmış görev - {0} 「{1}」 başarıyla tamamlandı',
|
||||
notificationRule: '{0}: {1}, günde en fazla {2} bildirim',
|
||||
clamsTitle: 'Virüs tarama görevi 「 {0} 」 enfekte dosya algıladı uyarısı',
|
||||
cronJobAppTitle: 'Zamanlanmış Görev - Yedekleme Uygulaması 「 {0} 」 Görev Başarısızlık Uyarısı',
|
||||
cronJobWebsiteTitle: 'Zamanlanmış Görev - Yedekleme Web Sitesi 「 {0} 」 Görev Başarısızlık Uyarısı',
|
||||
@@ -7199,9 +7308,34 @@ const message = {
|
||||
orphan_snapshot: 'Sahipsiz anlık görüntü',
|
||||
partial_file: 'Tamamlanmamış dosya',
|
||||
},
|
||||
disks: 'Diskler',
|
||||
resizeDisk: 'Genişlet',
|
||||
diskTarget: 'Aygıt',
|
||||
diskCapacityAfterResize: 'Genişletme sonrası toplam kapasite',
|
||||
diskResizeHelper:
|
||||
'Bu işlem yalnızca sanal disk kapasitesini artırır. Ardından sanal makine içindeki bölümleri ve dosya sistemlerini genişletin.',
|
||||
diskResizeLimit:
|
||||
'Sanal makine kapalı olmalı ve qcow2 diski bağımsız, yazılabilir, anlık görüntüsüz ve temel imaj zinciri olmadan kullanılmalıdır. Küçültme desteklenmez.',
|
||||
diskResizeReason: {
|
||||
referenceCheck:
|
||||
'Sanal disk başvuruları doğrulanamadı. Genişletme kullanılamıyor; diğer sanal makinelerin disklerini kontrol edin.',
|
||||
referenced:
|
||||
'Bu sanal disk başka bir sanal makine veya aygıt tarafından kullanılıyor; genişletme henüz desteklenmiyor.',
|
||||
running: 'Diski genişletmeden önce sanal makineyi kapatın.',
|
||||
saved: 'Genişletmeden önce sanal makineyi sürdürün ve normal şekilde kapatın.',
|
||||
snapshots: 'Geri yüklemeyi etkilememek için anlık görüntüler varken genişletme desteklenmiyor.',
|
||||
backing: 'Disk başka bir imaja bağlı; genişletme henüz desteklenmiyor.',
|
||||
shared: 'Disk paylaşımlı olarak işaretlenmiş; diğer kullanıcıları etkilememek için genişletme desteklenmiyor.',
|
||||
unsupported: 'Disk biçimi, konumu veya erişim izinleri genişletmeyi desteklemiyor.',
|
||||
unavailable: 'Disk durumu doğrulanamıyor. Kontrol edip yenileyin.',
|
||||
},
|
||||
diskSize: 'Disk Boyutu',
|
||||
isoHelper:
|
||||
'Birden fazla ISO bağlayabilirsiniz. Önce işletim sistemi kurulum imajını, ardından VirtIO gibi sürücü imajlarını seçin. Her imaj ayrı bir CD-ROM sürücüsü kullanır.',
|
||||
windowsDriverHelper:
|
||||
'Windows kalıbı VirtIO ağ sürücüsünü (NetKVM) içermeyebilir. Bir VirtIO sürücü ISO dosyası da bağlayın ve sürücüyü Windows içinde yükleyin. Kalıp sürücüyü zaten içeriyorsa bu bildirimi yok sayın.',
|
||||
windowsDiskDriverHelper:
|
||||
'VirtIO diskleri için disk sürücüsünün (viostor) yüklenmesi de gerekebilir; aksi takdirde Windows Kurulumu diski algılamayabilir.',
|
||||
osType: 'İşletim sistemi türü',
|
||||
osOther: 'Diğer',
|
||||
diskBus: 'Disk veri yolu',
|
||||
@@ -7253,6 +7387,10 @@ const message = {
|
||||
rx: 'RX',
|
||||
tx: 'TX',
|
||||
console: 'Konsol',
|
||||
consoleShortcutKeys: 'Kısayol tuşları',
|
||||
consoleCustomKeys: 'Özel tuş kombinasyonu',
|
||||
consoleMainKey: 'Ana tuşu seçin',
|
||||
consoleSendKeys: 'Gönder',
|
||||
consoleNotReady: 'VNC konsolu kullanılamıyor. VMyi başlatın ve VNC grafik yapılandırmasını kontrol edin.',
|
||||
consoleDisconnected: 'VNC konsolu bağlantısı kesildi.',
|
||||
consoleConnectFailed: 'VNC konsoluna bağlanılamadı. Konsol proxy hizmetini kontrol edin.',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user