@@ -1080,3 +1080,8 @@ BASE_PATH = attachment/ | |||||
[modelarts] | [modelarts] | ||||
ENDPOINT = 112.95.163.80 | 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 CloudbrainStatus string | ||||
type JobType string | type JobType string | ||||
type ModelArtsJobStatus string | |||||
const ( | const ( | ||||
JobWaiting CloudbrainStatus = "WAITING" | JobWaiting CloudbrainStatus = "WAITING" | ||||
@@ -24,6 +25,22 @@ const ( | |||||
JobTypeDebug JobType = "DEBUG" | JobTypeDebug JobType = "DEBUG" | ||||
JobTypeBenchmark JobType = "BENCHMARK" | 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 { | type Cloudbrain struct { | ||||
@@ -41,6 +58,7 @@ type Cloudbrain struct { | |||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | ||||
DeletedAt time.Time `xorm:"deleted"` | DeletedAt time.Time `xorm:"deleted"` | ||||
CanDebug bool `xorm:"-"` | CanDebug bool `xorm:"-"` | ||||
Type int `xorm:"INDEX DEFAULT 0"` | |||||
User *User `xorm:"-"` | User *User `xorm:"-"` | ||||
Repo *Repository `xorm:"-"` | Repo *Repository `xorm:"-"` | ||||
@@ -112,6 +130,7 @@ type CloudbrainsOptions struct { | |||||
SortType string | SortType string | ||||
CloudbrainIDs []int64 | CloudbrainIDs []int64 | ||||
// JobStatus CloudbrainStatus | // JobStatus CloudbrainStatus | ||||
Type int | |||||
} | } | ||||
type TaskPod struct { | type TaskPod struct { | ||||
TaskRoleStatus struct { | TaskRoleStatus struct { | ||||
@@ -257,6 +276,137 @@ type StopJobResult struct { | |||||
Msg string `json:"msg"` | 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) { | func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) { | ||||
sess := x.NewSession() | sess := x.NewSession() | ||||
defer sess.Close() | 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 { | // switch opts.JobStatus { | ||||
// case JobWaiting: | // case JobWaiting: | ||||
// cond.And(builder.Eq{"cloudbrain.status": int(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) { | func GetBlockChainUnSuccessUsers() ([]*User, error) { | ||||
users := make([]*User, 0, 10) | 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) | Find(&users) | ||||
return users, err | 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" | "gitea.com/macaron/macaron" | ||||
) | ) | ||||
// CreateDatasetForm form for dataset page | |||||
type CreateCloudBrainForm struct { | type CreateCloudBrainForm struct { | ||||
JobName string `form:"job_name" binding:"Required"` | JobName string `form:"job_name" binding:"Required"` | ||||
Image string `form:"image" 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, | JobName: jobName, | ||||
SubTaskName: SubTaskName, | SubTaskName: SubTaskName, | ||||
JobType: jobType, | JobType: jobType, | ||||
Type: models.TypeCloudBrainOne, | |||||
}) | }) | ||||
if err != nil { | 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 | Location string | ||||
BasePath string | BasePath string | ||||
//RealPath 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. | // DateLang transforms standard language locale name to corresponding value in datetime plugin. | ||||
@@ -1148,6 +1156,14 @@ func NewContext() { | |||||
Bucket = sec.Key("BUCKET").MustString("testopendata") | Bucket = sec.Key("BUCKET").MustString("testopendata") | ||||
Location = sec.Key("LOCATION").MustString("cn-south-222") | Location = sec.Key("LOCATION").MustString("cn-south-222") | ||||
BasePath = sec.Key("BASE_PATH").MustString("attachment/") | 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 { | func loadInternalToken(sec *ini.Section) string { | ||||
@@ -46,7 +46,7 @@ func CloudBrainIndex(ctx *context.Context) { | |||||
PageSize: setting.UI.IssuePagingNum, | PageSize: setting.UI.IssuePagingNum, | ||||
}, | }, | ||||
RepoID: repo.ID, | RepoID: repo.ID, | ||||
// SortType: sortType, | |||||
Type: models.TypeCloudBrainOne, | |||||
}) | }) | ||||
if err != nil { | if err != nil { | ||||
ctx.ServerError("Cloudbrain", err) | 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) | m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | ||||
}, context.RepoRef()) | }, 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.Group("/blockchain", func() { | ||||
m.Get("", repo.BlockChainIndex) | m.Get("", repo.BlockChainIndex) | ||||
}, context.RepoRef()) | }, context.RepoRef()) | ||||