| @@ -1080,3 +1080,8 @@ BASE_PATH = attachment/ | |||
| [modelarts] | |||
| ENDPOINT = 112.95.163.80 | |||
| PROJECT_ID = edfccf24aace4e17a56da6bcbb55a5aa | |||
| PROJECT_NAME = cn-south-222_test | |||
| USERNAME = test1 | |||
| PASSWORD = Qizhi@test. | |||
| DOMAIN = cn-south-222 | |||
| @@ -14,6 +14,7 @@ import ( | |||
| type CloudbrainStatus string | |||
| type JobType string | |||
| type ModelArtsJobStatus string | |||
| const ( | |||
| JobWaiting CloudbrainStatus = "WAITING" | |||
| @@ -24,6 +25,22 @@ const ( | |||
| JobTypeDebug JobType = "DEBUG" | |||
| JobTypeBenchmark JobType = "BENCHMARK" | |||
| ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中 | |||
| ModelArtsCreating ModelArtsJobStatus = "CREATING" //创建中 | |||
| ModelArtsCreateFailed ModelArtsJobStatus = "CREATE_FAILED" //创建失败 | |||
| ModelArtsStartQueuing ModelArtsJobStatus = "START_QUEUING" //免费资源启动排队中 | |||
| ModelArtsReadyToStart ModelArtsJobStatus = "READY_TO_START" //免费资源等待启动 | |||
| ModelArtsStarting ModelArtsJobStatus = "STARTING" //启动中 | |||
| ModelArtsRestarting ModelArtsJobStatus = "RESTARTING" //重启中 | |||
| ModelArtsStartFailed ModelArtsJobStatus = "START_FAILED" //启动失败 | |||
| ModelArtsRunning ModelArtsJobStatus = "RUNNING" //运行中 | |||
| ModelArtsStopping ModelArtsJobStatus = "STOPPING" //停止中 | |||
| ModelArtsStopped ModelArtsJobStatus = "STOPPED" //停止 | |||
| ModelArtsUnavailable ModelArtsJobStatus = "UNAVAILABLE" //故障 | |||
| ModelArtsDeleted ModelArtsJobStatus = "DELETED" //已删除 | |||
| ModelArtsResizing ModelArtsJobStatus = "RESIZING" //规格变更中 | |||
| ModelArtsResizFailed ModelArtsJobStatus = "RESIZE_FAILED" //规格变更失败 | |||
| ) | |||
| type Cloudbrain struct { | |||
| @@ -41,6 +58,7 @@ type Cloudbrain struct { | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
| DeletedAt time.Time `xorm:"deleted"` | |||
| CanDebug bool `xorm:"-"` | |||
| Type int `xorm:"INDEX DEFAULT 0"` | |||
| User *User `xorm:"-"` | |||
| Repo *Repository `xorm:"-"` | |||
| @@ -112,6 +130,7 @@ type CloudbrainsOptions struct { | |||
| SortType string | |||
| CloudbrainIDs []int64 | |||
| // JobStatus CloudbrainStatus | |||
| Type int | |||
| } | |||
| type TaskPod struct { | |||
| TaskRoleStatus struct { | |||
| @@ -257,6 +276,137 @@ type StopJobResult struct { | |||
| Msg string `json:"msg"` | |||
| } | |||
| type CreateNotebookParams struct { | |||
| JobName string `json:"name"` | |||
| Description string `json:"description"` | |||
| ProfileID string `json:"profile_id"` | |||
| Flavor string `json:"flavor"` | |||
| Spec Spec `json:"spec"` | |||
| Workspace Workspace `json:"workspace"` | |||
| } | |||
| type Workspace struct { | |||
| ID string `json:"id"` | |||
| } | |||
| type Spec struct { | |||
| Storage Storage `json:"storage"` | |||
| AutoStop AutoStop `json:"auto_stop"` | |||
| } | |||
| type AutoStop struct { | |||
| Enable bool `json:"enable"` | |||
| Duration int `json:"duration"` | |||
| } | |||
| type Storage struct { | |||
| Type string `json:"type"` | |||
| Location Location `json:"location"` | |||
| } | |||
| type Location struct { | |||
| Path string `json:"path"` | |||
| } | |||
| type CreateNotebookResult struct { | |||
| ErrorCode string `json:"error_code"` | |||
| ErrorMsg string `json:"error_msg"` | |||
| ID string `json:"id"` | |||
| Name string `json:"name"` | |||
| Description string `json:"description"` | |||
| Status string `json:"status"` | |||
| CreationTimestamp string `json:"creation_timestamp"` | |||
| LatestUpdateTimestamp string `json:"latest_update_timestamp"` | |||
| Profile struct { | |||
| ID string `json:"id"` | |||
| Name string `json:"name"` | |||
| Description string `json:"description"` | |||
| DeType string `json:"de_type"` | |||
| FlavorType string `json:"flavor_type"` | |||
| } `json:"profile"` | |||
| Flavor string `json:"flavor"` | |||
| FlavorDetails struct{ | |||
| Name string `json:"name"` | |||
| Status string `json:"status"` | |||
| QueuingNum int `json:"queuing_num"` | |||
| QueueLeftTime int `json:"queue_left_time"` //s | |||
| Duration int `json:"duration"` //auto_stop_time s | |||
| } `json:"flavor_details"` | |||
| } | |||
| type GetNotebookResult struct { | |||
| ErrorCode string `json:"error_code"` | |||
| ErrorMsg string `json:"error_msg"` | |||
| ID string `json:"id"` | |||
| Name string `json:"name"` | |||
| Description string `json:"description"` | |||
| Status string `json:"status"` | |||
| CreationTimestamp string `json:"creation_timestamp"` | |||
| LatestUpdateTimestamp string `json:"latest_update_timestamp"` | |||
| Profile struct { | |||
| ID string `json:"id"` | |||
| Name string `json:"name"` | |||
| Description string `json:"description"` | |||
| DeType string `json:"de_type"` | |||
| FlavorType string `json:"flavor_type"` | |||
| } `json:"profile"` | |||
| Flavor string `json:"flavor"` | |||
| FlavorDetails struct{ | |||
| Name string `json:"name"` | |||
| Status string `json:"status"` | |||
| QueuingNum int `json:"queuing_num"` | |||
| QueueLeftTime int `json:"queue_left_time"` //s | |||
| Duration int `json:"duration"` //auto_stop_time s | |||
| } `json:"flavor_details"` | |||
| QueuingInfo struct{ | |||
| ID string `json:"id"` | |||
| Name string `json:"name"` | |||
| Flavor string `json:"flavor"` | |||
| DeType string `json:"de_type"` | |||
| Status string `json:"status"` | |||
| BeginTimestamp int `json:"begin_timestamp"`//time of instance begin in queue | |||
| RemainTime int `json:"remain_time"` //remain time of instance | |||
| EndTimestamp int `json:"end_timestamp"` // | |||
| Rank int `json:"rank"` //rank of instance in queue | |||
| } `json:"queuing_info"` | |||
| } | |||
| type GetTokenParams struct { | |||
| Auth Auth `json:auth` | |||
| } | |||
| type Auth struct { | |||
| Identity Identity `json:identity` | |||
| Scope Scope `json:scope` | |||
| } | |||
| type Scope struct { | |||
| Project Project `json:project` | |||
| } | |||
| type Project struct { | |||
| Name string `json:name` | |||
| } | |||
| type Identity struct { | |||
| Methods []string `json:"methods"` | |||
| Password Password `json:password` | |||
| } | |||
| type Password struct { | |||
| User NotebookUser `json:user` | |||
| } | |||
| type NotebookUser struct { | |||
| Name string `json:name` | |||
| Password string `json:"password"` | |||
| Domain Domain `json:domain` | |||
| } | |||
| type Domain struct { | |||
| Name string `json:name` | |||
| } | |||
| func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| @@ -280,6 +430,12 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) { | |||
| ) | |||
| } | |||
| if (opts.Type) >= 0 { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.type": opts.Type}, | |||
| ) | |||
| } | |||
| // switch opts.JobStatus { | |||
| // case JobWaiting: | |||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobWaiting)}) | |||
| @@ -2045,8 +2045,8 @@ func SyncExternalUsers(ctx context.Context, updateExisting bool) error { | |||
| func GetBlockChainUnSuccessUsers() ([]*User, error) { | |||
| users := make([]*User, 0, 10) | |||
| err := x.Where("public_key is null"). | |||
| Or("private_key is null"). | |||
| err := x.Where("public_key = ''"). | |||
| Or("private_key = ''"). | |||
| Find(&users) | |||
| return users, err | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| // based on https://github.com/golang/go/blob/master/src/net/url/url.go | |||
| // Copyright 2009 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package core | |||
| func shouldEscape(c byte) bool { | |||
| if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c == '-' || c == '~' || c == '.' { | |||
| return false | |||
| } | |||
| return true | |||
| } | |||
| func escape(s string) string { | |||
| hexCount := 0 | |||
| for i := 0; i < len(s); i++ { | |||
| c := s[i] | |||
| if shouldEscape(c) { | |||
| hexCount++ | |||
| } | |||
| } | |||
| if hexCount == 0 { | |||
| return s | |||
| } | |||
| t := make([]byte, len(s)+2*hexCount) | |||
| j := 0 | |||
| for i := 0; i < len(s); i++ { | |||
| switch c := s[i]; { | |||
| case shouldEscape(c): | |||
| t[j] = '%' | |||
| t[j+1] = "0123456789ABCDEF"[c>>4] | |||
| t[j+2] = "0123456789ABCDEF"[c&15] | |||
| j += 3 | |||
| default: | |||
| t[j] = s[i] | |||
| j++ | |||
| } | |||
| } | |||
| return string(t) | |||
| } | |||
| @@ -0,0 +1,208 @@ | |||
| // HWS API Gateway Signature | |||
| // based on https://github.com/datastream/aws/blob/master/signv4.go | |||
| // Copyright (c) 2014, Xianjie | |||
| package core | |||
| import ( | |||
| "bytes" | |||
| "crypto/hmac" | |||
| "crypto/sha256" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "net/http" | |||
| "sort" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| const ( | |||
| BasicDateFormat = "20060102T150405Z" | |||
| Algorithm = "SDK-HMAC-SHA256" | |||
| HeaderXDate = "X-Sdk-Date" | |||
| HeaderHost = "host" | |||
| HeaderAuthorization = "Authorization" | |||
| HeaderContentSha256 = "X-Sdk-Content-Sha256" | |||
| ) | |||
| func hmacsha256(key []byte, data string) ([]byte, error) { | |||
| h := hmac.New(sha256.New, []byte(key)) | |||
| if _, err := h.Write([]byte(data)); err != nil { | |||
| return nil, err | |||
| } | |||
| return h.Sum(nil), nil | |||
| } | |||
| // Build a CanonicalRequest from a regular request string | |||
| // | |||
| // CanonicalRequest = | |||
| // HTTPRequestMethod + '\n' + | |||
| // CanonicalURI + '\n' + | |||
| // CanonicalQueryString + '\n' + | |||
| // CanonicalHeaders + '\n' + | |||
| // SignedHeaders + '\n' + | |||
| // HexEncode(Hash(RequestPayload)) | |||
| func CanonicalRequest(r *http.Request, signedHeaders []string) (string, error) { | |||
| var hexencode string | |||
| var err error | |||
| if hex := r.Header.Get(HeaderContentSha256); hex != "" { | |||
| hexencode = hex | |||
| } else { | |||
| data, err := RequestPayload(r) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| hexencode, err = HexEncodeSHA256Hash(data) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| } | |||
| return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", r.Method, CanonicalURI(r), CanonicalQueryString(r), CanonicalHeaders(r, signedHeaders), strings.Join(signedHeaders, ";"), hexencode), err | |||
| } | |||
| // CanonicalURI returns request uri | |||
| func CanonicalURI(r *http.Request) string { | |||
| pattens := strings.Split(r.URL.Path, "/") | |||
| var uri []string | |||
| for _, v := range pattens { | |||
| uri = append(uri, escape(v)) | |||
| } | |||
| urlpath := strings.Join(uri, "/") | |||
| if len(urlpath) == 0 || urlpath[len(urlpath)-1] != '/' { | |||
| urlpath = urlpath + "/" | |||
| } | |||
| return urlpath | |||
| } | |||
| // CanonicalQueryString | |||
| func CanonicalQueryString(r *http.Request) string { | |||
| var keys []string | |||
| query := r.URL.Query() | |||
| for key := range query { | |||
| keys = append(keys, key) | |||
| } | |||
| sort.Strings(keys) | |||
| var a []string | |||
| for _, key := range keys { | |||
| k := escape(key) | |||
| sort.Strings(query[key]) | |||
| for _, v := range query[key] { | |||
| kv := fmt.Sprintf("%s=%s", k, escape(v)) | |||
| a = append(a, kv) | |||
| } | |||
| } | |||
| queryStr := strings.Join(a, "&") | |||
| r.URL.RawQuery = queryStr | |||
| return queryStr | |||
| } | |||
| // CanonicalHeaders | |||
| func CanonicalHeaders(r *http.Request, signerHeaders []string) string { | |||
| var a []string | |||
| header := make(map[string][]string) | |||
| for k, v := range r.Header { | |||
| header[strings.ToLower(k)] = v | |||
| } | |||
| for _, key := range signerHeaders { | |||
| value := header[key] | |||
| if strings.EqualFold(key, HeaderHost) { | |||
| value = []string{r.Host} | |||
| } | |||
| sort.Strings(value) | |||
| for _, v := range value { | |||
| a = append(a, key+":"+strings.TrimSpace(v)) | |||
| } | |||
| } | |||
| return fmt.Sprintf("%s\n", strings.Join(a, "\n")) | |||
| } | |||
| // SignedHeaders | |||
| func SignedHeaders(r *http.Request) []string { | |||
| var a []string | |||
| for key := range r.Header { | |||
| a = append(a, strings.ToLower(key)) | |||
| } | |||
| sort.Strings(a) | |||
| return a | |||
| } | |||
| // RequestPayload | |||
| func RequestPayload(r *http.Request) ([]byte, error) { | |||
| if r.Body == nil { | |||
| return []byte(""), nil | |||
| } | |||
| b, err := ioutil.ReadAll(r.Body) | |||
| if err != nil { | |||
| return []byte(""), err | |||
| } | |||
| r.Body = ioutil.NopCloser(bytes.NewBuffer(b)) | |||
| return b, err | |||
| } | |||
| // Create a "String to Sign". | |||
| func StringToSign(canonicalRequest string, t time.Time) (string, error) { | |||
| hash := sha256.New() | |||
| _, err := hash.Write([]byte(canonicalRequest)) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| return fmt.Sprintf("%s\n%s\n%x", | |||
| Algorithm, t.UTC().Format(BasicDateFormat), hash.Sum(nil)), nil | |||
| } | |||
| // Create the HWS Signature. | |||
| func SignStringToSign(stringToSign string, signingKey []byte) (string, error) { | |||
| hm, err := hmacsha256(signingKey, stringToSign) | |||
| return fmt.Sprintf("%x", hm), err | |||
| } | |||
| // HexEncodeSHA256Hash returns hexcode of sha256 | |||
| func HexEncodeSHA256Hash(body []byte) (string, error) { | |||
| hash := sha256.New() | |||
| if body == nil { | |||
| body = []byte("") | |||
| } | |||
| _, err := hash.Write(body) | |||
| return fmt.Sprintf("%x", hash.Sum(nil)), err | |||
| } | |||
| // Get the finalized value for the "Authorization" header. The signature parameter is the output from SignStringToSign | |||
| func AuthHeaderValue(signature, accessKey string, signedHeaders []string) string { | |||
| return fmt.Sprintf("%s Access=%s, SignedHeaders=%s, Signature=%s", Algorithm, accessKey, strings.Join(signedHeaders, ";"), signature) | |||
| } | |||
| // Signature HWS meta | |||
| type Signer struct { | |||
| Key string | |||
| Secret string | |||
| } | |||
| // SignRequest set Authorization header | |||
| func (s *Signer) Sign(r *http.Request) error { | |||
| var t time.Time | |||
| var err error | |||
| var dt string | |||
| if dt = r.Header.Get(HeaderXDate); dt != "" { | |||
| t, err = time.Parse(BasicDateFormat, dt) | |||
| } | |||
| if err != nil || dt == "" { | |||
| t = time.Now() | |||
| r.Header.Set(HeaderXDate, t.UTC().Format(BasicDateFormat)) | |||
| } | |||
| signedHeaders := SignedHeaders(r) | |||
| canonicalRequest, err := CanonicalRequest(r, signedHeaders) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| stringToSign, err := StringToSign(canonicalRequest, t) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| signature, err := SignStringToSign(stringToSign, []byte(s.Secret)) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| authValue := AuthHeaderValue(signature, s.Key, signedHeaders) | |||
| r.Header.Set(HeaderAuthorization, authValue) | |||
| return nil | |||
| } | |||
| @@ -5,7 +5,6 @@ import ( | |||
| "gitea.com/macaron/macaron" | |||
| ) | |||
| // CreateDatasetForm form for dataset page | |||
| type CreateCloudBrainForm struct { | |||
| JobName string `form:"job_name" binding:"Required"` | |||
| Image string `form:"image" binding:"Required"` | |||
| @@ -0,0 +1,18 @@ | |||
| package auth | |||
| import ( | |||
| "gitea.com/macaron/binding" | |||
| "gitea.com/macaron/macaron" | |||
| ) | |||
| type CreateModelArtsForm struct { | |||
| JobName string `form:"job_name" binding:"Required"` | |||
| Image string `form:"image" binding:"Required"` | |||
| Command string `form:"command" binding:"Required"` | |||
| Attachment string `form:"attachment" binding:"Required"` | |||
| JobType string `form:"job_type" binding:"Required"` | |||
| } | |||
| func (f *CreateModelArtsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||
| } | |||
| @@ -98,6 +98,7 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, | |||
| JobName: jobName, | |||
| SubTaskName: SubTaskName, | |||
| JobType: jobType, | |||
| Type: models.TypeCloudBrainOne, | |||
| }) | |||
| if err != nil { | |||
| @@ -0,0 +1,65 @@ | |||
| package modelarts | |||
| import ( | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "path" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| const ( | |||
| storageTypeOBS = "obs" | |||
| autoStopDuration = 4 * 60 *60 | |||
| flavor = "modelarts.kat1.xlarge" | |||
| profileID = "Python3-ascend910-arm" | |||
| subTaskName = "task1" | |||
| DataSetMountPath = "/home/ma-user/work" | |||
| ) | |||
| func GenerateTask(ctx *context.Context, jobName, uuid string) error { | |||
| dataActualPath := setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" | |||
| jobResult, err := CreateJob(models.CreateNotebookParams{ | |||
| JobName: jobName, | |||
| Description:"", | |||
| ProfileID: profileID, | |||
| Flavor: flavor, | |||
| Spec: models.Spec{ | |||
| Storage: models.Storage{ | |||
| Type: storageTypeOBS, | |||
| Location:models.Location{ | |||
| Path: dataActualPath, | |||
| }, | |||
| }, | |||
| AutoStop: models.AutoStop{ | |||
| Enable: true, | |||
| Duration: autoStopDuration, | |||
| }, | |||
| }, | |||
| }) | |||
| if err != nil { | |||
| log.Error("CreateJob failed:", err.Error()) | |||
| return err | |||
| } | |||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
| Status: string(models.JobWaiting), | |||
| UserID: ctx.User.ID, | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| JobID: jobResult.ID, | |||
| JobName: jobName, | |||
| SubTaskName: subTaskName, | |||
| JobType: string(models.JobTypeDebug), | |||
| Type: models.TypeCloudBrainTwo, | |||
| }) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| @@ -0,0 +1,240 @@ | |||
| package modelarts | |||
| import ( | |||
| "code.gitea.io/gitea/modules/log" | |||
| "fmt" | |||
| "net/http" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "github.com/go-resty/resty/v2" | |||
| ) | |||
| var ( | |||
| restyClient *resty.Client | |||
| HOST string | |||
| TOKEN string | |||
| ) | |||
| const ( | |||
| methodPassword = "password" | |||
| urlGetToken = "/v3/auth/tokens" | |||
| urlNotebook = "/demanager/instances" | |||
| urlQuaryNotebook = "/demanager/instances" | |||
| ) | |||
| func getRestyClient() *resty.Client { | |||
| if restyClient == nil { | |||
| restyClient = resty.New() | |||
| } | |||
| return restyClient | |||
| } | |||
| func checkSetting() { | |||
| if len(HOST) != 0 && len(TOKEN) != 0 && restyClient != nil { | |||
| return | |||
| } | |||
| getToken() | |||
| } | |||
| func getToken() error { | |||
| HOST = setting.ModelArtsHost | |||
| client := getRestyClient() | |||
| params := models.GetTokenParams{ | |||
| Auth: models.Auth{ | |||
| Identity: models.Identity{ | |||
| Methods: []string{methodPassword}, | |||
| Password: models.Password{ | |||
| User: models.NotebookUser{ | |||
| Name: setting.ModelArtsUsername, | |||
| Password: setting.ModelArtsPassword, | |||
| Domain: models.Domain{ | |||
| Name: setting.ModelArtsDomain, | |||
| }, | |||
| }, | |||
| }, | |||
| }, | |||
| Scope: models.Scope{ | |||
| Project: models.Project{ | |||
| Name: setting.ProjectName, | |||
| }, | |||
| }, | |||
| }, | |||
| } | |||
| res, err := client.R(). | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetBody(params). | |||
| Post(HOST + urlGetToken) | |||
| if err != nil { | |||
| return fmt.Errorf("resty getToken: %s", err) | |||
| } | |||
| if res.StatusCode() != http.StatusCreated { | |||
| return fmt.Errorf("getToken failed:%s", res.String()) | |||
| } | |||
| TOKEN = res.Header().Get("X-Subject-Token") | |||
| return nil | |||
| } | |||
| func CreateJob(createJobParams models.CreateNotebookParams) (*models.CreateNotebookResult, error) { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var result models.CreateNotebookResult | |||
| retry := 0 | |||
| sendjob: | |||
| res, err := client.R(). | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetAuthToken(TOKEN). | |||
| SetBody(createJobParams). | |||
| SetResult(&result). | |||
| Post(HOST + "/v1/" + setting.ProjectID + urlNotebook) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("resty create job: %s", err) | |||
| } | |||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
| retry++ | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| if len(result.ErrorCode) != 0 { | |||
| log.Error("CreateJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| return &result, fmt.Errorf("CreateJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| } | |||
| return &result, nil | |||
| } | |||
| func GetJob(jobID string) (*models.GetNotebookResult, error) { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var result models.GetNotebookResult | |||
| retry := 0 | |||
| sendjob: | |||
| res, err := client.R(). | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetAuthToken(TOKEN). | |||
| SetResult(&result). | |||
| Get(HOST + "/v1/" + setting.ProjectID + urlNotebook + jobID) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("resty GetJob: %v", err) | |||
| } | |||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
| retry++ | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| if len(result.ErrorCode) != 0 { | |||
| return &result, fmt.Errorf("CreateJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| } | |||
| return &result, nil | |||
| } | |||
| func GetImages() (*models.GetImagesResult, error) { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var getImagesResult models.GetImagesResult | |||
| retry := 0 | |||
| sendjob: | |||
| res, err := client.R(). | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetAuthToken(TOKEN). | |||
| SetResult(&getImagesResult). | |||
| Get(HOST + "/rest-server/api/v1/image/list/") | |||
| if err != nil { | |||
| return nil, fmt.Errorf("resty GetImages: %v", err) | |||
| } | |||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
| retry++ | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| //if len(result.ErrorCode) != 0 { | |||
| // return &result, fmt.Errorf("CreateJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| //} | |||
| return &getImagesResult, nil | |||
| } | |||
| func CommitImage(jobID string, params models.CommitImageParams) error { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var result models.CommitImageResult | |||
| retry := 0 | |||
| sendjob: | |||
| res, err := client.R(). | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetAuthToken(TOKEN). | |||
| SetBody(params). | |||
| SetResult(&result). | |||
| Post(HOST + "/rest-server/api/v1/jobs/" + jobID + "/commitImage") | |||
| if err != nil { | |||
| return fmt.Errorf("resty CommitImage: %v", err) | |||
| } | |||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
| retry++ | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| //if len(result.ErrorCode) != 0 { | |||
| // return &result, fmt.Errorf("CreateJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| //} | |||
| return nil | |||
| } | |||
| func StopJob(jobID string) error { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var result models.StopJobResult | |||
| retry := 0 | |||
| sendjob: | |||
| res, err := client.R(). | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetAuthToken(TOKEN). | |||
| SetResult(&result). | |||
| Delete(HOST + "/rest-server/api/v1/jobs/" + jobID) | |||
| if err != nil { | |||
| return fmt.Errorf("resty StopJob: %v", err) | |||
| } | |||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
| retry++ | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| //if len(result.ErrorCode) != 0 { | |||
| // return &result, fmt.Errorf("CreateJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| //} | |||
| return nil | |||
| } | |||
| @@ -456,6 +456,14 @@ var ( | |||
| Location string | |||
| BasePath string | |||
| //RealPath string | |||
| //modelarts config | |||
| ModelArtsHost string | |||
| ProjectID string | |||
| ProjectName string | |||
| ModelArtsUsername string | |||
| ModelArtsPassword string | |||
| ModelArtsDomain string | |||
| ) | |||
| // DateLang transforms standard language locale name to corresponding value in datetime plugin. | |||
| @@ -1148,6 +1156,14 @@ func NewContext() { | |||
| Bucket = sec.Key("BUCKET").MustString("testopendata") | |||
| Location = sec.Key("LOCATION").MustString("cn-south-222") | |||
| BasePath = sec.Key("BASE_PATH").MustString("attachment/") | |||
| sec = Cfg.Section("modelarts") | |||
| ModelArtsHost = sec.Key("ENDPOINT").MustString("112.95.163.80") | |||
| ProjectID = sec.Key("PROJECT_ID").MustString("") | |||
| ProjectName = sec.Key("PROJECT_NAME").MustString("") | |||
| ModelArtsUsername = sec.Key("USERNAME").MustString("") | |||
| ModelArtsPassword = sec.Key("PASSWORD").MustString("") | |||
| ModelArtsDomain = sec.Key("DOMAIN").MustString("cn-south-222") | |||
| } | |||
| func loadInternalToken(sec *ini.Section) string { | |||
| @@ -46,7 +46,7 @@ func CloudBrainIndex(ctx *context.Context) { | |||
| PageSize: setting.UI.IssuePagingNum, | |||
| }, | |||
| RepoID: repo.ID, | |||
| // SortType: sortType, | |||
| Type: models.TypeCloudBrainOne, | |||
| }) | |||
| if err != nil { | |||
| ctx.ServerError("Cloudbrain", err) | |||
| @@ -0,0 +1,250 @@ | |||
| package repo | |||
| import ( | |||
| "code.gitea.io/gitea/modules/modelarts" | |||
| "errors" | |||
| "strconv" | |||
| "time" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/auth" | |||
| "code.gitea.io/gitea/modules/base" | |||
| "code.gitea.io/gitea/modules/cloudbrain" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| ) | |||
| const ( | |||
| tplModelArtsIndex base.TplName = "repo/modelarts/index" | |||
| tplModelArtsNew base.TplName = "repo/modelarts/new" | |||
| tplModelArtsShow base.TplName = "repo/modelarts/show" | |||
| ) | |||
| // MustEnableDataset check if repository enable internal cb | |||
| func MustEnableModelArts(ctx *context.Context) { | |||
| if !ctx.Repo.CanRead(models.UnitTypeCloudBrain) { | |||
| ctx.NotFound("MustEnableCloudbrain", nil) | |||
| return | |||
| } | |||
| } | |||
| func ModelArtsIndex(ctx *context.Context) { | |||
| MustEnableCloudbrain(ctx) | |||
| repo := ctx.Repo.Repository | |||
| page := ctx.QueryInt("page") | |||
| if page <= 0 { | |||
| page = 1 | |||
| } | |||
| ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||
| ListOptions: models.ListOptions{ | |||
| Page: page, | |||
| PageSize: setting.UI.IssuePagingNum, | |||
| }, | |||
| RepoID: repo.ID, | |||
| // SortType: sortType, | |||
| }) | |||
| if err != nil { | |||
| ctx.ServerError("Cloudbrain", err) | |||
| return | |||
| } | |||
| timestamp := time.Now().Unix() | |||
| for i, task := range ciTasks { | |||
| if task.Status == string(models.JobRunning) && (timestamp-int64(task.CreatedUnix) > 30) { | |||
| ciTasks[i].CanDebug = true | |||
| } else { | |||
| ciTasks[i].CanDebug = false | |||
| } | |||
| } | |||
| pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) | |||
| pager.SetDefaultParams(ctx) | |||
| ctx.Data["Page"] = pager | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| ctx.Data["Tasks"] = ciTasks | |||
| ctx.HTML(200, tplCloudBrainIndex) | |||
| } | |||
| func ModelArtsNew(ctx *context.Context) { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| t := time.Now() | |||
| var jobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| ctx.Data["job_name"] = jobName | |||
| //attachs, err := models.GetAllUserAttachments(ctx.User.ID) | |||
| //if err != nil { | |||
| // ctx.ServerError("GetAllUserAttachments failed:", err) | |||
| // return | |||
| //} | |||
| //ctx.Data["attachments"] = attachs | |||
| //ctx.Data["command"] = cloudbrain.Command | |||
| //ctx.Data["code_path"] = cloudbrain.CodeMountPath | |||
| ctx.Data["dataset_path"] = modelarts.DataSetMountPath | |||
| //ctx.Data["model_path"] = cloudbrain.ModelMountPath | |||
| ctx.HTML(200, tplModelArtsNew) | |||
| } | |||
| func ModelArtsCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| jobName := form.JobName | |||
| uuid := form.Attachment | |||
| //repo := ctx.Repo.Repository | |||
| err := modelarts.GenerateTask(ctx, jobName, uuid) | |||
| if err != nil { | |||
| ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | |||
| return | |||
| } | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | |||
| } | |||
| func ModelArtsShow(ctx *context.Context) { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| var jobID = ctx.Params(":jobid") | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.Data["error"] = err.Error() | |||
| } | |||
| //result, err := modelarts.GetJob(jobID) | |||
| result, err := cloudbrain.GetJob(jobID) | |||
| if err != nil { | |||
| ctx.Data["error"] = err.Error() | |||
| } | |||
| if result != nil { | |||
| jobRes, _ := models.ConvertToJobResultPayload(result.Payload) | |||
| ctx.Data["result"] = jobRes | |||
| taskRoles := jobRes.TaskRoles | |||
| taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) | |||
| ctx.Data["taskRes"] = taskRes | |||
| task.Status = taskRes.TaskStatuses[0].State | |||
| task.ContainerID = taskRes.TaskStatuses[0].ContainerID | |||
| task.ContainerIp = taskRes.TaskStatuses[0].ContainerIP | |||
| err = models.UpdateJob(task) | |||
| if err != nil { | |||
| ctx.Data["error"] = err.Error() | |||
| } | |||
| } | |||
| ctx.Data["task"] = task | |||
| ctx.Data["jobID"] = jobID | |||
| ctx.HTML(200, tplCloudBrainShow) | |||
| } | |||
| func ModelArtsDebug(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
| return | |||
| } | |||
| //https://console.ai.pcl.cn/modelarts/internal/hub/notebook/user/DE-afcdf674-6489-11eb-bfe7-0255ac100057/lab | |||
| //debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName | |||
| debugUrl := "https://console.ai.pcl.cn/modelarts/internal/hub/notebook/user/" + task.JobID + "/lab" | |||
| ctx.Redirect(debugUrl) | |||
| } | |||
| func ModelArtsCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | |||
| var jobID = ctx.Params(":jobid") | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.JSON(200, map[string]string{ | |||
| "result_code": "-1", | |||
| "error_msg": "GetCloudbrainByJobID failed", | |||
| }) | |||
| return | |||
| } | |||
| err = cloudbrain.CommitImage(jobID, models.CommitImageParams{ | |||
| Ip: task.ContainerIp, | |||
| TaskContainerId: task.ContainerID, | |||
| ImageDescription: form.Description, | |||
| ImageTag: form.Tag, | |||
| }) | |||
| if err != nil { | |||
| log.Error("CommitImage(%s) failed:", task.JobName, err.Error()) | |||
| ctx.JSON(200, map[string]string{ | |||
| "result_code": "-1", | |||
| "error_msg": "CommitImage failed", | |||
| }) | |||
| return | |||
| } | |||
| ctx.JSON(200, map[string]string{ | |||
| "result_code": "0", | |||
| "error_msg": "", | |||
| }) | |||
| } | |||
| func ModelArtsStop(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| log.Info(jobID) | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
| return | |||
| } | |||
| if task.Status != string(models.JobRunning) { | |||
| log.Error("the job(%s) is not running", task.JobName) | |||
| ctx.ServerError("the job is not running", errors.New("the job is not running")) | |||
| return | |||
| } | |||
| err = cloudbrain.StopJob(jobID) | |||
| if err != nil { | |||
| log.Error("StopJob(%s) failed:%v", task.JobName, err.Error()) | |||
| ctx.ServerError("StopJob failed", err) | |||
| return | |||
| } | |||
| task.Status = string(models.JobStopped) | |||
| err = models.UpdateJob(task) | |||
| if err != nil { | |||
| ctx.ServerError("UpdateJob failed", err) | |||
| return | |||
| } | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/cloudbrain") | |||
| } | |||
| func ModelArtsDel(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
| return | |||
| } | |||
| if task.Status != string(models.JobStopped) { | |||
| log.Error("the job(%s) has not been stopped", task.JobName) | |||
| ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | |||
| return | |||
| } | |||
| err = models.DeleteJob(task) | |||
| if err != nil { | |||
| ctx.ServerError("DeleteJob failed", err) | |||
| return | |||
| } | |||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/cloudbrain") | |||
| } | |||
| func ModelArtsBenchmark(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| _, err := models.GetCloudbrainByJobID(jobID) | |||
| if err != nil { | |||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||
| return | |||
| } | |||
| ctx.Redirect(setting.BenchmarkServerHost) | |||
| } | |||
| @@ -915,6 +915,18 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | |||
| }, context.RepoRef()) | |||
| m.Group("/modelarts", func() { | |||
| m.Get("", reqRepoCloudBrainReader, repo.ModelArtsIndex) | |||
| m.Group("/:jobid", func() { | |||
| m.Get("", reqRepoCloudBrainReader, repo.ModelArtsShow) | |||
| m.Get("/debug", reqRepoCloudBrainReader, repo.ModelArtsDebug) | |||
| //m.Post("/stop", reqRepoCloudBrainWriter, repo.CloudBrainStop) | |||
| //m.Post("/del", reqRepoCloudBrainWriter, repo.CloudBrainDel) | |||
| }) | |||
| m.Get("/create", reqRepoCloudBrainWriter, repo.ModelArtsNew) | |||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsForm{}), repo.ModelArtsCreate) | |||
| }, context.RepoRef()) | |||
| m.Group("/blockchain", func() { | |||
| m.Get("", repo.BlockChainIndex) | |||
| }, context.RepoRef()) | |||