Browse Source

update

tags/v1.22.11.1^2
liuzx 3 years ago
parent
commit
52d69f1287
4 changed files with 313 additions and 68 deletions
  1. +40
    -13
      models/cloudbrain_static.go
  2. +1
    -1
      modules/cron/tasks_basic.go
  3. +267
    -34
      routers/api/v1/repo/cloudbrain_dashboard.go
  4. +5
    -20
      routers/repo/cloudbrain_statistic.go

+ 40
- 13
models/cloudbrain_static.go View File

@@ -46,7 +46,6 @@ type CloudbrainDurationStatistic struct {
AiCenterName string
ComputeResource string
AccCardType string
TotalUse bool
TotalCanUse bool

DateTime string
@@ -55,9 +54,20 @@ type CloudbrainDurationStatistic struct {
CardsTotalDuration int
CardsTotalNum int

DeletedTime timeutil.TimeStamp `xorm:"deleted"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
DeletedUnix timeutil.TimeStamp `xorm:"deleted"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
type DurationStatisticOptions struct {
BeginTime time.Time
EndTime time.Time
AiCenterCode string
}
type DateCloudbrainStatistic struct {
Date string `json:"date"`
AiCenterUsageDuration map[string]int `json:"aiCenterUsageDuration"`
AiCenterTotalDuration map[string]int `json:"aiCenterTotalDuration"`
AiCenterUsageRate map[string]int `json:"aiCenterUsageRate"`
}

func GetTodayCreatorCount(beginTime time.Time, endTime time.Time) (int64, error) {
@@ -273,14 +283,14 @@ func InsertCloudbrainDurationStatistic(cloudbrainDurationStatistic *CloudbrainDu
return xStatistic.Insert(cloudbrainDurationStatistic)
}

func DeleteCloudbrainDurationStatisticHour(date string, hour int, aiCenterCode string, accCardType string, tatalUse bool, totalCanUse bool) error {
func DeleteCloudbrainDurationStatisticHour(date string, hour int, aiCenterCode string, accCardType string, totalCanUse bool) error {
sess := xStatistic.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return fmt.Errorf("Begin: %v", err)
}

if _, err := sess.Where("day_time = ? AND hour_time = ? AND ai_center_code = ? AND acc_card_type = ? And total_use = ? And total_can_use = ?", date, hour, aiCenterCode, accCardType, tatalUse, totalCanUse).Delete(&CloudbrainDurationStatistic{}); err != nil {
if _, err := sess.Where("day_time = ? AND hour_time = ? AND ai_center_code = ? AND acc_card_type = ? And total_can_use = ?", date, hour, aiCenterCode, accCardType, totalCanUse).Delete(&CloudbrainDurationStatistic{}); err != nil {
return fmt.Errorf("Delete: %v", err)
}

@@ -307,16 +317,21 @@ func GetCanUseCardInfo() ([]*ResourceQueue, error) {
}
return ResourceQueues, nil
}
func GetCardDurationStatistics(beginTime time.Time, endTime time.Time, totalUse bool, totalCanUse bool) ([]*CloudbrainDurationStatistic, error) {

func GetCardDurationStatistics(opts *DurationStatisticOptions) ([]*CloudbrainDurationStatistic, error) {
sess := xStatistic.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.And(builder.Gte{"cloudbrain_duration_statistic.created_time": beginTime.Unix()}, builder.Lte{"cloudbrain_duration_statistic.created_time": endTime.Unix()}),
)
cond = cond.And(
builder.And(builder.Eq{"cloudbrain_duration_statistic.total_use": totalUse}, builder.Eq{"cloudbrain_duration_statistic.total_can_use": totalCanUse}),
)
if opts.BeginTime.Unix() > 0 && opts.EndTime.Unix() > 0 {
cond = cond.And(
builder.And(builder.Gte{"cloudbrain_duration_statistic.created_unix": opts.BeginTime.Unix()}, builder.Lte{"cloudbrain_duration_statistic.created_unix": opts.EndTime.Unix()}),
)
}
if opts.AiCenterCode != "" {
cond = cond.And(
builder.Eq{"cloudbrain_duration_statistic.ai_center_code": opts.AiCenterCode},
)
}
CloudbrainDurationStatistics := make([]*CloudbrainDurationStatistic, 0, 10)
if err := sess.Table(&CloudbrainDurationStatistic{}).Where(cond).
Find(&CloudbrainDurationStatistics); err != nil {
@@ -324,3 +339,15 @@ func GetCardDurationStatistics(beginTime time.Time, endTime time.Time, totalUse
}
return CloudbrainDurationStatistics, nil
}

func GetDurationRecordBeginTime() ([]*CloudbrainDurationStatistic, error) {
sess := xStatistic.NewSession()
defer sess.Close()
sess.OrderBy("cloudbrain_duration_statistic.id ASC limit 1")
CloudbrainDurationStatistics := make([]*CloudbrainDurationStatistic, 0)
if err := sess.Table(&CloudbrainDurationStatistic{}).Unscoped().
Find(&CloudbrainDurationStatistics); err != nil {
log.Info("find error.")
}
return CloudbrainDurationStatistics, nil
}

+ 1
- 1
modules/cron/tasks_basic.go View File

@@ -261,7 +261,7 @@ func registerHandleCloudbrainDurationStatistic() {
RunAtStart: false,
Schedule: "@every 60m",
}, func(ctx context.Context, _ *models.User, _ Config) error {
repo.CloudbrainDurationStatistic()
repo.CloudbrainDurationStatisticHour()
return nil
})
}


+ 267
- 34
routers/api/v1/repo/cloudbrain_dashboard.go View File

@@ -532,6 +532,21 @@ func getPageDateCloudbrainInfo(dateCloudbrainInfo []DateCloudbrainInfo, page int

}

func getPageDateCloudbrainDuration(dateCloudbrainDuration []models.DateCloudbrainStatistic, page int, pagesize int) []models.DateCloudbrainStatistic {
begin := (page - 1) * pagesize
end := (page) * pagesize

if begin > len(dateCloudbrainDuration)-1 {
return nil
}
if end > len(dateCloudbrainDuration)-1 {
return dateCloudbrainDuration[begin:]
} else {
return dateCloudbrainDuration[begin:end]
}

}

func GetAllCloudbrainsPeriodDistribution(ctx *context.Context) {
queryType := ctx.QueryTrim("type")
beginTimeStr := ctx.QueryTrim("beginTime")
@@ -1426,59 +1441,277 @@ func GetCloudbrainResourceUsage(ctx *context.Context) {
}
cardUsageRes := make(map[string]int)
cardCanUsageRes := make(map[string]int)
cardUseInfo, err := models.GetCardDurationStatistics(beginTime, endTime, true, false)
if err != nil {
log.Error("GetCardDurationStatistics error:", err)
return
}
cardCanUseInfo, err := models.GetCardDurationStatistics(beginTime, endTime, false, true)
cardUtilizationRate := make(map[string]int)
// cardDurationStatistics, err := models.GetCardDurationStatistics(beginTime, endTime)
cardDurationStatistics, err := models.GetCardDurationStatistics(&models.DurationStatisticOptions{
BeginTime: beginTime,
EndTime: endTime,
})
if err != nil {
log.Error("GetCardDurationStatistics error:", err)
return
}

for _, cloudbrainStat := range cardUseInfo {
if _, ok := cardUsageRes[cloudbrainStat.AiCenterCode]; !ok {
cardUsageRes[cloudbrainStat.AiCenterCode] = cloudbrainStat.CardsTotalDuration
for _, cloudbrainStat := range cardDurationStatistics {
if cloudbrainStat.TotalCanUse {
if _, ok := cardCanUsageRes[cloudbrainStat.AiCenterCode]; !ok {
cardCanUsageRes[cloudbrainStat.AiCenterCode] = cloudbrainStat.CardsTotalDuration
} else {
cardCanUsageRes[cloudbrainStat.AiCenterCode] += cloudbrainStat.CardsTotalDuration
}
} else {
cardUsageRes[cloudbrainStat.AiCenterCode] += cloudbrainStat.CardsTotalDuration
if _, ok := cardUsageRes[cloudbrainStat.AiCenterCode]; !ok {
cardUsageRes[cloudbrainStat.AiCenterCode] = cloudbrainStat.CardsTotalDuration
} else {
cardUsageRes[cloudbrainStat.AiCenterCode] += cloudbrainStat.CardsTotalDuration
}
}
}

for _, cloudbrainStat := range cardCanUseInfo {
if _, ok := cardCanUsageRes[cloudbrainStat.AiCenterCode]; !ok {
cardCanUsageRes[cloudbrainStat.AiCenterCode] = cloudbrainStat.CardsTotalDuration
} else {
cardCanUsageRes[cloudbrainStat.AiCenterCode] += cloudbrainStat.CardsTotalDuration
for k, v := range cardCanUsageRes {
for j, i := range cardUsageRes {
if k == j {
cardUtilizationRate[k] = i / v
}
}
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"cardUseInfo": cardUseInfo,
"cardCanUseInfo": cardCanUseInfo,
"cardUsageRes": cardUsageRes,
"cardCanUsageRes": cardCanUsageRes,
"cardDurationStatistics": cardDurationStatistics,
"cardUsageRes": cardUsageRes,
"cardCanUsageRes": cardCanUsageRes,
"cardUtilizationRate": cardUtilizationRate,
})

}

func GetCloudbrainResourceUsageDetail(ctx *context.Context) {
recordBeginTime := time.Now().AddDate(0, 0, -6)
beginTime, endTime, err := getCloudbrainTimePeroid(ctx, recordBeginTime)
if err != nil {
log.Error("getCloudbrainTimePeroid error:", err)
return
queryType := ctx.QueryTrim("type")
now := time.Now()

beginTimeStr := ctx.QueryTrim("beginTime")
endTimeStr := ctx.QueryTrim("endTime")
var beginTime time.Time
var endTime time.Time
var endTimeTemp time.Time
dayCloudbrainDuration := make([]models.DateCloudbrainStatistic, 0)
var err error
var count int
if queryType != "" {
if queryType == "all" {
recordCloudbrainDuration, err := models.GetDurationRecordBeginTime()
if err != nil {
log.Error("Can not get GetDurationRecordBeginTime", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err"))
return
}
brainRecordBeginTime := recordCloudbrainDuration[0].CreatedUnix.AsTime()
beginTime = brainRecordBeginTime
endTime = now
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}
} else if queryType == "today" {
beginTime = now.AddDate(0, 0, 0)
beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location())
endTime = now
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}

} else if queryType == "yesterday" {
beginTime = now.AddDate(0, 0, -1)
beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location())
endTime = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}

} else if queryType == "last_7day" {
beginTime = now.AddDate(0, 0, -6)
beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location())
endTime = now
endTimeTemp = time.Date(endTimeTemp.Year(), endTimeTemp.Month(), endTimeTemp.Day(), 0, 0, 0, 0, now.Location())
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}
} else if queryType == "last_30day" {
beginTime = now.AddDate(0, 0, -29)
beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location())
endTime = now
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}
} else if queryType == "current_month" {
endTime = now
beginTime = time.Date(endTime.Year(), endTime.Month(), 1, 0, 0, 0, 0, now.Location())
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}

} else if queryType == "current_year" {
endTime = now
beginTime = time.Date(endTime.Year(), 1, 1, 0, 0, 0, 0, now.Location())
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}

} else if queryType == "last_month" {

lastMonthTime := now.AddDate(0, -1, 0)
beginTime = time.Date(lastMonthTime.Year(), lastMonthTime.Month(), 1, 0, 0, 0, 0, now.Location())
endTime = time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}

}

} else {
if beginTimeStr == "" || endTimeStr == "" {
//如果查询类型和开始时间结束时间都未设置,按queryType=all处理
recordCloudbrainDuration, err := models.GetDurationRecordBeginTime()
if err != nil {
log.Error("Can not get recordCloudbrain", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err"))
return
}
brainRecordBeginTime := recordCloudbrainDuration[0].CreatedUnix.AsTime()
beginTime = brainRecordBeginTime
endTime = now
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}
} else {
beginTime, err = time.ParseInLocation("2006-01-02", beginTimeStr, time.Local)
if err != nil {
log.Error("Can not ParseInLocation.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("ParseInLocation_get_error"))
return
}
endTime, err = time.ParseInLocation("2006-01-02", endTimeStr, time.Local)
if err != nil {
log.Error("Can not ParseInLocation.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("ParseInLocation_get_error"))
return
}
if endTime.After(time.Now()) {
endTime = time.Now()
}
endTimeTemp = beginTime.AddDate(0, 0, 1)
dayCloudbrainDuration, count, err = getDayCloudbrainDuration(beginTime, endTime)
if err != nil {
log.Error("Can not query dayCloudbrainDuration.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("getDayCloudbrainInfo_get_error"))
return
}
}

}
totalUse := true
totalCanUse := false
cardDurationStatisticsInfo, err := models.GetCardDurationStatistics(beginTime, endTime, totalUse, totalCanUse)
if err != nil {
log.Error("GetCardDurationStatistics error:", err)
return

page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pagesize := ctx.QueryInt("pagesize")
if pagesize <= 0 {
pagesize = 5
}
pageDateCloudbrainDuration := getPageDateCloudbrainDuration(dayCloudbrainDuration, page, pagesize)

ctx.JSON(http.StatusOK, map[string]interface{}{
"cardDurationStatisticsInfo": cardDurationStatisticsInfo,
"beginTime": beginTime,
"endTime": endTime,
"totalCount": count,
"pageDateCloudbrainDuration": pageDateCloudbrainDuration,
})

}

func getAiCenterUsageDuration(beginTime time.Time, endTime time.Time, cloudbrainStatistics []*models.CloudbrainDurationStatistic) (map[string]int, map[string]int, map[string]int) {

aiCenterTotalDuration := make(map[string]int)
aiCenterUsageDuration := make(map[string]int)
aiCenterUsageRate := make(map[string]int)
for _, cloudbrainStatistic := range cloudbrainStatistics {
if int64(cloudbrainStatistic.CreatedUnix) >= beginTime.Unix() && int64(cloudbrainStatistic.CreatedUnix) < endTime.Unix() {
if cloudbrainStatistic.TotalCanUse {
if _, ok := aiCenterTotalDuration[cloudbrainStatistic.AiCenterCode]; !ok {
aiCenterTotalDuration[cloudbrainStatistic.AiCenterCode] = cloudbrainStatistic.CardsTotalDuration
} else {
aiCenterTotalDuration[cloudbrainStatistic.AiCenterCode] += cloudbrainStatistic.CardsTotalDuration
}
} else {
if _, ok := aiCenterUsageDuration[cloudbrainStatistic.AiCenterCode]; !ok {
aiCenterUsageDuration[cloudbrainStatistic.AiCenterCode] = cloudbrainStatistic.CardsTotalDuration
} else {
aiCenterUsageDuration[cloudbrainStatistic.AiCenterCode] += cloudbrainStatistic.CardsTotalDuration
}
}
}
}
for k, v := range aiCenterTotalDuration {
for i, j := range aiCenterUsageDuration {
if k == i {
aiCenterUsageRate[k] = j / v
}
}
}

return aiCenterUsageDuration, aiCenterTotalDuration, aiCenterUsageRate
}

func getDayCloudbrainDuration(beginTime time.Time, endTime time.Time) ([]models.DateCloudbrainStatistic, int, error) {
now := time.Now()
endTimeTemp := time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, now.Location())
if endTimeTemp.Equal(endTime) {
endTimeTemp = endTimeTemp.AddDate(0, 0, -1)
}
cardDurationStatistics, err := models.GetCardDurationStatistics(&models.DurationStatisticOptions{
BeginTime: beginTime,
EndTime: endTime,
})
if err != nil {
log.Error("GetCardDurationStatistics error:", err)
return nil, 0, err
}
dayCloudbrainInfo := make([]models.DateCloudbrainStatistic, 0)
count := 0
for beginTime.Before(endTimeTemp) || beginTime.Equal(endTimeTemp) {
aiCenterUsageDuration, aiCenterTotalDuration, aiCenterUsageRate := getAiCenterUsageDuration(endTimeTemp, endTime, cardDurationStatistics)
dayCloudbrainInfo = append(dayCloudbrainInfo, models.DateCloudbrainStatistic{
Date: endTimeTemp.Format("2006/01/02"),
AiCenterUsageDuration: aiCenterUsageDuration,
AiCenterTotalDuration: aiCenterTotalDuration,
AiCenterUsageRate: aiCenterUsageRate,
})
endTime = endTimeTemp
endTimeTemp = endTimeTemp.AddDate(0, 0, -1)
count += 1
}
return dayCloudbrainInfo, count, nil
}

+ 5
- 20
routers/repo/cloudbrain_statistic.go View File

@@ -4,19 +4,11 @@ import (
"time"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
)

func CloudbrainDurationStatistic() {
log.Info("Generate Cloudbrain Duration statistic begin")
// yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
// CloudbrainDurationStatisticHour(yesterday)
log.Info("Generate Cloudbrain Duration statistic end")
}

func CloudbrainDurationStatisticHour(ctx *context.Context) {
func CloudbrainDurationStatisticHour() {
hourTime := time.Now().Hour()
dateTime := time.Now().Format("2006-01-02 15:04:05")
dayTime := time.Now().Format("2006-01-02")
@@ -25,11 +17,9 @@ func CloudbrainDurationStatisticHour(ctx *context.Context) {
m, _ := time.ParseDuration("-1h")
beginTime := currentTime.Add(m).Unix()
endTime := currentTime.Unix()
// fmt.Println(beginTime)

ciTasks1, err := models.GetCloudbrainRunning()
if err != nil {
// ctx.ServerError("Get job failed:", err)
log.Info("GetCloudbrainRunning err: %v", err)
return
}
@@ -38,7 +28,6 @@ func CloudbrainDurationStatisticHour(ctx *context.Context) {
log.Info("beginTime: %s", beginTime)
log.Info("endTime: %s", endTime)
if err != nil {
// ctx.ServerError("Get job failed:", err)
log.Info("GetCloudbrainCompleteByTime err: %v", err)
return
}
@@ -55,9 +44,8 @@ func CloudbrainDurationStatisticHour(ctx *context.Context) {
}
log.Info("cloudbrain: %s", cloudbrain)
if cloudbrain != nil {
totalUse := true
totalCanUse := false
if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, centerCode, cardType, totalUse, totalCanUse); err != nil {
if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, centerCode, cardType, totalCanUse); err != nil {
log.Error("DeleteCloudbrainDurationStatisticHour failed: %v", err.Error())
return
}
@@ -71,8 +59,7 @@ func CloudbrainDurationStatisticHour(ctx *context.Context) {
ComputeResource: cloudbrain[0].ComputeResource,
AccCardType: cardType,
CardsTotalDuration: cardDuration,
CreatedTime: timeutil.TimeStampNow(),
TotalUse: true,
CreatedUnix: timeutil.TimeStampNow(),
TotalCanUse: false,
}
if _, err = models.InsertCloudbrainDurationStatistic(&cloudbrainDurationStat); err != nil {
@@ -90,9 +77,8 @@ func CloudbrainDurationStatisticHour(ctx *context.Context) {
}
log.Info("resourceQueues here: %s", resourceQueues)
for _, resourceQueue := range resourceQueues {
totalUse := false
totalCanUse := true
if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, resourceQueue.AiCenterCode, resourceQueue.AccCardType, totalUse, totalCanUse); err != nil {
if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, resourceQueue.AiCenterCode, resourceQueue.AccCardType, totalCanUse); err != nil {
log.Error("DeleteCloudbrainDurationStatisticHour failed: %v", err.Error())
return
}
@@ -108,8 +94,7 @@ func CloudbrainDurationStatisticHour(ctx *context.Context) {
AccCardType: resourceQueue.AccCardType,
CardsTotalDuration: cardsTotalDuration,
CardsTotalNum: resourceQueue.CardsTotalNum,
CreatedTime: timeutil.TimeStampNow(),
TotalUse: false,
CreatedUnix: timeutil.TimeStampNow(),
TotalCanUse: true,
}
if _, err = models.InsertCloudbrainDurationStatistic(&cloudbrainDurationStat); err != nil {


Loading…
Cancel
Save