| @@ -33,27 +33,27 @@ const ( | |||||
| // Attachment represent a attachment of issue/comment/release. | // Attachment represent a attachment of issue/comment/release. | ||||
| type Attachment struct { | type Attachment struct { | ||||
| ID int64 `xorm:"pk autoincr"` | |||||
| UUID string `xorm:"uuid UNIQUE"` | |||||
| IssueID int64 `xorm:"INDEX"` | |||||
| DatasetID int64 `xorm:"INDEX DEFAULT 0"` | |||||
| ReleaseID int64 `xorm:"INDEX"` | |||||
| UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added | |||||
| CommentID int64 | |||||
| Name string | |||||
| Description string `xorm:"TEXT"` | |||||
| DownloadCount int64 `xorm:"DEFAULT 0"` | |||||
| UseNumber int64 `xorm:"DEFAULT 0"` | |||||
| Size int64 `xorm:"DEFAULT 0"` | |||||
| IsPrivate bool `xorm:"DEFAULT false"` | |||||
| DecompressState int32 `xorm:"DEFAULT 0"` | |||||
| Type int `xorm:"DEFAULT 0"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
| FileChunk *FileChunk `xorm:"-"` | |||||
| CanDel bool `xorm:"-"` | |||||
| Uploader *User `xorm:"-"` | |||||
| Md5 string `xorm:"-"` | |||||
| ID int64 `xorm:"pk autoincr" json:"id"` | |||||
| UUID string `xorm:"uuid UNIQUE" json:"uuid"` | |||||
| IssueID int64 `xorm:"INDEX" json:"issueId"` | |||||
| DatasetID int64 `xorm:"INDEX DEFAULT 0" json:"datasetId"` | |||||
| ReleaseID int64 `xorm:"INDEX" json:"releaseId"` | |||||
| UploaderID int64 `xorm:"INDEX DEFAULT 0" json:"uploaderId"` // Notice: will be zero before this column added | |||||
| CommentID int64 `json:"commentId"` | |||||
| Name string `json:"name"` | |||||
| Description string `xorm:"TEXT" json:"description"` | |||||
| DownloadCount int64 `xorm:"DEFAULT 0" json:"downloadCount"` | |||||
| UseNumber int64 `xorm:"DEFAULT 0" json:"useNumber"` | |||||
| Size int64 `xorm:"DEFAULT 0" json:"size"` | |||||
| IsPrivate bool `xorm:"DEFAULT false" json:"isPrivate"` | |||||
| DecompressState int32 `xorm:"DEFAULT 0" json:"decompressState"` | |||||
| Type int `xorm:"DEFAULT 0" json:"type"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"created" json:"createdUnix"` | |||||
| FileChunk *FileChunk `xorm:"-" json:"fileChunk"` | |||||
| CanDel bool `xorm:"-" json:"canDel"` | |||||
| Uploader *User `xorm:"-" json:"uploader"` | |||||
| Md5 string `xorm:"-" json:"md5"` | |||||
| } | } | ||||
| type AttachmentUsername struct { | type AttachmentUsername struct { | ||||
| @@ -61,30 +61,6 @@ type AttachmentUsername struct { | |||||
| Name string | Name string | ||||
| } | } | ||||
| type AttachmentInfo struct { | |||||
| Attachment `xorm:"extends"` | |||||
| Repo *Repository `xorm:"extends"` | |||||
| RelAvatarLink string `xorm:"extends"` | |||||
| UserName string `xorm:"extends"` | |||||
| Recommend bool `xorm:"-"` | |||||
| } | |||||
| type AttachmentsOptions struct { | |||||
| ListOptions | |||||
| DatasetIDs []int64 | |||||
| DecompressState int | |||||
| Type int | |||||
| UploaderID int64 | |||||
| NeedDatasetIDs bool | |||||
| NeedIsPrivate bool | |||||
| IsPrivate bool | |||||
| JustNeedZipFile bool | |||||
| NeedRepoInfo bool | |||||
| Keyword string | |||||
| RecommendOnly bool | |||||
| UserId int64 | |||||
| } | |||||
| func (a *Attachment) AfterUpdate() { | func (a *Attachment) AfterUpdate() { | ||||
| if a.DatasetID > 0 { | if a.DatasetID > 0 { | ||||
| datasetIsPublicCount, err := x.Where("dataset_id = ? AND is_private = ?", a.DatasetID, false).Count(new(Attachment)) | datasetIsPublicCount, err := x.Where("dataset_id = ? AND is_private = ?", a.DatasetID, false).Count(new(Attachment)) | ||||
| @@ -493,19 +469,6 @@ func getPrivateAttachments(e Engine, userID int64) ([]*AttachmentUsername, error | |||||
| return attachments, nil | return attachments, nil | ||||
| } | } | ||||
| func getAllUserAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) { | |||||
| attachments := make([]*AttachmentUsername, 0, 10) | |||||
| if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+ | |||||
| "= `user`.id").Where("decompress_state= ? and attachment.type = ? and (uploader_id= ? or is_private = ?)", DecompressStateDone, TypeCloudBrainOne, userID, false).Find(&attachments); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return attachments, nil | |||||
| } | |||||
| func GetAllUserAttachments(userID int64) ([]*AttachmentUsername, error) { | |||||
| return getAllUserAttachments(x, userID) | |||||
| } | |||||
| func getModelArtsUserAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) { | func getModelArtsUserAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) { | ||||
| attachments := make([]*AttachmentUsername, 0, 10) | attachments := make([]*AttachmentUsername, 0, 10) | ||||
| if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+ | if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+ | ||||
| @@ -601,107 +564,6 @@ func GetAllAttachmentSize() (int64, error) { | |||||
| return x.SumInt(&Attachment{}, "size") | return x.SumInt(&Attachment{}, "size") | ||||
| } | } | ||||
| func Attachments(opts *AttachmentsOptions) ([]*AttachmentInfo, int64, error) { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| var cond = builder.NewCond() | |||||
| if opts.NeedDatasetIDs { | |||||
| cond = cond.And( | |||||
| builder.In("attachment.dataset_id", opts.DatasetIDs), | |||||
| ) | |||||
| } | |||||
| if opts.UploaderID > 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"attachment.uploader_id": opts.UploaderID}, | |||||
| ) | |||||
| } | |||||
| if (opts.Type) >= 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"attachment.type": opts.Type}, | |||||
| ) | |||||
| } | |||||
| if opts.NeedIsPrivate { | |||||
| cond = cond.And( | |||||
| builder.Eq{"attachment.is_private": opts.IsPrivate}, | |||||
| ) | |||||
| } | |||||
| if opts.RecommendOnly { | |||||
| cond = cond.And(builder.In("attachment.id", builder.Select("attachment.id"). | |||||
| From("attachment"). | |||||
| Join("INNER", "dataset", "attachment.dataset_id = dataset.id and dataset.recommend=true"))) | |||||
| } | |||||
| if opts.JustNeedZipFile { | |||||
| var DecompressState []int32 | |||||
| DecompressState = append(DecompressState, DecompressStateDone, DecompressStateIng, DecompressStateFailed) | |||||
| cond = cond.And( | |||||
| builder.In("attachment.decompress_state", DecompressState), | |||||
| ) | |||||
| } | |||||
| var count int64 | |||||
| var err error | |||||
| if len(opts.Keyword) == 0 { | |||||
| count, err = sess.Where(cond).Count(new(Attachment)) | |||||
| } else { | |||||
| lowerKeyWord := strings.ToLower(opts.Keyword) | |||||
| cond = cond.And(builder.Or(builder.Like{"LOWER(attachment.name)", lowerKeyWord}, builder.Like{"LOWER(attachment.description)", lowerKeyWord})) | |||||
| count, err = sess.Table(&Attachment{}).Where(cond).Count(new(AttachmentInfo)) | |||||
| } | |||||
| if err != nil { | |||||
| return nil, 0, fmt.Errorf("Count: %v", err) | |||||
| } | |||||
| if opts.Page >= 0 && opts.PageSize > 0 { | |||||
| var start int | |||||
| if opts.Page == 0 { | |||||
| start = 0 | |||||
| } else { | |||||
| start = (opts.Page - 1) * opts.PageSize | |||||
| } | |||||
| sess.Limit(opts.PageSize, start) | |||||
| } | |||||
| sess.OrderBy("attachment.created_unix DESC") | |||||
| attachments := make([]*AttachmentInfo, 0, setting.UI.DatasetPagingNum) | |||||
| if err := sess.Table(&Attachment{}).Where(cond). | |||||
| Find(&attachments); err != nil { | |||||
| return nil, 0, fmt.Errorf("Find: %v", err) | |||||
| } | |||||
| if opts.NeedRepoInfo { | |||||
| for _, attachment := range attachments { | |||||
| dataset, err := GetDatasetByID(attachment.DatasetID) | |||||
| if err != nil { | |||||
| return nil, 0, fmt.Errorf("GetDatasetByID failed error: %v", err) | |||||
| } | |||||
| attachment.Recommend = dataset.Recommend | |||||
| repo, err := GetRepositoryByID(dataset.RepoID) | |||||
| if err == nil { | |||||
| attachment.Repo = repo | |||||
| } else { | |||||
| return nil, 0, fmt.Errorf("GetRepositoryByID failed error: %v", err) | |||||
| } | |||||
| user, err := GetUserByID(attachment.UploaderID) | |||||
| if err == nil { | |||||
| attachment.RelAvatarLink = user.RelAvatarLink() | |||||
| attachment.UserName = user.Name | |||||
| } else { | |||||
| return nil, 0, fmt.Errorf("GetUserByID failed error: %v", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| return attachments, count, nil | |||||
| } | |||||
| func GetAllDatasetContributorByDatasetId(datasetId int64) ([]*User, error) { | func GetAllDatasetContributorByDatasetId(datasetId int64) ([]*User, error) { | ||||
| r := make([]*User, 0) | r := make([]*User, 0) | ||||
| if err := x.Select("distinct(public.user.*)").Table("attachment").Join("LEFT", "user", "public.user.ID = attachment.uploader_id").Where("attachment.dataset_id = ?", datasetId).Find(&r); err != nil { | if err := x.Select("distinct(public.user.*)").Table("attachment").Join("LEFT", "user", "public.user.ID = attachment.uploader_id").Where("attachment.dataset_id = ?", datasetId).Find(&r); err != nil { | ||||
| @@ -1,8 +1,8 @@ | |||||
| package models | package models | ||||
| type BaseMessage struct { | type BaseMessage struct { | ||||
| Code int | |||||
| Message string | |||||
| Code int `json:"code"` | |||||
| Message string `json:"message"` | |||||
| } | } | ||||
| var BaseOKMessage = BaseMessage{ | var BaseOKMessage = BaseMessage{ | ||||
| @@ -21,26 +21,26 @@ const ( | |||||
| ) | ) | ||||
| type Dataset struct { | type Dataset struct { | ||||
| ID int64 `xorm:"pk autoincr"` | |||||
| Title string `xorm:"INDEX NOT NULL"` | |||||
| Status int32 `xorm:"INDEX"` // normal_private: 0, pulbic: 1, is_delete: 2 | |||||
| Category string | |||||
| Description string `xorm:"TEXT"` | |||||
| DownloadTimes int64 | |||||
| UseCount int64 `xorm:"DEFAULT 0"` | |||||
| NumStars int `xorm:"INDEX NOT NULL DEFAULT 0"` | |||||
| Recommend bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||||
| License string | |||||
| Task string | |||||
| ReleaseID int64 `xorm:"INDEX"` | |||||
| UserID int64 `xorm:"INDEX"` | |||||
| RepoID int64 `xorm:"INDEX"` | |||||
| Repo *Repository `xorm:"-"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| User *User `xorm:"-"` | |||||
| Attachments []*Attachment `xorm:"-"` | |||||
| ID int64 `xorm:"pk autoincr" json:"id"` | |||||
| Title string `xorm:"INDEX NOT NULL" json:"title"` | |||||
| Status int32 `xorm:"INDEX" json:"status"` // normal_private: 0, pulbic: 1, is_delete: 2 | |||||
| Category string `json:"category"` | |||||
| Description string `xorm:"TEXT" json:"description"` | |||||
| DownloadTimes int64 `json:"downloadTimes"` | |||||
| UseCount int64 `xorm:"DEFAULT 0" json:"useCount"` | |||||
| NumStars int `xorm:"INDEX NOT NULL DEFAULT 0" json:"numStars"` | |||||
| Recommend bool `xorm:"INDEX NOT NULL DEFAULT false" json:"recommend"` | |||||
| License string `json:"license"` | |||||
| Task string `json:"task"` | |||||
| ReleaseID int64 `xorm:"INDEX" json:"releaseId"` | |||||
| UserID int64 `xorm:"INDEX" json:"userId"` | |||||
| RepoID int64 `xorm:"INDEX" json:"repoId"` | |||||
| Repo *Repository `xorm:"-" json:"repo"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"` | |||||
| User *User `xorm:"-" json:"user"` | |||||
| Attachments []*Attachment `xorm:"-" json:"attachments"` | |||||
| } | } | ||||
| type DatasetWithStar struct { | type DatasetWithStar struct { | ||||
| @@ -162,79 +162,79 @@ const ( | |||||
| // Repository represents a git repository. | // Repository represents a git repository. | ||||
| type Repository struct { | type Repository struct { | ||||
| ID int64 `xorm:"pk autoincr"` | |||||
| OwnerID int64 `xorm:"UNIQUE(s) index"` | |||||
| OwnerName string | |||||
| Owner *User `xorm:"-"` | |||||
| LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` | |||||
| Name string `xorm:"INDEX NOT NULL"` | |||||
| Description string `xorm:"TEXT"` | |||||
| Website string `xorm:"VARCHAR(2048)"` | |||||
| OriginalServiceType api.GitServiceType `xorm:"index"` | |||||
| OriginalURL string `xorm:"VARCHAR(2048)"` | |||||
| DefaultBranch string | |||||
| CreatorID int64 `xorm:"INDEX NOT NULL DEFAULT 0"` | |||||
| Creator *User `xorm:"-"` | |||||
| NumWatches int | |||||
| NumStars int | |||||
| NumForks int | |||||
| NumIssues int | |||||
| NumClosedIssues int | |||||
| NumOpenIssues int `xorm:"-"` | |||||
| NumPulls int | |||||
| NumClosedPulls int | |||||
| NumOpenPulls int `xorm:"-"` | |||||
| NumMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumOpenMilestones int `xorm:"-"` | |||||
| NumCommit int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| RepoType RepoType `xorm:"NOT NULL DEFAULT 0"` | |||||
| IsPrivate bool `xorm:"INDEX"` | |||||
| IsEmpty bool `xorm:"INDEX"` | |||||
| IsArchived bool `xorm:"INDEX"` | |||||
| IsMirror bool `xorm:"INDEX"` | |||||
| ID int64 `xorm:"pk autoincr" json:"id"` | |||||
| OwnerID int64 `xorm:"UNIQUE(s) index" json:"ownerId"` | |||||
| OwnerName string `json:"ownerName"` | |||||
| Owner *User `xorm:"-" json:"owner"` | |||||
| LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL" json:"lowerName"` | |||||
| Name string `xorm:"INDEX NOT NULL" json:"name"` | |||||
| Description string `xorm:"TEXT" json:"description"` | |||||
| Website string `xorm:"VARCHAR(2048)" json:"website"` | |||||
| OriginalServiceType api.GitServiceType `xorm:"index" json:"originalServiceType"` | |||||
| OriginalURL string `xorm:"VARCHAR(2048)" json:"originalUrl"` | |||||
| DefaultBranch string `json:"defaultBranch"` | |||||
| CreatorID int64 `xorm:"INDEX NOT NULL DEFAULT 0" json:"creatorId"` | |||||
| Creator *User `xorm:"-" json:"creator"` | |||||
| NumWatches int `json:"numWatches"` | |||||
| NumStars int `json:"numStars"` | |||||
| NumForks int `json:"numForks"` | |||||
| NumIssues int `json:"numIssues"` | |||||
| NumClosedIssues int `json:"numClosedIssues"` | |||||
| NumOpenIssues int `xorm:"-" json:"numOpenIssues"` | |||||
| NumPulls int `json:"numPulls"` | |||||
| NumClosedPulls int `json:"numClosedPulls"` | |||||
| NumOpenPulls int `xorm:"-" json:"numOpenPulls"` | |||||
| NumMilestones int `xorm:"NOT NULL DEFAULT 0" json:"numMilestones"` | |||||
| NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0" json:"numClosedMilestones"` | |||||
| NumOpenMilestones int `xorm:"-" json:"numOpenMilestones"` | |||||
| NumCommit int64 `xorm:"NOT NULL DEFAULT 0" json:"numCommit"` | |||||
| RepoType RepoType `xorm:"NOT NULL DEFAULT 0" json:"repoType"` | |||||
| IsPrivate bool `xorm:"INDEX" json:"isPrivate"` | |||||
| IsEmpty bool `xorm:"INDEX" json:"isEmpty"` | |||||
| IsArchived bool `xorm:"INDEX" json:"isArchived"` | |||||
| IsMirror bool `xorm:"INDEX" json:"isMirror"` | |||||
| *Mirror `xorm:"-"` | *Mirror `xorm:"-"` | ||||
| Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0"` | |||||
| Status RepositoryStatus `xorm:"NOT NULL DEFAULT 0" json:"status"` | |||||
| RenderingMetas map[string]string `xorm:"-"` | RenderingMetas map[string]string `xorm:"-"` | ||||
| Units []*RepoUnit `xorm:"-"` | |||||
| PrimaryLanguage *LanguageStat `xorm:"-"` | |||||
| IsFork bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||||
| ForkID int64 `xorm:"INDEX"` | |||||
| BaseRepo *Repository `xorm:"-"` | |||||
| IsTemplate bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||||
| TemplateID int64 `xorm:"INDEX"` | |||||
| TemplateRepo *Repository `xorm:"-"` | |||||
| Size int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| CodeIndexerStatus *RepoIndexerStatus `xorm:"-"` | |||||
| StatsIndexerStatus *RepoIndexerStatus `xorm:"-"` | |||||
| IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` | |||||
| CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` | |||||
| Topics []string `xorm:"TEXT JSON"` | |||||
| Units []*RepoUnit `xorm:"-" json:"units"` | |||||
| PrimaryLanguage *LanguageStat `xorm:"-" json:"primaryLanguage"` | |||||
| IsFork bool `xorm:"INDEX NOT NULL DEFAULT false" json:"isFork"` | |||||
| ForkID int64 `xorm:"INDEX" json:"forkId"` | |||||
| BaseRepo *Repository `xorm:"-" json:"baseRepo"` | |||||
| IsTemplate bool `xorm:"INDEX NOT NULL DEFAULT false" json:"isTemplate"` | |||||
| TemplateID int64 `xorm:"INDEX" json:"templateId"` | |||||
| TemplateRepo *Repository `xorm:"-" json:"templateRepo"` | |||||
| Size int64 `xorm:"NOT NULL DEFAULT 0" json:"size"` | |||||
| CodeIndexerStatus *RepoIndexerStatus `xorm:"-" json:"codeIndexerStatus"` | |||||
| StatsIndexerStatus *RepoIndexerStatus `xorm:"-" json:"statsIndexerStatus"` | |||||
| IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true" json:"isFsckEnabled"` | |||||
| CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false" json:"closeIssuesViaCommitInAnyBranch"` | |||||
| Topics []string `xorm:"TEXT JSON" json:"topics"` | |||||
| // Avatar: ID(10-20)-md5(32) - must fit into 64 symbols | // Avatar: ID(10-20)-md5(32) - must fit into 64 symbols | ||||
| Avatar string `xorm:"VARCHAR(64)"` | |||||
| Avatar string `xorm:"VARCHAR(64)" json:"avatar"` | |||||
| //blockchain | //blockchain | ||||
| ContractAddress string `xorm:"INDEX"` | |||||
| Balance string `xorm:"NOT NULL DEFAULT '0'"` | |||||
| BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0"` | |||||
| ContractAddress string `xorm:"INDEX" json:"contractAddress"` | |||||
| Balance string `xorm:"NOT NULL DEFAULT '0'" json:"balance"` | |||||
| BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0" json:"blockChainStatus"` | |||||
| // git clone and git pull total count | // git clone and git pull total count | ||||
| CloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| CloneCnt int64 `xorm:"NOT NULL DEFAULT 0" json:"clone_cnt" json:"cloneCnt"` | |||||
| // only git clone total count | // only git clone total count | ||||
| GitCloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| GitCloneCnt int64 `xorm:"NOT NULL DEFAULT 0" json:"git_clone_cnt" json:"gitCloneCnt"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"` | |||||
| Hot int64 `xorm:"-"` | |||||
| Active int64 `xorm:"-"` | |||||
| Alias string `xorm:"INDEX"` | |||||
| LowerAlias string `xorm:"INDEX"` | |||||
| Hot int64 `xorm:"-" json:"hot"` | |||||
| Active int64 `xorm:"-" json:"active"` | |||||
| Alias string `xorm:"INDEX" json:"alias"` | |||||
| LowerAlias string `xorm:"INDEX" json:"lowerAlias"` | |||||
| } | } | ||||
| type RepositoryShow struct { | type RepositoryShow struct { | ||||
| @@ -57,29 +57,26 @@ type CreateModelArtsTrainJobForm struct { | |||||
| } | } | ||||
| type CreateModelArtsInferenceJobForm struct { | type CreateModelArtsInferenceJobForm struct { | ||||
| DisplayJobName string `form:"display_job_name" binding:"Required"` | |||||
| JobName string `form:"job_name" binding:"Required"` | |||||
| Attachment string `form:"attachment" binding:"Required"` | |||||
| BootFile string `form:"boot_file" binding:"Required"` | |||||
| WorkServerNumber int `form:"work_server_number" binding:"Required"` | |||||
| EngineID int `form:"engine_id" binding:"Required"` | |||||
| PoolID string `form:"pool_id" binding:"Required"` | |||||
| Flavor string `form:"flavor" binding:"Required"` | |||||
| Params string `form:"run_para_list" binding:"Required"` | |||||
| Description string `form:"description"` | |||||
| IsSaveParam string `form:"is_save_para"` | |||||
| ParameterTemplateName string `form:"parameter_template_name"` | |||||
| PrameterDescription string `form:"parameter_description"` | |||||
| BranchName string `form:"branch_name" binding:"Required"` | |||||
| VersionName string `form:"version_name" binding:"Required"` | |||||
| FlavorName string `form:"flaver_names" binding:"Required"` | |||||
| EngineName string `form:"engine_names" binding:"Required"` | |||||
| LabelName string `form:"label_names" binding:"Required"` | |||||
| TrainUrl string `form:"train_url" binding:"Required"` | |||||
| ModelName string `form:"model_name" binding:"Required"` | |||||
| ModelVersion string `form:"model_version" binding:"Required"` | |||||
| CkptName string `form:"ckpt_name" binding:"Required"` | |||||
| SpecId int64 `form:"spec_id" binding:"Required"` | |||||
| DisplayJobName string `form:"display_job_name" binding:"Required"` | |||||
| JobName string `form:"job_name" binding:"Required"` | |||||
| Attachment string `form:"attachment" binding:"Required"` | |||||
| BootFile string `form:"boot_file" binding:"Required"` | |||||
| WorkServerNumber int `form:"work_server_number" binding:"Required"` | |||||
| EngineID int `form:"engine_id" binding:"Required"` | |||||
| PoolID string `form:"pool_id" binding:"Required"` | |||||
| Flavor string `form:"flavor" binding:"Required"` | |||||
| Params string `form:"run_para_list" binding:"Required"` | |||||
| Description string `form:"description"` | |||||
| BranchName string `form:"branch_name" binding:"Required"` | |||||
| VersionName string `form:"version_name" binding:"Required"` | |||||
| FlavorName string `form:"flaver_names" binding:"Required"` | |||||
| EngineName string `form:"engine_names" binding:"Required"` | |||||
| LabelName string `form:"label_names" binding:"Required"` | |||||
| TrainUrl string `form:"train_url" binding:"Required"` | |||||
| ModelName string `form:"model_name" binding:"Required"` | |||||
| ModelVersion string `form:"model_version" binding:"Required"` | |||||
| CkptName string `form:"ckpt_name" binding:"Required"` | |||||
| SpecId int64 `form:"spec_id" binding:"Required"` | |||||
| } | } | ||||
| func (f *CreateModelArtsTrainJobForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | func (f *CreateModelArtsTrainJobForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | ||||
| @@ -228,7 +228,7 @@ func AdminOrImageCreaterRight(ctx *context.Context) { | |||||
| } | } | ||||
| func GenerateTask(req GenerateCloudBrainTaskReq) error { | |||||
| func GenerateTask(req GenerateCloudBrainTaskReq) (string, error) { | |||||
| var versionCount int | var versionCount int | ||||
| if req.JobType == string(models.JobTypeTrain) { | if req.JobType == string(models.JobTypeTrain) { | ||||
| versionCount = 1 | versionCount = 1 | ||||
| @@ -335,11 +335,11 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("CreateJob failed:", err.Error(), req.Ctx.Data["MsgID"]) | log.Error("CreateJob failed:", err.Error(), req.Ctx.Data["MsgID"]) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| if jobResult.Code != Success { | if jobResult.Code != Success { | ||||
| log.Error("CreateJob(%s) failed:%s", req.JobName, jobResult.Msg, req.Ctx.Data["MsgID"]) | log.Error("CreateJob(%s) failed:%s", req.JobName, jobResult.Msg, req.Ctx.Data["MsgID"]) | ||||
| return errors.New(jobResult.Msg) | |||||
| return "", errors.New(jobResult.Msg) | |||||
| } | } | ||||
| var jobID = jobResult.Payload["jobId"].(string) | var jobID = jobResult.Payload["jobId"].(string) | ||||
| @@ -380,13 +380,13 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| task, err := models.GetCloudbrainByJobID(jobID) | task, err := models.GetCloudbrainByJobID(jobID) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainByJobID failed: %v", err.Error()) | log.Error("GetCloudbrainByJobID failed: %v", err.Error()) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| stringId := strconv.FormatInt(task.ID, 10) | stringId := strconv.FormatInt(task.ID, 10) | ||||
| @@ -401,7 +401,7 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { | |||||
| notification.NotifyOtherTask(req.Ctx.User, req.Ctx.Repo.Repository, stringId, req.DisplayJobName, models.ActionCreateDebugGPUTask) | notification.NotifyOtherTask(req.Ctx.User, req.Ctx.Repo.Repository, stringId, req.DisplayJobName, models.ActionCreateDebugGPUTask) | ||||
| } | } | ||||
| return nil | |||||
| return jobID, nil | |||||
| } | } | ||||
| func IsBenchmarkJob(jobType string) bool { | func IsBenchmarkJob(jobType string) bool { | ||||
| @@ -98,7 +98,7 @@ func getDatasetGrampus(datasetInfos map[string]models.DatasetInfo) []models.Gram | |||||
| return datasetGrampus | return datasetGrampus | ||||
| } | } | ||||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | |||||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId string, err error) { | |||||
| createTime := timeutil.TimeStampNow() | createTime := timeutil.TimeStampNow() | ||||
| centerID, centerName := getCentersParamter(ctx, req) | centerID, centerName := getCentersParamter(ctx, req) | ||||
| @@ -146,7 +146,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("createJob failed: %v", err.Error()) | log.Error("createJob failed: %v", err.Error()) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| jobID := jobResult.JobInfo.JobID | jobID := jobResult.JobInfo.JobID | ||||
| @@ -187,7 +187,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("CreateCloudbrain(%s) failed:%v", req.DisplayJobName, err.Error()) | log.Error("CreateCloudbrain(%s) failed:%v", req.DisplayJobName, err.Error()) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| var actionType models.ActionType | var actionType models.ActionType | ||||
| @@ -198,7 +198,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error | |||||
| } | } | ||||
| notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, req.DisplayJobName, actionType) | notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, req.DisplayJobName, actionType) | ||||
| return nil | |||||
| return jobID, nil | |||||
| } | } | ||||
| func getCentersParamter(ctx *context.Context, req *GenerateTrainJobReq) ([]string, []string) { | func getCentersParamter(ctx *context.Context, req *GenerateTrainJobReq) ([]string, []string) { | ||||
| @@ -680,7 +680,7 @@ func GetOutputPathByCount(TotalVersionCount int) (VersionOutputPath string) { | |||||
| return VersionOutputPath | return VersionOutputPath | ||||
| } | } | ||||
| func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (err error) { | |||||
| func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (jobId string, err error) { | |||||
| createTime := timeutil.TimeStampNow() | createTime := timeutil.TimeStampNow() | ||||
| jobResult, err := createInferenceJob(models.CreateInferenceJobParams{ | jobResult, err := createInferenceJob(models.CreateInferenceJobParams{ | ||||
| JobName: req.JobName, | JobName: req.JobName, | ||||
| @@ -715,10 +715,10 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("InsertCloudbrainTemp failed: %v", err.Error()) | log.Error("InsertCloudbrainTemp failed: %v", err.Error()) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| } | } | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| // attach, err := models.GetAttachmentByUUID(req.Uuid) | // attach, err := models.GetAttachmentByUUID(req.Uuid) | ||||
| @@ -769,7 +769,7 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error()) | log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error()) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| if req.JobType == string(models.JobTypeModelSafety) { | if req.JobType == string(models.JobTypeModelSafety) { | ||||
| task, err := models.GetCloudbrainByJobID(jobID) | task, err := models.GetCloudbrainByJobID(jobID) | ||||
| @@ -780,7 +780,7 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e | |||||
| notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, req.DisplayJobName, models.ActionCreateInferenceTask) | notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, req.DisplayJobName, models.ActionCreateInferenceTask) | ||||
| } | } | ||||
| return nil | |||||
| return jobID, nil | |||||
| } | } | ||||
| func GetNotebookImageName(imageId string) (string, error) { | func GetNotebookImageName(imageId string) (string, error) { | ||||
| @@ -517,6 +517,15 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown) | m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown) | ||||
| m.Post("/markdown/raw", misc.MarkdownRaw) | m.Post("/markdown/raw", misc.MarkdownRaw) | ||||
| m.Group("/images", func() { | |||||
| m.Get("public", repo.GetPublicImages) | |||||
| m.Get("custom", repo.GetCustomImages) | |||||
| m.Get("star", repo.GetStarImages) | |||||
| m.Get("npu", repo.GetNpuImages) | |||||
| }, reqToken()) | |||||
| // Notifications | // Notifications | ||||
| m.Group("/notifications", func() { | m.Group("/notifications", func() { | ||||
| m.Combo(""). | m.Combo(""). | ||||
| @@ -696,6 +705,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID) | m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID) | ||||
| m.Group("/:username/:reponame/datasets", func() { | |||||
| m.Get("/current_repo", repo.CurrentRepoDatasetMultiple) | |||||
| m.Get("/my_datasets", repo.MyDatasetsMultiple) | |||||
| m.Get("/public_datasets", repo.PublicDatasetMultiple) | |||||
| m.Get("/my_favorite", repo.MyFavoriteDatasetMultiple) | |||||
| }, reqToken()) | |||||
| m.Group("/repos", func() { | m.Group("/repos", func() { | ||||
| m.Get("/search", repo.Search) | m.Get("/search", repo.Search) | ||||
| @@ -955,6 +971,17 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/:id/modelartlog", repo.TrainJobForModelConvertGetLog) | m.Get("/:id/modelartlog", repo.TrainJobForModelConvertGetLog) | ||||
| m.Get("/:id/model_list", repo.CloudBrainModelConvertList) | m.Get("/:id/model_list", repo.CloudBrainModelConvertList) | ||||
| }, reqRepoReader(models.UnitTypeModelManage)) | }, reqRepoReader(models.UnitTypeModelManage)) | ||||
| m.Group("/cloudbrain", func() { | |||||
| m.Group("/train-job", func() { | |||||
| m.Post("/create", bind(api.CreateTrainJobOption{}), repo.CreateCloudBrain) | |||||
| }) | |||||
| m.Group("/inference-job", func() { | |||||
| m.Post("/create", bind(api.CreateTrainJobOption{}), repo.CreateCloudBrainInferenceTask) | |||||
| }) | |||||
| }, reqRepoReader(models.UnitTypeCloudBrain)) | |||||
| m.Group("/modelarts", func() { | m.Group("/modelarts", func() { | ||||
| m.Group("/notebook", func() { | m.Group("/notebook", func() { | ||||
| //m.Get("/:jobid", repo.GetModelArtsNotebook) | //m.Get("/:jobid", repo.GetModelArtsNotebook) | ||||
| @@ -16,6 +16,10 @@ import ( | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/services/cloudbrain/cloudbrainTask" | |||||
| api "code.gitea.io/gitea/modules/structs" | |||||
| "code.gitea.io/gitea/modules/notification" | "code.gitea.io/gitea/modules/notification" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| @@ -29,6 +33,28 @@ import ( | |||||
| routerRepo "code.gitea.io/gitea/routers/repo" | routerRepo "code.gitea.io/gitea/routers/repo" | ||||
| ) | ) | ||||
| func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) { | |||||
| if option.Type == 2 { | |||||
| cloudbrainTask.GrampusTrainJobGpuCreate(ctx.Context, option) | |||||
| } | |||||
| if option.Type == 3 { | |||||
| cloudbrainTask.GrampusTrainJobNpuCreate(ctx.Context, option) | |||||
| } | |||||
| } | |||||
| func CreateCloudBrainInferenceTask(ctx *context.APIContext, option api.CreateTrainJobOption) { | |||||
| if option.Type == 0 { | |||||
| cloudbrainTask.GrampusTrainJobGpuCreate(ctx.Context, option) | |||||
| } | |||||
| if option.Type == 1 { | |||||
| cloudbrainTask.GrampusTrainJobNpuCreate(ctx.Context, option) | |||||
| } | |||||
| } | |||||
| // cloudbrain get job task by jobid | // cloudbrain get job task by jobid | ||||
| func GetCloudbrainTask(ctx *context.APIContext) { | func GetCloudbrainTask(ctx *context.APIContext) { | ||||
| // swagger:operation GET /repos/{owner}/{repo}/cloudbrain/{jobid} cloudbrain jobTask | // swagger:operation GET /repos/{owner}/{repo}/cloudbrain/{jobid} cloudbrain jobTask | ||||
| @@ -0,0 +1,114 @@ | |||||
| package repo | |||||
| import ( | |||||
| "fmt" | |||||
| "strings" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/context" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| ) | |||||
| func PublicDatasetMultiple(ctx *context.APIContext) { | |||||
| opts := &models.SearchDatasetOptions{ | |||||
| PublicOnly: true, | |||||
| NeedAttachment: true, | |||||
| CloudBrainType: ctx.QueryInt("type"), | |||||
| } | |||||
| datasetMultiple(ctx, opts) | |||||
| } | |||||
| func MyFavoriteDatasetMultiple(ctx *context.APIContext) { | |||||
| opts := &models.SearchDatasetOptions{ | |||||
| StarByMe: true, | |||||
| DatasetIDs: models.GetDatasetIdsStarByUser(ctx.User.ID), | |||||
| NeedAttachment: true, | |||||
| CloudBrainType: ctx.QueryInt("type"), | |||||
| } | |||||
| datasetMultiple(ctx, opts) | |||||
| } | |||||
| func CurrentRepoDatasetMultiple(ctx *context.APIContext) { | |||||
| datasetIds := models.GetDatasetIdsByRepoID(ctx.Repo.Repository.ID) | |||||
| searchOrderBy := getSearchOrderByInValues(datasetIds) | |||||
| opts := &models.SearchDatasetOptions{ | |||||
| RepoID: ctx.Repo.Repository.ID, | |||||
| NeedAttachment: true, | |||||
| CloudBrainType: ctx.QueryInt("type"), | |||||
| DatasetIDs: datasetIds, | |||||
| SearchOrderBy: searchOrderBy, | |||||
| } | |||||
| datasetMultiple(ctx, opts) | |||||
| } | |||||
| func MyDatasetsMultiple(ctx *context.APIContext) { | |||||
| opts := &models.SearchDatasetOptions{ | |||||
| UploadAttachmentByMe: true, | |||||
| NeedAttachment: true, | |||||
| CloudBrainType: ctx.QueryInt("type"), | |||||
| } | |||||
| datasetMultiple(ctx, opts) | |||||
| } | |||||
| func datasetMultiple(ctx *context.APIContext, opts *models.SearchDatasetOptions) { | |||||
| page := ctx.QueryInt("page") | |||||
| if page < 1 { | |||||
| page = 1 | |||||
| } | |||||
| pageSize := ctx.QueryInt("pageSize") | |||||
| if pageSize < 1 { | |||||
| pageSize = setting.UI.DatasetPagingNum | |||||
| } | |||||
| keyword := strings.Trim(ctx.Query("q"), " ") | |||||
| opts.Keyword = keyword | |||||
| if opts.SearchOrderBy.String() == "" { | |||||
| opts.SearchOrderBy = models.SearchOrderByRecentUpdated | |||||
| } | |||||
| opts.RecommendOnly = ctx.QueryBool("recommend") | |||||
| opts.ListOptions = models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: pageSize, | |||||
| } | |||||
| opts.JustNeedZipFile = true | |||||
| opts.User = ctx.User | |||||
| datasets, count, err := models.SearchDataset(opts) | |||||
| if err != nil { | |||||
| log.Error("json.Marshal failed:", err.Error()) | |||||
| ctx.JSON(200, map[string]interface{}{ | |||||
| "code": 1, | |||||
| "message": err.Error(), | |||||
| "data": []*models.Dataset{}, | |||||
| "count": 0, | |||||
| }) | |||||
| return | |||||
| } | |||||
| ctx.JSON(200, map[string]interface{}{ | |||||
| "code": 0, | |||||
| "message": "", | |||||
| "data": datasets, | |||||
| "count": count, | |||||
| }) | |||||
| } | |||||
| func getSearchOrderByInValues(datasetIds []int64) models.SearchOrderBy { | |||||
| if len(datasetIds) == 0 { | |||||
| return "" | |||||
| } | |||||
| searchOrderBy := "CASE id " | |||||
| for i, id := range datasetIds { | |||||
| searchOrderBy += fmt.Sprintf(" WHEN %d THEN %d", id, i+1) | |||||
| } | |||||
| searchOrderBy += " ELSE 0 END" | |||||
| return models.SearchOrderBy(searchOrderBy) | |||||
| } | |||||
| @@ -0,0 +1,141 @@ | |||||
| package repo | |||||
| import ( | |||||
| "encoding/json" | |||||
| "net/http" | |||||
| "strconv" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/context" | |||||
| "code.gitea.io/gitea/modules/grampus" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/modelarts" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| ) | |||||
| type NPUImageINFO struct { | |||||
| ID string `json:"id"` | |||||
| Value string `json:"value"` | |||||
| } | |||||
| func GetPublicImages(ctx *context.APIContext) { | |||||
| uid := getUID(ctx) | |||||
| opts := models.SearchImageOptions{ | |||||
| IncludePublicOnly: true, | |||||
| UID: uid, | |||||
| Keyword: ctx.Query("q"), | |||||
| Topics: ctx.Query("topic"), | |||||
| IncludeOfficialOnly: ctx.QueryBool("recommend"), | |||||
| SearchOrderBy: "type desc, num_stars desc,id desc", | |||||
| Status: models.IMAGE_STATUS_SUCCESS, | |||||
| CloudbrainType: ctx.QueryInt("cloudbrainType"), | |||||
| } | |||||
| getImages(ctx, &opts) | |||||
| } | |||||
| func GetCustomImages(ctx *context.APIContext) { | |||||
| uid := getUID(ctx) | |||||
| opts := models.SearchImageOptions{ | |||||
| UID: uid, | |||||
| IncludeOwnerOnly: true, | |||||
| Keyword: ctx.Query("q"), | |||||
| Topics: ctx.Query("topic"), | |||||
| Status: -1, | |||||
| SearchOrderBy: "id desc", | |||||
| } | |||||
| getImages(ctx, &opts) | |||||
| } | |||||
| func GetStarImages(ctx *context.APIContext) { | |||||
| uid := getUID(ctx) | |||||
| opts := models.SearchImageOptions{ | |||||
| UID: uid, | |||||
| IncludeStarByMe: true, | |||||
| Keyword: ctx.Query("q"), | |||||
| Topics: ctx.Query("topic"), | |||||
| Status: models.IMAGE_STATUS_SUCCESS, | |||||
| SearchOrderBy: "id desc", | |||||
| } | |||||
| getImages(ctx, &opts) | |||||
| } | |||||
| func GetNpuImages(ctx *context.APIContext) { | |||||
| cloudbrainType := ctx.QueryInt("type") | |||||
| if cloudbrainType == 0 { //modelarts | |||||
| getModelArtsImages(ctx) | |||||
| } else { //c2net | |||||
| getC2netNpuImages(ctx) | |||||
| } | |||||
| } | |||||
| func getModelArtsImages(ctx *context.APIContext) { | |||||
| var versionInfos modelarts.VersionInfo | |||||
| _ = json.Unmarshal([]byte(setting.EngineVersions), &versionInfos) | |||||
| var npuImageInfos []NPUImageINFO | |||||
| for _, info := range versionInfos.Version { | |||||
| npuImageInfos = append(npuImageInfos, NPUImageINFO{ | |||||
| ID: strconv.Itoa(info.ID), | |||||
| Value: info.Value, | |||||
| }) | |||||
| } | |||||
| ctx.JSON(http.StatusOK, npuImageInfos) | |||||
| } | |||||
| func getC2netNpuImages(ctx *context.APIContext) { | |||||
| images, err := grampus.GetImages(grampus.ProcessorTypeNPU) | |||||
| var npuImageInfos []NPUImageINFO | |||||
| if err != nil { | |||||
| log.Error("GetImages failed:", err.Error()) | |||||
| ctx.JSON(http.StatusOK, []NPUImageINFO{}) | |||||
| } else { | |||||
| for _, info := range images.Infos { | |||||
| npuImageInfos = append(npuImageInfos, NPUImageINFO{ | |||||
| ID: info.ID, | |||||
| Value: info.Name, | |||||
| }) | |||||
| } | |||||
| ctx.JSON(http.StatusOK, npuImageInfos) | |||||
| } | |||||
| } | |||||
| func getImages(ctx *context.APIContext, opts *models.SearchImageOptions) { | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| pageSize := ctx.QueryInt("pageSize") | |||||
| if pageSize <= 0 { | |||||
| pageSize = 15 | |||||
| } | |||||
| opts.ListOptions = models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: pageSize, | |||||
| } | |||||
| imageList, total, err := models.SearchImage(opts) | |||||
| if err != nil { | |||||
| log.Error("Can not get images:%v", err) | |||||
| ctx.JSON(http.StatusOK, models.ImagesPageResult{ | |||||
| Count: 0, | |||||
| Images: []*models.Image{}, | |||||
| }) | |||||
| } else { | |||||
| ctx.JSON(http.StatusOK, models.ImagesPageResult{ | |||||
| Count: total, | |||||
| Images: imageList, | |||||
| }) | |||||
| } | |||||
| } | |||||
| func getUID(ctx *context.APIContext) int64 { | |||||
| var uid int64 = -1 | |||||
| if ctx.IsSigned { | |||||
| uid = ctx.User.ID | |||||
| } | |||||
| return uid | |||||
| } | |||||
| @@ -0,0 +1,631 @@ | |||||
| package cloudbrainTask | |||||
| import ( | |||||
| "bufio" | |||||
| "encoding/json" | |||||
| "errors" | |||||
| "fmt" | |||||
| "io" | |||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "os" | |||||
| "path" | |||||
| "strconv" | |||||
| "strings" | |||||
| "unicode/utf8" | |||||
| "code.gitea.io/gitea/modules/modelarts" | |||||
| "code.gitea.io/gitea/modules/git" | |||||
| api "code.gitea.io/gitea/modules/structs" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/cloudbrain" | |||||
| "code.gitea.io/gitea/modules/context" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/redis/redis_key" | |||||
| "code.gitea.io/gitea/modules/redis/redis_lock" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/storage" | |||||
| "code.gitea.io/gitea/modules/util" | |||||
| "code.gitea.io/gitea/services/cloudbrain/resource" | |||||
| "code.gitea.io/gitea/services/reward/point/account" | |||||
| ) | |||||
| const CLONE_FILE_PREFIX = "file:///" | |||||
| func CloudBrainInferenceJobCreate(ctx *context.Context, option api.CreateTrainJobOption) { | |||||
| displayJobName := option.DisplayJobName | |||||
| jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | |||||
| image := strings.TrimSpace(option.Image) | |||||
| uuid := option.Attachment | |||||
| jobType := string(models.JobTypeInference) | |||||
| codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | |||||
| branchName := option.BranchName | |||||
| bootFile := strings.TrimSpace(option.BootFile) | |||||
| labelName := option.LabelName | |||||
| repo := ctx.Repo.Repository | |||||
| lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), jobType, displayJobName)) | |||||
| defer lock.UnLock() | |||||
| isOk, err := lock.Lock(models.CloudbrainKeyDuration) | |||||
| if !isOk { | |||||
| log.Error("lock processed failed:%v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.cloudbrain_samejob_err"))) | |||||
| return | |||||
| } | |||||
| ckptUrl := setting.Attachment.Minio.RealPath + option.PreTrainModelUrl + option.CkptName | |||||
| log.Info("ckpt url:" + ckptUrl) | |||||
| command, err := getInferenceJobCommand(option) | |||||
| if err != nil { | |||||
| log.Error("getTrainJobCommand failed: %v", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName) | |||||
| if err == nil { | |||||
| if len(tasks) != 0 { | |||||
| log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("the job name did already exist")) | |||||
| return | |||||
| } | |||||
| } else { | |||||
| if !models.IsErrJobNotExist(err) { | |||||
| log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("system error")) | |||||
| return | |||||
| } | |||||
| } | |||||
| if !jobNamePattern.MatchString(displayJobName) { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.cloudbrain_jobname_err"))) | |||||
| return | |||||
| } | |||||
| bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) | |||||
| if err != nil || !bootFileExist { | |||||
| log.Error("Get bootfile error:", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.cloudbrain_bootfile_err"))) | |||||
| return | |||||
| } | |||||
| count, err := models.GetCloudbrainCountByUserID(ctx.User.ID, jobType) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("system error")) | |||||
| return | |||||
| } else { | |||||
| if count >= 1 { | |||||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.cloudbrain.morethanonejob"))) | |||||
| return | |||||
| } | |||||
| } | |||||
| if branchName == "" { | |||||
| branchName = cloudbrain.DefaultBranchName | |||||
| } | |||||
| errStr := loadCodeAndMakeModelPath(repo, codePath, branchName, jobName, cloudbrain.ResultPath) | |||||
| if errStr != "" { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr(errStr))) | |||||
| return | |||||
| } | |||||
| commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName) | |||||
| datasetInfos, datasetNames, err := models.GetDatasetInfo(uuid) | |||||
| if err != nil { | |||||
| log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.error.dataset_select"))) | |||||
| return | |||||
| } | |||||
| spec, err := resource.GetAndCheckSpec(ctx.User.ID, option.SpecId, models.FindSpecsOptions{ | |||||
| JobType: models.JobTypeInference, | |||||
| ComputeResource: models.GPU, | |||||
| Cluster: models.OpenICluster, | |||||
| AiCenterCode: models.AICenterOfCloudBrainOne}) | |||||
| if err != nil || spec == nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("Resource specification is not available")) | |||||
| return | |||||
| } | |||||
| if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) { | |||||
| log.Error("point balance is not enough,userId=%d specId=%d", ctx.User.ID, spec.ID) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("points.insufficient_points_balance"))) | |||||
| return | |||||
| } | |||||
| req := cloudbrain.GenerateCloudBrainTaskReq{ | |||||
| Ctx: ctx, | |||||
| DisplayJobName: displayJobName, | |||||
| JobName: jobName, | |||||
| Image: image, | |||||
| Command: command, | |||||
| Uuids: uuid, | |||||
| DatasetNames: datasetNames, | |||||
| DatasetInfos: datasetInfos, | |||||
| CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"), | |||||
| ModelPath: setting.Attachment.Minio.RealPath + option.PreTrainModelUrl, | |||||
| BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"), | |||||
| Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"), | |||||
| BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), | |||||
| JobType: jobType, | |||||
| Description: option.Description, | |||||
| BranchName: branchName, | |||||
| BootFile: option.BootFile, | |||||
| Params: option.Params, | |||||
| CommitID: commitID, | |||||
| ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"), | |||||
| ModelName: option.ModelName, | |||||
| ModelVersion: option.ModelVersion, | |||||
| CkptName: option.CkptName, | |||||
| TrainUrl: option.PreTrainModelUrl, | |||||
| LabelName: labelName, | |||||
| Spec: spec, | |||||
| } | |||||
| jobId, err := cloudbrain.GenerateTask(req) | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, models.BaseMessage{Code: 0, Message: jobId}) | |||||
| } | |||||
| func ModelArtsInferenceJobCreate(ctx *context.Context, option api.CreateTrainJobOption) { | |||||
| ctx.Data["PageIsTrainJob"] = true | |||||
| VersionOutputPath := modelarts.GetOutputPathByCount(modelarts.TotalVersionCount) | |||||
| displayJobName := option.DisplayJobName | |||||
| jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | |||||
| uuid := option.Attachment | |||||
| description := option.Description | |||||
| workServerNumber := option.WorkServerNumber | |||||
| engineID, _ := strconv.Atoi(option.ImageID) | |||||
| bootFile := strings.TrimSpace(option.BootFile) | |||||
| params := option.Params | |||||
| repo := ctx.Repo.Repository | |||||
| codeLocalPath := setting.JobPath + jobName + modelarts.CodePath | |||||
| codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath | |||||
| resultObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.ResultPath + VersionOutputPath + "/" | |||||
| logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath + VersionOutputPath + "/" | |||||
| //dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/" | |||||
| branchName := option.BranchName | |||||
| EngineName := option.Image | |||||
| LabelName := option.LabelName | |||||
| isLatestVersion := modelarts.IsLatestVersion | |||||
| VersionCount := modelarts.VersionCountOne | |||||
| trainUrl := option.PreTrainModelUrl | |||||
| modelName := option.ModelName | |||||
| modelVersion := option.ModelVersion | |||||
| ckptName := option.CkptName | |||||
| ckptUrl := "/" + option.PreTrainModelUrl + option.CkptName | |||||
| errStr := checkInferenceJobMultiNode(ctx.User.ID, option.WorkServerNumber) | |||||
| if errStr != "" { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr(errStr))) | |||||
| return | |||||
| } | |||||
| lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), string(models.JobTypeInference), displayJobName)) | |||||
| isOk, err := lock.Lock(models.CloudbrainKeyDuration) | |||||
| if !isOk { | |||||
| log.Error("lock processed failed:%v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.cloudbrain_samejob_err"))) | |||||
| return | |||||
| } | |||||
| defer lock.UnLock() | |||||
| count, err := models.GetCloudbrainInferenceJobCountByUserID(ctx.User.ID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainInferenceJobCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("system error")) | |||||
| return | |||||
| } else { | |||||
| if count >= 1 { | |||||
| log.Error("the user already has running or waiting inference task", ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("you have already a running or waiting inference task, can not create more")) | |||||
| return | |||||
| } | |||||
| } | |||||
| if err := paramCheckCreateInferenceJob(option); err != nil { | |||||
| log.Error("paramCheckCreateInferenceJob failed:(%v)", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) | |||||
| if err != nil || !bootFileExist { | |||||
| log.Error("Get bootfile error:", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.cloudbrain_bootfile_err"))) | |||||
| return | |||||
| } | |||||
| //Determine whether the task name of the task in the project is duplicated | |||||
| tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeInference), displayJobName) | |||||
| if err == nil { | |||||
| if len(tasks) != 0 { | |||||
| log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("the job name did already exist")) | |||||
| return | |||||
| } | |||||
| } else { | |||||
| if !models.IsErrJobNotExist(err) { | |||||
| log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("system error")) | |||||
| return | |||||
| } | |||||
| } | |||||
| spec, err := resource.GetAndCheckSpec(ctx.User.ID, option.SpecId, models.FindSpecsOptions{ | |||||
| JobType: models.JobTypeInference, | |||||
| ComputeResource: models.NPU, | |||||
| Cluster: models.OpenICluster, | |||||
| AiCenterCode: models.AICenterOfCloudBrainTwo}) | |||||
| if err != nil || spec == nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("Resource specification not available")) | |||||
| return | |||||
| } | |||||
| if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) { | |||||
| log.Error("point balance is not enough,userId=%d specId=%d ", ctx.User.ID, spec.ID) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("points.insufficient_points_balance"))) | |||||
| return | |||||
| } | |||||
| //todo: del the codeLocalPath | |||||
| _, err = ioutil.ReadDir(codeLocalPath) | |||||
| if err == nil { | |||||
| os.RemoveAll(codeLocalPath) | |||||
| } | |||||
| gitRepo, _ := git.OpenRepository(repo.RepoPath()) | |||||
| commitID, _ := gitRepo.GetBranchCommitID(branchName) | |||||
| if err := downloadCode(repo, codeLocalPath, branchName); err != nil { | |||||
| log.Error("Create task failed, server timed out: %s (%v)", repo.FullName(), err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| //todo: upload code (send to file_server todo this work?) | |||||
| if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.ResultPath + VersionOutputPath + "/"); err != nil { | |||||
| log.Error("Failed to obsMkdir_result: %s (%v)", repo.FullName(), err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("Failed to obsMkdir_result")) | |||||
| return | |||||
| } | |||||
| if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.LogPath + VersionOutputPath + "/"); err != nil { | |||||
| log.Error("Failed to obsMkdir_log: %s (%v)", repo.FullName(), err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("Failed to obsMkdir_log")) | |||||
| return | |||||
| } | |||||
| if err := uploadCodeToObs(codeLocalPath, jobName, ""); err != nil { | |||||
| log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| var parameters models.Parameters | |||||
| param := make([]models.Parameter, 0) | |||||
| param = append(param, models.Parameter{ | |||||
| Label: modelarts.ResultUrl, | |||||
| Value: "s3:/" + resultObsPath, | |||||
| }, models.Parameter{ | |||||
| Label: modelarts.CkptUrl, | |||||
| Value: "s3:/" + ckptUrl, | |||||
| }) | |||||
| datasUrlList, dataUrl, datasetNames, isMultiDataset, err := getDatasUrlListByUUIDS(uuid) | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| dataPath := dataUrl | |||||
| jsondatas, err := json.Marshal(datasUrlList) | |||||
| if err != nil { | |||||
| log.Error("Failed to Marshal: %v", err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("json error:"+err.Error())) | |||||
| return | |||||
| } | |||||
| if isMultiDataset { | |||||
| param = append(param, models.Parameter{ | |||||
| Label: modelarts.MultiDataUrl, | |||||
| Value: string(jsondatas), | |||||
| }) | |||||
| } | |||||
| existDeviceTarget := false | |||||
| if len(params) != 0 { | |||||
| err := json.Unmarshal([]byte(params), ¶meters) | |||||
| if err != nil { | |||||
| log.Error("Failed to Unmarshal params: %s (%v)", params, err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("运行参数错误")) | |||||
| return | |||||
| } | |||||
| for _, parameter := range parameters.Parameter { | |||||
| if parameter.Label == modelarts.DeviceTarget { | |||||
| existDeviceTarget = true | |||||
| } | |||||
| if parameter.Label != modelarts.TrainUrl && parameter.Label != modelarts.DataUrl { | |||||
| param = append(param, models.Parameter{ | |||||
| Label: parameter.Label, | |||||
| Value: parameter.Value, | |||||
| }) | |||||
| } | |||||
| } | |||||
| } | |||||
| if !existDeviceTarget { | |||||
| param = append(param, models.Parameter{ | |||||
| Label: modelarts.DeviceTarget, | |||||
| Value: modelarts.Ascend, | |||||
| }) | |||||
| } | |||||
| req := &modelarts.GenerateInferenceJobReq{ | |||||
| JobName: jobName, | |||||
| DisplayJobName: displayJobName, | |||||
| DataUrl: dataPath, | |||||
| Description: description, | |||||
| CodeObsPath: codeObsPath, | |||||
| BootFileUrl: codeObsPath + bootFile, | |||||
| BootFile: bootFile, | |||||
| TrainUrl: trainUrl, | |||||
| WorkServerNumber: workServerNumber, | |||||
| EngineID: int64(engineID), | |||||
| LogUrl: logObsPath, | |||||
| PoolID: getPoolId(), | |||||
| Uuid: uuid, | |||||
| Parameters: param, //modelarts train parameters | |||||
| CommitID: commitID, | |||||
| BranchName: branchName, | |||||
| Params: option.Params, | |||||
| EngineName: EngineName, | |||||
| LabelName: LabelName, | |||||
| IsLatestVersion: isLatestVersion, | |||||
| VersionCount: VersionCount, | |||||
| TotalVersionCount: modelarts.TotalVersionCount, | |||||
| ModelName: modelName, | |||||
| ModelVersion: modelVersion, | |||||
| CkptName: ckptName, | |||||
| ResultUrl: resultObsPath, | |||||
| Spec: spec, | |||||
| DatasetName: datasetNames, | |||||
| JobType: string(models.JobTypeInference), | |||||
| } | |||||
| jobId, err := modelarts.GenerateInferenceJob(ctx, req) | |||||
| if err != nil { | |||||
| log.Error("GenerateTrainJob failed:%v", err.Error()) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, models.BaseMessage{Code: 0, Message: jobId}) | |||||
| } | |||||
| func getDatasUrlListByUUIDS(uuidStr string) ([]models.Datasurl, string, string, bool, error) { | |||||
| var isMultiDataset bool | |||||
| var dataUrl string | |||||
| var datasetNames string | |||||
| var datasUrlList []models.Datasurl | |||||
| uuids := strings.Split(uuidStr, ";") | |||||
| if len(uuids) > setting.MaxDatasetNum { | |||||
| log.Error("the dataset count(%d) exceed the limit", len(uuids)) | |||||
| return datasUrlList, dataUrl, datasetNames, isMultiDataset, errors.New("the dataset count exceed the limit") | |||||
| } | |||||
| datasetInfos := make(map[string]models.DatasetInfo) | |||||
| attachs, err := models.GetAttachmentsByUUIDs(uuids) | |||||
| if err != nil || len(attachs) != len(uuids) { | |||||
| log.Error("GetAttachmentsByUUIDs failed: %v", err) | |||||
| return datasUrlList, dataUrl, datasetNames, isMultiDataset, errors.New("GetAttachmentsByUUIDs failed") | |||||
| } | |||||
| for i, tmpUuid := range uuids { | |||||
| var attach *models.Attachment | |||||
| for _, tmpAttach := range attachs { | |||||
| if tmpAttach.UUID == tmpUuid { | |||||
| attach = tmpAttach | |||||
| break | |||||
| } | |||||
| } | |||||
| if attach == nil { | |||||
| log.Error("GetAttachmentsByUUIDs failed: %v", err) | |||||
| return datasUrlList, dataUrl, datasetNames, isMultiDataset, errors.New("GetAttachmentsByUUIDs failed") | |||||
| } | |||||
| fileName := strings.TrimSuffix(strings.TrimSuffix(strings.TrimSuffix(attach.Name, ".zip"), ".tar.gz"), ".tgz") | |||||
| for _, datasetInfo := range datasetInfos { | |||||
| if fileName == datasetInfo.Name { | |||||
| log.Error("the dataset name is same: %v", attach.Name) | |||||
| return datasUrlList, dataUrl, datasetNames, isMultiDataset, errors.New("the dataset name is same") | |||||
| } | |||||
| } | |||||
| if len(attachs) <= 1 { | |||||
| dataUrl = "/" + setting.Bucket + "/" + setting.BasePath + path.Join(attach.UUID[0:1], attach.UUID[1:2]) + "/" + attach.UUID + attach.UUID + "/" | |||||
| isMultiDataset = false | |||||
| } else { | |||||
| dataUrl = "/" + setting.Bucket + "/" + setting.BasePath + path.Join(attachs[0].UUID[0:1], attachs[0].UUID[1:2]) + "/" + attachs[0].UUID + attachs[0].UUID + "/" | |||||
| datasetUrl := "s3://" + setting.Bucket + "/" + setting.BasePath + path.Join(attach.UUID[0:1], attach.UUID[1:2]) + "/" + attach.UUID + attach.UUID + "/" | |||||
| datasUrlList = append(datasUrlList, models.Datasurl{ | |||||
| DatasetUrl: datasetUrl, | |||||
| DatasetName: fileName, | |||||
| }) | |||||
| isMultiDataset = true | |||||
| } | |||||
| if i == 0 { | |||||
| datasetNames = attach.Name | |||||
| } else { | |||||
| datasetNames += ";" + attach.Name | |||||
| } | |||||
| } | |||||
| return datasUrlList, dataUrl, datasetNames, isMultiDataset, nil | |||||
| } | |||||
| func checkInferenceJobMultiNode(userId int64, serverNum int) string { | |||||
| if serverNum == 1 { | |||||
| return "" | |||||
| } | |||||
| return "repo.modelarts.no_node_right" | |||||
| } | |||||
| func paramCheckCreateInferenceJob(option api.CreateTrainJobOption) error { | |||||
| if !strings.HasSuffix(strings.TrimSpace(option.BootFile), ".py") { | |||||
| log.Error("the boot file(%s) must be a python file", strings.TrimSpace(option.BootFile)) | |||||
| return errors.New("启动文件必须是python文件") | |||||
| } | |||||
| if option.ModelName == "" { | |||||
| log.Error("the ModelName(%d) must not be nil", option.ModelName) | |||||
| return errors.New("模型名称不能为空") | |||||
| } | |||||
| if option.ModelVersion == "" { | |||||
| log.Error("the ModelVersion(%d) must not be nil", option.ModelVersion) | |||||
| return errors.New("模型版本不能为空") | |||||
| } | |||||
| if option.CkptName == "" { | |||||
| log.Error("the CkptName(%d) must not be nil", option.CkptName) | |||||
| return errors.New("权重文件不能为空") | |||||
| } | |||||
| if option.BranchName == "" { | |||||
| log.Error("the Branch(%d) must not be nil", option.BranchName) | |||||
| return errors.New("分支名不能为空") | |||||
| } | |||||
| if utf8.RuneCountInString(option.Description) > 255 { | |||||
| log.Error("the Description length(%d) must not more than 255", option.Description) | |||||
| return errors.New("描述字符不能超过255个字符") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func loadCodeAndMakeModelPath(repo *models.Repository, codePath string, branchName string, jobName string, resultPath string) string { | |||||
| err := downloadCode(repo, codePath, branchName) | |||||
| if err != nil { | |||||
| return "cloudbrain.load_code_failed" | |||||
| } | |||||
| err = uploadCodeToMinio(codePath+"/", jobName, cloudbrain.CodeMountPath+"/") | |||||
| if err != nil { | |||||
| return "cloudbrain.load_code_failed" | |||||
| } | |||||
| modelPath := setting.JobPath + jobName + resultPath + "/" | |||||
| err = mkModelPath(modelPath) | |||||
| if err != nil { | |||||
| return "cloudbrain.load_code_failed" | |||||
| } | |||||
| err = uploadCodeToMinio(modelPath, jobName, resultPath+"/") | |||||
| if err != nil { | |||||
| return "cloudbrain.load_code_failed" | |||||
| } | |||||
| return "" | |||||
| } | |||||
| func downloadCode(repo *models.Repository, codePath, branchName string) error { | |||||
| //add "file:///" prefix to make the depth valid | |||||
| if err := git.Clone(CLONE_FILE_PREFIX+repo.RepoPath(), codePath, git.CloneRepoOptions{Branch: branchName, Depth: 1}); err != nil { | |||||
| log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) | |||||
| return err | |||||
| } | |||||
| configFile, err := os.OpenFile(codePath+"/.git/config", os.O_RDWR, 0666) | |||||
| if err != nil { | |||||
| log.Error("open file(%s) failed:%v", codePath+"/,git/config", err) | |||||
| return err | |||||
| } | |||||
| defer configFile.Close() | |||||
| pos := int64(0) | |||||
| reader := bufio.NewReader(configFile) | |||||
| for { | |||||
| line, err := reader.ReadString('\n') | |||||
| if err != nil { | |||||
| if err == io.EOF { | |||||
| log.Error("not find the remote-url") | |||||
| return nil | |||||
| } else { | |||||
| log.Error("read error: %v", err) | |||||
| return err | |||||
| } | |||||
| } | |||||
| if strings.Contains(line, "url") && strings.Contains(line, ".git") { | |||||
| originUrl := "\turl = " + repo.CloneLink().HTTPS + "\n" | |||||
| if len(line) > len(originUrl) { | |||||
| originUrl += strings.Repeat(" ", len(line)-len(originUrl)) | |||||
| } | |||||
| bytes := []byte(originUrl) | |||||
| _, err := configFile.WriteAt(bytes, pos) | |||||
| if err != nil { | |||||
| log.Error("WriteAt failed:%v", err) | |||||
| return err | |||||
| } | |||||
| break | |||||
| } | |||||
| pos += int64(len(line)) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func getInferenceJobCommand(option api.CreateTrainJobOption) (string, error) { | |||||
| var command string | |||||
| bootFile := strings.TrimSpace(option.BootFile) | |||||
| params := option.Params | |||||
| if !strings.HasSuffix(bootFile, ".py") { | |||||
| log.Error("bootFile(%s) format error", bootFile) | |||||
| return command, errors.New("bootFile format error") | |||||
| } | |||||
| var parameters models.Parameters | |||||
| var param string | |||||
| if len(params) != 0 { | |||||
| err := json.Unmarshal([]byte(params), ¶meters) | |||||
| if err != nil { | |||||
| log.Error("Failed to Unmarshal params: %s (%v)", params, err) | |||||
| return command, err | |||||
| } | |||||
| for _, parameter := range parameters.Parameter { | |||||
| param += " --" + parameter.Label + "=" + parameter.Value | |||||
| } | |||||
| } | |||||
| param += " --modelname" + "=" + option.CkptName | |||||
| command += "python /code/" + bootFile + param + " > " + cloudbrain.ResultPath + "/" + option.DisplayJobName + "-" + cloudbrain.LogFile | |||||
| return command, nil | |||||
| } | |||||
| @@ -0,0 +1,678 @@ | |||||
| package cloudbrainTask | |||||
| import ( | |||||
| "encoding/json" | |||||
| "errors" | |||||
| "fmt" | |||||
| "io" | |||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "os" | |||||
| "path" | |||||
| "regexp" | |||||
| "strings" | |||||
| "code.gitea.io/gitea/modules/obs" | |||||
| "code.gitea.io/gitea/modules/git" | |||||
| "code.gitea.io/gitea/modules/storage" | |||||
| "github.com/unknwon/com" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/cloudbrain" | |||||
| "code.gitea.io/gitea/modules/context" | |||||
| "code.gitea.io/gitea/modules/grampus" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/modelarts" | |||||
| "code.gitea.io/gitea/modules/redis/redis_key" | |||||
| "code.gitea.io/gitea/modules/redis/redis_lock" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| api "code.gitea.io/gitea/modules/structs" | |||||
| "code.gitea.io/gitea/modules/util" | |||||
| "code.gitea.io/gitea/services/cloudbrain/resource" | |||||
| "code.gitea.io/gitea/services/reward/point/account" | |||||
| ) | |||||
| var jobNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$`) | |||||
| func GrampusTrainJobGpuCreate(ctx *context.Context, option api.CreateTrainJobOption) { | |||||
| displayJobName := option.DisplayJobName | |||||
| jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | |||||
| uuid := option.Attachment | |||||
| description := option.Description | |||||
| bootFile := strings.TrimSpace(option.BootFile) | |||||
| params := option.Params | |||||
| repo := ctx.Repo.Repository | |||||
| codeLocalPath := setting.JobPath + jobName + cloudbrain.CodeMountPath + "/" | |||||
| codeMinioPath := setting.CBCodePathPrefix + jobName + cloudbrain.CodeMountPath + "/" | |||||
| branchName := option.BranchName | |||||
| image := strings.TrimSpace(option.Image) | |||||
| lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), string(models.JobTypeTrain), displayJobName)) | |||||
| defer lock.UnLock() | |||||
| spec, datasetInfos, datasetNames, err := checkParameters(ctx, option, lock, repo) | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| //prepare code and out path | |||||
| _, err = ioutil.ReadDir(codeLocalPath) | |||||
| if err == nil { | |||||
| os.RemoveAll(codeLocalPath) | |||||
| } | |||||
| if err := downloadZipCode(ctx, codeLocalPath, branchName); err != nil { | |||||
| log.Error("downloadZipCode failed, server timed out: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| } | |||||
| //todo: upload code (send to file_server todo this work?) | |||||
| //upload code | |||||
| if err := uploadCodeToMinio(codeLocalPath+"/", jobName, cloudbrain.CodeMountPath+"/"); err != nil { | |||||
| log.Error("Failed to uploadCodeToMinio: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath + "/" | |||||
| if err := mkModelPath(modelPath); err != nil { | |||||
| log.Error("Failed to mkModelPath: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| //init model readme | |||||
| if err := uploadCodeToMinio(modelPath, jobName, cloudbrain.ModelMountPath+"/"); err != nil { | |||||
| log.Error("Failed to uploadCodeToMinio: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| var datasetRemotePath, allFileName string | |||||
| for _, datasetInfo := range datasetInfos { | |||||
| if datasetRemotePath == "" { | |||||
| datasetRemotePath = datasetInfo.DataLocalPath | |||||
| allFileName = datasetInfo.FullName | |||||
| } else { | |||||
| datasetRemotePath = datasetRemotePath + ";" + datasetInfo.DataLocalPath | |||||
| allFileName = allFileName + ";" + datasetInfo.FullName | |||||
| } | |||||
| } | |||||
| //prepare command | |||||
| preTrainModelPath := getPreTrainModelPath(option.PreTrainModelUrl, option.CkptName) | |||||
| command, err := generateCommand(repo.Name, grampus.ProcessorTypeGPU, codeMinioPath+cloudbrain.DefaultBranchName+".zip", datasetRemotePath, bootFile, params, setting.CBCodePathPrefix+jobName+cloudbrain.ModelMountPath+"/", allFileName, preTrainModelPath, option.CkptName) | |||||
| if err != nil { | |||||
| log.Error("Failed to generateCommand: %s (%v)", displayJobName, err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("Create task failed, internal error")) | |||||
| return | |||||
| } | |||||
| commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName) | |||||
| req := &grampus.GenerateTrainJobReq{ | |||||
| JobName: jobName, | |||||
| DisplayJobName: displayJobName, | |||||
| ComputeResource: models.GPUResource, | |||||
| ProcessType: grampus.ProcessorTypeGPU, | |||||
| Command: command, | |||||
| ImageUrl: image, | |||||
| Description: description, | |||||
| BootFile: bootFile, | |||||
| Uuid: uuid, | |||||
| CommitID: commitID, | |||||
| BranchName: branchName, | |||||
| Params: option.Params, | |||||
| EngineName: image, | |||||
| DatasetNames: datasetNames, | |||||
| DatasetInfos: datasetInfos, | |||||
| IsLatestVersion: modelarts.IsLatestVersion, | |||||
| VersionCount: modelarts.VersionCountOne, | |||||
| WorkServerNumber: 1, | |||||
| Spec: spec, | |||||
| } | |||||
| if option.ModelName != "" { //使用预训练模型训练 | |||||
| req.ModelName = option.ModelName | |||||
| req.LabelName = option.LabelName | |||||
| req.CkptName = option.CkptName | |||||
| req.ModelVersion = option.ModelVersion | |||||
| req.PreTrainModelUrl = option.PreTrainModelUrl | |||||
| } | |||||
| jobId, err := grampus.GenerateTrainJob(ctx, req) | |||||
| if err != nil { | |||||
| log.Error("GenerateTrainJob failed:%v", err.Error(), ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, models.BaseMessage{Code: 0, Message: jobId}) | |||||
| } | |||||
| func checkParameters(ctx *context.Context, option api.CreateTrainJobOption, lock *redis_lock.DistributeLock, repo *models.Repository) (*models.Specification, map[string]models.DatasetInfo, string, error) { | |||||
| isOk, err := lock.Lock(models.CloudbrainKeyDuration) | |||||
| if !isOk { | |||||
| log.Error("lock processed failed:%v", err, ctx.Data["MsgID"]) | |||||
| return nil, nil, "", fmt.Errorf(ctx.Tr("repo.cloudbrain_samejob_err")) | |||||
| } | |||||
| if !jobNamePattern.MatchString(option.DisplayJobName) { | |||||
| return nil, nil, "", fmt.Errorf(ctx.Tr("repo.cloudbrain_jobname_err")) | |||||
| } | |||||
| bootFileExist, err := ctx.Repo.FileExists(option.BootFile, option.BranchName) | |||||
| if err != nil || !bootFileExist { | |||||
| log.Error("Get bootfile error:", err, ctx.Data["MsgID"]) | |||||
| return nil, nil, "", fmt.Errorf(ctx.Tr("repo.cloudbrain_bootfile_err")) | |||||
| } | |||||
| //check count limit | |||||
| count, err := models.GetGrampusCountByUserID(ctx.User.ID, string(models.JobTypeTrain), models.GPUResource) | |||||
| if err != nil { | |||||
| log.Error("GetGrampusCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||||
| return nil, nil, "", fmt.Errorf("system error") | |||||
| } else { | |||||
| if count >= 1 { | |||||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||||
| return nil, nil, "", fmt.Errorf("you have already a running or waiting task, can not create more.") | |||||
| } | |||||
| } | |||||
| //check param | |||||
| if err := grampusParamCheckCreateTrainJob(option.BootFile, option.BranchName); err != nil { | |||||
| log.Error("paramCheckCreateTrainJob failed:(%v)", err, ctx.Data["MsgID"]) | |||||
| return nil, nil, "", err | |||||
| } | |||||
| //check whether the task name in the project is duplicated | |||||
| tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeTrain), option.DisplayJobName) | |||||
| if err == nil { | |||||
| if len(tasks) != 0 { | |||||
| log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||||
| return nil, nil, "", fmt.Errorf("The job name did already exist.") | |||||
| } | |||||
| } else { | |||||
| if !models.IsErrJobNotExist(err) { | |||||
| log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||||
| return nil, nil, "", fmt.Errorf("system error") | |||||
| } | |||||
| } | |||||
| //check specification | |||||
| computeResource := models.GPU | |||||
| if option.Type == 3 { | |||||
| computeResource = models.NPU | |||||
| } | |||||
| spec, err := resource.GetAndCheckSpec(ctx.User.ID, option.SpecId, models.FindSpecsOptions{ | |||||
| JobType: models.JobTypeTrain, | |||||
| ComputeResource: computeResource, | |||||
| Cluster: models.C2NetCluster, | |||||
| }) | |||||
| if err != nil || spec == nil { | |||||
| return nil, nil, "", fmt.Errorf("Resource specification is not available.") | |||||
| } | |||||
| if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) { | |||||
| log.Error("point balance is not enough,userId=%d specId=%d", ctx.User.ID, spec.ID) | |||||
| return nil, nil, "", fmt.Errorf(ctx.Tr("points.insufficient_points_balance")) | |||||
| } | |||||
| //check dataset | |||||
| datasetInfos, datasetNames, err := models.GetDatasetInfo(option.Attachment, computeResource) | |||||
| if err != nil { | |||||
| log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"]) | |||||
| return nil, nil, "", fmt.Errorf(ctx.Tr("cloudbrain.error.dataset_select")) | |||||
| } | |||||
| return spec, datasetInfos, datasetNames, err | |||||
| } | |||||
| func GrampusTrainJobNpuCreate(ctx *context.Context, option api.CreateTrainJobOption) { | |||||
| displayJobName := option.DisplayJobName | |||||
| jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | |||||
| uuid := option.Attachment | |||||
| description := option.Description | |||||
| bootFile := strings.TrimSpace(option.BootFile) | |||||
| params := option.Params | |||||
| repo := ctx.Repo.Repository | |||||
| codeLocalPath := setting.JobPath + jobName + modelarts.CodePath | |||||
| codeObsPath := grampus.JobPath + jobName + modelarts.CodePath | |||||
| branchName := option.BranchName | |||||
| isLatestVersion := modelarts.IsLatestVersion | |||||
| versionCount := modelarts.VersionCountOne | |||||
| engineName := option.Image | |||||
| lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), string(models.JobTypeTrain), displayJobName)) | |||||
| defer lock.UnLock() | |||||
| spec, datasetInfos, datasetNames, err := checkParameters(ctx, option, lock, repo) | |||||
| if err != nil { | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| //prepare code and out path | |||||
| _, err = ioutil.ReadDir(codeLocalPath) | |||||
| if err == nil { | |||||
| os.RemoveAll(codeLocalPath) | |||||
| } | |||||
| if err := downloadZipCode(ctx, codeLocalPath, branchName); err != nil { | |||||
| log.Error("downloadZipCode failed, server timed out: %s (%v)", repo.FullName(), err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| //todo: upload code (send to file_server todo this work?) | |||||
| if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath); err != nil { | |||||
| log.Error("Failed to obsMkdir_output: %s (%v)", repo.FullName(), err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| if err := uploadCodeToObs(codeLocalPath, jobName, ""); err != nil { | |||||
| log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("cloudbrain.load_code_failed"))) | |||||
| return | |||||
| } | |||||
| var datasetRemotePath, allFileName string | |||||
| for _, datasetInfo := range datasetInfos { | |||||
| if datasetRemotePath == "" { | |||||
| datasetRemotePath = datasetInfo.DataLocalPath + "'" + datasetInfo.FullName + "'" | |||||
| allFileName = datasetInfo.FullName | |||||
| } else { | |||||
| datasetRemotePath = datasetRemotePath + ";" + datasetInfo.DataLocalPath + "'" + datasetInfo.FullName + "'" | |||||
| allFileName = allFileName + ";" + datasetInfo.FullName | |||||
| } | |||||
| } | |||||
| //prepare command | |||||
| preTrainModelPath := getPreTrainModelPath(option.PreTrainModelUrl, option.CkptName) | |||||
| command, err := generateCommand(repo.Name, grampus.ProcessorTypeNPU, codeObsPath+cloudbrain.DefaultBranchName+".zip", datasetRemotePath, bootFile, params, setting.CodePathPrefix+jobName+modelarts.OutputPath, allFileName, preTrainModelPath, option.CkptName) | |||||
| if err != nil { | |||||
| log.Error("Failed to generateCommand: %s (%v)", displayJobName, err, ctx.Data["MsgID"]) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage("Create task failed, internal error")) | |||||
| return | |||||
| } | |||||
| commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName) | |||||
| req := &grampus.GenerateTrainJobReq{ | |||||
| JobName: jobName, | |||||
| DisplayJobName: displayJobName, | |||||
| ComputeResource: models.NPUResource, | |||||
| ProcessType: grampus.ProcessorTypeNPU, | |||||
| Command: command, | |||||
| ImageId: option.ImageID, | |||||
| Description: description, | |||||
| CodeObsPath: codeObsPath, | |||||
| BootFileUrl: codeObsPath + bootFile, | |||||
| BootFile: bootFile, | |||||
| WorkServerNumber: option.WorkServerNumber, | |||||
| Uuid: uuid, | |||||
| CommitID: commitID, | |||||
| IsLatestVersion: isLatestVersion, | |||||
| BranchName: branchName, | |||||
| Params: option.Params, | |||||
| EngineName: engineName, | |||||
| VersionCount: versionCount, | |||||
| TotalVersionCount: modelarts.TotalVersionCount, | |||||
| DatasetNames: datasetNames, | |||||
| DatasetInfos: datasetInfos, | |||||
| Spec: spec, | |||||
| CodeName: strings.ToLower(repo.Name), | |||||
| } | |||||
| if option.ModelName != "" { //使用预训练模型训练 | |||||
| req.ModelName = option.ModelName | |||||
| req.LabelName = option.LabelName | |||||
| req.CkptName = option.CkptName | |||||
| req.ModelVersion = option.ModelVersion | |||||
| req.PreTrainModelUrl = option.PreTrainModelUrl | |||||
| req.PreTrainModelPath = preTrainModelPath | |||||
| } | |||||
| jobId, err := grampus.GenerateTrainJob(ctx, req) | |||||
| if err != nil { | |||||
| log.Error("GenerateTrainJob failed:%v", err.Error()) | |||||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(err.Error())) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, models.BaseMessage{Code: 0, Message: jobId}) | |||||
| } | |||||
| func obsMkdir(dir string) error { | |||||
| input := &obs.PutObjectInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = dir | |||||
| _, err := storage.ObsCli.PutObject(input) | |||||
| if err != nil { | |||||
| log.Error("PutObject(%s) failed: %s", input.Key, err.Error()) | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func uploadCodeToObs(codePath, jobName, parentDir string) error { | |||||
| files, err := readDir(codePath) | |||||
| if err != nil { | |||||
| log.Error("readDir(%s) failed: %s", codePath, err.Error()) | |||||
| return err | |||||
| } | |||||
| for _, file := range files { | |||||
| if file.IsDir() { | |||||
| input := &obs.PutObjectInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = parentDir + file.Name() + "/" | |||||
| _, err = storage.ObsCli.PutObject(input) | |||||
| if err != nil { | |||||
| log.Error("PutObject(%s) failed: %s", input.Key, err.Error()) | |||||
| return err | |||||
| } | |||||
| if err = uploadCodeToObs(codePath+file.Name()+"/", jobName, parentDir+file.Name()+"/"); err != nil { | |||||
| log.Error("uploadCodeToObs(%s) failed: %s", file.Name(), err.Error()) | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| input := &obs.PutFileInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = setting.CodePathPrefix + jobName + "/code/" + parentDir + file.Name() | |||||
| input.SourceFile = codePath + file.Name() | |||||
| _, err = storage.ObsCli.PutFile(input) | |||||
| if err != nil { | |||||
| log.Error("PutFile(%s) failed: %s", input.SourceFile, err.Error()) | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func grampusParamCheckCreateTrainJob(bootFile string, branchName string) error { | |||||
| if !strings.HasSuffix(strings.TrimSpace(bootFile), ".py") { | |||||
| log.Error("the boot file(%s) must be a python file", bootFile) | |||||
| return errors.New("启动文件必须是python文件") | |||||
| } | |||||
| if branchName == "" { | |||||
| log.Error("the branch must not be null!", branchName) | |||||
| return errors.New("代码分支不能为空!") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func downloadZipCode(ctx *context.Context, codePath, branchName string) error { | |||||
| archiveType := git.ZIP | |||||
| archivePath := codePath | |||||
| if !com.IsDir(archivePath) { | |||||
| if err := os.MkdirAll(archivePath, os.ModePerm); err != nil { | |||||
| log.Error("MkdirAll failed:" + err.Error()) | |||||
| return err | |||||
| } | |||||
| } | |||||
| // Get corresponding commit. | |||||
| var ( | |||||
| commit *git.Commit | |||||
| err error | |||||
| ) | |||||
| gitRepo := ctx.Repo.GitRepo | |||||
| if err != nil { | |||||
| log.Error("OpenRepository failed:" + err.Error()) | |||||
| return err | |||||
| } | |||||
| if gitRepo.IsBranchExist(branchName) { | |||||
| commit, err = gitRepo.GetBranchCommit(branchName) | |||||
| if err != nil { | |||||
| log.Error("GetBranchCommit failed:" + err.Error()) | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| log.Error("the branch is not exist: " + branchName) | |||||
| return fmt.Errorf("The branch does not exist.") | |||||
| } | |||||
| archivePath = path.Join(archivePath, grampus.CodeArchiveName) | |||||
| if !com.IsFile(archivePath) { | |||||
| if err := commit.CreateArchive(archivePath, git.CreateArchiveOpts{ | |||||
| Format: archiveType, | |||||
| Prefix: setting.Repository.PrefixArchiveFiles, | |||||
| }); err != nil { | |||||
| log.Error("CreateArchive failed:" + err.Error()) | |||||
| return err | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func uploadCodeToMinio(codePath, jobName, parentDir string) error { | |||||
| files, err := readDir(codePath) | |||||
| if err != nil { | |||||
| log.Error("readDir(%s) failed: %s", codePath, err.Error()) | |||||
| return err | |||||
| } | |||||
| for _, file := range files { | |||||
| if file.IsDir() { | |||||
| if err = uploadCodeToMinio(codePath+file.Name()+"/", jobName, parentDir+file.Name()+"/"); err != nil { | |||||
| log.Error("uploadCodeToMinio(%s) failed: %s", file.Name(), err.Error()) | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| destObject := setting.CBCodePathPrefix + jobName + parentDir + file.Name() | |||||
| sourceFile := codePath + file.Name() | |||||
| err = storage.Attachments.UploadObject(destObject, sourceFile) | |||||
| if err != nil { | |||||
| log.Error("UploadObject(%s) failed: %s", file.Name(), err.Error()) | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func readDir(dirname string) ([]os.FileInfo, error) { | |||||
| f, err := os.Open(dirname) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| list, err := f.Readdir(0) | |||||
| f.Close() | |||||
| if err != nil { | |||||
| //todo: can not upload empty folder | |||||
| if err == io.EOF { | |||||
| return nil, nil | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| //sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() }) | |||||
| return list, nil | |||||
| } | |||||
| func mkModelPath(modelPath string) error { | |||||
| return mkPathAndReadMeFile(modelPath, "You can put the files into this directory and download the files by the web page.") | |||||
| } | |||||
| func mkPathAndReadMeFile(path string, text string) error { | |||||
| err := os.MkdirAll(path, os.ModePerm) | |||||
| if err != nil { | |||||
| log.Error("MkdirAll(%s) failed:%v", path, err) | |||||
| return err | |||||
| } | |||||
| fileName := path + "README" | |||||
| f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) | |||||
| if err != nil { | |||||
| log.Error("OpenFile failed", err.Error()) | |||||
| return err | |||||
| } | |||||
| defer f.Close() | |||||
| _, err = f.WriteString(text) | |||||
| if err != nil { | |||||
| log.Error("WriteString failed", err.Error()) | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func getPreTrainModelPath(pretrainModelDir string, fileName string) string { | |||||
| index := strings.Index(pretrainModelDir, "/") | |||||
| if index > 0 { | |||||
| filterBucket := pretrainModelDir[index+1:] | |||||
| return filterBucket + fileName | |||||
| } else { | |||||
| return "" | |||||
| } | |||||
| } | |||||
| func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bootFile, paramSrc, outputRemotePath, datasetName, pretrainModelPath, pretrainModelFileName string) (string, error) { | |||||
| var command string | |||||
| workDir := grampus.NpuWorkDir | |||||
| if processorType == grampus.ProcessorTypeGPU { | |||||
| workDir = grampus.GpuWorkDir | |||||
| } | |||||
| command += "pwd;cd " + workDir + fmt.Sprintf(grampus.CommandPrepareScript, setting.Grampus.SyncScriptProject, setting.Grampus.SyncScriptProject) | |||||
| //download code & dataset | |||||
| if processorType == grampus.ProcessorTypeNPU { | |||||
| //no need to download code & dataset by internet | |||||
| } else if processorType == grampus.ProcessorTypeGPU { | |||||
| commandDownload := "./downloader_for_minio " + setting.Grampus.Env + " " + codeRemotePath + " " + grampus.CodeArchiveName + " '" + dataRemotePath + "' '" + datasetName + "'" | |||||
| commandDownload = processPretrainModelParameter(pretrainModelPath, pretrainModelFileName, commandDownload) | |||||
| command += commandDownload | |||||
| } | |||||
| //unzip code & dataset | |||||
| if processorType == grampus.ProcessorTypeNPU { | |||||
| //no need to process | |||||
| } else if processorType == grampus.ProcessorTypeGPU { | |||||
| unZipDatasetCommand := generateDatasetUnzipCommand(datasetName) | |||||
| commandUnzip := "cd " + workDir + "code;unzip -q master.zip;echo \"start to unzip dataset\";cd " + workDir + "dataset;" + unZipDatasetCommand | |||||
| command += commandUnzip | |||||
| } | |||||
| command += "echo \"unzip finished;start to exec code;\";" | |||||
| // set export | |||||
| var commandExport string | |||||
| if processorType == grampus.ProcessorTypeNPU { | |||||
| commandExport = "export bucket=" + setting.Bucket + " && export remote_path=" + outputRemotePath + ";" | |||||
| } else if processorType == grampus.ProcessorTypeGPU { | |||||
| commandExport = "export env=" + setting.Grampus.Env + " && export remote_path=" + outputRemotePath + ";" | |||||
| } | |||||
| command += commandExport | |||||
| //exec code | |||||
| var parameters models.Parameters | |||||
| var paramCode string | |||||
| if len(paramSrc) != 0 { | |||||
| err := json.Unmarshal([]byte(paramSrc), ¶meters) | |||||
| if err != nil { | |||||
| log.Error("Failed to Unmarshal params: %s (%v)", paramSrc, err) | |||||
| return command, err | |||||
| } | |||||
| for _, parameter := range parameters.Parameter { | |||||
| paramCode += " --" + parameter.Label + "=" + parameter.Value | |||||
| } | |||||
| } | |||||
| var commandCode string | |||||
| if processorType == grampus.ProcessorTypeNPU { | |||||
| commandCode = "/bin/bash /home/work/run_train_for_openi.sh /home/work/openi.py /tmp/log/train.log" + paramCode + ";" | |||||
| } else if processorType == grampus.ProcessorTypeGPU { | |||||
| if pretrainModelFileName != "" { | |||||
| paramCode += " --ckpt_url" + "=" + workDir + "pretrainmodel/" + pretrainModelFileName | |||||
| } | |||||
| commandCode = "cd " + workDir + "code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";" | |||||
| } | |||||
| command += commandCode | |||||
| //get exec result | |||||
| commandGetRes := "result=$?;" | |||||
| command += commandGetRes | |||||
| //upload models | |||||
| if processorType == grampus.ProcessorTypeNPU { | |||||
| commandUpload := "cd " + workDir + setting.Grampus.SyncScriptProject + "/;./uploader_for_npu " + setting.Bucket + " " + outputRemotePath + " " + workDir + "output/;" | |||||
| command += commandUpload | |||||
| } else if processorType == grampus.ProcessorTypeGPU { | |||||
| commandUpload := "cd " + workDir + setting.Grampus.SyncScriptProject + "/;./uploader_for_gpu " + setting.Grampus.Env + " " + outputRemotePath + " " + workDir + "output/;" | |||||
| command += commandUpload | |||||
| } | |||||
| //check exec result | |||||
| commandCheckRes := "bash -c \"[[ $result -eq 0 ]] && exit 0 || exit -1\"" | |||||
| command += commandCheckRes | |||||
| return command, nil | |||||
| } | |||||
| func processPretrainModelParameter(pretrainModelPath string, pretrainModelFileName string, commandDownload string) string { | |||||
| commandDownloadTemp := commandDownload | |||||
| if pretrainModelPath != "" { | |||||
| commandDownloadTemp += " '" + pretrainModelPath + "' '" + pretrainModelFileName + "'" | |||||
| } | |||||
| commandDownloadTemp += ";" | |||||
| return commandDownloadTemp | |||||
| } | |||||
| func generateDatasetUnzipCommand(datasetName string) string { | |||||
| var unZipDatasetCommand string | |||||
| datasetNameArray := strings.Split(datasetName, ";") | |||||
| if len(datasetNameArray) == 1 { //单数据集 | |||||
| unZipDatasetCommand = "unzip -q '" + datasetName + "';" | |||||
| if strings.HasSuffix(datasetNameArray[0], ".tar.gz") { | |||||
| unZipDatasetCommand = "tar --strip-components=1 -zxvf '" + datasetName + "';" | |||||
| } | |||||
| } else { //多数据集 | |||||
| for _, datasetNameTemp := range datasetNameArray { | |||||
| if strings.HasSuffix(datasetNameTemp, ".tar.gz") { | |||||
| unZipDatasetCommand = unZipDatasetCommand + "tar -zxvf '" + datasetNameTemp + "';" | |||||
| } else { | |||||
| unZipDatasetCommand = unZipDatasetCommand + "unzip -q '" + datasetNameTemp + "' -d './" + strings.TrimSuffix(datasetNameTemp, ".zip") + "';" | |||||
| } | |||||
| } | |||||
| } | |||||
| return unZipDatasetCommand | |||||
| } | |||||
| func getPoolId() string { | |||||
| var resourcePools modelarts.ResourcePool | |||||
| json.Unmarshal([]byte(setting.ResourcePools), &resourcePools) | |||||
| return resourcePools.Info[0].ID | |||||
| } | |||||