| @@ -1394,7 +1394,7 @@ type ResourceCostRecord { | |||||
| } | } | ||||
| type ResourceSpecReq { | type ResourceSpecReq { | ||||
| ClusterId string `form:"clusterId"` | |||||
| ClusterId string `form:"clusterId,optional"` | |||||
| Type string `form:"type,optional"` | Type string `form:"type,optional"` | ||||
| Name string `form:"name,optional"` | Name string `form:"name,optional"` | ||||
| Status string `form:"status,optional"` | Status string `form:"status,optional"` | ||||
| @@ -1446,4 +1446,24 @@ type BaseResourceSpec { | |||||
| UserId string `json:"userId" gorm:"column:user_id"` | UserId string `json:"userId" gorm:"column:user_id"` | ||||
| CreateTime string `json:"createTime" gorm:"column:create_time"` | CreateTime string `json:"createTime" gorm:"column:create_time"` | ||||
| UpdateTime string `json:"updateTime" gorm:"column:update_time"` | UpdateTime string `json:"updateTime" gorm:"column:update_time"` | ||||
| } | |||||
| type EditResourceReq { | |||||
| Id int64 `json:"id,string" gorm:"column:id;primaryKey;autoIncrement"` | |||||
| status string `json:"status" gorm:"column:status"` | |||||
| CostPerUnit string `json:"costPerUnit" gorm:"column:cost_per_unit"` | |||||
| CostType string `json:"costType" gorm:"column:cost_type"` //计费类型(hourly, daily, monthly,perUse) | |||||
| Type string `json:"type,optional" gorm:"column:type"` | |||||
| // 基础资源规格 | |||||
| StorageValue string `json:"storageValue,optional"` | |||||
| StorageUnit string `json:"storageUnit,optional"` | |||||
| CpuValue string `json:"cpuValue,optional"` | |||||
| CpuUnit string `json:"cpuUnit,optional"` | |||||
| MemoryValue string `json:"memoryValue,optional"` | |||||
| MemoryUnit string `json:"memoryUnit,optional"` | |||||
| } | |||||
| type SyncResourceReq { | |||||
| Id string `json:"id"` | |||||
| } | } | ||||
| @@ -179,9 +179,14 @@ service pcm { | |||||
| @handler getClusterByIdHandler | @handler getClusterByIdHandler | ||||
| get /core/getClusterById (getClusterByIdReq) returns (getClusterByIdResp) | get /core/getClusterById (getClusterByIdReq) returns (getClusterByIdResp) | ||||
| @doc "获取指定集群资源规格列表" | |||||
| @handler getClusterResourceSpecHandler | |||||
| get /core/ai/resourceSpec/list (ResourceSpecReq) returns (PageResult) | |||||
| //集群资源规格----- 开始 | |||||
| @doc "与Api接口对比集群资源规格" | |||||
| @handler compareResourceSpecHandler | |||||
| get /core/ai/resourceSpec/compare (ResourceSpecReq) returns (PageResult) | |||||
| @doc "同步指定资源规格" | |||||
| @handler syncResourceSpecHandler | |||||
| put /core/ai/resourceSpec/sync (SyncResourceReq) returns (CommonResp) | |||||
| @doc "获取指定资源规格详情" | @doc "获取指定资源规格详情" | ||||
| @handler detailResourceSpecHandler | @handler detailResourceSpecHandler | ||||
| @@ -193,11 +198,12 @@ service pcm { | |||||
| @doc "编辑资源规格" | @doc "编辑资源规格" | ||||
| @handler editResourceSpecHandler | @handler editResourceSpecHandler | ||||
| put /core/ai/resourceSpec/edit (ResourceSpec) returns (CommonResp) | |||||
| put /core/ai/resourceSpec/edit (EditResourceReq) returns (CommonResp) | |||||
| @doc "删除资源规格" | @doc "删除资源规格" | ||||
| @handler deleteResourceSpecHandler | @handler deleteResourceSpecHandler | ||||
| delete /core/ai/resourceSpec/delete/:id (DeletePathId) returns (CommonResp) | delete /core/ai/resourceSpec/delete/:id (DeletePathId) returns (CommonResp) | ||||
| //集群资源规格----- 结束 | |||||
| } | } | ||||
| //hpc二级接口 | //hpc二级接口 | ||||
| @@ -9,7 +9,7 @@ import ( | |||||
| "net/http" | "net/http" | ||||
| ) | ) | ||||
| func GetClusterResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | |||||
| func CompareResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | |||||
| return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
| var req types.ResourceSpecReq | var req types.ResourceSpecReq | ||||
| if err := httpx.Parse(r, &req); err != nil { | if err := httpx.Parse(r, &req); err != nil { | ||||
| @@ -17,8 +17,8 @@ func GetClusterResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc | |||||
| return | return | ||||
| } | } | ||||
| l := core.NewGetClusterResourceSpecLogic(r.Context(), svcCtx) | |||||
| resp, err := l.GetClusterResourceSpec(&req) | |||||
| l := core.NewCompareResourceSpecLogic(r.Context(), svcCtx) | |||||
| resp, err := l.CompareResourceSpec(&req) | |||||
| result.HttpResult(r, w, resp, err) | result.HttpResult(r, w, resp, err) | ||||
| } | } | ||||
| } | } | ||||
| @@ -11,7 +11,7 @@ import ( | |||||
| func EditResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | func EditResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | ||||
| return func(w http.ResponseWriter, r *http.Request) { | return func(w http.ResponseWriter, r *http.Request) { | ||||
| var req types.ResourceSpec | |||||
| var req types.EditResourceReq | |||||
| if err := httpx.Parse(r, &req); err != nil { | if err := httpx.Parse(r, &req); err != nil { | ||||
| result.ParamErrorResult(r, w, err) | result.ParamErrorResult(r, w, err) | ||||
| return | return | ||||
| @@ -0,0 +1,24 @@ | |||||
| package core | |||||
| import ( | |||||
| "github.com/zeromicro/go-zero/rest/httpx" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/logic/core" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/repository/result" | |||||
| "net/http" | |||||
| ) | |||||
| func SyncResourceSpecHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { | |||||
| return func(w http.ResponseWriter, r *http.Request) { | |||||
| var req types.SyncResourceReq | |||||
| if err := httpx.Parse(r, &req); err != nil { | |||||
| result.ParamErrorResult(r, w, err) | |||||
| return | |||||
| } | |||||
| l := core.NewSyncResourceSpecLogic(r.Context(), svcCtx) | |||||
| resp, err := l.SyncResourceSpec(&req) | |||||
| result.HttpResult(r, w, resp, err) | |||||
| } | |||||
| } | |||||
| @@ -370,6 +370,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { | |||||
| Path: "/core/ai/resourceSpec/cancelAlarm", | Path: "/core/ai/resourceSpec/cancelAlarm", | ||||
| Handler: core.CancelResourceSpecAlarmHandler(serverCtx), | Handler: core.CancelResourceSpecAlarmHandler(serverCtx), | ||||
| }, | }, | ||||
| { | |||||
| // 与Api接口对比集群资源规格 | |||||
| Method: http.MethodGet, | |||||
| Path: "/core/ai/resourceSpec/compare", | |||||
| Handler: core.CompareResourceSpecHandler(serverCtx), | |||||
| }, | |||||
| { | { | ||||
| // 删除资源规格 | // 删除资源规格 | ||||
| Method: http.MethodDelete, | Method: http.MethodDelete, | ||||
| @@ -389,10 +395,10 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { | |||||
| Handler: core.EditResourceSpecHandler(serverCtx), | Handler: core.EditResourceSpecHandler(serverCtx), | ||||
| }, | }, | ||||
| { | { | ||||
| // 获取指定集群资源规格列表 | |||||
| Method: http.MethodGet, | |||||
| Path: "/core/ai/resourceSpec/list", | |||||
| Handler: core.GetClusterResourceSpecHandler(serverCtx), | |||||
| // 同步指定资源规格 | |||||
| Method: http.MethodPut, | |||||
| Path: "/core/ai/resourceSpec/sync", | |||||
| Handler: core.SyncResourceSpecHandler(serverCtx), | |||||
| }, | }, | ||||
| { | { | ||||
| // 获取节点资产 | // 获取节点资产 | ||||
| @@ -2,31 +2,31 @@ package core | |||||
| import ( | import ( | ||||
| "context" | "context" | ||||
| "errors" | |||||
| "fmt" | "fmt" | ||||
| "github.com/mitchellh/mapstructure" | "github.com/mitchellh/mapstructure" | ||||
| "github.com/rs/zerolog/log" | "github.com/rs/zerolog/log" | ||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/logic/schedule" | "gitlink.org.cn/JointCloud/pcm-coordinator/internal/logic/schedule" | ||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | ||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils" | "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils" | ||||
| "gorm.io/gorm" | |||||
| "time" | "time" | ||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||||
| "github.com/zeromicro/go-zero/core/logx" | "github.com/zeromicro/go-zero/core/logx" | ||||
| "gorm.io/gorm" | |||||
| ) | ) | ||||
| type GetClusterResourceSpecLogic struct { | |||||
| type CompareResourceSpecLogic struct { | |||||
| logx.Logger | logx.Logger | ||||
| ctx context.Context | ctx context.Context | ||||
| svcCtx *svc.ServiceContext | svcCtx *svc.ServiceContext | ||||
| } | } | ||||
| const ( | const ( | ||||
| ChangeTypeCreated = 0 | |||||
| ChangeTypeModified = 1 | |||||
| ChangeTypeDeleted = 2 | |||||
| ChangeTypeNormal = 0 // 资源规格正常 | |||||
| ChangeTypeModified = 1 // 资源规格变更 | |||||
| ChangeTypeDeleted = 2 // 资源被删除 | |||||
| ) | ) | ||||
| type APIResponse struct { | type APIResponse struct { | ||||
| @@ -54,22 +54,23 @@ type Metric struct { | |||||
| Value float64 `json:"value"` | Value float64 `json:"value"` | ||||
| } | } | ||||
| func NewGetClusterResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetClusterResourceSpecLogic { | |||||
| return &GetClusterResourceSpecLogic{ | |||||
| func NewCompareResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CompareResourceSpecLogic { | |||||
| return &CompareResourceSpecLogic{ | |||||
| Logger: logx.WithContext(ctx), | Logger: logx.WithContext(ctx), | ||||
| ctx: ctx, | ctx: ctx, | ||||
| svcCtx: svcCtx, | svcCtx: svcCtx, | ||||
| } | } | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) GetClusterResourceSpec(req *types.ResourceSpecReq) (*types.PageResult, error) { | |||||
| func (l *CompareResourceSpecLogic) CompareResourceSpec(req *types.ResourceSpecReq) (resp *types.PageResult, err error) { | |||||
| log.Debug().Msgf("开始比较资源规格,ClusterId: %s, PageNum: %d, PageSize: %d", req.ClusterId, req.PageNum, req.PageSize) | |||||
| if req.ClusterId == "" { | if req.ClusterId == "" { | ||||
| return nil, errors.New("ClusterId is required") | |||||
| return resp, nil | |||||
| } | } | ||||
| // 获取集群资源数据 | // 获取集群资源数据 | ||||
| startTime := time.Now() | startTime := time.Now() | ||||
| apiResources, err := l.fetchClusterResources(req.ClusterId) | |||||
| apiResources, err := l.FetchClusterResources(req.ClusterId) | |||||
| log.Debug().Msgf("调用获取ai训练资源接口耗时: %v", time.Since(startTime)) | log.Debug().Msgf("调用获取ai训练资源接口耗时: %v", time.Since(startTime)) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("failed to fetch cluster resources: %w", err) | return nil, fmt.Errorf("failed to fetch cluster resources: %w", err) | ||||
| @@ -84,24 +85,31 @@ func (l *GetClusterResourceSpecLogic) GetClusterResourceSpec(req *types.Resource | |||||
| return l.queryDatabaseResults(req) | return l.queryDatabaseResults(req) | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) fetchClusterResources(ClusterId string) ([]APIResponse, error) { | |||||
| func (l *CompareResourceSpecLogic) FetchClusterResources(clusterId string) ([]APIResponse, error) { | |||||
| queryLogic := schedule.NewQueryResourcesLogic(l.ctx, l.svcCtx) | queryLogic := schedule.NewQueryResourcesLogic(l.ctx, l.svcCtx) | ||||
| resources, err := queryLogic.QueryResources(&types.QueryResourcesReq{ | |||||
| ClusterIDs: []string{ClusterId}, | |||||
| }) | |||||
| resources, err := queryLogic.QueryResources(&types.QueryResourcesReq{}) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("query resources failed: %w", err) | return nil, fmt.Errorf("query resources failed: %w", err) | ||||
| } | } | ||||
| var apiResponses []APIResponse | var apiResponses []APIResponse | ||||
| if err := l.decodeAPIResponse(resources.Data, &apiResponses); err != nil { | |||||
| if err := decodeAPIResponse(resources.Data, &apiResponses); err != nil { | |||||
| return nil, fmt.Errorf("decode response failed: %w", err) | return nil, fmt.Errorf("decode response failed: %w", err) | ||||
| } | } | ||||
| return apiResponses, nil | |||||
| // 过滤出指定集群的资源 | |||||
| var filteredResponses []APIResponse | |||||
| for _, response := range apiResponses { | |||||
| if response.ClusterId == clusterId && response.Resources != nil { | |||||
| filteredResponses = append(filteredResponses, response) | |||||
| } | |||||
| } | |||||
| if len(filteredResponses) == 0 { | |||||
| return nil, fmt.Errorf("no resources found for cluster ID: %s", clusterId) | |||||
| } | |||||
| return filteredResponses, nil | |||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) decodeAPIResponse(input interface{}, output *[]APIResponse) error { | |||||
| func decodeAPIResponse(input interface{}, output *[]APIResponse) error { | |||||
| config := &mapstructure.DecoderConfig{ | config := &mapstructure.DecoderConfig{ | ||||
| Result: output, | Result: output, | ||||
| TagName: "json", | TagName: "json", | ||||
| @@ -124,7 +132,7 @@ func (l *GetClusterResourceSpecLogic) decodeAPIResponse(input interface{}, outpu | |||||
| return nil | return nil | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) syncResourcesToDB(apiResponses []APIResponse) error { | |||||
| func (l *CompareResourceSpecLogic) syncResourcesToDB(apiResponses []APIResponse) error { | |||||
| for _, response := range apiResponses { | for _, response := range apiResponses { | ||||
| // 转换API响应到数据库模型 | // 转换API响应到数据库模型 | ||||
| dbSpecs, apiSpecs, err := l.processAPIResponse(response) | dbSpecs, apiSpecs, err := l.processAPIResponse(response) | ||||
| @@ -133,14 +141,14 @@ func (l *GetClusterResourceSpecLogic) syncResourcesToDB(apiResponses []APIRespon | |||||
| } | } | ||||
| // 处理资源变更 | // 处理资源变更 | ||||
| if err := l.handleResourceChanges(utils.StringToInt64(response.ClusterId), dbSpecs, apiSpecs); err != nil { | |||||
| if err := l.handleResourceChanges(dbSpecs, apiSpecs); err != nil { | |||||
| return fmt.Errorf("failed to handle resource changes: %w", err) | return fmt.Errorf("failed to handle resource changes: %w", err) | ||||
| } | } | ||||
| } | } | ||||
| return nil | return nil | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) processAPIResponse(response APIResponse) ([]models.TResourceSpec, []models.TResourceSpec, error) { | |||||
| func (l *CompareResourceSpecLogic) processAPIResponse(response APIResponse) ([]models.TResourceSpec, []models.TResourceSpec, error) { | |||||
| ClusterId := utils.StringToInt64(response.ClusterId) | ClusterId := utils.StringToInt64(response.ClusterId) | ||||
| var dbSpecs []models.TResourceSpec | var dbSpecs []models.TResourceSpec | ||||
| if err := l.svcCtx.DbEngin.Model(models.TResourceSpec{}).Preload("BaseResourceSpecs"). | if err := l.svcCtx.DbEngin.Model(models.TResourceSpec{}).Preload("BaseResourceSpecs"). | ||||
| @@ -151,6 +159,10 @@ func (l *GetClusterResourceSpecLogic) processAPIResponse(response APIResponse) ( | |||||
| var apiSpecs []models.TResourceSpec | var apiSpecs []models.TResourceSpec | ||||
| for _, res := range response.Resources { | for _, res := range response.Resources { | ||||
| // 检查资源类型和名称是否存在 | |||||
| if res.Resource.Name == "" || res.Resource.Type == "" { | |||||
| continue | |||||
| } | |||||
| spec := l.convertToResourceSpec(ClusterId, res) | spec := l.convertToResourceSpec(ClusterId, res) | ||||
| apiSpecs = append(apiSpecs, spec) | apiSpecs = append(apiSpecs, spec) | ||||
| } | } | ||||
| @@ -158,11 +170,10 @@ func (l *GetClusterResourceSpecLogic) processAPIResponse(response APIResponse) ( | |||||
| return dbSpecs, apiSpecs, nil | return dbSpecs, apiSpecs, nil | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) handleResourceChanges(ClusterId int64, dbSpecs, apiSpecs []models.TResourceSpec) error { | |||||
| // 创建资源映射 | |||||
| func (l *CompareResourceSpecLogic) handleResourceChanges(dbSpecs, apiSpecs []models.TResourceSpec) error { | |||||
| dbMap := make(map[string]models.TResourceSpec) | dbMap := make(map[string]models.TResourceSpec) | ||||
| for _, spec := range dbSpecs { | for _, spec := range dbSpecs { | ||||
| key := resourceKey(spec.Type, spec.Name) | |||||
| key := spec.SourceKey | |||||
| dbMap[key] = spec | dbMap[key] = spec | ||||
| } | } | ||||
| @@ -172,21 +183,93 @@ func (l *GetClusterResourceSpecLogic) handleResourceChanges(ClusterId int64, dbS | |||||
| apiMap[key] = spec | apiMap[key] = spec | ||||
| } | } | ||||
| // 处理新增或更新的资源 | |||||
| var createSpecs []*models.TResourceSpec | |||||
| var modifiedIDs []int64 | |||||
| var normalIDs []int64 | |||||
| // 第一阶段:收集需要处理的操作 | |||||
| for key, apiSpec := range apiMap { | for key, apiSpec := range apiMap { | ||||
| dbSpec, exists := dbMap[key] | dbSpec, exists := dbMap[key] | ||||
| if !exists { | if !exists { | ||||
| if err := l.createNewResource(&apiSpec); err != nil { | |||||
| return err | |||||
| } | |||||
| // 创建资源副本避免指针重复 | |||||
| newSpec := apiSpec | |||||
| // 初始化时间字段 | |||||
| newSpec.CreateTime = time.Now() | |||||
| newSpec.UpdateTime = time.Now() | |||||
| createSpecs = append(createSpecs, &newSpec) | |||||
| continue | continue | ||||
| } | } | ||||
| // 检查资源规格变更 | |||||
| if l.isSpecChanged(dbSpec, apiSpec) { | if l.isSpecChanged(dbSpec, apiSpec) { | ||||
| if err := l.updateResource(&dbSpec, apiSpec); err != nil { | |||||
| return err | |||||
| modifiedIDs = append(modifiedIDs, dbSpec.Id) | |||||
| } else { | |||||
| normalIDs = append(normalIDs, dbSpec.Id) | |||||
| } | |||||
| } | |||||
| // 第二阶段:批量处理数据库操作 | |||||
| // 批量创建新资源及关联资源 | |||||
| if len(createSpecs) > 0 { | |||||
| tx := l.svcCtx.DbEngin.Begin() | |||||
| if tx.Error != nil { | |||||
| return fmt.Errorf("failed to start transaction: %w", tx.Error) | |||||
| } | |||||
| // 批量插入主资源 | |||||
| if err := tx.CreateInBatches(createSpecs, 100).Error; err != nil { | |||||
| tx.Rollback() | |||||
| return fmt.Errorf("failed to batch create resources: %w", err) | |||||
| } | |||||
| // 准备关联资源数据 | |||||
| var baseResources []models.TBaseResourceSpec | |||||
| for _, spec := range createSpecs { | |||||
| for i := range spec.BaseResourceSpecs { | |||||
| br := &spec.BaseResourceSpecs[i] | |||||
| br.ResourceSpecId = spec.Id | |||||
| br.CreateTime = time.Now() | |||||
| br.UpdateTime = time.Now() | |||||
| baseResources = append(baseResources, *br) | |||||
| } | |||||
| } | |||||
| // 批量插入关联资源 | |||||
| if len(baseResources) > 0 { | |||||
| if err := tx.CreateInBatches(baseResources, 100).Error; err != nil { | |||||
| tx.Rollback() | |||||
| return fmt.Errorf("failed to batch create base resources: %w", err) | |||||
| } | } | ||||
| } | } | ||||
| if err := tx.Commit().Error; err != nil { | |||||
| return fmt.Errorf("transaction commit failed: %w", err) | |||||
| } | |||||
| } | |||||
| // 批量更新变更资源 | |||||
| now := time.Now() | |||||
| if len(modifiedIDs) > 0 { | |||||
| if err := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | |||||
| Where("id IN ?", modifiedIDs). | |||||
| Updates(map[string]interface{}{ | |||||
| "change_type": ChangeTypeModified, | |||||
| "update_time": now, | |||||
| }).Error; err != nil { | |||||
| return fmt.Errorf("batch update modified failed: %w", err) | |||||
| } | |||||
| } | |||||
| // 批量更新正常资源 | |||||
| if len(normalIDs) > 0 { | |||||
| if err := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | |||||
| Where("id IN ?", normalIDs). | |||||
| Updates(map[string]interface{}{ | |||||
| "change_type": ChangeTypeNormal, | |||||
| "update_time": now, | |||||
| }).Error; err != nil { | |||||
| return fmt.Errorf("batch update normal failed: %w", err) | |||||
| } | |||||
| } | } | ||||
| // 处理删除的资源 | // 处理删除的资源 | ||||
| @@ -205,101 +288,40 @@ func resourceKey(resType, name string) string { | |||||
| return fmt.Sprintf("%s::%s", resType, name) | return fmt.Sprintf("%s::%s", resType, name) | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) createNewResource(spec *models.TResourceSpec) error { | |||||
| func (l *CompareResourceSpecLogic) createNewResource(spec *models.TResourceSpec) error { | |||||
| return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | ||||
| if err := tx.Create(spec).Error; err != nil { | if err := tx.Create(spec).Error; err != nil { | ||||
| return fmt.Errorf("failed to create resource: %w", err) | return fmt.Errorf("failed to create resource: %w", err) | ||||
| } | } | ||||
| for i := range spec.BaseResourceSpecs { | |||||
| spec.BaseResourceSpecs[i].ResourceSpecId = spec.Id | |||||
| spec.BaseResourceSpecs[i].Id = 0 | |||||
| } | |||||
| if len(spec.BaseResourceSpecs) > 0 { | |||||
| if err := tx.CreateInBatches(spec.BaseResourceSpecs, 100).Error; err != nil { | |||||
| return fmt.Errorf("failed to create base resources: %w", err) | |||||
| } | |||||
| } | |||||
| return nil | return nil | ||||
| }) | }) | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) updateResource(existing *models.TResourceSpec, newSpec models.TResourceSpec) error { | |||||
| // 标识资源规格变更 | |||||
| func (l *CompareResourceSpecLogic) updateResource(existing *models.TResourceSpec, newSpec models.TResourceSpec, changeType int) error { | |||||
| return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | ||||
| updates := map[string]interface{}{ | updates := map[string]interface{}{ | ||||
| "total_count": newSpec.TotalCount, | |||||
| "available_count": newSpec.AvailableCount, | |||||
| "change_type": ChangeTypeModified, | |||||
| "update_time": time.Now(), | |||||
| "change_type": changeType, | |||||
| "update_time": time.Now(), | |||||
| } | } | ||||
| if err := tx.Model(existing).Updates(updates).Error; err != nil { | if err := tx.Model(existing).Updates(updates).Error; err != nil { | ||||
| return fmt.Errorf("failed to update resource: %w", err) | return fmt.Errorf("failed to update resource: %w", err) | ||||
| } | } | ||||
| return l.syncBaseResources(tx, existing.Id, newSpec.BaseResourceSpecs) | |||||
| return nil | |||||
| }) | }) | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) syncBaseResources(tx *gorm.DB, specID int64, newResources []models.TBaseResourceSpec) error { | |||||
| // 处理基础资源更新 | |||||
| var existingResources []models.TBaseResourceSpec | |||||
| if err := tx.Where("resource_spec_id = ?", specID).Find(&existingResources).Error; err != nil { | |||||
| return fmt.Errorf("failed to query base resources: %w", err) | |||||
| } | |||||
| existingMap := make(map[string]models.TBaseResourceSpec) | |||||
| for _, r := range existingResources { | |||||
| key := resourceKey(r.Type, r.Name) | |||||
| existingMap[key] = r | |||||
| } | |||||
| // 处理更新和新增 | |||||
| for i, newRes := range newResources { | |||||
| newRes.ResourceSpecId = specID | |||||
| key := resourceKey(newRes.Type, newRes.Name) | |||||
| if existing, exists := existingMap[key]; exists { | |||||
| newRes.Id = existing.Id | |||||
| newRes.CreateTime = existing.CreateTime | |||||
| if err := tx.Save(&newRes).Error; err != nil { | |||||
| return fmt.Errorf("failed to update base resource: %w", err) | |||||
| } | |||||
| } else { | |||||
| if err := tx.Create(&newRes).Error; err != nil { | |||||
| return fmt.Errorf("failed to create base resource: %w", err) | |||||
| } | |||||
| } | |||||
| newResources[i] = newRes | |||||
| } | |||||
| // 处理删除 | |||||
| currentIDs := make(map[int64]struct{}) | |||||
| for _, r := range newResources { | |||||
| currentIDs[r.Id] = struct{}{} | |||||
| } | |||||
| for _, existing := range existingResources { | |||||
| if _, exists := currentIDs[existing.Id]; !exists { | |||||
| if err := tx.Delete(&existing).Error; err != nil { | |||||
| return fmt.Errorf("failed to delete base resource: %w", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (l *GetClusterResourceSpecLogic) markResourceDeleted(id int64) error { | |||||
| func (l *CompareResourceSpecLogic) markResourceDeleted(id int64) error { | |||||
| return l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | return l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | ||||
| Where("id = ?", id). | Where("id = ?", id). | ||||
| Update("change_type", ChangeTypeDeleted). | Update("change_type", ChangeTypeDeleted). | ||||
| Error | Error | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) isSpecChanged(old, new models.TResourceSpec) bool { | |||||
| func (l *CompareResourceSpecLogic) isSpecChanged(old, new models.TResourceSpec) bool { | |||||
| if old.TotalCount != new.TotalCount || | if old.TotalCount != new.TotalCount || | ||||
| old.AvailableCount != new.AvailableCount || | old.AvailableCount != new.AvailableCount || | ||||
| old.Region != new.Region { | old.Region != new.Region { | ||||
| @@ -326,8 +348,9 @@ func (l *GetClusterResourceSpecLogic) isSpecChanged(old, new models.TResourceSpe | |||||
| return len(oldBaseMap) > 0 | return len(oldBaseMap) > 0 | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) convertToResourceSpec(ClusterId int64, res Resource) models.TResourceSpec { | |||||
| func (l *CompareResourceSpecLogic) convertToResourceSpec(ClusterId int64, res Resource) models.TResourceSpec { | |||||
| spec := models.TResourceSpec{ | spec := models.TResourceSpec{ | ||||
| SourceKey: resourceKey(res.Resource.Type, res.Resource.Name), | |||||
| Type: res.Resource.Type, | Type: res.Resource.Type, | ||||
| Name: res.Resource.Name, | Name: res.Resource.Name, | ||||
| TotalCount: int64(res.Resource.Total.Value), | TotalCount: int64(res.Resource.Total.Value), | ||||
| @@ -335,7 +358,7 @@ func (l *GetClusterResourceSpecLogic) convertToResourceSpec(ClusterId int64, res | |||||
| ClusterId: ClusterId, | ClusterId: ClusterId, | ||||
| CreateTime: time.Now(), | CreateTime: time.Now(), | ||||
| UpdateTime: time.Now(), | UpdateTime: time.Now(), | ||||
| ChangeType: ChangeTypeCreated, | |||||
| ChangeType: ChangeTypeNormal, | |||||
| } | } | ||||
| for _, br := range res.BaseResources { | for _, br := range res.BaseResources { | ||||
| @@ -354,7 +377,7 @@ func (l *GetClusterResourceSpecLogic) convertToResourceSpec(ClusterId int64, res | |||||
| return spec | return spec | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) queryDatabaseResults(req *types.ResourceSpecReq) (*types.PageResult, error) { | |||||
| func (l *CompareResourceSpecLogic) queryDatabaseResults(req *types.ResourceSpecReq) (*types.PageResult, error) { | |||||
| result := &types.PageResult{ | result := &types.PageResult{ | ||||
| PageNum: req.PageNum, | PageNum: req.PageNum, | ||||
| PageSize: req.PageSize, | PageSize: req.PageSize, | ||||
| @@ -376,7 +399,7 @@ func (l *GetClusterResourceSpecLogic) queryDatabaseResults(req *types.ResourceSp | |||||
| return result, nil | return result, nil | ||||
| } | } | ||||
| func (l *GetClusterResourceSpecLogic) buildBaseQuery(req *types.ResourceSpecReq) *gorm.DB { | |||||
| func (l *CompareResourceSpecLogic) buildBaseQuery(req *types.ResourceSpecReq) *gorm.DB { | |||||
| query := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | query := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}). | ||||
| Where("cluster_id = ?", utils.StringToInt64(req.ClusterId)). | Where("cluster_id = ?", utils.StringToInt64(req.ClusterId)). | ||||
| Where("deleted_at IS NULL") | Where("deleted_at IS NULL") | ||||
| @@ -2,7 +2,10 @@ package core | |||||
| import ( | import ( | ||||
| "context" | "context" | ||||
| "github.com/pkg/errors" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | ||||
| "gorm.io/gorm" | |||||
| "time" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | ||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | ||||
| @@ -25,11 +28,49 @@ func NewDeleteResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) | |||||
| } | } | ||||
| func (l *DeleteResourceSpecLogic) DeleteResourceSpec(req *types.DeletePathId) (resp *types.CommonResp, err error) { | func (l *DeleteResourceSpecLogic) DeleteResourceSpec(req *types.DeletePathId) (resp *types.CommonResp, err error) { | ||||
| db := l.svcCtx.DbEngin.Model(&models.TResourceSpec{}).Table("t_resource_spec") | |||||
| err = db.Delete(&models.TResourceSpec{}, req.Id).Error | |||||
| if err != nil { | |||||
| return nil, err | |||||
| // 初始化事务 | |||||
| tx := l.svcCtx.DbEngin.Begin() | |||||
| defer func() { | |||||
| if r := recover(); r != nil { | |||||
| tx.Rollback() | |||||
| panic(r) | |||||
| } | |||||
| }() | |||||
| // 1. 检查主资源规格是否存在 | |||||
| var mainSpec models.TResourceSpec | |||||
| if err := tx.Where("id = ? AND deleted_at IS NULL", req.Id). | |||||
| First(&mainSpec). | |||||
| Error; err != nil { | |||||
| tx.Rollback() | |||||
| if errors.Is(err, gorm.ErrRecordNotFound) { | |||||
| return nil, errors.Errorf("资源规格不存在 (ID: %s)", req.Id) | |||||
| } | |||||
| return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %s)", req.Id) | |||||
| } | |||||
| // 2. 删除主资源规格(软删除) | |||||
| if err := tx.Model(&models.TResourceSpec{}). | |||||
| Where("id = ?", req.Id). | |||||
| Update("deleted_at", time.Now()). | |||||
| Error; err != nil { | |||||
| tx.Rollback() | |||||
| return nil, errors.Wrapf(err, "删除主资源规格失败 (ID: %s)", req.Id) | |||||
| } | |||||
| // 3. 删除关联的基础资源规格(软删除) | |||||
| if err := tx.Model(&models.TBaseResourceSpec{}). | |||||
| Where("resource_spec_id = ?", req.Id). | |||||
| Update("deleted_at", time.Now()). | |||||
| Error; err != nil { | |||||
| tx.Rollback() | |||||
| return nil, errors.Wrapf(err, "删除基础资源规格失败 (ID: %s)", req.Id) | |||||
| } | |||||
| // 提交事务 | |||||
| if err := tx.Commit().Error; err != nil { | |||||
| return nil, errors.Wrap(err, "提交事务失败") | |||||
| } | } | ||||
| return | |||||
| return resp, nil | |||||
| } | } | ||||
| @@ -3,12 +3,12 @@ package core | |||||
| import ( | import ( | ||||
| "context" | "context" | ||||
| "github.com/pkg/errors" | "github.com/pkg/errors" | ||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||||
| "github.com/zeromicro/go-zero/core/logx" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | ||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | ||||
| "github.com/zeromicro/go-zero/core/logx" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/utils" | |||||
| "gorm.io/gorm" | |||||
| ) | ) | ||||
| type EditResourceSpecLogic struct { | type EditResourceSpecLogic struct { | ||||
| @@ -24,31 +24,140 @@ func NewEditResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) * | |||||
| svcCtx: svcCtx, | svcCtx: svcCtx, | ||||
| } | } | ||||
| } | } | ||||
| func (l *EditResourceSpecLogic) EditResourceSpec(req *types.EditResourceReq) (resp *types.CommonResp, err error) { | |||||
| // 初始化事务 | |||||
| tx := l.svcCtx.DbEngin.Begin() | |||||
| defer func() { | |||||
| if r := recover(); r != nil { | |||||
| tx.Rollback() | |||||
| panic(r) | |||||
| } else if err != nil { | |||||
| tx.Rollback() | |||||
| } | |||||
| }() | |||||
| func (l *EditResourceSpecLogic) EditResourceSpec(req *types.ResourceSpec) (resp *types.CommonResp, err error) { | |||||
| //检查是否存在 | |||||
| var count int64 | |||||
| db := l.svcCtx.DbEngin.Model(&types.ResourceSpec{}).Table("t_resource_spec") | |||||
| err = db.Where("id = ? and deleted_at is null", req.Id).Count(&count).Error | |||||
| if err != nil { | |||||
| // 1. 验证资源规格存在 | |||||
| var existing models.TResourceSpec | |||||
| if err = tx.Model(&models.TResourceSpec{}). | |||||
| Where("id = ? AND deleted_at IS NULL", req.Id). | |||||
| First(&existing). | |||||
| Error; err != nil { | |||||
| if errors.Is(err, gorm.ErrRecordNotFound) { | |||||
| return nil, errors.Errorf("资源规格不存在 (ID: %d)", req.Id) | |||||
| } | |||||
| return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %d)", req.Id) | |||||
| } | |||||
| // 2. 参数校验 | |||||
| if err = validateRequestParams(req); err != nil { | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| if count == 0 { | |||||
| return nil, errors.New("资源规格不存在") | |||||
| // 3. 转换参数 | |||||
| statusInt := utils.StringToInt64(req.Status) | |||||
| costPerUnit := utils.StringToFloat64(req.CostPerUnit) | |||||
| // 4. 更新主资源规格 | |||||
| if err = updateMainResourceSpec(tx, req.Id, statusInt, req.CostType, costPerUnit); err != nil { | |||||
| return nil, err | |||||
| } | } | ||||
| resp = &types.CommonResp{} | |||||
| db = l.svcCtx.DbEngin.Model(&types.ResourceSpec{}).Table("t_resource_spec") | |||||
| err = db.Where("id = ?", req.Id).Save(req).Error | |||||
| if err != nil { | |||||
| // 5. 更新子资源规格 | |||||
| if err = updateSubResources(tx, req); err != nil { | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| baseDb := l.svcCtx.DbEngin.Model(models.TBaseResourceSpec{}).Table("t_base_resource_spec") | |||||
| for _, spec := range req.BaseResourceSpecs { | |||||
| err = baseDb.Where("id = ?", spec.Id).Updates(&spec).Error | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| // 提交事务 | |||||
| if err = tx.Commit().Error; err != nil { | |||||
| return nil, errors.Wrap(err, "提交事务失败") | |||||
| } | } | ||||
| // 返回成功响应 | |||||
| return resp, nil | return resp, nil | ||||
| } | } | ||||
| // validateRequestParams 验证请求参数合法性 | |||||
| func validateRequestParams(req *types.EditResourceReq) error { | |||||
| // 状态校验 | |||||
| if req.Status != "0" && req.Status != "1" { | |||||
| return errors.Errorf("资源规格状态不合法 (ID: %d)", req.Id) | |||||
| } | |||||
| // 计费类型校验 | |||||
| validCostTypes := map[string]struct{}{ | |||||
| "hourly": {}, | |||||
| "daily": {}, | |||||
| "monthly": {}, | |||||
| "perUse": {}, | |||||
| } | |||||
| if _, ok := validCostTypes[req.CostType]; !ok { | |||||
| return errors.Errorf("资源规格计费类型不合法 (ID: %d)", req.Id) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // updateMainResourceSpec 更新主资源规格 | |||||
| func updateMainResourceSpec(tx *gorm.DB, id int64, status int64, costType string, costPerUnit float64) error { | |||||
| return tx.Model(&models.TResourceSpec{}). | |||||
| Where("id = ?", id). | |||||
| Updates(map[string]interface{}{ | |||||
| "status": status, | |||||
| "cost_type": costType, | |||||
| "cost_per_unit": costPerUnit, | |||||
| }). | |||||
| Error | |||||
| } | |||||
| // updateSubResources 更新子资源规格 | |||||
| func updateSubResources(tx *gorm.DB, req *types.EditResourceReq) error { | |||||
| // 定义更新操作集合 | |||||
| updateOperations := []struct { | |||||
| Value string | |||||
| Unit string | |||||
| SpecType string | |||||
| SpecName string | |||||
| }{ | |||||
| {req.CpuValue, req.CpuUnit, "CPU", ""}, | |||||
| {req.MemoryValue, req.MemoryUnit, "MEMORY", "RAM"}, | |||||
| {req.StorageValue, req.StorageUnit, "STORAGE", ""}, | |||||
| } | |||||
| // 批量执行更新操作 | |||||
| for _, op := range updateOperations { | |||||
| if op.Value == "" && op.Unit == "" { | |||||
| continue | |||||
| } | |||||
| if err := updateBaseResourceSpec(tx, req.Id, op.SpecType, op.SpecName, op.Value, op.Unit); err != nil { | |||||
| return errors.Wrapf(err, "更新%s规格失败 (ID: %d)", op.SpecType, req.Id) | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // updateBaseResourceSpec 通用基础资源规格更新函数 | |||||
| func updateBaseResourceSpec(tx *gorm.DB, specID int64, specType string, specName string, value, unit string) error { | |||||
| updates := make(map[string]interface{}) | |||||
| if value != "" { | |||||
| updates["total_value"] = value | |||||
| } | |||||
| if unit != "" { | |||||
| updates["total_unit"] = unit | |||||
| } | |||||
| if len(updates) == 0 { | |||||
| return nil | |||||
| } | |||||
| query := tx.Model(&models.TBaseResourceSpec{}). | |||||
| Where("resource_spec_id = ? AND type = ?", specID, specType) | |||||
| if specName != "" { | |||||
| query = query.Where("name = ?", specName) | |||||
| } | |||||
| if err := query.Updates(updates).Error; err != nil { | |||||
| return errors.Wrapf(err, "更新%s规格失败", specType) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -0,0 +1,135 @@ | |||||
| package core | |||||
| import ( | |||||
| "context" | |||||
| "fmt" | |||||
| "github.com/pkg/errors" | |||||
| "github.com/rs/zerolog/log" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/pkg/models" | |||||
| "gorm.io/gorm" | |||||
| "strconv" | |||||
| "time" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/svc" | |||||
| "gitlink.org.cn/JointCloud/pcm-coordinator/internal/types" | |||||
| "github.com/zeromicro/go-zero/core/logx" | |||||
| ) | |||||
| type SyncResourceSpecLogic struct { | |||||
| logx.Logger | |||||
| ctx context.Context | |||||
| svcCtx *svc.ServiceContext | |||||
| } | |||||
| func NewSyncResourceSpecLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SyncResourceSpecLogic { | |||||
| return &SyncResourceSpecLogic{ | |||||
| Logger: logx.WithContext(ctx), | |||||
| ctx: ctx, | |||||
| svcCtx: svcCtx, | |||||
| } | |||||
| } | |||||
| func (l *SyncResourceSpecLogic) SyncResourceSpec(req *types.SyncResourceReq) (resp *types.CommonResp, err error) { | |||||
| var mainSpec models.TResourceSpec | |||||
| if err := l.svcCtx.DbEngin.Where("id = ? AND deleted_at IS NULL", req.Id). | |||||
| First(&mainSpec). | |||||
| Error; err != nil { | |||||
| if errors.Is(err, gorm.ErrRecordNotFound) { | |||||
| return nil, errors.Errorf("资源规格不存在 (ID: %s)", req.Id) | |||||
| } | |||||
| return nil, errors.Wrapf(err, "查询资源规格失败 (ID: %s)", req.Id) | |||||
| } | |||||
| // 获取集群资源数据 | |||||
| startTime := time.Now() | |||||
| compareLogic := NewCompareResourceSpecLogic(l.ctx, l.svcCtx) | |||||
| apiResources, err := compareLogic.FetchClusterResources(strconv.FormatInt(mainSpec.ClusterId, 10)) | |||||
| log.Debug().Msgf("调用获取ai训练资源接口耗时: %v", time.Since(startTime)) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("failed to fetch cluster resources: %w", err) | |||||
| } | |||||
| for _, response := range apiResources { | |||||
| // 转换API响应到数据库模型 | |||||
| _, apiSpecs, err := compareLogic.processAPIResponse(response) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| // 同步资源到数据库 | |||||
| for _, spec := range apiSpecs { | |||||
| if spec.SourceKey == mainSpec.SourceKey { | |||||
| err := l.updateResource(&mainSpec, spec) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil, nil | |||||
| } | |||||
| func (l *SyncResourceSpecLogic) updateResource(existing *models.TResourceSpec, newSpec models.TResourceSpec) error { | |||||
| return l.svcCtx.DbEngin.Transaction(func(tx *gorm.DB) error { | |||||
| updates := map[string]interface{}{ | |||||
| "type": newSpec.Type, | |||||
| "total_count": newSpec.TotalCount, | |||||
| "available_count": newSpec.AvailableCount, | |||||
| "change_type": ChangeTypeNormal, | |||||
| "update_time": time.Now(), | |||||
| } | |||||
| if err := tx.Model(existing).Updates(updates).Error; err != nil { | |||||
| return fmt.Errorf("failed to update resource: %w", err) | |||||
| } | |||||
| return l.syncBaseResources(tx, existing.Id, newSpec.BaseResourceSpecs) | |||||
| }) | |||||
| } | |||||
| func (l *SyncResourceSpecLogic) syncBaseResources(tx *gorm.DB, specID int64, newResources []models.TBaseResourceSpec) error { | |||||
| // 处理基础资源更新 | |||||
| var existingResources []models.TBaseResourceSpec | |||||
| if err := tx.Where("resource_spec_id = ?", specID).Find(&existingResources).Error; err != nil { | |||||
| return fmt.Errorf("failed to query base resources: %w", err) | |||||
| } | |||||
| existingMap := make(map[string]models.TBaseResourceSpec) | |||||
| for _, r := range existingResources { | |||||
| key := resourceKey(r.Type, r.Name) | |||||
| existingMap[key] = r | |||||
| } | |||||
| // 处理更新和新增 | |||||
| for i, newRes := range newResources { | |||||
| newRes.ResourceSpecId = specID | |||||
| key := resourceKey(newRes.Type, newRes.Name) | |||||
| if existing, exists := existingMap[key]; exists { | |||||
| newRes.Id = existing.Id | |||||
| newRes.CreateTime = existing.CreateTime | |||||
| if err := tx.Save(&newRes).Error; err != nil { | |||||
| return fmt.Errorf("failed to update base resource: %w", err) | |||||
| } | |||||
| } else { | |||||
| if err := tx.Create(&newRes).Error; err != nil { | |||||
| return fmt.Errorf("failed to create base resource: %w", err) | |||||
| } | |||||
| } | |||||
| newResources[i] = newRes | |||||
| } | |||||
| // 处理删除 | |||||
| currentIDs := make(map[int64]struct{}) | |||||
| for _, r := range newResources { | |||||
| currentIDs[r.Id] = struct{}{} | |||||
| } | |||||
| for _, existing := range existingResources { | |||||
| if _, exists := currentIDs[existing.Id]; !exists { | |||||
| if err := tx.Delete(&existing).Error; err != nil { | |||||
| return fmt.Errorf("failed to delete base resource: %w", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -52,6 +52,7 @@ func (l *CommitHpcTaskLogic) CommitHpcTask(req *types.CommitHpcTaskReq) (resp *t | |||||
| // 构建主任务结构体 | // 构建主任务结构体 | ||||
| userId, _ := strconv.ParseInt(req.Parameters["UserId"], 10, 64) | userId, _ := strconv.ParseInt(req.Parameters["UserId"], 10, 64) | ||||
| taskModel := models.Task{ | taskModel := models.Task{ | ||||
| Id: utils.GenSnowflakeID(), | |||||
| Name: req.Name, | Name: req.Name, | ||||
| Description: req.Description, | Description: req.Description, | ||||
| CommitTime: time.Now(), | CommitTime: time.Now(), | ||||
| @@ -76,6 +77,7 @@ func (l *CommitHpcTaskLogic) CommitHpcTask(req *types.CommitHpcTaskReq) (resp *t | |||||
| cardCount, _ := strconv.ParseInt(req.Parameters["cardCount"], 10, 64) | cardCount, _ := strconv.ParseInt(req.Parameters["cardCount"], 10, 64) | ||||
| timelimit, _ := strconv.ParseInt(req.Parameters["timeLimit"], 10, 64) | timelimit, _ := strconv.ParseInt(req.Parameters["timeLimit"], 10, 64) | ||||
| hpcInfo := models.TaskHpc{ | hpcInfo := models.TaskHpc{ | ||||
| Id: utils.GenSnowflakeID(), | |||||
| TaskId: taskModel.Id, | TaskId: taskModel.Id, | ||||
| AdapterId: clusterInfo.AdapterId, | AdapterId: clusterInfo.AdapterId, | ||||
| AdapterName: adapterInfo.Name, | AdapterName: adapterInfo.Name, | ||||
| @@ -51,7 +51,7 @@ func StatusReport(url string, report interface{}) error { | |||||
| Post(url) | Post(url) | ||||
| bodyStr, _ := jsoniter.MarshalToString(report) | bodyStr, _ := jsoniter.MarshalToString(report) | ||||
| log.Debug().Msgf("任务状态上报到中间件请求参数:[%v], 返回值: [%v]", bodyStr, string(rp.Body())) | |||||
| log.Debug().Msgf("上报任务状态到中间件,请求url:%s, 请求参数:%s, 响应结果:%s", url, bodyStr, string(rp.Body())) | |||||
| if err != nil { | if err != nil { | ||||
| logx.Errorf("############ Report Status Message Error %s", err.Error()) | logx.Errorf("############ Report Status Message Error %s", err.Error()) | ||||
| @@ -41,8 +41,16 @@ func UpdateHpcTaskStatus(svc *svc.ServiceContext) { | |||||
| svc.Scheduler.HpcService.TaskSyncLock.Lock() | svc.Scheduler.HpcService.TaskSyncLock.Lock() | ||||
| defer svc.Scheduler.HpcService.TaskSyncLock.Unlock() | defer svc.Scheduler.HpcService.TaskSyncLock.Unlock() | ||||
| taskList := make([]*models.TaskHpc, 0) | taskList := make([]*models.TaskHpc, 0) | ||||
| //TODO 暂时忽略任务状态 | |||||
| sqlStr := `select * from task_hpc where job_id!='' order by created_time desc limit 10` | |||||
| sqlStr := `SELECT * | |||||
| FROM task_hpc | |||||
| WHERE | |||||
| job_id != '' | |||||
| AND ( | |||||
| status NOT IN ('Failed', 'Completed', 'Cancelled') | |||||
| OR start_time < created_time | |||||
| ) | |||||
| ORDER BY created_time DESC | |||||
| LIMIT 10` | |||||
| db := svc.DbEngin.Raw(sqlStr).Scan(&taskList) | db := svc.DbEngin.Raw(sqlStr).Scan(&taskList) | ||||
| if db.Error != nil { | if db.Error != nil { | ||||
| logx.Errorf(db.Error.Error()) | logx.Errorf(db.Error.Error()) | ||||
| @@ -2125,6 +2125,20 @@ type Driver_info struct { | |||||
| Ipmi_username string `json:"ipmi_username" copier:"ipmi_username"` | Ipmi_username string `json:"ipmi_username" copier:"ipmi_username"` | ||||
| } | } | ||||
| type EditResourceReq struct { | |||||
| Id int64 `json:"id,string" gorm:"column:id;primaryKey;autoIncrement"` | |||||
| Status string `json:"status" gorm:"column:status"` | |||||
| CostPerUnit string `json:"costPerUnit" gorm:"column:cost_per_unit"` | |||||
| CostType string `json:"costType" gorm:"column:cost_type"` //计费类型(hourly, daily, monthly,perUse) | |||||
| Type string `json:"type,optional" gorm:"column:type"` | |||||
| StorageValue string `json:"storageValue,optional"` | |||||
| StorageUnit string `json:"storageUnit,optional"` | |||||
| CpuValue string `json:"cpuValue,optional"` | |||||
| CpuUnit string `json:"cpuUnit,optional"` | |||||
| MemoryValue string `json:"memoryValue,optional"` | |||||
| MemoryUnit string `json:"memoryUnit,optional"` | |||||
| } | |||||
| type EndpointsReq struct { | type EndpointsReq struct { | ||||
| AllowedAccessIps []string `json:"allowedAccessIps" copier:"AllowedAccessIps"` | AllowedAccessIps []string `json:"allowedAccessIps" copier:"AllowedAccessIps"` | ||||
| DevService string `json:"devService" copier:"DevService"` | DevService string `json:"devService" copier:"DevService"` | ||||
| @@ -4591,7 +4605,7 @@ type ResourceSpec struct { | |||||
| } | } | ||||
| type ResourceSpecReq struct { | type ResourceSpecReq struct { | ||||
| ClusterId string `form:"clusterId"` | |||||
| ClusterId string `form:"clusterId,optional"` | |||||
| Type string `form:"type,optional"` | Type string `form:"type,optional"` | ||||
| Name string `form:"name,optional"` | Name string `form:"name,optional"` | ||||
| Status string `form:"status,optional"` | Status string `form:"status,optional"` | ||||
| @@ -5498,6 +5512,10 @@ type SyncClusterAlertReq struct { | |||||
| AlertRecordsMap map[string]interface{} `json:"alertRecordsMap"` | AlertRecordsMap map[string]interface{} `json:"alertRecordsMap"` | ||||
| } | } | ||||
| type SyncResourceReq struct { | |||||
| Id string `json:"id"` | |||||
| } | |||||
| type Tags struct { | type Tags struct { | ||||
| } | } | ||||
| @@ -38,18 +38,18 @@ type ( | |||||
| } | } | ||||
| TBaseResourceSpec struct { | TBaseResourceSpec struct { | ||||
| Id int64 `db:"id" json:"id,omitempty"` // 主键id | |||||
| ResourceSpecId int64 `db:"resource_spec_id" json:"resourceSpecId,omitempty"` // 关联资源规格ID | |||||
| Type string `db:"type" json:"type,omitempty"` // 类型名称 | |||||
| Name string `db:"name" json:"name,omitempty"` // 名称(如显存类型) | |||||
| TotalValue float64 `db:"total_value" json:"totalValue,omitempty"` // 总量值 | |||||
| TotalUnit string `db:"total_unit" json:"totalUnit,omitempty"` // 总量值单位(GB/core等) | |||||
| AvailableValue float64 `db:"available_value" json:"availableValue,omitempty"` // 可用值 | |||||
| AvailableUnit string `db:"available_unit" json:"availableUnit,omitempty"` // 可用值单位(GB/core等) | |||||
| UserId int64 `db:"user_id" json:"userId"` // 用户ID | |||||
| CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间 | |||||
| UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间 | |||||
| DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间 | |||||
| Id int64 `db:"id" json:"id"` // 主键id | |||||
| ResourceSpecId int64 `db:"resource_spec_id" json:"resourceSpecId"` // 关联资源规格ID | |||||
| Type string `db:"type" json:"type"` // 类型名称 | |||||
| Name string `db:"name" json:"name"` // 名称(如显存类型) | |||||
| TotalValue float64 `db:"total_value" json:"totalValue"` // 总量值 | |||||
| TotalUnit string `db:"total_unit" json:"totalUnit"` // 总量值单位(GB/core等) | |||||
| AvailableValue float64 `db:"available_value" json:"availableValue"` // 可用值 | |||||
| AvailableUnit string `db:"available_unit" json:"availableUnit"` // 可用值单位(GB/core等) | |||||
| UserId int64 `db:"user_id" json:"userId"` // 用户ID | |||||
| CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间 | |||||
| UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间 | |||||
| DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间 | |||||
| } | } | ||||
| ) | ) | ||||
| @@ -38,21 +38,22 @@ type ( | |||||
| } | } | ||||
| TResourceSpec struct { | TResourceSpec struct { | ||||
| Id int64 `db:"id" json:"id,omitempty"` // 主键id | |||||
| Type string `db:"type" json:"type,omitempty"` // 类型名称 | |||||
| Name string `db:"name" json:"name,omitempty"` // 规格名称 | |||||
| TotalCount int64 `db:"total_count" json:"totalCount,omitempty"` // 资源总量 | |||||
| AvailableCount int64 `db:"available_count" json:"availableCount,omitempty"` // 可用数量 | |||||
| ChangeType int64 `db:"change_type" json:"changeType,omitempty"` // 变更类型(0: 正常,1:变更,2:删除) | |||||
| Status int64 `db:"status" json:"status,omitempty"` // 状态(0:未上架,1:已上架) | |||||
| Region string `db:"region" json:"region,omitempty"` // 所属区域(可扩展多区域) | |||||
| ClusterId int64 `db:"cluster_id" json:"clusterId,string,omitempty"` // 集群ID | |||||
| CostPerUnit float64 `db:"cost_per_unit" json:"costPerUnit,omitempty"` // 单位时间积分消耗 | |||||
| CostType string `db:"cost_type" json:"costType,omitempty"` // 计费类型(hourly, daily, monthly,perUse) | |||||
| UserId int64 `db:"user_id" json:"userId"` // 用户ID | |||||
| CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间 | |||||
| UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间 | |||||
| DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间 | |||||
| Id int64 `db:"id" json:"id"` // 主键id | |||||
| SourceKey string `db:"source_key" json:"source_key"` // 数据源标识(type-name) | |||||
| Type string `db:"type" json:"type"` // 类型名称 | |||||
| Name string `db:"name" json:"name"` // 规格名称 | |||||
| TotalCount int64 `db:"total_count" json:"totalCount"` // 资源总量 | |||||
| AvailableCount int64 `db:"available_count" json:"availableCount"` // 可用数量 | |||||
| ChangeType int64 `db:"change_type" json:"changeType"` // 变更类型(0: 正常,1:变更,2:删除) | |||||
| Status int64 `db:"status" json:"status"` // 状态(0:未上架,1:已上架) | |||||
| Region string `db:"region" json:"region"` // 所属区域(可扩展多区域) | |||||
| ClusterId int64 `db:"cluster_id" json:"clusterId,string"` // 集群ID | |||||
| CostPerUnit float64 `db:"cost_per_unit" json:"costPerUnit"` // 单位时间积分消耗 | |||||
| CostType string `db:"cost_type" json:"costType"` // 计费类型(hourly, daily, monthly,perUse) | |||||
| UserId int64 `db:"user_id" json:"userId"` // 用户ID | |||||
| CreateTime time.Time `db:"create_time" json:"createTime"` // 创建时间 | |||||
| UpdateTime time.Time `db:"update_time" json:"updateTime"` // 更新时间 | |||||
| DeletedAt gorm.DeletedAt `db:"deleted_at" json:"-"` // 删除时间 | |||||
| BaseResourceSpecs []TBaseResourceSpec `gorm:"foreignKey:ResourceSpecId" json:"baseResourceSpecs,omitempty"` | BaseResourceSpecs []TBaseResourceSpec `gorm:"foreignKey:ResourceSpecId" json:"baseResourceSpecs,omitempty"` | ||||
| } | } | ||||