diff --git a/models/models.go b/models/models.go index ebbc584e2..d6198c720 100755 --- a/models/models.go +++ b/models/models.go @@ -147,6 +147,8 @@ func init() { new(AiModelConvert), new(ResourceQueue), new(ResourceSpecification), + new(ResourceScene), + new(ResourceSceneSpec), ) tablesStatistic = append(tablesStatistic, diff --git a/models/resource_queue.go b/models/resource_queue.go index 9440b302b..b6b29ee16 100644 --- a/models/resource_queue.go +++ b/models/resource_queue.go @@ -82,6 +82,10 @@ type ResourceQueueCodesRes struct { AiCenterCode string } +type GetQueueCodesOptions struct { + Cluster string +} + func NewResourceQueueListRes(totalSize int64, list []ResourceQueue) *ResourceQueueListRes { resList := make([]*ResourceQueueRes, len(list)) for i, v := range list { @@ -143,9 +147,15 @@ func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueu return n, r, nil } -func GetResourceQueueCodes() ([]*ResourceQueueCodesRes, error) { +func GetResourceQueueCodes(opts GetQueueCodesOptions) ([]*ResourceQueueCodesRes, error) { + cond := builder.NewCond() + if opts.Cluster != "" { + cond = cond.And(builder.Eq{"cluster": opts.Cluster}) + } + cond = cond.And(builder.NotNull{"queue_code"}) + cond = cond.And(builder.Neq{"queue_code": ""}) r := make([]*ResourceQueueCodesRes, 0) - err := x.Table("resource_queue").Where("queue_code is not null AND queue_code != '' ").Find(&r) + err := x.Table("resource_queue").Where(cond).OrderBy("id desc").Find(&r) if err != nil { return nil, err } diff --git a/models/resource_scene.go b/models/resource_scene.go index a5730f0bd..114313f7f 100644 --- a/models/resource_scene.go +++ b/models/resource_scene.go @@ -1,6 +1,15 @@ package models -import "code.gitea.io/gitea/modules/timeutil" +import ( + "code.gitea.io/gitea/modules/timeutil" + "errors" + "xorm.io/builder" +) + +const ( + Exclusive = iota + 1 + NotExclusive +) type ResourceScene struct { ID int64 `xorm:"pk autoincr"` @@ -9,9 +18,9 @@ type ResourceScene struct { IsExclusive bool ExclusiveOrg string QueueId int64 - CreatedTime timeutil.TimeStamp `xorm:"INDEX created"` + CreatedTime timeutil.TimeStamp `xorm:"created"` CreatedBy int64 - UpdatedTime timeutil.TimeStamp `xorm:"INDEX updated"` + UpdatedTime timeutil.TimeStamp `xorm:"updated"` UpdatedBy int64 DeleteTime timeutil.TimeStamp `xorm:"deleted"` DeletedBy int64 @@ -19,7 +28,285 @@ type ResourceScene struct { type ResourceSceneSpec struct { ID int64 `xorm:"pk autoincr"` - SceneId int64 `xorm:"INDEX"` - SpecId int64 `xorm:"INDEX"` + SceneId int64 `xorm:"unique(idx_scene_spec)"` + SpecId int64 `xorm:"unique(idx_scene_spec)"` CreatedTime timeutil.TimeStamp `xorm:"created"` } + +type ResourceSceneReq struct { + ID int64 + SceneName string + JobType string + IsExclusive bool + ExclusiveOrg string + QueueId int64 + CreatorId int64 + SpecIds []int64 +} + +type SearchResourceSceneOptions struct { + ListOptions + JobType string + IsExclusive int + AiCenterCode string + QueueId int64 +} + +type ResourceSceneListRes struct { + TotalSize int64 + List []ResourceSceneRes +} + +func NewResourceSceneListRes(totalSize int64, list []ResourceSceneRes) *ResourceSceneListRes { + return &ResourceSceneListRes{ + TotalSize: totalSize, + List: list, + } +} + +type ResourceSceneRes struct { + ID int64 + SceneName string + JobType JobType + IsExclusive bool + ExclusiveOrg string + Cluster string + AiCenterCode string + QueueCode string + Specs []ResourceSpecWithSceneId +} + +func (ResourceSceneRes) TableName() string { + return "resource_scene" +} + +type ResourceSpecWithSceneId struct { + ID int64 + SourceSpecId string + AccCardsNum int + CpuCores int + MemGiB float32 + GPUMemGiB float32 + ShareMemGiB float32 + UnitPrice int + Status int + UpdatedTime timeutil.TimeStamp + SceneId int64 +} + +func (ResourceSpecWithSceneId) TableName() string { + return "resource_specification" +} + +func InsertResourceScene(r ResourceSceneReq) error { + sess := x.NewSession() + defer sess.Close() + + //check + specs := make([]ResourceSpecification, 0) + cond := builder.In("id", r.SpecIds).And(builder.Eq{"queue_id": r.QueueId}).And(builder.Eq{"status": SpecOnShelf}) + if err := sess.Where(cond).Find(&specs); err != nil { + return err + } + if len(specs) < len(r.SpecIds) { + return errors.New("specIds not correct") + } + + rs := ResourceScene{ + SceneName: r.SceneName, + JobType: r.JobType, + IsExclusive: r.IsExclusive, + ExclusiveOrg: r.ExclusiveOrg, + QueueId: r.QueueId, + CreatedBy: r.CreatorId, + UpdatedBy: r.CreatorId, + } + _, err := sess.InsertOne(&rs) + if err != nil { + sess.Rollback() + return err + } + + if len(r.SpecIds) == 0 { + return sess.Commit() + } + rss := make([]ResourceSceneSpec, len(r.SpecIds)) + for i, v := range r.SpecIds { + rss[i] = ResourceSceneSpec{ + SceneId: rs.ID, + SpecId: v, + } + } + + _, err = sess.Insert(&rss) + if err != nil { + sess.Rollback() + return err + } + + return sess.Commit() +} + +func UpdateResourceScene(r ResourceSceneReq) error { + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + + // find old scene + old := ResourceScene{} + if has, _ := sess.ID(r.ID).Get(&old); !has { + return errors.New("ResourceScene not exist") + } + //check specification + specs := make([]ResourceSpecification, 0) + cond := builder.In("id", r.SpecIds).And(builder.Eq{"queue_id": old.QueueId}).And(builder.Eq{"status": SpecOnShelf}) + if err := sess.Where(cond).Find(&specs); err != nil { + return err + } + if len(specs) < len(r.SpecIds) { + return errors.New("specIds not correct") + } + + //update scene + rs := ResourceScene{ + SceneName: r.SceneName, + IsExclusive: r.IsExclusive, + ExclusiveOrg: r.ExclusiveOrg, + } + if _, err = sess.ID(r.ID).UseBool("is_exclusive").Update(&rs); err != nil { + return err + } + + //delete scene spec relation + if _, err = sess.Where("scene_id = ? ", r.ID).Delete(&ResourceSceneSpec{}); err != nil { + sess.Rollback() + return err + } + + if len(r.SpecIds) == 0 { + return sess.Commit() + } + //build new scene spec relation + rss := make([]ResourceSceneSpec, len(r.SpecIds)) + for i, v := range r.SpecIds { + rss[i] = ResourceSceneSpec{ + SceneId: r.ID, + SpecId: v, + } + } + if _, err = sess.Insert(&rss); err != nil { + sess.Rollback() + return err + } + + return sess.Commit() +} + +func DeleteResourceScene(sceneId int64) error { + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + + if _, err = sess.ID(sceneId).Delete(&ResourceScene{}); err != nil { + return err + } + if _, err = sess.Where("scene_id = ? ", sceneId).Delete(&ResourceSceneSpec{}); err != nil { + return err + } + return sess.Commit() +} + +func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceSceneRes, error) { + var cond = builder.NewCond() + if opts.Page <= 0 { + opts.Page = 1 + } + if opts.JobType != "" { + cond = cond.And(builder.Eq{"resource_scene.job_type": opts.JobType}) + } + if opts.IsExclusive == Exclusive { + cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 1}) + } else if opts.IsExclusive == NotExclusive { + cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 0}) + } + if opts.AiCenterCode != "" { + cond = cond.And(builder.Eq{"resource_queue.ai_center_code": opts.AiCenterCode}) + } + if opts.QueueId > 0 { + cond = cond.And(builder.Eq{"resource_scene.queue_id": opts.QueueId}) + } + cond = cond.And(builder.NewCond().Or(builder.Eq{"resource_scene.delete_time": 0}).Or(builder.IsNull{"resource_scene.delete_time"})) + count, err := x.Where(cond).Join("INNER", "resource_queue", "resource_queue.id = resource_scene.queue_id").Count(&ResourceSceneRes{}) + if err != nil { + return 0, nil, err + } + cols := []string{"resource_scene.id", "resource_scene.scene_name", "resource_scene.job_type", "resource_scene.is_exclusive", + "resource_scene.exclusive_org", "resource_queue.cluster", "resource_queue.ai_center_code", + "resource_queue.queue_code"} + r := make([]ResourceSceneRes, 0) + if err = x.Where(cond).Cols(cols...). + Join("INNER", "resource_queue", "resource_queue.id = resource_scene.queue_id"). + Desc("resource_scene.id"). + Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). + Find(&r); err != nil { + return 0, nil, err + } + + if len(r) == 0 { + return 0, r, err + } + //find related specs + sceneIds := make([]int64, 0, len(r)) + for _, v := range r { + sceneIds = append(sceneIds, v.ID) + } + + specs := make([]ResourceSpecWithSceneId, 0) + + if err := x.Cols("resource_specification.id", + "resource_specification.source_spec_id", + "resource_specification.acc_cards_num", + "resource_specification.cpu_cores", + "resource_specification.mem_gi_b", + "resource_specification.gpu_mem_gi_b", + "resource_specification.share_mem_gi_b", + "resource_specification.unit_price", + "resource_specification.status", + "resource_specification.updated_time", + "resource_scene_spec.scene_id", + ).In("resource_scene_spec.scene_id", sceneIds). + Join("INNER", "resource_scene_spec", "resource_scene_spec.spec_id = resource_specification.id"). + OrderBy("resource_specification.acc_cards_num"). + Find(&specs); err != nil { + return 0, nil, err + } + + specsMap := make(map[int64][]ResourceSpecWithSceneId, 0) + for _, v := range specs { + if _, ok := specsMap[v.SceneId]; !ok { + specsMap[v.SceneId] = []ResourceSpecWithSceneId{v} + } else { + specsMap[v.SceneId] = append(specsMap[v.SceneId], v) + } + } + + for i, v := range r { + s := specsMap[v.ID] + if s == nil { + s = make([]ResourceSpecWithSceneId, 0) + } + r[i].Specs = s + } + + return count, r, nil +} diff --git a/models/resource_specification.go b/models/resource_specification.go index 281bb4e5d..2a2093bf6 100644 --- a/models/resource_specification.go +++ b/models/resource_specification.go @@ -46,6 +46,7 @@ func (r ResourceSpecification) ConvertToRes() *ResourceSpecificationRes { type ResourceSpecificationReq struct { QueueId int64 `binding:"Required"` + SourceSpecId string AccCardsNum int CpuCores int MemGiB float32 @@ -60,6 +61,7 @@ type ResourceSpecificationReq struct { func (r ResourceSpecificationReq) ToDTO() ResourceSpecification { return ResourceSpecification{ QueueId: r.QueueId, + SourceSpecId: r.SourceSpecId, AccCardsNum: r.AccCardsNum, CpuCores: r.CpuCores, MemGiB: r.MemGiB, @@ -77,6 +79,12 @@ type SearchResourceSpecificationOptions struct { ListOptions QueueId int64 Status int + Cluster string +} + +type SearchResourceBriefSpecificationOptions struct { + QueueId int64 + Cluster string } type ResourceSpecAndQueueListRes struct { @@ -108,6 +116,10 @@ type ResourceSpecificationRes struct { UpdatedTime timeutil.TimeStamp } +func (ResourceSpecificationRes) TableName() string { + return "resource_specification" +} + type ResourceSpecAndQueueRes struct { Spec *ResourceSpecificationRes Queue *ResourceQueueRes @@ -133,8 +145,8 @@ func InsertResourceSpecification(r ResourceSpecification) (int64, error) { return x.Insert(&r) } -func UpdateResourceSpecificationById(queueId int64, queue ResourceSpecification) (int64, error) { - return x.ID(queueId).Update(&queue) +func UpdateResourceSpecificationById(queueId int64, spec ResourceSpecification) (int64, error) { + return x.ID(queueId).Update(&spec) } func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64, []ResourceSpecAndQueue, error) { @@ -148,14 +160,18 @@ func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64 if opts.Status > 0 { cond = cond.And(builder.Eq{"resource_specification.status": opts.Status}) } - n, err := x.Where(cond).Count(&ResourceSpecAndQueue{}) + if opts.Cluster != "" { + cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster}) + } + n, err := x.Where(cond).Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id"). + Count(&ResourceSpecAndQueue{}) if err != nil { return 0, nil, err } r := make([]ResourceSpecAndQueue, 0) err = x.Where(cond). - Join("LEFT", "resource_queue", "resource_queue.ID = resource_specification.queue_id"). + Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id"). Desc("resource_specification.id"). Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). Find(&r) @@ -165,17 +181,32 @@ func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64 return n, r, nil } -func ResourceSpecOnShelf(id int64) (int64, error) { - param := ResourceSpecification{ - Status: SpecOnShelf, - } - return x.Where("id = ? and status in (?,?)", id, SpecOffShelf, SpecNotVerified).Update(¶m) +func ResourceSpecOnShelf(id int64, spec ResourceSpecification) (int64, error) { + spec.Status = SpecOnShelf + return x.Where("id = ? and status in (?,?)", id, SpecOffShelf, SpecNotVerified).Update(&spec) } func ResourceSpecOffShelf(id int64) (int64, error) { - //todo delete scene + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + //delete scene spec relation + if _, err = sess.Where("spec_id = ?", id).Delete(&ResourceSceneSpec{}); err != nil { + return 0, err + } + param := ResourceSpecification{ Status: SpecOffShelf, } - return x.Where("id = ? and status = ?", id, SpecOnShelf).Update(¶m) + n, err := sess.Where("id = ? and status = ?", id, SpecOnShelf).Update(¶m) + if err != nil { + return 0, err + } + sess.Commit() + return n, err } diff --git a/routers/admin/resources.go b/routers/admin/resources.go index 172b8dc7a..9f45d4e3d 100644 --- a/routers/admin/resources.go +++ b/routers/admin/resources.go @@ -59,7 +59,8 @@ func GetResourceQueueList(ctx *context.Context) { } func GetResourceQueueCodes(ctx *context.Context) { - list, err := resource.GetResourceQueueCodes() + cluster := ctx.Query("cluster") + list, err := resource.GetResourceQueueCodes(models.GetQueueCodesOptions{Cluster: cluster}) if err != nil { log.Error("GetResourceQueueCodes error.%v", err) ctx.JSON(http.StatusOK, response.ServerError(err.Error())) @@ -96,10 +97,12 @@ func GetResourceSpecificationList(ctx *context.Context) { page := ctx.QueryInt("page") queue := ctx.QueryInt64("queue") status := ctx.QueryInt("status") + cluster := ctx.Query("cluster") list, err := resource.GetResourceSpecificationList(models.SearchResourceSpecificationOptions{ ListOptions: models.ListOptions{Page: page, PageSize: 20}, QueueId: queue, Status: status, + Cluster: cluster, }) if err != nil { log.Error("GetResourceSpecificationList error.%v", err) @@ -135,7 +138,7 @@ func UpdateResourceSpecification(ctx *context.Context, req models.ResourceSpecif //only UnitPrice and permitted to change err = resource.UpdateResourceSpecification(id, req) case "on-shelf": - err = resource.ResourceSpecOnShelf(id) + err = resource.ResourceSpecOnShelf(id, req) case "off-shelf": err = resource.ResourceSpecOffShelf(id) } @@ -147,3 +150,56 @@ func UpdateResourceSpecification(ctx *context.Context, req models.ResourceSpecif } ctx.JSON(http.StatusOK, response.Success()) } + +func GetResourceSceneList(ctx *context.Context) { + page := ctx.QueryInt("page") + jobType := ctx.Query("jobType") + aiCenterCode := ctx.Query("center") + queueId := ctx.QueryInt64("queue") + isExclusive := ctx.QueryInt("IsExclusive") + list, err := resource.GetResourceSceneList(models.SearchResourceSceneOptions{ + ListOptions: models.ListOptions{Page: page, PageSize: 20}, + JobType: jobType, + IsExclusive: isExclusive, + AiCenterCode: aiCenterCode, + QueueId: queueId, + }) + if err != nil { + log.Error("GetResourceSceneList error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.SuccessWithData(list)) +} + +func AddResourceScene(ctx *context.Context, req models.ResourceSceneReq) { + req.CreatorId = ctx.User.ID + err := resource.AddResourceScene(req) + if err != nil { + log.Error("AddResourceScene error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func UpdateResourceScene(ctx *context.Context, req models.ResourceSceneReq) { + id := ctx.ParamsInt64(":id") + action := ctx.Query("action") + + req.ID = id + var err error + switch action { + case "edit": + err = resource.UpdateResourceScene(req) + case "delete": + err = resource.DeleteResourceScene(id) + } + + if err != nil { + log.Error("UpdateResourceScene error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index bad06dd5e..b52709099 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -622,6 +622,9 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Group("/scene", func() { m.Get("", admin.GetScenePage) + m.Get("/list", admin.GetResourceSceneList) + m.Post("/add", binding.Bind(models.ResourceSceneReq{}), admin.AddResourceScene) + m.Post("/update/:id", binding.BindIgnErr(models.ResourceSceneReq{}), admin.UpdateResourceScene) }) }) }, adminReq) diff --git a/services/cloudbrain/resource/resource_queue.go b/services/cloudbrain/resource/resource_queue.go index b336dedaf..d576307be 100644 --- a/services/cloudbrain/resource/resource_queue.go +++ b/services/cloudbrain/resource/resource_queue.go @@ -30,8 +30,8 @@ func GetResourceQueueList(opts models.SearchResourceQueueOptions) (*models.Resou return models.NewResourceQueueListRes(n, r), nil } -func GetResourceQueueCodes() ([]*models.ResourceQueueCodesRes, error) { - r, err := models.GetResourceQueueCodes() +func GetResourceQueueCodes(opts models.GetQueueCodesOptions) ([]*models.ResourceQueueCodesRes, error) { + r, err := models.GetResourceQueueCodes(opts) if err != nil { return nil, err } diff --git a/services/cloudbrain/resource/resource_scene.go b/services/cloudbrain/resource/resource_scene.go new file mode 100644 index 000000000..4cc840e76 --- /dev/null +++ b/services/cloudbrain/resource/resource_scene.go @@ -0,0 +1,35 @@ +package resource + +import ( + "code.gitea.io/gitea/models" +) + +func AddResourceScene(req models.ResourceSceneReq) error { + if err := models.InsertResourceScene(req); err != nil { + return err + } + return nil +} + +func UpdateResourceScene(req models.ResourceSceneReq) error { + if err := models.UpdateResourceScene(req); err != nil { + return err + } + return nil +} + +func DeleteResourceScene(id int64) error { + if err := models.DeleteResourceScene(id); err != nil { + return err + } + return nil +} + +func GetResourceSceneList(opts models.SearchResourceSceneOptions) (*models.ResourceSceneListRes, error) { + n, r, err := models.SearchResourceScene(opts) + if err != nil { + return nil, err + } + + return models.NewResourceSceneListRes(n, r), nil +} diff --git a/services/cloudbrain/resource/resource_specification.go b/services/cloudbrain/resource/resource_specification.go index a22f0415e..f29d4874d 100644 --- a/services/cloudbrain/resource/resource_specification.go +++ b/services/cloudbrain/resource/resource_specification.go @@ -23,6 +23,7 @@ func UpdateResourceSpecification(specId int64, req models.ResourceSpecificationR return nil } +//GetResourceSpecificationList returns specification and queue func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions) (*models.ResourceSpecAndQueueListRes, error) { n, r, err := models.SearchResourceSpecification(opts) if err != nil { @@ -32,8 +33,10 @@ func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions return models.NewResourceSpecAndQueueListRes(n, r), nil } -func ResourceSpecOnShelf(id int64) error { - _, err := models.ResourceSpecOnShelf(id) +func ResourceSpecOnShelf(id int64, req models.ResourceSpecificationReq) error { + _, err := models.ResourceSpecOnShelf(id, models.ResourceSpecification{ + UnitPrice: req.UnitPrice, + }) if err != nil { return err }